Jarvis/jarvis/people/views.py

1486 lines
49 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import date
from statistics import mean
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.db.models import (
Q,
Avg,
Sum,
Min,
Max,
)
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods
from django.urls import reverse
from django.template.loader import render_to_string
from django.conf import settings
from django.core.mail import send_mail
from weasyprint import HTML, CSS
# from weasyprint.fonts import FontConfiguration
import pendulum
from jarvis.followup.models import Event
from jarvis.followup.forms import GymnastHasRoutineForm
from jarvis.followup.models import (
Note,
Plan,
Skill,
Point,
Chrono,
Injury,
WellBeing,
Intensity,
LearnedSkill,
HeightWeight,
SeasonInformation,
NumberOfRoutineDone,
)
from jarvis.followup.models import LEARNING_STEP_CHOICES
from jarvis.tools.models import Season
# from jarvis.tools.pdf_generator import GymnastReportDocument
from jarvis.tools.date_week_transition import (
from_date_to_week_number,
from_week_number_to_date,
from_month_number_to_date,
from_season_to_date,
)
from .models import Gymnast
from .forms import GymnastForm, UserForm
User = get_user_model()
@login_required
@require_http_methods(["POST"])
def gymnast_lookup(request):
"""
Récupère la liste des gymnastes à la volée suivant des caractères de
recherche entrés (min 3 caractères).
"""
results = []
pattern = request.POST.get("pattern", None)
if pattern is not None and len(pattern) > 2:
model_results = Gymnast.objects.filter(
Q(last_name__icontains=pattern) | Q(first_name__icontains=pattern)
)
results = [{"ID": x.id, "Name": str(x)} for x in model_results]
return JsonResponse(results, safe=False)
@login_required
@require_http_methods(["GET"])
def gymnast_listing(request):
"""
Si la personne connectée est un entraîneur : liste tous les gymnasts ACTIFS connus.
Si la personne connectée est un gymnaste : renvoie sa fiche détaillée.
"""
season = Season()
if request.user.groups.filter(name="trainer").exists():
season_information_list = SeasonInformation.objects.filter(
season=season.label
).select_related("gymnast")
context = {"season_information_list": season_information_list}
return render(request, "gymnasts/list.html", context)
gymnast = Gymnast.objects.get(user=request.user) # a mettre dans un TRY
return gymnast_details(request, gymnast.id)
@login_required
@require_http_methods(["GET"])
def gymnast_details(request, gymnast_id, tab=None):
"""
Récupère toutes les informations d'un gymnaste si la personne connectée est un "trainer".
Si la personne connectée est un gymnaste : renvoie sa fiche détaillée.
Args:
gymnast_id (int) identifiant du gymnast
tab (str) <string> de l'onglet désiré
"""
if request.user.groups.filter(name="trainer").exists():
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
else:
gymnast = Gymnast.objects.get(user=request.user)
last_season_information = gymnast.season_informations.order_by("-season").first()
gymnast_nb_known_skills = gymnast.known_skills.distinct(
"skill"
).count() # devrait disparaitre
nb_skill = Skill.objects.all().count()
nb_known_skill = (
LearnedSkill.objects.filter(gymnast=gymnast_id).distinct("skill").count()
)
if nb_skill != 0:
percentage_known_skill = (nb_known_skill / nb_skill) * 100
else:
percentage_known_skill = 0
# base_queryset = Chrono.objects.filter(gymnast=gymnast_id).order_by("-date")
chronos_list = Chrono.objects.filter(gymnast=gymnast_id).order_by("-date")[:10]
straightjump_score = (
Chrono.objects.filter(gymnast=gymnast_id)
.filter(chrono_type=0)
.order_by("-date")
)
best_straightjump = (
Chrono.objects.filter(gymnast=gymnast_id)
.filter(chrono_type=0)
.order_by("-score")[:1]
)
best_routine = (
Chrono.objects.filter(gymnast=gymnast_id)
.filter(chrono_type=1)
.order_by("-score")[:1]
)
context = {
"gymnast": gymnast,
"last_season_information": last_season_information,
"gymnast_nb_known_skills": gymnast_nb_known_skills,
"chronos_list": chronos_list,
"straightjump_score": straightjump_score,
"best_routine": best_routine,
"best_straightjump": best_straightjump,
"nb_skill": nb_skill,
"nb_known_skill": nb_known_skill,
"percentage_known_skill": percentage_known_skill,
"tab": tab,
}
context["user_is_trainer"] = request.user.groups.filter(
name="trainer"
).exists() # TODO: utiliser les {{ perms }}
return render(request, "gymnasts/details.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_report_list(request, gymnast_id):
"""
Args:
gymnast_id (int) identifiant du gymnast
"""
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
today = pendulum.now().date()
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)
)
context = {
"gymnast": gymnast,
"season": season,
"season_list": season_list,
"week_number": week_number,
"week_number_list": week_number_list,
}
return render(request, "gymnasts/tabs/tab_documents.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_events_and_notes(request, gymnast_id):
"""
Renvoie deux listes d'évènements : ceux à venir et ceux passés.
Args:
gymnast_id (int) identifiant du gymnast
"""
today = pendulum.now().date()
next_event_list = Event.objects.filter(gymnasts=gymnast_id, date_begin__gte=today)
previous_event_list = Event.objects.filter(
gymnasts=gymnast_id, date_begin__lte=today
)
base_queryset = Note.objects.filter(gymnast=gymnast_id)
if not request.user.groups.filter(name="trainer").exists():
notes_list = base_queryset.filter(status=1)
else:
notes_list = base_queryset
notes_list = notes_list.order_by("-created_at")
last_notes_list = notes_list[:6]
latest_published_note = (
base_queryset.filter(status=1).order_by("-created_at").first()
)
context = {
"next_event_list": next_event_list,
"previous_event_list": previous_event_list,
"last_notes_list": last_notes_list,
"latest_published_note": latest_published_note,
"gymnast_id": gymnast_id,
}
return render(request, "gymnasts/tabs/tab_events_and_notes.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_injury(request, gymnast_id):
"""
Renvoie la liste des blessures d'un gymnaste.
Args:
gymnast_id (int) identifiant du gymnast
"""
injuries_list = Injury.objects.filter(gymnast=gymnast_id)
context = {"injuries_list": injuries_list, "gymnast_id": gymnast_id}
return render(request, "gymnasts/list_injury.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_physiological(request, gymnast_id):
"""
Renvoie les listes des tailles/poids, état d'esprit et blessures.
Args:
gymnast_id (int) identifiant du gymnast
"""
injuries_list = Injury.objects.filter(gymnast=gymnast_id).order_by("date")
wellbeing_list = WellBeing.objects.filter(gymnast=gymnast_id).order_by("date")
height_weight_list = HeightWeight.objects.filter(gymnast=gymnast_id).order_by(
"date"
)
context = {
"injuries_list": injuries_list,
"wellbeing_list": wellbeing_list,
"height_weight_list": height_weight_list,
"gymnast_id": gymnast_id,
}
return render(request, "gymnasts/tabs/tab_physiological.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_scores_chrono(request, gymnast_id):
"""
Selectionne tous les scores réalisés par le gymnaste.
Afin de ne pas avoir plusieurs valeurs pour une même date, nous faisons une
moyenne par date.
Args:
gymnast_id (int) identifiant du gymnast
"""
score_list = Point.objects.filter(gymnast=gymnast_id).order_by("-event__date_begin")
chrono_list = Chrono.objects.filter(gymnast=gymnast_id).order_by("date")
base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof"))
context = {
"score_list": score_list,
"score_routine1_list": score_list.filter(routine_type=1),
"score_routine2_list": score_list.filter(routine_type=2),
"score_routine3_list": score_list.filter(routine_type=3),
"chrono_list": chrono_list,
"chrono_10c": base_queryset.filter(chrono_type=0),
"chrono_r1": base_queryset.filter(chrono_type=1),
"chrono_r2": base_queryset.filter(chrono_type=2),
"chrono_rf": base_queryset.filter(chrono_type=3),
"gymnast_id": gymnast_id,
}
return render(request, "gymnasts/tabs/tab_scores_and_chronos.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_wellbeing(request, gymnast_id):
"""
Selectionne tous les scores réalisés par le gymnaste.
Args:
gymnast_id (int) identifiant du gymnast
"""
wellbeing_list = WellBeing.objects.filter(gymnast=gymnast_id).order_by("-date")
context = {
"wellbeing_list": wellbeing_list,
"gymnast_id": gymnast_id,
}
return render(request, "gymnasts/list_wellbeing.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_routine_statistics(request, gymnast_id):
"""
Tag affichant les statistiques et informations de séries d'un gymnaste.
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
ghr_list = gymnast.has_routine.prefetch_related("routine").filter(
Q(date_end__gte=date.today()) | Q(date_end__isnull=True)
)
has_routine_1 = ghr_list.filter(routine_type=1)
has_routine_2 = ghr_list.filter(routine_type=2)
has_routine_3 = ghr_list.filter(routine_type=3)
has_routine_4 = ghr_list.filter(routine_type=4)
has_routine_5 = ghr_list.filter(routine_type=5)
routine_one_done_list = NumberOfRoutineDone.objects.filter(
gymnast=gymnast_id, routine_type=1
).order_by("date")
routine_two_done_list = NumberOfRoutineDone.objects.filter(
gymnast=gymnast_id, routine_type=2
).order_by("date")
intensity_list = Intensity.objects.filter(gymnast=gymnast_id).order_by("date")
context = {
"ghr_list": ghr_list,
"has_routine_1": has_routine_1,
"has_routine_2": has_routine_2,
"has_routine_3": has_routine_3,
"has_routine_4": has_routine_4,
"has_routine_5": has_routine_5,
"routine_one_done_list": routine_one_done_list,
"routine_two_done_list": routine_two_done_list,
"intensity_list": intensity_list,
"gymnast_id": gymnast_id,
}
return render(request, "gymnasts/tabs/tab_routines_and_routine_stats.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def link_routine_to_gymnast(request, gymnast_id=None):
"""Lie une série à un gymnaste
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
if gymnast_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
data = {
"gymnast": gymnast_id,
"gymnast_related": str(gymnast),
"date_end": None,
}
else:
gymnast = None
data = {"date_end": None}
if request.method == "POST":
form = GymnastHasRoutineForm(request.POST)
if form.is_valid():
form.save()
if not gymnast:
gymnast = get_object_or_404(Gymnast, pk=form.cleaned_data["gymnast"])
receiver = []
functionality = ContentType.objects.get(model="gymnasthasroutine")
for notification in gymnast.notifications.filter(
functionality=functionality
):
receiver.append(notification.user.email)
send_mail(
"Nouvelle série",
"Une nouvelle série vous a été associée.",
settings.EMAIL_HOST_USER,
[gymnast.user.email, gymnast.email_trainer].append(receiver),
fail_silently=False,
html_message="""<p>Bonjour,</p>
<p>Une nouvelle série vous a été associée.</p><br />
<p>Excellente journée</p><p>Jarvis</p>""",
)
return HttpResponseRedirect(
reverse("gymnast_details_tab", args=(gymnast_id, "routine"))
)
else:
form = GymnastHasRoutineForm(instance=gymnast, initial=data)
context = {"form": form, "gymnast_id": gymnast_id}
return render(request, "gymnasts/link_to_routine.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def gymnast_create_or_update(request, gymnast_id=None):
"""
Formulaire de création et modification d'un gymnaste.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
"""
if gymnast_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
else:
gymnast = None
if request.method == "POST":
gymnast_form = GymnastForm(request.POST, instance=gymnast)
if gymnast_form.is_valid():
gymnast = gymnast_form.save()
form_data = request.POST.dict()
user_data = {}
user_data["first_name"] = form_data["first_name"]
user_data["last_name"] = form_data["last_name"]
user_data["username"] = (
form_data["first_name"].lower() + "_" + form_data["last_name"].lower()
)
user_data["email"] = form_data["email"].lower()
user_data["is_active"] = True
user_form = UserForm(user_data, instance=gymnast.user)
if user_form.is_valid():
user = user_form.save()
gymnast_group, _ = Group.objects.get_or_create(name="gymnast")
user.groups.add(gymnast_group)
gymnast.user = user
gymnast.save()
# if not user.has_usable_password():
# user.set_password(gymnast.last_name.lower() + _ + str(gymnast.birthdate)[-2:])
return HttpResponseRedirect(reverse("gymnast_details", args=(gymnast.pk,)))
return render(request, "people/gymnasts/create.html", {"form": gymnast_form})
form = GymnastForm(instance=gymnast)
context = {"form": form, "gymnast_id": gymnast_id}
return render(request, "gymnasts/create.html", context)
@login_required
@require_http_methods(["GET"])
def gymnast_display_skill(request, gymnast_id):
"""Tag affichant les statistiques de skill d'un gymnaste
Le nombre de saut qu'il sait faire (total, par niveau, par rank, …), calcule la complétude, …
.. todo:: Générer UNE fois la liste de skill que le gymnaste ne sait pas faire (1 query)
et les counts puis, dans le template on parcourt plusieurs fois cette même liste mais on
affiche conditionnellement (par age, par rank, ...)
Il y a 4 étapes dans l'apprentissage :
- skill confusion syndrom "No" - 0
- avec aide (tapis, manip, …) "with help" - 1
- sans aide () "without help" - 2
- enchaîné (un saut avec et après) "chained" - 3
- maitrisé (dans n'importe quelle condition) "masterised" - 4
Args:
gymnast_id (int) Identifiant de la classe Gymnast
"""
context = {}
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
gymnast_nb_known_skills = gymnast.known_skills.distinct("skill").count()
context = gymnast.get_informations_from_type("level")
context.update(gymnast.get_informations_from_type("rank"))
planned_skill = (
Plan.objects.filter(gymnast=gymnast.id, educative__in=Skill.objects.all())
.select_related("educative", "educative__skill")
.order_by("-date", "educative__long_label")
)
context["planned_skill"] = planned_skill
if gymnast.gender:
context["skill_by_age"] = Skill.objects.filter(
age_girl_masterised__lte=gymnast.age
).exclude(known_by__gymnast=gymnast.id)
else:
context["skill_by_age"] = Skill.objects.filter(
age_boy_masterised__lte=gymnast.age
).exclude(known_by__gymnast=gymnast.id)
learned_skills = (
LearnedSkill.objects.filter(gymnast=gymnast_id)
.select_related("skill")
.order_by("skill_id", "-date")
.distinct("skill_id")
)
# skill_learned_by_phase = [[]] * 5
# print(skill_learned_by_phase)
confused_skill = []
skill_whith_help = []
skill_without_help = []
skill_chained = []
skill_masterised = []
for learned_skill in learned_skills:
# print('Add skill for ' + str(learned_skill.learning_step) + ' phase')
# skill_learned_by_phase[learned_skill.learning_step].append(learned_skill.skill)
if learned_skill.learning_step == 0:
confused_skill.append(learned_skill.skill)
elif learned_skill.learning_step == 1:
skill_whith_help.append(learned_skill.skill)
elif learned_skill.learning_step == 2:
skill_without_help.append(learned_skill.skill)
elif learned_skill.learning_step == 3:
skill_chained.append(learned_skill.skill)
else:
skill_masterised.append(learned_skill.skill)
# for i in range(0,4):
# print(skill_learned_by_phase[i])
# context["confused_skill"] = skill_learned_by_phase[0]
# context["skill_whith_help"] = skill_learned_by_phase[1]
# context["skill_without_help"] = skill_learned_by_phase[2]
# context["skill_chained"] = skill_learned_by_phase[3]
# context["skill_masterised"] = skill_learned_by_phase[4]
context["confused_skill"] = confused_skill
context["skill_whith_help"] = skill_whith_help
context["skill_without_help"] = skill_without_help
context["skill_chained"] = skill_chained
context["skill_masterised"] = skill_masterised
context["gymnast"] = gymnast
context["gymnast_nb_known_skills"] = gymnast_nb_known_skills
context["user_is_trainer"] = request.user.groups.filter(
name="trainer"
).exists() # TODO: utiliser les {{ perms }}
return render(request, "gymnasts/tabs/tab_skill.html", context)
def analyse_score(value, value_list):
"""Analyse une valeur (value) par rapport à la moyenne de value_list et à la dernière
valeur de value_list.
Args:
value float valeur
value_list array<float> liste de valeurs
Returns:
string
Examples:
"""
result = ""
mean_value = mean(value_list)
if value > value_list[-1]:
result += "+"
elif value < value_list[-1]:
result += "-"
else:
result += "="
if value > mean_value:
result = "+" + result
elif value < mean_value:
result = "-" + result
else:
result = "=" + result
return result
def __compute_curve_orientation(value, previous_value, previous_previous_value):
"""Analyse une valeur (value) par rapport à la moyenne de value_list et à la dernière
valeur de value_list.
Args:
value float valeur
value_list array<float> liste de valeurs
Returns:
string
Examples:
"""
result = ""
if value > previous_value:
result += "+"
elif value < previous_value:
result += "-"
else:
result += "="
if previous_value > previous_previous_value:
result = "+" + result
elif previous_value < previous_previous_value:
result = "-" + result
else:
result = "=" + result
return result
def __get_distinct_followup_season_for_gymnast(gymnast_id):
"""Recupère les saisons pour lesquelles le gymnastes à des followup.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
"""
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
season_list = list(
gymnast.chronos.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.injuries.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.known_skills.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.todo.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.wellbeings.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.number_of_routine_done.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.height_weight.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.remarks.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
season_list += list(
gymnast.intensities.values_list("season", flat=True)
.distinct("season")
.order_by("season")
)
return list(dict.fromkeys(season_list))
def __get_distinct_week_number_for_season_and_gymnast(gymnast_id, season):
"""Récupère les numéro de semaines pour lesquelles le gymnaste à des followup.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine
"""
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")
)
weeknumber_list += list(
gymnast.injuries.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.known_skills.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.todo.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.wellbeings.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.number_of_routine_done.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.height_weight.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.remarks.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
weeknumber_list += list(
gymnast.intensities.values_list("week_number", flat=True)
.filter(season=season)
.distinct("week_number")
.order_by("week_number")
)
return list(dict.fromkeys(weeknumber_list))
def get_distinct_week_number_for_season_and_gymnast(gymnast_id, season):
"""
Args:
gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine
"""
if not season:
season = Season()
weeknumber_list = __get_distinct_week_number_for_season_and_gymnast(
gymnast_id, season
)
return JsonResponse(weeknumber_list, safe=False)
@require_http_methods(["GET"])
def report_choice(request, gymnast_id):
"""Recupère les saisons pour lesquelles le gymnastes à des followup.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
"""
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
today = pendulum.now().date()
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)
)
context = {
"gymnast": gymnast,
"season": season,
"season_list": season_list,
"week_number": week_number,
"week_number_list": week_number_list,
}
return render(request, "gymnasts/report_choices.html", context)
def __mindstate_analyse(gymnast, date_begin, date_end, period, mindstate_score):
"""Analyse de l'état d'esprit entre deux dates."""
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_mindstate_score = gymnast.wellbeings.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_mindstate_value=Avg("mindstate"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_mindstate_score = gymnast.wellbeings.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_mindstate_value=Avg("mindstate"))
return __compute_curve_orientation(
mindstate_score,
previous_mindstate_score["mean_mindstate_value"],
previous_previous_mindstate_score["mean_mindstate_value"],
)
def __height_analyse(gymnast, date_begin, date_end, period, height_score):
"""Analyse de la taille du gymnaste entre deux dates."""
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_height_score = gymnast.height_weight.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_height_value=Avg("height"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_height_score = gymnast.height_weight.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_height_value=Avg("height"))
return __compute_curve_orientation(
height_score,
previous_height_score["mean_height_value"],
previous_previous_height_score["mean_height_value"],
)
def __weight_analyse(gymnast, date_begin, date_end, period, weight_score):
"""Analyse du poids du gymnaste entre deux dates."""
previous_period_date_end = date_begin
if period == "week":
period_length = 7
elif period == "month":
period_length = 30
else:
period_length = 365
previous_period_date_begin = previous_period_date_end.subtract(days=period_length)
previous_height_score = gymnast.height_weight.filter(
date__gte=previous_period_date_begin, date__lte=previous_period_date_end
).aggregate(mean_weight_value=Avg("weight"))
previous_previous_period_date_end = previous_period_date_begin
previous_previous_period_date_begin = previous_previous_period_date_end.subtract(
days=period_length
)
previous_previous_height_score = gymnast.height_weight.filter(
date__gte=previous_previous_period_date_begin,
date__lte=previous_previous_period_date_end,
).aggregate(mean_weight_value=Avg("weight"))
return __compute_curve_orientation(
weight_score,
previous_height_score["mean_weight_value"],
previous_previous_height_score["mean_weight_value"],
)
@login_required
@require_http_methods(["GET"])
def generate_report_for_period(
request, gymnast_id, season, period_value, date_begin, date_end, period="week"
):
"""Génère un rapport de toutes les informations entre les deux dates passées en paramètre.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
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
"""
today = pendulum.now().date()
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
season_informations = gymnast.season_informations.filter(season=season).first()
# PHYSIOLOGICAL INFORMATIONS
# WellBeing Score
wellbeing_score = gymnast.wellbeings.filter(
date__gte=date_begin, date__lte=date_end
).aggregate(
min_mindstate_value=Min("mindstate"),
mean_mindstate_value=Avg("mindstate"),
max_mindstate_value=Max("mindstate"),
min_sleep_value=Min("sleep"),
mean_sleep_value=Avg("sleep"),
max_sleep_value=Max("sleep"),
min_stress_value=Min("stress"),
mean_stress_value=Avg("stress"),
max_stress_value=Max("stress"),
min_fatigue_value=Min("fatigue"),
mean_fatigue_value=Avg("fatigue"),
max_fatigue_value=Max("fatigue"),
min_muscle_soreness_value=Min("muscle_soreness"),
mean_muscle_soreness_value=Avg("muscle_soreness"),
max_muscle_soreness_value=Max("muscle_soreness"),
)
height_weight_value = gymnast.height_weight.filter(
date__gte=date_begin, date__lte=date_end
).aggregate(
min_height_value=Min("height"),
mean_height_value=Avg("height"),
max_height_value=Max("height"),
min_weight_value=Min("weight"),
mean_weight_value=Avg("weight"),
max_weight_value=Max("weight"),
)
intensity_value = gymnast.intensities.filter(
date__gte=date_begin, date__lte=date_end
).aggregate(
mean_intensity_time_value=Avg("time"),
mean_intensity_difficulty_value=Avg("difficulty"),
mean_quantity_of_skill_value=Avg("quantity_of_skill"),
mean_number_of_passes_value=Avg("number_of_passes"),
min_intensity_time_value=Min("time"),
min_intensity_difficulty_value=Min("difficulty"),
min_quantity_of_skill_value=Min("quantity_of_skill"),
min_number_of_passes_value=Min("number_of_passes"),
max_intensity_time_value=Max("time"),
max_intensity_difficulty_value=Max("difficulty"),
max_quantity_of_skill_value=Max("quantity_of_skill"),
max_number_of_passes_value=Max("number_of_passes"),
)
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
).count()
best_tof_straightjump = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=0
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_q1r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=1
).count()
best_tof_q1r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=1
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_q1r2 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=2
).count()
best_tof_q1r2 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=2
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_q2r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_q2r1 = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_sf = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_sf = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
number_of_tof_f = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).count()
best_tof_f = Chrono.objects.filter(
gymnast=gymnast, date__gte=date_begin, date__lte=date_end, chrono_type=3
).aggregate(
min_score=Min("score"),
min_tof=Min("tof"),
mean_score=Avg("score"),
mean_tof=Avg("tof"),
max_score=Max("score"),
max_tof=Max("tof"),
)
# BEST SCORES
best_point_routine_1 = (
Point.objects.filter(
gymnast=gymnast, 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")
)
context = {
# HEADER
"SITE_TITLE": settings.SITE_TITLE,
"CLUB_NAME": settings.CLUB_NAME,
"ADDRESS": settings.ADDRESS,
"CITY": settings.CITY,
"ZIP": settings.ZIP,
"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,
"today": today,
# GYMNAST INFORMATIONS
"gymnast": gymnast,
"season_informations": season_informations,
# MEDICAL INFORMATIONS
"wellbeing_score": wellbeing_score,
"height_weight_value": height_weight_value,
"injury_list": injury_list,
# INTENSITY
"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,
}
# return render(request, "gymnasts/reports/report_periodical.html", context)
response = HttpResponse(content_type="application/pdf")
response[
"Content-Disposition"
] = f"attachment; filename={gymnast.last_name}_{gymnast.first_name}_{period}-report_{date_begin}_{date_end}.pdf" # pylint: disable=line-too-long
html = render_to_string("gymnasts/reports/report_periodical.html", context)
# font_config = FontConfiguration()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
response,
stylesheets=[
CSS(settings.STATICFILES_DIRS[0] + "/css/gymnast_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/black-dashboard_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/font_awesome_all_5.15.3.css"),
],
) # , font_config=font_config)
return response
@login_required
@require_http_methods(["GET"])
def generate_season_report(request, gymnast_id, season):
"""Génère un rapport pour une saison passée en paramètre.
Args:
gymnast_id (int) identifiant de gymnaste
season (str) saison
"""
date_begin, date_end = from_season_to_date(season)
return generate_report_for_period(
request,
gymnast_id,
season,
season,
date_begin,
date_end,
period="season",
)
@login_required
@require_http_methods(["GET"])
def generate_month_report(request, gymnast_id, season, month_number):
"""Génère un rapport pour une saison et un mois passée en paramètre.
Args:
gymnast_id (int) identifiant de gymnaste
season (str) saison
month_number (int) mois de l'année
"""
date_begin, date_end = from_month_number_to_date(season, month_number)
month = date_begin.format("MMMM")
return generate_report_for_period(
request,
gymnast_id,
season,
month,
date_begin,
date_end,
period="month",
)
@login_required
@require_http_methods(["GET"])
def generate_week_report(request, gymnast_id, season, week_number):
"""Génère un rapport hebdomadaire.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
season (int) Numéro de semaine
week_number (int) Numéro de semaine
"""
date_begin, date_end = from_week_number_to_date(season, week_number)
return generate_report_for_period(
request,
gymnast_id,
season,
week_number,
date_begin,
date_end,
period="week",
)
@login_required
@require_http_methods(["GET"])
def generate_timeline_report(
request, gymnast_id, season=None, week_number=None, date=None
):
"""Génère une timeline pour un gymnaste. On va chercher tous les
- records (Chrono/ToF),
- points (compétition),
- nouveau apprentissage (learned skill)
- blessures
- GymnastHasRoutine
et on les trie par date.
Args:
gymnast_id (int) identifiant du gymnast
season (int) saison
week_number (int) numéro de semaine
date (date) date
"""
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
if season is None:
date_begin = pendulum.now().date()
season, week_number = from_date_to_week_number(date_begin)
else:
date_begin, _ = from_week_number_to_date(season, week_number)
date_begin = date_begin.date()
selected_record = []
list_record_straightjumps = list(
gymnast.chronos.filter(chrono_type=0).order_by("date")
)
last_top_score = 0
for record in list_record_straightjumps:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
list_record_first_routine = list(
gymnast.chronos.filter(chrono_type=1).order_by("date")
)
last_top_score = 0
for record in list_record_first_routine:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
list_record_second_routine = list(
gymnast.chronos.filter(chrono_type=2).order_by("date")
)
last_top_score = 0
for record in list_record_second_routine:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
list_record_third_routine = list(
gymnast.chronos.filter(chrono_type=3).order_by("date")
)
last_top_score = 0
for record in list_record_third_routine:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
list_record_fourth_routine = list(
gymnast.chronos.filter(chrono_type=4).order_by("date")
)
last_top_score = 0
for record in list_record_fourth_routine:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
list_record_fifth_routine = list(
gymnast.chronos.filter(chrono_type=5).order_by("date")
)
last_top_score = 0
for record in list_record_fifth_routine:
if record.score > last_top_score:
last_top_score = record.score
selected_record.append(record)
# print(selected_record)
list_record = selected_record
list_record.extend(list(gymnast.injuries.all().order_by("date")))
# list_record.extend(list(gymnast.points.all().order_by("date")))
list_record.extend(list(gymnast.known_skills.all().order_by("date")))
# print(list_record)
sorted_records = sorted(list_record, key=lambda x: x.date)
# print(sorted_records)
context = {
"SITE_TITLE": settings.SITE_TITLE,
"CLUB_NAME": settings.CLUB_NAME,
"ADDRESS": settings.ADDRESS,
"CITY": settings.CITY,
"ZIP": settings.ZIP,
"HEAD_COACH": settings.HEAD_COACH,
"MOBILE_PHONE": settings.MOBILE_PHONE,
"HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL,
"week_number": week_number,
"gymnast": gymnast,
"today": date_begin,
"season": season,
"sorted_records": sorted_records,
}
# return render(request, "gymnasts/reports/report_timeline.html", context)
response = HttpResponse(content_type="application/pdf")
response[
"Content-Disposition"
] = "attachment; filename={lastname}-{firstname}-report-timeline.pdf".format(
lastname=gymnast.last_name,
firstname=gymnast.first_name,
)
html = render_to_string("gymnasts/reports/report_timeline.html", context)
# font_config = FontConfiguration()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
response,
stylesheets=[
CSS(settings.STATICFILES_DIRS[0] + "/css/gymnast_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/black-dashboard_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/font_awesome_all_5.15.3.css"),
],
) # , font_config=font_config)
return response
# @login_required
# @require_http_methods(["GET"])
# def generate_week_resume(request, gymnast_id, season=None, week_number=None):
# """va rechercher les informations nécessaires pour aider à la note de la semaine
# - wellbeing
# - intensity
# - injury
# Args:
# gymnast_id (int) identifiant du gymnast
# season (int) saison
# week_number (int) numéro de semaine
# date (date) date
# """
# gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
# if season is None:
# date_begin = pendulum.now().date()
# season, week_number = from_date_to_week_number(date_begin)
# else:
# date_begin, _ = from_week_number_to_date(season, week_number)
# date_begin = date_begin.date()
# wellbeing_list = gymnast.wellbeing.objects.filter(season="season", week_number="week_number")
# intensity_list = gymnast.intensities.objects.filter(season="season", week_number="week_number") # pylint: disable=line-too-long
# injury_list = gymnast.injuries.filter(season="season", week_number="week_number")