Split followup.views in smaller files. Add report week comparison
This commit is contained in:
parent
c22d6062ea
commit
d6d709ef44
|
@ -0,0 +1,53 @@
|
|||
MAIL_HEADER = """
|
||||
<html><head><style id="canary-invert">html {
|
||||
filter: invert(100%) hue-rotate(180deg) !important;
|
||||
}
|
||||
|
||||
img,
|
||||
video,
|
||||
:not(object):not(body)>embed,
|
||||
object,
|
||||
svg image,
|
||||
[style*="background:url"],
|
||||
[style*="background-image:url"],
|
||||
[style*="background: url"],
|
||||
[style*="background-image: url"],
|
||||
[background],
|
||||
twitterwidget,
|
||||
.canary-emoji {
|
||||
filter: invert(100%) hue-rotate(180deg) !important;
|
||||
}
|
||||
|
||||
[style*="background:url"] *,
|
||||
[style*="background-image:url"] *,
|
||||
[style*="background: url"] *,
|
||||
[style*="background-image: url"] *,
|
||||
input,
|
||||
[background] *,
|
||||
img[src^="https://s0.wp.com/latex.php"],
|
||||
twitterwidget .NaturalImage-image {
|
||||
filter: none !important;
|
||||
}
|
||||
</style></head><body contenteditable="true" style="font-family:Helvetica;font-size:13px;">Gregory Trullemans<br>
|
||||
|
||||
|
||||
</body></html>"""
|
||||
|
||||
MAIL_FOOTER = """<br />
|
||||
<p>Excellente journée</p>
|
||||
<p>Jarvis<br />
|
||||
<b>Trampoline Trainer Help</b></p>
|
||||
<table border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<img id="CB70323B-AC4A-4992-9DD8-3F25DC32658C" height="80px" src="https://www.flyingacrobaticstrampoline.be/img/logo_120px.png" style="max-width: 100vw;">
|
||||
</td>
|
||||
<td>
|
||||
<b><span class="" style="color: rgb(253, 221, 12);">F</span><span class="" style="color: rgb(251, 214, 13);">l</span><span class="" style="color: rgb(249, 207, 14);">y</span><span class="" style="color: rgb(247, 200, 15);">i</span><span class="" style="color: rgb(245, 194, 16);">n</span><span class="" style="color: rgb(243, 187, 17);">g</span> <span class="" style="color: rgb(241, 180, 18);"></span><span class="" style="color: rgb(239, 173, 19);">A</span><span class="" style="color: rgb(237, 166, 20);">c</span><span class="" style="color: rgb(234, 159, 21);">r</span><span class="" style="color: rgb(232, 153, 22);">o</span><span class="" style="color: rgb(230, 146, 23);">b</span><span class="" style="color: rgb(228, 139, 24);">a</span><span class="" style="color: rgb(226, 132, 25);">t</span><span class="" style="color: rgb(224, 125, 26);">i</span><span class="" style="color: rgb(222, 118, 27);">c</span><span class="" style="color: rgb(220, 112, 28);">s</span> <span class="" style="color: rgb(218, 105, 29);"></span><span class="" style="color: rgb(216, 98, 30);">T</span><span class="" style="color: rgb(214, 91, 31);">r</span><span class="" style="color: rgb(212, 84, 32);">a</span><span class="" style="color: rgb(210, 77, 33);">m</span><span class="" style="color: rgb(208, 70, 34);">p</span><span class="" style="color: rgb(206, 64, 35);">o</span><span class="" style="color: rgb(204, 57, 36);">l</span><span class="" style="color: rgb(201, 50, 37);">i</span><span class="" style="color: rgb(199, 43, 38);">n</span><span class="" style="color: rgb(197, 36, 39);">e</span> <span class="" style="color: rgb(195, 29, 40);"></span><span class="" style="color: rgb(193, 23, 41);">C</span><span class="" style="color: rgb(191, 16, 42);">l</span><span class="" style="color: rgb(189, 9, 43);">u</span><span class="" style="color: rgb(187, 2, 44);">b</span></b><br>
|
||||
<span style="font-size: 13px; letter-spacing: 0.01em; line-height: 1.2;">Rue René Francq, 7</span><br>
|
||||
<span style="font-size: 13px; letter-spacing: 0.01em; line-height: 1.2;">1428 Lillois-Witterzée</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>"""
|
|
@ -1,84 +1,87 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
from . import views_chrono
|
||||
from . import views_intensity
|
||||
from . import views_physiological
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path(r"chrono/", views.chrono_listing, name="chrono_list"),
|
||||
path(r"chrono/", views_chrono.chrono_listing, name="chrono_list"),
|
||||
path(
|
||||
r"chrono/gymnast/<int:gymnast_id>/",
|
||||
views.chrono_listing,
|
||||
views_chrono.chrono_listing,
|
||||
name="chrono_list_for_gymnast",
|
||||
),
|
||||
path(r"chrono/add/", views.chrono_create_or_update, name="chrono_create"),
|
||||
path(r"chrono/add/", views_chrono.chrono_create_or_update, name="chrono_create"),
|
||||
path(
|
||||
r"chrono/add/<int:gymnast_id>/",
|
||||
views.chrono_create_or_update,
|
||||
views_chrono.chrono_create_or_update,
|
||||
name="chrono_create_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"chrono/edit/<int:chrono_id>/",
|
||||
views.chrono_create_or_update,
|
||||
views_chrono.chrono_create_or_update,
|
||||
name="chrono_update",
|
||||
),
|
||||
path(
|
||||
r"chrono/details/<int:chrono_id>/",
|
||||
views.jump_chrono_details,
|
||||
views_chrono.jump_chrono_details,
|
||||
name="jump_chrono_details",
|
||||
),
|
||||
path(
|
||||
r"chrono/details/<int:chrono_id>/add/",
|
||||
views.jump_chrono_values_create_or_update,
|
||||
views_chrono.jump_chrono_values_create_or_update,
|
||||
name="jump_chrono_values_create_or_update",
|
||||
),
|
||||
path(
|
||||
r"chrono/add_jump_chrono_value/",
|
||||
views.add_jump_chrono_value,
|
||||
views_chrono.add_jump_chrono_value,
|
||||
name="add_jump_chrono_value",
|
||||
),
|
||||
path(
|
||||
r"chrono/remove_jump_chrono_value/",
|
||||
views.remove_jump_chrono_value,
|
||||
views_chrono.remove_jump_chrono_value,
|
||||
name="remove_jump_chrono_value",
|
||||
),
|
||||
path(
|
||||
r"chrono/range/<str:date_begin>/<str:date_end>/gymnast/<int:gymnast_id>/routine_type<int:routine_type>/",
|
||||
views.average_jump_chrono_details_between_two_date,
|
||||
views_chrono.average_jump_chrono_details_between_two_date,
|
||||
name="average_jump_chrono_details_between_two_date",
|
||||
),
|
||||
path(
|
||||
r"chrono/season/<str:season>/week/<int:week_number>/gymnast/<int:gymnast_id>/routine_type/<int:routine_type>/",
|
||||
views.average_jump_chrono_details_for_gymnast,
|
||||
views_chrono.average_jump_chrono_details_for_gymnast,
|
||||
name="average_jump_chrono_details_for_week_of_season",
|
||||
),
|
||||
path(
|
||||
r"chrono/statistics/gymnast/<int:gymnast_id>/routine_type/<int:routine_type>/season/<str:season>/week/<int:week_number>/",
|
||||
views.average_jump_chrono_details_for_gymnast,
|
||||
views_chrono.average_jump_chrono_details_for_gymnast,
|
||||
name="average_jump_chrono_details_for_gymnast_routinetype_season_and_week",
|
||||
),
|
||||
path(
|
||||
r"chrono/statistics/gymnast/<int:gymnast_id>/routine_type/<int:routine_type>/",
|
||||
views.average_jump_chrono_details_for_gymnast,
|
||||
views_chrono.average_jump_chrono_details_for_gymnast,
|
||||
name="average_jump_chrono_details_for_gymnast_and_routinetype",
|
||||
),
|
||||
path(
|
||||
r"chrono/statistics/gymnast/<int:gymnast_id>/",
|
||||
views.average_jump_chrono_details_for_gymnast,
|
||||
views_chrono.average_jump_chrono_details_for_gymnast,
|
||||
name="average_jump_chrono_details_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"chrono/get_distinct_season/gymnast/<int:gymnast_id>/",
|
||||
views.get_chrono_detail_distinct_season,
|
||||
views_chrono.get_chrono_detail_distinct_season,
|
||||
name="get_chrono_detail_distinct_season",
|
||||
),
|
||||
path(
|
||||
r"chrono/get_distinct_weeknumber/gymnast/<int:gymnast_id>/season/<str:season>/",
|
||||
views.get_chrono_detail_distinct_weeknumber_for_season,
|
||||
views_chrono.get_chrono_detail_distinct_weeknumber_for_season,
|
||||
name="get_chrono_detail_distinct_weeknumber_for_season",
|
||||
),
|
||||
path(
|
||||
r"chrono/get_average_jump_chrono_details_for_season_and_week/gymnast/<int:gymnast_id>/routine_type/<int:routine_type>/season/<str:season>/week/<int:week_number>/",
|
||||
views.get_average_jump_chrono_details_for_season_and_week,
|
||||
views_chrono.get_average_jump_chrono_details_for_season_and_week,
|
||||
name="get_average_jump_chrono_details_for_season_and_week",
|
||||
),
|
||||
#
|
||||
|
@ -87,7 +90,7 @@ urlpatterns = [
|
|||
path(r"note/", views.note_listing, name="note_list"),
|
||||
path(
|
||||
r"note/gymnast/<int:gymnast_id>/",
|
||||
views.chrono_listing,
|
||||
views.note_listing,
|
||||
name="note_list_for_gymnast",
|
||||
),
|
||||
path(r"note/add/", views.note_create_or_update, name="note_create"),
|
||||
|
@ -101,26 +104,30 @@ urlpatterns = [
|
|||
#
|
||||
#
|
||||
# INTENSITY
|
||||
path(r"intensity/", views.intensity_listing, name="intensity_list"),
|
||||
path(r"intensity/", views_intensity.intensity_listing, name="intensity_list"),
|
||||
path(
|
||||
r"intensity/gymnast/<int:gymnast_id>/",
|
||||
views.intensity_listing,
|
||||
views_intensity.intensity_listing,
|
||||
name="intensity_list_for_gymnast",
|
||||
),
|
||||
path(r"intensity/add/", views.intensity_create_or_update, name="intensity_create"),
|
||||
path(
|
||||
r"intensity/add/",
|
||||
views_intensity.intensity_create_or_update,
|
||||
name="intensity_create",
|
||||
),
|
||||
path(
|
||||
r"intensity/add/<int:gymnast_id>/",
|
||||
views.intensity_create_or_update,
|
||||
views_intensity.intensity_create_or_update,
|
||||
name="intensity_create_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"intensity/<int:intensity_id>/edit/",
|
||||
views.intensity_create_or_update,
|
||||
views_intensity.intensity_create_or_update,
|
||||
name="intensity_update",
|
||||
),
|
||||
path(
|
||||
r"intensity/details/<int:intensity_id>/",
|
||||
views.intensity_details,
|
||||
views_intensity.intensity_details,
|
||||
name="intensity_details",
|
||||
),
|
||||
#
|
||||
|
@ -158,43 +165,55 @@ urlpatterns = [
|
|||
#
|
||||
#
|
||||
# ACCIDENT
|
||||
path(r"injury/search/", views.injuries_listing, name="injury_search"),
|
||||
path(r"injury/", views.injuries_listing, name="injuries_list"),
|
||||
path(r"injury/add/", views.injury_create_or_update, name="injury_create"),
|
||||
path(r"injury/search/", views_physiological.injuries_listing, name="injury_search"),
|
||||
path(r"injury/", views_physiological.injuries_listing, name="injuries_list"),
|
||||
path(
|
||||
r"injury/add/",
|
||||
views_physiological.injury_create_or_update,
|
||||
name="injury_create",
|
||||
),
|
||||
path(
|
||||
r"injury/add/<int:gymnast_id>/",
|
||||
views.injury_create_or_update,
|
||||
views_physiological.injury_create_or_update,
|
||||
name="injury_create_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"injury/edit/<int:injury_id>/",
|
||||
views.injury_create_or_update,
|
||||
views_physiological.injury_create_or_update,
|
||||
name="injury_update",
|
||||
),
|
||||
path(r"injury/<int:injury_id>/", views.injury_details, name="injury_details"),
|
||||
path(
|
||||
r"injury/<int:injury_id>/",
|
||||
views_physiological.injury_details,
|
||||
name="injury_details",
|
||||
),
|
||||
#
|
||||
#
|
||||
# WELLBEING
|
||||
path(r"wellbeing/", views.wellbeing_listing, name="wellbeing_list"),
|
||||
path(r"wellbeing/", views_physiological.wellbeing_listing, name="wellbeing_list"),
|
||||
path(
|
||||
r"wellbeing/gymnast/<int:gymnast_id>/",
|
||||
views.wellbeing_listing,
|
||||
views_physiological.wellbeing_listing,
|
||||
name="wellbeing_list_for_gymnast",
|
||||
),
|
||||
path(r"wellbeing/add/", views.wellbeing_create_or_update, name="wellbeing_create"),
|
||||
path(
|
||||
r"wellbeing/add/",
|
||||
views_physiological.wellbeing_create_or_update,
|
||||
name="wellbeing_create",
|
||||
),
|
||||
path(
|
||||
r"wellbeing/add/<int:gymnast_id>/",
|
||||
views.wellbeing_create_or_update,
|
||||
views_physiological.wellbeing_create_or_update,
|
||||
name="wellbeing_create_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"wellbeing/edit/<int:wellbeing_id>/",
|
||||
views.wellbeing_create_or_update,
|
||||
views_physiological.wellbeing_create_or_update,
|
||||
name="wellbeing_update",
|
||||
),
|
||||
path(
|
||||
r"wellbeing/<int:wellbeing_id>/",
|
||||
views.wellbeing_details,
|
||||
views_physiological.wellbeing_details,
|
||||
name="wellbeing_details",
|
||||
),
|
||||
#
|
||||
|
@ -202,22 +221,22 @@ urlpatterns = [
|
|||
# Height/Weight
|
||||
path(
|
||||
r"heightweight/gymnast/<int:gymnast_id>/",
|
||||
views.heightweight_listing,
|
||||
views_physiological.heightweight_listing,
|
||||
name="heightweight_list_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"heightweight/add/",
|
||||
views.heightweight_create_or_update,
|
||||
views_physiological.heightweight_create_or_update,
|
||||
name="heightweight_create",
|
||||
),
|
||||
path(
|
||||
r"heightweight/add/<int:gymnast_id>/",
|
||||
views.heightweight_create_or_update,
|
||||
views_physiological.heightweight_create_or_update,
|
||||
name="heightweight_create_for_gymnast",
|
||||
),
|
||||
path(
|
||||
r"heightweight/edit/<int:heightweight_id>/",
|
||||
views.heightweight_create_or_update,
|
||||
views_physiological.heightweight_create_or_update,
|
||||
name="heightweight_update",
|
||||
),
|
||||
#
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,476 @@
|
|||
from datetime import date, datetime
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
from django.db.models import Min, Avg, Max, Sum
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.mail import send_mail
|
||||
|
||||
import pendulum
|
||||
|
||||
from jarvis.core.models import Email
|
||||
from jarvis.people.models import Gymnast
|
||||
from jarvis.tools.date_week_transition import from_date_to_week_number
|
||||
from jarvis.tools.models import Season
|
||||
from .models import (
|
||||
Chrono,
|
||||
ChronoDetails,
|
||||
)
|
||||
|
||||
from .forms import ChronoForm
|
||||
|
||||
from .models import (
|
||||
SCORE_TYPE_CHOICE,
|
||||
CHRONO_TYPE_CHOICE,
|
||||
)
|
||||
|
||||
from .email_vars import MAIL_HEADER, MAIL_FOOTER
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def jump_chrono_details(request, chrono_id):
|
||||
"""Récupère toutes les informations détaillées d'un chrono. La fonction en profite pour
|
||||
recalculer le total et s'assure que cela correspond à la valeur stockée dans le model
|
||||
Chrono.
|
||||
|
||||
Args:
|
||||
chrono_id (int) identifiant chrono
|
||||
|
||||
QTF : Est-ce que je ne devrais pas faire un prefetch_related sur mon objet chrono pour
|
||||
optimiser mon affichage ?
|
||||
chrono = Chrono.object.get(pk=chrono_id).prefetch_related('chrono_details') ?
|
||||
"""
|
||||
|
||||
chrono = get_object_or_404(Chrono, pk=chrono_id)
|
||||
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and chrono.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return chrono_listing(request)
|
||||
|
||||
sum_value = chrono.details.all().aggregate(total=Sum("value"))
|
||||
|
||||
if chrono.score != sum_value["total"]:
|
||||
chrono.score = sum_value["total"]
|
||||
if chrono.score_type == 0:
|
||||
chrono.tof = Chrono.compute_tof(sum_value["total"])
|
||||
chrono.save()
|
||||
|
||||
mean_value = chrono.details.all().aggregate(mean=Avg("value"))["mean"]
|
||||
tmp_min_value = chrono.details.all().aggregate(min=Min("value"))["min"]
|
||||
tmp_max_value = chrono.details.all().aggregate(max=Max("value"))["max"]
|
||||
chart_min_value = mean_value - (tmp_min_value / 20)
|
||||
chart_max_value = mean_value - (tmp_max_value / 20)
|
||||
|
||||
context = {
|
||||
"chrono": chrono,
|
||||
"mean_value": mean_value,
|
||||
"chart_min_value": chart_min_value,
|
||||
"chart_max_value": chart_max_value,
|
||||
}
|
||||
return render(request, "chronos/details.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def average_jump_chrono_details_for_gymnast(
|
||||
request, gymnast_id, routine_type=1, season=None, week_number=1
|
||||
):
|
||||
"""Récupère tout les chronos entre deux date pour un gymnaste et un type de série
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
routine_type (int) Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE)
|
||||
season (string) Saison sous forme "xxxx-xxxy"
|
||||
week_number (int) numéro de semaine (1, …, 52)
|
||||
"""
|
||||
if season is None:
|
||||
today = pendulum.now().date()
|
||||
season, week_number = from_date_to_week_number(today)
|
||||
else:
|
||||
season = Season(season).label
|
||||
week_number = min(week_number, 52)
|
||||
week_number = max(week_number, 1)
|
||||
|
||||
return average_jump_chrono_details_for_season_and_week(
|
||||
request,
|
||||
gymnast_id,
|
||||
routine_type,
|
||||
season,
|
||||
week_number,
|
||||
)
|
||||
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
def remove_jump_chrono_value(request):
|
||||
"""
|
||||
Recoit trois informations permettant de supprimer le chrono d'un saut à un chrono.
|
||||
"""
|
||||
chrono_id = request.POST.get("chrono_id", None)
|
||||
order = request.POST.get("order", None)
|
||||
chrono = get_object_or_404(Chrono, pk=chrono_id)
|
||||
try:
|
||||
ChronoDetails.objects.filter(chrono=chrono, order=order).delete()
|
||||
except Exception:
|
||||
return HttpResponse(409)
|
||||
|
||||
return HttpResponse(200)
|
||||
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
def add_jump_chrono_value(request):
|
||||
"""
|
||||
Recoit trois informations permettant d'ajouter le chrono d'un saut à un chrono.
|
||||
"""
|
||||
chrono_id = request.POST.get("chrono_id", None)
|
||||
order = request.POST.get("order", None)
|
||||
value = request.POST.get("value", None)
|
||||
|
||||
chrono = get_object_or_404(Chrono, pk=chrono_id)
|
||||
row, created = ChronoDetails.objects.get_or_create(
|
||||
chrono=chrono, order=order, value=value
|
||||
)
|
||||
|
||||
if created:
|
||||
return HttpResponse(200, (row, created)) # devrait être un 201
|
||||
|
||||
return HttpResponse(400, (row, created))
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def jump_chrono_values_create_or_update(request, chrono_id):
|
||||
"""
|
||||
Ajoute des scores de saut à un chrono.
|
||||
|
||||
Args:
|
||||
chrono_id (int) identifiant chrono
|
||||
"""
|
||||
|
||||
chrono = get_object_or_404(Chrono, pk=chrono_id)
|
||||
jump_list = chrono.details.all()
|
||||
number_of_jump = jump_list.count()
|
||||
context = {
|
||||
"chrono": chrono,
|
||||
"jump_list": jump_list,
|
||||
"number_of_jump": number_of_jump,
|
||||
"score_type": chrono.score_type,
|
||||
}
|
||||
return render(request, "chronos/add_details.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def average_jump_chrono_details_between_two_date(
|
||||
request, gymnast_id, routine_type=1, date_begin=None, date_end=None
|
||||
):
|
||||
"""Récupère tout les chronos entre deux date pour un gymnaste et un type de série
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
routine_type (int) type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE)
|
||||
date_begin (date) date de début
|
||||
date_end (date) date de fin
|
||||
|
||||
QTF : le cast en date devrait être dans un try mais comment gérer correctement l'erreur - si
|
||||
erreur il y a ?
|
||||
"""
|
||||
|
||||
if date_end:
|
||||
try:
|
||||
date_end = datetime.strptime(date_end, "%Y-%m-%d").date()
|
||||
except (ValueError, TypeError):
|
||||
date_end = pendulum.now().date()
|
||||
else:
|
||||
date_end = pendulum.now().date()
|
||||
|
||||
if date_begin:
|
||||
try:
|
||||
date_begin = datetime.strptime(date_begin, "%Y-%m-%d").date()
|
||||
except (ValueError, TypeError):
|
||||
date_begin = datetime(date_end.year, 9, 1)
|
||||
else:
|
||||
date_begin = datetime(date_end.year, 9, 1)
|
||||
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
|
||||
stat_values = (
|
||||
ChronoDetails.objects.filter(
|
||||
chrono__gymnast=gymnast_id,
|
||||
chrono__chrono_type=routine_type,
|
||||
chrono__date__gte=date_begin,
|
||||
chrono__date__lte=date_end,
|
||||
)
|
||||
.values("order")
|
||||
.annotate(
|
||||
avg_score=Avg("value"), max_score=Max("value"), min_score=Min("value")
|
||||
)
|
||||
.order_by("order")
|
||||
)
|
||||
|
||||
chrono_list = Chrono.objects.filter(
|
||||
gymnast=gymnast_id,
|
||||
date__gte=date_begin,
|
||||
date__lte=date_end,
|
||||
chrono_type=routine_type,
|
||||
)
|
||||
|
||||
context = {
|
||||
"gymnast": gymnast,
|
||||
"date_begin": date_begin,
|
||||
"date_end": date_end,
|
||||
"chrono_list": chrono_list,
|
||||
"stat_values": stat_values,
|
||||
}
|
||||
return render(request, "chronos/list_details.html", context)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def get_chrono_detail_distinct_season(request, gymnast_id):
|
||||
"""Récupère toutes les saisons pour lesquelles le gymnaste a des chronos détaillés.
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
"""
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
season_list = list(
|
||||
gymnast.chronos.values_list("season", flat=True)
|
||||
.distinct("season")
|
||||
.order_by("season")
|
||||
)
|
||||
return JsonResponse(season_list, safe=False)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def get_chrono_detail_distinct_weeknumber_for_season(request, gymnast_id, season):
|
||||
"""Récupère toutes les week_number pour lesquelles le gymnaste a des chronos détaillés au cours
|
||||
d'une saison.
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
season (string) Season
|
||||
"""
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
weeknumber_list = list(
|
||||
gymnast.chronos.values_list("week_number", flat=True)
|
||||
.filter(season=season)
|
||||
.distinct("week_number")
|
||||
.order_by("week_number")
|
||||
)
|
||||
return JsonResponse(weeknumber_list, safe=False)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def get_average_jump_chrono_details_for_season_and_week(
|
||||
request, gymnast_id, routine_type, season, week_number
|
||||
):
|
||||
"""Récupère tout les chronos moyen par saut pour une saison & semaine d'un gymnaste
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
routine_type (int) Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE)
|
||||
season (string) Season
|
||||
week_number (int) Numero de la semaine
|
||||
"""
|
||||
|
||||
stat_values = list(
|
||||
ChronoDetails.objects.filter(
|
||||
chrono__gymnast=gymnast_id,
|
||||
chrono__chrono_type=routine_type,
|
||||
chrono__season=season,
|
||||
chrono__week_number=week_number,
|
||||
)
|
||||
.values("order")
|
||||
.annotate(avg_score=Avg("value"))
|
||||
.order_by("order")
|
||||
)
|
||||
return JsonResponse(stat_values, safe=False)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def average_jump_chrono_details_for_season_and_week(
|
||||
request, gymnast_id, routine_type, season, week_number
|
||||
):
|
||||
"""Récupère tout les chronos entre deux date pour un gymnaste et un type de série
|
||||
|
||||
Args:
|
||||
gymnast_id (int) Identifiant d'un gymnaste
|
||||
routine_type (int) Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE)
|
||||
season (string) Season
|
||||
week_number (int) Numero de la semaine
|
||||
"""
|
||||
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
|
||||
stat_values = (
|
||||
ChronoDetails.objects.filter(
|
||||
chrono__gymnast=gymnast_id,
|
||||
chrono__chrono_type=routine_type,
|
||||
chrono__season=season,
|
||||
chrono__week_number=week_number,
|
||||
)
|
||||
.values("order")
|
||||
.annotate(
|
||||
avg_score=Avg("value"), max_score=Max("value"), min_score=Min("value")
|
||||
)
|
||||
.order_by("order")
|
||||
)
|
||||
# print(stat_values)
|
||||
|
||||
distinct_season_list = (
|
||||
gymnast.chronos.values_list("season", flat=True)
|
||||
.distinct("season")
|
||||
.order_by("season")
|
||||
)
|
||||
distinct_week_number_list = (
|
||||
gymnast.chronos.values_list("week_number", flat=True)
|
||||
.filter(season=season)
|
||||
.distinct("week_number")
|
||||
.order_by("week_number")
|
||||
)
|
||||
distinct_routine_type_list = (
|
||||
gymnast.chronos.values_list("chrono_type", flat=True)
|
||||
.distinct("chrono_type")
|
||||
.order_by("chrono_type")
|
||||
)
|
||||
|
||||
chrono_list = Chrono.objects.filter(
|
||||
gymnast=gymnast_id,
|
||||
season=season,
|
||||
week_number=week_number,
|
||||
chrono_type=routine_type,
|
||||
)
|
||||
# print(chrono_list)
|
||||
|
||||
context = {
|
||||
"gymnast": gymnast,
|
||||
"selected_season": season,
|
||||
"selected_week_number": week_number,
|
||||
"selected_routine_type": routine_type,
|
||||
"chrono_list": chrono_list,
|
||||
"stat_values": stat_values,
|
||||
"distinct_season_list": distinct_season_list,
|
||||
"distinct_week_number_list": distinct_week_number_list,
|
||||
"distinct_routine_type_list": distinct_routine_type_list,
|
||||
}
|
||||
return render(request, "chronos/list_details.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def chrono_listing(request, gymnast_id=None):
|
||||
"""
|
||||
Récupère les chronos des gymnastes autorisé(e)s.
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
gymnast = None
|
||||
if gymnast_id and (
|
||||
request.user.is_superuser
|
||||
or (
|
||||
request.session.has_key("available_gymnast")
|
||||
and gymnast_id in request.session["available_gymnast"]
|
||||
)
|
||||
):
|
||||
chrono_list = Chrono.objects.filter(gymnast=gymnast_id)
|
||||
gymnast = Gymnast.objects.get(pk=gymnast_id)
|
||||
else:
|
||||
if request.user.is_superuser:
|
||||
chrono_list = Chrono.objects.all()
|
||||
else:
|
||||
chrono_list = Chrono.objects.filter(
|
||||
gymnast__in=request.session["available_gymnast"]
|
||||
)
|
||||
|
||||
context = {"chrono_list": chrono_list, "gymnast": gymnast}
|
||||
return render(request, "chronos/list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def chrono_create_or_update(request, chrono_id=None, gymnast_id=None):
|
||||
"""Création ou modification d'un chrono
|
||||
|
||||
Args:
|
||||
chrono_id (int) identifiant d'un chrono
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
if chrono_id:
|
||||
chrono = get_object_or_404(Chrono, pk=chrono_id)
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and chrono.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return chrono_listing(request)
|
||||
|
||||
data = {
|
||||
"gymnast": chrono.gymnast.id,
|
||||
"gymnast_related": str(chrono.gymnast),
|
||||
}
|
||||
else:
|
||||
chrono = None
|
||||
data = None
|
||||
if gymnast_id is not None:
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
data = {"gymnast": gymnast_id, "gymnast_related": gymnast}
|
||||
|
||||
if request.method == "POST":
|
||||
form = ChronoForm(request.POST, instance=chrono)
|
||||
|
||||
if form.is_valid():
|
||||
new_chrono = form.save(commit=False)
|
||||
|
||||
if new_chrono.score_type == 1:
|
||||
new_chrono.tof = new_chrono.score
|
||||
else:
|
||||
new_chrono.tof = Chrono.compute_tof(new_chrono.score)
|
||||
|
||||
new_chrono.save()
|
||||
|
||||
# notification
|
||||
receivers = []
|
||||
functionality = ContentType.objects.get(model="chrono")
|
||||
for notification in new_chrono.gymnast.notifications.filter(
|
||||
functionality=functionality
|
||||
):
|
||||
receivers.append(notification.user.email)
|
||||
|
||||
title = f"{new_chrono.gymnast} : Nouveau chrono"
|
||||
body = f"""<p>Bonjour,</p><p>Nouveau chrono pour {new_chrono.gymnast} : {SCORE_TYPE_CHOICE[new_chrono.score_type][1]} {CHRONO_TYPE_CHOICE[new_chrono.chrono_type][1]} - {new_chrono.score}.</p>"""
|
||||
|
||||
Email.objects.create(
|
||||
receivers=receivers,
|
||||
title=title,
|
||||
body=body,
|
||||
)
|
||||
|
||||
send_mail(
|
||||
title,
|
||||
f"{new_chrono.gymnast} a enregistrer un nouveau chrono ({date})",
|
||||
settings.EMAIL_HOST_USER,
|
||||
receivers,
|
||||
fail_silently=False,
|
||||
html_message=body + MAIL_FOOTER,
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse("gymnast_details_tab", args=(new_chrono.gymnast.id, "scores"))
|
||||
)
|
||||
|
||||
return render(request, "chronos/create.html", {"form": form})
|
||||
|
||||
form = ChronoForm(instance=chrono, initial=data)
|
||||
context = {"form": form, "chrono_id": chrono_id}
|
||||
return render(request, "chronos/create.html", context)
|
|
@ -0,0 +1,211 @@
|
|||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.db.models import Min, Avg, Max
|
||||
from django.core.mail import send_mail
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from jarvis.core.models import Email
|
||||
from .models import Gymnast
|
||||
from .models import Intensity
|
||||
from .forms import IntensityForm
|
||||
|
||||
from .email_vars import MAIL_HEADER, MAIL_FOOTER
|
||||
|
||||
|
||||
def get_intensity_stats_for_season_week(gymnast_id, season, week_number):
|
||||
"""Calcule le min, max et moyenne de bien-être d'un gymnaste pour une semaine donnéee
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
season (str) saison
|
||||
week_number (int) numéro de semaine
|
||||
"""
|
||||
intensity_score = Intensity.objects.filter(
|
||||
gymnast_id=gymnast_id, season=season, week_number=week_number
|
||||
).aggregate(
|
||||
average_intensity_time_value=Avg("time"),
|
||||
average_intensity_difficulty_value=Avg("difficulty"),
|
||||
average_quantity_of_skill_value=Avg("quantity_of_skill"),
|
||||
average_number_of_passes_value=Avg("number_of_passes"),
|
||||
average_time_quality=Avg("time_quality"),
|
||||
average_difficulty_quality=Avg("difficulty_quality"),
|
||||
average_quantity_of_skill_quality=Avg("quantity_of_skill_quality"),
|
||||
average_number_of_passes_quality=Avg("number_of_passes_quality"),
|
||||
average_average_training_quality=Avg("average_training_quality"),
|
||||
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"),
|
||||
min_time_quality=Min("time_quality"),
|
||||
min_difficulty_quality=Min("difficulty_quality"),
|
||||
min_quantity_of_skill_quality=Min("quantity_of_skill_quality"),
|
||||
min_number_of_passes_quality=Min("number_of_passes_quality"),
|
||||
min_average_training_quality=Min("average_training_quality"),
|
||||
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"),
|
||||
max_time_quality=Max("time_quality"),
|
||||
max_difficulty_quality=Max("difficulty_quality"),
|
||||
max_quantity_of_skill_quality=Max("quantity_of_skill_quality"),
|
||||
max_number_of_passes_quality=Max("number_of_passes_quality"),
|
||||
max_average_training_quality=Max("average_training_quality"),
|
||||
)
|
||||
return intensity_score
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def intensity_listing(request, gymnast_id=None):
|
||||
"""
|
||||
Si la personne connectée est un entraîneur, la fonction récupère la liste des intensités d'un
|
||||
gymnaste précis ou de tout le monde.
|
||||
Si la personne connectée est un gymnaste, la fonction récupère la liste de ses intensités.
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
gymnast = None
|
||||
if gymnast_id and (
|
||||
request.user.is_superuser
|
||||
or (
|
||||
request.session.has_key("available_gymnast")
|
||||
and gymnast_id in request.session["available_gymnast"]
|
||||
)
|
||||
):
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
intensity_list = Intensity.objects.filter(gymnast=gymnast_id)
|
||||
else:
|
||||
if request.user.is_superuser:
|
||||
intensity_list = Intensity.objects.all()
|
||||
else:
|
||||
intensity_list = Intensity.objects.filter(
|
||||
gymnast__in=request.session["available_gymnast"]
|
||||
)
|
||||
|
||||
context = {"intensity_list": intensity_list, "gymnast": gymnast}
|
||||
return render(request, "intensities/list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def intensity_details(request, intensity_id):
|
||||
"""
|
||||
Récupère toutes les informations d'une intensité.
|
||||
|
||||
Args:
|
||||
intensity_id (int) identifiant d'une intensité
|
||||
"""
|
||||
intensity = get_object_or_404(Intensity, pk=intensity_id)
|
||||
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and intensity.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return intensity_listing(request)
|
||||
|
||||
return render(request, "intensities/details.html", {"intensity": intensity})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def intensity_create_or_update(request, intensity_id=None, gymnast_id=None):
|
||||
"""Création d'un record de la class Intentity.
|
||||
|
||||
Args:
|
||||
intensity_id (int) identifiant d'une intensité (classe <Intensity>).
|
||||
gymnast_id (int) identifiant d'un gymnaste (classe <Gymnast>).
|
||||
"""
|
||||
|
||||
if intensity_id:
|
||||
intensity = get_object_or_404(Intensity, pk=intensity_id)
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and intensity.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return intensity_listing(request)
|
||||
data = {
|
||||
"gymnast": intensity.gymnast.id,
|
||||
"gymnast_related": str(intensity.gymnast),
|
||||
}
|
||||
else:
|
||||
intensity = None
|
||||
data = {}
|
||||
|
||||
if gymnast_id:
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
data["gymnast"] = gymnast_id
|
||||
data["gymnast_related"] = str(gymnast)
|
||||
|
||||
if request.method == "POST":
|
||||
form = IntensityForm(request.POST, instance=intensity)
|
||||
|
||||
if form.is_valid():
|
||||
intensity = form.save()
|
||||
|
||||
# notification
|
||||
receivers = []
|
||||
date = form.cleaned_data["date"]
|
||||
functionality = ContentType.objects.get(model="intensity")
|
||||
for notification in intensity.gymnast.notifications.filter(
|
||||
functionality=functionality
|
||||
):
|
||||
receivers.append(notification.user.email)
|
||||
|
||||
title = f"{intensity.gymnast} : Nouvelle intensité"
|
||||
body = f"""<p>Bonjour,</p><p>{intensity.gymnast} a encodé une nouvelle intensité pour le {date.strftime('%d %B %Y')}:</p>
|
||||
<ul>
|
||||
<li>{intensity.number_of_passes} passages</li>
|
||||
<li>{intensity.time} minutes</li>
|
||||
<li>{intensity.quantity_of_skill} figures</li>
|
||||
<li>{intensity.difficulty_in_unit} difficulty</li>
|
||||
</ul>
|
||||
<p><u>Quality:</u></p>
|
||||
<ul>
|
||||
<li>Time: {intensity.time_quality:.1f}%</li>
|
||||
<li>Diff: {intensity.difficulty_quality:.1f}%</li>
|
||||
<li>Skill: {intensity.quantity_of_skill_quality:.1f}%</li>
|
||||
<li>Passe: {intensity.number_of_passes_quality:.1f}%</li>
|
||||
<li><b>Passe: {intensity.average_quality:.2f}%</b></li>
|
||||
</ul>
|
||||
<p><u>Statistics:</u></p>
|
||||
<ul>
|
||||
<li>Passe/time: {intensity.mean_time_by_passe:.2f}min</li>
|
||||
<li>Skill/time: {intensity.average_quantity_of_skill_by_time:.2f}min</li>
|
||||
<li>Skill/passe: {intensity.average_quantity_of_skill_by_passe:.2f}</li>
|
||||
<li>Diff/passe: {intensity.average_difficulty_by_passe:.2f}</li>
|
||||
<li>Diff/skill: {intensity.average_difficulty_by_skill:.2f}</li>
|
||||
</ul>"""
|
||||
|
||||
Email.objects.create(
|
||||
receivers=receivers,
|
||||
title=title,
|
||||
body=body,
|
||||
)
|
||||
|
||||
send_mail(
|
||||
title,
|
||||
f"Une nouvelle information de saison enregistrée pour {intensity.gymnast}",
|
||||
settings.EMAIL_HOST_USER,
|
||||
receivers,
|
||||
fail_silently=False,
|
||||
html_message=body + MAIL_FOOTER,
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse(
|
||||
"intensity_details",
|
||||
args=(intensity.id,),
|
||||
)
|
||||
)
|
||||
|
||||
return render(request, "intensities/create.html", {"form": form})
|
||||
|
||||
form = IntensityForm(instance=intensity, initial=data)
|
||||
context = {"form": form, "intensity_id": intensity_id}
|
||||
return render(request, "intensities/create.html", context)
|
|
@ -0,0 +1,438 @@
|
|||
from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.db.models import Min, Avg, Max
|
||||
from django.core.mail import send_mail
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from jarvis.core.models import Email
|
||||
from .models import Gymnast
|
||||
from .models import (
|
||||
Injury,
|
||||
WellBeing,
|
||||
HeightWeight,
|
||||
)
|
||||
|
||||
from .forms import (
|
||||
InjuryForm,
|
||||
WellBeingForm,
|
||||
HeightWeightForm,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
INJURY_MECHANISM_CHOICE,
|
||||
INJURY_BODY_SIDE_CHOICE,
|
||||
INJURY_TYPE_CHOICE,
|
||||
INJURY_LOCATION_CHOICE,
|
||||
)
|
||||
|
||||
from .email_vars import MAIL_HEADER, MAIL_FOOTER
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def injuries_listing(request, gymnast_id=None):
|
||||
"""
|
||||
Récupère la liste des bessures.
|
||||
Si c'est un gymnaste qui est connecté, il ne peut récupérer que la liste de ses blessures.
|
||||
Si c'est un autre utilisateur (entraîneur), la liste peut répondre à un pattern si celui-ci est
|
||||
définit.
|
||||
"""
|
||||
|
||||
gymnast = None
|
||||
if gymnast_id and (
|
||||
request.user.is_superuser
|
||||
or (
|
||||
request.session.has_key("available_gymnast")
|
||||
and gymnast_id in request.session["available_gymnast"]
|
||||
)
|
||||
):
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
injuries_list = Injury.objects.filter(gymnast=gymnast_id)
|
||||
else:
|
||||
if request.user.is_superuser:
|
||||
injuries_list = Injury.objects.all()
|
||||
else:
|
||||
injuries_list = Injury.objects.filter(
|
||||
gymnast__in=request.session["available_gymnast"]
|
||||
)
|
||||
|
||||
context = {"injuries_list": injuries_list, "gymnast": gymnast}
|
||||
return render(request, "injuries/list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def injury_create_or_update(request, injury_id=None, gymnast_id=None):
|
||||
"""
|
||||
Formulaire de création d'un nouvel blessure.
|
||||
|
||||
Args:
|
||||
injury_id (int) identifiant d'une blessure
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
if injury_id:
|
||||
injury = get_object_or_404(Injury, pk=injury_id)
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and injury.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return injury_listing(request)
|
||||
data = {
|
||||
"gymnast_related": injury.gymnast,
|
||||
"skill_related": injury.skill,
|
||||
}
|
||||
else:
|
||||
injury = None
|
||||
data = None
|
||||
if gymnast_id is not None:
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)}
|
||||
|
||||
if request.method == "POST":
|
||||
form = InjuryForm(request.POST, instance=injury)
|
||||
|
||||
if form.is_valid():
|
||||
injury = form.save()
|
||||
|
||||
# notification
|
||||
receivers = []
|
||||
date = form.cleaned_data["date"]
|
||||
functionality = ContentType.objects.get(model="injury")
|
||||
for notification in injury.gymnast.notifications.filter(
|
||||
functionality=functionality
|
||||
):
|
||||
receivers.append(notification.user.email)
|
||||
|
||||
title = f"{injury.gymnast} : Nouvelle blessure enregistrée"
|
||||
body = f"""<p>Bonjour,</p>
|
||||
<p>Un nouvelle blessure enregistrée pour {injury.gymnast} pour le {date.strftime('%d %B %Y')}:</p>
|
||||
<ul>
|
||||
<li>{INJURY_TYPE_CHOICE[injury.injury_type][1]},</li>
|
||||
<li>caused by {INJURY_MECHANISM_CHOICE[injury.mechanism][1]},</li>
|
||||
<li>on {INJURY_LOCATION_CHOICE[injury.location][1]},</li>
|
||||
<li>{INJURY_BODY_SIDE_CHOICE[injury.body_side][1]} side,</li>
|
||||
</ul>"""
|
||||
Email.objects.create(receivers=receivers, title=title, body=body)
|
||||
|
||||
send_mail(
|
||||
title,
|
||||
f"{injury.gymnast} a ajouté état de bien être ({date})",
|
||||
settings.EMAIL_HOST_USER,
|
||||
receivers,
|
||||
fail_silently=False,
|
||||
html_message=body + MAIL_FOOTER,
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(reverse("injury_details", args=(injury.pk,)))
|
||||
|
||||
return render(request, "injuries/create.html", {"form": form})
|
||||
|
||||
form = InjuryForm(instance=injury, initial=data)
|
||||
context = {"form": form, "injury_id": injury_id}
|
||||
return render(request, "injuries/create.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def injury_details(request, injury_id):
|
||||
"""
|
||||
Récupère toutes les informations d'une blessure.
|
||||
|
||||
Args:
|
||||
injury_id (int) identifiant d'une blessure
|
||||
"""
|
||||
injury = get_object_or_404(Injury, pk=injury_id)
|
||||
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and injury.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return injuries_listing(request)
|
||||
|
||||
return render(request, "injuries/details.html", {"injury": injury})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def wellbeing_listing(request, gymnast_id=None):
|
||||
"""
|
||||
Récupère la liste des évaluations de bien-être.
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
gymnast = None
|
||||
if gymnast_id and (
|
||||
request.user.is_superuser
|
||||
or (
|
||||
request.session.has_key("available_gymnast")
|
||||
and gymnast_id in request.session["available_gymnast"]
|
||||
)
|
||||
):
|
||||
# wellbeing_list = WellBeing.objects.filter(gymnast=gymnast_id)
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
wellbeing_list = gymnast.wellbeings.all()
|
||||
else:
|
||||
if request.user.is_superuser:
|
||||
# les super user peuvent voir tout le monde.
|
||||
wellbeing_list = WellBeing.objects.all()
|
||||
else:
|
||||
# les autres entraîneurs ne peuvent voir que certains élèves.
|
||||
wellbeing_list = WellBeing.objects.filter(
|
||||
gymnast__in=request.session["available_gymnast"]
|
||||
)
|
||||
|
||||
context = {"wellbeing_list": wellbeing_list, "gymnast": gymnast}
|
||||
return render(request, "wellbeing/list.html", context)
|
||||
|
||||
|
||||
def get_wellbeing_stats_for_season_week(gymnast_id, season, week_number):
|
||||
"""Calcule le min, max et moyenne de bien-être d'un gymnaste pour une semaine donnéee
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
season (str) saison
|
||||
week_number (int) numéro de semaine
|
||||
"""
|
||||
wellbeing_score = WellBeing.objects.filter(
|
||||
gymnast_id=gymnast_id, season=season, week_number=week_number
|
||||
).aggregate(
|
||||
min_mindstate_value=Min("mindstate"),
|
||||
average_mindstate_value=Avg("mindstate"),
|
||||
max_mindstate_value=Max("mindstate"),
|
||||
min_sleep_value=Min("sleep"),
|
||||
average_sleep_value=Avg("sleep"),
|
||||
max_sleep_value=Max("sleep"),
|
||||
min_stress_value=Min("stress"),
|
||||
average_stress_value=Avg("stress"),
|
||||
max_stress_value=Max("stress"),
|
||||
min_fatigue_value=Min("fatigue"),
|
||||
average_fatigue_value=Avg("fatigue"),
|
||||
max_fatigue_value=Max("fatigue"),
|
||||
min_muscle_soreness_value=Min("muscle_soreness"),
|
||||
average_muscle_soreness_value=Avg("muscle_soreness"),
|
||||
max_muscle_soreness_value=Max("muscle_soreness"),
|
||||
)
|
||||
return wellbeing_score
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def wellbeing_create_or_update(
|
||||
request, wellbeing_id=None, gymnast_id=None, event_id=None
|
||||
):
|
||||
"""
|
||||
Formulaire de création d'une nouvelle blessure.
|
||||
|
||||
Args:
|
||||
wellbeing_id (int) identifiant d'une blessure
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
event_id (int) identifiant d'un événement
|
||||
"""
|
||||
|
||||
if wellbeing_id:
|
||||
wellbeing = get_object_or_404(WellBeing, pk=wellbeing_id)
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and wellbeing.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return wellbeing_listing(request)
|
||||
data = {"gymnast_related": wellbeing.gymnast, "event_related": wellbeing.event}
|
||||
else:
|
||||
wellbeing = None
|
||||
data = None
|
||||
if gymnast_id is not None:
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)}
|
||||
if event_id is not None:
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
data = {"event": event_id, "event_related": str(event)}
|
||||
|
||||
if request.method == "POST":
|
||||
form = WellBeingForm(request.POST, instance=wellbeing)
|
||||
|
||||
if form.is_valid():
|
||||
wellbeing = form.save()
|
||||
|
||||
# notification
|
||||
receivers = []
|
||||
date = form.cleaned_data["date"]
|
||||
functionality = ContentType.objects.get(model="wellbeing")
|
||||
for notification in wellbeing.gymnast.notifications.filter(
|
||||
functionality=functionality
|
||||
):
|
||||
receivers.append(notification.user.email)
|
||||
|
||||
title = f"{wellbeing.gymnast} : Nouveau score de bien être"
|
||||
html_message = f"""<p>Bonjour,</p>
|
||||
<p>{wellbeing.gymnast} a ajouté son état de bien être pour le ({date.strftime('%d %B %Y')}) :</p>
|
||||
<ul>
|
||||
<li>Mindstate: {wellbeing.mindstate}</li>
|
||||
<li>Sleep: {wellbeing.sleep}</li>
|
||||
<li>Stress: {wellbeing.stress}</li>
|
||||
<li>Fatigue: {wellbeing.fatigue}</li>
|
||||
<li>Muscle soreness: {wellbeing.muscle_soreness}</li>
|
||||
</ul>
|
||||
{wellbeing.informations}"""
|
||||
|
||||
Email.objects.create(receivers=receivers, title=title, body=html_message)
|
||||
send_mail(
|
||||
title,
|
||||
f"{wellbeing.gymnast} a ajouté état de bien être ({date})",
|
||||
settings.EMAIL_HOST_USER,
|
||||
receivers,
|
||||
fail_silently=False,
|
||||
html_message=html_message + MAIL_FOOTER,
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse("wellbeing_details", args=(wellbeing.pk,))
|
||||
)
|
||||
|
||||
return render(request, "wellbeing/create.html", {"form": form})
|
||||
|
||||
form = WellBeingForm(instance=wellbeing, initial=data)
|
||||
context = {"form": form, "wellbeing_id": wellbeing_id}
|
||||
return render(request, "wellbeing/create.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def wellbeing_details(request, wellbeing_id):
|
||||
"""
|
||||
Récupère toutes les informations d'une évaluation psychologique.
|
||||
|
||||
Args:
|
||||
wellbeing_id (int) identifiant d'une évaluation psycho
|
||||
"""
|
||||
wellbeing = get_object_or_404(WellBeing, pk=wellbeing_id)
|
||||
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and wellbeing.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return wellbeing_listing(request)
|
||||
|
||||
return render(request, "wellbeing/details.html", {"wellbeing": wellbeing})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def heightweight_listing(request, gymnast_id=None):
|
||||
"""
|
||||
Récupère la liste des couples taille/poids suivant (d'un gymnast si définit en paramètre).
|
||||
|
||||
Args:
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
gymnast = None
|
||||
if gymnast_id and (
|
||||
request.user.is_superuser
|
||||
or (
|
||||
request.session.has_key("available_gymnast")
|
||||
and gymnast_id in request.session["available_gymnast"]
|
||||
)
|
||||
):
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
heightweight_list = HeightWeight.objects.filter(gymnast=gymnast_id)
|
||||
else:
|
||||
if request.user.is_superuser:
|
||||
heightweight_list = HeightWeight.objects.all()
|
||||
else:
|
||||
heightweight_list = HeightWeight.objects.filter(
|
||||
gymnast__in=request.session["available_gymnast"]
|
||||
)
|
||||
|
||||
context = {"heightweight_list": heightweight_list, "gymnast": gymnast}
|
||||
return render(request, "heightweight/list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def heightweight_create_or_update(request, heightweight_id=None, gymnast_id=None):
|
||||
"""
|
||||
Formulaire de creation et modification d'un couple taille/couple.
|
||||
|
||||
Args:
|
||||
heightweight_id (int) identifiant d'un couple taille/couple
|
||||
gymnast_id (int) identifiant d'un gymnaste
|
||||
"""
|
||||
|
||||
if heightweight_id:
|
||||
heightweight = get_object_or_404(HeightWeight, pk=heightweight_id)
|
||||
if not request.user.is_superuser and (
|
||||
request.session.has_key("available_gymnast")
|
||||
and heightweight.gymnast.id not in request.session["available_gymnast"]
|
||||
):
|
||||
return heightweight_listing(request)
|
||||
data = {"gymnast_related": heightweight.gymnast}
|
||||
else:
|
||||
heightweight = None
|
||||
data = None
|
||||
if gymnast_id:
|
||||
heightweight = (
|
||||
HeightWeight.objects.filter(gymnast=gymnast_id)
|
||||
.order_by("-date")
|
||||
.first()
|
||||
)
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)}
|
||||
|
||||
if request.method == "POST":
|
||||
form = HeightWeightForm(request.POST, instance=heightweight)
|
||||
|
||||
if form.is_valid():
|
||||
heightweight = form.save()
|
||||
|
||||
# notification
|
||||
receivers = []
|
||||
date = form.cleaned_data["date"]
|
||||
functionality = ContentType.objects.get(model="heightweight")
|
||||
for notification in heightweight.gymnast.notifications.filter(
|
||||
functionality=functionality
|
||||
):
|
||||
receivers.append(notification.user.email)
|
||||
|
||||
title = f"{heightweight.gymnast} : Nouveau poids/taille enregistré"
|
||||
body = f"""<p>Bonjour,</p>
|
||||
<p>Un nouveau poids/taille enregistré pour {heightweight.gymnast} pour le {date.strftime('%d %B %Y')}:</p>
|
||||
<ul>
|
||||
<li>Height: {heightweight.height} cm</li>
|
||||
<li>Weight: {heightweight.weight} kg</li>
|
||||
<li>BMI: {heightweight.bmi}</li>
|
||||
</ul>"""
|
||||
|
||||
Email.objects.create(receivers=receivers, title=title, body=body)
|
||||
|
||||
send_mail(
|
||||
title,
|
||||
f"Un nouveau poids/taille enregistré pour {heightweight.gymnast} ({date}) : {heightweight.height} cm / {heightweight.weight} kg (BMI: {heightweight.bmi}).", # pylint: disable=line-too-long
|
||||
settings.EMAIL_HOST_USER,
|
||||
receivers,
|
||||
fail_silently=False,
|
||||
html_message=body + MAIL_FOOTER,
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(
|
||||
reverse(
|
||||
"gymnast_details_tab",
|
||||
args=(form.cleaned_data["gymnast"].id, "physiological"),
|
||||
)
|
||||
)
|
||||
|
||||
return render(request, "heightweight/create.html", {"form": form})
|
||||
|
||||
form = HeightWeightForm(instance=heightweight, initial=data)
|
||||
context = {
|
||||
"form": form,
|
||||
"gymnast_id": gymnast_id,
|
||||
"heightweight_id": heightweight_id,
|
||||
}
|
||||
return render(request, "heightweight/create.html", context)
|
|
@ -89,7 +89,6 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<label id="month_management" for="select_month_number" class="col-md-1 col-form-label">month</label>
|
||||
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2">
|
||||
<select id="select_month_number" class="selectpicker">
|
||||
<option value="">---</option>
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
{% 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 />
|
||||
(tbd)
|
||||
</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-9" style="text-align: justify;">
|
||||
<h3 class="title mb-0">{{ gymnast.first_name }} {{ gymnast.last_name }}</h3>
|
||||
|
||||
{% if gymnast.informations %}
|
||||
{{ gymnast.to_markdown | safe }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Physiological ({{ source_wellbeing_score_quantity }})</h4>
|
||||
{% if source_wellbeing_score.average_mindstate_value %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Mind state</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.min_mindstate_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_wellbeing_score.average_mindstate_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.max_mindstate_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Sleep</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.min_sleep_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_wellbeing_score.average_sleep_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.max_sleep_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Stress</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.min_stress_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_wellbeing_score.average_stress_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.max_stress_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Fatigue</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.min_fatigue_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_wellbeing_score.average_fatigue_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.max_fatigue_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Muscle soreness</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.min_muscle_soreness_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_wellbeing_score.average_muscle_soreness_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_wellbeing_score.max_muscle_soreness_value }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% else %}
|
||||
No information encoded for selected week.
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Physiological ({{ target_wellbeing_score_quantity }})</h4>
|
||||
{% if target_wellbeing_score.average_mindstate_value %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Mind state</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.min_mindstate_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_wellbeing_score.average_mindstate_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.max_mindstate_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Sleep</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.min_sleep_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_wellbeing_score.average_sleep_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.max_sleep_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Stress</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.min_stress_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_wellbeing_score.average_stress_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.max_stress_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Fatigue</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.min_fatigue_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_wellbeing_score.average_fatigue_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.max_fatigue_value }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Muscle soreness</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.min_muscle_soreness_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_wellbeing_score.average_muscle_soreness_value|stringformat:".1f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_wellbeing_score.max_muscle_soreness_value }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% else %}
|
||||
No information encoded for selected week.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Intensity ({{ source_intensity_quantity }})</h4>
|
||||
{% if source_intensity_quantity %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0 text-right"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Time</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_intensity_time_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_intensity_time_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_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-center">{{ source_intensity_value.min_intensity_difficulty_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_intensity_difficulty_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_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-center">{{ source_intensity_value.min_quantity_of_skill_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_quantity_of_skill_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_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-center">{{ source_intensity_value.min_number_of_passes_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_number_of_passes_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_number_of_passes_value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No training intensity encoded for the selected period.
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Intensity ({{ target_intensity_quantity }})</h4>
|
||||
{% if target_intensity_quantity %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0 text-right"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Time</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_intensity_time_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_intensity_time_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_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-center">{{ target_intensity_value.min_intensity_difficulty_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_intensity_difficulty_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_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-center">{{ target_intensity_value.min_quantity_of_skill_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_quantity_of_skill_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_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-center">{{ target_intensity_value.min_number_of_passes_value }}</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_number_of_passes_value|stringformat:".0f" }}</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_number_of_passes_value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No training intensity encoded for the selected period.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Training quality</h4>
|
||||
{% if source_intensity_value.average_time_quality %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0 text-right"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Time</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_time_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_time_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_time_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Diff.</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_difficulty_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_difficulty_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_difficulty_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b># Skill</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_quantity_of_skill_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_quantity_of_skill_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_quantity_of_skill_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b># Passes</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_number_of_passes_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_number_of_passes_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_number_of_passes_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Average</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.min_average_training_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ source_intensity_value.average_average_training_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ source_intensity_value.max_average_training_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No training quality computed for the selected period.
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h4 class="mb-1">Training quality</h4>
|
||||
{% if target_intensity_value.average_time_quality %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="pt-0 pb-0 text-right"></th>
|
||||
<th class="pt-0 pb-0 text-center">Min</th>
|
||||
<th class="pt-0 pb-0 text-center">Average</th>
|
||||
<th class="pt-0 pb-0 text-center">Max</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Time</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_time_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_time_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_time_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Diff.</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_difficulty_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_difficulty_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_difficulty_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b># Skill</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_quantity_of_skill_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_quantity_of_skill_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_quantity_of_skill_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b># Passes</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_number_of_passes_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_number_of_passes_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_number_of_passes_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pt-0 pb-0"><b>Average</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.min_average_training_quality | stringformat:".0f" }}%</td>
|
||||
<td class="pt-0 pb-0 text-center"><b>{{ target_intensity_value.average_average_training_quality | stringformat:".0f" }}%</b></td>
|
||||
<td class="pt-0 pb-0 text-center">{{ target_intensity_value.max_average_training_quality | stringformat:".0f" }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No training quality computed for the selected period.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -32,21 +32,11 @@ gymnast_urlpatterns = [
|
|||
views.gymnast_display_events_and_notes,
|
||||
name="gymnast_display_events_and_notes",
|
||||
),
|
||||
# path(
|
||||
# r"details/<int:gymnast_id>/injury/",
|
||||
# views.gymnast_display_injury,
|
||||
# name="gymnast_display_injury",
|
||||
# ),
|
||||
path(
|
||||
r"details/<int:gymnast_id>/scores_chrono/",
|
||||
views.gymnast_display_scores_chrono,
|
||||
name="gymnast_display_scores_chrono",
|
||||
),
|
||||
# path(
|
||||
# r"details/<int:gymnast_id>/wellbeing/",
|
||||
# views.gymnast_display_wellbeing,
|
||||
# name="gymnast_display_wellbeing",
|
||||
# ),
|
||||
path(
|
||||
r"details/<int:gymnast_id>/physiological/",
|
||||
views.gymnast_display_physiological,
|
||||
|
@ -74,11 +64,6 @@ gymnast_urlpatterns = [
|
|||
views_reports.get_distinct_week_number_for_season_and_gymnast,
|
||||
name="get_distinct_week_number_for_season_and_gymnast",
|
||||
),
|
||||
# path(
|
||||
# r"report/<int:gymnast_id>/",
|
||||
# views_reports.generate_week_report,
|
||||
# name="gymnast_report_export",
|
||||
# ),
|
||||
path(
|
||||
r"report/periodical/<int:gymnast_id>/season/<str:season>/week_number/<int:week_number>/",
|
||||
views_reports.generate_week_report,
|
||||
|
@ -94,6 +79,11 @@ gymnast_urlpatterns = [
|
|||
views_reports.generate_season_report,
|
||||
name="gymnast_report_export_for_season",
|
||||
),
|
||||
path(
|
||||
r"report/comparison/<int:gymnast_id>/season/<str:season_source>/week/<int:week_source>/season/<str:season_target>/week/<int:week_target>",
|
||||
views_reports.generate_report_week_comparison,
|
||||
name="gymnast_generate_report_week_comparison",
|
||||
),
|
||||
path(
|
||||
r"report/timeline/<int:gymnast_id>/",
|
||||
views_reports.generate_timeline_report,
|
||||
|
|
|
@ -41,6 +41,9 @@ from jarvis.followup.models import (
|
|||
|
||||
from jarvis.followup.models import LEARNING_STEP_CHOICES
|
||||
|
||||
from jarvis.followup.views_physiological import get_wellbeing_stats_for_season_week
|
||||
from jarvis.followup.views_intensity import get_intensity_stats_for_season_week
|
||||
|
||||
from jarvis.tools.models import Season
|
||||
|
||||
# from jarvis.tools.pdf_generator import GymnastReportDocument
|
||||
|
@ -177,7 +180,10 @@ def __get_distinct_week_number_for_season_and_gymnast(gymnast_id, season):
|
|||
.distinct("week_number")
|
||||
.order_by("week_number")
|
||||
)
|
||||
return list(dict.fromkeys(weeknumber_list))
|
||||
|
||||
weeknumber_list = list(dict.fromkeys(weeknumber_list))
|
||||
weeknumber_list.sort(reverse=True)
|
||||
return weeknumber_list
|
||||
|
||||
|
||||
def get_distinct_week_number_for_season_and_gymnast(gymnast_id, season):
|
||||
|
@ -208,15 +214,15 @@ def report_choice(request, gymnast_id):
|
|||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
season, week_number = from_date_to_week_number(today)
|
||||
season_list = __get_distinct_followup_season_for_gymnast(gymnast_id)
|
||||
week_number_list = sorted(
|
||||
__get_distinct_week_number_for_season_and_gymnast(gymnast_id, season)
|
||||
week_number_list = __get_distinct_week_number_for_season_and_gymnast(
|
||||
gymnast_id, season
|
||||
)
|
||||
|
||||
context = {
|
||||
"gymnast": gymnast,
|
||||
"actual_season": season,
|
||||
"season_list": season_list,
|
||||
"week_number": week_number,
|
||||
"actual_week_number": week_number,
|
||||
"week_number_list": week_number_list,
|
||||
}
|
||||
return render(request, "gymnasts/report_choices.html", context)
|
||||
|
@ -776,307 +782,50 @@ def generate_report_for_period(
|
|||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def generate_report_for_period(
|
||||
request, gymnast_id, season, period_value, date_begin, date_end, period="week"
|
||||
def generate_report_week_comparison(
|
||||
request, gymnast_id, season_source, week_source, season_target, week_target
|
||||
):
|
||||
"""Génère un rapport de toutes les informations entre les deux dates passées en paramètre.
|
||||
"""Génère un rapport avec une comparaison de deux semaines entre elles.
|
||||
|
||||
Args:
|
||||
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
|
||||
gymnast_id (int) Identifiant de la classe Gymnast
|
||||
season_source (str) saison de comparaison
|
||||
week_source (int) semaine de comparaison
|
||||
season_target (str) saison comparée
|
||||
week_target (int) semaine comparée
|
||||
"""
|
||||
today = pendulum.now().date()
|
||||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||||
season_informations = gymnast.season_informations.filter(season=season).first()
|
||||
|
||||
# season_informations = gymnast.season_informations.filter(season=season).first()
|
||||
|
||||
# PHYSIOLOGICAL INFORMATIONS
|
||||
# WellBeing Score
|
||||
wellbeing_score_quantity = gymnast.wellbeings.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
source_wellbeing_score_quantity = gymnast.wellbeings.filter(
|
||||
season=season_source, week_number=week_source
|
||||
).count()
|
||||
wellbeing_score = gymnast.wellbeings.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
min_mindstate_value=Min("mindstate"),
|
||||
average_mindstate_value=Avg("mindstate"),
|
||||
max_mindstate_value=Max("mindstate"),
|
||||
min_sleep_value=Min("sleep"),
|
||||
average_sleep_value=Avg("sleep"),
|
||||
max_sleep_value=Max("sleep"),
|
||||
min_stress_value=Min("stress"),
|
||||
average_stress_value=Avg("stress"),
|
||||
max_stress_value=Max("stress"),
|
||||
min_fatigue_value=Min("fatigue"),
|
||||
average_fatigue_value=Avg("fatigue"),
|
||||
max_fatigue_value=Max("fatigue"),
|
||||
min_muscle_soreness_value=Min("muscle_soreness"),
|
||||
average_muscle_soreness_value=Avg("muscle_soreness"),
|
||||
max_muscle_soreness_value=Max("muscle_soreness"),
|
||||
source_wellbeing_score = get_wellbeing_stats_for_season_week(
|
||||
gymnast_id, season_source, week_source
|
||||
)
|
||||
|
||||
height_weight_quantity = gymnast.height_weight.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
target_wellbeing_score_quantity = gymnast.wellbeings.filter(
|
||||
season=season_target, week_number=week_target
|
||||
).count()
|
||||
height_weight_value = gymnast.height_weight.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
min_height_value=Min("height"),
|
||||
average_height_value=Avg("height"),
|
||||
max_height_value=Max("height"),
|
||||
min_weight_value=Min("weight"),
|
||||
average_weight_value=Avg("weight"),
|
||||
max_weight_value=Max("weight"),
|
||||
target_wellbeing_score = get_wellbeing_stats_for_season_week(
|
||||
gymnast_id, season_target, week_target
|
||||
)
|
||||
|
||||
intensity_quantity = gymnast.intensities.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
# Intensity Score
|
||||
source_intensity_quantity = gymnast.intensities.filter(
|
||||
season=season_source, week_number=week_source
|
||||
).count()
|
||||
intensity_value = gymnast.intensities.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
average_intensity_time_value=Avg("time"),
|
||||
average_intensity_difficulty_value=Avg("difficulty"),
|
||||
average_quantity_of_skill_value=Avg("quantity_of_skill"),
|
||||
average_number_of_passes_value=Avg("number_of_passes"),
|
||||
average_time_quality=Avg("time_quality"),
|
||||
average_difficulty_quality=Avg("difficulty_quality"),
|
||||
average_quantity_of_skill_quality=Avg("quantity_of_skill_quality"),
|
||||
average_number_of_passes_quality=Avg("number_of_passes_quality"),
|
||||
average_average_training_quality=Avg("average_training_quality"),
|
||||
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"),
|
||||
min_time_quality=Min("time_quality"),
|
||||
min_difficulty_quality=Min("difficulty_quality"),
|
||||
min_quantity_of_skill_quality=Min("quantity_of_skill_quality"),
|
||||
min_number_of_passes_quality=Min("number_of_passes_quality"),
|
||||
min_average_training_quality=Min("average_training_quality"),
|
||||
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"),
|
||||
max_time_quality=Max("time_quality"),
|
||||
max_difficulty_quality=Max("difficulty_quality"),
|
||||
max_quantity_of_skill_quality=Max("quantity_of_skill_quality"),
|
||||
max_number_of_passes_quality=Max("number_of_passes_quality"),
|
||||
max_average_training_quality=Max("average_training_quality"),
|
||||
source_intensity_value = get_intensity_stats_for_season_week(
|
||||
gymnast_id, season_source, week_source
|
||||
)
|
||||
|
||||
injury_list = gymnast.injuries.filter(
|
||||
date__gte=date_begin, date__lte=date_end
|
||||
).order_by("date")
|
||||
|
||||
# BEST TOF
|
||||
number_of_tof_straightjump = Chrono.objects.filter(
|
||||
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
|
||||
target_intensity_quantity = gymnast.intensities.filter(
|
||||
season=season_target, week_number=week_target
|
||||
).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"),
|
||||
average_score=Avg("score"),
|
||||
average_tof=Avg("tof"),
|
||||
max_score=Max("score"),
|
||||
max_tof=Max("tof"),
|
||||
)
|
||||
|
||||
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"),
|
||||
average_score=Avg("score"),
|
||||
average_tof=Avg("tof"),
|
||||
max_score=Max("score"),
|
||||
max_tof=Max("tof"),
|
||||
)
|
||||
|
||||
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"),
|
||||
average_score=Avg("score"),
|
||||
average_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"),
|
||||
average_score=Avg("score"),
|
||||
average_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"),
|
||||
average_score=Avg("score"),
|
||||
average_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"),
|
||||
average_score=Avg("score"),
|
||||
average_tof=Avg("tof"),
|
||||
max_score=Max("score"),
|
||||
max_tof=Max("tof"),
|
||||
)
|
||||
|
||||
# BEST SCORES
|
||||
best_point_routine_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, event__date_begin__lte=date_begin, routine_type=2
|
||||
)
|
||||
.order_by("-total")
|
||||
.first()
|
||||
)
|
||||
best_point_routine_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, event__date_begin__lte=date_begin, routine_type=4
|
||||
)
|
||||
.order_by("-total")
|
||||
.first()
|
||||
)
|
||||
best_point_routine_5 = (
|
||||
Point.objects.filter(
|
||||
gymnast=gymnast, event__date_begin__lte=date_begin, routine_type=5
|
||||
)
|
||||
.order_by("-total")
|
||||
.first()
|
||||
)
|
||||
# ROUTINES
|
||||
q1r1 = (
|
||||
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, date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
|
||||
)
|
||||
|
||||
q1r2 = (
|
||||
gymnast.has_routine.filter(routine_type=2)
|
||||
.filter(date_begin__lte=date_begin)
|
||||
.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, date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
|
||||
)
|
||||
|
||||
q2r1 = (
|
||||
gymnast.has_routine.filter(routine_type=3)
|
||||
.filter(date_begin__lte=date_begin)
|
||||
.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, date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
|
||||
)
|
||||
|
||||
sfinal = (
|
||||
gymnast.has_routine.filter(routine_type=4)
|
||||
.filter(date_begin__lte=date_begin)
|
||||
.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, date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
|
||||
)
|
||||
|
||||
final = (
|
||||
gymnast.has_routine.filter(routine_type=5)
|
||||
.filter(date_begin__lte=date_begin)
|
||||
.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, date__gte=date_begin, date__lte=date_end
|
||||
).aggregate(
|
||||
total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes")
|
||||
)
|
||||
|
||||
# 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()))
|
||||
.order_by("date")
|
||||
.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())
|
||||
|
||||
notes = (
|
||||
gymnast.remarks.filter(date__gte=date_begin, date__lte=date_end)
|
||||
.filter(status=1)
|
||||
.order_by("date")
|
||||
target_intensity_value = get_intensity_stats_for_season_week(
|
||||
gymnast_id, season_target, week_target
|
||||
)
|
||||
|
||||
context = {
|
||||
|
@ -1089,67 +838,71 @@ def generate_report_for_period(
|
|||
"HEAD_COACH": settings.HEAD_COACH,
|
||||
"MOBILE_PHONE": settings.MOBILE_PHONE,
|
||||
"HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL,
|
||||
"season": season,
|
||||
"date_begin": date_begin,
|
||||
"date_end": date_end,
|
||||
"period": period,
|
||||
"period_value": period_value,
|
||||
# "season": season,
|
||||
# "date_begin": date_begin,
|
||||
# "date_end": date_end,
|
||||
# "period": period,
|
||||
# "period_value": period_value,
|
||||
"today": today,
|
||||
# GYMNAST INFORMATIONS
|
||||
"gymnast": gymnast,
|
||||
"season_informations": season_informations,
|
||||
# "season_informations": season_informations,
|
||||
# MEDICAL INFORMATIONS
|
||||
"wellbeing_score_quantity": wellbeing_score_quantity,
|
||||
"wellbeing_score": wellbeing_score,
|
||||
"height_weight_value": height_weight_value,
|
||||
"injury_list": injury_list,
|
||||
# INTENSITY
|
||||
"intensity_quantity": intensity_quantity,
|
||||
"intensity_value": intensity_value,
|
||||
# TOF
|
||||
"number_of_tof_straightjump": number_of_tof_straightjump,
|
||||
"best_tof_straightjump": best_tof_straightjump,
|
||||
"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": q1r1,
|
||||
"q1r2": q1r2,
|
||||
"q2r1": q2r1,
|
||||
"sfinal": sfinal,
|
||||
"final": final,
|
||||
"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,
|
||||
"next_event_list": next_event_list,
|
||||
"notes": notes,
|
||||
"source_wellbeing_score_quantity": source_wellbeing_score_quantity,
|
||||
"source_wellbeing_score": source_wellbeing_score,
|
||||
"target_wellbeing_score_quantity": target_wellbeing_score_quantity,
|
||||
"target_wellbeing_score": target_wellbeing_score,
|
||||
# "height_weight_value": height_weight_value,
|
||||
# "injury_list": injury_list,
|
||||
# # INTENSITY
|
||||
"source_intensity_quantity": source_intensity_quantity,
|
||||
"source_intensity_value": source_intensity_value,
|
||||
"target_intensity_quantity": target_intensity_quantity,
|
||||
"target_intensity_value": target_intensity_value,
|
||||
# # TOF
|
||||
# "number_of_tof_straightjump": number_of_tof_straightjump,
|
||||
# "best_tof_straightjump": best_tof_straightjump,
|
||||
# "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": q1r1,
|
||||
# "q1r2": q1r2,
|
||||
# "q2r1": q2r1,
|
||||
# "sfinal": sfinal,
|
||||
# "final": final,
|
||||
# "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,
|
||||
# "next_event_list": next_event_list,
|
||||
# "notes": notes,
|
||||
}
|
||||
|
||||
return render(request, "gymnasts/reports/report_periodical.html", context)
|
||||
return render(request, "gymnasts/reports/report_week_comparison.html", context)
|
||||
|
||||
# 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" # pylint: disable=line-too-long
|
||||
|
||||
# html = render_to_string("gymnasts/reports/report_periodical.html", context)
|
||||
# html = render_to_string("gymnasts/reports/report_week_comparison.html", context)
|
||||
|
||||
# # font_config = FontConfiguration()
|
||||
# HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
|
||||
|
|
Loading…
Reference in New Issue