362 lines
12 KiB
Python
362 lines
12 KiB
Python
|
||
from django.contrib.auth.decorators import login_required
|
||
from django.contrib.auth.models import Group
|
||
from django.contrib.auth import get_user_model
|
||
User = get_user_model()
|
||
|
||
from django.db.models import Q, F, Avg
|
||
from django.db.models.functions import TruncDay
|
||
from django.http import 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
|
||
|
||
|
||
import pendulum
|
||
|
||
from ultron.followup.models import Event
|
||
from ultron.followup.forms import GymnastHasRoutineForm
|
||
from ultron.followup.models import (
|
||
Chrono,
|
||
LearnedSkill,
|
||
MindState,
|
||
Skill,
|
||
Point,
|
||
Accident,
|
||
HeightWeight,
|
||
NumberOfRoutineDone,
|
||
)
|
||
|
||
from .models import Gymnast
|
||
from .forms import GymnastForm, UserForm
|
||
|
||
|
||
@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)
|
||
|
||
# Ignore queries shorter than length 3
|
||
if pattern is not None and len(pattern) > 3:
|
||
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):
|
||
"""
|
||
Liste tous les gymnasts connus
|
||
"""
|
||
gymnast_list = Gymnast.objects.all()
|
||
context = {"gymnast_list": gymnast_list}
|
||
return render(request, "people/gymnasts/list.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET"])
|
||
def gymnast_details(request, gymnast_id, tab=None):
|
||
"""
|
||
Récupère toutes les informations d'un gymnaste.
|
||
"""
|
||
|
||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||
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,
|
||
"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, "people/gymnasts/details.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET"])
|
||
def gymnast_display_event(request, gymnast_id):
|
||
"""
|
||
Renvoie deux listes d'évènements : ceux à venir et ceux passés.
|
||
"""
|
||
today = pendulum.now().date()
|
||
next_event_list = Event.objects.filter(gymnasts=gymnast_id, datebegin__gte=today)
|
||
previous_event_list = Event.objects.filter(
|
||
gymnasts=gymnast_id, datebegin__lte=today
|
||
)
|
||
|
||
context = {
|
||
"next_event_list": next_event_list,
|
||
"previous_event_list": previous_event_list,
|
||
}
|
||
return render(request, "people/gymnasts/list_event.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET"])
|
||
def gymnast_display_accident(request, gymnast_id):
|
||
"""
|
||
Renvoie deux listes d'évènements : ceux à venir et ceux passés.
|
||
"""
|
||
accident_list = Accident.objects.filter(gymnast=gymnast_id)
|
||
context = {"accident_list": accident_list, "gymnast_id": gymnast_id}
|
||
return render(request, "people/gymnasts/list_accident.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 accidents.
|
||
"""
|
||
accident_list = Accident.objects.filter(gymnast=gymnast_id).order_by("date")
|
||
mindstate_list = MindState.objects.filter(gymnast=gymnast_id).order_by("date")
|
||
height_weight_list = HeightWeight.objects.filter(gymnast=gymnast_id).order_by(
|
||
"date"
|
||
)
|
||
|
||
context = {
|
||
"accident_list": accident_list,
|
||
"mindstate_list": mindstate_list,
|
||
"height_weight_list": height_weight_list,
|
||
"gymnast_id": gymnast_id,
|
||
}
|
||
return render(request, "people/gymnasts/physiological_followup.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.
|
||
"""
|
||
score_list = Point.objects.filter(gymnast=gymnast_id).order_by("-event__datebegin")
|
||
chrono_list = Chrono.objects.filter(gymnast=gymnast_id).order_by("date")
|
||
base_queryset = chrono_list.values('date').annotate(score_avg=Avg('score'))
|
||
|
||
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, "people/gymnasts/list_scores_chronos.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET"])
|
||
def gymnast_display_mindstate(request, gymnast_id):
|
||
"""
|
||
Selectionne tous les scores réalisés par le gymnaste
|
||
"""
|
||
mindstate_list = MindState.objects.filter(gymnast=gymnast_id).order_by("-date")
|
||
|
||
context = {
|
||
"mindstate_list": mindstate_list,
|
||
"gymnast_id": gymnast_id,
|
||
}
|
||
return render(request, "people/gymnasts/list_mindstate.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET"])
|
||
def gymnast_display_routines(request, gymnast_id):
|
||
"""
|
||
Tag affichant les séries d'un gymnaste.
|
||
"""
|
||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||
ghr_list = gymnast.has_routine.prefetch_related("routine").filter(
|
||
dateend__isnull=True
|
||
)
|
||
routine_done_list = NumberOfRoutineDone.objects.filter(gymnast=gymnast_id).order_by(
|
||
"-date"
|
||
)[:8]
|
||
|
||
context = {
|
||
"ghr_list": ghr_list,
|
||
"routine_done_list": routine_done_list,
|
||
"gymnast_id": gymnast_id,
|
||
}
|
||
return render(request, "people/gymnasts/list_routine.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET", "POST"])
|
||
def link_routine_to_gymnast(request, gymnast_id=None):
|
||
""" """
|
||
|
||
if gymnast_id:
|
||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||
data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast), "dateend": None}
|
||
else:
|
||
gymnast = None
|
||
data = {"dateend": None}
|
||
|
||
if request.method == "POST":
|
||
form = GymnastHasRoutineForm(request.POST)
|
||
|
||
if form.is_valid():
|
||
form.save()
|
||
if gymnast_id is not None:
|
||
return HttpResponseRedirect(reverse("gymnast_details", args=(gymnast_id,)))
|
||
|
||
return HttpResponseRedirect(reverse("gymnast_list"))
|
||
|
||
else:
|
||
form = GymnastHasRoutineForm(instance=gymnast, initial=data)
|
||
|
||
context = {"form": form, "gymnast_id": gymnast_id}
|
||
return render(request, "people/gymnasts/link_to_routine.html", context)
|
||
|
||
|
||
@login_required
|
||
@require_http_methods(["GET", "POST"])
|
||
def gymnast_create_or_update(request, gymnast_id=None):
|
||
"""
|
||
Formulaire de creation et modification d'un gymnaste.
|
||
"""
|
||
|
||
if gymnast_id:
|
||
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
|
||
data = {"club_related": gymnast.club}
|
||
else:
|
||
gymnast = None
|
||
data = {}
|
||
|
||
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()
|
||
form_data['username'] = gymnast.first_name.lower() + '_' + gymnast.last_name.lower()
|
||
user_form = UserForm(form_data, instance=gymnast.user)
|
||
|
||
if user_form.is_valid():
|
||
user = user_form.save()
|
||
|
||
gymnast_group = Group.objects.get(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,)))
|
||
|
||
form = GymnastForm(instance=gymnast, initial=data)
|
||
|
||
context = {"form": form, "gymnast_id": gymnast_id}
|
||
return render(request, "people/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, ...)
|
||
"""
|
||
|
||
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_level()
|
||
context.update(gymnast.get_informations_from_rank())
|
||
|
||
planified_skill = (
|
||
Skill.objects.filter(plan__gymnast=gymnast.id)
|
||
.order_by("-plan__date")
|
||
.annotate(plan_date=F("plan__date"))
|
||
)
|
||
|
||
context["planified_skill"] = planified_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)
|
||
|
||
skill_whith_help = (
|
||
Skill.objects.filter(known_by__gymnast=gymnast_id, known_by__cando=1)
|
||
.exclude(known_by__gymnast=gymnast.id, known_by__cando__gte=2)
|
||
.distinct()
|
||
)
|
||
|
||
skill_not_chained = (
|
||
Skill.objects.filter(known_by__gymnast=gymnast_id, known_by__cando=2)
|
||
.exclude(known_by__gymnast=gymnast.id, known_by__cando=3)
|
||
.distinct()
|
||
)
|
||
|
||
context["gymnast"] = gymnast
|
||
context["skill_whith_help"] = skill_whith_help
|
||
context["skill_not_chained"] = skill_not_chained
|
||
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, "people/gymnasts/list_skill.html", context)
|