From 43180c6e054bf48f6c3db272af8399bf2509f89d Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Tue, 21 Nov 2023 20:14:12 +0100 Subject: [PATCH] First running version Not yet comparable with previous code base due to different outputs. --- src/solo_turnier/competition_class.py | 6 + src/solo_turnier/group.py | 6 + src/solo_turnier/html_parser.py | 37 ++- src/solo_turnier/output.py | 6 +- src/solo_turnier/types/__init__.py | 9 +- src/solo_turnier/types/competitionTuple.py | 27 ++ .../types/htmlCompetitionTotalResults.py | 11 +- src/solo_turnier/types/htmlParticipant.py | 2 +- src/solo_turnier/types/htmlPreviewImport.py | 18 -- .../types/htmlSingleCompetitionFixture.py | 17 ++ .../types/htmlSingleCompetitionResult.py | 11 +- src/solo_turnier/types/outputTable.py | 7 - src/solo_turnier/types/participant.py | 25 +- src/solo_turnier/types/participantResult.py | 23 -- src/solo_turnier/types/person.py | 3 + .../types/singleParticipantResult.py | 31 +-- src/solo_turnier/types/stages.py | 18 +- .../types/tableCompetitionEntry.py | 44 ---- src/solo_turnier/types/tableEntry.py | 9 - src/solo_turnier/types/tableRow.py | 16 -- src/solo_turnier/types/totalGroupResult.py | 6 +- src/solo_turnier/workers/ResultExtractor.py | 32 ++- src/solo_turnier/workers/Worker.py | 238 ++++++++++++++---- 23 files changed, 339 insertions(+), 263 deletions(-) create mode 100644 src/solo_turnier/types/competitionTuple.py delete mode 100644 src/solo_turnier/types/htmlPreviewImport.py create mode 100644 src/solo_turnier/types/htmlSingleCompetitionFixture.py delete mode 100644 src/solo_turnier/types/outputTable.py delete mode 100644 src/solo_turnier/types/participantResult.py create mode 100644 src/solo_turnier/types/person.py delete mode 100644 src/solo_turnier/types/tableCompetitionEntry.py delete mode 100644 src/solo_turnier/types/tableEntry.py delete mode 100644 src/solo_turnier/types/tableRow.py diff --git a/src/solo_turnier/competition_class.py b/src/solo_turnier/competition_class.py index 52f68ec..7411845 100644 --- a/src/solo_turnier/competition_class.py +++ b/src/solo_turnier/competition_class.py @@ -26,6 +26,12 @@ class CombinedCompetitionClass: else: return f"{self.clsA}/{self.clsB}/{self.clsC}" + def __eq__(self, other): + return type(self) == type(other) and self.__dict__ == other.__dict__ + + def __hash__(self): + return hash(("combinedClass", self.clsA, self.clsB, self.clsC)) + Class_t = CompetitionClass | CombinedCompetitionClass diff --git a/src/solo_turnier/group.py b/src/solo_turnier/group.py index ffd5b7b..4efc369 100644 --- a/src/solo_turnier/group.py +++ b/src/solo_turnier/group.py @@ -20,6 +20,12 @@ class CombinedGroup: def __repr__(self): return f"{self.clsA}/{self.clsB}" + def __hash__(self): + return hash(("combinedGroup", self.clsA, self.clsB)) + + def __eq__(self, other): + return type(self) == type(other) and self.__hash__() == other.__hash__() + def getContainedGroups(self): return (self.clsA, self.clsB) diff --git a/src/solo_turnier/html_parser.py b/src/solo_turnier/html_parser.py index 2f1c02d..81b04bf 100644 --- a/src/solo_turnier/html_parser.py +++ b/src/solo_turnier/html_parser.py @@ -3,10 +3,11 @@ from bs4 import BeautifulSoup import logging import re -from .types import HtmlPreviewParticipant, HtmlParticipant, HtmlResultTotalTable -from .types import HtmlPreviewImport as HtmlImport, HtmlResultImport +from .types import HtmlParticipant, HtmlResultTotalTable +from .types import HtmlResultImport from .group import GroupParser from .competition_class import CompetitionClassParser +import solo_turnier class IncompleteRoundException(Exception): @@ -45,11 +46,11 @@ class HtmlParser: return { "dance": dance.strip(), - "class_": str(self.classParser.parseClass(rawClass, True)), - "group": str(self.groupParser.parseGroup(rawGroup)), + "class_": self.classParser.parseClass(rawClass, True), + "group": self.groupParser.parseGroup(rawGroup), } - def parseResult(self): + def parseResult(self) -> HtmlResultImport: participants = {} def __parseRows(rows, finalist: bool): @@ -111,6 +112,8 @@ class HtmlParser: def parseIndividualResult(self, competitionGroup, competitionClass, dance): participants = {} + rePlaceParser = re.compile("([0-9]+)(?:-([0-9]+))?") + def __parseTable(table): rows = table.find_all("tr") @@ -148,7 +151,20 @@ class HtmlParser: rawStr = tag.contents[0].strip() if rawStr.endswith("-"): rawStr = rawStr[:-1] - return rawStr + + matcher = rePlaceParser.fullmatch(rawStr) + if matcher is None: + self.l.error( + "Could not parse place string '%s' to get fixture.", rawStr + ) + return None + + place = int(matcher.group(1)) + placeTo = matcher.group(2) + if placeTo is not None: + placeTo = int(placeTo) + + return solo_turnier.types.Place(place, placeTo) places = list(map(getSinglePlaceStr, placeTags)) return places @@ -180,8 +196,13 @@ class HtmlParser: cls = classes[idx] if classes is not None else None grp = groups[idx] if groups is not None else None - tup = (competitionGroup, competitionClass, dance, id) - participants[tup] = (places[idx], cls, grp) + tup = solo_turnier.types.CompetitionTuple( + competitionGroup, competitionClass, dance, int(id) + ) + fixture = solo_turnier.types.HtmlSingleCompetitionFixture( + place=places[idx], class_=cls, group=grp + ) + participants[tup] = fixture tables = self.soup.find("div", class_="extract").find_all("table") for table in tables: diff --git a/src/solo_turnier/output.py b/src/solo_turnier/output.py index 8d21f46..69816ce 100644 --- a/src/solo_turnier/output.py +++ b/src/solo_turnier/output.py @@ -87,8 +87,8 @@ class ConsoleOutputter(AbstractOutputter): if result is None: return "" - placeNative = getPlace(result.placeNative, result.placeNativeTo) - place = getPlace(result.place, result.placeTo) + placeNative = str(result.nativePlace) + place = str(result.place) lineOne = f"{placeNative} ({result.nativeClass})" lineTwo = f"[{place} in {result.competitionClass}]" @@ -106,7 +106,7 @@ class ConsoleOutputter(AbstractOutputter): print(tabulate(tableData, headers="firstrow", tablefmt="fancy_grid")) def output(self, data: types.State4): - for idx, group in enumerate(data.groups): + for idx, group in enumerate(data.results): if idx > 0: print() diff --git a/src/solo_turnier/types/__init__.py b/src/solo_turnier/types/__init__.py index 31e08e4..09f5ff1 100644 --- a/src/solo_turnier/types/__init__.py +++ b/src/solo_turnier/types/__init__.py @@ -1,20 +1,17 @@ from .place import Place +from .person import Person from .htmlPreviewParticipant import HtmlPreviewParticipant from .htmlParticipant import HtmlParticipant -from .htmlPreviewImport import HtmlPreviewImport from .htmlResultImport import HtmlResultImport from .htmlResultTotalTable import HtmlResultTotalTable from .htmlCompetitionResultRow import HtmlCompetitionResultRow +from .competitionTuple import CompetitionTuple from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult +from .htmlSingleCompetitionFixture import HtmlSingleCompetitionFixture from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults from .singleParticipantResult import SingleParticipantResult from .totalGroupResult import TotalGroupResult from .participant import Participant -from .participantResult import ParticipantResult -from .tableCompetitionEntry import TableCompetitionEntry -from .tableEntry import TableEntry -from .tableRow import TableRow -from .outputTable import OutputTable from .stages import * diff --git a/src/solo_turnier/types/competitionTuple.py b/src/solo_turnier/types/competitionTuple.py new file mode 100644 index 0000000..947355f --- /dev/null +++ b/src/solo_turnier/types/competitionTuple.py @@ -0,0 +1,27 @@ +import solo_turnier + + +class CompetitionTuple: + def __init__( + self, + group: solo_turnier.group.Group_t, + class_: solo_turnier.competition_class.Class_t, + dance: str, + id: int, + ): + self.group = group + self.class_ = class_ + self.dance = dance + self.id = id + + def __hash__(self): + return hash(("Tuple", self.group, self.class_, self.dance, self.id)) + + def __repr__(self): + return f"T({self.group},{self.class_},{self.dance},{self.id})" + + def __eq__(self, other): + if type(other) != type(self): + return False + + return self.__hash__() == other.__hash__() diff --git a/src/solo_turnier/types/htmlCompetitionTotalResults.py b/src/solo_turnier/types/htmlCompetitionTotalResults.py index e6d5ea1..fb46b0e 100644 --- a/src/solo_turnier/types/htmlCompetitionTotalResults.py +++ b/src/solo_turnier/types/htmlCompetitionTotalResults.py @@ -1,11 +1,12 @@ import solo_turnier from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult +from .competitionTuple import CompetitionTuple class HtmlCompetitionTotalResults: def __init__(self): self.results = {} - self.tabges = {} + self.fixups = {} def __getTuple( self, @@ -14,7 +15,7 @@ class HtmlCompetitionTotalResults: dance: str, id: int, ): - return (group, class_, dance, id) + return CompetitionTuple(group, class_, dance, id) def get( self, @@ -34,16 +35,16 @@ class HtmlCompetitionTotalResults: ret = {} for k in self.results: - if int(k[3]) != id: + if int(k.id) != id: continue # ret = ret + self.results[k] # Dance, Group, Class - key = (k[2], k[0], k[1]) + key = (k.dance, k.group, k.class_) ret[key] = self.results[k] return ret - def add(self, group, class_, dance, id, result: HtmlSingleCompetitionResult): + def add(self, group, class_, dance, id: int, result: HtmlSingleCompetitionResult): tup = self.__getTuple(group, class_, dance, id) l = self.results.get(tup, []) l.append(result) diff --git a/src/solo_turnier/types/htmlParticipant.py b/src/solo_turnier/types/htmlParticipant.py index a0be30c..8502d6e 100644 --- a/src/solo_turnier/types/htmlParticipant.py +++ b/src/solo_turnier/types/htmlParticipant.py @@ -5,7 +5,7 @@ class HtmlParticipant: self.finalist = None def __eq__(self, o): - if type(o) != HtmlPreviewParticipant: + if type(o) != HtmlParticipant: return False return all( diff --git a/src/solo_turnier/types/htmlPreviewImport.py b/src/solo_turnier/types/htmlPreviewImport.py deleted file mode 100644 index 36405ed..0000000 --- a/src/solo_turnier/types/htmlPreviewImport.py +++ /dev/null @@ -1,18 +0,0 @@ -from .htmlPreviewParticipant import HtmlPreviewParticipant -import solo_turnier - - -class HtmlPreviewImport: - def __init__( - self, - participants: dict[int, list[HtmlPreviewParticipant]], - results: dict[ - HtmlPreviewParticipant, - dict[str, solo_turnier.competition_class.CompetitionClass], - ], - ): - self.participants = participants - self.results = results - - def __repr__(self): - return (str(self.participants), str(self.results)) diff --git a/src/solo_turnier/types/htmlSingleCompetitionFixture.py b/src/solo_turnier/types/htmlSingleCompetitionFixture.py new file mode 100644 index 0000000..fbd2334 --- /dev/null +++ b/src/solo_turnier/types/htmlSingleCompetitionFixture.py @@ -0,0 +1,17 @@ +from .place import Place +import solo_turnier + + +class HtmlSingleCompetitionFixture: + def __init__( + self, + place: Place, + group: solo_turnier.group.Group, + class_: solo_turnier.competition_class.CompetitionClass, + ): + self.place = place + self.group = group + self.class_ = class_ + + def __repr__(self): + return f"Fix({self.place},{self.group},{self.class_})" diff --git a/src/solo_turnier/types/htmlSingleCompetitionResult.py b/src/solo_turnier/types/htmlSingleCompetitionResult.py index 916748b..175aca0 100644 --- a/src/solo_turnier/types/htmlSingleCompetitionResult.py +++ b/src/solo_turnier/types/htmlSingleCompetitionResult.py @@ -1,15 +1,14 @@ +from .place import Place + + class HtmlSingleCompetitionResult: - def __init__(self, name, place, placeTo, finalist): + def __init__(self, name: str, place: Place, finalist: bool): self.name = name self.place = place - self.placeTo = placeTo self.finalist = finalist def __repr__(self): - if self.placeTo is None: - place = self.place - else: - place = f"{self.place}-{self.placeTo}" + place = self.place if self.finalist: return f"Res({self.name} [F], placed {place})" diff --git a/src/solo_turnier/types/outputTable.py b/src/solo_turnier/types/outputTable.py deleted file mode 100644 index 8eebe01..0000000 --- a/src/solo_turnier/types/outputTable.py +++ /dev/null @@ -1,7 +0,0 @@ -from .tableRow import TableRow - - -class OutputTable: - def __init__(self, dances: list[str], rows: list[TableRow]): - self.dances = dances - self.rows = rows diff --git a/src/solo_turnier/types/participant.py b/src/solo_turnier/types/participant.py index f0cfd65..c69fece 100644 --- a/src/solo_turnier/types/participant.py +++ b/src/solo_turnier/types/participant.py @@ -1,17 +1,20 @@ import solo_turnier +from .person import Person -class Participant: + +class Participant(Person): def __init__( self, - firstName: str, - lastName: str, - club: str, - group: solo_turnier.group.Group, - class_: solo_turnier.competition_class.CompetitionClass, + name: str, + id: int, + finalist: bool = None, ): - self.firstName = firstName - self.lastName = lastName - self.club = club - self.group = group - self.class_ = class_ + super().__init__(name) + self.id = id + self.finalist = finalist + + def __repr__(self): + if self.finalist == True: + return f"Part({self.id} {self.name},F)" + return f"Part({self.id} {self.name})" diff --git a/src/solo_turnier/types/participantResult.py b/src/solo_turnier/types/participantResult.py deleted file mode 100644 index aeb684e..0000000 --- a/src/solo_turnier/types/participantResult.py +++ /dev/null @@ -1,23 +0,0 @@ -import solo_turnier - - -class ParticipantResult: - def __init__( - self, - id: int, - finalist: bool, - cancelled: bool, - group: solo_turnier.group.Group_t, - class_: solo_turnier.competition_class.Class_t, - dance: str, - place, - placeTo, - ): - self.id = id - self.finalist = finalist - self.cancelled = cancelled - self.group = group - self.class_ = class_ - self.dance = dance - self.place = place - self.placeTo = placeTo diff --git a/src/solo_turnier/types/person.py b/src/solo_turnier/types/person.py new file mode 100644 index 0000000..5c7b20e --- /dev/null +++ b/src/solo_turnier/types/person.py @@ -0,0 +1,3 @@ +class Person: + def __init__(self, name: str): + self.name = name diff --git a/src/solo_turnier/types/singleParticipantResult.py b/src/solo_turnier/types/singleParticipantResult.py index b280c83..bf548cf 100644 --- a/src/solo_turnier/types/singleParticipantResult.py +++ b/src/solo_turnier/types/singleParticipantResult.py @@ -1,5 +1,7 @@ import solo_turnier +from .place import Place + class SingleParticipantResult: def __init__( @@ -8,38 +10,17 @@ class SingleParticipantResult: nativeClass: solo_turnier.competition_class.CompetitionClass, dance: str, finalist: bool, - place: int, - placeTo: int | None, + place: Place, + nativePlace: Place = None, ): self.competitionClass = competitionClass self.nativeClass = nativeClass self.dance = dance self.finalist = finalist self.place = place - self.placeTo = placeTo - - if placeTo == place: - self.placeTo = None - - self.placeNative = None - self.placeNativeTo = None + self.nativePlace = nativePlace def __repr__(self): asFinalist = " as finalist" if self.finalist else "" - if self.placeTo is None: - return f"SR[{self.place} in {self.dance} {self.competitionClass} ({self.placeNative}-{self.placeNativeTo}, {self.nativeClass}){asFinalist}]" - - return f"SR[{self.place}-{self.placeTo} in {self.dance} {self.competitionClass} ({self.placeNative}-{self.placeNativeTo}, {self.nativeClass}){asFinalist}]" - - def getPlace(self): - if self.placeTo is None: - return f"{self.place}." - else: - return f"{self.place}.-{self.placeTo}." - - def getNativePlace(self): - if self.placeNativeTo is None: - return f"{self.placeNative}." - else: - return f"{self.placeNative}.-{self.placeNativeTo}." + return f"SR[{self.place} in {self.dance} {self.competitionClass} ({self.nativePlace} {self.nativeClass}){asFinalist}]" diff --git a/src/solo_turnier/types/stages.py b/src/solo_turnier/types/stages.py index d834050..301c11d 100644 --- a/src/solo_turnier/types/stages.py +++ b/src/solo_turnier/types/stages.py @@ -2,14 +2,11 @@ import solo_turnier from .totalGroupResult import TotalGroupResult from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults -from .participant import Participant -from .participantResult import ParticipantResult -from .outputTable import OutputTable class State4: def __init__( - self, resultPerGroup: dict[solo_turnier.group.Group, TotalGroupResult] + self, resultPerGroup: dict[solo_turnier.group.Group | None, TotalGroupResult] ): parser = solo_turnier.group.GroupParser() self.groups = parser.getGroupsAsSortedList(resultPerGroup.keys()) @@ -19,16 +16,3 @@ class State4: class State3: def __init__(self, htmlResults: HtmlCompetitionTotalResults): self.htmlResults = htmlResults - - -class Stage2: - def __init__(self, results: dict[Participant, list[ParticipantResult]]): - self.results = results - - -class Stage1: - def __init__( - self, - tables: dict[solo_turnier.group.Group, OutputTable], - ): - self.tables = tables diff --git a/src/solo_turnier/types/tableCompetitionEntry.py b/src/solo_turnier/types/tableCompetitionEntry.py deleted file mode 100644 index f7e037d..0000000 --- a/src/solo_turnier/types/tableCompetitionEntry.py +++ /dev/null @@ -1,44 +0,0 @@ -import solo_turnier - - -class TableCompetitionEntry: - def __init__( - self, - cancelled: bool, - finalist: bool, - class_: solo_turnier.competition_class.Class_t, - place: int = -1, - placeTo: int = -1, - group: solo_turnier.group.Group_t = None, - id: int = None, - ): - self.finalist = finalist - self.cancelled = cancelled - self.group = group - self.class_ = class_ - self.place = place - self.placeTo = placeTo - - def __repr__(self): - def paramMerging(l): - return ", ".join(filter(lambda x: x is not None, l)) - - if self.cancelled: - params = paramMerging([self.group, self.class_, self.id]) - if len(params) > 0: - return f"- ({params})" - else: - return "-" - elif not self.finalist: - params = paramMerging([self.group, self.class_, self.id]) - if len(params) > 0: - return f"x ({params})" - else: - return "x" - else: - if self.place == self.placeTo: - place = f"{self.place}." - else: - place = f"{self.place}.-{self.placeTo}." - params = paramMerging([self.group, self.class_, self.id]) - return f"{place} ({params})" diff --git a/src/solo_turnier/types/tableEntry.py b/src/solo_turnier/types/tableEntry.py deleted file mode 100644 index 1a9334f..0000000 --- a/src/solo_turnier/types/tableEntry.py +++ /dev/null @@ -1,9 +0,0 @@ -from .tableCompetitionEntry import TableCompetitionEntry - - -class TableEntry: - def __init__(self, competitions: list[TableCompetitionEntry]): - self.competitions = competitions - - def __repr__(self): - return ", ".join(self.competitions) diff --git a/src/solo_turnier/types/tableRow.py b/src/solo_turnier/types/tableRow.py deleted file mode 100644 index 427bf49..0000000 --- a/src/solo_turnier/types/tableRow.py +++ /dev/null @@ -1,16 +0,0 @@ -from .participant import Participant -from .tableEntry import TableEntry - - -class TableRow: - def __init__(self, participant: Participant, id: int, entries: list[TableEntry]): - self.participant = participant - self.id = id - self.entries = entries - - def getRowList(self): - if self.id is not None: - first = f"{self.id}. {self.participant.firstName} {self.participant.lastName} ({self.participant.club})" - else: - first = f"{self.participant.firstName} {self.participant.lastName} ({self.participant.club})" - return [first] + map(str, self.entries) diff --git a/src/solo_turnier/types/totalGroupResult.py b/src/solo_turnier/types/totalGroupResult.py index 58f811a..ac1e6c7 100644 --- a/src/solo_turnier/types/totalGroupResult.py +++ b/src/solo_turnier/types/totalGroupResult.py @@ -1,15 +1,15 @@ -from .htmlPreviewParticipant import HtmlPreviewParticipant from .singleParticipantResult import SingleParticipantResult +from .participant import Participant class TotalGroupResult: def __init__( self, dances: list[str], - results: dict[HtmlPreviewParticipant, list[SingleParticipantResult]], + results: dict[Participant, list[SingleParticipantResult]], ): self.dances = dances self.results = results def __repr__(self): - return f"TotalGroupResult({self.dances}, {self.results})" + return f"TotalGrR({self.dances}, {self.results})" diff --git a/src/solo_turnier/workers/ResultExtractor.py b/src/solo_turnier/workers/ResultExtractor.py index 54dc0e9..507794d 100644 --- a/src/solo_turnier/workers/ResultExtractor.py +++ b/src/solo_turnier/workers/ResultExtractor.py @@ -38,13 +38,7 @@ class ResultExtractor: ) continue - try: - guessedClass = classParser.parseClass(data["class_"]) - except: - self.l.error( - "Issue parsing class of file %s. Check manually.", filePair[0] - ) - continue + guessedClass = data["class_"] self.l.debug( "Fetched result data: %s, guessed class %s", data, guessedClass @@ -82,18 +76,20 @@ class ResultExtractor: for person in result.results.keys(): placeStr = result.results[person] - placeObj = self._extractPlace(placeStr) - place = placeObj.place - placeTo = placeObj.placeTo + place = self._extractPlace(placeStr) competitionResult = types.HtmlSingleCompetitionResult( - person.name, place, placeTo, person.finalist + person.name, place, person.finalist ) results.add( - competitionGroup, competitionClass, dance, person.id, competitionResult + competitionGroup, + competitionClass, + dance, + int(person.id), + competitionResult, ) # - def _analyzeIndividualResults( + def _analyzeResultFixups( self, parser: html_parser.HtmlParser, results: types.HtmlCompetitionTotalResults ): data = parser.guessDataFromHtmlTitle() @@ -101,9 +97,11 @@ class ResultExtractor: competitionGroup = data["group"] dance = data["dance"] - result = parser.parseIndividualResult(competitionGroup, competitionClass, dance) - self.l.log(5, "Found individual results: %s", result.participants) - results.tabges.update(result.participants) + resultFixups = parser.parseIndividualResult( + competitionGroup, competitionClass, dance + ) + self.l.log(5, "Found additional result fixups: %s", resultFixups.participants) + results.fixups.update(resultFixups.participants) def extractAllData( self, parsers: ParserList_t @@ -124,6 +122,6 @@ class ResultExtractor: "Fetching individual result of combined competitions in %s", fileName, ) - self._analyzeIndividualResults(parsers[fileNameTuple][1], ret) + self._analyzeResultFixups(parsers[fileNameTuple][1], ret) return ret diff --git a/src/solo_turnier/workers/Worker.py b/src/solo_turnier/workers/Worker.py index 11e8bfe..cb7b5e3 100644 --- a/src/solo_turnier/workers/Worker.py +++ b/src/solo_turnier/workers/Worker.py @@ -18,12 +18,16 @@ class Worker: "Quickstep", ] self._groupParser = solo_turnier.group.GroupParser() + self._classParser = solo_turnier.competition_class.CompetitionClassParser() def collectAllData(self, htmlResultsFileNames: list[str]) -> types.State3: resultExtractor = ResultExtractor() resultParsers = resultExtractor.getAllParsers(htmlResultsFileNames) htmlResults = resultExtractor.extractAllData(resultParsers) - self.l.debug("Overall result data extracted: %s", pformat(htmlResults.results)) + self.l.log(5, "Overall result data extracted: %s", pformat(htmlResults.results)) + self.l.log( + 5, "Overall result fixups extracted: %s", pformat(htmlResults.fixups) + ) return types.State3(htmlResults) @@ -34,15 +38,92 @@ class Worker: groupMapping = self._getGroupMapping(importedData) self.l.log(5, "ID-to-group mapping of the parsed data: %s", str(groupMapping)) - # groups = self._extractGroups(importedData) groups = self._extractGroupsFromGroupMapping(groupMapping) self.l.debug("Found groups in the dataset: %s", groups) invertedGroupMapping = self._invertGroupMapping(groupMapping, groups) self.l.log(5, "Inverted group maping: %s", invertedGroupMapping) + idToParticipantMapping = self._invertIdMapping(importedData.htmlResults) + self.l.log(5, "Id to participant mappting: %s", idToParticipantMapping) + totalResult = {} + + for group in invertedGroupMapping: + self.l.debug("Collecting data for group %s", group) + + participants = invertedGroupMapping[group] + self.l.log(5, "Participants in group: %s", participants) + + tuplesInCurrentGroup = [] + + for participantId in participants: + tuplesInCurrentGroup.extend( + self._filterResultKeys(importedData.htmlResults, id=participantId) + ) + self.l.log( + 5, + "Tuples of filtered in group %s: %s", + group, + list(tuplesInCurrentGroup), + ) + + dancesInGroup = self._extractAllDancesFromTuples(tuplesInCurrentGroup) + self.l.debug("Found dances in group %s: %s", group, dancesInGroup) + + resultsInCurrentGroup = {} + + for participantId in participants: + self.l.log(5, "Handling participant with ID %d", participantId) + # tuples = self._filterResultKeys(im) + participant = idToParticipantMapping[participantId] + self.l.log(5, "Participant in question: %s", participant) + + participant.finalist = False + + resultsInCurrentGroup[participant] = [] + + for tup in self._filterResultKeys( + importedData.htmlResults, id=participantId + ): + singleHtmlResultList = importedData.htmlResults.results[tup] + if len(singleHtmlResultList) > 1: + self.l.warning( + "More than one result per tuple (%s) found.", tup + ) + + singleHtmlResult = singleHtmlResultList[0] + singleResult = solo_turnier.types.SingleParticipantResult( + competitionClass=tup.class_, + nativeClass=tup.class_, + dance=tup.dance, + finalist=singleHtmlResult.finalist, + place=singleHtmlResult.place, + nativePlace=singleHtmlResult.place, + ) + + if tup in importedData.htmlResults.fixups: + fixup = importedData.htmlResults.fixups[tup] + self.l.log( + 5, "Fixture found for %s, %s: %s", participant, tup, fixup + ) + self._applyFixture(singleResult, fixup) + + resultsInCurrentGroup[participant].append(singleResult) + + if singleHtmlResult.finalist: + participant.finalist = True + + ########################################################################################################################### + + totalGroupResult = types.TotalGroupResult( + dancesInGroup, resultsInCurrentGroup + ) + self.l.log(5, "Total group result of group %s: %s", group, totalGroupResult) + totalResult[group] = totalGroupResult + ret = types.State4(totalResult) + return ret for group in groups: self.l.debug("Collecting data for total result of group %s", group) @@ -80,15 +161,9 @@ class Worker: def _extractGroups(self, data: types.State3): groupSet = set([]) - # for id in data.previewImport.participants: - # participants = data.previewImport.participants[id] - # for participant in participants: - # groupSet.add(participant.group) for tup in data.htmlResults.results.keys(): gr = self._groupParser.parseGroup(tup[0]) - # groupSet.add(gr) groupSet.update(gr.getContainedGroups()) - # self.l.log(5, 'Group type %s', type(gr)) self.l.log(5, "Set of active groups: %s", groupSet) groups = self._groupParser.getGroupsAsSortedList(groupSet) @@ -124,8 +199,9 @@ class Worker: if counts[candidates[0]] > counts[candidates[1]]: if candidates[0] is None: self.l.error( - "Majority of guessed groups is ambigous. Guessing failed for id %d. Falling back to second best guess.", + "Majority of guessed groups is ambiguous. Guessing failed for id %d. Falling back to second best guess %s.", id, + candidates[1], ) return candidates[1] @@ -137,20 +213,22 @@ class Worker: groupsPerId = {} for tup in importedData.htmlResults.results: - competitionGroup = self._groupParser.parseGroup(tup[0]) - fixture = importedData.htmlResults.tabges.get(tup, (None, None, None)) - id = int(tup[3]) - if fixture[2] is not None: - group = self._groupParser.parseGroup(fixture[2]) + competitionGroup = tup.group + fixture = importedData.htmlResults.fixups.get( + tup, solo_turnier.types.HtmlSingleCompetitionFixture(None, None, None) + ) + id = tup.id + if fixture.group is not None: + group = fixture.group else: containedGroups = competitionGroup.getContainedGroups() if len(containedGroups) > 1: self.l.error( "The group for participant %d is ambiguous in (%s %s %s).", id, - tup[0], - tup[1], - tup[2], + tup.group, + tup.class_, + tup.dance, ) group = containedGroups else: @@ -193,8 +271,95 @@ class Worker: ret[group] = [] for id in mapping: ret[mapping[id]].append(id) + for key in ret: + ret[key].sort() return ret + def _filterResultKeys( + self, + results: solo_turnier.types.HtmlCompetitionTotalResults, + group: solo_turnier.group.Group_t | None = None, + class_: solo_turnier.competition_class.Class_t | None = None, + dance: str | None = None, + id: int | None = None, + ): + def checker(x: solo_turnier.types.CompetitionTuple) -> bool: + if group is not None and group != x.group: + return False + if class_ is not None and class_ != x.class_: + return False + if dance is not None and dance != x.dance: + return False + if id is not None and id != x.id: + return False + return True + + return filter(checker, results.results.keys()) + + def _extractAllDancesFromTuples( + self, tuples: list[solo_turnier.types.CompetitionTuple] + ) -> list[str]: + danceSet = set() + danceSet.update(map(lambda x: x.dance, tuples)) + + # Check for unknown dances here + setDiff = danceSet.difference(self._allDances) + if len(setDiff) > 0: + self.l.warning( + "There are dances in the data set that are not known in the program. A bug?" + ) + return [x for x in self._allDances if x in danceSet] + list(setDiff) + + def _invertIdMapping( + self, htmlData: solo_turnier.types.HtmlCompetitionTotalResults + ): + mapping = {} + for tup in htmlData.results: + id = tup.id + results = htmlData.results[tup] + if len(results) > 1: + self.l.error( + "Non-unique results for tuple %s were found. Most probably this is a bug. The results are %s.", + tup, + results, + ) + elif len(results) == 0: + self.l.error("No results for tuple %s found.", tup) + continue + + if id not in mapping: + mapping[id] = solo_turnier.types.Participant( + name=results[0].name, id=id + ) + else: + if mapping[id].name != results[0].name or mapping[id].id != id: + self.l.error( + "Invalid id to participant mapping found. The name of id has changed. Tuple was %s (values %s), mapping was %s", + tup, + results, + mapping[id], + ) + return mapping + + def _filterResultsById( + self, data: solo_turnier.types.HtmlCompetitionTotalResults, ids: list[int] + ): + ret = {} + + return ret + + def _applyFixture( + self, + singleResult: solo_turnier.types.SingleParticipantResult, + fixture: solo_turnier.types.HtmlSingleCompetitionFixture, + ): + singleResult.nativePlace = fixture.place + + if fixture.class_ is not None: + singleResult.nativeClass = self._classParser.parseAbbreviatedClass( + fixture.class_ + ) + def _extractDancesPerGroup( self, data: types.State3, group: solo_turnier.group.Group ): @@ -365,34 +530,19 @@ class Worker: pass def filterOutFinalists(self, data: types.State4, filterOut: bool): - for group in data.results: - self.l.debug("Cleaning up group %s", group.name) - participants = data.results[group].results.keys() - droppedParticipants = [] + if filterOut: + for group in data.results: + groupName = "unknown" if group is None else group.name + self.l.debug("Cleaning up group %s", groupName) + participants = data.results[group].results.keys() + droppedParticipants = [] - for participant in participants: - self.l.debug("Checking %s", participant) + for participant in participants: + if participant.finalist == False: + self.l.info( + "Dropping %s from the output as no finalist", participant + ) + droppedParticipants.append(participant) - def isFinalistInDance(x: types.HtmlSingleCompetitionResult | None): - if x is None: - return False - return x.finalist - - mapped = list( - map(isFinalistInDance, data.results[group].results[participant]) - ) - finalist = True in mapped - self.l.log(5, "Check for finalist (in dances %s): %s", mapped, finalist) - - if finalist: - participant.finalist = True - else: - participant.finalist = False - self.l.info( - "Dropping %s from the output as no finalist", participant - ) - droppedParticipants.append(participant) - - if filterOut: for droppedParticipant in droppedParticipants: data.results[group].results.pop(droppedParticipant)