Delete weasyprint file
This commit is contained in:
parent
97c5081bb3
commit
5122bf98f3
|
@ -1,615 +0,0 @@
|
|||
import locale
|
||||
import os
|
||||
import re
|
||||
from datetime import date, datetime, timedelta
|
||||
from statistics import mean
|
||||
|
||||
import pendulum
|
||||
import yaml
|
||||
from django.conf import settings
|
||||
from django.db.models import Max, Q
|
||||
from html2rml import html2rml # ####
|
||||
from PIL import Image
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.pdfgen.canvas import Canvas
|
||||
from reportlab.platypus import Paragraph, Table, TableStyle
|
||||
from ultron.followup.models import (
|
||||
Accident,
|
||||
Chrono,
|
||||
HeightWeight,
|
||||
MindState,
|
||||
Plan,
|
||||
Point,
|
||||
)
|
||||
from ultron.objective.models import Skill
|
||||
from ultron.people.models import Gymnast
|
||||
from ultron.planning.models import Event
|
||||
|
||||
from .date_week_transition import from_date_to_week_number
|
||||
|
||||
# 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(response, pagesize=A4)
|
||||
self.y = Y - X
|
||||
self.styles = getSampleStyleSheet()
|
||||
self.style = self.styles["Normal"]
|
||||
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
|
||||
print(self.y)
|
||||
|
||||
# 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.document.setFillColorRGB(0.75, 0.75, 0.75)
|
||||
self.add_vspace(15)
|
||||
self.add_new_line(
|
||||
X, self.club_infos["SITE_TITLE"] + " - " + self.club_infos["CLUB_NAME"]
|
||||
)
|
||||
self.document.drawRightString(
|
||||
RIGHT_X,
|
||||
self.y,
|
||||
self.club_infos["ADDRESS"]
|
||||
+ " - "
|
||||
+ self.club_infos["ZIP"]
|
||||
+ " "
|
||||
+ self.club_infos["CITY"],
|
||||
)
|
||||
|
||||
self.add_new_line(X, "Head Coach : " + self.club_infos["HEAD_COACH"])
|
||||
self.document.drawRightString(RIGHT_X, self.y, self.club_infos["MOBILE_PHONE"])
|
||||
|
||||
today = pendulum.now().date()
|
||||
# print(today)
|
||||
self.add_new_line(X, str(today))
|
||||
begin_season = date(today.year, 9, 1)
|
||||
self.document.drawRightString(
|
||||
RIGHT_X, self.y, "Week " + str(from_date_to_week_number())
|
||||
)
|
||||
|
||||
self.document.setFillColorRGB(0, 0, 0)
|
||||
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_best_scores(gymnast)
|
||||
self.add_gymnast_active_routine(gymnast)
|
||||
# self.add_gymnast_level_information(gymnast)
|
||||
self.add_gymnast_next_skills(gymnast)
|
||||
self.add_gymnast_next_events(gymnast)
|
||||
self.add_gymnast_week_notes(gymnast)
|
||||
|
||||
def add_gymnast_personnal_information(self, gymnast):
|
||||
"""Ajoute les informations personnelles du gymnast.
|
||||
|
||||
Args:
|
||||
gymnast <Gymnast>: gymnaste
|
||||
|
||||
Returns:
|
||||
ne retourne rien.
|
||||
"""
|
||||
self.y = 26 * cm
|
||||
url = os.path.join(settings.STATICFILES_DIRS[0], "img/default-avatar.png")
|
||||
self.document.drawImage(url, X, self.y - 65, width=80, height=80)
|
||||
|
||||
self.add_string(
|
||||
130,
|
||||
str(gymnast),
|
||||
font_decoration="Bold",
|
||||
font_size=14,
|
||||
)
|
||||
# self.add_vspace()
|
||||
self.document.setFillColorRGB(0.75, 0.75, 0.75)
|
||||
self.add_new_line(130, str(gymnast.age))
|
||||
self.document.setFillColorRGB(0, 0, 0)
|
||||
self.add_new_line(130, str(gymnast.informations))
|
||||
# self.add_vspace(HUGE_LINE_HEIGHT)
|
||||
|
||||
def analyse_score(self, value, value_list):
|
||||
"""Analyse une value (value) par rapport à la moyenne de value_list et à la dernière
|
||||
valeur de value_list
|
||||
|
||||
Args:
|
||||
value
|
||||
value_list
|
||||
|
||||
Returns:
|
||||
string
|
||||
|
||||
Examples:
|
||||
"""
|
||||
res = ""
|
||||
mean_value = mean(value_list)
|
||||
|
||||
if value > value_list[-1]:
|
||||
res += "+"
|
||||
elif value < value_list[-1]:
|
||||
res += "-"
|
||||
else:
|
||||
res += "="
|
||||
|
||||
if value > mean_value:
|
||||
res = "+" + res
|
||||
elif value < mean_value:
|
||||
res = "-" + res
|
||||
else:
|
||||
res = "=" + res
|
||||
|
||||
return res
|
||||
|
||||
def add_gymnast_physiological_information(self, gymnast):
|
||||
"""Ajoute les informations physique et psychologique.
|
||||
|
||||
Args:
|
||||
gymnast <Gymnast>: gymnaste
|
||||
|
||||
Returns:
|
||||
ne retourne rien
|
||||
"""
|
||||
self.y = 26 * cm
|
||||
self.add_new_line(
|
||||
13.5 * cm,
|
||||
"Physics/Mind state",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
|
||||
data = []
|
||||
mindstate_queryset = MindState.objects.filter(gymnast=gymnast).order_by("-date")
|
||||
last_mindstate = mindstate_queryset.first()
|
||||
lasts_mindstate = list(mindstate_queryset.values_list("score", flat=True)[1:6])
|
||||
|
||||
res = self.analyse_score(last_mindstate.score, lasts_mindstate)
|
||||
data.append(["Mind state", str(last_mindstate.score), res])
|
||||
|
||||
height_weight_queryset = HeightWeight.objects.filter(gymnast=gymnast).order_by(
|
||||
"-date"
|
||||
)
|
||||
last_height_weigth = height_weight_queryset.first()
|
||||
lasts_height = list(
|
||||
height_weight_queryset.values_list("height", flat=True)[1:6]
|
||||
)
|
||||
lasts_weight = list(
|
||||
height_weight_queryset.values_list("weight", flat=True)[1:6]
|
||||
)
|
||||
|
||||
res = self.analyse_score(last_height_weigth.height, lasts_height)
|
||||
data.append(["Height", str(last_height_weigth.height), res])
|
||||
|
||||
res = self.analyse_score(last_height_weigth.weight, lasts_weight)
|
||||
data.append(["Weight", str(last_height_weigth.weight), res])
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("ALIGN", (1, 0), (-1, -1), "RIGHT"),
|
||||
# ('GRID', (0,0), (-1,-1), 0.25, colors.black),
|
||||
# ('BOX', (0,0), (-1,-1), 0.25, colors.black),
|
||||
]
|
||||
)
|
||||
table = Table(data, [2 * cm, 1.5 * cm, 1.5 * cm])
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15.5 * cm)
|
||||
table.drawOn(self.document, 13.3 * cm, self.y - height - 5)
|
||||
|
||||
# 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
|
||||
"""
|
||||
self.y = 23 * cm
|
||||
self.add_new_line(
|
||||
X,
|
||||
"Best ToF",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
# self.add_vspace()
|
||||
best_tof = (
|
||||
Chrono.objects.filter(gymnast=gymnast)
|
||||
.filter(chrono_type=0)
|
||||
.order_by("-score")
|
||||
.first()
|
||||
)
|
||||
data = [
|
||||
[
|
||||
"ToF |:",
|
||||
str(best_tof.tof),
|
||||
str(best_tof.score),
|
||||
"(" + best_tof.date.strftime("%d-%m-%Y") + ")",
|
||||
],
|
||||
]
|
||||
best_tof = (
|
||||
Chrono.objects.filter(gymnast=gymnast)
|
||||
.filter(chrono_type=1)
|
||||
.order_by("-score")
|
||||
.first()
|
||||
)
|
||||
data.append(
|
||||
[
|
||||
"ToF R1:",
|
||||
str(best_tof.tof),
|
||||
str(best_tof.score),
|
||||
"(" + best_tof.date.strftime("%d-%m-%Y") + ")",
|
||||
]
|
||||
)
|
||||
best_tof = (
|
||||
Chrono.objects.filter(gymnast=gymnast)
|
||||
.filter(chrono_type=2)
|
||||
.order_by("-score")
|
||||
.first()
|
||||
)
|
||||
data.append(
|
||||
[
|
||||
"ToF R2:",
|
||||
str(best_tof.tof),
|
||||
str(best_tof.score),
|
||||
"(" + best_tof.date.strftime("%d-%m-%Y") + ")",
|
||||
]
|
||||
)
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("TEXTCOLOR", (-1, 0), (-1, -1), "#AAAAAA"),
|
||||
]
|
||||
)
|
||||
table = Table(data)
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15 * cm)
|
||||
table.drawOn(self.document, X - 6, self.y - height - 5)
|
||||
# self.add_vspace(self.y - height - 5)
|
||||
self.y = 20 * cm
|
||||
self.add_new_line(
|
||||
X,
|
||||
"Best Scores",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
|
||||
data = [["", "Exe.", "Diff.", "ToF", "HD", "Tot.", ""]]
|
||||
best_point_routine_1 = (
|
||||
Point.objects.filter(gymnast=gymnast)
|
||||
.filter(routine_type=1)
|
||||
.order_by("-total")
|
||||
.first()
|
||||
)
|
||||
if best_point_routine_1:
|
||||
data.append(
|
||||
[
|
||||
"R1: ",
|
||||
best_point_routine_1.point_execution,
|
||||
best_point_routine_1.point_difficulty,
|
||||
best_point_routine_1.point_time_of_flight,
|
||||
best_point_routine_1.point_horizontal_displacement,
|
||||
best_point_routine_1.total,
|
||||
best_point_routine_1.event.date_begin.strftime("%d-%m-%Y"),
|
||||
]
|
||||
)
|
||||
|
||||
best_point_routine_2 = (
|
||||
Point.objects.filter(gymnast=gymnast)
|
||||
.filter(routine_type=2)
|
||||
.order_by("-total")
|
||||
.first()
|
||||
)
|
||||
if best_point_routine_2:
|
||||
data.append(
|
||||
[
|
||||
"R2 :",
|
||||
best_point_routine_2.point_execution,
|
||||
best_point_routine_2.point_difficulty,
|
||||
best_point_routine_2.point_time_of_flight,
|
||||
best_point_routine_2.point_horizontal_displacement,
|
||||
best_point_routine_2.total,
|
||||
best_point_routine_2.event.date_begin.strftime("%d-%m-%Y"),
|
||||
]
|
||||
)
|
||||
else:
|
||||
data.append(
|
||||
[
|
||||
"R2:",
|
||||
"-",
|
||||
"-",
|
||||
"-",
|
||||
"-",
|
||||
"-",
|
||||
]
|
||||
)
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("ALIGN", (0, 0), (-1, 0), "CENTER"),
|
||||
("ALIGN", (1, 1), (-1, -1), "RIGHT"),
|
||||
("TEXTCOLOR", (-1, 0), (-1, -1), "#AAAAAA"),
|
||||
# ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
# ('LINEABOVE', (0,-1), (-1,-1), 0.25, colors.black),
|
||||
]
|
||||
)
|
||||
table = Table(data)
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15 * cm)
|
||||
table.drawOn(self.document, X - 6, self.y - height - 5)
|
||||
|
||||
# routine_1 = gymnast.has_routine.prefetch_related("routine").filter(
|
||||
# date_end__isnull=True
|
||||
# )
|
||||
|
||||
self.add_vspace(HUGE_LINE_HEIGHT)
|
||||
|
||||
def add_gymnast_active_routine(self, gymnast):
|
||||
"""Ajoute les routines actives"""
|
||||
self.y = 23 * cm
|
||||
self.add_new_line(
|
||||
15.9 * cm,
|
||||
"Routines",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
routine_1 = (
|
||||
gymnast.has_routine.filter(routine_type=1)
|
||||
.filter(date_begin__lte=date.today())
|
||||
.filter(Q(date_end__gte=date.today()) | Q(date_end__isnull=True))
|
||||
.first()
|
||||
)
|
||||
|
||||
routine_2 = (
|
||||
gymnast.has_routine.filter(routine_type=2)
|
||||
.filter(date_begin__lte=date.today())
|
||||
.filter(Q(date_end__gte=date.today()) | Q(date_end__isnull=True))
|
||||
.first()
|
||||
)
|
||||
|
||||
data = []
|
||||
for routine_skill in routine_1.routine.skill_links.all():
|
||||
data.append([routine_skill.skill.notation, routine_skill.skill.difficulty])
|
||||
data.append([None, routine_1.routine.difficulty])
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("ALIGN", (1, 0), (1, -1), "RIGHT"),
|
||||
# ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
("LINEABOVE", (0, -1), (-1, -1), 0.25, colors.black),
|
||||
]
|
||||
)
|
||||
table = Table(data, [2 * cm, 1 * cm])
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15 * cm)
|
||||
table.drawOn(self.document, 13.5 * cm, self.y - height - 5)
|
||||
|
||||
data = []
|
||||
for routine_skill in routine_2.routine.skill_links.all():
|
||||
data.append([routine_skill.skill.notation, routine_skill.skill.difficulty])
|
||||
data.append([None, routine_2.routine.difficulty])
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("ALIGN", (1, 0), (1, -1), "RIGHT"),
|
||||
# ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
("LINEABOVE", (0, -1), (-1, -1), 0.25, colors.black),
|
||||
]
|
||||
)
|
||||
table = Table(data, [2 * cm, 1 * cm])
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15.5 * cm)
|
||||
table.drawOn(self.document, 17 * cm, self.y - height - 5)
|
||||
|
||||
def add_gymnast_next_skills(self, gymnast):
|
||||
"""Ajoute les prochains skill (skill planifié) à apprendre
|
||||
|
||||
Args:
|
||||
gymnast <Gymnast> gymnaste
|
||||
|
||||
Returns:
|
||||
Ne retourne rien
|
||||
"""
|
||||
|
||||
self.y = 17 * cm
|
||||
self.add_new_line(
|
||||
X,
|
||||
"Next skills to learn",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
self.add_vspace(-3)
|
||||
|
||||
planified_skills = (
|
||||
Skill.objects.filter(plan__gymnast=gymnast.id)
|
||||
.filter(Q(plan__is_done=False) | Q(plan__date__gte=date.today()))
|
||||
.order_by("-plan__date")[:6]
|
||||
)
|
||||
|
||||
# Ne permet pas de récupérer que les skill, or je voudrais bien.
|
||||
# planified_skills = (
|
||||
# Plan.objects.filter(gymnast=gymnast.id)
|
||||
# .filter(
|
||||
# Q(is_done=False)
|
||||
# | Q(date__gte=date.today())
|
||||
# )
|
||||
# .order_by("-date")[:6]
|
||||
# )
|
||||
|
||||
for planified_skill in planified_skills:
|
||||
self.add_new_line(
|
||||
X,
|
||||
planified_skill.skill.short_label
|
||||
+ " ("
|
||||
+ planified_skill.skill.notation
|
||||
+ ") for (todo: compute deadline)",
|
||||
)
|
||||
|
||||
def add_gymnast_next_events(self, gymnast):
|
||||
"""Ajoute les évènements futurs du gymnaste"""
|
||||
self.y = 13.5 * cm
|
||||
self.add_new_line(
|
||||
X,
|
||||
"Next event",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
self.add_vspace(-3)
|
||||
|
||||
today = pendulum.now().date()
|
||||
next_event_list = Event.objects.filter(
|
||||
gymnasts=gymnast.id, date_begin__gte=today
|
||||
).order_by("date_begin")[:5]
|
||||
|
||||
data = []
|
||||
for event in next_event_list:
|
||||
data.append(
|
||||
[
|
||||
event.date_begin.strftime("%d-%m-%Y"),
|
||||
"in " + str(event.number_of_week_from_today) + " week(s)",
|
||||
event.name,
|
||||
]
|
||||
)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
style = TableStyle(
|
||||
[
|
||||
("ALIGN", (1, 0), (1, -1), "CENTER"),
|
||||
# ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
# ('GRID', (0,0), (-1,-1), 0.25, colors.black),
|
||||
# ('LINEABOVE', (0,-1), (-1,-1), 0.25, colors.black),
|
||||
]
|
||||
)
|
||||
table = Table(data, [2.3 * cm, 2.2 * cm, 8 * cm])
|
||||
table.setStyle(style)
|
||||
width, height = table.wrapOn(self.document, 19 * cm, 15.5 * cm)
|
||||
table.drawOn(self.document, X - 6, self.y - height - 5)
|
||||
|
||||
def add_gymnast_week_notes(self, gymnast):
|
||||
"""Ajoute les notes de la semaine du gymnaste passé en paramètre"""
|
||||
|
||||
self.y = 10.3 * cm
|
||||
self.add_new_line(
|
||||
X,
|
||||
"Notes",
|
||||
font_decoration="Bold",
|
||||
)
|
||||
self.add_vspace(-2 * cm)
|
||||
|
||||
today = pendulum.today().date()
|
||||
begin_week = today
|
||||
if today.weekday() != 0:
|
||||
begin_week -= today.weekday()
|
||||
|
||||
notes = gymnast.remarks.filter(created_at__gte=begin_week)
|
||||
|
||||
html_text = ""
|
||||
for note in notes:
|
||||
html_text += "<br />" + note.to_markdown()
|
||||
|
||||
html_text = html_text[6:]
|
||||
print(html_text)
|
||||
|
||||
paragraph = Paragraph(html_text, self.style, bulletText="*")
|
||||
width, height = paragraph.wrap(18 * cm, 18 * cm)
|
||||
paragraph.drawOn(self.document, X, self.y)
|
Loading…
Reference in New Issue