TrofeoGP_2025/pyiofload/__init__.py
2025-03-26 15:39:35 +01:00

396 lines
15 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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])