TrofeoGP_2025/Trofeo2025.py

360 lines
15 KiB
Python
Raw Normal View History

2025-03-26 15:39:35 +01:00
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()