Jarvis/jarvis/people/views.py

524 lines
18 KiB
Python
Raw Normal View History

2024-01-14 14:48:31 +01:00
from datetime import date
2023-04-25 17:06:14 +02:00
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model
2023-04-29 15:31:14 +02:00
from django.contrib.contenttypes.models import ContentType
2023-04-25 17:06:14 +02:00
from django.db.models import (
Q,
Avg,
)
from django.http import HttpResponseRedirect, JsonResponse
2023-04-25 17:06:14 +02:00
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.conf import settings
from django.core.mail import send_mail
import pendulum
2024-01-14 14:48:31 +01:00
2023-04-25 17:06:14 +02:00
from jarvis.followup.models import Event
from jarvis.followup.forms import GymnastHasRoutineForm
from jarvis.followup.models import (
Note,
Plan,
Skill,
Point,
Chrono,
2023-10-11 16:32:09 +02:00
Injury,
2023-07-05 10:51:49 +02:00
WellBeing,
2023-04-25 17:06:14 +02:00
Intensity,
LearnedSkill,
HeightWeight,
SeasonInformation,
NumberOfRoutineDone,
)
from jarvis.tools.models import Season
2024-02-13 12:59:35 +01:00
from jarvis.tools.clean_name import clean_name
2023-04-25 17:06:14 +02:00
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).
2023-04-25 17:06:14 +02:00
"""
results = []
pattern = request.POST.get("pattern", None)
2023-05-08 10:12:15 +02:00
if pattern is not None and len(pattern) > 2:
2024-02-13 12:59:35 +01:00
name = clean_name(pattern)
# gymnast_list = Gymnast.objects.filter(
# Q(cleaned_last_name__icontains=name) | Q(cleaned_first_name__icontains=name)
# )
gymnast_list = Gymnast.objects.filter(
is_active=True, pk__in=request.session["available_gymnast"]
).filter(
Q(cleaned_last_name__icontains=name) | Q(cleaned_first_name__icontains=name)
2023-04-25 17:06:14 +02:00
)
2024-02-13 12:59:35 +01:00
results = [{"ID": x.id, "Name": str(x)} for x in gymnast_list]
2023-04-25 17:06:14 +02:00
return JsonResponse(results, safe=False)
@login_required
@require_http_methods(["GET"])
def gymnast_listing(request):
"""
2024-02-11 09:25:20 +01:00
Si la personne connectée est un entraîneur : liste tous les gymnasts actifs connus mais n'aura
accès qu'aux gymnastes autorisé(e)s.
Si la personne connectée est un gymnaste : renvoie directement sa fiche détaillée.
2023-04-25 17:06:14 +02:00
"""
season = Season()
2024-02-13 12:59:35 +01:00
2023-04-25 17:06:14 +02:00
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)
2024-01-14 14:48:31 +01:00
gymnast = Gymnast.objects.get(user=request.user) # a mettre dans un TRY
return gymnast_details(request, gymnast.id)
2023-04-25 17:06:14 +02:00
@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é
2023-04-25 17:06:14 +02:00
"""
2024-02-11 12:12:40 +01:00
if request.user.is_superuser or (
request.session.has_key("available_gymnast")
and gymnast_id in request.session["available_gymnast"]
):
2023-04-25 17:06:14 +02:00
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
else:
2024-02-11 09:25:20 +01:00
return gymnast_listing(request)
2023-04-25 17:06:14 +02:00
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_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
2023-04-25 17:06:14 +02:00
"""
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]
2023-04-25 17:06:14 +02:00
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_physiological(request, gymnast_id):
"""
2023-07-06 23:37:07 +02:00
Renvoie les listes des tailles/poids, état d'esprit et blessures.
Args:
gymnast_id (int) identifiant du gymnast
2023-04-25 17:06:14 +02:00
"""
number_of_injuries = Injury.objects.filter(gymnast=gymnast_id).count()
2024-01-25 10:56:59 +01:00
start_date = pendulum.now().date().subtract(months=6)
injuries_list = Injury.objects.filter(
gymnast=gymnast_id, date__gte=start_date
).order_by("date")
wellbeing_list = WellBeing.objects.filter(
gymnast=gymnast_id, date__gte=start_date
).order_by("date")
height_weight_list = HeightWeight.objects.filter(
gymnast=gymnast_id, date__gte=start_date
).order_by("date")
2023-04-25 17:06:14 +02:00
context = {
2023-07-06 23:37:07 +02:00
"injuries_list": injuries_list,
"number_of_injuries": number_of_injuries,
2023-07-05 09:33:03 +02:00
"wellbeing_list": wellbeing_list,
2023-04-25 17:06:14 +02:00
"height_weight_list": height_weight_list,
"gymnast_id": gymnast_id,
}
return render(
request, "gymnasts/tabs/tab_physiological_and_wellbeing.html", context
)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET"])
def gymnast_display_scores_chrono(request, gymnast_id):
"""
Selectionne tous les scores réalisés par le gymnaste.
2023-04-25 17:06:14 +02:00
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
2024-02-04 18:55:02 +01:00
TODO: limiter les intensités aux X derniers records/semaines/mois ?
2023-04-25 17:06:14 +02:00
"""
2024-01-25 10:56:59 +01:00
start_date = pendulum.now().date().subtract(months=6)
2024-02-04 18:55:02 +01:00
intensity_list = Intensity.objects.filter(gymnast=gymnast_id).order_by("date")
2024-01-25 10:56:59 +01:00
chrono_list = Chrono.objects.filter(
gymnast=gymnast_id, date__gte=start_date
).order_by("date")
2023-04-25 17:06:14 +02:00
base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof"))
context = {
2024-02-04 18:55:02 +01:00
"intensity_list": intensity_list,
2023-04-25 17:06:14 +02:00
"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,
}
2024-02-04 18:55:02 +01:00
return render(request, "gymnasts/tabs/tab_intensity_and_chronos.html", context)
2023-04-25 17:06:14 +02:00
@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.
2023-05-08 10:12:15 +02:00
Args:
gymnast_id (int) identifiant de la classe Gymnast
2023-04-25 17:06:14 +02:00
"""
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")
2024-02-04 18:55:02 +01:00
score_list = Point.objects.filter(gymnast=gymnast_id).order_by("-event__date_begin")
2023-04-25 17:06:14 +02:00
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,
2024-02-04 18:55:02 +01:00
"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),
2023-04-25 17:06:14 +02:00
"gymnast_id": gymnast_id,
}
2024-02-04 18:55:02 +01:00
return render(request, "gymnasts/tabs/tab_routines_and_scores.html", context)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET", "POST"])
def link_routine_to_gymnast(request, gymnast_id=None):
2023-05-08 10:12:15 +02:00
"""Lie une série à un gymnaste
Args:
gymnast_id (int) identifiant de la classe Gymnast
2023-05-08 10:12:15 +02:00
"""
2023-04-25 17:06:14 +02:00
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"])
2023-04-29 15:31:14 +02:00
receiver = []
functionality = ContentType.objects.get(model="gymnasthasroutine")
for notification in gymnast.notifications.filter(
functionality=functionality
):
receiver.append(notification.user.email)
2023-04-25 17:06:14 +02:00
send_mail(
"Nouvelle série",
"Une nouvelle série vous a été associée.",
settings.EMAIL_HOST_USER,
2023-04-29 15:31:14 +02:00
[gymnast.user.email, gymnast.email_trainer].append(receiver),
2023-04-25 17:06:14 +02:00
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):
"""
2023-05-08 10:12:15 +02:00
Formulaire de création et modification d'un gymnaste.
Args:
gymnast_id (int) Identifiant de la classe Gymnast
2023-04-25 17:06:14 +02:00
"""
2024-02-13 12:59:35 +01:00
if gymnast_id and (
request.user.is_superuser
or (
request.session.has_key("available_gymnast")
and gymnast_id in request.session["available_gymnast"]
)
):
2023-04-25 17:06:14 +02:00
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,)))
2024-01-14 14:48:31 +01:00
return render(request, "people/gymnasts/create.html", {"form": gymnast_form})
2023-04-25 17:06:14 +02:00
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
2023-04-25 17:06:14 +02:00
"""
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 = (
2024-01-14 14:48:31 +01:00
Plan.objects.filter(gymnast=gymnast.id, educative__in=Skill.objects.all())
2023-04-25 17:06:14 +02:00
.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)
2024-01-14 14:48:31 +01:00
confused_skill = []
skill_whith_help = []
skill_without_help = []
skill_chained = []
skill_masterised = []
2023-04-25 17:06:14 +02:00
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 }}
2024-02-24 22:27:20 +01:00
return render(request, "gymnasts/tabs/tab_skills.html", context)