from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 from django.conf import settings from datetime import date, datetime, timedelta from django.db.models import Q, Max import os import locale from PIL import Image from ultron.people.models import Gymnast from ultron.followup.models import ( Plan, Chrono, Accident, MindState, HeightWeight, ) from ultron.objective.models import Skill from statistics import mean import os import yaml import re # EXPENSES = 0 # RECETTES = 1 X = 35 Y = 841.89 INDENT = 5 RIGHT_X = 595.27 - X TITLED_X = 125 INDENTED_X = X + INDENT INDENTED_RIGHT_X = RIGHT_X - INDENT MIDDLE = (RIGHT_X - X) / 2 PRESTATION_COLUMN_2 = INDENTED_X + 65 PRESTATION_COLUMN_3 = INDENTED_X + 400 PRESTATION_COLUMN_4 = INDENTED_X + 455 COMMON_LINE_HEIGHT = -15 SMALL_LINE_HEIGHT = COMMON_LINE_HEIGHT + 5 LARGE_LINE_HEIGHT = COMMON_LINE_HEIGHT - 5 DOUBLE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 2 BIG_LINE_HEIGHT = COMMON_LINE_HEIGHT * 3 HUGE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 4 class PDFDocument(object): # Create the PDF object, using the response object as its "file." # http://www.reportlab.com/docs/reportlab-userguide.pdf # canvas.rect(x, y, width, height, stroke=1, fill=0) # localhost:8000/billing/contract/pdf/2 def __init__(self, response): # Create the PDF object, using the response object as its "file." self.document = canvas.Canvas(response, pagesize=A4) self.y = Y - X self.__load_config() def __load_config(self): """ Charge le contenu du fichier SITE_CONFIG.YAML qui contient les données relatives à l'ASBL. """ current_path = os.path.dirname(os.path.realpath(__file__)) self.club_infos = None with open(os.path.join(current_path, "site_config.yaml"), "r") as stream: try: self.club_infos = yaml.load(stream, Loader=yaml.FullLoader) except yaml.YAMLError as exc: print(exc) def add_vspace(self, height=COMMON_LINE_HEIGHT): """ Passe à la ligne, la hauteur de la ligne étant passée en paramètre. Args: height (int): hauteur de la ligne. Returns: ne retourne rien. """ self.y += height # if y < 120; # document.PageBreak() # y = 790 def add_string( self, x, string, font_family="Helvetica", font_decoration=None, font_size=10 ): if font_decoration: font_family += "-" + font_decoration self.document.setFont(font_family, font_size) self.document.drawString(x, self.y, string) def add_new_line( self, x, string, height=COMMON_LINE_HEIGHT, font_family="Helvetica", font_decoration=None, font_size=10, ): self.add_vspace(height) self.add_string(x, string, font_family, font_decoration, font_size) def add_header(self, contract=None): """ Génère le header du document. Args: contract (contract): instance de la class Contract. Returns: ne retourne rien. """ self.add_new_line(INDENTED_X, self.club_infos["SITE_TITLE"] + ' - ' + self.club_infos['CLUB_NAME']) self.document.drawRightString( INDENTED_RIGHT_X, self.y, "Head Coach : " + self.club_infos["HEAD_COACH"] ) self.add_new_line( INDENTED_X, self.club_infos["ADDRESS"] + " - " + self.club_infos["CITY"] + " " + self.club_infos["ZIP"], ) # N° de semaine self.document.drawRightString( INDENTED_RIGHT_X, self.y, self.club_infos["MOBILE_PHONE"] ) # self.add_new_line( # INDENTED_X, # "Head Coach : " # + self.club_infos["HEAD_COACH"] # + " (" # + self.club_infos["MOBILE_PHONE"] # + ")" # ) # if contract is not None: # self.document.drawRightString( # INDENTED_RIGHT_X, self.y, "N° de Référence : " + str(contract.reference) # ) self.add_vspace(BIG_LINE_HEIGHT) def download(self): # Close the PDF object cleanly, and we're done. self.document.showPage() self.document.save() class GymnastReportDocument(PDFDocument): def generate(self, gymnast_id): """ Genère un document aux normes du SPF Finance. Args: accounting_year (int): année comptable. Returns: ne retourne rien. """ gymnast = Gymnast.objects.get(pk=gymnast_id) self.document.setTitle(gymnast.first_name + ' ' + gymnast.last_name) self.add_header() self.add_gymnast_personnal_information(gymnast) self.add_gymnast_physiological_information(gymnast) # self.add_gymnast_level_information(gymnast) self.add_gymnast_best_scores(gymnast) self.add_gymnast_next_skills(gymnast) # self.add_gymnast_active_routine(gymnast) def add_gymnast_personnal_information(self, gymnast): """ Ajoute les informations personnelles du gymnast. Args: gymnast : gymnaste Returns: ne retourne rien. """ # Rectangle pour la photo rect_height = 80 self.document.rect(X, self.y - (rect_height + COMMON_LINE_HEIGHT), 60, rect_height, fill=0) self.add_string( 110, str(gymnast), font_decoration="Bold", font_size=16, ) # self.add_vspace() self.add_new_line( 110, str(gymnast.age) ) self.add_new_line( 110, str(gymnast.informations) ) self.add_vspace(HUGE_LINE_HEIGHT) def add_gymnast_physiological_information(self, gymnast): """ Ajoute les informations physique et psychologique. Args: gymnast : gymnaste Returns: ne retourne rien """ last_mindstate = MindState.objects.filter(gymnast=gymnast).order_by('-date').first() mean_mindstate = mean(MindState.objects.filter(gymnast=gymnast).order_by('-date').values_list("score", flat=True)[:5]) print(mean_mindstate) if last_mindstate.score > mean_mindstate: print('increasing') elif last_mindstate.score < mean_mindstate: print('decreasing') else: print('stabilised') last_height_weigth = HeightWeight.objects.filter(gymnast=gymnast).order_by('-date').first() mean_height = mean(HeightWeight.objects.filter(gymnast=gymnast).order_by('-date').values_list("height", flat=True)[:5]) mean_weight = mean(HeightWeight.objects.filter(gymnast=gymnast).order_by('-date').values_list("weight", flat=True)[:5]) print(mean_height) if last_height_weigth.height > mean_height: print('Height increasing') elif last_height_weigth.height < mean_height: print('Height decreasing') else: print('Height stabilised') print(mean_weight) if last_height_weigth.weight > mean_weight: print('Weight increasing') elif last_height_weigth.weight < mean_weight: print('Weight decreasing') else: print('Weight stabilised') last_accident = Accident.objects.filter(gymnast=gymnast).order_by("-date").first() # print(last_accident) def add_gymnast_best_scores(self, gymnast): """ Ajoute les meilleurs scores du gymnaste (Tof, compétition, …). Args: gymnast : gymnaste Returns: ne retourne rien """ lines = [] text_object = self.document.beginText() text_object.setFont("Helvetica", 10) text_object.setTextOrigin(INDENTED_X, self.y) best_tof = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=0) .order_by("-score") .first() ) lines.append("Best ToF |: " + str(best_tof.tof) + " (" + str(best_tof.date) + ")") best_tof = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=1) .order_by("-score") .first() ) lines.append("Best ToF R1: " + str(best_tof.tof) + " (" + str(best_tof.date) + ")") best_tof = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=2) .order_by("-score") .first() ) lines.append("Best ToF R2: " + str(best_tof.tof) + " (" + str(best_tof.date) + ")") for line in lines: text_object.textLine(line) self.document.drawText(text_object) # best_point_routine_1 = Point.objects.filter(gymnast=gymnast).filter(routine_type=1).order_by('-total').first() # best_point_routine_2 = Point.objects.filter(gymnast=gymnast).filter(routine_type=2).order_by('-total').first() # routine_1 = gymnast.has_routine.prefetch_related("routine").filter( # dateend__isnull=True # ) self.add_vspace() def add_gymnast_next_skills(self, gymnast): """ Ajoute les prochains skill (skill planifié) à apprendre Args: gymnast gymnaste Returns: Ne retourne rien """ planified_skill = ( Skill.objects.filter(plan__gymnast=gymnast.id) .filter( Q(plan__is_done=False) | Q(plan__date__gte=date.today()) ) .order_by("-plan__date")[:6] ) for skill in planified_skill: self.add_new_line( X, skill.short_label + " (" + skill.notation + ") for (date)" )