Add new report : timeline report

This commit is contained in:
Gregory Trullemans 2022-12-12 20:54:31 +01:00
parent 7daf0a70dc
commit 3dba301c05
11 changed files with 487 additions and 6 deletions

View File

@ -37,6 +37,16 @@
</span>
</div>
</div>
<div class="form-group row">
<label for="" class="col-7 col-sm-5 col-md-6 col-lg-6 col-xl-6 col-form-label">
Generate timeline
<a href="{% url 'gymnast_timeline_export' gymnast.id %}">
<button type="button" class="btn btn-icon btn-warning mt-0">
<i class="far fa-download"></i>
</button>
</a>
</label>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,67 @@
{% 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>
<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-12">
<h4 class="mb-1">Timeline</h4>
{% if sorted_records %}
<ul>
{% for record in sorted_records %}
{{ record.timeline_representation | safe }}
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,380 @@
{% 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>
{% 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

@ -100,6 +100,9 @@ class Chrono(Seasonisable):
self.chrono_type,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - New personel best {CHRONO_TYPE_CHOICE[self.chrono_type][1]}: {self.score}' ({self.tof}')</li>"
@staticmethod
def compute_tof(value):
tof = round((value * 13) / 15, 3) * 1000
@ -161,6 +164,9 @@ class Accident(Markdownizable, Seasonisable):
self.date,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)</li>"
class LearnedSkill(Seasonisable):
"""
@ -198,6 +204,9 @@ class LearnedSkill(Seasonisable):
self.skill,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - learning of {self.skill.long_label} ({self.skill.short_label}): {LEARNING_STEP_CHOICES[self.learning_step][1]}</li>"
class Plan(Seasonisable, Markdownizable):
"""

View File

@ -71,6 +71,21 @@ gymnast_urlpatterns = [
views.generate_report,
name="gymnast_report_export_for_week_number",
),
path(
r"report/timeline/<int:gymnast_id>/",
views.generate_timeline_report,
name="gymnast_timeline_export",
),
path(
r"report/timeline/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
views.generate_timeline_report,
name="gymnast_timeline_export_from_week_number",
),
path(
r"report/timeline/<int:gymnast_id>/date/<str:date>/",
views.generate_timeline_report,
name="gymnast_timeline_export_from_date",
),
path(r"add/", views.gymnast_create_or_update, name="gymnast_create"),
path(
r"edit/<int:gymnast_id>/", views.gymnast_create_or_update, name="gymnast_update"

View File

@ -231,7 +231,7 @@ def gymnast_display_events_and_notes(request, gymnast_id):
"latest_published_note": latest_published_note,
"gymnast_id": gymnast_id,
}
return render(request, "people/gymnasts/tab_events_and_notes.html", context)
return render(request, "people/gymnasts/tabs/tab_events_and_notes.html", context)
@login_required
@ -263,7 +263,7 @@ def gymnast_display_physiological(request, gymnast_id):
"height_weight_list": height_weight_list,
"gymnast_id": gymnast_id,
}
return render(request, "people/gymnasts/tab_physiological.html", context)
return render(request, "people/gymnasts/tabs/tab_physiological.html", context)
@login_required
@ -291,7 +291,7 @@ def gymnast_display_scores_chrono(request, gymnast_id):
"chrono_rf": base_queryset.filter(chrono_type=3),
"gymnast_id": gymnast_id,
}
return render(request, "people/gymnasts/tab_scores_and_chronos.html", context)
return render(request, "people/gymnasts/tabs/tab_scores_and_chronos.html", context)
@login_required
@ -343,7 +343,7 @@ def gymnast_display_routine_statistics(request, gymnast_id):
"gymnast_id": gymnast_id,
}
return render(
request, "people/gymnasts/tab_routines_and_routine_stats.html", context
request, "people/gymnasts/tabs/tab_routines_and_routine_stats.html", context
)
@ -523,7 +523,7 @@ def gymnast_display_skill(request, gymnast_id):
context["user_is_trainer"] = request.user.groups.filter(
name="trainer"
).exists() # TODO: utiliser les {{ perms }}
return render(request, "people/gymnasts/tab_skill.html", context)
return render(request, "people/gymnasts/tabs/tab_skill.html", context)
def analyse_score(value, value_list):
@ -976,7 +976,7 @@ def generate_report(request, gymnast_id, season=None, week_number=None):
# + '.pdf"'
# )
html = render_to_string("people/gymnasts/report.html", context)
html = render_to_string("people/gymnasts/reports/report_week.html", context)
# font_config = FontConfiguration()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(