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>
</div>
<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>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>

View File

@ -72,6 +72,11 @@ AGE_CATOGORY_CHOICES = (
(22, "Senior"),
)
NOTE_STATUS_CHOICES = (
(0, "Draft"),
(1, "Published"),
)
class Chrono(Seasonisable):
"""
@ -399,11 +404,6 @@ class Note(Markdownizable, Seasonisable):
Notes relatives à un gymnaste
"""
STATUS_CHOICES = (
(0, "Draft"),
(1, "Published"),
)
gymnast = models.ForeignKey(
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"
)
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)
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
receiver = []
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")
for notification in gymnast.notifications.filter(
functionality=functionality
@ -734,7 +737,7 @@ def score_create_or_update(request, score_id=None, gymnast_id=None):
receiver,
fail_silently=False,
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>""",
)

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,
name="get_distinct_week_number_for_season_and_gymnast",
),
# path(
# r"report/<int:gymnast_id>/",
# views.generate_week_report,
# name="gymnast_report_export",
# ),
path(
r"report/<int:gymnast_id>/",
views.generate_week_report,
name="gymnast_report_export",
),
path(
r"report/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
r"report/periodical/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
views.generate_week_report,
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(
r"report/timeline/<int:gymnast_id>/",
views.generate_timeline_report,

View File

@ -7,6 +7,8 @@ from django.db.models import (
Q,
Avg,
Sum,
Min,
Max,
)
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
@ -22,7 +24,7 @@ from weasyprint import HTML, CSS
# from weasyprint.fonts import FontConfiguration
import pendulum
from datetime import date, timedelta
from datetime import date
from statistics import mean
from jarvis.followup.models import Event
@ -51,6 +53,8 @@ from jarvis.tools.models import Season
from jarvis.tools.date_week_transition import (
from_date_to_week_number,
from_week_number_to_date,
from_month_number_to_date,
from_season_to_date,
)
from .models import Gymnast
@ -597,7 +601,7 @@ def gymnast_display_skill(request, gymnast_id):
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.
Args:
@ -629,6 +633,38 @@ def analyse_score(value, value_list):
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):
"""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)
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
@require_http_methods(["GET"])
def generate_week_report(request, gymnast_id, season=None, week_number=None):
"""Genere un rapport hebdomadaire.
TODO: devrait prendre deux dates en paramètre afin de pouvoir resservir pour les rapports mensuels et annuels.
def generate_report_for_period(
request, gymnast_id, season, period_value, date_begin, date_end, period="week"
):
"""Génère un rapport de toutes les informations entre les deux dates passées en paramètre.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine
week_number (int) Numéro de semaine
gymnast_id (int) Identifiant de la classe Gymnast
date_begin (datetime) Date de début de la période à considérer # pendulum
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)
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
#
# Mindstate Score
mindstate_week_score = (
gymnast.mindstate.filter(season=season)
.filter(week_number=week_number)
.aggregate(mean_mindstate_value=Avg("score"))
mindstate_score = gymnast.mindstate.filter(
date__gte=date_begin, date__lte=date_end
).aggregate(
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")
have_physiological = False
if mindstate_week_score["mean_mindstate_value"]:
lasts_mindstate = list(mindstate_queryset.values_list("score", flat=True)[1:6])
mindstate_analyse = analyse_score(
mindstate_week_score["mean_mindstate_value"], lasts_mindstate
)
else:
mindstate_analyse = None
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_value = gymnast.height_weight.filter(
date__gte=date_begin, date__lte=date_end
).aggregate(
min_height_value=Min("height"),
mean_height_value=Avg("height"),
max_height_value=Max("height"),
min_weight_value=Min("weight"),
mean_weight_value=Avg("weight"),
max_weight_value=Max("weight"),
)
height_weight_queryset = HeightWeight.objects.filter(gymnast=gymnast).order_by(
"-date"
intensity_value = gymnast.intensities.filter(
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_straightjump = (
Chrono.objects.filter(gymnast=gymnast)
.filter(chrono_type=0)
.order_by("-score")
.first()
number_of_tof_straightjump = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
).count()
best_tof_straightjump = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
).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)
.filter(chrono_type=1)
.order_by("-score")
.first()
number_of_tof_q1r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=1
).count()
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)
.filter(chrono_type=2)
.order_by("-score")
.first()
number_of_tof_q1r2 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=2
).count()
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_point_routine_1 = (
Point.objects.filter(gymnast=gymnast)
.filter(routine_type=1)
Point.objects.filter(
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=1
)
.order_by("-total")
.first()
)
best_point_routine_2 = (
Point.objects.filter(gymnast=gymnast)
.filter(routine_type=2)
Point.objects.filter(
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=2
)
.order_by("-total")
.first()
)
best_point_routine_3 = (
Point.objects.filter(gymnast=gymnast)
.filter(routine_type=3)
Point.objects.filter(
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=3
)
.order_by("-total")
.first()
)
best_point_routine_4 = (
Point.objects.filter(gymnast=gymnast)
.filter(routine_type=4)
Point.objects.filter(
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=4
)
.order_by("-total")
.first()
)
best_point_routine_5 = (
Point.objects.filter(gymnast=gymnast)
.filter(routine_type=5)
Point.objects.filter(
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=5
)
.order_by("-total")
.first()
)
#
# ROUTINES
#
routine_1 = (
gymnast.has_routine.filter(routine_type=1)
.filter(date_begin__lte=date_begin)
.filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True))
.first()
)
routine_1_done_stat = (
gymnast.number_of_routine_done.filter(routine_type=1)
.filter(season=season)
.filter(week_number=week_number)
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
routine_1_done_stat = gymnast.number_of_routine_done.filter(
routine_type=1, date__gte=date_begin, date__lte=date_end
).aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
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))
.first()
)
routine_2_done_stat = (
gymnast.number_of_routine_done.filter(routine_type=2)
.filter(season=season)
.filter(week_number=week_number)
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
routine_2_done_stat = gymnast.number_of_routine_done.filter(
routine_type=2, date__gte=date_begin, date__lte=date_end
).aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
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))
.first()
)
routine_3_done_stat = (
gymnast.number_of_routine_done.filter(routine_type=3)
.filter(season=season)
.filter(week_number=week_number)
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
routine_3_done_stat = gymnast.number_of_routine_done.filter(
routine_type=3, date__gte=date_begin, date__lte=date_end
).aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
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))
.first()
)
routine_4_done_stat = (
gymnast.number_of_routine_done.filter(routine_type=4)
.filter(season=season)
.filter(week_number=week_number)
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
routine_4_done_stat = gymnast.number_of_routine_done.filter(
routine_type=4, date__gte=date_begin, date__lte=date_end
).aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
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))
.first()
)
routine_5_done_stat = (
gymnast.number_of_routine_done.filter(routine_type=5)
.filter(season=season)
.filter(week_number=week_number)
.aggregate(
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
)
routine_5_done_stat = gymnast.number_of_routine_done.filter(
routine_type=5, date__gte=date_begin, date__lte=date_end
).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
#
plan_list = (
Plan.objects.filter(gymnast=gymnast, educative__in=(Skill.objects.all()))
.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]
)
#
# NEXT EVENTS
#
next_event_list = Event.objects.filter(
gymnasts=gymnast, date_begin__gte=date_begin
).order_by("date_begin")[:5]
#
# NOTES
#
begin_of_the_week = date_begin
if date_begin.weekday() != 0:
begin_of_the_week -= timedelta(date_begin.weekday())
# begin_of_the_week = date_begin
# if date_begin.weekday() != 0:
# begin_of_the_week -= timedelta(date_begin.weekday())
notes = (
gymnast.remarks.filter(date__gte=begin_of_the_week)
gymnast.remarks.filter(date__gte=date_begin, date__lte=date_end)
.filter(status=1)
.order_by("date")
)
context = {
# HEADER
"SITE_TITLE": settings.SITE_TITLE,
"CLUB_NAME": settings.CLUB_NAME,
"ADDRESS": settings.ADDRESS,
@ -1056,34 +1203,49 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
"HEAD_COACH": settings.HEAD_COACH,
"MOBILE_PHONE": settings.MOBILE_PHONE,
"HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL,
"week_number": week_number,
"gymnast": gymnast,
"today": date_begin,
"season": season,
"last_mindstate": mindstate_week_score["mean_mindstate_value"],
"mindstate_analyse": mindstate_analyse,
"last_height": height_weight_week_value["mean_height_value"],
"last_weigth": height_weight_week_value["mean_weight_value"],
"height_analyse": height_analyse,
"weight_analyse": weight_analyse,
"date_begin": date_begin,
"date_end": date_end,
"period": period,
"period_value": period_value,
"today": today,
# 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_routine_1": best_tof_routine_1,
"best_tof_routine_2": best_tof_routine_2,
"best_point_routine_1": best_point_routine_1,
"best_point_routine_2": best_point_routine_2,
"best_point_routine_3": best_point_routine_3,
"best_point_routine_4": best_point_routine_4,
"best_point_routine_5": best_point_routine_5,
"routine_1": routine_1,
"routine_2": routine_2,
"routine_3": routine_3,
"routine_4": routine_4,
"routine_5": routine_5,
"routine_1_done_stat": routine_1_done_stat,
"routine_2_done_stat": routine_2_done_stat,
"routine_3_done_stat": routine_3_done_stat,
"routine_4_done_stat": routine_4_done_stat,
"routine_5_done_stat": routine_5_done_stat,
"number_of_tof_q1r1": number_of_tof_q1r1,
"best_tof_q1r1": best_tof_q1r1,
"number_of_tof_q1r2": number_of_tof_q1r2,
"best_tof_q1r2": best_tof_q1r2,
"number_of_tof_q2r1": number_of_tof_q2r1,
"best_tof_q2r1": best_tof_q2r1,
"number_of_tof_sf": number_of_tof_sf,
"best_tof_sf": best_tof_sf,
"number_of_tof_f": number_of_tof_f,
"best_tof_f": best_tof_f,
# SCORES
"best_point_q1r1": best_point_routine_1,
"best_point_q1r2": best_point_routine_2,
"best_point_q2r1": best_point_routine_3,
"best_point_sf": best_point_routine_4,
"best_point_f": best_point_routine_5,
"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,
"learned_skills": learned_skills,
"plan_list": plan_list,
@ -1091,25 +1253,94 @@ def generate_week_report(request, gymnast_id, season=None, week_number=None):
"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[
"Content-Disposition"
] = f"attachment; filename={gymnast.last_name}_{gymnast.first_name}_weekreport_{season}_w{week_number}.pdf"
# response = HttpResponse(content_type="application/pdf")
# response[
# "Content-Disposition"
# ] = 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()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
response,
stylesheets=[
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/font_awesome_all_5.15.3.css"),
],
) # , font_config=font_config)
return response
# # font_config = FontConfiguration()
# HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
# response,
# stylesheets=[
# 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/font_awesome_all_5.15.3.css"),
# ],
# ) # , font_config=font_config)
# 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
@ -1126,10 +1357,10 @@ def generate_timeline_report(
eton les trie par date.
Args:
gymnast_id (int) identifiant du gymnast
season (int) saison
week_number (int) numéro de semaine
date (date) date
gymnast_id (int) identifiant du gymnast
season (int) saison
week_number (int) numéro de semaine
date (date) date
"""
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:
>>> from jarvis.tools.date_week_transition import from_week_number_to_date
>>> 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)
>>> (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)
>>> (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("-")
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))
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):

View File

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

View File

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