From ac2643f66e6dbd5b280e35c8b8db39a9e62299c7 Mon Sep 17 00:00:00 2001 From: Gregory Trullemans Date: Mon, 19 Jun 2023 20:41:14 +0200 Subject: [PATCH] Big report update --- .../core/templates/dashboard/dashboard.html | 4 +- jarvis/followup/models.py | 16 +- jarvis/followup/views.py | 5 +- .../gymnasts/reports/report_periodical.html | 477 +++++++++++++ .../gymnasts/reports/report_week.html | 382 ----------- jarvis/people/urls.py | 22 +- jarvis/people/views.py | 637 ++++++++++++------ jarvis/tools/date_week_transition.py | 84 ++- jarvis/tools/models.py | 1 - static/css/gymnast_report.css | 4 +- 10 files changed, 1024 insertions(+), 608 deletions(-) create mode 100644 jarvis/people/templates/gymnasts/reports/report_periodical.html delete mode 100644 jarvis/people/templates/gymnasts/reports/report_week.html diff --git a/jarvis/core/templates/dashboard/dashboard.html b/jarvis/core/templates/dashboard/dashboard.html index a76614d..5f6d810 100644 --- a/jarvis/core/templates/dashboard/dashboard.html +++ b/jarvis/core/templates/dashboard/dashboard.html @@ -27,9 +27,9 @@

Hi {{ user.username }} !

-

Welcome to Jarvi v0.83 (last update : 12-06-2023)

+

Welcome to Jarvi v0.84 (last update : 12-06-2023)

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 email.

-

You can find the user manuel here (in french).

+

You can find the user manual here (in french).

