diff --git a/otime/otime.py b/otime/otime.py index 55eb183..e6431d7 100644 --- a/otime/otime.py +++ b/otime/otime.py @@ -1,19 +1,9 @@ -from copy import copy import datetime -import csv import re -import json -import tomllib -import tomlkit -import io -import xml.etree.ElementTree as ET -# from fpdf import FPDF +import xml.etree.ElementTree as etree - -# The event object stores all the event data. -# A .otime file is more or less just a json dump of the Event object. class Event: - def __init__(self, eventid, name, start_time=None, end_time=None, + def __init__(self, eventid, name, git astart_time=None, end_time=None, organiser=None, courses=[], o_classes=[], runners=[], card_dumps=[], fees=[]): self.id = eventid @@ -27,52 +17,8 @@ class Event: self.card_dumps = card_dumps self.fees = fees - def from_toml(f): - cfg = tomllib.load(f) - courses = [] - for c in cfg['course']: - courses.append(Course(c['name'] , c['codes'], c['forked'], c['variations'])) - o_classes = [] - for c in cfg['classes']: - for n in courses: - if n.name == cfg['classes'][c]: - course = n - break - o_classes.append(OClass(c, cfg['classes'][c], course)) - - return Event(1, cfg['name'], cfg['start_time'], cfg['end_time'], cfg['organiser'], courses, o_classes, [], [], []) - - def create_toml(self, dir='./'): - doc = tomlkit.document() - cdicts = [] - for course in self.courses: - cdicts.append(course.asdict()) - #Fjern None fra dictene - for c in cdicts: - if c['forked'] == False: - c['variations'] = [] - - - ocdicts = [] - for o_class in self.o_classes: - ocdicts.append(o_class.asdict()) - - fdicts = [] - for fee in self.fees: - fdicts.append(fee.asdict()) - - data = { - 'id': self.id, - 'name': self.name, - 'organiser': self.organiser, - 'start_time': self.start_time.isoformat(), - 'end_time': self.end_time.isoformat(), - 'course': cdicts, - #'o_classes': ocdicts, - #'fees': fdicts - } - with open(dir + 'cfg.toml', "w") as f: - tomlkit.dump(data, f) + def __repr__(self): + return(f'id({self.id}), name({self.name}), organiser({self.organiser}), courses({self.courses}), o_classes({self.o_classes}), runners({self.runners})') def add_course(self, *args): for n in args: @@ -89,11 +35,21 @@ class Event: def add_fees(self, *args): for n in args: self.fees.append(n) + + def get_course(self, name): + pass - def import_xml_entries(self, xml_file): + def get_o_class(self, name): + pass + + def get_runner(self, name): + pass + + def read_xml_entries(self, xml_file): self.add_runners(*runners_from_xml_entries(xml_file)) self.add_fees(*fees_from_xml_entries(xml_file)) + # Må endres def from_xml_entries(xml_file): tree = ET.parse(xml_file) root = tree.getroot() @@ -117,62 +73,23 @@ class Event: return Event(event_id, name, organiser=organiser, runners=runners, fees=fees, start_time=start_time, end_time=end_time) - def import_xml_courses(self, xml_file): + + def read_xml_courses(self, xml_file): self.courses = courses_from_xml(xml_file) - def import_ttime_cnf(self, ttime_file): + def read_ttime_cnf(self, ttime_file): self.add_course(*courses_from_ttime_conf(ttime_file)) - if isinstance(ttime_file, io.TextIOBase): - ttime_file.seek(0) self.add_o_class(*classes_from_ttime_conf(ttime_file, self.courses)) - def import_ttime_db(self, ttime_file): - if type(ttime_file) == str: - f_list = ttime_file.splitlines() - elif isinstance(ttime_file, io.TextIOBase): - f_list = csv.reader(ttime_file, delimiter=';',) - runnerarray = [] - for row in f_list: - if len(row) == 0 or row[1] == '': - continue - runnerarray.append(Runner.from_string(row, self.o_classes)) - self.runners = runnerarray + def read_ttime_db(self, ttime_file): + with open(ttime_file) as f: + data = f.readlines() + self.runners = [Runner.from_ttime_string(i) for i in data if i] - def import_mtr_file(self, mtr_file): + def read_mtr_file(self, mtr_file): self.card_dumps = CardDump.list_from_mtr_f(mtr_file) - def match_runners_cards(self): - for r in self.runners: - for d in self.card_dumps: - if r.card == d.card: - r.card_r = d - - def match_runners_o_classes(self): - for r in self.runners: - for c in self.o_classes: - if r.o_class_str == c.name: - r.o_class = c - - def match_o_classes_courses(self): - for oc in self.o_classes: - for c in self.courses: - if oc.course_str == c.name: - oc.course = c - - def match_runners_fees(self): - for r in self.runners: - for f in self.fees: - if r.fee_id == f.id: tree = ET.parse(xml_file) - root = tree.getroot() - r.fee = f - - def match_all(self): - self.match_runners_cards() - self.match_runners_o_classes() - self.match_o_classes_courses() - self.match_runners_fees() - - def get_xml_res(self): + def get_result_xml(self): root = ET.Element('ResultList') root.set('xmlns', 'http://www.orienteering.org/datastandard/3.0') root.set('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance') @@ -258,276 +175,10 @@ class Event: ET.indent(root, space=' ', level=0) return tree - def create_json_file(self): - rdicts = [] - for runner in self.runners: - rdicts.append(runner.asdict()) - - cdicts = [] - for course in self.courses: - cdicts.append(course.asdict()) - - ocdicts = [] - for o_class in self.o_classes: - ocdicts.append(o_class.asdict()) - - ddicts = [] - for dump in self.card_dumps: - ddicts.append(dump.asdict()) - - fdicts = [] - for fee in self.fees: - fdicts.append(fee.asdict()) - - json_data = { - 'id': self.id, - 'name': self.name, - 'orginser': self.organiser, - 'start_time': self.start_time.isoformat(), - 'end_time': self.end_time.isoformat(), - 'runners': rdicts, - 'courses': cdicts, - 'o_classes': ocdicts, - 'card_dumps': ddicts, - 'fees': fdicts - } - return json.dumps(json_data, sort_keys=True, indent=4) - - # Get event object from .otime json file - def from_json(f): - data = json.load(f) - runners = [] - for r in data['runners']: - runners.append(Runner(r['id'], r['first'], r['last'], club_id=r['club_id'], - club=r['club'], country=r['country'], card=r['card'], - o_class_str=r['o_class_str'], fork=r['fork'], - start_time=r['start_time'], fee_id=int(r['fee_id']))) - - courses = [] - for c in data['courses']: - courses.append(Course(c['name'], c['codes'], forked=c['forked'], - variations=c['variations'])) - - o_classes = [] - for c in data['o_classes']: - o_classes.append(OClass(c['name'], c['course_str'], None)) - - card_dumps = [] - for d in data['card_dumps']: - card_dumps.append(CardDump(d['card'], d['controls'], d['splits'], - datetime.datetime.fromisoformat(d['read_time']), - datetime.datetime.fromisoformat(d['s_time']), - datetime.datetime.fromisoformat(d['f_time']))) - - fees = [] - for f in data['fees']: - fees.append(Fee(f['id'], f['name'], f['currency'], f['amount'], - from_birth_date=f['from_birth_date'], - to_birth_date=f['to_birth_date'])) - - return Event(data['id'], data['name'], organiser=data['orginiser'], - start_time=datetime.datetime.fromisoformat(data['start_time']), - end_time=datetime.datetime.fromisoformat(data['end_time']), - runners=runners, courses=courses, o_classes=o_classes, - card_dumps=card_dumps, fees=fees) - - def create_start_list_pdf(self, file_name): - pdf = FPDF() - pdf.add_page() - pdf.add_font("LiberationSans", fname="data/fonts/LiberationSans-Regular.ttf") - pdf.set_font("LiberationSans", size=10) - line_height = pdf.font_size * 2 - col_width = pdf.epw / 4 # distribute content evenly - for runner in self.runners: - pdf.multi_cell(col_width, line_height, runner.fullname(), border=1, ln=3, max_line_height=pdf.font_size, align='L') - pdf.multi_cell(col_width, line_height, runner.o_class_str, border=1, ln=3, max_line_height=pdf.font_size) - pdf.multi_cell(col_width, line_height, str(runner.card), border=1, ln=3, max_line_height=pdf.font_size) - if runner.start_time is not None: - pdf.multi_cell(col_width, line_height, str(runner.start_time), border=1, ln=3, max_line_height=pdf.font_size) - else: - pdf.multi_cell(col_width, line_height, '', border=1, ln=3, max_line_height=pdf.font_size) - pdf.ln(line_height) - pdf.output(file_name) - - def create_all_invoices(self, filename_prefix): - clubs = [x.club for x in self.runners if x.club != self.runners[self.runners.index(x)-1].club and x.club is not None] - for club in clubs: - self.create_club_invoice(club, filename_prefix + club + '.pdf') - - def create_club_invoice(self, club, file_name): - # Get only runners in specified club - runners_ic = [x for x in self.runners if x.club == club] - payments = [x.fee.amount for x in runners_ic] - subtotal = sum(payments) - - # Dict of runners in each fee - fee_dict = {} - for fee in self.fees: - fee_dict.update({fee.name: [x for x in runners_ic if x.fee.name == fee.name]}) - if len(fee_dict[fee.name]) == 0: - fee_dict.pop(fee.name) - - pdf = PDF() - - pdf.set_title(f'Faktura {self.name} {club}') - pdf.set_producer('oTime 0.0.1') - pdf.set_creator('oTime 0.0.1') - pdf.set_creation_date() - - pdf.add_page() - pdf.add_font("LiberationSans", fname="data/fonts/LiberationSans-Regular.ttf") - pdf.add_font("LiberationSans-Bold", fname="data/fonts/LiberationSans-Bold.ttf") - pdf.set_font("LiberationSans", size=10) - line_height = pdf.font_size * 1.5 - - # Topp venstre: - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(pdf.epw / 2, line_height, self.name, align='L') - pdf.ln(0.2) - pdf.set_font("LiberationSans", size=10) - pdf.multi_cell(pdf.epw / 2, line_height, self.organiser, align='L') - pdf.ln(14) - pdf.multi_cell(pdf.epw / 2, line_height, club, align='L') - pdf.ln() - - # Topp høyre: - pdf.set_xy(-pdf.epw / 3, 10) - pdf.set_font("LiberationSans-Bold", size=20) - pdf.multi_cell(pdf.epw, line_height, 'Faktura', align='L') - pdf.set_xy(-pdf.epw / 3, 20) - - # Dato - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(0, line_height, 'Dato:', align='L') - pdf.set_xy(-pdf.epw / 3, 20) - pdf.set_font("LiberationSans", size=10) - pdf.multi_cell(0, line_height, datetime.date.today().strftime('%d.%m.%Y'), align='R') - - # Nummer - pdf.set_xy(-pdf.epw / 3, 20 + line_height) - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(0, line_height, 'Nummer:', align='L') - pdf.set_xy(-pdf.epw / 3, 20 + line_height) - pdf.multi_cell(0, line_height, '1', align='R') - - # Kundeid - pdf.set_xy(-pdf.epw / 3, 20 + line_height*2) - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(0, line_height, 'Kundeid:', align='L') - pdf.set_xy(-pdf.epw / 3, 20 + line_height*2) - pdf.set_font("LiberationSans", size=10) - pdf.multi_cell(0, line_height, '123', align='R') - - # Forfall - pdf.set_xy(-pdf.epw / 3, 20 + line_height*3) - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(0, line_height, 'Forfall:', align='L') - pdf.set_xy(-pdf.epw / 3, 20 + line_height*3) - pdf.set_font("LiberationSans", size=10) - pdf.multi_cell(0, line_height, datetime.date.today().strftime('%d.08.%Y'), align='R') - - pdf.set_xy(-pdf.epw, 20 + line_height*5) - pdf.set_font("LiberationSans-Bold", size=10) - date = self.start_time.strftime('%d.%m.%Y') - pdf.multi_cell(0, line_height, f'Kontingentliste {self.name} dato: {date}', align='R') - - pdf.ln() - - pdf.set_font("LiberationSans", size=10) - # Tabell - line_height = pdf.font_size * 2 - col_width = pdf.epw / 8 # distribute content evenly - # Top row - pdf.set_fill_color(191, 191, 191) - pdf.set_draw_color(191, 191, 191) - pdf.set_font("LiberationSans-Bold", size=10) - pdf.multi_cell(col_width, line_height, 'Startnr', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width*2, line_height, 'Navn', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width, line_height, 'Klasse', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width, line_height, 'Brikke', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - # pdf.multi_cell(col_width, line_height, 'Starttid', border=1, ln=3,max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width, line_height, 'Resultat', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width, line_height, 'Plass', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.multi_cell(col_width, line_height, 'Kontigent', border=1, ln=3, max_line_height=pdf.font_size, align='L', fill=True) - pdf.ln() - pdf.set_draw_color(0, 0, 0) - for runners in fee_dict.values(): - pdf.set_font("LiberationSans-Bold", size=11) - pdf.multi_cell(len(runners[0].fee.name)*2.3, pdf.font_size * 1.1, runners[0].fee.name, border='B', align='L') - pdf.set_font("LiberationSans", size=8) - line_height = pdf.font_size * 1.6 - pdf.ln() - for runner in runners: - pdf.multi_cell(col_width, line_height, runner.id, ln=3, max_line_height=pdf.font_size, align='L') # Start Number - pdf.multi_cell(col_width*2, line_height, f'{runner.last}, {runner.first}', ln=3, max_line_height=pdf.font_size, align='L') # Name - pdf.multi_cell(col_width, line_height, runner.o_class_str, ln=3, max_line_height=pdf.font_size, align='L') # Class - pdf.multi_cell(col_width, line_height, str(runner.card), ln=3, max_line_height=pdf.font_size) # card - # Starttime: - #if runner.start_time != None: - # pdf.multi_cell(col_width, line_height, str(runner.start_time), ln=3, max_line_height=pdf.font_size) - #else: - # pdf.multi_cell(col_width, line_height, 'Fristart', ln=3, max_line_height=pdf.font_size) - # Time used: - if runner.status() == 'OK': - pdf.multi_cell(col_width, line_height, str(datetime.timedelta(seconds=runner.totaltime())), ln=3, max_line_height=pdf.font_size) - elif runner.status() == 'Disqualified': - pdf.multi_cell(col_width, line_height, 'DSQ', ln=3, max_line_height=pdf.font_size) - elif runner.status() == 'Active': - pdf.multi_cell(col_width, line_height, 'DNS', ln=3, max_line_height=pdf.font_size) - else: - pdf.multi_cell(col_width, line_height, runner.status(), ln=3, max_line_height=pdf.font_size) - - # Rank: - if runner.status() == 'OK': - pdf.multi_cell(col_width, line_height, str(runner.rank(self.runners)) + '.', ln=3, max_line_height=pdf.font_size) - else: - pdf.multi_cell(col_width, line_height, '', ln=3, max_line_height=pdf.font_size) - - pdf.multi_cell(col_width, line_height, str(runner.fee.amount), ln=3, max_line_height=pdf.font_size, align='R') - pdf.ln(line_height) - - pdf.set_draw_color(0, 0, 0) - # sum - pdf.set_font("LiberationSans-Bold", size=10) - pdf.set_x(col_width*8) - pdf.cell(0, line_height, 'Sum ' + str(subtotal), border='TB', align='R') - - pdf.set_font("LiberationSans", size=10) - line_height = pdf.font_size * 1.5 - pdf.ln(20) - pdf.multi_cell(0, line_height, 'Antall løpere ' + str(len(runners_ic)), ln=3, max_line_height=pdf.font_size, align='L') - - pdf.set_x(col_width*7) - pdf.multi_cell(col_width, line_height, 'Total sum', ln=3, max_line_height=pdf.font_size, align='L') - pdf.multi_cell(0, line_height, str(subtotal), ln=3, max_line_height=pdf.font_size, align='R') - pdf.ln() - pdf.set_x(col_width*7) - pdf.multi_cell(col_width, line_height, 'Betalt', ln=3, max_line_height=pdf.font_size, align='L') - pdf.multi_cell(0, line_height, '0', ln=3, max_line_height=pdf.font_size, align='R') - pdf.ln() - pdf.set_font("LiberationSans-Bold", size=10) - pdf.set_x(col_width*7) - pdf.multi_cell(col_width, line_height, 'Skyldig', border='B', ln=3, max_line_height=pdf.font_size, align='L') - pdf.multi_cell(0, line_height, str(subtotal), border='B',ln=3, max_line_height=pdf.font_size, align='R') - - - pdf.output(file_name) -""" -class PDF(FPDF): - def footer(self): - self.set_y(-15) - self.set_font("LiberationSans", size=10) - self.set_fill_color(191, 191, 191) - self.set_draw_color(191, 191, 191) - col_width = self.epw / 3 - self.cell(col_width, 7, 'oTime', border=1, align='L', fill=True) - self.cell(col_width, 7, datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S'), border=1, align='C', fill=True) - self.cell(col_width, 7, f"Side {self.page_no()} av {{nb}}", border=1, align='R', fill=True) -""" # The runner object stores all the data specific to a runner. class Runner: - def __init__(self, runner_id, first, last, club=None, club_id=None, - country=None, card=None, o_class_str=None, o_class=None, + def __init__(self, runner_id: int, first: str, last: str, club=None, club_id=None, + country=None, card_id=None, o_class_str=None, o_class=None, fork=0, start_time=None, fee_id=None, fee=None): self.id = runner_id self.first = first @@ -535,48 +186,40 @@ class Runner: self.club = club self.club_id = club_id self.country = country - self.card = card - self.o_class_str = o_class_str + self.card_id = card_id self.o_class = o_class self.fork = fork self.start_time = start_time self.fee_id = fee_id - self.fee = fee - - def from_string(tt_line, o_classes): + + def __repr__(self): + return(f'name({self.fullname()})') + + def from_ttime_string(ttime_db_line): + # Reads 1 line of a ttime db #https://web.archive.org/web/20191229124046/http://wiki.ttime.no/index.php/Developer - eventorid = tt_line[0] + runner_data = ttime_db_line.split(';') + eventorid = runner_data[0] country = '' - name = tt_line[2].split(',') + name = runner_data[2].split(',') try: first = name[1].strip() except: first = '' last = name[0] try: - club = tt_line[4] + club = runner_data[4] except: - club = "None" + club = '' try: - card = int(tt_line[6]) + card = int(runner_data[6]) except: card = 0 - runner_o_class = None try: - raw_class_str = tt_line[3] + o_class = runner_data[3] except: - # VELDIG MIDLERTIDIG - runner_o_class = None - else: - if raw_class_str != '': - for i in o_classes: - if i.name == raw_class_str: - runner_o_class = i - break - else: - runner_o_class = None - # TODO: Gjør sånn at den lager nye o klasser om den ikke finnes fra før - options = tt_line[5].split(',') + o_class = '' + options = runner_data[5].split(',') try: club_id = options[options.index('A')+3] except: @@ -586,8 +229,7 @@ class Runner: except: start_time = None return Runner(eventorid, first, last, club=club, club_id=club_id, - country=country, card=card, o_class_str=raw_class_str, - o_class=runner_o_class, start_time=start_time) + country=country, card_id=card, o_class=o_class, start_time=start_time) def fullname(self): return '{} {}'.format(self.first, self.last) @@ -727,18 +369,13 @@ class CardDump: return(CardDump(card, controls, splits, read_time, s_time, f_time)) - def list_from_mtr_f(mtr_f): - if type(mtr_f) == str: - f_list = mtr_f.splitlines() - - rows = [] + def list_from_mtr_f(mtr_file): + with open(mtr_file) as f: + data = [i.replace('"','').split(',') for i in f.readlines()] + print(data) cards = [] # hver rad er brikkenummer med tilhørende info - csvreader = csv.reader(f_list) - for row in csvreader: - if len(row) == 0: - continue - rows.append(row) + for row in data: controls = [] splits = [] # postkodene kommer på oddetall fra og med den 11. De blir hevet inn i controls @@ -809,7 +446,6 @@ class Course: class OClass: def __init__(self, name, course_str, course): self.name = name - self.course_str = course_str self.course = course def __repr__(self): @@ -843,13 +479,10 @@ class Fee: # TODO: Take string instead of file. def courses_from_ttime_conf(ttime_file): - if type(ttime_file) == str: - conf = ttime_file.splitlines() - elif isinstance(ttime_file, io.TextIOBase): - conf = ttime_file.readlines() - + with open(ttime_file, 'r') as f: + data = f.readlines() courses = [] - for line in conf: + for line in data: if '-codes' in line: code_list = re.search(r'(?<=\")(.*?)(?=\")', line).group().split(';') loops = 0 @@ -861,12 +494,10 @@ def courses_from_ttime_conf(ttime_file): return courses def classes_from_ttime_conf(ttime_file, courses): - if type(ttime_file) == str: - conf = ttime_file.splitlines() - elif isinstance(ttime_file, io.TextIOBase): - conf = ttime_file.readlines() + with open(ttime_file, 'r') as f: + data = f.readlines() o_classes = [] - for line in conf: + for line in data: if '-courses' in line: raw_courselist = re.search(r'(?<=\")(.*?)(?=\")', line).group().split(';') loops = 0 @@ -1009,5 +640,3 @@ def rank_runners(allrunners, o_class): def xml_child(parent, tag, content): e = ET.SubElement(parent, tag) e.text = str(content) - -