Ultron/ultron/tools/pdf_generator.py

333 lines
9.8 KiB
Python
Raw Normal View History

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 <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 <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 <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 <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)"
)