First running version
Not yet comparable with previous code base due to different outputs.
This commit is contained in:
parent
3ea5b74557
commit
43180c6e05
@ -26,6 +26,12 @@ class CombinedCompetitionClass:
|
|||||||
else:
|
else:
|
||||||
return f"{self.clsA}/{self.clsB}/{self.clsC}"
|
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
|
Class_t = CompetitionClass | CombinedCompetitionClass
|
||||||
|
|
||||||
|
@ -20,6 +20,12 @@ class CombinedGroup:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{self.clsA}/{self.clsB}"
|
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):
|
def getContainedGroups(self):
|
||||||
return (self.clsA, self.clsB)
|
return (self.clsA, self.clsB)
|
||||||
|
|
||||||
|
@ -3,10 +3,11 @@ from bs4 import BeautifulSoup
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .types import HtmlPreviewParticipant, HtmlParticipant, HtmlResultTotalTable
|
from .types import HtmlParticipant, HtmlResultTotalTable
|
||||||
from .types import HtmlPreviewImport as HtmlImport, HtmlResultImport
|
from .types import HtmlResultImport
|
||||||
from .group import GroupParser
|
from .group import GroupParser
|
||||||
from .competition_class import CompetitionClassParser
|
from .competition_class import CompetitionClassParser
|
||||||
|
import solo_turnier
|
||||||
|
|
||||||
|
|
||||||
class IncompleteRoundException(Exception):
|
class IncompleteRoundException(Exception):
|
||||||
@ -45,11 +46,11 @@ class HtmlParser:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"dance": dance.strip(),
|
"dance": dance.strip(),
|
||||||
"class_": str(self.classParser.parseClass(rawClass, True)),
|
"class_": self.classParser.parseClass(rawClass, True),
|
||||||
"group": str(self.groupParser.parseGroup(rawGroup)),
|
"group": self.groupParser.parseGroup(rawGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseResult(self):
|
def parseResult(self) -> HtmlResultImport:
|
||||||
participants = {}
|
participants = {}
|
||||||
|
|
||||||
def __parseRows(rows, finalist: bool):
|
def __parseRows(rows, finalist: bool):
|
||||||
@ -111,6 +112,8 @@ class HtmlParser:
|
|||||||
def parseIndividualResult(self, competitionGroup, competitionClass, dance):
|
def parseIndividualResult(self, competitionGroup, competitionClass, dance):
|
||||||
participants = {}
|
participants = {}
|
||||||
|
|
||||||
|
rePlaceParser = re.compile("([0-9]+)(?:-([0-9]+))?")
|
||||||
|
|
||||||
def __parseTable(table):
|
def __parseTable(table):
|
||||||
rows = table.find_all("tr")
|
rows = table.find_all("tr")
|
||||||
|
|
||||||
@ -148,7 +151,20 @@ class HtmlParser:
|
|||||||
rawStr = tag.contents[0].strip()
|
rawStr = tag.contents[0].strip()
|
||||||
if rawStr.endswith("-"):
|
if rawStr.endswith("-"):
|
||||||
rawStr = rawStr[:-1]
|
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))
|
places = list(map(getSinglePlaceStr, placeTags))
|
||||||
return places
|
return places
|
||||||
@ -180,8 +196,13 @@ class HtmlParser:
|
|||||||
cls = classes[idx] if classes is not None else None
|
cls = classes[idx] if classes is not None else None
|
||||||
grp = groups[idx] if groups is not None else None
|
grp = groups[idx] if groups is not None else None
|
||||||
|
|
||||||
tup = (competitionGroup, competitionClass, dance, id)
|
tup = solo_turnier.types.CompetitionTuple(
|
||||||
participants[tup] = (places[idx], cls, grp)
|
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")
|
tables = self.soup.find("div", class_="extract").find_all("table")
|
||||||
for table in tables:
|
for table in tables:
|
||||||
|
@ -87,8 +87,8 @@ class ConsoleOutputter(AbstractOutputter):
|
|||||||
if result is None:
|
if result is None:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
placeNative = getPlace(result.placeNative, result.placeNativeTo)
|
placeNative = str(result.nativePlace)
|
||||||
place = getPlace(result.place, result.placeTo)
|
place = str(result.place)
|
||||||
lineOne = f"{placeNative} ({result.nativeClass})"
|
lineOne = f"{placeNative} ({result.nativeClass})"
|
||||||
lineTwo = f"[{place} in {result.competitionClass}]"
|
lineTwo = f"[{place} in {result.competitionClass}]"
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ class ConsoleOutputter(AbstractOutputter):
|
|||||||
print(tabulate(tableData, headers="firstrow", tablefmt="fancy_grid"))
|
print(tabulate(tableData, headers="firstrow", tablefmt="fancy_grid"))
|
||||||
|
|
||||||
def output(self, data: types.State4):
|
def output(self, data: types.State4):
|
||||||
for idx, group in enumerate(data.groups):
|
for idx, group in enumerate(data.results):
|
||||||
if idx > 0:
|
if idx > 0:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
from .place import Place
|
from .place import Place
|
||||||
|
|
||||||
|
from .person import Person
|
||||||
from .htmlPreviewParticipant import HtmlPreviewParticipant
|
from .htmlPreviewParticipant import HtmlPreviewParticipant
|
||||||
from .htmlParticipant import HtmlParticipant
|
from .htmlParticipant import HtmlParticipant
|
||||||
from .htmlPreviewImport import HtmlPreviewImport
|
|
||||||
from .htmlResultImport import HtmlResultImport
|
from .htmlResultImport import HtmlResultImport
|
||||||
from .htmlResultTotalTable import HtmlResultTotalTable
|
from .htmlResultTotalTable import HtmlResultTotalTable
|
||||||
from .htmlCompetitionResultRow import HtmlCompetitionResultRow
|
from .htmlCompetitionResultRow import HtmlCompetitionResultRow
|
||||||
|
from .competitionTuple import CompetitionTuple
|
||||||
from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult
|
from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult
|
||||||
|
from .htmlSingleCompetitionFixture import HtmlSingleCompetitionFixture
|
||||||
from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults
|
from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults
|
||||||
from .singleParticipantResult import SingleParticipantResult
|
from .singleParticipantResult import SingleParticipantResult
|
||||||
from .totalGroupResult import TotalGroupResult
|
from .totalGroupResult import TotalGroupResult
|
||||||
from .participant import Participant
|
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 *
|
from .stages import *
|
||||||
|
27
src/solo_turnier/types/competitionTuple.py
Normal file
27
src/solo_turnier/types/competitionTuple.py
Normal file
@ -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__()
|
@ -1,11 +1,12 @@
|
|||||||
import solo_turnier
|
import solo_turnier
|
||||||
from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult
|
from .htmlSingleCompetitionResult import HtmlSingleCompetitionResult
|
||||||
|
from .competitionTuple import CompetitionTuple
|
||||||
|
|
||||||
|
|
||||||
class HtmlCompetitionTotalResults:
|
class HtmlCompetitionTotalResults:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.results = {}
|
self.results = {}
|
||||||
self.tabges = {}
|
self.fixups = {}
|
||||||
|
|
||||||
def __getTuple(
|
def __getTuple(
|
||||||
self,
|
self,
|
||||||
@ -14,7 +15,7 @@ class HtmlCompetitionTotalResults:
|
|||||||
dance: str,
|
dance: str,
|
||||||
id: int,
|
id: int,
|
||||||
):
|
):
|
||||||
return (group, class_, dance, id)
|
return CompetitionTuple(group, class_, dance, id)
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
@ -34,16 +35,16 @@ class HtmlCompetitionTotalResults:
|
|||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
for k in self.results:
|
for k in self.results:
|
||||||
if int(k[3]) != id:
|
if int(k.id) != id:
|
||||||
continue
|
continue
|
||||||
# ret = ret + self.results[k]
|
# ret = ret + self.results[k]
|
||||||
# Dance, Group, Class
|
# Dance, Group, Class
|
||||||
key = (k[2], k[0], k[1])
|
key = (k.dance, k.group, k.class_)
|
||||||
ret[key] = self.results[k]
|
ret[key] = self.results[k]
|
||||||
|
|
||||||
return ret
|
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)
|
tup = self.__getTuple(group, class_, dance, id)
|
||||||
l = self.results.get(tup, [])
|
l = self.results.get(tup, [])
|
||||||
l.append(result)
|
l.append(result)
|
||||||
|
@ -5,7 +5,7 @@ class HtmlParticipant:
|
|||||||
self.finalist = None
|
self.finalist = None
|
||||||
|
|
||||||
def __eq__(self, o):
|
def __eq__(self, o):
|
||||||
if type(o) != HtmlPreviewParticipant:
|
if type(o) != HtmlParticipant:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return all(
|
return all(
|
||||||
|
@ -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))
|
|
17
src/solo_turnier/types/htmlSingleCompetitionFixture.py
Normal file
17
src/solo_turnier/types/htmlSingleCompetitionFixture.py
Normal file
@ -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_})"
|
@ -1,15 +1,14 @@
|
|||||||
|
from .place import Place
|
||||||
|
|
||||||
|
|
||||||
class HtmlSingleCompetitionResult:
|
class HtmlSingleCompetitionResult:
|
||||||
def __init__(self, name, place, placeTo, finalist):
|
def __init__(self, name: str, place: Place, finalist: bool):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.place = place
|
self.place = place
|
||||||
self.placeTo = placeTo
|
|
||||||
self.finalist = finalist
|
self.finalist = finalist
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.placeTo is None:
|
|
||||||
place = self.place
|
place = self.place
|
||||||
else:
|
|
||||||
place = f"{self.place}-{self.placeTo}"
|
|
||||||
|
|
||||||
if self.finalist:
|
if self.finalist:
|
||||||
return f"Res({self.name} [F], placed {place})"
|
return f"Res({self.name} [F], placed {place})"
|
||||||
|
@ -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
|
|
@ -1,17 +1,20 @@
|
|||||||
import solo_turnier
|
import solo_turnier
|
||||||
|
|
||||||
|
from .person import Person
|
||||||
|
|
||||||
class Participant:
|
|
||||||
|
class Participant(Person):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
firstName: str,
|
name: str,
|
||||||
lastName: str,
|
id: int,
|
||||||
club: str,
|
finalist: bool = None,
|
||||||
group: solo_turnier.group.Group,
|
|
||||||
class_: solo_turnier.competition_class.CompetitionClass,
|
|
||||||
):
|
):
|
||||||
self.firstName = firstName
|
super().__init__(name)
|
||||||
self.lastName = lastName
|
self.id = id
|
||||||
self.club = club
|
self.finalist = finalist
|
||||||
self.group = group
|
|
||||||
self.class_ = class_
|
def __repr__(self):
|
||||||
|
if self.finalist == True:
|
||||||
|
return f"Part({self.id} {self.name},F)"
|
||||||
|
return f"Part({self.id} {self.name})"
|
||||||
|
@ -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
|
|
3
src/solo_turnier/types/person.py
Normal file
3
src/solo_turnier/types/person.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class Person:
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.name = name
|
@ -1,5 +1,7 @@
|
|||||||
import solo_turnier
|
import solo_turnier
|
||||||
|
|
||||||
|
from .place import Place
|
||||||
|
|
||||||
|
|
||||||
class SingleParticipantResult:
|
class SingleParticipantResult:
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -8,38 +10,17 @@ class SingleParticipantResult:
|
|||||||
nativeClass: solo_turnier.competition_class.CompetitionClass,
|
nativeClass: solo_turnier.competition_class.CompetitionClass,
|
||||||
dance: str,
|
dance: str,
|
||||||
finalist: bool,
|
finalist: bool,
|
||||||
place: int,
|
place: Place,
|
||||||
placeTo: int | None,
|
nativePlace: Place = None,
|
||||||
):
|
):
|
||||||
self.competitionClass = competitionClass
|
self.competitionClass = competitionClass
|
||||||
self.nativeClass = nativeClass
|
self.nativeClass = nativeClass
|
||||||
self.dance = dance
|
self.dance = dance
|
||||||
self.finalist = finalist
|
self.finalist = finalist
|
||||||
self.place = place
|
self.place = place
|
||||||
self.placeTo = placeTo
|
self.nativePlace = nativePlace
|
||||||
|
|
||||||
if placeTo == place:
|
|
||||||
self.placeTo = None
|
|
||||||
|
|
||||||
self.placeNative = None
|
|
||||||
self.placeNativeTo = None
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
asFinalist = " as finalist" if self.finalist else ""
|
asFinalist = " as finalist" if self.finalist else ""
|
||||||
|
|
||||||
if self.placeTo is None:
|
return f"SR[{self.place} in {self.dance} {self.competitionClass} ({self.nativePlace} {self.nativeClass}){asFinalist}]"
|
||||||
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}."
|
|
||||||
|
@ -2,14 +2,11 @@ import solo_turnier
|
|||||||
|
|
||||||
from .totalGroupResult import TotalGroupResult
|
from .totalGroupResult import TotalGroupResult
|
||||||
from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults
|
from .htmlCompetitionTotalResults import HtmlCompetitionTotalResults
|
||||||
from .participant import Participant
|
|
||||||
from .participantResult import ParticipantResult
|
|
||||||
from .outputTable import OutputTable
|
|
||||||
|
|
||||||
|
|
||||||
class State4:
|
class State4:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, resultPerGroup: dict[solo_turnier.group.Group, TotalGroupResult]
|
self, resultPerGroup: dict[solo_turnier.group.Group | None, TotalGroupResult]
|
||||||
):
|
):
|
||||||
parser = solo_turnier.group.GroupParser()
|
parser = solo_turnier.group.GroupParser()
|
||||||
self.groups = parser.getGroupsAsSortedList(resultPerGroup.keys())
|
self.groups = parser.getGroupsAsSortedList(resultPerGroup.keys())
|
||||||
@ -19,16 +16,3 @@ class State4:
|
|||||||
class State3:
|
class State3:
|
||||||
def __init__(self, htmlResults: HtmlCompetitionTotalResults):
|
def __init__(self, htmlResults: HtmlCompetitionTotalResults):
|
||||||
self.htmlResults = htmlResults
|
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
|
|
||||||
|
@ -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})"
|
|
@ -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)
|
|
@ -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)
|
|
@ -1,15 +1,15 @@
|
|||||||
from .htmlPreviewParticipant import HtmlPreviewParticipant
|
|
||||||
from .singleParticipantResult import SingleParticipantResult
|
from .singleParticipantResult import SingleParticipantResult
|
||||||
|
from .participant import Participant
|
||||||
|
|
||||||
|
|
||||||
class TotalGroupResult:
|
class TotalGroupResult:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
dances: list[str],
|
dances: list[str],
|
||||||
results: dict[HtmlPreviewParticipant, list[SingleParticipantResult]],
|
results: dict[Participant, list[SingleParticipantResult]],
|
||||||
):
|
):
|
||||||
self.dances = dances
|
self.dances = dances
|
||||||
self.results = results
|
self.results = results
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"TotalGroupResult({self.dances}, {self.results})"
|
return f"TotalGrR({self.dances}, {self.results})"
|
||||||
|
@ -38,13 +38,7 @@ class ResultExtractor:
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
guessedClass = data["class_"]
|
||||||
guessedClass = classParser.parseClass(data["class_"])
|
|
||||||
except:
|
|
||||||
self.l.error(
|
|
||||||
"Issue parsing class of file %s. Check manually.", filePair[0]
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.l.debug(
|
self.l.debug(
|
||||||
"Fetched result data: %s, guessed class %s", data, guessedClass
|
"Fetched result data: %s, guessed class %s", data, guessedClass
|
||||||
@ -82,18 +76,20 @@ class ResultExtractor:
|
|||||||
|
|
||||||
for person in result.results.keys():
|
for person in result.results.keys():
|
||||||
placeStr = result.results[person]
|
placeStr = result.results[person]
|
||||||
placeObj = self._extractPlace(placeStr)
|
place = self._extractPlace(placeStr)
|
||||||
place = placeObj.place
|
|
||||||
placeTo = placeObj.placeTo
|
|
||||||
competitionResult = types.HtmlSingleCompetitionResult(
|
competitionResult = types.HtmlSingleCompetitionResult(
|
||||||
person.name, place, placeTo, person.finalist
|
person.name, place, person.finalist
|
||||||
)
|
)
|
||||||
results.add(
|
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
|
self, parser: html_parser.HtmlParser, results: types.HtmlCompetitionTotalResults
|
||||||
):
|
):
|
||||||
data = parser.guessDataFromHtmlTitle()
|
data = parser.guessDataFromHtmlTitle()
|
||||||
@ -101,9 +97,11 @@ class ResultExtractor:
|
|||||||
competitionGroup = data["group"]
|
competitionGroup = data["group"]
|
||||||
dance = data["dance"]
|
dance = data["dance"]
|
||||||
|
|
||||||
result = parser.parseIndividualResult(competitionGroup, competitionClass, dance)
|
resultFixups = parser.parseIndividualResult(
|
||||||
self.l.log(5, "Found individual results: %s", result.participants)
|
competitionGroup, competitionClass, dance
|
||||||
results.tabges.update(result.participants)
|
)
|
||||||
|
self.l.log(5, "Found additional result fixups: %s", resultFixups.participants)
|
||||||
|
results.fixups.update(resultFixups.participants)
|
||||||
|
|
||||||
def extractAllData(
|
def extractAllData(
|
||||||
self, parsers: ParserList_t
|
self, parsers: ParserList_t
|
||||||
@ -124,6 +122,6 @@ class ResultExtractor:
|
|||||||
"Fetching individual result of combined competitions in %s",
|
"Fetching individual result of combined competitions in %s",
|
||||||
fileName,
|
fileName,
|
||||||
)
|
)
|
||||||
self._analyzeIndividualResults(parsers[fileNameTuple][1], ret)
|
self._analyzeResultFixups(parsers[fileNameTuple][1], ret)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -18,12 +18,16 @@ class Worker:
|
|||||||
"Quickstep",
|
"Quickstep",
|
||||||
]
|
]
|
||||||
self._groupParser = solo_turnier.group.GroupParser()
|
self._groupParser = solo_turnier.group.GroupParser()
|
||||||
|
self._classParser = solo_turnier.competition_class.CompetitionClassParser()
|
||||||
|
|
||||||
def collectAllData(self, htmlResultsFileNames: list[str]) -> types.State3:
|
def collectAllData(self, htmlResultsFileNames: list[str]) -> types.State3:
|
||||||
resultExtractor = ResultExtractor()
|
resultExtractor = ResultExtractor()
|
||||||
resultParsers = resultExtractor.getAllParsers(htmlResultsFileNames)
|
resultParsers = resultExtractor.getAllParsers(htmlResultsFileNames)
|
||||||
htmlResults = resultExtractor.extractAllData(resultParsers)
|
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)
|
return types.State3(htmlResults)
|
||||||
|
|
||||||
@ -34,15 +38,92 @@ class Worker:
|
|||||||
groupMapping = self._getGroupMapping(importedData)
|
groupMapping = self._getGroupMapping(importedData)
|
||||||
self.l.log(5, "ID-to-group mapping of the parsed data: %s", str(groupMapping))
|
self.l.log(5, "ID-to-group mapping of the parsed data: %s", str(groupMapping))
|
||||||
|
|
||||||
# groups = self._extractGroups(importedData)
|
|
||||||
groups = self._extractGroupsFromGroupMapping(groupMapping)
|
groups = self._extractGroupsFromGroupMapping(groupMapping)
|
||||||
self.l.debug("Found groups in the dataset: %s", groups)
|
self.l.debug("Found groups in the dataset: %s", groups)
|
||||||
|
|
||||||
invertedGroupMapping = self._invertGroupMapping(groupMapping, groups)
|
invertedGroupMapping = self._invertGroupMapping(groupMapping, groups)
|
||||||
self.l.log(5, "Inverted group maping: %s", invertedGroupMapping)
|
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 = {}
|
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)
|
ret = types.State4(totalResult)
|
||||||
|
return ret
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
self.l.debug("Collecting data for total result of group %s", group)
|
self.l.debug("Collecting data for total result of group %s", group)
|
||||||
@ -80,15 +161,9 @@ class Worker:
|
|||||||
|
|
||||||
def _extractGroups(self, data: types.State3):
|
def _extractGroups(self, data: types.State3):
|
||||||
groupSet = set([])
|
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():
|
for tup in data.htmlResults.results.keys():
|
||||||
gr = self._groupParser.parseGroup(tup[0])
|
gr = self._groupParser.parseGroup(tup[0])
|
||||||
# groupSet.add(gr)
|
|
||||||
groupSet.update(gr.getContainedGroups())
|
groupSet.update(gr.getContainedGroups())
|
||||||
# self.l.log(5, 'Group type %s', type(gr))
|
|
||||||
|
|
||||||
self.l.log(5, "Set of active groups: %s", groupSet)
|
self.l.log(5, "Set of active groups: %s", groupSet)
|
||||||
groups = self._groupParser.getGroupsAsSortedList(groupSet)
|
groups = self._groupParser.getGroupsAsSortedList(groupSet)
|
||||||
@ -124,8 +199,9 @@ class Worker:
|
|||||||
if counts[candidates[0]] > counts[candidates[1]]:
|
if counts[candidates[0]] > counts[candidates[1]]:
|
||||||
if candidates[0] is None:
|
if candidates[0] is None:
|
||||||
self.l.error(
|
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,
|
id,
|
||||||
|
candidates[1],
|
||||||
)
|
)
|
||||||
return candidates[1]
|
return candidates[1]
|
||||||
|
|
||||||
@ -137,20 +213,22 @@ class Worker:
|
|||||||
|
|
||||||
groupsPerId = {}
|
groupsPerId = {}
|
||||||
for tup in importedData.htmlResults.results:
|
for tup in importedData.htmlResults.results:
|
||||||
competitionGroup = self._groupParser.parseGroup(tup[0])
|
competitionGroup = tup.group
|
||||||
fixture = importedData.htmlResults.tabges.get(tup, (None, None, None))
|
fixture = importedData.htmlResults.fixups.get(
|
||||||
id = int(tup[3])
|
tup, solo_turnier.types.HtmlSingleCompetitionFixture(None, None, None)
|
||||||
if fixture[2] is not None:
|
)
|
||||||
group = self._groupParser.parseGroup(fixture[2])
|
id = tup.id
|
||||||
|
if fixture.group is not None:
|
||||||
|
group = fixture.group
|
||||||
else:
|
else:
|
||||||
containedGroups = competitionGroup.getContainedGroups()
|
containedGroups = competitionGroup.getContainedGroups()
|
||||||
if len(containedGroups) > 1:
|
if len(containedGroups) > 1:
|
||||||
self.l.error(
|
self.l.error(
|
||||||
"The group for participant %d is ambiguous in (%s %s %s).",
|
"The group for participant %d is ambiguous in (%s %s %s).",
|
||||||
id,
|
id,
|
||||||
tup[0],
|
tup.group,
|
||||||
tup[1],
|
tup.class_,
|
||||||
tup[2],
|
tup.dance,
|
||||||
)
|
)
|
||||||
group = containedGroups
|
group = containedGroups
|
||||||
else:
|
else:
|
||||||
@ -193,8 +271,95 @@ class Worker:
|
|||||||
ret[group] = []
|
ret[group] = []
|
||||||
for id in mapping:
|
for id in mapping:
|
||||||
ret[mapping[id]].append(id)
|
ret[mapping[id]].append(id)
|
||||||
|
for key in ret:
|
||||||
|
ret[key].sort()
|
||||||
return ret
|
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(
|
def _extractDancesPerGroup(
|
||||||
self, data: types.State3, group: solo_turnier.group.Group
|
self, data: types.State3, group: solo_turnier.group.Group
|
||||||
):
|
):
|
||||||
@ -365,34 +530,19 @@ class Worker:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def filterOutFinalists(self, data: types.State4, filterOut: bool):
|
def filterOutFinalists(self, data: types.State4, filterOut: bool):
|
||||||
|
if filterOut:
|
||||||
for group in data.results:
|
for group in data.results:
|
||||||
self.l.debug("Cleaning up group %s", group.name)
|
groupName = "unknown" if group is None else group.name
|
||||||
|
self.l.debug("Cleaning up group %s", groupName)
|
||||||
participants = data.results[group].results.keys()
|
participants = data.results[group].results.keys()
|
||||||
droppedParticipants = []
|
droppedParticipants = []
|
||||||
|
|
||||||
for participant in participants:
|
for participant in participants:
|
||||||
self.l.debug("Checking %s", participant)
|
if participant.finalist == False:
|
||||||
|
|
||||||
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(
|
self.l.info(
|
||||||
"Dropping %s from the output as no finalist", participant
|
"Dropping %s from the output as no finalist", participant
|
||||||
)
|
)
|
||||||
droppedParticipants.append(participant)
|
droppedParticipants.append(participant)
|
||||||
|
|
||||||
if filterOut:
|
|
||||||
for droppedParticipant in droppedParticipants:
|
for droppedParticipant in droppedParticipants:
|
||||||
data.results[group].results.pop(droppedParticipant)
|
data.results[group].results.pop(droppedParticipant)
|
||||||
|
Loading…
Reference in New Issue
Block a user