diff --git a/otime/data/fonts/LiberationSans-Bold.ttf b/otime/data/fonts/LiberationSans-Bold.ttf new file mode 100644 index 0000000..dc5d57f Binary files /dev/null and b/otime/data/fonts/LiberationSans-Bold.ttf differ diff --git a/otime/data/fonts/LiberationSans-Regular.ttf b/otime/data/fonts/LiberationSans-Regular.ttf new file mode 100644 index 0000000..22bae91 Binary files /dev/null and b/otime/data/fonts/LiberationSans-Regular.ttf differ diff --git a/otime/otime.py b/otime/otime.py index e6431d7..b98e3f7 100644 --- a/otime/otime.py +++ b/otime/otime.py @@ -1,9 +1,313 @@ +import copy import datetime import re import xml.etree.ElementTree as etree +import pdf + +class Runner: + 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 + self.last = last + self.club = club + self.club_id = club_id + self.country = country + self.card_id = card_id + self.o_class = o_class + self.fork = fork + self.start_time = start_time + self.fee_id = fee_id + + 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 + runner_data = ttime_db_line.split(';') + eventorid = runner_data[0] + country = '' + name = runner_data[2].split(',') + try: + first = name[1].strip() + except: + first = '' + last = name[0] + try: + club = runner_data[4] + except: + club = '' + try: + card = int(runner_data[6]) + except: + card = 0 + try: + o_class = runner_data[3] + except: + o_class = '' + options = runner_data[5].split(',') + try: + club_id = options[options.index('A')+3] + except: + club_id = 0 + try: + start_time = options[options.index('U')+1] + except: + start_time = None + return Runner(eventorid, first, last, club=club, club_id=club_id, + country=country, card_id=card, o_class=o_class, start_time=start_time) + + def fullname(self): + return '{} {}'.format(self.first, self.last) + + def check_codes(self): + return contains(self.res_codes(), self.card_r.controls) + + def totaltime(self): + f_control = self.res_codes()[-1] + try: + index = self.card_r.controls.index(f_control) + return self.card_r.splits[index] + except: + return 0 + + def status(self): + if hasattr(self, 'card_r') is False or self.o_class is None: + return 'Active' + elif self.check_codes(): + return 'OK' + elif self.check_codes() is False: + return 'Disqualified' + + # TODO: må forbedres + def rank(self, allrunners): + c_ranked = rank_runners(allrunners, self.o_class) + try: + return c_ranked.index(self) + 1 + except ValueError: + return None + + def get_codes(self): + if self.o_class.course.forked is False: + return self.o_class.course.codes + else: + return self.o_class.course.variations[self.fork] + + # TODO: Mange bugs med løyper som har samme post flere ganger + # Used for making result files and tables + def get_splits(self): + if self.status() == 'OK': + splits_cpy = self.card_r.splits.copy() + for control in self.card_r.controls: + if control not in self.res_codes(): + index = self.card_r.controls.index(control) + split = self.card_r.splits[index] + splits_cpy.remove(split) + return splits_cpy + + else: + splits_cpy = self.card_r.splits.copy() + for control in self.card_r.controls: + if control not in self.res_codes(): + index = self.card_r.controls.index(control) + split = self.card_r.splits[index] + try: + splits_cpy.remove(split) + except Exception: + print('aaaaaa') + + + punches = self.card_r.controls.copy() + splits = [] + for code in self.res_codes(): + if punches[0] == code: + splits.append(splits_cpy[0]) + splits_cpy.pop(0) + punches.pop(0) + continue + else: + splits.append(None) + return splits + + def asdict(self): + return { + 'id': self.id, + 'first': self.first, + 'last': self.last, + 'club_id': self.club_id, + 'club': self.club, + 'country': self.country, + 'card': self.card, + 'o_class_str': self.o_class_str, + 'fork' : self.fork, + 'start_time': self.start_time, + 'fee_id': self.fee_id + } + + + +class CardDump: + def __init__(self, card, controls, splits, read_time, s_time, f_time): + self.card = card + self.controls = controls + self.splits = splits + self.read_time = read_time + self.s_time = s_time + self.f_time = f_time + + def __repr__(self): + return f'card({self.card}) controls({self.controls}) splits({self.splits})' + + def from_mtr_bytes(datamsg): + card = int.from_bytes(datamsg[20:23], 'little') + + #Extract codes and splits: + controls = [] + splits = [] + splits_offset = 26 + code_numbytes = 1 + time_numbytes = 2 + split_numbytes = code_numbytes + time_numbytes + for split_index in range(50): + code_offset = splits_offset + split_index * split_numbytes + time_offset = code_offset + code_numbytes + code = int.from_bytes(datamsg[code_offset:code_offset+code_numbytes], 'little') + time = int.from_bytes(datamsg[time_offset:time_offset+time_numbytes], 'little') + if code != 0: + controls.append(code) + splits.append(time) + + # Extract start time: + year = int.from_bytes(datamsg[8:9], 'little') + month = int.from_bytes(datamsg[9:10], 'little') + day = int.from_bytes(datamsg[10:11], 'little') + hours = int.from_bytes(datamsg[11:12], 'little') + minutes = int.from_bytes(datamsg[12:13], 'little') + seconds = int.from_bytes(datamsg[13:14], 'little') + milliseconds = int.from_bytes(datamsg[14:16], 'little') + + read_time = datetime.datetime(year, month, day, hours, minutes, seconds, milliseconds) + if len(controls) > 2: + s_time = read_time - datetime.timedelta(seconds=splits[-1]) + f_time = read_time - (datetime.timedelta(seconds=splits[-1]) + datetime.timedelta(seconds=splits[-2])) + else: + s_time = read_time + f_time = read_time + + return(CardDump(card, controls, splits, read_time, s_time, f_time)) + + def list_from_mtr_f(mtr_file): + with open(mtr_file) as f: + data = [i.replace('"','').split(',') for i in f.readlines()] + cards = [] + # hver rad er brikkenummer med tilhørende info + for row in data: + controls = [] + splits = [] + # postkodene kommer på oddetall fra og med den 11. De blir hevet inn i controls + for item in row[11::2]: + if item == '250': + controls.append(int(item)) + break + elif item == '000': + break + else: + controls.append(int(item)) + # strekktidene kommer på partall fra og med den 12. De blir hevet i splits. + for item in row[12::2]: + if item == '00000': + break + else: + splits.append(int(item)) + # looper gjonnom løperobjektene og legger til poster og strekktider + start og sluttid + # usikker på om dette er riktig klokeslett + tl = row[5].split(' ') + tl[0] = tl[0].split('.') + tl[0][2] = '20' + tl[0][2] + tl[0] = list(map(int, tl[0])) + tl[1] = tl[1].split(':') + tl[1][2] = float(tl[1][2]) + tl[1] = list(map(int, tl[1])) + read_time = datetime.datetime(tl[0][2], tl[0][1], tl[0][0], tl[1][0], tl[1][1], tl[1][2]) + if len(controls) > 2 and len(splits) > 2: + s_time = read_time - datetime.timedelta(seconds=splits[-1]) + f_time = read_time - (datetime.timedelta(seconds=splits[-1]) + datetime.timedelta(seconds=splits[-2])) + else: + s_time = read_time + f_time = read_time + cards.append(CardDump(int(row[6]), controls, splits, read_time, s_time, f_time)) + return cards + + def asdict(self): + return { + 'card': self.card, + 'controls': self.controls, + 'splits': self.splits, + 'read_time': self.read_time.isoformat(), + 's_time': self.s_time.isoformat(), + 'f_time': self.f_time.isoformat() + } + +# Stored in Event.courses +class Course: + def __init__(self, name, codes, forked=False, variations=None): + self.name = name + self.codes = codes + self.forked = forked + # Variations is a list + self.variations = variations + + def __repr__(self): + return f'name({self.name})' + + def asdict(self): + return { + 'name': self.name, + 'codes': self.codes, + 'forked': self.forked, + 'variations': self.variations + } + +# Stored in Event.o_classes +class OClass: + def __init__(self, name, course): + self.name = name + self.course = course + + def __repr__(self): + return f'name({self.name})' + +class RunnerResult: + def __init__(self, runner_id: int, first: str, last: str, status: str, place: int, total_time: int, split_times: list[int], end_time, + club=None, club_id=None, country=None, card_id=None, o_class=None, fork=0, start_time=None, fee_id=None): + self.id = runner_id + self.first = first + self.last = last + self.club = club + self.club_id = club_id + self.country = country + self.card_id = card_id + self.o_class = o_class + self.fork = fork + self.start_time = start_time + self.fee_id = fee_id + + self.status = status + self.place = place + self.total_time = total_time + self.split_times = split_times + self.end_time = end_time + +class ClassResult: + def __init__(self, name: str, course: Course, runner_results: list[RunnerResult]): + self.name + self.course + self.runner_results class Event: - def __init__(self, eventid, name, git astart_time=None, end_time=None, + def __init__(self, eventid, name, start_time=None, end_time=None, organiser=None, courses=[], o_classes=[], runners=[], card_dumps=[], fees=[]): self.id = eventid @@ -31,6 +335,9 @@ class Event: def add_runners(self, *args): for n in args: self.runners.append(n) + + def get_runners_in_o_class(self, o_class: str) -> list[Runner]: + return [i for i in self.runners if i.o_class == o_class] def add_fees(self, *args): for n in args: @@ -40,10 +347,15 @@ class Event: pass def get_o_class(self, name): - pass + return next((copy.copy(i) for i in self.o_classes if i.name == name), None) def get_runner(self, name): pass + + def get_result(self, o_classes: list[str] = []) -> list[ClassResult]: + if not o_classes: + o_classes = [i.name for i in self.o_classes] + return [produce_class_result(self, i) for i in o_classes] def read_xml_entries(self, xml_file): self.add_runners(*runners_from_xml_entries(xml_file)) @@ -89,7 +401,13 @@ class Event: def read_mtr_file(self, mtr_file): self.card_dumps = CardDump.list_from_mtr_f(mtr_file) - def get_result_xml(self): + def create_start_list_pdf(self, file_path): + pdf.create_start_list(copy.deepcopy(self), file_path) + + def create_result_pdf(self, file_path): + pdf.create_result_list(copy.deepcopy(self), file_path) + + def create_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') @@ -176,286 +494,6 @@ class Event: return tree # The runner object stores all the data specific to a runner. -class Runner: - 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 - self.last = last - self.club = club - self.club_id = club_id - self.country = country - self.card_id = card_id - self.o_class = o_class - self.fork = fork - self.start_time = start_time - self.fee_id = fee_id - - 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 - runner_data = ttime_db_line.split(';') - eventorid = runner_data[0] - country = '' - name = runner_data[2].split(',') - try: - first = name[1].strip() - except: - first = '' - last = name[0] - try: - club = runner_data[4] - except: - club = '' - try: - card = int(runner_data[6]) - except: - card = 0 - try: - o_class = runner_data[3] - except: - o_class = '' - options = runner_data[5].split(',') - try: - club_id = options[options.index('A')+3] - except: - club_id = 0 - try: - start_time = options[options.index('U')+1] - except: - start_time = None - return Runner(eventorid, first, last, club=club, club_id=club_id, - country=country, card_id=card, o_class=o_class, start_time=start_time) - - def fullname(self): - return '{} {}'.format(self.first, self.last) - - def check_codes(self): - return contains(self.res_codes(), self.card_r.controls) - - def totaltime(self): - f_control = self.res_codes()[-1] - try: - index = self.card_r.controls.index(f_control) - return self.card_r.splits[index] - except: - return 0 - - def status(self): - if hasattr(self, 'card_r') is False or self.o_class is None: - return 'Active' - elif self.check_codes(): - return 'OK' - elif self.check_codes() is False: - return 'Disqualified' - - # TODO: må forbedres - def rank(self, allrunners): - c_ranked = rank_runners(allrunners, self.o_class) - try: - return c_ranked.index(self) + 1 - except ValueError: - return None - - def res_codes(self): - if self.o_class.course.forked is False: - return self.o_class.course.codes - else: - return self.o_class.course.variations[self.fork] - - # TODO: Mange bugs med løyper som har samme post flere ganger - # Used for making result files and tables - def res_splits(self): - if self.status() == 'OK': - splits_cpy = self.card_r.splits.copy() - for control in self.card_r.controls: - if control not in self.res_codes(): - index = self.card_r.controls.index(control) - split = self.card_r.splits[index] - splits_cpy.remove(split) - return splits_cpy - - else: - splits_cpy = self.card_r.splits.copy() - for control in self.card_r.controls: - if control not in self.res_codes(): - index = self.card_r.controls.index(control) - split = self.card_r.splits[index] - try: - splits_cpy.remove(split) - except Exception: - print('aaaaaa') - - - punches = self.card_r.controls.copy() - splits = [] - for code in self.res_codes(): - if punches[0] == code: - splits.append(splits_cpy[0]) - splits_cpy.pop(0) - punches.pop(0) - continue - else: - splits.append(None) - return splits - - def asdict(self): - return { - 'id': self.id, - 'first': self.first, - 'last': self.last, - 'club_id': self.club_id, - 'club': self.club, - 'country': self.country, - 'card': self.card, - 'o_class_str': self.o_class_str, - 'fork' : self.fork, - 'start_time': self.start_time, - 'fee_id': self.fee_id - } - - -class CardDump: - def __init__(self, card, controls, splits, read_time, s_time, f_time): - self.card = card - self.controls = controls - self.splits = splits - self.read_time = read_time - self.s_time = s_time - self.f_time = f_time - - def __repr__(self): - return f'card({self.card}) controls({self.controls}) splits({self.splits})' - - def from_mtr_bytes(datamsg): - card = int.from_bytes(datamsg[20:23], 'little') - - #Extract codes and splits: - controls = [] - splits = [] - splits_offset = 26 - code_numbytes = 1 - time_numbytes = 2 - split_numbytes = code_numbytes + time_numbytes - for split_index in range(50): - code_offset = splits_offset + split_index * split_numbytes - time_offset = code_offset + code_numbytes - code = int.from_bytes(datamsg[code_offset:code_offset+code_numbytes], 'little') - time = int.from_bytes(datamsg[time_offset:time_offset+time_numbytes], 'little') - if code != 0: - controls.append(code) - splits.append(time) - - # Extract start time: - year = int.from_bytes(datamsg[8:9], 'little') - month = int.from_bytes(datamsg[9:10], 'little') - day = int.from_bytes(datamsg[10:11], 'little') - hours = int.from_bytes(datamsg[11:12], 'little') - minutes = int.from_bytes(datamsg[12:13], 'little') - seconds = int.from_bytes(datamsg[13:14], 'little') - milliseconds = int.from_bytes(datamsg[14:16], 'little') - - read_time = datetime.datetime(year, month, day, hours, minutes, seconds, milliseconds) - if len(controls) > 2: - s_time = read_time - datetime.timedelta(seconds=splits[-1]) - f_time = read_time - (datetime.timedelta(seconds=splits[-1]) + datetime.timedelta(seconds=splits[-2])) - else: - s_time = read_time - f_time = read_time - - return(CardDump(card, controls, splits, read_time, s_time, f_time)) - - 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 - for row in data: - controls = [] - splits = [] - # postkodene kommer på oddetall fra og med den 11. De blir hevet inn i controls - for item in row[11::2]: - if item == '250': - controls.append(int(item)) - break - elif item == '000': - break - else: - controls.append(int(item)) - # strekktidene kommer på partall fra og med den 12. De blir hevet i splits. - for item in row[12::2]: - if item == '00000': - break - else: - splits.append(int(item)) - # looper gjonnom løperobjektene og legger til poster og strekktider + start og sluttid - # usikker på om dette er riktig klokeslett - tl = row[5].split(' ') - tl[0] = tl[0].split('.') - tl[0][2] = '20' + tl[0][2] - tl[0] = list(map(int, tl[0])) - tl[1] = tl[1].split(':') - tl[1][2] = float(tl[1][2]) - tl[1] = list(map(int, tl[1])) - read_time = datetime.datetime(tl[0][2], tl[0][1], tl[0][0], tl[1][0], tl[1][1], tl[1][2]) - if len(controls) > 2 and len(splits) > 2: - s_time = read_time - datetime.timedelta(seconds=splits[-1]) - f_time = read_time - (datetime.timedelta(seconds=splits[-1]) + datetime.timedelta(seconds=splits[-2])) - else: - s_time = read_time - f_time = read_time - cards.append(CardDump(int(row[6]), controls, splits, read_time, s_time, f_time)) - return cards - - def asdict(self): - return { - 'card': self.card, - 'controls': self.controls, - 'splits': self.splits, - 'read_time': self.read_time.isoformat(), - 's_time': self.s_time.isoformat(), - 'f_time': self.f_time.isoformat() - } - -# Stored in Event.courses -class Course: - def __init__(self, name, codes, forked=False, variations=None): - self.name = name - self.codes = codes - self.forked = forked - # Variations is a list - self.variations = variations - - def __repr__(self): - return f'name({self.name})' - - def asdict(self): - return { - 'name': self.name, - 'codes': self.codes, - 'forked': self.forked, - 'variations': self.variations - } - -# Stored in Event.o_classes -class OClass: - def __init__(self, name, course_str, course): - self.name = name - self.course = course - - def __repr__(self): - return f'name({self.name})' - - def asdict(self): - return { - 'name': self.name, - 'course_str': self.course_str - } class Fee: def __init__(self, fee_id, name, currency, amount, from_birth_date=None, @@ -477,6 +515,12 @@ class Fee: 'to_birth_date': self.to_birth_date } +def produce_class_result(event, o_class_name) -> ClassResult: + o_class = event.get_o_class(o_class_name) + runners = event.get_runners_in_o_class(o_class_name) + + + # TODO: Take string instead of file. def courses_from_ttime_conf(ttime_file): with open(ttime_file, 'r') as f: @@ -504,7 +548,7 @@ def classes_from_ttime_conf(ttime_file, courses): for n in raw_courselist: split = n.split(',') for i in split: - o_classes.append(OClass(i, courses[loops].name, courses[loops])) + o_classes.append(OClass(i, courses[loops].name)) loops += 1 return o_classes @@ -616,16 +660,6 @@ def contains(small, big): else: return False - -def get_runners_in_class(runners, o_class): - # Filters out runner objects that dont have the correct o_class - list_filtrd = [] - for i in runners: - if i.o_class == o_class: - list_filtrd.append(i) - return list_filtrd - - def rank_runners(allrunners, o_class): runners = get_runners_in_class(allrunners, o_class) runners_ranked = [] diff --git a/otime/pdf.py b/otime/pdf.py index cc21221..ab57854 100644 --- a/otime/pdf.py +++ b/otime/pdf.py @@ -1,186 +1,187 @@ -# Denne koden funker ikke! Den er bare revet ut av prsojektet og skal fikses senere from fpdf import FPDF -def create_start_list_pdf(self, file_name): + +def create_start_list(event, file_path): pdf = FPDF() + pdf.add_page() - pdf.add_font("LiberationSans", fname="data/fonts/LiberationSans-Regular.ttf") + pdf.add_font("LiberationSans", fname="../../otime/data/fonts/LiberationSans-Regular.ttf") pdf.set_font("LiberationSans", size=10) - line_height = pdf.font_size * 2 + line_height = pdf.font_size * 1.5 col_width = pdf.epw / 4 # distribute content evenly - for runner in self.runners: + for runner in event.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) + pdf.multi_cell(col_width, line_height, runner.o_class, border=1, ln=3, max_line_height=pdf.font_size) + pdf.multi_cell(col_width, line_height, str(runner.card_id), 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) + pdf.output(file_path) - 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_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) +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) + # 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 = 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.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 + 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') + # 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) - # 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) + # 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) - # 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') + 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) - # 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') + 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') - # 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') + 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') - # 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.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) + pdf.output(file_name) """ class PDF(FPDF): def footer(self): diff --git a/test_files/sc3_23/sc3_test.py b/test_files/sc3_23/sc3_test.py index fe03f61..61242a7 100644 --- a/test_files/sc3_23/sc3_test.py +++ b/test_files/sc3_23/sc3_test.py @@ -7,8 +7,7 @@ def main(): event.read_ttime_cnf('tt.cnf') event.read_ttime_db('db.csv') event.read_mtr_file('mtr.csv') - print(event) - print(event.card_dumps[1]) - + print(event.get_result()) + event.create_start_list_pdf('output/result.pdf') if __name__ == '__main__': main() \ No newline at end of file