Initial Commit
This commit is contained in:
commit
00ee3b70ef
18 changed files with 31446 additions and 0 deletions
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
12
.idea/Trofeo_GP_2025.iml
generated
Normal file
12
.idea/Trofeo_GP_2025.iml
generated
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.11" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.11" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Trofeo_GP_2025.iml" filepath="$PROJECT_DIR$/.idea/Trofeo_GP_2025.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
5
Links.txt.txt
Normal file
5
Links.txt.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
https://www.fiso.emr.it/wp0/trofeoemr_parz_2023_7/
|
||||||
|
https://www.fiso.emr.it/wp0/wp-content/uploads/2024/06/TrofeoEMR_parz_2024.pdf
|
||||||
|
https://www.fiso.emr.it/wp0/trofeoemr_parz_2024/
|
||||||
|
|
||||||
|
|
362
Torfeo2024.py
Normal file
362
Torfeo2024.py
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
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 = {
|
||||||
|
2024149, # Parma
|
||||||
|
2024157, # Piacenza
|
||||||
|
2024151, # Bologna
|
||||||
|
2024999, # Busseto
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUBLE_POINTS = {2024999, }
|
||||||
|
|
||||||
|
yeartodo = '2024'
|
||||||
|
|
||||||
|
outfile = f'TrofeoEMR_f_{yeartodo}.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)
|
||||||
|
('Parma', 'Parma1_2024149.xml', 'Parma2_2024150.xml', 2024149),
|
||||||
|
('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=1):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
NSCARTI = 0
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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()
|
359
Trofeo2025.py
Normal file
359
Trofeo2025.py
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
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()
|
125
TrofeoEMR_2025_2.pdf
Normal file
125
TrofeoEMR_2025_2.pdf
Normal file
File diff suppressed because one or more lines are too long
105
merge_races.py
Normal file
105
merge_races.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from pyiofload import *
|
||||||
|
import os
|
||||||
|
|
||||||
|
source_dir = 'xmls'
|
||||||
|
|
||||||
|
|
||||||
|
def do_merge_races(xmlfile1, xmlfile2, class_remap=None, club_ko=None, fiso_code=0):
|
||||||
|
race1 = Race()
|
||||||
|
race2 = Race()
|
||||||
|
race1.load_from_iofxml(filename_=xmlfile1, class_remap=class_remap, clubs_ko=club_ko, avoid_unk=True)
|
||||||
|
race2.load_from_iofxml(filename_=xmlfile2, class_remap=class_remap, clubs_ko=club_ko, avoid_unk=True)
|
||||||
|
# print(race1.name, race1.date)
|
||||||
|
# print(race2.name, race2.date)
|
||||||
|
classes1_set = set(race1.classes.keys())
|
||||||
|
classes2_set = set(race2.classes.keys())
|
||||||
|
classes_both = classes1_set & classes2_set
|
||||||
|
# print(len(classes1_set), len(classes2_set), len(classes_both))
|
||||||
|
# print(classes1_set == classes2_set)
|
||||||
|
if classes1_set != classes2_set:
|
||||||
|
raise Exception('Different classes!')
|
||||||
|
runners1_set = set(race1.runners.keys())
|
||||||
|
runners2_set = set(race2.runners.keys())
|
||||||
|
# print(len(runners1_set), runners1_set)
|
||||||
|
# print(len(runners2_set), runners2_set)
|
||||||
|
runners_both = runners1_set & runners2_set
|
||||||
|
# print(len(runners_both))
|
||||||
|
runners_one_only = runners1_set - runners_both
|
||||||
|
runners_two_only = runners2_set - runners_both
|
||||||
|
# print(runners_one_only)
|
||||||
|
# print(runners_two_only)
|
||||||
|
|
||||||
|
for id_to_remove in runners_one_only:
|
||||||
|
race1.runners.pop(id_to_remove, None)
|
||||||
|
for id_to_remove in runners_two_only:
|
||||||
|
race2.runners.pop(id_to_remove, None)
|
||||||
|
|
||||||
|
runners1_set = set(race1.runners.keys())
|
||||||
|
runners2_set = set(race2.runners.keys())
|
||||||
|
# print(len(runners1_set), runners1_set)
|
||||||
|
# print(len(runners2_set), runners2_set)
|
||||||
|
|
||||||
|
combined_race = Race(fiso_code=fiso_code)
|
||||||
|
combined_race.name = race1.name + '_' + race2.name
|
||||||
|
combined_race.date = race2.date
|
||||||
|
combined_race.classes = race2.classes
|
||||||
|
combined_race.clubs = {**race1.clubs, **race2.clubs}
|
||||||
|
|
||||||
|
combined_runners = {}
|
||||||
|
for rid in runners_both:
|
||||||
|
# print(race1.runners[rid])
|
||||||
|
# print(race2.runners[rid])
|
||||||
|
runner = Runner(name=race1.runners[rid].name,
|
||||||
|
family=race1.runners[rid].family,
|
||||||
|
fisoid=race1.runners[rid].fisoid,
|
||||||
|
class_=race1.runners[rid].class_,
|
||||||
|
club=race1.runners[rid].club,
|
||||||
|
clubid=race1.runners[rid].clubid,
|
||||||
|
sicard=race1.runners[rid].sicard,
|
||||||
|
total=race1.runners[rid].time + race2.runners[rid].time
|
||||||
|
)
|
||||||
|
combined_runners[rid] = runner
|
||||||
|
# print(runner)
|
||||||
|
combined_race.runners = combined_runners
|
||||||
|
for cid, class_ in combined_race.classes.items():
|
||||||
|
class_.min = None
|
||||||
|
class_.max = None
|
||||||
|
class_.avg = 0.0
|
||||||
|
class_.tot_t = 0.0
|
||||||
|
both_runners = [rid for rid in class_.runnersid if rid in combined_race.runners]
|
||||||
|
# print(both_runners)
|
||||||
|
both_runners.sort(key=lambda x: combined_race.runners[x].time)
|
||||||
|
# print(both_runners)
|
||||||
|
class_.runnersid = both_runners
|
||||||
|
if both_runners is None:
|
||||||
|
continue
|
||||||
|
for rank, rid in enumerate(class_.runnersid):
|
||||||
|
r = combined_race.runners[rid]
|
||||||
|
r.place = rank + 1
|
||||||
|
# print(r)
|
||||||
|
if class_.min is None or class_.min > r.time:
|
||||||
|
class_.min = r.time
|
||||||
|
if class_.max is None or class_.max < r.time:
|
||||||
|
class_.max = r.time
|
||||||
|
class_.tot_t += r.time
|
||||||
|
class_.avg = class_.tot_t / float(len(class_.runnersid))
|
||||||
|
|
||||||
|
# print(class_)
|
||||||
|
return combined_race, race1, race2
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cf, r1, r2 = do_merge_races(os.path.join(source_dir, 'POC-1_2019109.xml'), os.path.join(source_dir, 'POC-2_2019110.xml'))
|
||||||
|
for cid, c in cf.classes.items():
|
||||||
|
print(c.name)
|
||||||
|
for rid in c.runnersid:
|
||||||
|
r = cf.runners[rid]
|
||||||
|
# print(r)
|
||||||
|
tt = time.strftime('%H:%M:%S', time.gmtime(r.time))
|
||||||
|
t1 = time.strftime('%H:%M:%S', time.gmtime(r1.runners[rid].time))
|
||||||
|
t2 = time.strftime('%H:%M:%S', time.gmtime(r2.runners[rid].time))
|
||||||
|
print('{:2} {:15} {:15} tempo_totale: {} ({} {})'.format(
|
||||||
|
r.place, r.name, r.family, tt, t1, t2)
|
||||||
|
)
|
396
pyiofload/__init__.py
Normal file
396
pyiofload/__init__.py
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
try:
|
||||||
|
import xml.etree.cElementTree as ET
|
||||||
|
except ImportError:
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class Race:
|
||||||
|
"""A single race event."""
|
||||||
|
|
||||||
|
def __init__(self, name=None, date=None, fiso_code=0):
|
||||||
|
""""""
|
||||||
|
self.name = name
|
||||||
|
self.date = date
|
||||||
|
self.classes = {} # Key = classes_id, Values = the Class itself.
|
||||||
|
self.runners = {} # Key = runner_id, Values = the Runner itself.
|
||||||
|
self.legs = {} # Key = legname, Values = the leg itself.
|
||||||
|
self.clubs = {} # Key = club_id, Values = the Club itself.
|
||||||
|
self.ns = '' # Current iof file namespace.
|
||||||
|
self.statuses = [] # List of all NON 'OK' statuses found.
|
||||||
|
self.unk_cnt = 0 # A counter to name/list the ones without a fiso id.
|
||||||
|
self.org_cnt = 0 # A counter to name/list the ones without an organisation
|
||||||
|
self.avoid_unk = False # Used to skip the ones without a valid Fiso_id.
|
||||||
|
self.fiso_code = fiso_code
|
||||||
|
|
||||||
|
def load_from_iofxml(self, filename_=None, class_remap=None, clubs_ko=None, avoid_unk=False):
|
||||||
|
if class_remap is None:
|
||||||
|
class_remap = {}
|
||||||
|
if clubs_ko is None:
|
||||||
|
clubs_ko = []
|
||||||
|
if filename_ is None:
|
||||||
|
return
|
||||||
|
self.avoid_unk = avoid_unk
|
||||||
|
# print('Loading:', filename_)
|
||||||
|
xmlf = ET.ElementTree(file=filename_)
|
||||||
|
root = xmlf.getroot()
|
||||||
|
self.ns = root.tag.split('}')[0] + '}'
|
||||||
|
|
||||||
|
self.name = (root.find('{0}Event/{0}Name'.format(self.ns))).text
|
||||||
|
dt = (root.find('{0}Event/{0}StartTime/{0}Date'.format(self.ns))).text
|
||||||
|
tm = (root.find('{0}Event/{0}StartTime/{0}Time'.format(self.ns))).text
|
||||||
|
self.date = '{} {}'.format(dt, tm)
|
||||||
|
|
||||||
|
for child_of_root in root:
|
||||||
|
clean_tag = child_of_root.tag.split("}")[1]
|
||||||
|
if 'ClassResult' == clean_tag:
|
||||||
|
for res in child_of_root:
|
||||||
|
clean_res = res.tag.split("}")[1]
|
||||||
|
if 'Class' == clean_res:
|
||||||
|
id_ = res.find('{0}Id'.format(self.ns)).text
|
||||||
|
# print('Class id:', id_)
|
||||||
|
if id_ in class_remap:
|
||||||
|
id_ = class_remap[id_]
|
||||||
|
# print('Remaped to:', id_)
|
||||||
|
name = res.find('{0}Name'.format(self.ns))
|
||||||
|
if id_ in self.classes:
|
||||||
|
now_category = self.classes[id_]
|
||||||
|
else:
|
||||||
|
now_category = Class(name=name.text, id_=id_)
|
||||||
|
self.classes[id_] = now_category
|
||||||
|
if 'Course' == clean_res:
|
||||||
|
course_len = res.find('{0}Length'.format(self.ns))
|
||||||
|
course_clib = res.find('{0}Climb'.format(self.ns))
|
||||||
|
now_category.distance = float(course_len.text)
|
||||||
|
now_category.climb = float(course_clib.text)
|
||||||
|
if 'PersonResult' == clean_res:
|
||||||
|
self._load_a_runner(res, now_category, clubs_ko=clubs_ko)
|
||||||
|
|
||||||
|
def _load_a_runner(self, pers_res, class_, clubs_ko=[]):
|
||||||
|
"""
|
||||||
|
It loads the runner details from an ElemnetTree.
|
||||||
|
:param pers_res: the base element found (the PersonResult element)
|
||||||
|
:type pers_res: ElementTree
|
||||||
|
:return: the runner object
|
||||||
|
:rtype: Runner
|
||||||
|
"""
|
||||||
|
fisoid = pers_res.find('{0}Person/{0}Id'.format(self.ns))
|
||||||
|
ng = pers_res.find('{0}Person/{0}Name/{0}Given'.format(self.ns))
|
||||||
|
nf = pers_res.find('{0}Person/{0}Name/{0}Family'.format(self.ns))
|
||||||
|
clubid = pers_res.find('{0}Organisation/{0}Id'.format(self.ns))
|
||||||
|
club_name = pers_res.find('{0}Organisation/{0}Name'.format(self.ns))
|
||||||
|
bib = pers_res.find('{0}Result/{0}BibNumber'.format(self.ns))
|
||||||
|
time = pers_res.find('{0}Result/{0}Time'.format(self.ns))
|
||||||
|
position = pers_res.find('{0}Result/{0}Position'.format(self.ns))
|
||||||
|
score = pers_res.find('{0}Result/{0}Score'.format(self.ns))
|
||||||
|
status = pers_res.find('{0}Result/{0}Status'.format(self.ns))
|
||||||
|
sicard = pers_res.find('{0}Result/{0}ControlCard'.format(self.ns))
|
||||||
|
current_runner = None
|
||||||
|
if fisoid.text is None:
|
||||||
|
if self.avoid_unk:
|
||||||
|
return
|
||||||
|
if '{Libero}' == nf.text:
|
||||||
|
return
|
||||||
|
runner_id = 'UNK{:03d}'.format(self.unk_cnt)
|
||||||
|
print('#### UNK ####' * 10)
|
||||||
|
print(ng.text, nf.text)
|
||||||
|
self.unk_cnt += 1
|
||||||
|
else:
|
||||||
|
runner_id = fisoid.text
|
||||||
|
|
||||||
|
if club_name is None or club_name.text is None or clubid is None or clubid.text is None:
|
||||||
|
club_str = 'ORG{:03d}'.format(self.org_cnt)
|
||||||
|
clubid_str = club_str
|
||||||
|
self.org_cnt += 1
|
||||||
|
else:
|
||||||
|
club_str = club_name.text
|
||||||
|
clubid_str = clubid.text
|
||||||
|
|
||||||
|
if 'OK' == status.text.upper() and clubid_str not in clubs_ko:
|
||||||
|
# print fisoid.text, ng.text, nf.text,\
|
||||||
|
# clubid.text, club_name.text, bib.text,\
|
||||||
|
# time.text, position.text, score.text, status.text
|
||||||
|
|
||||||
|
if clubid_str not in self.clubs:
|
||||||
|
new_club = Club(name=club_str, id_=clubid_str)
|
||||||
|
self.clubs[clubid_str] = new_club
|
||||||
|
|
||||||
|
current_runner = Runner(name=ng.text,
|
||||||
|
class_=class_.id_,
|
||||||
|
club=club_str,
|
||||||
|
total=float(time.text),
|
||||||
|
bib=int(bib.text),
|
||||||
|
fisoid=runner_id,
|
||||||
|
place=int(position.text),
|
||||||
|
score=float(score.text),
|
||||||
|
family=nf.text,
|
||||||
|
sicard=int(sicard.text),
|
||||||
|
clubid=clubid_str
|
||||||
|
)
|
||||||
|
last_time = 0.0
|
||||||
|
previous_cn = 0
|
||||||
|
self.runners[current_runner.fisoid] = current_runner
|
||||||
|
# print current_runner
|
||||||
|
results = pers_res.find('{0}Result'.format(self.ns))
|
||||||
|
# visit_deep(results, 1000)
|
||||||
|
for spl in results.findall('{0}SplitTime'.format(self.ns)):
|
||||||
|
cne = spl.find('{0}ControlCode'.format(self.ns))
|
||||||
|
splte = spl.find('{0}Time'.format(self.ns))
|
||||||
|
if cne is not None and cne.text is not None and splte is not None and splte.text is not None:
|
||||||
|
cn = cne.text
|
||||||
|
# @MC to float directly PLEASE!
|
||||||
|
splt = splte.text
|
||||||
|
tsplit = float(splt) - last_time
|
||||||
|
last_time = float(splt)
|
||||||
|
self.__leg_to_insert(previous_cn, cn, tsplit, current_runner)
|
||||||
|
previous_cn = int(cn)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
# do add the last (implicit) split time
|
||||||
|
tsplit = current_runner.time - last_time
|
||||||
|
self.__leg_to_insert(previous_cn, 999, tsplit, current_runner) # Last control point receives the name '999'
|
||||||
|
else:
|
||||||
|
if status.text not in self.statuses:
|
||||||
|
self.statuses.append(status.text)
|
||||||
|
if current_runner is not None:
|
||||||
|
# print 'Adding {} {}'.format(current_runner,current_runner.fisoid)
|
||||||
|
class_.runnersid.append(current_runner.fisoid) # do add the current runner to the class list
|
||||||
|
if class_.min is None or class_.min > current_runner.time:
|
||||||
|
class_.min = current_runner.time
|
||||||
|
if class_.max is None or class_.max < current_runner.time:
|
||||||
|
class_.max = current_runner.time
|
||||||
|
class_.tot_t += current_runner.time
|
||||||
|
class_.avg = class_.tot_t / float(len(class_.runnersid))
|
||||||
|
|
||||||
|
return current_runner
|
||||||
|
|
||||||
|
def __leg_to_insert(self, from_, to_, time_, runner):
|
||||||
|
"""It computes the leg name, then adds/updates the leg in the legs list"""
|
||||||
|
nfrom = int(from_)
|
||||||
|
runner.add_cn(nfrom, time_)
|
||||||
|
legname = "%03d-%03d" % (nfrom, int(to_))
|
||||||
|
if legname not in self.legs: # Do instantiate a new leg if it is a new one
|
||||||
|
newleg = Leg(legname)
|
||||||
|
self.legs[legname] = newleg
|
||||||
|
self.legs[legname].add_a_leg(time_, runner.class_, runner)
|
||||||
|
|
||||||
|
def adj_scores(self, k_factor=1.0):
|
||||||
|
"""It copmutes the score foreach runner"""
|
||||||
|
for clid in self.classes:
|
||||||
|
cl = self.classes[clid]
|
||||||
|
if len(cl.runnersid) > 0:
|
||||||
|
# print '{}: winner time= {:6.1f}'.format(cl.id_, cl.min),
|
||||||
|
for runid in cl.runnersid:
|
||||||
|
new_score = k_factor * 100.0 * cl.min / self.runners[runid].time
|
||||||
|
# print ' {}({:7.3f})'.format(runid, new_score),
|
||||||
|
# if self.runners[runid].score != 0.0 and self.runners[runid].score != new_score:
|
||||||
|
# print '!!!Score Mismatch: new={:7.3f} {}'.format(new_score, self.runners[runid])
|
||||||
|
self.runners[runid].score = new_score
|
||||||
|
# print
|
||||||
|
|
||||||
|
|
||||||
|
class Class:
|
||||||
|
"""A class defining a Category"""
|
||||||
|
|
||||||
|
def __init__(self, name=None, id_=None, distance=None, climb=None, kmsf=None):
|
||||||
|
self.name = name
|
||||||
|
self.id_ = id_
|
||||||
|
self.distance = distance
|
||||||
|
self.climb = climb
|
||||||
|
self.kmsf = kmsf
|
||||||
|
self.runnersid = [] # A list of runners.fifoid 's belonging to the class
|
||||||
|
self.min = None
|
||||||
|
self.max = None
|
||||||
|
self.tot_t = 0.0
|
||||||
|
self.avg = 0.0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
cl_string = 'Cat: '
|
||||||
|
if self.name is not None:
|
||||||
|
# cl_string += '{:6.6s} ({:3s})'.format(self.name.encode('utf-8'), self.id_)
|
||||||
|
cl_string += '{:6.6s} ({:3s})'.format(self.name, self.id_)
|
||||||
|
else:
|
||||||
|
cl_string += "Sconos"
|
||||||
|
if self.distance is not None:
|
||||||
|
cl_string += ' Lunghezza: %.0f' % float(self.distance)
|
||||||
|
if self.climb is not None:
|
||||||
|
cl_string += ' Dislivello: %.0f ' % float(self.climb)
|
||||||
|
if self.kmsf is not None:
|
||||||
|
cl_string += ' kmsf: %.0f ' % float(self.kmsf)
|
||||||
|
nr = len(self.runnersid)
|
||||||
|
if nr > 0:
|
||||||
|
cl_string += 'winner: {:6.1f} average: {:6.1f} last: {:4.0f} runners({:2d})'.format(
|
||||||
|
self.min,
|
||||||
|
self.avg,
|
||||||
|
self.max,
|
||||||
|
nr,
|
||||||
|
)
|
||||||
|
for runner in self.runnersid:
|
||||||
|
cl_string += ' {}'.format(runner)
|
||||||
|
return cl_string
|
||||||
|
|
||||||
|
|
||||||
|
class Club:
|
||||||
|
"""A class defining a Club"""
|
||||||
|
|
||||||
|
def __init__(self, name=None, id_=None, country=None, region=None):
|
||||||
|
self.name = name
|
||||||
|
self.id_ = id_
|
||||||
|
self.country = country
|
||||||
|
self.region = region
|
||||||
|
|
||||||
|
|
||||||
|
class Runner:
|
||||||
|
"""An object describing a single runner"""
|
||||||
|
|
||||||
|
def __init__(self, name=None, class_=None, club=None, bib=0, total=0.0,
|
||||||
|
place=0, score=0.0, family=None, fisoid=None, sicard=None, clubid=None):
|
||||||
|
self.name = name
|
||||||
|
self.family = family
|
||||||
|
self.fisoid = fisoid
|
||||||
|
self.class_ = class_
|
||||||
|
self.club = club
|
||||||
|
self.bib = bib
|
||||||
|
self.time = total
|
||||||
|
self.place = place
|
||||||
|
self.score = score
|
||||||
|
self.sicard = int(sicard)
|
||||||
|
self.clubid = clubid
|
||||||
|
self.cn_seq = [(0, 0.0), ] # each list items is a tuple with the control number and the time (cn=0 for start
|
||||||
|
# for k, v in self.__dict__.items():
|
||||||
|
# print('{}= {} type={}'.format(k, v, type(v)))
|
||||||
|
|
||||||
|
def add_cn(self, c_n=None, tmm=0.0):
|
||||||
|
"""Method to add a splittime/controlpoint in a sigle race"""
|
||||||
|
if c_n is not None:
|
||||||
|
self.cn_seq.append((c_n, tmm))
|
||||||
|
|
||||||
|
def full_name(self, given_first=False):
|
||||||
|
if given_first:
|
||||||
|
return '{} {}'.format(self.name, self.family.upper())
|
||||||
|
else:
|
||||||
|
return '{} {}'.format(self.family.upper(), self.name)
|
||||||
|
|
||||||
|
# if given_first:
|
||||||
|
# return '{} {}'.format(self.name.encode('utf-8'), self.family.encode('utf-8').upper())
|
||||||
|
# else:
|
||||||
|
# return '{} {}'.format(self.family.encode('utf-8').upper(), self.name.encode('utf-8'))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# runner_str = ''
|
||||||
|
# try:
|
||||||
|
# runner_str = '{:8.8} "{:10.10} {:10.10}" Categoria:{:5}'.format(
|
||||||
|
# self.fisoid,
|
||||||
|
# self.name,
|
||||||
|
# self.family.upper(),
|
||||||
|
# self.class_,
|
||||||
|
# )
|
||||||
|
# except:
|
||||||
|
# print('%' * 100)
|
||||||
|
# for k, v in self.__dict__.items():
|
||||||
|
# print('{}= {} type={}'.format(k, v, type(v)))
|
||||||
|
# print('/' * 100)
|
||||||
|
|
||||||
|
runner_str = ('{:8.8} "{:10.10} {:10.10}" Categoria:{:5.5} Posizione:{:3d}'
|
||||||
|
# ' Tempo:{:6.1f} Pettorale:{:4} score:{:7.3f} sicard:{} Società :{} '
|
||||||
|
' Tempo: {:5} Pettorale:{:4} score:{:7.3f} sicard:{} Società :{} '
|
||||||
|
).format(
|
||||||
|
self.fisoid,
|
||||||
|
self.name,
|
||||||
|
self.family.upper(),
|
||||||
|
self.class_,
|
||||||
|
self.place,
|
||||||
|
time.strftime('%H:%M:%S', time.gmtime(self.time)),
|
||||||
|
self.bib,
|
||||||
|
self.score,
|
||||||
|
self.sicard,
|
||||||
|
self.club,
|
||||||
|
)
|
||||||
|
# print(type(self.sicard), type(self.bib))
|
||||||
|
for j in range(1, len(self.cn_seq)):
|
||||||
|
runner_str += '[%d/%.0f]' % self.cn_seq[j]
|
||||||
|
return runner_str
|
||||||
|
|
||||||
|
|
||||||
|
class Leg:
|
||||||
|
"""A Leg in a race.
|
||||||
|
It will hold a list of classes and a list of runners involved in that leg.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name=None):
|
||||||
|
self.name = name
|
||||||
|
self.runners = []
|
||||||
|
self.classes = []
|
||||||
|
self.min = None
|
||||||
|
self.max = None
|
||||||
|
self.tot_t = 0.0
|
||||||
|
self.avg = 0.0
|
||||||
|
|
||||||
|
def add_a_leg(self, tempo_, class_, runner):
|
||||||
|
self.runners.append((tempo_, class_, runner))
|
||||||
|
if self.max is None or self.max < tempo_:
|
||||||
|
self.max = tempo_
|
||||||
|
if self.min is None or self.min > tempo_:
|
||||||
|
self.min = tempo_
|
||||||
|
self.tot_t += tempo_
|
||||||
|
self.avg = self.tot_t / float(len(self.runners))
|
||||||
|
if class_ not in self.classes:
|
||||||
|
self.classes.append(class_)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
leg_str = ('Tratta: {:.7s} corridori: {:3d} migliore: {:3.0f}'
|
||||||
|
' media: {:5.1f} peggiore: {:3.0f} Categorie({:2d}):'
|
||||||
|
).format(
|
||||||
|
self.name.encode('utf-8'),
|
||||||
|
len(self.runners),
|
||||||
|
self.min,
|
||||||
|
self.avg,
|
||||||
|
self.max,
|
||||||
|
len(self.classes),
|
||||||
|
)
|
||||||
|
for cl in self.classes:
|
||||||
|
leg_str += ' ' + cl.encode('utf-8')
|
||||||
|
return leg_str
|
||||||
|
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
# test
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# filename = 'c:/MC/O/Bologna_2017/201713.xml'
|
||||||
|
filename = 'C:/Mc/Python/PyPrjs/TrofeoGPeroni2018/01_Carrega_2018199.xml'
|
||||||
|
|
||||||
|
gara = Race()
|
||||||
|
gara.load_from_iofxml(filename)
|
||||||
|
|
||||||
|
print('Race name: ', gara.name)
|
||||||
|
print(' Classes: ', len(gara.classes))
|
||||||
|
print(' Runners: ', len(gara.runners))
|
||||||
|
print(' Legs: ', len(gara.legs))
|
||||||
|
print(' Unrecognized statuses: ', gara.statuses)
|
||||||
|
|
||||||
|
|
||||||
|
def get_runners(rrr):
|
||||||
|
return len(gara.legs[rrr].runners)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
for ll in sorted(gara.legs, key=get_runners):
|
||||||
|
theleg = gara.legs[ll]
|
||||||
|
print theleg
|
||||||
|
print '-' * 70
|
||||||
|
for rr in gara.runners:
|
||||||
|
print gara.runners[rr]
|
||||||
|
print '+' * 70
|
||||||
|
for cl in gara.classes:
|
||||||
|
print gara.classes[cl]
|
||||||
|
print '/\\' * 35
|
||||||
|
"""
|
||||||
|
gara.adj_scores()
|
||||||
|
|
||||||
|
print('[-]' * 35)
|
||||||
|
for rr in gara.runners:
|
||||||
|
print(gara.runners[rr])
|
BIN
r_2025_001.pdf
Normal file
BIN
r_2025_001.pdf
Normal file
Binary file not shown.
142
trophyPDF/__init__.py
Normal file
142
trophyPDF/__init__.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from reportlab.lib.pagesizes import A4, landscape, A3, portrait
|
||||||
|
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
|
||||||
|
from reportlab.lib.units import mm
|
||||||
|
from reportlab.platypus.flowables import PageBreak, Spacer
|
||||||
|
from reportlab.platypus.tables import Table
|
||||||
|
from reportlab.platypus.paragraph import Paragraph
|
||||||
|
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||||
|
from reportlab.lib import colors
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def make_portrait(canvas,doc):
|
||||||
|
canvas.setPageSize(PAGE_SIZE)
|
||||||
|
|
||||||
|
def make_landscape(canvas,doc):
|
||||||
|
canvas.setPageSize(landscape(PAGE_SIZE))
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BaseStyle:
|
||||||
|
BASE = (
|
||||||
|
('GRID', (0, 0), (-1, -1), 0.7, colors.grey),
|
||||||
|
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||||
|
('FONTSIZE', (0, 0), (-1, -1), 7),
|
||||||
|
('FONTSIZE', (1, 0), (1, 0), 9),
|
||||||
|
('FONTSIZE', (4, 0), (-1, -1), 6),
|
||||||
|
('FONTSIZE', (2, 1), (2, -1), 4),
|
||||||
|
# ('VALIGN', (2, 1), (2, -1), 'MIDDLE'),
|
||||||
|
('BOX', (0, 0), (-1, -1), 1, colors.black),
|
||||||
|
('BACKGROUND', (0, 1), (-1, 1), (0, 1.0, 1.0)),
|
||||||
|
('BACKGROUND', (0, 2), (-1, 2), (0.4, 1.0, 1.0)),
|
||||||
|
('BACKGROUND', (0, 3), (-1, 3), (0.7, 1.0, 1.0)),
|
||||||
|
('ALIGN', (3, 0), (-1, -1), 'CENTER'),
|
||||||
|
('ALIGN', (0, 0), (0, -1), 'CENTER'),
|
||||||
|
('ROWBACKGROUNDS', (0, 4), (-1, -1), (colors.white, colors.lightgrey))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TrophyPdf:
|
||||||
|
"""To build a pdf report from a series of tables"""
|
||||||
|
def __init__(self, filename='default_name.pdf', separated_pages=False, page='A4P'):
|
||||||
|
"""page can be A4P, A4L, A3P, A3L"""
|
||||||
|
self.separated_pages = separated_pages
|
||||||
|
self.styleSheet = getSampleStyleSheet()
|
||||||
|
self.MARGIN_SIZE = 10 * mm
|
||||||
|
if page == 'A4L':
|
||||||
|
self.PAGE_SIZE = landscape(A4)
|
||||||
|
elif page == 'A4P':
|
||||||
|
self.PAGE_SIZE = portrait(A4)
|
||||||
|
elif page == 'A3L':
|
||||||
|
self.PAGE_SIZE = landscape(A3)
|
||||||
|
elif page == 'A3P':
|
||||||
|
self.PAGE_SIZE = portrait(A3)
|
||||||
|
else:
|
||||||
|
raise ValueError(f'page value not recognized: {page}. Possible values are: A4P, A4L, A3P, A3L')
|
||||||
|
|
||||||
|
self.filename = filename
|
||||||
|
self.story = []
|
||||||
|
self.style = list(BaseStyle.BASE)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
self.__create_pdfdoc(self.filename, self.story)
|
||||||
|
|
||||||
|
def __create_pdfdoc(self, pdfdoc, story):
|
||||||
|
"""Creates PDF doc from story."""
|
||||||
|
pdf_doc = BaseDocTemplate(pdfdoc, pagesize=self.PAGE_SIZE,
|
||||||
|
leftMargin=self.MARGIN_SIZE, rightMargin=self.MARGIN_SIZE,
|
||||||
|
topMargin=self.MARGIN_SIZE, bottomMargin=self.MARGIN_SIZE)
|
||||||
|
main_frame = Frame(self.MARGIN_SIZE, self.MARGIN_SIZE,
|
||||||
|
self.PAGE_SIZE[0] - 2 * self.MARGIN_SIZE, self.PAGE_SIZE[1] - 2 * self.MARGIN_SIZE,
|
||||||
|
leftPadding=0, rightPadding=0, bottomPadding=0,
|
||||||
|
topPadding=0, id='main_frame')
|
||||||
|
main_template = PageTemplate(id='main_template', frames=[main_frame])
|
||||||
|
pdf_doc.addPageTemplates([main_template])
|
||||||
|
pdf_doc.build(story)
|
||||||
|
|
||||||
|
def add_table(self, table=None, title=None):
|
||||||
|
if table is None:
|
||||||
|
return
|
||||||
|
if self.separated_pages and (0 != len(self.story)):
|
||||||
|
self.story.append(PageBreak())
|
||||||
|
if title is not None:
|
||||||
|
self.story.append(Paragraph(title, self.styleSheet['BodyText']))
|
||||||
|
self.story.append(Spacer(0, 4 * mm))
|
||||||
|
if table is not None:
|
||||||
|
nrows = len(table)
|
||||||
|
heights = [9*mm, ]
|
||||||
|
heights.extend((5*mm,)*(nrows-1))
|
||||||
|
self.style = list(BaseStyle.BASE)
|
||||||
|
y = 0
|
||||||
|
for row in table:
|
||||||
|
x = 0
|
||||||
|
for el in row:
|
||||||
|
if el[0] == '_' and el[-1] == '_':
|
||||||
|
self.style.append(('BACKGROUND', (x, y), (x, y), colors.pink))
|
||||||
|
x += 1
|
||||||
|
y += 1
|
||||||
|
self.story.append(Table(data=table, style=self.style, rowHeights=heights))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tb = (
|
||||||
|
[['Rank', 'Atleta (M18)', 'Soc.', 'Punteggio\nNetto', 'Punteggio\nLordo', 'TROFEO EMI\n2017-02-26',
|
||||||
|
'13^ Ori de\n2017-03-19', '1 Maggio 2\n2017-05-01', 'Trofeo Emi\n2017-05-27', 'Trofeo ER \n2017-05-28',
|
||||||
|
'Campionato\n2017-06-18', 'Camp. Reg.\n2017-06-25', 'Scaffaiolo\n2017-07-16', 'Campionato\n2017-08-27',
|
||||||
|
'Trofeo Emi\n2017-09-30', 'Trofeo Emi\n2017-10-01', 'Monte Mori\n2017-10-08'],
|
||||||
|
['1', 'MANNOCCI Daniele', "0221\nPol 'G. MASI' ", ' 652.91', ' 652.91', ' ', '100.00', ' 87.38', '100.00',
|
||||||
|
'100.00', ' ', '100.00', '100.00', ' 65.53', ' ', ' ', ' '],
|
||||||
|
['2', 'VESCHI Francesco', '0610\nOr. CLUB APPENNIN', ' 383.27', ' 383.27', ' 51.87', ' ', ' 61.29', ' 61.05',
|
||||||
|
' 73.97', ' ', ' ', ' ', ' 37.81', ' 97.28', ' ', ' '],
|
||||||
|
['3', 'MIRZA Lucian', '0098\nAtl. INTERFLUMINA', ' 366.51', ' 366.51', ' 96.10', ' ', '100.00', ' ', ' ', ' ',
|
||||||
|
' ', ' ', ' ', ' ', ' 90.08', ' 80.32'],
|
||||||
|
['4', 'RONTINI Mattia', '0255\nASDS CARCHIDIO-ST', ' 355.28', ' 355.28', ' 62.64', ' 88.11', ' 88.70', ' ',
|
||||||
|
' 81.60', ' ', ' ', ' ', ' 34.22', ' ', ' ', ' ']]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
mstyle = [
|
||||||
|
('GRID', (0, 0), (-1, -1), 0.7, colors.grey),
|
||||||
|
('FONTSIZE', (0, 0), (-1, -1), 6),
|
||||||
|
('FONTSIZE', (4, 0), (-1, -1), 5),
|
||||||
|
('FONTSIZE', (2, 1), (2, -1), 5),
|
||||||
|
|
||||||
|
# ('GRID',(1,1),(-2,-2),1,colors.green),
|
||||||
|
# ('BOX',(0,0),(1,-1),2,colors.red),
|
||||||
|
('BOX', (0, 0), (-1, -1), 1, colors.black),
|
||||||
|
# ('LINEABOVE',(1,2),(-2,2),1,colors.blue),
|
||||||
|
# ('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
|
||||||
|
# ('BACKGROUND', (0, 0), (0, 1), colors.pink),
|
||||||
|
# ('BACKGROUND', (1, 1), (1, 2), colors.lavender),
|
||||||
|
# ('BACKGROUND', (2, 2), (2, 3), colors.orange),
|
||||||
|
('ALIGN', (3, 0), (-1, -1), 'CENTER'),
|
||||||
|
('ALIGN', (0, 0), (0, -1), 'CENTER'),
|
||||||
|
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||||
|
]
|
||||||
|
tro = TrophyPdf(filename='test01.pdf')
|
||||||
|
tro.add_table(table=tb)
|
||||||
|
tro.add_table(table=tb, title='A Tittle.........')
|
||||||
|
tro.commit()
|
87
trophyPDF/test01.pdf
Normal file
87
trophyPDF/test01.pdf
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 8 0 R /MediaBox [ 0 0 1190.551 841.8898 ] /Parent 7 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 9 0 R /MediaBox [ 0 0 1190.551 841.8898 ] /Parent 7 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Author (\(anonymous\)) /CreationDate (D:20201017194222-01'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20201017194222-01'00') /Producer (ReportLab PDF Library - www.reportlab.com)
|
||||||
|
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/Count 2 /Kids [ 3 0 R 4 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1824
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau`UgMRrh&:N/3n1>GA/;KQ8q5DqdWksp(8a/SS'G)8;:hiY*Wh@,aiG*SE9._I409$R!fKEl%[kfg%!@I"8MVjJuN."m6TC.5\A;_f>RVT@AB>[1,miI]a$&VrhJ6<%\.>Pk-KbqOJcb0cr%!)!g'*8mbBNUl8JQ5UQh24@Dm#fg&>YAtMUJJdE'.t;,)Drl#cpZLS;J8oYMpUS.D6\%_/t3n_lUHVhC[E+THY)YYbKZY$f]"tsHn9i"m8Iht_d5(9L5EbGA[PmNYOBNi^4&%!QCJ;4i#lTX6M'RhDqh5L@HjGQeID;kO3N.Hr4Ll53*'uM]O<3-UrdAdhk!RW;\PDt\P\)aPODY+jN8rAP<O$T\%?Q-]JeO^eb688:%*g82B7cU,fcHkqiCkq?c]t""NT!M3SS][l45f/XkC0`MPRd-.<Kf4IIQ&<\g^ldN'+B81@,pC.?`f4RP%C50-EQ$K);5&f&lA.:TH&d^95p1BmdB;BtHr?;V5sENb0i]Oka`;E9%WW1`U(K9K1/H^Xh/a1;:Fl&tQ/h]F`_NKI8!,pK!i2ibZ&L:5I;ZG#;\W-YX2=$E7EDgXH(?q=M%!DdO,m,#N=J3d8pD+`YU5?#mk6GL(NW[I;m6Qb3m.JYP$Q?+iK9d((L\=!BqN'RW8s\]U+7Pf7Ho-P/j0=c(p'a#)XN\7=Rd0&\3tPj=2<_3)>V/P%KCY>Jt#Ja*e'&dgg5UF("DQqXA92^<LXgoOR1X&He0;E?T,BMM*dn6M_:CtIm+m0e:8RBuEk5'k95cS_ambCujPe6EGM6_OUb8Q@T!6+YSFJsDci#Gro)Zj;NU(3F=@?oF]$aC!rD.]k.agU:g/a^$A=;tQ52gV/uF"$f%6A-p2EV&DVeK[)JmOD]]mT*bqZReVuC`^la3]p"O6EV,8a@</#A$S!4q3G=FJ8VC7r@1RrS/(>@,\)I8Ik7P>\/N8`kIi^]:Z&;6)WaS[G,W<8d;k\\o:Nr>bgjf.<"qM6^a^d5RMcO$iUAM!9S0kP[WlubF21od5#d/$!!U^R'7EJh\*tcfa[>dW510*7pQmKTu5pQefTNJBl7'Mp6;rq%/fpq:GBfKdsLCN,\5+_R1X[Zr6\P!')U]<"i-;\E=Wq7d_k00[MloAmBBOt';3aM4g;5Y+F$T]?RINPfk+-5aLjpcp=j$261,bag]"l"RVB-q8da^R6A]DjW-9a5MJChkb!;7UN"P$KA=1(t&(c(O>Y's&n$[L&oH.XDqKm+I=kT5g1>iZ.!Im^P0:LFm)+K<8[QKe6l)K13B*6KHCb8uPlp:L?.1#=8s]U:/XO$5HQ_.ZAOAam4Y<j9?2+7eBiW[\0tkA20O3k[StS?SC:Z<A4Z!AJo:s&\ZJl:$70)m@;u@AsD1k8(7HbnJ8+4Q9t]!S-71iWS38B@]?dR-on0uSa'YH<CCedMUM)&p4Qg,kd=,ET3EQ#oQ>JR*6p9FM)G`GMDb]daK<i-BtF-'FdlsR9WL2+.]k&&Tl9i:rVbU57Xujh`;e+HLBd4^7CUj1G(leHkh!CQ><dFBo)^-T01G3+LquU6)1-M8qM;*m<LC?dIDjWr.SSZb5%<eI'rLC^+#3G`""$hm,_[,&_e?nP#9Vqp5ZHb\66G3\hci@gE<MDglU#*in5G?WM"3uA[=FM%(d\Q<c-=.B&*!qikh@8&,m0q]`t5;0G%(J$`t:_g=(Q%J#"UV3+iY=8^B.;n,DZr3kgou/23&j6(-Su*;N>6X88EHh</i+.;O&-2;uomV@u.N]H`;O[j7r=GbnV-a`U,7t/]cnc/kA4r5KW(iqI.);!(BP'(]~>endstream
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1876
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau`U>E;rH&:WMDiO<VIiHfnG,9^kq[D3PK+0)7P,4(2/(KsG\^YeRlEGUZOnC"D$=Qb(hN,RUtS66sCJ"ZR\og[$C"o7B`#]R(o-6T95MaoO-h#2X+'p;cNUm@HO##n/;OGC#BnTt4ULDHO;MIt^;K<-cJ]t+#(=_nS'f/9]1]I/X+AF[JtqA"K@S/K?qB@;$;!<<6[;?/GqqKMoVph=R7[F/#9mdR%gP\ER4\0<VY$aO-'k7;dH]G=amWk`>8L7:+!34B#1g?@[)$JjQ(^LQE9@CRnk?.D>I.]'Plk2+FMgHSp>=0Z%g/L2(J'!5@bqKNd2-2DjO#5e)lWNkM`!#Pmb_[4S3.iR,Ti-so5M30CbduWm)rUR8I,rgWVadaQD[n)_UiNPD7W(p'O1JO-2`2J(0*&n[&&=iBTo=3P)9Au_A8r:p8@Q&WO:<@H;8s([42Y=g'Qlb9NXqMe1LM'?F#fIfq]&/TflP)ogMc/d.,^SC[8U+/^1HP(p`_4'^KVq@q.\jR;W?-ZpaYN.oN.]C0n"/,m147iP8R/fD:Y^&U,fb^L],f-eMSlEW,2pTi86Sm8ULP2nhu;?r[`eI>*_CUbWq/R@*[Eisk,:)qe.X.7oh.W1$FQpgme4S!JqP9^'G!@O=3Kdij_"7U=;-RD3nq2lV?_rNY4.TiIHBlGB;fpC]4[<D6;+!*Wp?gZ-SagiPNrO@+B],e5/1@aaU67$VSa(OUmqkFe3q>6hI3o=j4uoXaE*%%M@eX,]d>=Mat^).B[]Y512%pI>\9`hEnm9\VLu^@Hs1DHldtg/?[4(@.<@>d3)B=Pd29;2o[E5n]_bCE\3D(K,P^Gj<GOsi_i*#N%FGMM#@*ImE*<Zm.GY.X%06u5?k*JKM=&<GNFBV=6\1rG`.\q(@OO1/Bku($.Ni6ni[3EojGj:,52KoYX`Kf.5Z>j:[V;T.2Q6PQBSK_)*tm-A$+.Qn*-*C/S]+3&jZnf9=?O*7clAUN#dK>$VA/r2Ta&m$L@`7-,P&'GDHH.R<ZUkc7AWW(ZH"dI2S4\OBYgTn8d:>`5*Goodr7&[9lA?nYTUTnK#VLK9<:Hc1=IrH-MZL;1)*QS+:([/_RYJA?EYMQ"6HK`&&laMS.\qu!:Ykg;FL_@#sM@,<KbUp^>b$%P^-C2c/IZMT@7Os7Y]Vb5h-^e)8lY//Q.j?/_QZ)e=&OUbn2N\8ma(VINn&VU(*dUdHpTkH2Or$/@RPD(6AK/MkPgL$PlDDNh;`(N$aJAm,@KXGVn[B,1p9X6t!R,DTT%$/t%P")`"WY9a.S$YTY046:Lbq`1G5pp_d_K/C#t&nW$6GH.Y`d3S7N4=kH?D3EV8BS$BUde-Ru?lgRR1j?W3C>gtYM'u$E":5.U9$%BQa27h0R8gEo.:]oot3a%Cg,Q-1X*&btZU^F+Y=-^TO&^d*'g32>FJ!uQeItmdtdqII)05cneYT+'rl6Lml8_%'&'2(2nBMN\bhm"62:.,5#Yp_c-?r%'jJmLg]q:ll+a03r"j`$7M>-qGb9!gV21:K_c,k:#hf56i<.]qi@la3,!qNgY)q"OY+<=Bki,6!)cff+0U/\8?U(Yr[uhO8n1oB;I`$OggaAAM1[K9*5mp6oml&^rkB0hmT&LhRp\R&2Nb`&*Ni9[e>P@[]e5``"Q:q&*8OBYo9)n)K-meDhBt+6(#l><o3."Q>a6Ht0VS;iP:CE(+-AJ5kPGJdV.pc#U8S3._G6G48d/&#mT(`5n[D=?rm+"HiIZko50p;NG=DE^e`dJ5kP7U6mA*FWOGA@u)s1c`DXriu$e)RP#W2is;Bab4Jsh&#mT(`0d:4<'[I'I!`-\nr9@&p/^2R;Z>q_o`P5XI/GXWaPD>=,a!B~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 10
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000073 00000 n
|
||||||
|
0000000104 00000 n
|
||||||
|
0000000211 00000 n
|
||||||
|
0000000414 00000 n
|
||||||
|
0000000617 00000 n
|
||||||
|
0000000685 00000 n
|
||||||
|
0000000968 00000 n
|
||||||
|
0000001033 00000 n
|
||||||
|
0000002948 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<0442b5015a4ca0c7bacf2f195742da29><0442b5015a4ca0c7bacf2f195742da29>]
|
||||||
|
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
|
||||||
|
|
||||||
|
/Info 6 0 R
|
||||||
|
/Root 5 0 R
|
||||||
|
/Size 10
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
4915
|
||||||
|
%%EOF
|
11307
xmls/Bologna_2025124.xml
Normal file
11307
xmls/Bologna_2025124.xml
Normal file
File diff suppressed because it is too large
Load diff
8918
xmls/ParmaColorno1_2025122.xml
Normal file
8918
xmls/ParmaColorno1_2025122.xml
Normal file
File diff suppressed because it is too large
Load diff
9598
xmls/ParmaColorno2_2025123.xml
Normal file
9598
xmls/ParmaColorno2_2025123.xml
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue