** Das Python-Skript, dessen Erfassung und Erstellung von Übereinstimmungsdaten für ein Jahr etwas mehr als 20 Minuten dauerte, wurde so weit verbessert, dass Daten für 15 Jahre in 10 Minuten erstellt werden können. ** **.
Die Ergebnisse finden Sie in diesem Repository.
Shinichi-Nakagawa/py-retrosheet
Es ist uns gelungen, die Leistung gegenüber dem Original um das 20-fache zu verbessern!
Ich benutze jedoch nicht viel Technologie und warte auf Ihre Meinung.
Diese Geschichte ist eine Fortsetzung der Baseball-Geschichte von PyCon JP 2015.
Dies ist Qiitas erster Beitrag. Ich freue mich darauf, mit Dir zu arbeiten.
Auf der PyCon JP 2015 Baseball Hack! ~ Datenanalyse und -visualisierung mit Python Ich habe MLB-Daten heruntergeladen und migriert und sie mit IPython Notebook und Pandas analysiert und visualisiert! Ich stellte die Geschichte vor.
Darunter "[py-retrosheet](https://github.com/wellsoliver/py-retrosheet" py-retrosheet ")"
** Laden Sie die MLB-Spiel- und Spielerleistungsdaten "[RETROSHEET](http://retrosheet.org/" retrosheet.org ")" herunter und migrieren Sie zu Ihrer Lieblingsdatenbank! ** **.
Ich stellte die Bibliothek mit einem doy Gesicht vor, fand aber verschiedene unangenehme Dinge in dieser Bibliothek, obwohl es kein Fehler ist.
Ohne py-retrosheet könnte ich es nicht zu PyCon schaffen und nicht mit Joey Bot oder Adam Dan spielen. Ich bin den Autoren und Mitwirkenden der Bibliothek sehr dankbar, aber als ich versuchte, sie zu verwenden, hatte ich die folgenden nicht zu vernachlässigenden Probleme.
Retrosheet-Daten bestehen aus etwa 190.000 bis 200.000 Zeilen mit Aufzeichnungen pro Saison, aber wo machen Sie einen Umweg?
** Vom Herunterladen der Daten bis zum Erstellen der Datenbank dauerte es 20 Minuten. ** **.
Das Erstellen von 10.000 Datenzeilen dauert 1 Minute, was unmöglich ist.
Wenn Sie den Code für einen Moment lesen, können Sie für alle Datensätze zusätzliches Scraping durchführen, eine mysteriöse SQL-Anweisung ausgeben (z. B. eine Select-Anweisung mit einer eindeutigen Schlüsselprüfung auslösen, um eine Datenbank aus einem neuen Status zu erstellen) usw. Ich habe Zeilen nacheinander eingefügt und so weiter.
** Und fast kopieren und einfügen! ** **.
Da ich Python 3 persönlich zur Hauptentwicklung machen möchte, dachte ich, dass eine Bibliothek, die nur Python 2 unterstützt, das allgemein als "Legacy Python" bekannt ist, nicht cool wäre, und entschied mich daher, die Bibliothek zu verbessern.
Also gabelte ich sofort das ursprüngliche Repository und trug selbst bei. -Nakagawa / py-retrosheet "py-retrosheet") Bereit und begann sich zu verbessern. Verbesserungen wurden in der folgenden Reihenfolge vorgenommen.
Das Problem der langsamen Verarbeitung ist schwerwiegender als alles andere, und die ursprüngliche Reihenfolge soll von dort aus beginnen. Ich habe mich jedoch entschlossen, den Inhalt des Codes genau zu verstehen und den Engpass zu finden. Zunächst ist der ursprüngliche Code Python 3 Ich habe an der entsprechenden Stelle angefangen. Zusätzlich zum Erfassen der Spezifikationen waren die Testbedingungen klar (es ist in Ordnung, wenn dieselbe Bewegung wie das Original ausgeführt wird).
Danach wollte ich den ursprünglichen Code umgestalten, die Wartbarkeit verbessern und den Engpass der Geschwindigkeitsverzögerung angreifen.
** Der Code war so hässlich, dass ich beschlossen habe, ihn neu zu erstellen. ** **.
Konvertieren Sie den Code mit 2to3, einem Standard bei der Migration von Python 2 auf 3, und antworten Sie, während Ausnahmen stetig beseitigt werden. Hat. Übrigens habe ich so viel wie möglich behoben, wo es nicht pep8 folgt, und es scheint besser (oder kann behoben werden), es zu beheben.
Der schwierige Teil war, dass eine solche Importanweisung unvermeidlich platzte.
scripts/download.py
import os
import requests
try:
# Python 3.x
from configparser import ConfigParser
import queue
except ImportError:
# Python 2.x (Nein, ich will nicht schreiben...orz)
from ConfigParser import ConfigParser
import Queue as queue
Ich konnte entkommen, indem ich die Importeinheit änderte und den Alias schnitt.
Nach dieser Renovierung habe ich Pull Request to Original Author ausgegeben.
Nachdem ich Python 3 unterstützt hatte, dachte ich darüber nach, einen Test zu schreiben und ihn umzugestalten, aber ich war wütend und entschied mich, ihn neu zu schreiben, weil es schwierig war, einen Test zu schreiben und der Code überhaupt nicht gut war.
py-retrosheet
Es ist wirklich einfach zu tun, aber ich hatte das Gefühl, dass ich diese einfache Sache absichtlich gemacht habe. Gleichzeitig mit der Überprüfung der Konfiguration selbst habe ich die Spezifikationen hinzugefügt, die aus Python 3.5 hinzugefügt wurden (weil ich es persönlich ausprobieren möchte). Machen wir es zu einer genutzten Bibliothek! Also habe ich einige Verbesserungen vorgenommen.
Darüber hinaus wurde das ursprüngliche Skript so konzipiert, dass nur Daten für eine Saison (ein Jahr) pro Ausführung erfasst und erstellt werden. Da jedoch Daten für zwei Jahre oder länger mit tatsächlichen Problemen angefordert wurden, haben wir uns entschlossen, dies ebenfalls zu unterstützen. ..
Zunächst sieht der Download so aus. Dies ist eigentlich fast das gleiche wie der ursprüngliche Code.
scripts/retrosheet_download.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Download .
Python 3.5.0+ (don't know about 3.4- and 2.x, sorry)
MySQL 5.6.0+ (don't know about 5.5- , sorry)
"""
import os
import click
from configparser import ConfigParser
from classes.fetcher import Fetcher
from queue import Queue
__author__ = 'Shinichi Nakagawa'
class RetrosheetDownload(object):
FILES = (
{
'name': 'eventfiles',
'config_flg': 'dl_eventfiles',
'url': 'eventfiles_url',
'pattern': r'({year})eve\.zip',
'download_url': 'http://www.retrosheet.org/events/{year}eve.zip',
},
{
'name': 'gamelogs',
'config_flg': 'dl_gamelogs',
'url': 'gamelogs_url',
'pattern': r'gl({year})\.zip',
'download_url': 'http://www.retrosheet.org/gamelogs/gl{year}.zip',
}
)
def __init__(self, configfile: str):
"""
initialize
:param configfile: configuration file
"""
# configuration
self.config = ConfigParser()
self.config.read(configfile)
self.num_threads = self.config.getint('download', 'num_threads')
self.path = self.config.get('download', 'directory')
self.absolute_path = os.path.abspath(self.path)
def download(self, queue):
"""
Download & a Archives
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
threads = []
for i in range(self.num_threads):
t = Fetcher(queue, self.absolute_path, {'verbose': self.config.get('debug', 'verbose')})
t.start()
threads.append(t)
for thread in threads:
thread.join()
@classmethod
def run(cls, from_year: int, to_year: int, configfile: str):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
client = RetrosheetDownload(configfile)
if not os.path.exists(client.absolute_path):
print("Directory %s does not exist, creating..." % client.absolute_path)
os.makedirs(client.absolute_path)
urls = Queue()
for year in range(from_year, to_year + 1):
for _file in RetrosheetDownload.FILES:
urls.put(_file['download_url'].format(year=year))
client.download(urls)
@click.command()
@click.option('--from_year', '-f', default=2001, help='From Season')
@click.option('--to_year', '-t', default=2014, help='To Season')
@click.option('--configfile', '-c', default='config.ini', help='Config File')
def download(from_year, to_year, configfile):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
# from <= to check
if from_year > to_year:
print('not From <= To({from_year} <= {to_year})'.format(from_year=from_year, to_year=to_year))
raise SystemExit
RetrosheetDownload.run(from_year, to_year, configfile)
if __name__ == '__main__':
download()
Hören Sie auf, unnötige Seiten zu bekommen und zu kratzen.
In Bezug auf das Problem der "komplexen Behandlung von Argumenten in der Befehlszeile und in der Konfiguration", das im Allgemeinen ein Wartungsproblem für py-retrosheet darstellte, lautet das erstere click. Es wurde durch Klicken auf ") vereinfacht, und letzteres wurde gelöst, indem der Erfassungsort von config in der anfänglichen Verarbeitung der Klasse aggregiert wurde.
Dies gilt auch für die Datenerfassung und -erstellung, die später eingeführt werden.
Als nächstes folgt der Teil, um Daten mit Chadwick abzurufen.
scripts/parse_csv.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Parse to Event Files, Game Logs and Roster.
Python 3.5.0+ (don't know about 3.4- and 2.x, sorry)
"""
import os
import subprocess
import click
from configparser import ConfigParser
__author__ = 'Shinichi Nakagawa'
class ParseCsv(object):
CW_EVENT = '{chadwick_path}cwevent'
CW_GAME = '{chadwick_path}cwgame'
CW_EVENT_CMD = '{chadwick_path}cwevent -q -n -f 0-96 -x 0-62 -y {year} {year}*.EV* > {csvpath}/events-{year}.csv'
CW_GAME_CMD = '{chadwick_path}cwgame -q -n -f 0-83 -y {year} {year}*.EV* > {csvpath}/games-{year}.csv'
EV_FILE_PATTERN = '{path}/{year}*.EV*'
EVENT_FILE = '{csvpath}/events-{year}.csv'
GAME_FILE = '{csvpath}/games-{year}.csv'
CSV_PATH = 'csv'
def __init__(self):
pass
@classmethod
def exists_chadwick(cls, chadwick_path: str):
"""
exists chadwick binary
:param chadwick_path: chadwick path
:return: True or False
"""
if os.path.exists(chadwick_path) \
& os.path.exists(cls.CW_EVENT.format(chadwick_path=chadwick_path)) \
& os.path.exists(cls.CW_GAME.format(chadwick_path=chadwick_path)):
return True
return False
@classmethod
def generate_files(
cls,
year: int,
cmd_format: str,
filename_format: str,
chadwick_path: str,
verbose: bool,
csvpath: str
):
"""
Generate CSV file
:param year: Season
:param cmd_format: Command format
:param filename_format: Filename format
:param chadwick_path: Chadwick Command Path
:param verbose: Debug flg
:param csvpath: csv output path
"""
cmd = cmd_format.format(csvpath=csvpath, year=year, chadwick_path=chadwick_path)
filename = filename_format.format(csvpath=csvpath, year=year)
if os.path.isfile(filename):
os.remove(filename)
if verbose:
print('calling {cmd}'.format(cmd=cmd))
subprocess.call(cmd, shell=True)
@classmethod
def generate_retrosheet_files(
cls,
from_year: int,
to_year: int,
chadwick_path: str,
verbose: str,
csvpath: str
):
"""
Generate CSV file
:param from_year: Season(from)
:param to_year: Season(to)
:param chadwick_path: Chadwick Command Path
:param verbose: Debug flg
:param csvpath: csv output path
"""
# generate files
for year in [year for year in range(from_year, to_year + 1)]:
# game
ParseCsv.generate_files(
year=year,
cmd_format=ParseCsv.CW_GAME_CMD,
filename_format=ParseCsv.GAME_FILE,
chadwick_path=chadwick_path,
verbose=verbose,
csvpath=csvpath
)
# event
ParseCsv.generate_files(
year=year,
cmd_format=ParseCsv.CW_EVENT_CMD,
filename_format=ParseCsv.EVENT_FILE,
chadwick_path=chadwick_path,
verbose=verbose,
csvpath=csvpath
)
@classmethod
def run(cls, from_year: int, to_year: int, configfile: str):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
config = ConfigParser()
config.read(configfile)
verbose = config.getboolean('debug', 'verbose')
chadwick = config.get('chadwick', 'directory')
path = os.path.abspath(config.get('download', 'directory'))
csv_path = '{path}/csv'.format(path=path)
# command exists check
if not cls.exists_chadwick(chadwick):
print('chadwick does not exist in {chadwick} - exiting'.format(chadwick=chadwick))
raise SystemExit
# make directory
os.chdir(path)
if not os.path.exists(ParseCsv.CSV_PATH):
os.makedirs(ParseCsv.CSV_PATH)
# generate files
cls.generate_retrosheet_files(
from_year=from_year,
to_year=to_year,
chadwick_path=chadwick,
verbose=verbose,
csvpath=csv_path
)
# change directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
@click.command()
@click.option('--from_year', '-f', default=2001, help='From Season')
@click.option('--to_year', '-t', default=2014, help='To Season')
@click.option('--configfile', '-c', default='config.ini', help='Config File')
def create_retrosheet_csv(from_year, to_year, configfile):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
# from <= to check
if from_year > to_year:
print('not From <= To({from_year} <= {to_year})'.format(from_year=from_year, to_year=to_year))
raise SystemExit
ParseCsv.run(from_year, to_year, configfile)
if __name__ == '__main__':
create_retrosheet_csv()
Ich habe die Spieldaten (Spielprotokoll) und die Ereignisdaten (Ereignisprotokoll) während des Spiels verarbeitet, z. B. beim Schlagen und beim separaten Stehlen, aber ich habe dasselbe getan (nur der verwendete Befehl und das Ausgabeziel sind unterschiedlich) Die Verarbeitung ist in Methoden gruppiert und die Definition erfolgt von außen. Dies gilt auch für die Implementierung der Datenerstellung.
scripts/retrosheet_mysql.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Migrate Database, Game Logs and Roster.
Python 3.5.0+ (don't know about 3.4- and 2.x, sorry)
MySQL 5.6.0+ (don't know about 5.5- , sorry)
"""
import os
import csv
import click
import sqlalchemy
from glob import glob
from configparser import ConfigParser, NoOptionError
__author__ = 'Shinichi Nakagawa'
class RetrosheetMySql(object):
DATABASE_ENGINE = 'mysql+pymysql'
ENGINE = '{engine}://{user}:{password}@{host}/{database}'
TABLES = (
{
'name': 'teams',
'header': False,
'year': False,
'mask': '{path}/TEAM{year}*',
'select': "SELECT * FROM teams WHERE team_id = '{key_0}'",
'where_index': [0],
'insert': 'INSERT INTO teams VALUES {values}',
},
{
'name': 'rosters',
'header': False,
'year': True,
'mask': '{path}/*{year}*.ROS',
'select': "SELECT * FROM rosters WHERE year = {year} AND player_id = '{key_0}' AND team_tx = '{key_1}'",
'where_index': [0, 5],
'insert': 'INSERT INTO rosters VALUES {values}',
},
{
'name': 'games',
'header': True,
'year': False,
'mask': '{path}/csv/games-{year}*.csv',
'select': "SELECT * FROM games WHERE game_id = '{key_0}'",
'where_index': [0],
'insert': 'INSERT INTO games({columns}) VALUES {values}',
},
{
'name': 'events',
'header': True,
'year': False,
'mask': '{path}/csv/events-{year}*.csv',
'select': "SELECT * FROM events WHERE game_id = '{key_0}' AND event_id = '{key_1}'",
'where_index': [0, 96],
'insert': 'INSERT INTO events({columns}) VALUES {values}',
},
)
def __init__(self, configfile: str):
"""
initialize
:param configfile: configuration file
"""
# configuration
config = ConfigParser()
config.read(configfile)
self.path = os.path.abspath(config.get('download', 'directory'))
self.record_check = config.getboolean('retrosheet_mysql', 'record_check')
self.multiple_insert_rows = config.getint('retrosheet_mysql', 'multiple_insert_rows')
# connection
self.connection = self._generate_connection(config)
@classmethod
def _generate_connection(cls, config: ConfigParser):
"""
generate database connection
:param config: ConfigParser object
:return:
"""
try:
database_engine = cls.DATABASE_ENGINE
database = config.get('database', 'database')
host = config.get('database', 'host')
user = config.get('database', 'user')
password = config.get('database', 'password')
except NoOptionError:
print('Need to define engine, user, password, host, and database parameters')
raise SystemExit
db = sqlalchemy.create_engine(
cls.ENGINE.format(
engine=database_engine,
user=user,
password=password,
host=host,
database=database,
)
)
return db.connect()
@classmethod
def _exists_record(cls, year: int, table: dict, csv_row: list, connection):
where = {'key_{i}'.format(i=i): csv_row[v] for i, v in enumerate(table['where_index'])}
where['year'] = year
sql = table['select'].format(**where)
res = connection.execute(sql)
if res.rowcount > 0:
return True
return False
def _multiple_insert(self, query: str, columns: list, values: list):
params = {
'columns': columns,
'values': ', '.join(values),
}
sql = query.format(**params)
try:
self.connection.execute(sql)
except Exception as e:
print(e)
raise SystemExit
def _create_record(self, year: int, csv_file: str, table: dict):
reader = csv.reader(open(csv_file))
headers = []
values = []
if table['header']:
headers = next(reader)
columns = ', '.join(headers)
for row in reader:
# Record Exists Check
if self.record_check and RetrosheetMySql._exists_record(year, table, row, self.connection):
continue
# append record values
if table['year']: row.insert(0, str(year))
values.append('("{rows}")'.format(rows='", "'.join(row)))
if len(values) == self.multiple_insert_rows:
# inserts
self._multiple_insert(table['insert'], columns, values)
values = []
if len(values) > 0:
# inserts
self._multiple_insert(table['insert'], columns, values)
def execute(self, year: int):
for table in self.TABLES:
for csv_file in glob(table['mask'].format(path=self.path, year=year)):
self._create_record(year, csv_file, table)
@classmethod
def run(cls, from_year: int, to_year: int, configfile: str):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
client = RetrosheetMySql(configfile)
for year in range(from_year, to_year + 1):
client.execute(year)
client.connection.close()
@click.command()
@click.option('--from_year', '-f', default=2001, help='From Season')
@click.option('--to_year', '-t', default=2014, help='To Season')
@click.option('--configfile', '-c', default='config.ini', help='Config File')
def migration(from_year, to_year, configfile):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
# from <= to check
if from_year > to_year:
print('not From <= To({from_year} <= {to_year})'.format(from_year=from_year, to_year=to_year))
raise SystemExit
RetrosheetMySql.run(from_year, to_year, configfile)
if __name__ == '__main__':
migration()
Die meisten Verbesserungen in dieser Implementierung,
Die Auswahlanweisungsprüfung wird implementiert, aber nicht verwendet (sie kann durch Ändern der Konfigurationseinstellung verwendet werden).
Die Geschwindigkeit hat sich dramatisch verbessert, indem die zeilenweise gut benommene Einfügeanweisung in 1000 Zeilen geändert wurde (die auch mit config angepasst werden kann).
Außerdem hat sich der allgemeine Ausblick für den Code verbessert, sodass das Hinzufügen von All-Star- und Playoff-Daten, die Sie noch nicht verwendet haben, einfach zu handhaben ist!
Nachdem ich mich bisher verbessert hatte, versuchte ich, die Daten von 2014 (ca. 190.000 Zeilen) zu vergleichen.
Das Ziel ist
ist. Messung ist
Ich habe es unter der Bedingung getan.
#Vor der Verbesserung
$ time python download.py -y 2014
Queuing up Event Files for download (2014 only).
Queuing up Game Logs for download (2014 only).
Fetching 2014eve.zip
Fetching gl2014.zip
Zip file detected. Extracting gl2014.zip
Zip file detected. Extracting 2014eve.zip
real 0m5.816s
user 0m0.276s
sys 0m0.066s
$ time python parse.py -y 2014
calling '/usr/local/bin//cwevent -q -n -f 0-96 -x 0-62 -y 2014 2014*.EV* > /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/events-2014.csv'
calling '/usr/local/bin//cwgame -q -n -f 0-83 -y 2014 2014*.EV* > /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/games-2014.csv'
processing TEAM2014
processing ANA2014.ROS
processing ARI2014.ROS
processing ATL2014.ROS
processing BAL2014.ROS
processing BOS2014.ROS
processing CHA2014.ROS
processing CHN2014.ROS
processing CIN2014.ROS
processing CLE2014.ROS
processing COL2014.ROS
processing DET2014.ROS
processing HOU2014.ROS
processing KCA2014.ROS
processing LAN2014.ROS
processing MIA2014.ROS
processing MIL2014.ROS
processing MIN2014.ROS
processing NYA2014.ROS
processing NYN2014.ROS
processing OAK2014.ROS
processing PHI2014.ROS
processing PIT2014.ROS
processing SDN2014.ROS
processing SEA2014.ROS
processing SFN2014.ROS
processing SLN2014.ROS
processing TBA2014.ROS
processing TEX2014.ROS
processing TOR2014.ROS
processing WAS2014.ROS
processing /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/games-2014.csv
processing /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/events-2014.csv
real 19m53.224s
user 13m16.074s
sys 0m26.294s
#Nach der Verbesserung
$ time python retrosheet_download.py -f 2014 -t 2014
Fetching 2014eve.zip
Fetching gl2014.zip
Zip file detected. Extracting gl2014.zip
Zip file detected. Extracting 2014eve.zip
real 0m4.630s
user 0m0.217s
sys 0m0.055s
$ time python parse_csv.py -f 2014 -t 2014
calling /usr/local/bin/cwgame -q -n -f 0-83 -y 2014 2014*.EV* > /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/games-2014.csv
calling /usr/local/bin/cwevent -q -n -f 0-96 -x 0-62 -y 2014 2014*.EV* > /Users/shinyorke_mbp/PycharmProjects/py-retrosheet/scripts/files/csv/events-2014.csv
real 0m8.713s
user 0m8.321s
sys 0m0.221s
$ time python retrosheet_mysql.py -f 2014 -t 2014
real 0m21.435s
user 0m3.580s
sys 0m0.416s
** Der Prozess, der ursprünglich 20 Minuten dauerte, wurde auf weniger als 1 Minute verbessert! ** **.
Selbst wenn Sie eine große Menge an Spiel- / Sitzdaten benötigen, können Sie diese problemlos abrufen oder erstellen.
Es wird interessant, deshalb habe ich beschlossen, Daten für 15 Jahre von 2000 bis 2014 zu erstellen.
Die Gesamtzahl der Datensätze beträgt ungefähr 2,9 Millionen, nicht ungefähr 3 Millionen.
Ich habe das Skript für die Ausführung auf einmal neu transkribiert.
scripts/migration.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Migrate Retrosheet Database.
Python 3.5.0+ (don't know about 3.4- and 2.x, sorry)
MySQL 5.6.0+ (don't know about 5.5- , sorry)
"""
import logging
import click
from retrosheet_download import RetrosheetDownload
from retrosheet_mysql import RetrosheetMySql
from parse_csv import ParseCsv
__author__ = 'Shinichi Nakagawa'
@click.command()
@click.option('--from_year', '-f', default=2010, help='From Season')
@click.option('--to_year', '-t', default=2014, help='To Season')
@click.option('--configfile', '-c', default='config.ini', help='Config File')
def main(from_year, to_year, configfile):
"""
:param from_year: Season(from)
:param to_year: Season(to)
:param configfile: Config file
"""
# from <= to check
if from_year > to_year:
print('not From <= To({from_year} <= {to_year})'.format(from_year=from_year, to_year=to_year))
raise SystemExit
# logging setting
logging.basicConfig(
level=logging.INFO,
format="Time:%(asctime)s.%(msecs)03d\t message:%(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# Download
logging.info('File Download Start({from_year}-{to_year})'.format(from_year=from_year, to_year=to_year))
RetrosheetDownload.run(from_year, to_year, configfile)
logging.info('File Download End')
# Parse Csv
logging.info('Csv Parse Start({from_year}-{to_year})'.format(from_year=from_year, to_year=to_year))
ParseCsv.run(from_year, to_year, configfile)
logging.info('Csv Parse End')
# Migrate MySQL Database
logging.info('Migrate Database Start({from_year}-{to_year})'.format(from_year=from_year, to_year=to_year))
RetrosheetMySql.run(from_year, to_year, configfile)
logging.info('Migrate Database End')
if __name__ == '__main__':
main()
Klicken Sie hier für Ergebnisse
$ python migration.py -f 2000 -t 2014
Time:2015-11-15 15:23:48.291 message:File Download Start(2000-2014)
Directory /Users/shinyorke_mbp/PycharmProjects/hatteberg/py-retrosheet/scripts/files does not exist, creating...
Time:2015-11-15 15:23:59.673 message:File Download End
Time:2015-11-15 15:23:59.673 message:Csv Parse Start(2000-2014)
Time:2015-11-15 15:26:37.219 message:Csv Parse End
Time:2015-11-15 15:26:37.220 message:Migrate Database Start(2000-2014)
Time:2015-11-15 15:32:50.070 message:Migrate Database End
** Es dauerte weniger als 10 Minuten von der Datenerfassung bis zur Fertigstellung der Abbildung! !! !! ** **.
Dies macht ein bisschen mehr Spaß Baseball Hack!
Recommended Posts