Initial Commit

This commit is contained in:
ddnthemc 2025-03-26 15:39:35 +01:00
commit 00ee3b70ef
18 changed files with 31446 additions and 0 deletions

3
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

12
.idea/Trofeo_GP_2025.iml generated Normal file
View 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>

View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

105
merge_races.py Normal file
View 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
View 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

Binary file not shown.

142
trophyPDF/__init__.py Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff