Big report update

This commit is contained in:
Gregory Trullemans 2023-06-19 20:41:14 +02:00
parent 11f303ad70
commit ac2643f66e
10 changed files with 1024 additions and 608 deletions

View File

@ -27,9 +27,9 @@
<h4 class=""><i class="text-primary fal fa-laugh-wink"></i> Hi {{ user.username }} !</h4> <h4 class=""><i class="text-primary fal fa-laugh-wink"></i> Hi {{ user.username }} !</h4>
</div> </div>
<div class="card-body text-justify"> <div class="card-body text-justify">
<p>Welcome to Jarvi v0.83 <span class="text-muted">(last update : 12-06-2023)</span></p> <p>Welcome to Jarvi v0.84 <span class="text-muted">(last update : 12-06-2023)</span></p>
<p>This application is here to help coaches to manage the gymnasts (evolution, evaluation, routines, scores, …). This tool is not perfect so feel free to make improvement proposals, bug reports, … by sending me an <a href="mailto:gregory@flyingacrobaticstrampoline.be">email</a>.</p> <p>This application is here to help coaches to manage the gymnasts (evolution, evaluation, routines, scores, …). This tool is not perfect so feel free to make improvement proposals, bug reports, … by sending me an <a href="mailto:gregory@flyingacrobaticstrampoline.be">email</a>.</p>
<p>You can find the user manuel <a href="{% static "files/Manuel_Utilisateur.pdf" %}" download>here (in french)</a>.</p> <p>You can find the user manual <a href="{% static "files/Manuel_Utilisateur.pdf" %}" download>here (in french)</a>.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -72,6 +72,11 @@ AGE_CATOGORY_CHOICES = (
(22, "Senior"), (22, "Senior"),
) )
NOTE_STATUS_CHOICES = (
(0, "Draft"),
(1, "Published"),
)
class Chrono(Seasonisable): class Chrono(Seasonisable):
""" """
@ -399,11 +404,6 @@ class Note(Markdownizable, Seasonisable):
Notes relatives à un gymnaste Notes relatives à un gymnaste
""" """
STATUS_CHOICES = (
(0, "Draft"),
(1, "Published"),
)
gymnast = models.ForeignKey( gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="remarks" Gymnast, on_delete=models.CASCADE, related_name="remarks"
) )
@ -411,9 +411,11 @@ class Note(Markdownizable, Seasonisable):
User, on_delete=models.SET_NULL, blank=True, null=True, related_name="notes" User, on_delete=models.SET_NULL, blank=True, null=True, related_name="notes"
) )
status = models.PositiveSmallIntegerField( status = models.PositiveSmallIntegerField(
choices=STATUS_CHOICES, verbose_name="Status", default=0 choices=NOTE_STATUS_CHOICES, verbose_name="Status", default=0
)
title = models.CharField(
default="Note of the week", verbose_name="Title", max_length=255
) )
title = models.CharField(default="Note of the week", verbose_name="Title", max_length=255)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)

View File

@ -721,6 +721,9 @@ def score_create_or_update(request, score_id=None, gymnast_id=None):
# notification # notification
receiver = [] receiver = []
gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id)
event = form.cleaned_data["event"]
routine_type = form.cleaned_data["routine_type"]
total = form.cleaned_data["total"]
functionality = ContentType.objects.get(model="point") functionality = ContentType.objects.get(model="point")
for notification in gymnast.notifications.filter( for notification in gymnast.notifications.filter(
functionality=functionality functionality=functionality
@ -734,7 +737,7 @@ def score_create_or_update(request, score_id=None, gymnast_id=None):
receiver, receiver,
fail_silently=False, fail_silently=False,
html_message=f"""<p>Bonjour,</p> html_message=f"""<p>Bonjour,</p>
<p>Un nouveau score a été enregistré pour {gymnast}.</p><br /> <p>Un nouveau score a été enregistré pour {gymnast} ({event}) : {routine_type} - {total}.</p><br />
<p>Excellente journée</p><p>Jarvis</p>""", <p>Excellente journée</p><p>Jarvis</p>""",
) )

View File

@ -0,0 +1,477 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="author" content="Gregory Trullemans">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="apple-touch-icon" sizes="76x76" href="{% static "img/apple-icon.png" %}">
<link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
<title>{{ gymnast.first_name }} {{ gymnast.last_name }}</title>
<!-- Fonts and icons -->
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet" />
<!-- Font Awesome Pro -->
<link href="{% static "css/gymnast_report.css" %}" rel="stylesheet" />
<link href="{% static "css/font_awesome_all_5.15.3.css" %}" rel="stylesheet" />
<link href="{% static "css/black-dashboard_report.css" %}" rel="stylesheet" />
</head>
<header class="white-content">
<div class="row">
<div id="header-left" class="col-7 text-12">
{{ SITE_TITLE }} - {{ CLUB_NAME }}<br />
{{ ADDRESS }} - {{ ZIP }} {{ CITY }}<br />
Season {{ season }}{% if period != "season" %} - {% if period == "week" %}week{% endif %} {{ period_value }}{% endif %} ({{ date_begin | date:"j N Y" }} - {{ date_end | date:"j N Y" }})
</div>
<div id="header-right" class="col-5 text-right text-12">
Head Coach : {{ HEAD_COACH }}<br />
{{ HEAD_COACH_EMAIL }}<br />
{{ today | date:"j F Y" }}
</div>
</div>
</header>
<br />
<body class="white-content">
<div class="row">
<div class="col-2">
<img src="{% static 'img/default-avatar.png' %}" class="profil_img" />
</div>
<div class="col-10 pl-0" style="text-align: justify;">
<h3 class="title mb-0">{{ gymnast.first_name }} {{ gymnast.last_name }}</h3>
<p class="mb-3 text-muted">{{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.<br />
{{season_informations.number_of_hours_per_week}}h training ({{ season_informations.number_of_training_sessions_per_week }}/w) - {{ season_informations.number_of_s_and_c_hours_per_week}}h S&C ({{ season_informations.number_of_s_and_c_sessions_per_week }}/w)<br />
<b>{{ season_informations.get_category_display }}</b>
</p>
{% if gymnast.informations %}
{{ gymnast.to_markdown | safe }}
{% endif %}
</div>
</div>
<div class="row">
<div class="col-4">
{% if mindstate_score or height_weight_value %}
<h4 class="mb-1">Physiological</h4>
<table class="table">
<thead>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0 text-right">Min</th>
<th class="pt-0 pb-0 text-right">Average</th>
<th class="pt-0 pb-0 text-right">Max</th>
</thead>
{% if mindstate_score %}
<tr>
<td class="pt-0 pb-0"><b>Mind state</b></td>
<td class="pt-0 pb-0 text-right">{{ mindstate_score.min_mindstate_value }}</td>
<td class="pt-0 pb-0 text-right">{{ mindstate_score.mean_mindstate_value|stringformat:".1f" }}</td>
<td class="pt-0 pb-0 text-right">{{ mindstate_score.max_mindstate_value }}</td>
</tr>
{% endif %}
{% if height_weight_value.mean_height_value %}
<tr>
<td class="pt-0 pb-0"><b>Height</b></td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.min_height_value }}</td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.mean_height_value|stringformat:".1f" }}</td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.max_height_value }}</td>
</tr>
{% endif %}
{% if height_weight_value.mean_weight_value %}
<tr>
<td class="pt-0 pb-0"><b>Weight</b></td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.min_weight_value }}</td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.mean_weight_value|stringformat:".1f" }}</td>
<td class="pt-0 pb-0 text-right">{{ height_weight_value.max_weight_value }}</td>
</tr>
{% endif %}
</table>
{% endif %}
</div>
<div class="col-4">
<h4 class="mb-1">Intensity</h4>
{% if intensity_value %}
<table class="table">
<thead>
<th class="pt-0 pb-0 text-right"></th>
<th class="pt-0 pb-0 text-right">Min</th>
<th class="pt-0 pb-0 text-right">Average</th>
<th class="pt-0 pb-0 text-right">Max</th>
</thead>
<tbody>
<tr>
<td class="pt-0 pb-0"><b>Time</b></td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.min_intensity_time_value }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.mean_intensity_time_value|stringformat:".0f" }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.max_intensity_time_value }}</td>
</tr>
<tr>
<td class="pt-0 pb-0"><b>Diff.</b></td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.min_intensity_difficulty_value }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.mean_intensity_difficulty_value|stringformat:".0f" }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.max_intensity_difficulty_value }}</td>
</tr>
<tr>
<td class="pt-0 pb-0"><b># Skill</b></td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.min_quantity_of_skill_value }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.mean_quantity_of_skill_value|stringformat:".0f" }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.max_quantity_of_skill_value }}</td>
</tr>
<tr>
<td class="pt-0 pb-0"><b># Passes</b></td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.min_number_of_passes_value }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.mean_number_of_passes_value|stringformat:".0f" }}</td>
<td class="pt-0 pb-0 text-right">{{ intensity_value.max_number_of_passes_value }}</td>
</tr>
</tbody>
</table>
{% else %}
No intensty for selected period.
{% endif %}
</div>
</div>
<br />
<div class="row">
<div class="col-6">
<h4 class="mb-1">Time of flight</h4>
{% if number_of_tof_straightjump or number_of_tof_q1r1 or number_of_tof_q1r2 %}
<table class="table">
<thead>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0 text-right">Min</th>
<th class="pt-0 pb-0 text-right">Average</th>
<th class="pt-0 pb-0 text-right">Max</th>
</thead>
{% if best_tof_straightjump.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>10 |</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_straightjump }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.min_tof }} ({{ best_tof_straightjump.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.mean_tof|stringformat:".3f" }} ({{ best_tof_straightjump.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.max_tof }} ({{ best_tof_straightjump.max_score }})</td>
</tr>
{% endif %}
{% if best_tof_q1r1.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>Q1R1</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_q1r1 }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r1.min_tof }} ({{ best_tof_q1r1.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r1.mean_tof|stringformat:".3f" }} ({{ best_tof_q1r1.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r1.max_tof }} ({{ best_tof_q1r1.max_score }})</td>
</tr>
{% endif %}
{% if best_tof_q1r2.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>Q1R2</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_q1r2 }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r2.min_tof }} ({{ best_tof_q1r2.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r2.mean_tof|stringformat:".3f" }} ({{ best_tof_q1r2.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q1r2.max_tof }} ({{ best_tof_q1r2.max_score }})</td>
</tr>
{% endif %}
{% if best_tof_q2r1.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>Q2R1</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_q2r1 }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q2r1.min_tof }} ({{ best_tof_q2r1.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q2r1.mean_tof|stringformat:".3f" }} ({{ best_tof_q2r1.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_q2r1.max_tof }} ({{ best_tof_q2r1.max_score }})</td>
</tr>
{% endif %}
{% if best_tof_sf.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>SF</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_sf }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_sf.min_tof }} ({{ best_tof_sf.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_sf.mean_tof|stringformat:".3f" }} ({{ best_tof_sf.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_sf.max_tof }} ({{ best_tof_sf.max_score }})</td>
</tr>
{% endif %}
{% if best_tof_f.mean_score %}
<tr>
<td class="pt-0 pb-0"><b>F</b></td>
<td class="pt-0 pb-0">({{ number_of_tof_f }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_f.min_tof }} ({{ best_tof_f.min_score }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_f.mean_tof|stringformat:".3f" }} ({{ best_tof_f.mean_score|stringformat:".3f" }})</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_f.max_tof }} ({{ best_tof_f.max_score }})</td>
</tr>
{% endif %}
</table>
{% else %}
No chrono/ToF found.
{% endif %}
</div>
<div class="col-6">
<h4 class="mb-1">Best Scores</h4>
{% if best_point_routine_1 or best_point_routine_2 or best_point_routine_3 or best_point_routine_4 or best_point_routine_5 %}
<table class="table">
<thead>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0 text-right">Exe.</th>
<th class="pt-0 pb-0 text-right">Diff.</th>
<th class="pt-0 pb-0 text-right">HD</th>
<th class="pt-0 pb-0 text-right">ToF</th>
<th class="pt-0 pb-0 text-right">Total</th>
<th class="pt-0 pb-0"></th>
</thead>
{% if best_point_routine_1 %}
<tr>
<td class="pt-0 pb-0"><b>Q1R1</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_2 %}
<tr>
<td class="pt-0 pb-0"><b>Q1R2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_3 %}
<tr>
<td class="pt-0 pb-0"><b>Q2R1</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_4 %}
<tr>
<td class="pt-0 pb-0"><b>SF</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_5 %}
<tr>
<td class="pt-0 pb-0"><b>F</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
</table>
{% else %}
No scores during the selected week.
{% endif %}
</div>
</div>
<br />
<div class="row" id="learned_skill_and_plan">
<div class="col-6">
<h4 class="mb-1">Last learned skills</h4>
{% if learned_skills %}
<table class="table">
{% for learned_skill in learned_skills %}
<tr>
<td class="pt-1 pb-0">{{ learned_skill.skill.short_label }}</td>
<td class="pt-1 pb-0">({{ learned_skill.get_learning_step_display }})</td>
<td class="pt-1 pb-0">{{ learned_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ learned_skill.date|date:"j M Y" }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No new skill learned on the selected week.
{% endif %}
</div>
<div class="col-6">
<h4 class="mb-1">Objectives</h4>
{% if plan_list %}
<table class="table">
{% for plan in plan_list %}
<tr>
<td class="pt-1 pb-0">{{ plan.educative.short_label }}</td>
<td class="pt-1 pb-0">({{ plan.get_learning_step_display }})</td>
<td class="pt-1 pb-0">{{ plan.skill.notation }}</td>
<td class="pt-1 pb-0">{{ plan.date | date:"j M Y" }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No objective for the selected week.
{% endif %}
</div>
</div>
<br />
<div class="row">
<div class="col-6">
<h4 class="mb-1">Next Events</h4>
{% if next_event_list %}
<table class="table">
{% for event in next_event_list %}
<tr>
<td class="pt-1 pb-0">{{ event.date_begin | date:"j M Y" }}</td>
<td class="pt-1 pb-0">in {{ event.number_of_week_from_today }} week(s)</td>
<td class="pt-1 pb-0">{{ event.name }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No events on the selected week.
{% endif %}
</div>
</div>
<br />
<div class="row" id="note">
<div class="col-12">
<h4 class="mb-1">Notes</h4>
{% if notes %}
{% for note in notes %}
{{ note.to_markdown | safe }}
{% endfor %}
{% else %}
No note for the selected week.
{% endif %}
</div>
</div>
<br />
<div class="row" id="Routines" style="break-before: page;">
{% if q1r1 or q1r2 or q2r1 or sfinal or final %}
<div class="col-12">
<h4 class="mb-1">Routines</h4>
</div>
<div class="col-2">
<h5>Q1 routine 1</h5>
{% if q1r1 %}
<table class="table">
{% for routine_skill in q1r1.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0 text-right">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0 text-right"><b>{{ q1r1.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if q1r1_done_stat.total_succeeded %}
{{ q1r1_done_stat.total_succeeded }} | {{ q1r1_done_stat.total_try }} ({% widthratio q1r1_done_stat.total_succeeded q1r1_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Q1 routine 2</h5>
{% if q1r2 %}
<table class="table">
{% for routine_skill in q1r2.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0 text-right">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0 text-right"><b>{{ q1r2.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if q1r2_done_stat.total_succeeded %}
{{ q1r2_done_stat.total_succeeded }} | {{ q1r2_done_stat.total_try }} ({% widthratio q1r2_done_stat.total_succeeded q1r2_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Q2 routine 1</h5>
{% if q2r1 %}
<table class="table">
{% for routine_skill in q2r1.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0 text-right">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0 text-right"><b>{{ q2r1.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if q2r1_done_stat.total_succeeded %}
{{ q2r1_done_stat.total_succeeded }} | {{ q2r1_done_stat.total_try }} ({% widthratio q2r1_done_stat.total_succeeded q2r1_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Semi-final routine</h5>
{% if sfinal %}
<table class="table">
{% for routine_skill in sfinal.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0 text-right">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0 text-right"><b>{{ sfinal.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if sfinal_done_stat.total_succeeded %}
{{ sfinal_done_stat.total_succeeded }} | {{ sfinal_done_stat.total_try }} ({% widthratio sfinal_done_stat.total_succeeded sfinal_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Final routine</h5>
{% if final %}
<table class="table">
{% for routine_skill in final.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0 text-right">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0 text-right"><b>{{ final.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if final_done_stat.total_succeeded %}
{{ final_done_stat.total_succeeded }} | {{ final_done_stat.total_try }} ({% widthratio final_done_stat.total_succeeded final_done_stat.total_try 100 %}%)
{% endif %}
</div>
{% endif %}
</div>
</div>
</body>
</html>

View File

@ -1,382 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="author" content="Gregory Trullemans">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="apple-touch-icon" sizes="76x76" href="{% static "img/apple-icon.png" %}">
<link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
<title>{{ gymnast.first_name }} {{ gymnast.last_name }}</title>
<!-- Fonts and icons -->
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet" />
<!-- Font Awesome Pro -->
<link href="{% static "css/gymnast_report.css" %}" rel="stylesheet" />
<link href="{% static "css/font_awesome_all_5.15.3.css" %}" rel="stylesheet" />
<link href="{% static "css/black-dashboard_report.css" %}" rel="stylesheet" />
</head>
<header class="white-content">
<div class="row">
<div id="header-left" class="col-7 text-12">
{{ SITE_TITLE }} - {{ CLUB_NAME }}<br />
{{ ADDRESS }} - {{ ZIP }} {{ CITY }}<br />
Season {{ season }} - week {{ week_number }}
</div>
<div id="header-right" class="col-5 text-right text-12">
Head Coach : {{ HEAD_COACH }}<br />
{{ HEAD_COACH_EMAIL }}<br />
{{ today | date:"j F Y" }}
</div>
</div>
</header>
<br />
<body class="white-content">
<div class="row">
<div class="col-2">
<img src="{% static 'img/default-avatar.png' %}" class="profil_img" />
</div>
<div class="col-6 pl-0">
<h3 class="title mb-0">{{ gymnast.first_name }} {{ gymnast.last_name }}</h3>
<p class="mb-3 text-muted">{{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.</p>
{% if gymnast.informations %}
{{ gymnast.to_markdown | safe }}
{% endif %}
</div>
<div class="col-4">
{% if last_mindstate or last_height_weigth or mindstate_analyse or height_analyse or weight_analyse %}
<h4>Physiological</h4>
<table class="table">
{% if last_mindstate %}
<tr>
<td class="pt-0 pb-0">Mind state</td>
<td class="pt-0 pb-0">{{ last_mindstate }}</td>
<td class="pt-0 pb-0">{% if mindstate_analyse %}{{ mindstate_analyse }}{% endif %}</td>
</tr>
{% endif %}
{% if last_height and last_weight %}
<tr>
<td class="pt-0 pb-0">Height</td>
<td class="pt-0 pb-0">{{ last_height }}</td>
<td class="pt-0 pb-0">{% if height_analyse %}{{ height_analyse }}{% endif %}</td>
</tr>
<tr>
<td class="pt-0 pb-0">Weight</td>
<td class="pt-0 pb-0">{{ last_weigth }}</td>
<td class="pt-0 pb-0">{% if weight_analyse %}{{ weight_analyse }}{% endif %}</td>
</tr>
{% endif %}
</table>
{% endif %}
</div>
</div>
<br />
<div class="row">
<div class="col-8 row">
<div class="col-8">
<h4 class="mb-1">Best ToF</h4>
{% if best_tof_straightjump or best_tof_routine_1 or best_tof_routine_2 %}
<table class="table">
<thead>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0 text-center">Chrono</th>
<th class="pt-0 pb-0 text-center">ToF</th>
<th class="pt-0 pb-0"></th>
</thead>
{% if best_tof_straightjump %}
<tr>
<td class="pt-0 pb-0"><b>ToF |</b></td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.score }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.tof }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_straightjump.date|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_tof_routine_1 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 1</b></td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_1.score }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_1.tof }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_1.date|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_tof_routine_2 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_2.score }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_2.tof }}</td>
<td class="pt-0 pb-0 text-right">{{ best_tof_routine_2.date|date:"j M Y" }}</td>
</tr>
{% endif %}
</table>
{% endif %}
</div>
<div class="col-10">
<br />
<h4 class="mb-1">Best Scores</h4>
{% if best_point_routine_1 or best_point_routine_2 %}
<table class="table">
<thead>
<th class="pt-0 pb-0"></th>
<th class="pt-0 pb-0 text-center">Exe.</th>
<th class="pt-0 pb-0 text-center">Diff.</th>
<th class="pt-0 pb-0 text-center">HD</th>
<th class="pt-0 pb-0 text-center">ToF</th>
<th class="pt-0 pb-0 text-center">Total</th>
<th class="pt-0 pb-0"></th>
</thead>
{% if best_point_routine_1 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 1</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_1.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_2 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_2.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_3 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_3.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_4 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_4.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
{% if best_point_routine_5 %}
<tr>
<td class="pt-0 pb-0"><b>Routine 2</b></td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_execution }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_difficulty }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_horizontal_displacement }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.point_time_of_flight }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.total }}</td>
<td class="pt-0 pb-0 text-right">{{ best_point_routine_5.event.date_begin|date:"j M Y" }}</td>
</tr>
{% endif %}
</table>
{% endif %}
</div>
</div>
</div>
<br />
<div class="row" id="learned_skill_and_plan">
<div class="col-6">
<h4 class="mb-1">Last learned skills</h4>
<table class="table">
{% for learned_skill in learned_skills %}
<tr>
<td class="pt-1 pb-0">{{ learned_skill.skill.short_label }}</td>
<td class="pt-1 pb-0">({{ learned_skill.get_learning_step_display }})</td>
<td class="pt-1 pb-0">{{ learned_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ learned_skill.date|date:"j M Y" }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="col-6">
<h4 class="mb-1">Objectives</h4>
{% if plan_list %}
<table class="table">
{% for plan in plan_list %}
<tr>
<td class="pt-1 pb-0">{{ plan.educative.short_label }}</td>
<td class="pt-1 pb-0">({{ plan.get_learning_step_display }})</td>
<td class="pt-1 pb-0">{{ plan.skill.notation }}</td>
<td class="pt-1 pb-0">{{ plan.date | date:"j M Y" }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No objective defined.
{% endif %}
</div>
</div>
<br />
<div class="row">
<div class="col-6">
<h4 class="mb-1">Next Events</h4>
{% if next_event_list %}
<table class="table">
{% for event in next_event_list %}
<tr>
<td class="pt-1 pb-0">{{ event.date_begin | date:"j M Y" }}</td>
<td class="pt-1 pb-0">in {{ event.number_of_week_from_today }} week(s)</td>
<td class="pt-1 pb-0">{{ event.name }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No events defined.
{% endif %}
</div>
</div>
<br />
<div class="row" id="note">
<div class="col-12">
<h4 class="mb-1">Notes</h4>
{% if notes %}
{% for note in notes %}
{{ note.to_markdown | safe }}
{% endfor %}
{% else %}
No note this week.
{% endif %}
</div>
</div>
<br />
<div class="row" id="Routines" style="break-before: page;">
{% if routine_1 or routine_2 or routine_3 or routine_4 or routine_5 %}
<div class="col-12">
<h4 class="mb-1">Routines</h4>
</div>
<div class="col-2">
<h5>Q1 routine 1</h5>
{% if routine_1 %}
<table class="table">
{% for routine_skill in routine_1.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0"><b>{{ routine_1.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if routine_1_done_stat.total_succeeded %}
{{ routine_1_done_stat.total_succeeded }} | {{ routine_1_done_stat.total_try }} ({% widthratio routine_1_done_stat.total_succeeded routine_1_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Q1 routine 2</h5>
{% if routine_2 %}
<table class="table">
{% for routine_skill in routine_2.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0"><b>{{ routine_2.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if routine_2_done_stat.total_succeeded %}
{{ routine_2_done_stat.total_succeeded }} | {{ routine_2_done_stat.total_try }} ({% widthratio routine_2_done_stat.total_succeeded routine_2_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Q2 routine 1</h5>
{% if routine_3 %}
<table class="table">
{% for routine_skill in routine_3.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0"><b>{{ routine_3.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if routine_3_done_stat.total_succeeded %}
{{ routine_3_done_stat.total_succeeded }} | {{ routine_3_done_stat.total_try }} ({% widthratio routine_3_done_stat.total_succeeded routine_3_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Semi-final routine</h5>
{% if routine_4 %}
<table class="table">
{% for routine_skill in routine_4.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0"><b>{{ routine_4.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if routine_4_done_stat.total_succeeded %}
{{ routine_4_done_stat.total_succeeded }} | {{ routine_4_done_stat.total_try }} ({% widthratio routine_4_done_stat.total_succeeded routine_4_done_stat.total_try 100 %}%)
{% endif %}
</div>
<div class="col-2">
<h5>Final routine</h5>
{% if routine_5 %}
<table class="table">
{% for routine_skill in routine_5.routine.skill_links.all %}
<tr>
<td class="pt-1 pb-0">{{ routine_skill.skill.notation }}</td>
<td class="pt-1 pb-0">{{ routine_skill.skill.difficulty }}</td>
</tr>
{% endfor %}
<tr>
<td class="pt-1 pb-0"></td>
<td class="pt-1 pb-0"><b>{{ routine_5.routine.difficulty }}</b></td>
</tr>
</table>
{% else %}
No routine defined.
{% endif %}
{% if routine_5_done_stat.total_succeeded %}
{{ routine_5_done_stat.total_succeeded }} | {{ routine_5_done_stat.total_try }} ({% widthratio routine_5_done_stat.total_succeeded routine_5_done_stat.total_try 100 %}%)
{% endif %}
</div>
{% endif %}
</div>
</div>
</body>
</html>

View File

@ -66,16 +66,26 @@ gymnast_urlpatterns = [
views.get_distinct_week_number_for_season_and_gymnast, views.get_distinct_week_number_for_season_and_gymnast,
name="get_distinct_week_number_for_season_and_gymnast", name="get_distinct_week_number_for_season_and_gymnast",
), ),
# path(
# r"report/<int:gymnast_id>/",
# views.generate_week_report,
# name="gymnast_report_export",
# ),
path( path(
r"report/<int:gymnast_id>/", r"report/periodical/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
views.generate_week_report,
name="gymnast_report_export",
),
path(
r"report/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
views.generate_week_report, views.generate_week_report,
name="gymnast_report_export_for_week_number", name="gymnast_report_export_for_week_number",
), ),
path(
r"report/periodical/<int:gymnast_id>/season/<str:season>/month_number/<int:month_number>/",
views.generate_month_report,
name="gymnast_report_export_for_month_number",
),
path(
r"report/periodical/<int:gymnast_id>/season/<str:season>/",
views.generate_season_report,
name="gymnast_report_export_for_season",
),
path( path(
r"report/timeline/<int:gymnast_id>/", r"report/timeline/<int:gymnast_id>/",
views.generate_timeline_report, views.generate_timeline_report,

View File

@ -7,6 +7,8 @@ from django.db.models import (
Q, Q,
Avg, Avg,
Sum, Sum,
Min,
Max,
) )
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
@ -22,7 +24,7 @@ from weasyprint import HTML, CSS
# from weasyprint.fonts import FontConfiguration # from weasyprint.fonts import FontConfiguration
import pendulum import pendulum
from datetime import date, timedelta from datetime import date
from statistics import mean from statistics import mean
from jarvis.followup.models import Event from jarvis.followup.models import Event
@ -51,6 +53,8 @@ from jarvis.tools.models import Season
from jarvis.tools.date_week_transition import ( from jarvis.tools.date_week_transition import (
from_date_to_week_number, from_date_to_week_number,
from_week_number_to_date, from_week_number_to_date,
from_month_number_to_date,
from_season_to_date,
) )
from .models import Gymnast from .models import Gymnast
@ -597,7 +601,7 @@ def gymnast_display_skill(request, gymnast_id):
def analyse_score(value, value_list): def analyse_score(value, value_list):
"""Analyse une value (value) par rapport à la moyenne de value_list et à la dernière """Analyse une valeur (value) par rapport à la moyenne de value_list et à la dernière
valeur de value_list. valeur de value_list.
Args: Args:
@ -629,6 +633,38 @@ def analyse_score(value, value_list):
return result return result
def __compute_curve_orientation(value, previous_value, previous_previous_value):
"""Analyse une valeur (value) par rapport à la moyenne de value_list et à la dernière
valeur de value_list.
Args:
value float valeur
value_list array<float> liste de valeurs
Returns:
string
Examples:
"""
result = ""
if value > previous_value:
result += "+"
elif value < previous_value:
result += "-"
else:
result += "="
if previous_value > previous_previous_value:
result = "+" + result
elif previous_value < previous_previous_value:
result = "-" + result
else:
result = "=" + result
return result
def __get_distinct_followup_season_for_gymnast(gymnast_id): def __get_distinct_followup_season_for_gymnast(gymnast_id):
"""Recupère les saisons pour lesquelles le gymnastes à des followup. """Recupère les saisons pour lesquelles le gymnastes à des followup.
@ -791,156 +827,293 @@ def report_choice(request, gymnast_id):
return render(request, "gymnasts/report_choices.html", context) return render(request, "gymnasts/report_choices.html", context)
def __mindstate_analyse(gymnast, date_begin, date_end, period, mindstate_score):
""" """
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_mindstate_score = gymnast.mindstate.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_mindstate_value=Avg("score"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_mindstate_score = gymnast.mindstate.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_mindstate_value=Avg("score"))
return __compute_curve_orientation(
mindstate_score,
previous_mindstate_score["mean_mindstate_value"],
previous_previous_mindstate_score["mean_mindstate_value"],
)
def __height_analyse(gymnast, date_begin, date_end, period, height_score):
""" """
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_height_score = gymnast.height_weight.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_height_value=Avg("height"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_height_score = gymnast.height_weight.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_height_value=Avg("height"))
return __compute_curve_orientation(
height_score,
previous_height_score["mean_height_value"],
previous_previous_height_score["mean_height_value"],
)
def __weight_analyse(gymnast, date_begin, date_end, period, weight_score):
""" """
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_height_score = gymnast.height_weight.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_weight_value=Avg("weight"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_height_score = gymnast.height_weight.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_weight_value=Avg("weight"))
return __compute_curve_orientation(
weight_score,
previous_height_score["mean_weight_value"],
previous_previous_height_score["mean_weight_value"],
)
@login_required @login_required
@require_http_methods(["GET"]) @require_http_methods(["GET"])
def generate_week_report(request, gymnast_id, season=None, week_number=None): def generate_report_for_period(
"""Genere un rapport hebdomadaire. request, gymnast_id, season, period_value, date_begin, date_end, period="week"
):
TODO: devrait prendre deux dates en paramètre afin de pouvoir resservir pour les rapports mensuels et annuels. """Génère un rapport de toutes les informations entre les deux dates passées en paramètre.
Args: Args:
gymnast_id (int) Identifiant de la classe Gymnast gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine date_begin (datetime) Date de début de la période à considérer # pendulum
week_number (int) Numéro de semaine date_end (datetime) Date de fin de la période à considérer # pendulum
""" """
print(date_begin)
print(date_end)
today = pendulum.now().date()
gymnast = get_object_or_404(Gymnast, pk=gymnast_id) gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
season_informations = gymnast.season_informations.filter(season=season).first()
if season is None:
date_begin = pendulum.now().date()
season, week_number = from_date_to_week_number(date_begin)
else:
date_begin, _ = from_week_number_to_date(season, week_number)
date_begin = date_begin.date()
#
# PHYSIOLOGICAL INFORMATIONS # PHYSIOLOGICAL INFORMATIONS
#
# Mindstate Score # Mindstate Score
mindstate_week_score = ( mindstate_score = gymnast.mindstate.filter(
gymnast.mindstate.filter(season=season) date__gte=date_begin, date__lte=date_end
.filter(week_number=week_number) ).aggregate(
.aggregate(mean_mindstate_value=Avg("score")) min_mindstate_value=Min("score"),
mean_mindstate_value=Avg("score"),
max_mindstate_value=Max("score"),
) )
mindstate_queryset = MindState.objects.filter(gymnast=gymnast).order_by("-date") height_weight_value = gymnast.height_weight.filter(
have_physiological = False date__gte=date_begin, date__lte=date_end
if mindstate_week_score["mean_mindstate_value"]: ).aggregate(
lasts_mindstate = list(mindstate_queryset.values_list("score", flat=True)[1:6]) min_height_value=Min("height"),
mindstate_analyse = analyse_score( mean_height_value=Avg("height"),
mindstate_week_score["mean_mindstate_value"], lasts_mindstate max_height_value=Max("height"),
) min_weight_value=Min("weight"),
else: mean_weight_value=Avg("weight"),
mindstate_analyse = None max_weight_value=Max("weight"),
height_weight_week_value = (
gymnast.height_weight.filter(season=season)
.filter(week_number=week_number)
.aggregate(mean_height_value=Avg("height"), mean_weight_value=Avg("weight"))
) )
height_weight_queryset = HeightWeight.objects.filter(gymnast=gymnast).order_by( intensity_value = gymnast.intensities.filter(
"-date" date__gte=date_begin, date__lte=date_end
).aggregate(
mean_intensity_time_value=Avg("time"),
mean_intensity_difficulty_value=Avg("difficulty"),
mean_quantity_of_skill_value=Avg("quantity_of_skill"),
mean_number_of_passes_value=Avg("number_of_passes"),
min_intensity_time_value=Min("time"),
min_intensity_difficulty_value=Min("difficulty"),
min_quantity_of_skill_value=Min("quantity_of_skill"),
min_number_of_passes_value=Min("number_of_passes"),
max_intensity_time_value=Max("time"),
max_intensity_difficulty_value=Max("difficulty"),
max_quantity_of_skill_value=Max("quantity_of_skill"),
max_number_of_passes_value=Max("number_of_passes"),
) )
if height_weight_week_value["mean_height_value"]:
lasts_height = list(
height_weight_queryset.values_list("height", flat=True)[1:6]
)
height_analyse = analyse_score(
height_weight_week_value["mean_height_value"], lasts_height
)
else:
height_analyse = None
if height_weight_week_value["mean_weight_value"]:
lasts_weight = list(
height_weight_queryset.values_list("weight", flat=True)[1:6]
)
weight_analyse = analyse_score(
height_weight_week_value["mean_weight_value"], lasts_weight
)
else:
weight_analyse = None
intensity_week_value = (
gymnast.intensities.filter(season=season)
.filter(week_number=week_number)
.aggregate(
mean_intensity_time__value=Avg("time"),
mean_intensity_difficulty_value=Avg("difficulty"),
mean_quantity_of_skill_value=Avg("quantity_of_skill"),
mean_number_of_passes_value=Avg("number_of_passes"),
)
)
#
# BEST TOF # BEST TOF
# number_of_tof_straightjump = Chrono.objects.filter(
best_tof_straightjump = ( gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
Chrono.objects.filter(gymnast=gymnast) ).count()
.filter(chrono_type=0) best_tof_straightjump = Chrono.objects.filter(
.order_by("-score") gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
.first() ).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
) )
best_tof_routine_1 = (
Chrono.objects.filter(gymnast=gymnast) number_of_tof_q1r1 = Chrono.objects.filter(
.filter(chrono_type=1) gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=1
.order_by("-score") ).count()
.first() best_tof_q1r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=1
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
) )
best_tof_routine_2 = (
Chrono.objects.filter(gymnast=gymnast) number_of_tof_q1r2 = Chrono.objects.filter(
.filter(chrono_type=2) gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=2
.order_by("-score") ).count()
.first() best_tof_q1r2 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=2
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
) )
#
number_of_tof_q2r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_q2r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_sf = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_sf = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_f = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_f = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
# BEST SCORES # BEST SCORES
#
best_point_routine_1 = ( best_point_routine_1 = (
Point.objects.filter(gymnast=gymnast) Point.objects.filter(
.filter(routine_type=1) gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=1
)
.order_by("-total") .order_by("-total")
.first() .first()
) )
best_point_routine_2 = ( best_point_routine_2 = (
Point.objects.filter(gymnast=gymnast) Point.objects.filter(
.filter(routine_type=2) gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=2
)
.order_by("-total") .order_by("-total")
.first() .first()
) )
best_point_routine_3 = ( best_point_routine_3 = (
Point.objects.filter(gymnast=gymnast) Point.objects.filter(
.filter(routine_type=3) gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=3
)
.order_by("-total") .order_by("-total")
.first() .first()
) )
best_point_routine_4 = ( best_point_routine_4 = (
Point.objects.filter(gymnast=gymnast) Point.objects.filter(
.filter(routine_type=4) gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=4
)
.order_by("-total") .order_by("-total")
.first() .first()
) )
best_point_routine_5 = ( best_point_routine_5 = (
Point.objects.filter(gymnast=gymnast) Point.objects.filter(
.filter(routine_type=5) gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=5
)
.order_by("-total") .order_by("-total")
.first() .first()
) )
#
# ROUTINES # ROUTINES
#
routine_1 = ( routine_1 = (
gymnast.has_routine.filter(routine_type=1) gymnast.has_routine.filter(routine_type=1)
.filter(date_begin__lte=date_begin) .filter(date_begin__lte=date_begin)
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first() .first()
) )
routine_1_done_stat = ( routine_1_done_stat = gymnast.number_of_routine_done.filter(
gymnast.number_of_routine_done.filter(routine_type=1) routine_type=1, date__gte=date_begin, date__lte=date_end
.filter(season=season) ).aggregate(
.filter(week_number=week_number) total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
) )
routine_2 = ( routine_2 = (
@ -949,13 +1122,10 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first() .first()
) )
routine_2_done_stat = ( routine_2_done_stat = gymnast.number_of_routine_done.filter(
gymnast.number_of_routine_done.filter(routine_type=2) routine_type=2, date__gte=date_begin, date__lte=date_end
.filter(season=season) ).aggregate(
.filter(week_number=week_number) total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
) )
routine_3 = ( routine_3 = (
@ -964,13 +1134,10 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first() .first()
) )
routine_3_done_stat = ( routine_3_done_stat = gymnast.number_of_routine_done.filter(
gymnast.number_of_routine_done.filter(routine_type=3) routine_type=3, date__gte=date_begin, date__lte=date_end
.filter(season=season) ).aggregate(
.filter(week_number=week_number) total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
) )
routine_4 = ( routine_4 = (
@ -979,13 +1146,10 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first() .first()
) )
routine_4_done_stat = ( routine_4_done_stat = gymnast.number_of_routine_done.filter(
gymnast.number_of_routine_done.filter(routine_type=4) routine_type=4, date__gte=date_begin, date__lte=date_end
.filter(season=season) ).aggregate(
.filter(week_number=week_number) total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
) )
routine_5 = ( routine_5 = (
@ -994,32 +1158,18 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first() .first()
) )
routine_5_done_stat = ( routine_5_done_stat = gymnast.number_of_routine_done.filter(
gymnast.number_of_routine_done.filter(routine_type=5) routine_type=5, date__gte=date_begin, date__lte=date_end
.filter(season=season) ).aggregate(
.filter(week_number=week_number) total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
) )
#
# LAST LEARNED SKILLS
#
# TODO: il faudrait que ce soit classer d'abord par DATE et pas par skill.
#
# learned_skills = (
# LearnedSkill.objects.filter(gymnast=gymnast.id)
# .annotate(skill_notation=F("skill__notation"))
# .order_by("skill_notation", "-date")
# .distinct("skill_notation")[:6]
# )
learned_skills = LearnedSkill.objects.filter(gymnast=gymnast.id).order_by("-date")[
:6
]
# # LAST LEARNED SKILLS
learned_skills = LearnedSkill.objects.filter(
gymnast=gymnast.id, date__gte=date_begin, date__lte=date_end
).order_by("-date")
# PLANNED SKILLS # PLANNED SKILLS
#
plan_list = ( plan_list = (
Plan.objects.filter(gymnast=gymnast, educative__in=(Skill.objects.all())) Plan.objects.filter(gymnast=gymnast, educative__in=(Skill.objects.all()))
.filter(Q(is_done=False) | Q(date__gte=date.today())) .filter(Q(is_done=False) | Q(date__gte=date.today()))
@ -1027,27 +1177,24 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
.distinct()[:6] .distinct()[:6]
) )
#
# NEXT EVENTS # NEXT EVENTS
#
next_event_list = Event.objects.filter( next_event_list = Event.objects.filter(
gymnasts=gymnast, date_begin__gte=date_begin gymnasts=gymnast, date_begin__gte=date_begin
).order_by("date_begin")[:5] ).order_by("date_begin")[:5]
#
# NOTES # NOTES
# # begin_of_the_week = date_begin
begin_of_the_week = date_begin # if date_begin.weekday() != 0:
if date_begin.weekday() != 0: # begin_of_the_week -= timedelta(date_begin.weekday())
begin_of_the_week -= timedelta(date_begin.weekday())
notes = ( notes = (
gymnast.remarks.filter(date__gte=begin_of_the_week) gymnast.remarks.filter(date__gte=date_begin, date__lte=date_end)
.filter(status=1) .filter(status=1)
.order_by("date") .order_by("date")
) )
context = { context = {
# HEADER
"SITE_TITLE": settings.SITE_TITLE, "SITE_TITLE": settings.SITE_TITLE,
"CLUB_NAME": settings.CLUB_NAME, "CLUB_NAME": settings.CLUB_NAME,
"ADDRESS": settings.ADDRESS, "ADDRESS": settings.ADDRESS,
@ -1056,34 +1203,49 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
"HEAD_COACH": settings.HEAD_COACH, "HEAD_COACH": settings.HEAD_COACH,
"MOBILE_PHONE": settings.MOBILE_PHONE, "MOBILE_PHONE": settings.MOBILE_PHONE,
"HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL, "HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL,
"week_number": week_number,
"gymnast": gymnast,
"today": date_begin,
"season": season, "season": season,
"last_mindstate": mindstate_week_score["mean_mindstate_value"], "date_begin": date_begin,
"mindstate_analyse": mindstate_analyse, "date_end": date_end,
"last_height": height_weight_week_value["mean_height_value"], "period": period,
"last_weigth": height_weight_week_value["mean_weight_value"], "period_value": period_value,
"height_analyse": height_analyse, "today": today,
"weight_analyse": weight_analyse, # GYMNAST INFORMATIONS
"gymnast": gymnast,
"season_informations": season_informations,
# MEDICAL INFORMATIONS
"mindstate_score": mindstate_score,
"height_weight_value": height_weight_value,
# INTENSITY
"intensity_value": intensity_value,
# TOF
"number_of_tof_straightjump": number_of_tof_straightjump,
"best_tof_straightjump": best_tof_straightjump, "best_tof_straightjump": best_tof_straightjump,
"best_tof_routine_1": best_tof_routine_1, "number_of_tof_q1r1": number_of_tof_q1r1,
"best_tof_routine_2": best_tof_routine_2, "best_tof_q1r1": best_tof_q1r1,
"best_point_routine_1": best_point_routine_1, "number_of_tof_q1r2": number_of_tof_q1r2,
"best_point_routine_2": best_point_routine_2, "best_tof_q1r2": best_tof_q1r2,
"best_point_routine_3": best_point_routine_3, "number_of_tof_q2r1": number_of_tof_q2r1,
"best_point_routine_4": best_point_routine_4, "best_tof_q2r1": best_tof_q2r1,
"best_point_routine_5": best_point_routine_5, "number_of_tof_sf": number_of_tof_sf,
"routine_1": routine_1, "best_tof_sf": best_tof_sf,
"routine_2": routine_2, "number_of_tof_f": number_of_tof_f,
"routine_3": routine_3, "best_tof_f": best_tof_f,
"routine_4": routine_4, # SCORES
"routine_5": routine_5, "best_point_q1r1": best_point_routine_1,
"routine_1_done_stat": routine_1_done_stat, "best_point_q1r2": best_point_routine_2,
"routine_2_done_stat": routine_2_done_stat, "best_point_q2r1": best_point_routine_3,
"routine_3_done_stat": routine_3_done_stat, "best_point_sf": best_point_routine_4,
"routine_4_done_stat": routine_4_done_stat, "best_point_f": best_point_routine_5,
"routine_5_done_stat": routine_5_done_stat, "q1r1": routine_1,
"q1r2": routine_2,
"q2r1": routine_3,
"sfinal": routine_4,
"final": routine_5,
"q1r1_done_stat": routine_1_done_stat,
"q1r2_done_stat": routine_2_done_stat,
"q2r1_done_stat": routine_3_done_stat,
"sfinal_done_stat": routine_4_done_stat,
"final_done_stat": routine_5_done_stat,
"LEARNING_STEP_CHOICES": LEARNING_STEP_CHOICES, "LEARNING_STEP_CHOICES": LEARNING_STEP_CHOICES,
"learned_skills": learned_skills, "learned_skills": learned_skills,
"plan_list": plan_list, "plan_list": plan_list,
@ -1091,25 +1253,94 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
"notes": notes, "notes": notes,
} }
# return render(request, "gymnasts/reports/report_week.html", context) return render(request, "gymnasts/reports/report_periodical.html", context)
response = HttpResponse(content_type="application/pdf") # response = HttpResponse(content_type="application/pdf")
response[ # response[
"Content-Disposition" # "Content-Disposition"
] = f"attachment; filename={gymnast.last_name}_{gymnast.first_name}_weekreport_{season}_w{week_number}.pdf" # ] = f"attachment; filename={gymnast.last_name}_{gymnast.first_name}_{period}-report_{date_begin}_{date_end}.pdf"
html = render_to_string("gymnasts/reports/report_week.html", context) # html = render_to_string("gymnasts/reports/report_periodical.html", context)
# font_config = FontConfiguration() # # font_config = FontConfiguration()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf( # HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
response, # response,
stylesheets=[ # stylesheets=[
CSS(settings.STATICFILES_DIRS[0] + "/css/gymnast_report.css"), # CSS(settings.STATICFILES_DIRS[0] + "/css/gymnast_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/black-dashboard_report.css"), # CSS(settings.STATICFILES_DIRS[0] + "/css/black-dashboard_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/font_awesome_all_5.15.3.css"), # CSS(settings.STATICFILES_DIRS[0] + "/css/font_awesome_all_5.15.3.css"),
], # ],
) # , font_config=font_config) # ) # , font_config=font_config)
return response # return response
@login_required
@require_http_methods(["GET"])
def generate_season_report(request, gymnast_id, season):
"""Génère un rapport pour une saison passée en paramètre.
Args:
gymnast_id (int) identifiant de gymnaste
season (str) saison
"""
date_begin, date_end = from_season_to_date(season)
return generate_report_for_period(
request,
gymnast_id,
season,
season,
date_begin,
date_end,
period="season",
)
@login_required
@require_http_methods(["GET"])
def generate_month_report(request, gymnast_id, season, month_number):
"""Génère un rapport pour une saison et un mois passée en paramètre.
Args:
gymnast_id (int) identifiant de gymnaste
season (str) saison
month_number (int) mois de l'année
"""
date_begin, date_end = from_month_number_to_date(season, month_number)
month = date_begin.format("MMMM")
return generate_report_for_period(
request,
gymnast_id,
season,
month,
date_begin,
date_end,
period="month",
)
@login_required
@require_http_methods(["GET"])
def generate_week_report(request, gymnast_id, season, week_number):
"""Génère un rapport hebdomadaire.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine
week_number (int) Numéro de semaine
"""
date_begin, date_end = from_week_number_to_date(season, week_number)
return generate_report_for_period(
request,
gymnast_id,
season,
week_number,
date_begin,
date_end,
period="week",
)
@login_required @login_required
@ -1126,10 +1357,10 @@ def generate_timeline_report(
eton les trie par date. eton les trie par date.
Args: Args:
gymnast_id (int) identifiant du gymnast gymnast_id (int) identifiant du gymnast
season (int) saison season (int) saison
week_number (int) numéro de semaine week_number (int) numéro de semaine
date (date) date date (date) date
""" """
gymnast = get_object_or_404(Gymnast, pk=gymnast_id) gymnast = get_object_or_404(Gymnast, pk=gymnast_id)

View File

@ -21,14 +21,16 @@ def from_week_number_to_date(season, week_number):
Examples: Examples:
>>> from jarvis.tools.date_week_transition import from_week_number_to_date >>> from jarvis.tools.date_week_transition import from_week_number_to_date
>>> from_week_number_to_date("2022-2023", 6) >>> from_week_number_to_date("2022-2023", 6)
>>> (DateTime(2022, 10, 3, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2022, 10, 9, 0, 0, 0, tzinfo=Timezone('UTC'))) >>> (Date(2022, 10, 3), Date(2022, 10, 9))
>>> from_week_number_to_date("2022-2023", 22) >>> from_week_number_to_date("2022-2023", 22)
>>> (DateTime(2023, 1, 30, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2023, 2, 5, 0, 0, 0, tzinfo=Timezone('UTC'))) >>> (Date(2023, 1, 30), Date(2023, 2, 5))
>>> from_week_number_to_date("2022-2023", 44) >>> from_week_number_to_date("2022-2023", 44)
>>> (DateTime(2023, 7, 3, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2023, 7, 9, 0, 0, 0, tzinfo=Timezone('UTC'))) >>> (Date(2023, 7, 3), Date(2023, 7, 9))
""" """
if week_number <= 0:
return None, None
dash = season.find("-") dash = season.find("-")
first_year = str(season[:dash]) first_year = str(season[:dash])
@ -49,7 +51,81 @@ def from_week_number_to_date(season, week_number):
start_date = pendulum.parse(year + week_string + str(week_number)) start_date = pendulum.parse(year + week_string + str(week_number))
end_date = start_date.add(days=6) end_date = start_date.add(days=6)
return start_date, end_date return start_date.date(), end_date.date()
def from_month_number_to_date(season, month_number):
"""Renvoie la date du début du mois et la date de fin du mois à
partir d'une saison et d'un numéro de mois.
Args:
season str saison (ex : "2022-2023")
month_number int numéro du mois (ex : 9)
Returns :
start_date Date du 1 du mois
end_date Date du dernier jour du mois
Examples:
>>> from jarvis.tools.date_week_transition import from_month_number_to_date
>>> from_month_number_to_date("2022-2023", 9)
>>> (Date(2022, 9, 1), Date(2022, 9, 30))
>>> from_month_number_to_date("2022-2023", 2)
>>> (Date(2023, 2, 1), Date(2023, 2, 28))
>>> from_month_number_to_date("2023-2024", 2)
>>> (Date(2023, 2, 1), Date(2023, 2, 29))
"""
if month_number <= 0:
return None, None
dash = season.find("-")
if month_number >= 1 and month_number <= 8:
year = int(season[dash + 1 :])
else:
year = int(season[:dash])
start_date = pendulum.datetime(year, month_number, 1)
end_date = start_date.end_of("month")
return start_date.date(), end_date.date()
def from_season_to_date(season):
"""Renvoie la date du début du mois et la date de fin du mois à
partir d'une saison et d'un numéro de mois.
Args:
season str saison (ex : "2022-2023")
Returns :
start_date Date du premier jour de la saison (ex : 1-9-2022)
end_date Date du dernier jour de la saison (ex : 31-8-2023)
Examples:
>>> from jarvis.tools.date_week_transition import from_season_to_date
>>> from_season_to_date("2022-2023")
>>> (Date(2022, 9, 1), Date(2023, 8, 31))
>>> from_season_to_date("2023-2024")
>>> (Date(2023, 9, 1), Date(2022, 8, 31))
>>> from_season_to_date("2022-2024")
>>> (None, None)
"""
dash = season.find("-")
first_year = int(season[:dash])
second_year = int(season[dash + 1 :])
if second_year != first_year + 1:
return None, None
start_date = pendulum.datetime(first_year, 9, 1)
end_date = pendulum.datetime(second_year, 8, 31)
return start_date.date(), end_date.date()
def from_date_to_week_number(the_date=None): def from_date_to_week_number(the_date=None):

View File

@ -3,7 +3,6 @@
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from datetime import date
from .date_week_transition import ( from .date_week_transition import (
get_number_of_weeks_between, get_number_of_weeks_between,
from_date_to_week_number, from_date_to_week_number,

View File

@ -1,8 +1,8 @@
@page { @page {
size: A4; size: A4;
margin: 1cm 1cm 1cm 1cm; margin: 1.5cm 1.5cm 1.5cm 1.5cm;
} }
@page :first { @page :first {
margin: 0.8cm -1cm 1cm 1.3cm; margin: 0.8cm -1cm 1cm 1.3cm;
} }