diff --git a/jarvis/followup/models.py b/jarvis/followup/models.py index 9f0244c..396d641 100644 --- a/jarvis/followup/models.py +++ b/jarvis/followup/models.py @@ -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) diff --git a/jarvis/followup/views.py b/jarvis/followup/views.py index deaa767..68e2350 100644 --- a/jarvis/followup/views.py +++ b/jarvis/followup/views.py @@ -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"""

Bonjour,

-

Un nouveau score a été enregistré pour {gymnast}.


+

Un nouveau score a été enregistré pour {gymnast} ({event}) : {routine_type} - {total}.


Excellente journée

Jarvis

""", ) diff --git a/jarvis/people/templates/gymnasts/reports/report_periodical.html b/jarvis/people/templates/gymnasts/reports/report_periodical.html new file mode 100644 index 0000000..6abb7a8 --- /dev/null +++ b/jarvis/people/templates/gymnasts/reports/report_periodical.html @@ -0,0 +1,477 @@ +{% load static %} + + + + + + + + + + + + + + + + + {{ gymnast.first_name }} {{ gymnast.last_name }} + + + + + + + + + +
+
+
+ {{ SITE_TITLE }} - {{ CLUB_NAME }}
+ {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
+ 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" }}) +
+
+ Head Coach : {{ HEAD_COACH }}
+ {{ HEAD_COACH_EMAIL }}
+ {{ today | date:"j F Y" }} +
+
+
+
+ +
+
+ +
+
+

{{ gymnast.first_name }} {{ gymnast.last_name }}

+

{{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.
+ {{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)
+ {{ season_informations.get_category_display }} +

+ + {% if gymnast.informations %} + {{ gymnast.to_markdown | safe }} + {% endif %} +
+
+
+
+ {% if mindstate_score or height_weight_value %} +

Physiological

+ + + + + + + + {% if mindstate_score %} + + + + + + + {% endif %} + {% if height_weight_value.mean_height_value %} + + + + + + + {% endif %} + {% if height_weight_value.mean_weight_value %} + + + + + + + {% endif %} +
MinAverageMax
Mind state{{ mindstate_score.min_mindstate_value }}{{ mindstate_score.mean_mindstate_value|stringformat:".1f" }}{{ mindstate_score.max_mindstate_value }}
Height{{ height_weight_value.min_height_value }}{{ height_weight_value.mean_height_value|stringformat:".1f" }}{{ height_weight_value.max_height_value }}
Weight{{ height_weight_value.min_weight_value }}{{ height_weight_value.mean_weight_value|stringformat:".1f" }}{{ height_weight_value.max_weight_value }}
+ {% endif %} +
+
+

Intensity

+ {% if intensity_value %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MinAverageMax
Time{{ intensity_value.min_intensity_time_value }}{{ intensity_value.mean_intensity_time_value|stringformat:".0f" }}{{ intensity_value.max_intensity_time_value }}
Diff.{{ intensity_value.min_intensity_difficulty_value }}{{ intensity_value.mean_intensity_difficulty_value|stringformat:".0f" }}{{ intensity_value.max_intensity_difficulty_value }}
# Skill{{ intensity_value.min_quantity_of_skill_value }}{{ intensity_value.mean_quantity_of_skill_value|stringformat:".0f" }}{{ intensity_value.max_quantity_of_skill_value }}
# Passes{{ intensity_value.min_number_of_passes_value }}{{ intensity_value.mean_number_of_passes_value|stringformat:".0f" }}{{ intensity_value.max_number_of_passes_value }}
+ {% else %} + No intensty for selected period. + {% endif %} +
+
+
+
+
+

Time of flight

+ {% if number_of_tof_straightjump or number_of_tof_q1r1 or number_of_tof_q1r2 %} + + + + + + + + + {% if best_tof_straightjump.mean_score %} + + + + + + + + {% endif %} + {% if best_tof_q1r1.mean_score %} + + + + + + + + {% endif %} + {% if best_tof_q1r2.mean_score %} + + + + + + + + {% endif %} + {% if best_tof_q2r1.mean_score %} + + + + + + + + {% endif %} + {% if best_tof_sf.mean_score %} + + + + + + + + {% endif %} + {% if best_tof_f.mean_score %} + + + + + + + + {% endif %} +
MinAverageMax
10 |({{ number_of_tof_straightjump }}){{ best_tof_straightjump.min_tof }} ({{ best_tof_straightjump.min_score }}){{ best_tof_straightjump.mean_tof|stringformat:".3f" }} ({{ best_tof_straightjump.mean_score|stringformat:".3f" }}){{ best_tof_straightjump.max_tof }} ({{ best_tof_straightjump.max_score }})
Q1R1({{ number_of_tof_q1r1 }}){{ best_tof_q1r1.min_tof }} ({{ best_tof_q1r1.min_score }}){{ best_tof_q1r1.mean_tof|stringformat:".3f" }} ({{ best_tof_q1r1.mean_score|stringformat:".3f" }}){{ best_tof_q1r1.max_tof }} ({{ best_tof_q1r1.max_score }})
Q1R2({{ number_of_tof_q1r2 }}){{ best_tof_q1r2.min_tof }} ({{ best_tof_q1r2.min_score }}){{ best_tof_q1r2.mean_tof|stringformat:".3f" }} ({{ best_tof_q1r2.mean_score|stringformat:".3f" }}){{ best_tof_q1r2.max_tof }} ({{ best_tof_q1r2.max_score }})
Q2R1({{ number_of_tof_q2r1 }}){{ best_tof_q2r1.min_tof }} ({{ best_tof_q2r1.min_score }}){{ best_tof_q2r1.mean_tof|stringformat:".3f" }} ({{ best_tof_q2r1.mean_score|stringformat:".3f" }}){{ best_tof_q2r1.max_tof }} ({{ best_tof_q2r1.max_score }})
SF({{ number_of_tof_sf }}){{ best_tof_sf.min_tof }} ({{ best_tof_sf.min_score }}){{ best_tof_sf.mean_tof|stringformat:".3f" }} ({{ best_tof_sf.mean_score|stringformat:".3f" }}){{ best_tof_sf.max_tof }} ({{ best_tof_sf.max_score }})
F({{ number_of_tof_f }}){{ best_tof_f.min_tof }} ({{ best_tof_f.min_score }}){{ best_tof_f.mean_tof|stringformat:".3f" }} ({{ best_tof_f.mean_score|stringformat:".3f" }}){{ best_tof_f.max_tof }} ({{ best_tof_f.max_score }})
+ {% else %} + No chrono/ToF found. + {% endif %} +
+ +
+

Best Scores

+ {% 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 %} + + + + + + + + + + + {% if best_point_routine_1 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_2 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_3 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_4 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_5 %} + + + + + + + + + + {% endif %} +
Exe.Diff.HDToFTotal
Q1R1{{ best_point_routine_1.point_execution }}{{ best_point_routine_1.point_difficulty }}{{ best_point_routine_1.point_horizontal_displacement }}{{ best_point_routine_1.point_time_of_flight }}{{ best_point_routine_1.total }}{{ best_point_routine_1.event.date_begin|date:"j M Y" }}
Q1R2{{ best_point_routine_2.point_execution }}{{ best_point_routine_2.point_difficulty }}{{ best_point_routine_2.point_horizontal_displacement }}{{ best_point_routine_2.point_time_of_flight }}{{ best_point_routine_2.total }}{{ best_point_routine_2.event.date_begin|date:"j M Y" }}
Q2R1{{ best_point_routine_3.point_execution }}{{ best_point_routine_3.point_difficulty }}{{ best_point_routine_3.point_horizontal_displacement }}{{ best_point_routine_3.point_time_of_flight }}{{ best_point_routine_3.total }}{{ best_point_routine_3.event.date_begin|date:"j M Y" }}
SF{{ best_point_routine_4.point_execution }}{{ best_point_routine_4.point_difficulty }}{{ best_point_routine_4.point_horizontal_displacement }}{{ best_point_routine_4.point_time_of_flight }}{{ best_point_routine_4.total }}{{ best_point_routine_4.event.date_begin|date:"j M Y" }}
F{{ best_point_routine_5.point_execution }}{{ best_point_routine_5.point_difficulty }}{{ best_point_routine_5.point_horizontal_displacement }}{{ best_point_routine_5.point_time_of_flight }}{{ best_point_routine_5.total }}{{ best_point_routine_5.event.date_begin|date:"j M Y" }}
+ {% else %} + No scores during the selected week. + {% endif %} +
+
+
+
+
+

Last learned skills

+ {% if learned_skills %} + + {% for learned_skill in learned_skills %} + + + + + + + {% endfor %} +
{{ learned_skill.skill.short_label }}({{ learned_skill.get_learning_step_display }}){{ learned_skill.skill.notation }}{{ learned_skill.date|date:"j M Y" }}
+ {% else %} + No new skill learned on the selected week. + {% endif %} +
+
+

Objectives

+ {% if plan_list %} + + {% for plan in plan_list %} + + + + + + + {% endfor %} +
{{ plan.educative.short_label }}({{ plan.get_learning_step_display }}){{ plan.skill.notation }}{{ plan.date | date:"j M Y" }}
+ {% else %} + No objective for the selected week. + {% endif %} +
+
+
+
+
+

Next Events

+ {% if next_event_list %} + + {% for event in next_event_list %} + + + + + + {% endfor %} +
{{ event.date_begin | date:"j M Y" }}in {{ event.number_of_week_from_today }} week(s){{ event.name }}
+ {% else %} + No events on the selected week. + {% endif %} +
+
+
+
+
+

Notes

+ {% if notes %} + {% for note in notes %} + {{ note.to_markdown | safe }} + {% endfor %} + {% else %} + No note for the selected week. + {% endif %} +
+
+
+
+ {% if q1r1 or q1r2 or q2r1 or sfinal or final %} +
+

Routines

+
+
+
Q1 routine 1
+ {% if q1r1 %} + + {% for routine_skill in q1r1.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ q1r1.routine.difficulty }}
+ {% 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 %} +
+
+
Q1 routine 2
+ {% if q1r2 %} + + {% for routine_skill in q1r2.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ q1r2.routine.difficulty }}
+ {% 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 %} +
+
+
Q2 routine 1
+ {% if q2r1 %} + + {% for routine_skill in q2r1.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ q2r1.routine.difficulty }}
+ {% 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 %} +
+
+
Semi-final routine
+ {% if sfinal %} + + {% for routine_skill in sfinal.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ sfinal.routine.difficulty }}
+ {% 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 %} +
+
+
Final routine
+ {% if final %} + + {% for routine_skill in final.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ final.routine.difficulty }}
+ {% 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 %} +
+ {% endif %} +
+ + + + diff --git a/jarvis/people/templates/gymnasts/reports/report_week.html b/jarvis/people/templates/gymnasts/reports/report_week.html deleted file mode 100644 index 9468529..0000000 --- a/jarvis/people/templates/gymnasts/reports/report_week.html +++ /dev/null @@ -1,382 +0,0 @@ -{% load static %} - - - - - - - - - - - - - - - - - {{ gymnast.first_name }} {{ gymnast.last_name }} - - - - - - - - - -
-
-
- {{ SITE_TITLE }} - {{ CLUB_NAME }}
- {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
- Season {{ season }} - week {{ week_number }} -
-
- Head Coach : {{ HEAD_COACH }}
- {{ HEAD_COACH_EMAIL }}
- {{ today | date:"j F Y" }} -
-
-
-
- -
-
- -
-
-

{{ gymnast.first_name }} {{ gymnast.last_name }}

-

{{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.

- {% if gymnast.informations %} - {{ gymnast.to_markdown | safe }} - {% endif %} -
-
- {% if last_mindstate or last_height_weigth or mindstate_analyse or height_analyse or weight_analyse %} -

Physiological

- - {% if last_mindstate %} - - - - - - {% endif %} - {% if last_height and last_weight %} - - - - - - - - - - - {% endif %} -
Mind state{{ last_mindstate }}{% if mindstate_analyse %}{{ mindstate_analyse }}{% endif %}
Height{{ last_height }}{% if height_analyse %}{{ height_analyse }}{% endif %}
Weight{{ last_weigth }}{% if weight_analyse %}{{ weight_analyse }}{% endif %}
- {% endif %} -
-
-
-
-
-
-

Best ToF

- {% if best_tof_straightjump or best_tof_routine_1 or best_tof_routine_2 %} - - - - - - - - {% if best_tof_straightjump %} - - - - - - - {% endif %} - {% if best_tof_routine_1 %} - - - - - - - {% endif %} - {% if best_tof_routine_2 %} - - - - - - - {% endif %} -
ChronoToF
ToF |{{ best_tof_straightjump.score }}{{ best_tof_straightjump.tof }}{{ best_tof_straightjump.date|date:"j M Y" }}
Routine 1{{ best_tof_routine_1.score }}{{ best_tof_routine_1.tof }}{{ best_tof_routine_1.date|date:"j M Y" }}
Routine 2{{ best_tof_routine_2.score }}{{ best_tof_routine_2.tof }}{{ best_tof_routine_2.date|date:"j M Y" }}
- {% endif %} -
-
-
-

Best Scores

- {% if best_point_routine_1 or best_point_routine_2 %} - - - - - - - - - - - {% if best_point_routine_1 %} - - - - - - - - - - {% endif %} - {% if best_point_routine_2 %} - - - - - - - - - - {% endif %} - {% if best_point_routine_3 %} - - - - - - - - - - {% endif %} - {% if best_point_routine_4 %} - - - - - - - - - - {% endif %} - {% if best_point_routine_5 %} - - - - - - - - - - {% endif %} -
Exe.Diff.HDToFTotal
Routine 1{{ best_point_routine_1.point_execution }}{{ best_point_routine_1.point_difficulty }}{{ best_point_routine_1.point_horizontal_displacement }}{{ best_point_routine_1.point_time_of_flight }}{{ best_point_routine_1.total }}{{ best_point_routine_1.event.date_begin|date:"j M Y" }}
Routine 2{{ best_point_routine_2.point_execution }}{{ best_point_routine_2.point_difficulty }}{{ best_point_routine_2.point_horizontal_displacement }}{{ best_point_routine_2.point_time_of_flight }}{{ best_point_routine_2.total }}{{ best_point_routine_2.event.date_begin|date:"j M Y" }}
Routine 2{{ best_point_routine_3.point_execution }}{{ best_point_routine_3.point_difficulty }}{{ best_point_routine_3.point_horizontal_displacement }}{{ best_point_routine_3.point_time_of_flight }}{{ best_point_routine_3.total }}{{ best_point_routine_3.event.date_begin|date:"j M Y" }}
Routine 2{{ best_point_routine_4.point_execution }}{{ best_point_routine_4.point_difficulty }}{{ best_point_routine_4.point_horizontal_displacement }}{{ best_point_routine_4.point_time_of_flight }}{{ best_point_routine_4.total }}{{ best_point_routine_4.event.date_begin|date:"j M Y" }}
Routine 2{{ best_point_routine_5.point_execution }}{{ best_point_routine_5.point_difficulty }}{{ best_point_routine_5.point_horizontal_displacement }}{{ best_point_routine_5.point_time_of_flight }}{{ best_point_routine_5.total }}{{ best_point_routine_5.event.date_begin|date:"j M Y" }}
- {% endif %} -
-
-
-
-
-
-

Last learned skills

- - {% for learned_skill in learned_skills %} - - - - - - - {% endfor %} -
{{ learned_skill.skill.short_label }}({{ learned_skill.get_learning_step_display }}){{ learned_skill.skill.notation }}{{ learned_skill.date|date:"j M Y" }}
-
-
-

Objectives

- {% if plan_list %} - - {% for plan in plan_list %} - - - - - - - {% endfor %} -
{{ plan.educative.short_label }}({{ plan.get_learning_step_display }}){{ plan.skill.notation }}{{ plan.date | date:"j M Y" }}
- {% else %} - No objective defined. - {% endif %} -
-
-
-
-
-

Next Events

- {% if next_event_list %} - - {% for event in next_event_list %} - - - - - - {% endfor %} -
{{ event.date_begin | date:"j M Y" }}in {{ event.number_of_week_from_today }} week(s){{ event.name }}
- {% else %} - No events defined. - {% endif %} -
-
-
-
-
-

Notes

- {% if notes %} - {% for note in notes %} - {{ note.to_markdown | safe }} - {% endfor %} - {% else %} - No note this week. - {% endif %} -
-
-
-
- {% if routine_1 or routine_2 or routine_3 or routine_4 or routine_5 %} -
-

Routines

-
-
-
Q1 routine 1
- {% if routine_1 %} - - {% for routine_skill in routine_1.routine.skill_links.all %} - - - - - {% endfor %} - - - - -
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ routine_1.routine.difficulty }}
- {% 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 %} -
-
-
Q1 routine 2
- {% if routine_2 %} - - {% for routine_skill in routine_2.routine.skill_links.all %} - - - - - {% endfor %} - - - - -
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ routine_2.routine.difficulty }}
- {% 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 %} -
-
-
Q2 routine 1
- {% if routine_3 %} - - {% for routine_skill in routine_3.routine.skill_links.all %} - - - - - {% endfor %} - - - - -
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ routine_3.routine.difficulty }}
- {% 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 %} -
-
-
Semi-final routine
- {% if routine_4 %} - - {% for routine_skill in routine_4.routine.skill_links.all %} - - - - - {% endfor %} - - - - -
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ routine_4.routine.difficulty }}
- {% 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 %} -
-
-
Final routine
- {% if routine_5 %} - - {% for routine_skill in routine_5.routine.skill_links.all %} - - - - - {% endfor %} - - - - -
{{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
{{ routine_5.routine.difficulty }}
- {% 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 %} -
- {% endif %} -
- - - - diff --git a/jarvis/people/urls.py b/jarvis/people/urls.py index 9b36764..6ba7daa 100644 --- a/jarvis/people/urls.py +++ b/jarvis/people/urls.py @@ -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//", + # views.generate_week_report, + # name="gymnast_report_export", + # ), path( - r"report//", - views.generate_week_report, - name="gymnast_report_export", - ), - path( - r"report//season//week_number//", + r"report/periodical//season//week_number//", views.generate_week_report, name="gymnast_report_export_for_week_number", ), + path( + r"report/periodical//season//month_number//", + views.generate_month_report, + name="gymnast_report_export_for_month_number", + ), + path( + r"report/periodical//season//", + views.generate_season_report, + name="gymnast_report_export_for_season", + ), path( r"report/timeline//", views.generate_timeline_report, diff --git a/jarvis/people/views.py b/jarvis/people/views.py index 7879732..8d60d64 100644 --- a/jarvis/people/views.py +++ b/jarvis/people/views.py @@ -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 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) diff --git a/jarvis/tools/date_week_transition.py b/jarvis/tools/date_week_transition.py index fa54d21..47de4dc 100644 --- a/jarvis/tools/date_week_transition.py +++ b/jarvis/tools/date_week_transition.py @@ -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): diff --git a/jarvis/tools/models.py b/jarvis/tools/models.py index 8c5c36c..3b12701 100644 --- a/jarvis/tools/models.py +++ b/jarvis/tools/models.py @@ -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, diff --git a/static/css/gymnast_report.css b/static/css/gymnast_report.css index d0dedb5..6d2a2fd 100644 --- a/static/css/gymnast_report.css +++ b/static/css/gymnast_report.css @@ -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; -} \ No newline at end of file +}