commit c10f5a877468ddc755442cca0d9ec2c3d94ba593 Author: Christian Wolf Date: Thu Nov 10 22:40:12 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/auswertung.code-workspace b/auswertung.code-workspace new file mode 100644 index 0000000..57d88ef --- /dev/null +++ b/auswertung.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../../../../../nextcloud/Documents/Projekte/SLT/Auswertungsskript Solo" + } + ], + "settings": {} +} diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..b070a8c --- /dev/null +++ b/src/main.py @@ -0,0 +1,20 @@ +import solo_turnier +import logging + +def __initLogging(): + logging.basicConfig() + logging.root.setLevel(logging.NOTSET) + return logging.getLogger('solo_turnier') + +def main(): + l = __initLogging() + cli = solo_turnier.cli.Cli(l) + + if cli.showGUI(): + raise Exception('Not yet implemented') + else: + batchWorker = solo_turnier.batch.BatchWorker(cli) + batchWorker.run() + +if __name__ == '__main__': + main() diff --git a/src/solo_turnier/__init__.py b/src/solo_turnier/__init__.py new file mode 100644 index 0000000..82f696c --- /dev/null +++ b/src/solo_turnier/__init__.py @@ -0,0 +1,5 @@ + +from . import cli +from . import reader +from . import participant +from . import batch diff --git a/src/solo_turnier/batch.py b/src/solo_turnier/batch.py new file mode 100644 index 0000000..e211228 --- /dev/null +++ b/src/solo_turnier/batch.py @@ -0,0 +1,163 @@ + +import solo_turnier +import logging +import os + +class BatchWorker: + def __init__( + self, + config: solo_turnier.cli.Cli + ): + self.l = logging.getLogger('solo_turnier.batch') + self.config = config + + self.__groupMap = { + 'Kin.': 0, + 'Kin./Jun.': 1, + 'Jun.': 10, + 'Jun./Jug.': 11, + 'Jug.': 20 + } + self.__danceMap = { + 'Samba': 1, + 'Cha Cha': 2, + 'Rumba': 3, + 'Paso Doble': 4, + 'Jive': 5, + 'Langs. Walzer': 11, + 'Tango': 12, + 'Wiener Walzer': 13, + 'Slowfox': 14, + 'Quickstep': 15 + } + + def __extractDataFromFiles(self): + allResultName = os.path.join(self.config.importPath(), 'allresults.csv') + readerAllResults = solo_turnier.reader.AllResultReader(allResultName) + allResults = readerAllResults.readFile() + + finals = [] + + # Do this in a loop + # finalName = os.path.join(self.config.importPath(), 'wert_er.txt') + # readerFinal = solo_turnier.reader.ERReader(finalName) + # result = readerFinal.readFile() + # finals.append(result) + + return (allResults, finals) + + def __convertFinalToTuple(self, result): + return ( + result['gruppe'], + result['klasse'], + result['tanz'], + result['finalisten'] + ) + + def __extractAllData(self, r): + return { + 'date': r[1], + 'group': r[2], + 'class': r[3], + 'dance': r[4], + 'id': int(r[5]), + 'firstName': r[6], + 'lastName': r[7], + 'club': r[10], + 'ltv': r[11], + 'place': r[12], + 'placeTo': r[13], + 'points': r[14], + 'personGroup': r[15], + 'personClass': r[16], + 'remarks': r[17] + } + + def __extractPersonData(self, r): + data = self.__extractAllData(r) + person = solo_turnier.participant.Person( + firstName=data['firstName'], + lastName=data['lastName'], + club=data['club'], + group=data['personGroup'] + ) + return person + + def __extractPersons(self, allResults): + personRows = [self.__extractPersonData(r) for r in allResults['data']] + return list(set(personRows)) + + def __extractDanceResult(self, r): + data = self.__extractAllData(r) + try: + return { + 'dance': data['dance'], + 'class': data['personClass'], + 'place': int(data['place']), + 'placeTo': int(data['placeTo']), + 'id': data['id'] + } + except ValueError: + self.l.debug('Issue found with data %s.', data) + return None + + def __extractAllResults(self, allResults, persons: list[solo_turnier.participant.Person]): + ret = {} + for row in allResults['data']: + danceResult = self.__extractDanceResult(row) + if danceResult is not None: + person = self.__extractPersonData(row) + if person not in ret: + ret[person] = [] + + ret[person].append(danceResult) + return ret + + def __extractCompetitionEntries(self, r): + data = self.__extractAllData(r) + return (data['personGroup'], data['dance']) + + def __extractCompetitions(self, allResults): + def __decorate(r): + return (self.__groupMap[r[0]], self.__danceMap[r[1]],r) + competitionsRows = [self.__extractCompetitionEntries(r) for r in allResults['data']] + # competitions = list(set([self.__extractDanceResult(r) for r in allResults['data']])) + competitions = [__decorate(r) for r in list(set(competitionsRows))] + competitions.sort() + competitions = [r[2] for r in competitions] + return competitions + + def __extarctBasicData(self, allResults): + def __extractRow(r): + ret = { + 'firstName': r[6], + 'lastName': r[7], + 'number': int(r[5]), + 'club': r[10], + 'place': int(r[12]), + 'placeTo': int(r[13]), + 'group': r[15], + 'class': r[16] + } + return ret + + return [__extractRow(r) for r in allResults['data'][0:2]] + + def run(self): + allResults, finals = self.__extractDataFromFiles() + + persons = self.__extractPersons(allResults) + self.l.debug('Found persons: %s', persons) + + totalResults = self.__extractAllResults(allResults, persons) + self.l.debug('Total results: %s', totalResults) + + competitions = self.__extractCompetitions(allResults) + self.l.debug('Competitions: %s', competitions) + + # basicData = self.__extarctBasicData(allResults) + # self.l.debug('Basic extracted data: %s', basicData) + + # finalTuples = [self.__convertFinalToTuple(r) for r in finals] + # self.l.debug('Mapped to tuples: %s', finalTuples) + diff --git a/src/solo_turnier/cli.py b/src/solo_turnier/cli.py new file mode 100644 index 0000000..4aa7b87 --- /dev/null +++ b/src/solo_turnier/cli.py @@ -0,0 +1,34 @@ +import argparse +import logging + +class Cli: + def __init__(self, l: logging.Logger): + parser = argparse.ArgumentParser() + parser.add_argument('--gui', help='Show the GUI', action='store_true') + + parser.add_argument('-i', '--import-from', help='Set the path to load the TopTurnier exports from', nargs=1, default=['.']) + parser.add_argument('-o', '--output', help='Set the output path of the script', nargs=1) + + parser.add_argument('-v', '--verbose', help='Increase verbosity', action='count', default=0) + self.__args = parser.parse_args() + + map = { + 0: logging.ERROR, + 1: logging.WARN, + 2: logging.INFO, + 3: logging.DEBUG + } + logLevel = map.get(self.__args.verbose, logging.DEBUG) + l.setLevel(logLevel) + + def showGUI(self): + return self.__args.gui + + def importPath(self): + return self.__args.import_from[0] + + def output(self): + return self.__args.output[0] + + def getLogLevel(self): + return self.__args.verbose diff --git a/src/solo_turnier/participant.py b/src/solo_turnier/participant.py new file mode 100644 index 0000000..46976f0 --- /dev/null +++ b/src/solo_turnier/participant.py @@ -0,0 +1,31 @@ + +class Person: + def __init__( + self, + firstName: str, + lastName: str, + club: str, + group: str + ): + self.firstName = firstName + self.lastName = lastName + self.club = club + self.group = group + + def __eq__(self, o): + return ( + self.firstName == o.firstName and + self.lastName == o.lastName and + self.club == o.club and + self.group == o.group + ) + + def getTuple(self): + return (self.firstName, self.lastName, self.club) + + def __repr__(self): + return f'{self.firstName} {self.lastName} ({self.club}, {self.group})' + + def __hash__(self): + return self.firstName.__hash__() + self.lastName.__hash__() + self.club.__hash__() + diff --git a/src/solo_turnier/reader.py b/src/solo_turnier/reader.py new file mode 100644 index 0000000..347700a --- /dev/null +++ b/src/solo_turnier/reader.py @@ -0,0 +1,81 @@ + +import solo_turnier +import csv +import os +import logging +import re + +class AllResultReader: + def __init__(self, fileName: str): + self.fileName = fileName + + def readFile(self): + with open(self.fileName, 'r') as fp: + dialect = csv.Sniffer().sniff(fp.read(1024)) + fp.seek(0) + + csvReader = csv.reader(fp, dialect) + + rows = [] + for row in csvReader: + rows.append(row) + + ret = { + 'header': rows[0], + 'data': rows[1:] + } + + logging.getLogger('solo_turnier.reader.all_results').debug('Imported results from allresults.csv file: %s', ret) + return ret + +class ERReader: + def __init__(self, fileName: str): + self.fileName = fileName + self.l = logging.getLogger('solo_turnier.reader.wert_er') + + def __parseFileContent(self, lines): + gruppe = re.compile('Startgruppe:\W(.+)').search(lines[2]).group(1).strip() + klasse = re.compile('Startklasse:\W(.+)').search(lines[3]).group(1).strip() + tanz = re.compile('Turnierart:\W(.+)').search(lines[4]).group(1).strip() + + restLines = lines[5:] + + # Search for first line with the name of the dance + found = -1 + for i in range(len(restLines)): + if restLines[i].startswith(tanz): + found = i + break + + if found == -1: + raise Exception(f'Could not find the dance in the result file.') + + # Extract the finalists + finalists = [] + for i in range(found + 1, len(restLines)): + if restLines[i].startswith('TopTurnier'): + break + + match = re.compile('[0-9]+').match(restLines[i].strip()) + if match is None: + raise Exception('Could not parse starter number for end result table.') + else: + finalists.append(int(match.group(0))) + + ret = { + 'gruppe': gruppe, + 'klasse': klasse, + 'tanz': tanz, + 'finalisten': finalists + } + self.l.debug('Extracted data for final: %s', ret) + return ret + + def readFile(self): + with open(self.fileName, 'r') as fp: + lines = fp.readlines() + lines = [l.strip('\n') for l in lines] + lines = [l for l in lines if l != ''] + self.l.debug('Read lines for final: %s', lines) + + return self.__parseFileContent(lines)