360 lines
15 KiB
Python
360 lines
15 KiB
Python
![]() |
from pyiofload import *
|
||
|
from merge_races import do_merge_races
|
||
|
from trophyPDF import TrophyPdf
|
||
|
import os
|
||
|
|
||
|
# http://www.fiso.emr.it/wp0/wp-content/uploads/2022/06/TrofeoEMR_parz_2022_20220615.pdf
|
||
|
# https://www.fiso.emr.it/wp0/trofeoemr_parz_2022_20220615/
|
||
|
|
||
|
SPRINT_RACES = {
|
||
|
2025122, # Parma
|
||
|
2025124, # Bologna
|
||
|
}
|
||
|
|
||
|
DOUBLE_POINTS = {2024999, }
|
||
|
|
||
|
NSCARTI = 0
|
||
|
|
||
|
yeartodo = '2025'
|
||
|
|
||
|
outfile = f'TrofeoEMR_{yeartodo}_parz.pdf'
|
||
|
# source_dir = 'E:/O/Trofeo_GPeroni_' + yeartodo
|
||
|
source_dir = 'xmls'
|
||
|
source_ext = '.xml'
|
||
|
class_renames = {'W18': 'W15-18',
|
||
|
'M18': 'M15-18',
|
||
|
# 'M 13/14': 'M14',
|
||
|
# 'W 13/14': 'W14',
|
||
|
# 'M 17/18': 'M15-18',
|
||
|
# 'W 17/18': 'W15-18'
|
||
|
}
|
||
|
|
||
|
to_be_merged = ( # (nome, file1, file2)
|
||
|
('ParmaColorno', 'ParmaColorno1_2025122.xml', 'ParmaColorno2_2025123.xml', 22025122),
|
||
|
# ('Bologna', 'Bologna1_2024151.xml', 'Bologna2_2024152.xml', 2024151),
|
||
|
# ('Piacenza', 'Piacenza1_2024157.xml', 'Piacenza2_2024163.xml', 2024157),
|
||
|
# ('Piacenza', 'Piacenza1_2023224.xml', 'Piacenza2_2023225.xml', 2023224),
|
||
|
# ('Cesena', 'Cesena1_2023232.xml', 'Cesena2_2023233.xml', 2023232),
|
||
|
)
|
||
|
|
||
|
EMR_Clubs = ('0098', '0206', '0221', '0255', '0275', '0610', '0738', '0746', '0761', '0769', '0793', '0794',
|
||
|
'0821', '0840')
|
||
|
|
||
|
|
||
|
class TrophyRunner:
|
||
|
"""A runner ranking in the Championship."""
|
||
|
|
||
|
def __init__(self, id_=None, clubid=None, club=None, classid=None, fullname=None, original_id=None,
|
||
|
name=None, family=None, championship=None):
|
||
|
self.id_ = id_
|
||
|
self.original_id = original_id
|
||
|
self.fullname = fullname
|
||
|
self.name = name
|
||
|
self.family = family
|
||
|
self.clubid = clubid
|
||
|
self.club = club
|
||
|
self.class_ = classid
|
||
|
self.total_score = 0.0
|
||
|
self.net_score = 0.0
|
||
|
self.races = {} # A dictionry in the form of {race_id: score}
|
||
|
self.discarded = [] # A list of the discarded races
|
||
|
self.championship = championship
|
||
|
self.rank = None
|
||
|
|
||
|
# def __str__(self):
|
||
|
# return f'{self.fullname} {self.discarded=}'
|
||
|
|
||
|
def get_a_race_score(self, race_id):
|
||
|
return self.races.get(race_id, 0.0)
|
||
|
# if race_id in self.races:
|
||
|
# return self.races[race_id]
|
||
|
# else:
|
||
|
# return 0.0
|
||
|
|
||
|
def add_a_race(self, raceid=None, score=0.0):
|
||
|
if raceid is not None:
|
||
|
self.races[raceid] = score
|
||
|
self.total_score += score
|
||
|
|
||
|
def compute_scarti(self, trophy, scarti=0, sprint_to_discard=NSCARTI):
|
||
|
self.net_score = self.total_score
|
||
|
self.discarded = [] # Clear the list of the discarded races
|
||
|
sprint_races = [race for race in trophy.races if trophy.is_sprint(race)]
|
||
|
sprint_sorted_list = sorted(sprint_races, key=self.get_a_race_score)
|
||
|
self.discarded = sprint_sorted_list[:sprint_to_discard] # Le sprint da scartare di sicuro
|
||
|
remaining_races = [race_id for race_id in trophy.races if race_id not in self.discarded]
|
||
|
to_discard = scarti - len(self.discarded)
|
||
|
# if self.name == 'Olmo':
|
||
|
# print(f'------------- {self.fullname=} {scarti=}')
|
||
|
# print(f'------------- {sprint_races=}')
|
||
|
# print(f'------------- {sprint_sorted_list=}')
|
||
|
# print(f'------------- {self.discarded=}')
|
||
|
# print(f'------------- {remaining_races=}')
|
||
|
# print(f'------------- {to_discard=}')
|
||
|
if to_discard >= 1:
|
||
|
# do create a list of races, sorted by their scores
|
||
|
sl = sorted(remaining_races, key=self.get_a_race_score)
|
||
|
self.discarded.extend(sl[0:to_discard])
|
||
|
# Now do adj the net score
|
||
|
for dis in self.discarded:
|
||
|
self.net_score -= self.get_a_race_score(dis)
|
||
|
|
||
|
def __str__(self):
|
||
|
rstr = f'{self.id_:13s} {self.fullname:25s} {self.class_:3.3s} rank:{self.rank:2} pti: {self.net_score:6.2f} ({self.total_score:6.2f})'
|
||
|
for race_id in self.races:
|
||
|
race_name = self.championship.races[race_id].name
|
||
|
rstr += ' [{}{}{:6.2f}]'.format(race_name, ' ' if race_id not in self.discarded else '-', self.races[race_id])
|
||
|
return rstr
|
||
|
|
||
|
def to_str_list(self, races):
|
||
|
clubname = self.club.replace('A.S.D.', '').replace('POLISPORTIVA', 'Pol').replace(
|
||
|
'ISTITUTO COMPRENSIVO', 'IC').replace('ATLETICA', 'Atl.').replace(
|
||
|
'ORIENTEERING', 'Or').replace('Orienteering', 'Or')
|
||
|
rt = [self.fullname,
|
||
|
'{:4s} {:17.17s}'.format(self.clubid, clubname.replace('A.S.D.', '').strip()),
|
||
|
'{:7.2f}'.format(self.net_score),
|
||
|
'{:7.2f}'.format(self.total_score)
|
||
|
]
|
||
|
for race in races:
|
||
|
if race in self.races:
|
||
|
if race in self.discarded:
|
||
|
rt.append('_{:6.2f}_'.format(self.races[race]))
|
||
|
else:
|
||
|
rt.append('{:6.2f}'.format(self.races[race]))
|
||
|
else:
|
||
|
rt.append(' ')
|
||
|
return rt
|
||
|
|
||
|
|
||
|
class TrophyClass:
|
||
|
"""A Class that is part of a Championship"""
|
||
|
|
||
|
def __init__(self, id_=None):
|
||
|
self.id_ = id_
|
||
|
self.runners = [] # A list of the runner'id(s) belonging to this class.
|
||
|
|
||
|
|
||
|
class Championship:
|
||
|
"""A class representing a Championship composed by many races"""
|
||
|
|
||
|
def __init__(self, filename_=None, classes=None):
|
||
|
self.races = {} # Key = race_id, Values = the Race itself.
|
||
|
self.classes = {} # Key = classes_id, Values = the TrophyClass itself.
|
||
|
self.runners = {} # Key = runner_id, Values = the TrophyRunner itself.
|
||
|
self.clubs = {} # Key = club_id, Values = the Club itself.
|
||
|
self.trophy_classes = classes # A list/tuple of the classes involved in the Championship.
|
||
|
if filename_ is not None:
|
||
|
self.add_a_race(filename_or_race=filename_)
|
||
|
|
||
|
def is_sprint(self, raceid) -> bool:
|
||
|
fiso_code = self.races[raceid].fiso_code
|
||
|
sprint = fiso_code in SPRINT_RACES
|
||
|
# print(f'IS SPRINT {sprint} {raceid} {fiso_code}')
|
||
|
return sprint
|
||
|
|
||
|
def add_a_race(self, filename_or_race=None, name=None, class_remap=None, clubs_ko=None, k_factor=1.0, fiso_code=0):
|
||
|
"""It adds a Race starting from an iof xml file"""
|
||
|
if filename_or_race is None:
|
||
|
return
|
||
|
# print(filename_or_race, type(filename_or_race))
|
||
|
# print('#' * 80)
|
||
|
if type(filename_or_race) is str:
|
||
|
race = Race(fiso_code=int(fiso_code))
|
||
|
race.load_from_iofxml(filename_=filename_or_race,
|
||
|
class_remap=class_remap, clubs_ko=clubs_ko, avoid_unk=True)
|
||
|
else:
|
||
|
race = filename_or_race[0]
|
||
|
if name is not None:
|
||
|
race.name = name
|
||
|
raceid = 'Race{:02d}'.format(1 + len(self.races))
|
||
|
print('{} {} ({}) {}'.format(raceid, race.name, filename_or_race, race.date))
|
||
|
strclasses = ''
|
||
|
for cl in sorted(race.classes.keys()):
|
||
|
strclasses += ' {}'.format(cl)
|
||
|
print(strclasses)
|
||
|
race.adj_scores(k_factor=k_factor)
|
||
|
self.races[raceid] = race # Beware of duplicates!!! @TODO
|
||
|
print('Race_id:', raceid)
|
||
|
# print('#' * 80)
|
||
|
# if 'Race07' == raceid:
|
||
|
# for rr, r in race.runners.items():
|
||
|
# print(rr, r)
|
||
|
|
||
|
for cl_ in race.classes:
|
||
|
if cl_ in self.trophy_classes and (cl_ not in self.classes):
|
||
|
new_trophy_class = TrophyClass(id_=cl_)
|
||
|
self.classes[cl_] = new_trophy_class
|
||
|
|
||
|
for rn in race.runners:
|
||
|
dbg = False
|
||
|
# if rn == 'TO1637':
|
||
|
# dbg = True
|
||
|
# else:
|
||
|
# dbg = False
|
||
|
# Do keep up to date the organization dictionary
|
||
|
orgid = race.runners[rn].clubid
|
||
|
if orgid not in self.clubs:
|
||
|
self.clubs[orgid] = race.clubs[orgid]
|
||
|
|
||
|
runner_class = race.runners[rn].class_
|
||
|
runner_clubid = race.runners[rn].clubid
|
||
|
trophy_runner_id = '{}-{}'.format(rn, runner_class)
|
||
|
# if runner_class in self.trophy_classes and runner_clubid in EMR_Clubs:
|
||
|
if runner_class in self.trophy_classes:
|
||
|
# Add her/him if is part of a trophy class only
|
||
|
|
||
|
if trophy_runner_id not in self.runners: # First time seen! Do add her/him!
|
||
|
# print('============== ', race.runners[rn].name)
|
||
|
new_trophy_runner = TrophyRunner(id_=trophy_runner_id, original_id=rn,
|
||
|
fullname=race.runners[rn].full_name(),
|
||
|
clubid=race.runners[rn].clubid,
|
||
|
club=race.runners[rn].club,
|
||
|
classid=runner_class,
|
||
|
name=race.runners[rn].name,
|
||
|
family=race.runners[rn].family,
|
||
|
championship=self
|
||
|
)
|
||
|
self.runners[trophy_runner_id] = new_trophy_runner
|
||
|
else: # The runner is already present in the TrophyRunner database
|
||
|
if self.runners[trophy_runner_id].class_ != runner_class:
|
||
|
print('Runner Class mismatch!! {} prev:{} now:{}'.format(
|
||
|
self.runners[trophy_runner_id].fullname,
|
||
|
self.runners[trophy_runner_id].class_,
|
||
|
runner_class))
|
||
|
self.runners[trophy_runner_id].add_a_race(raceid=raceid, score=race.runners[rn].score)
|
||
|
if dbg:
|
||
|
print('----------', self.runners[trophy_runner_id])
|
||
|
if trophy_runner_id not in self.classes[runner_class].runners:
|
||
|
self.classes[runner_class].runners.append(trophy_runner_id)
|
||
|
|
||
|
|
||
|
|
||
|
def main():
|
||
|
trofy_classes = ('WE', 'ME', 'W15-18', 'M15-18', 'W35', 'M35', 'W14', 'M14', 'W12', 'M12', 'W55', 'M55')
|
||
|
# trofy_classes = ('M35', 'W18')
|
||
|
to_be_skipped = set()
|
||
|
for cr in to_be_merged:
|
||
|
to_be_skipped.update(cr[1:])
|
||
|
print(f'TO Be Skipped: {to_be_skipped}')
|
||
|
files = []
|
||
|
for (dirpath, dirnames, filenames) in os.walk(source_dir):
|
||
|
for f in filenames:
|
||
|
# print(f'Filename: {f}')
|
||
|
if f.endswith(source_ext) and f not in to_be_skipped:
|
||
|
files.append(os.path.join(dirpath, f))
|
||
|
print(f'found: {f}')
|
||
|
break
|
||
|
# for f in files:
|
||
|
# print(os.path.splitext(f))
|
||
|
# print(files)
|
||
|
race_list = []
|
||
|
for racename, r1, r2, fiso_code in to_be_merged:
|
||
|
ff1 = os.path.join(source_dir, r1)
|
||
|
ff2 = os.path.join(source_dir, r2)
|
||
|
print(f'loading ---- {racename} {fiso_code}')
|
||
|
race_list.append((do_merge_races(ff1, ff2, class_remap=class_renames, fiso_code=fiso_code), racename,
|
||
|
class_renames, fiso_code))
|
||
|
print(files)
|
||
|
for f in files:
|
||
|
parts = os.path.basename(f).rstrip(source_ext).split('_')
|
||
|
racename, fiso_code = parts
|
||
|
race_list.append((f, racename, class_renames, fiso_code))
|
||
|
|
||
|
# print(race_list)
|
||
|
|
||
|
clubs_ko = ('1000', '2055')
|
||
|
|
||
|
trofeo20XX = Championship(classes=trofy_classes)
|
||
|
# for r in race_list:
|
||
|
for r in race_list:
|
||
|
racefile, racename, remap, fiso_code = r
|
||
|
if int(fiso_code) in DOUBLE_POINTS:
|
||
|
kf = 2.0
|
||
|
else:
|
||
|
kf = 1.0
|
||
|
print(f'RACENAME= {racename} {fiso_code} KF={kf} {type(fiso_code)}')
|
||
|
trofeo20XX.add_a_race(filename_or_race=racefile, name=racename, class_remap=remap, clubs_ko=clubs_ko,
|
||
|
k_factor=kf, fiso_code=fiso_code)
|
||
|
|
||
|
def get_score(rrr):
|
||
|
return trofeo20XX.runners[rrr].net_score
|
||
|
|
||
|
# All runner iteration...
|
||
|
# for rr in sorted(trofeo20XX.runners, key=get_score_from_runner, reverse=True):
|
||
|
|
||
|
# print 'Totale Corridori: {}\n'.format(len(trofeo20XX.runners))
|
||
|
# for rr in trofeo20XX.runners:
|
||
|
# print trofeo20XX.runners[rr]
|
||
|
# Partecipants = {} # A dictionary with {raceid: participants}
|
||
|
print(f'Numero Gare: {len(trofeo20XX.races)}')
|
||
|
|
||
|
dup_runners = {}
|
||
|
for rnr in trofeo20XX.runners:
|
||
|
runner_obj = trofeo20XX.runners[rnr]
|
||
|
# print(f'{str(runner_obj)}')
|
||
|
if '-' in runner_obj.id_:
|
||
|
fiso_id, *_ = runner_obj.id_.split('-')
|
||
|
else:
|
||
|
fiso_id = runner_obj.id_
|
||
|
if fiso_id in dup_runners:
|
||
|
# print(f'MULTI-Class: {str(runner_obj)}')
|
||
|
dup_runners[fiso_id].append(runner_obj)
|
||
|
else:
|
||
|
dup_runners[fiso_id] = [runner_obj,]
|
||
|
runner_obj.compute_scarti(trophy=trofeo20XX, scarti=NSCARTI)
|
||
|
|
||
|
print('-' * 91)
|
||
|
race_columns = sorted(trofeo20XX.races, key=lambda x: trofeo20XX.races[x].date)
|
||
|
print(race_columns)
|
||
|
print(trofeo20XX.races.keys())
|
||
|
first_row = ['Rank', 'Atleta', 'Società', 'Al netto\ndegli scarti', 'Lordo']
|
||
|
for r in race_columns:
|
||
|
race_name_bare = trofeo20XX.races[r].name
|
||
|
race_name = race_name_bare if not trofeo20XX.is_sprint(r) else f'*{race_name_bare}'
|
||
|
first_row.append('{:10.10s}\n{}'.format(race_name, trofeo20XX.races[r].date.split(' ')[0]))
|
||
|
print(first_row)
|
||
|
# print first_row[3]
|
||
|
|
||
|
pdfreport = TrophyPdf(filename=outfile, page='A4P')
|
||
|
|
||
|
result_tables = {} # A dictionary in the form {class_id : result_table}
|
||
|
# for cl in trofeo20XX.classes:
|
||
|
for cl in trofy_classes:
|
||
|
first_row[1] = 'Categoria ({})'.format(cl)
|
||
|
newtab = [first_row, ]
|
||
|
print('#' * 40, ' {} ha {} partecipanti.'.format(cl, len(trofeo20XX.classes[cl].runners)), '#' * 40)
|
||
|
rank = 1
|
||
|
for rnr in sorted(trofeo20XX.classes[cl].runners, key=get_score, reverse=True):
|
||
|
# print(rnr)
|
||
|
current_runner = trofeo20XX.runners[rnr]
|
||
|
current_runner.rank = rank
|
||
|
print(current_runner)
|
||
|
tabrow = current_runner.to_str_list(race_columns)
|
||
|
tabrow.insert(0, '{:d}'.format(rank))
|
||
|
newtab.append(tabrow)
|
||
|
# print(tabrow)
|
||
|
rank += 1
|
||
|
# for row in newtab:
|
||
|
# print(len(row), row)
|
||
|
# print(newtab)
|
||
|
nrow = len(newtab)
|
||
|
lrow = len(newtab[0])
|
||
|
while nrow < 4:
|
||
|
newtab.append((' ',) * lrow)
|
||
|
nrow += 1
|
||
|
pdfreport.add_table(table=newtab, title='Categoria {}'.format(cl))
|
||
|
# pdfreport.add_table(table=newtab)
|
||
|
result_tables[cl] = newtab
|
||
|
|
||
|
pdfreport.commit()
|
||
|
for multir, runners in dup_runners.items():
|
||
|
if len(runners) > 1:
|
||
|
print(' MULTI Categoria ')
|
||
|
for runner in runners:
|
||
|
shortened = str(runner)[14:]
|
||
|
print(shortened)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|