Jarvis/jarvis/followup/views.py

1624 lines
56 KiB
Python
Raw Normal View History

2024-01-14 14:48:31 +01:00
from datetime import date, datetime
2023-04-25 17:06:14 +02:00
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 Q, Min, Avg, Max, Sum
from django.urls import reverse
from django.conf import settings
from django.contrib.auth import get_user_model
2023-04-28 12:20:55 +02:00
from django.contrib.contenttypes.models import ContentType
2023-04-25 17:06:14 +02:00
from django.core.mail import send_mail
2024-01-14 14:48:31 +01:00
import pendulum
2024-02-03 17:20:42 +01:00
from jarvis.core.models import Email
2023-04-25 17:06:14 +02:00
from jarvis.people.models import Gymnast
from jarvis.planning.models import Event
from jarvis.objective.models import Skill
2024-01-14 14:48:31 +01:00
from jarvis.tools.date_week_transition import from_date_to_week_number
from jarvis.tools.models import Season
2023-04-25 17:06:14 +02:00
2023-11-18 18:29:18 +01:00
from .models import (
CHRONO_TYPE_CHOICE,
SCORE_TYPE_CHOICE,
INJURY_MECHANISM_CHOICE,
INJURY_BODY_SIDE_CHOICE,
INJURY_TYPE_CHOICE,
INJURY_LOCATION_CHOICE,
)
2023-04-25 17:06:14 +02:00
from .models import (
Plan,
Note,
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,
ChronoDetails,
GymnastHasRoutine,
SeasonInformation,
NumberOfRoutineDone,
)
from .forms import (
PlanForm,
NoteForm,
ScoreForm,
ChronoForm,
2023-10-11 16:32:09 +02:00
InjuryForm,
2023-07-05 09:33:03 +02:00
WellBeingForm,
2023-04-25 17:06:14 +02:00
IntensityForm,
HeightWeightForm,
LearnedSkillForm,
SeasonInformationForm,
NumberOfRoutineDoneForm,
)
User = get_user_model()
2023-11-22 10:27:41 +01:00
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>&nbsp;<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>&nbsp;<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>&nbsp;<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>"""
2023-04-25 17:06:14 +02:00
@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
2024-01-14 14:48:31 +01:00
QTF : Est-ce que je ne devrais pas faire un prefetch_related sur mon objet chrono pour
optimiser mon affichage ?
2023-04-25 17:06:14 +02:00
chrono = Chrono.object.get(pk=chrono_id).prefetch_related('chrono_details') ?
"""
chrono = get_object_or_404(Chrono, pk=chrono_id)
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)
2023-04-25 17:06:14 +02:00
"""
if season is None:
today = pendulum.now().date()
season, week_number = from_date_to_week_number(today)
else:
2023-06-13 19:32:35 +02:00
season = Season(season).label
2024-01-14 14:48:31 +01:00
week_number = min(week_number, 52)
week_number = max(week_number, 1)
2023-04-25 17:06:14 +02:00
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.
2023-04-25 17:06:14 +02:00
"""
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.
2023-04-25 17:06:14 +02:00
"""
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
2024-01-14 14:48:31 +01:00
return HttpResponse(400, (row, created))
2023-04-25 17:06:14 +02:00
@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
2023-04-25 17:06:14 +02:00
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
2024-01-14 14:48:31 +01:00
QTF : le cast en date devrait être dans un try mais comment gérer correctement l'erreur - si
erreur il y a ?
2023-04-25 17:06:14 +02:00
"""
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
"""
2023-04-25 17:06:14 +02:00
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
"""
2023-04-25 17:06:14 +02:00
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
2023-04-25 17:06:14 +02:00
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
2023-04-25 17:06:14 +02:00
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):
"""
Si la personne connectée est un entraîneur, la fonction récupère la liste des chronos 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 chronos.
Args:
gymnast_id (int) identifiant d'un gymnaste
"""
gymnast = None
if request.user.groups.filter(name="trainer").exists():
if gymnast_id:
chrono_list = Chrono.objects.filter(gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
chrono_list = Chrono.objects.all()
else:
chrono_list = Chrono.objects.filter(
Q(gymnast__last_name=request.user.last_name)
& Q(gymnast__first_name=request.user.first_name)
)
context = {"chrono_list": chrono_list, "gymnast": gymnast}
return render(request, "chronos/list.html", context)
@login_required
@require_http_methods(["GET"])
def note_listing(request, gymnast_id=None):
"""
Si la personne connectée est un entraîneur, la fonction récupère la liste des notes 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 notes à
lui/elle.
Args:
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
gymnast = None
if request.user.groups.filter(name="trainer").exists():
if gymnast_id:
note_list = Note.objects.filter(gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
note_list = Note.objects.all()
else:
note_list = Note.objects.filter(
Q(gymnast__last_name=request.user.last_name)
& Q(gymnast__first_name=request.user.first_name)
)
context = {"note_list": note_list, "gymnast": gymnast}
return render(request, "notes/list.html", context)
@login_required
@require_http_methods(["GET"])
def note_details(request, note_id):
"""
Récupère toutes les informations d'un note.
Args:
note_id (int) identifiant d'une note
2023-04-25 17:06:14 +02:00
"""
note = get_object_or_404(Note, pk=note_id)
return render(request, "notes/details.html", {"note": note})
@login_required
@require_http_methods(["GET", "POST"])
def note_create_or_update(request, note_id=None, gymnast_id=None):
"""Création ou modification d'un chrono
Args:
note_id (int) identifiant d'une note
gymnast_id (int) identifiant d'un gymnaste
TODO: pq ne puis-je pas récuperer l'idantifiant du coach via le form ?
2023-04-25 17:06:14 +02:00
"""
coach = User.objects.get(pk=request.user.id)
if note_id:
note = get_object_or_404(Note, pk=note_id)
data = {
"coach": coach.id,
"gymnast": note.gymnast.id,
"gymnast_related": str(note.gymnast),
}
else:
note = None
2023-11-14 14:17:55 +01:00
today = pendulum.now().date()
season, week_number = from_date_to_week_number(today)
2023-11-14 14:19:15 +01:00
data = {
"coach": coach.id,
"title": f"Note of the week {week_number} ({season})",
}
2023-04-25 17:06:14 +02:00
if gymnast_id is not None:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
data["gymnast"] = gymnast_id
data["gymnast_related"] = str(gymnast)
if request.method == "POST":
form = NoteForm(request.POST, instance=note)
if form.is_valid():
new_note = form.save()
2023-07-05 09:33:03 +02:00
if (
(new_note.gymnast.user.email or new_note.gymnast.email_trainer)
and ((not note_id) or (note_id and note.status == 0))
and new_note.status == 1
):
url = request.build_absolute_uri(
reverse("gymnast_details_tab", args=(new_note.gymnast.id, "event"))
)
2024-02-03 18:19:39 +01:00
receivers = [
new_note.gymnast.user.email,
new_note.gymnast.email_trainer,
]
title = f"{new_note.gymnast} : Nouvelle note"
body = f"""<p>Bonjour,</p><p>Une nouvelle note vous a été envoyée. Vous pouvez la consulter en cliquant <a href='{url}'>ici</a>.</p>"""
2023-07-05 09:33:03 +02:00
send_mail(
2024-02-03 18:19:39 +01:00
title,
2023-07-05 09:33:03 +02:00
"Une nouvelle note vous a été envoyée",
settings.EMAIL_HOST_USER,
2024-02-03 18:19:39 +01:00
receivers,
2023-07-05 09:33:03 +02:00
fail_silently=False,
2024-02-03 18:19:39 +01:00
html_message=body + MAIL_FOOTER,
2023-07-05 09:33:03 +02:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
2024-02-03 19:56:07 +01:00
reverse("gymnast_details_tab", args=(new_note.gymnast.id, "event"))
2023-04-25 17:06:14 +02:00
)
2024-01-14 14:48:31 +01:00
return render(request, "notes/create.html", {"form": form})
last_note = (
Note.objects.filter(gymnast=gymnast_id, status=1).order_by("-date").first()
)
if last_note:
data["informations"] = last_note.informations
2023-04-25 17:06:14 +02:00
form = NoteForm(instance=note, initial=data)
context = {"form": form, "note_id": note_id}
return render(request, "notes/create.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
"""
2023-04-25 17:06:14 +02:00
if chrono_id:
chrono = get_object_or_404(Chrono, pk=chrono_id)
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
2024-02-03 17:20:42 +01:00
receivers = []
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="chrono")
2024-02-03 17:20:42 +01:00
for notification in new_chrono.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{new_chrono.gymnast} : Nouveau chrono"
2024-02-04 14:12:02 +01:00
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>"""
2024-02-03 17:20:42 +01:00
Email.objects.create(
receivers=receivers,
title=title,
2024-02-04 14:12:02 +01:00
body=body,
2023-04-29 15:31:14 +02:00
)
2023-04-25 17:06:14 +02:00
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"{new_chrono.gymnast} a enregistrer un nouveau chrono ({date})",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-03 17:39:12 +01:00
)
2024-02-03 17:20:42 +01:00
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse("gymnast_details_tab", args=(new_chrono.gymnast.id, "scores"))
)
2024-01-14 14:48:31 +01:00
return render(request, "chronos/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = ChronoForm(instance=chrono, initial=data)
context = {"form": form, "chrono_id": chrono_id}
return render(request, "chronos/create.html", context)
@login_required
@require_http_methods(["POST"])
def gymnast_learn_skill(request):
"""
Lie un gymnast à une figure.
"""
# utiliser un FORM pour cette fonction.
gymnast_id = request.POST.get("gymnast_id", None)
skill_id = request.POST.get("skill_id", None)
learning_step = request.POST.get("learning_step", 0)
if gymnast_id and skill_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
skill = Skill.objects.get(pk=skill_id)
learned_skill = LearnedSkill(
gymnast=gymnast,
skill=skill,
learning_step=learning_step,
date=datetime.now(),
)
learned_skill.save()
return HttpResponse(status=200)
if gymnast_id:
print("Error : can not link Gymnast and skill. Missing Skill_ID.")
else:
print("Error : can not link Gymnast and skill. Missing Gymnast_ID.")
return HttpResponse(status=500)
@login_required
@require_http_methods(["GET", "POST"])
def learnedskill_create_or_update(request, gymnast_id=None):
"""
Formulaire de creation et modification d'un lien skill/gymnaste.
Args:
gymnast_id (int) identifiant d'un gymnaste
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),
}
else:
data = None
if request.method == "POST":
form = LearnedSkillForm(request.POST)
if form.is_valid():
2024-02-03 17:20:42 +01:00
learned_skill = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="learnedskill")
for notification in gymnast.notifications.filter(
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{learned_skill.gymnast} : Nouveau skill appris"
html_message = f"""<p>Bonjour,</p><p>{learned_skill.gymnast} a appris {learned_skill.skill} ({learned_skill.learning_step}).</p>"""
Email.objects.create(
receivers=receivers,
title=title,
body=html_message,
2023-04-29 15:31:14 +02:00
)
2023-04-25 17:06:14 +02:00
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"{learned_skill.gymnast} a appris une nouvelle figure ({date})",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
html_message=html_message + MAIL_FOOTER,
)
2024-02-03 17:20:42 +01:00
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse("gymnast_details", args=(form.cleaned_data["gymnast"].id,))
)
2024-01-14 14:48:31 +01:00
2024-02-03 17:20:42 +01:00
return render(request, "learnedskills/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = LearnedSkillForm(initial=data)
context = {"form": form, "gymnast_id": gymnast_id}
return render(request, "learnedskills/create.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def score_create_or_update(request, score_id=None, gymnast_id=None):
"""
Formulaire de création d'un nouveau score.
Args:
score_id (int) identifiant d'un score
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
if score_id:
score = get_object_or_404(Point, pk=score_id)
data = {
"gymnast_related": str(score.gymnast),
"event_related": str(score.event),
}
else:
score = 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 = ScoreForm(request.POST, instance=score)
if form.is_valid():
2024-02-03 17:20:42 +01:00
score = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="point")
2024-02-03 17:20:42 +01:00
for notification in score.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{score.gymnast} : Nouveau score enregistré"
html_message = f"""<p>Bonjour,</p><p>Un nouveau score a été enregistré pour {score.gymnast} ({score.event}) : {score.routine_type} - {score.total}.</p>"""
Email.objects.create(
receivers=receivers,
title=title,
body=html_message,
2023-04-29 15:31:14 +02:00
)
2023-04-25 17:06:14 +02:00
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"{score.gymnast} a ajouté un nouveau score ({date})",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
html_message=html_message + MAIL_FOOTER,
)
2024-02-03 17:20:42 +01:00
2023-04-25 17:06:14 +02:00
if form.cleaned_data["add_to_chrono"]:
new_chrono = Chrono(
gymnast=form.cleaned_data["gymnast"],
chrono_type=form.cleaned_data["routine_type"],
score_type=1,
score=form.cleaned_data["point_time_of_flight"],
tof=form.cleaned_data["point_time_of_flight"],
date=form.cleaned_data["event"].date_begin,
)
new_chrono.save()
return HttpResponseRedirect(
reverse(
"gymnast_details_tab",
args=(form.cleaned_data["gymnast"].id, "scores"),
)
)
2024-01-14 14:48:31 +01:00
2024-02-03 17:20:42 +01:00
return render(request, "scores/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = ScoreForm(instance=score, initial=data)
context = {"form": form, "score_id": score_id}
return render(request, "scores/create.html", context)
@login_required
@require_http_methods(["GET"])
def score_listing(request, gymnast_id=None):
"""
Revoie la liste des scores
Args:
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
pattern = request.GET.get("pattern", None)
gymnast = None
if pattern:
score_list = Point.objects.filter(
Q(event__icontains=pattern) | Q(gymnast__icontains=pattern)
)
elif gymnast_id:
score_list = Point.objects.filter(gymnast=gymnast_id)
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
else:
score_list = Point.objects.all()
context = {"score_list": score_list, "gymnast": gymnast}
return render(request, "scores/list.html", context)
@login_required
@require_http_methods(["GET"])
2023-07-06 23:37:07 +02:00
def injuries_listing(request):
2023-04-25 17:06:14 +02:00
"""
2023-07-06 23:37:07 +02:00
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.
2023-04-25 17:06:14 +02:00
Si c'est un autre utilisateur (entraîneur), la liste peut répondre à un pattern si celui-ci est
définit.
"""
if request.user.groups.filter(name="trainer").exists():
pattern = request.GET.get("pattern", None)
if pattern:
2023-07-06 23:37:07 +02:00
injuries_list = Injury.objects.filter(
2023-04-25 17:06:14 +02:00
Q(gymnast__last_name__icontains=pattern)
| Q(gymnast__first_name__icontains=pattern)
)
else:
2023-07-06 23:37:07 +02:00
injuries_list = Injury.objects.all()
2023-04-25 17:06:14 +02:00
else:
2023-07-06 23:37:07 +02:00
injuries_list = Injury.objects.filter(
2023-04-25 17:06:14 +02:00
Q(gymnast__last_name=request.user.last_name)
& Q(gymnast__first_name=request.user.first_name)
)
2023-07-06 23:37:07 +02:00
context = {"injuries_list": injuries_list}
return render(request, "injuries/list.html", context)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET", "POST"])
2023-07-06 23:37:07 +02:00
def injury_create_or_update(request, injury_id=None, gymnast_id=None):
2023-04-25 17:06:14 +02:00
"""
2023-07-06 23:37:07 +02:00
Formulaire de création d'un nouvel blessure.
Args:
2023-07-06 23:37:07 +02:00
injury_id (int) identifiant d'une blessure
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
2023-07-06 23:37:07 +02:00
if injury_id:
injury = get_object_or_404(Injury, pk=injury_id)
2023-04-25 17:06:14 +02:00
data = {
2023-07-06 23:37:07 +02:00
"gymnast_related": injury.gymnast,
"skill_related": injury.skill,
2023-04-25 17:06:14 +02:00
}
else:
2023-07-06 23:37:07 +02:00
injury = None
2023-04-25 17:06:14 +02:00
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":
2023-07-06 23:37:07 +02:00
form = InjuryForm(request.POST, instance=injury)
2023-04-25 17:06:14 +02:00
if form.is_valid():
2023-07-06 23:37:07 +02:00
injury = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-11-18 18:29:18 +01:00
date = form.cleaned_data["date"]
2023-07-06 23:37:07 +02:00
functionality = ContentType.objects.get(model="injury")
2024-02-03 17:20:42 +01:00
for notification in injury.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
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)
2024-02-03 17:39:12 +01:00
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,
)
2023-04-25 17:06:14 +02:00
2023-07-06 23:37:07 +02:00
return HttpResponseRedirect(reverse("injury_details", args=(injury.pk,)))
2024-01-14 14:48:31 +01:00
return render(request, "injuries/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
2023-07-06 23:37:07 +02:00
form = InjuryForm(instance=injury, initial=data)
context = {"form": form, "injury_id": injury_id}
return render(request, "injuries/create.html", context)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET"])
2023-07-06 23:37:07 +02:00
def injury_detail(request, injury_id):
2023-04-25 17:06:14 +02:00
"""
2023-07-06 23:37:07 +02:00
Récupère toutes les informations d'une blessure.
Args:
2023-07-06 23:37:07 +02:00
injury_id (int) identifiant d'une blessure
2023-04-25 17:06:14 +02:00
"""
2023-07-06 23:37:07 +02:00
injury = get_object_or_404(Injury, pk=injury_id)
return render(request, "injuries/details.html", {"injury": injury})
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET"])
2023-07-05 09:33:03 +02:00
def wellbeing_listing(request, gymnast_id=None):
2023-04-25 17:06:14 +02:00
"""
Récupère la liste des évaluations mentales suivant (d'un gymnaste si définit en paramètre).
Args:
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
gymnast = None
if gymnast_id:
2023-07-05 10:51:49 +02:00
wellbeing_list = WellBeing.objects.filter(gymnast=gymnast_id)
2023-04-25 17:06:14 +02:00
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
else:
2023-07-05 10:51:49 +02:00
wellbeing_list = WellBeing.objects.all()
2023-04-25 17:06:14 +02:00
2023-07-05 09:33:03 +02:00
context = {"wellbeing_list": wellbeing_list, "gymnast": gymnast}
return render(request, "wellbeing/list.html", context)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET", "POST"])
2023-07-05 09:33:03 +02:00
def wellbeing_create_or_update(
request, wellbeing_id=None, gymnast_id=None, event_id=None
2023-04-25 17:06:14 +02:00
):
"""
2023-07-06 23:37:07 +02:00
Formulaire de création d'une nouvelle blessure.
Args:
2023-07-06 23:37:07 +02:00
wellbeing_id (int) identifiant d'une blessure
gymnast_id (int) identifiant d'un gymnaste
event_id (int) identifiant d'un événement
2023-04-25 17:06:14 +02:00
"""
2023-07-05 09:33:03 +02:00
if wellbeing_id:
2023-07-05 10:51:49 +02:00
wellbeing = get_object_or_404(WellBeing, pk=wellbeing_id)
2023-07-05 09:33:03 +02:00
data = {"gymnast_related": wellbeing.gymnast, "event_related": wellbeing.event}
2023-04-25 17:06:14 +02:00
else:
2023-07-05 09:33:03 +02:00
wellbeing = None
2023-04-25 17:06:14 +02:00
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":
2023-07-05 09:33:03 +02:00
form = WellBeingForm(request.POST, instance=wellbeing)
2023-04-25 17:06:14 +02:00
if form.is_valid():
2023-07-05 09:33:03 +02:00
wellbeing = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-05-11 10:51:10 +02:00
date = form.cleaned_data["date"]
2023-07-05 10:51:49 +02:00
functionality = ContentType.objects.get(model="wellbeing")
2024-02-03 17:20:42 +01:00
for notification in wellbeing.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
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>
2023-07-05 09:33:03 +02:00
<ul>
2024-02-03 17:20:42 +01:00
<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>
2023-10-11 12:12:01 +02:00
</ul>
2024-02-03 17:20:42 +01:00
{wellbeing.details}"""
Email.objects.create(receivers=receivers, title=title, body=html_message)
2024-02-03 17:39:12 +01:00
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,
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
2023-07-05 09:33:03 +02:00
reverse("wellbeing_details", args=(wellbeing.pk,))
2023-04-25 17:06:14 +02:00
)
2024-01-14 14:48:31 +01:00
return render(request, "wellbeing/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
2023-07-05 09:33:03 +02:00
form = WellBeingForm(instance=wellbeing, initial=data)
context = {"form": form, "wellbeing_id": wellbeing_id}
return render(request, "wellbeing/create.html", context)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET"])
2023-07-05 09:33:03 +02:00
def wellbeing_detail(request, wellbeing_id):
2023-04-25 17:06:14 +02:00
"""
Récupère toutes les informations d'une évaluation psychologique.
Args:
2023-07-05 09:33:03 +02:00
wellbeing_id (int) identifiant d'une évaluation psycho
2023-04-25 17:06:14 +02:00
"""
2023-07-05 10:51:49 +02:00
wellbeing = get_object_or_404(WellBeing, pk=wellbeing_id)
2023-07-05 09:33:03 +02:00
return render(request, "wellbeing/details.html", {"wellbeing": wellbeing})
2023-04-25 17:06:14 +02:00
@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
2023-04-25 17:06:14 +02:00
"""
gymnast = None
if gymnast_id:
heightweight_list = HeightWeight.objects.filter(gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
heightweight_list = HeightWeight.objects.all()
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
2023-04-25 17:06:14 +02:00
"""
if heightweight_id:
heightweight = get_object_or_404(HeightWeight, pk=heightweight_id)
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():
2024-02-03 17:20:42 +01:00
heightweight = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-11-14 12:15:09 +01:00
date = form.cleaned_data["date"]
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="heightweight")
2024-02-03 17:20:42 +01:00
for notification in heightweight.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
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>
2023-11-14 12:15:09 +01:00
<ul>
2024-02-03 17:20:42 +01:00
<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)
2024-02-03 17:39:12 +01:00
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,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-03 17:39:12 +01:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse(
"gymnast_details_tab",
args=(form.cleaned_data["gymnast"].id, "physiological"),
)
)
2024-01-14 14:48:31 +01:00
return render(request, "heightweight/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = HeightWeightForm(instance=heightweight, initial=data)
context = {
"form": form,
"gymnast_id": gymnast_id,
"heightweight_id": heightweight_id,
}
return render(request, "heightweight/create.html", context)
@login_required
@require_http_methods(["GET"])
def routine_done_listing(request, gymnast_id=None):
"""
Liste tous les record de la table NumberOfRoutineDone
Args:
gymnast_id (int) identifiant d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
if gymnast_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
routine_done_list = gymnast.number_of_routine_done.all()
else:
gymnast = None
routine_done_list = NumberOfRoutineDone.objects.all()
context = {"routine_done_list": routine_done_list, "gymnast": gymnast}
return render(request, "routinedone/list.html", context)
@require_http_methods(["POST"])
def increment_routinedone(request):
"""
Incrémente le nombre de routine faite pour aujourd'hui et incrémente, si besoin, le nombre
de routine réussie
"""
data = {
"gymnast": get_object_or_404(Gymnast, pk=request.POST.get("gymnast_id", None)),
"routine_type": request.POST.get("routine_type", 1),
"date": request.POST.get("date", date.today()),
}
routine = (
data["gymnast"]
.has_routine.filter(routine_type=request.POST.get("routine_type", 1))
.first()
)
data["routine"] = routine.routine
routinedone, _ = NumberOfRoutineDone.objects.get_or_create(
date=data["date"], gymnast=data["gymnast"], routine_type=data["routine_type"]
)
data["number_of_try"] = routinedone.number_of_try + 1
success = request.POST.get("success", 0)
if int(success) == 1:
data["number_of_successes"] = routinedone.number_of_successes + 1
else:
data["number_of_successes"] = routinedone.number_of_successes
form = NumberOfRoutineDoneForm(data, instance=routinedone)
if form.is_valid():
form.save()
return HttpResponse(status=200)
2024-01-14 14:48:31 +01:00
return HttpResponse(status=406)
2023-04-25 17:06:14 +02:00
@login_required
@require_http_methods(["GET", "POST"])
def routinedone_create_or_update(request, routinedone_id=None, gymnast_id=None):
"""Création ou modification d'un nombre de série tentée.
Args:
routinedone_id (int) identifiant d'une routinedone
gymnast_id (int) identifiant d'un gymnaste
"""
2023-04-25 17:06:14 +02:00
if routinedone_id:
routinedone = get_object_or_404(NumberOfRoutineDone, pk=routinedone_id)
data = {
"gymnast": routinedone.gymnast.id,
"gymnast_related": str(routinedone.gymnast),
}
if routinedone.routine:
data["routine"] = routinedone.routine.id
data["routine_related"] = str(routinedone.routine)
else:
routine = (
GymnastHasRoutine.objects.filter(gymnast=routinedone.gymnast)
.filter(routine_type=routinedone.routine_type)
.filter(date_begin__lte=routinedone.date)
.filter(Q(date_end__gte=routinedone.date) | Q(date_end__isnull=True))
.first()
)
if routine:
data["routine"] = routine.id
data["routine_related"] = str(routine)
else:
data = None
routinedone = 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 = NumberOfRoutineDoneForm(request.POST, instance=routinedone)
if form.is_valid():
2024-02-03 17:20:42 +01:00
routine_done = form.save()
2023-04-25 17:06:14 +02:00
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-04-25 17:06:14 +02:00
gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id)
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="numberofroutinedone")
2024-02-03 17:20:42 +01:00
for notification in routine_done.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{routine_done.gymnast} : Nouvelle série comptabilisée"
body = f"""<p>Bonjour,</p>
<p>Nouvelle série comptabilisée pour {routine_done.gymnast}.</p>"""
Email.objects.create(receivers=receivers, title=title, body=body)
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"Nouvelle série comptabilisée pour {routine_done.gymnast}",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-03 17:39:12 +01:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse(
"gymnast_details_tab",
args=(form.cleaned_data["gymnast"].id, "routine"),
)
)
2024-01-14 14:48:31 +01:00
return render(request, "routinedone/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = NumberOfRoutineDoneForm(instance=routinedone, initial=data)
context = {"form": form, "routinedone_id": routinedone_id}
return render(request, "routinedone/create.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def plan_create_or_update(request, plan_id=None, gymnast_id=None, skill_id=None):
"""Création d'un plan.
Args:
plan_id (int) identifiant d'un plan (classe <Plan>).
gymnast_id (int) identifiant d'un gymnaste (classe <Gymnast>).
skill_id (int) identifiant d'un skill (classe <Skill>).
2023-04-25 17:06:14 +02:00
"""
if plan_id:
plan = get_object_or_404(Plan, pk=plan_id)
data = {
"gymnast": plan.gymnast.id,
"gymnast_related": str(plan.gymnast),
"educative": plan.educative.id,
"educative_related": str(plan.educative),
}
else:
plan = None
data = {}
if gymnast_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
data["gymnast"] = gymnast_id
data["gymnast_related"] = str(gymnast)
if skill_id:
skill = get_object_or_404(Skill, pk=skill_id)
data["educative"] = skill_id
data["educative_related"] = str(skill)
if request.method == "POST":
form = PlanForm(request.POST, instance=plan)
if form.is_valid():
plan = form.save()
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-06-13 14:35:29 +02:00
date = form.cleaned_data["date"]
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="plan")
2024-02-03 17:20:42 +01:00
for notification in plan.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{plan.gymnast} : Nouvel objectif fixé"
body = f"""<p>Bonjour,</p>
<p>Nouvel objectif fixé pour {plan.gymnast} : {plan.educative} ({plan.learning_step}) pour le {date.strftime('%d %B %Y')} au plus tard.</p>"""
Email.objects.create(receivers=receivers, title=title, body=body)
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"Nouvel objectif fixé pour {gymnast}",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-03 17:39:12 +01:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse("gymnast_details", args=(form.cleaned_data["gymnast"].id,))
)
2024-01-14 14:48:31 +01:00
return render(request, "plan/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = PlanForm(instance=plan, initial=data)
context = {"form": form, "plan_id": plan_id}
return render(request, "plan/create.html", context)
2023-11-12 19:22:52 +01:00
@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)
return render(request, "intensities/details.html", {"intensity": intensity})
2023-04-25 17:06:14 +02:00
@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 à
lui/elle.
Args:
gymnast_id (int) identifiant d'un gymnaste
"""
gymnast = None
if request.user.groups.filter(name="trainer").exists():
if gymnast_id:
intensity_list = Intensity.objects.filter(gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
intensity_list = Intensity.objects.all()
else:
intensity_list = Intensity.objects.filter(
Q(gymnast__last_name=request.user.last_name)
& Q(gymnast__first_name=request.user.first_name)
)
context = {"intensity_list": intensity_list, "gymnast": gymnast}
return render(request, "intensities/list.html", context)
@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:
2023-05-15 13:58:02 +02:00
intensity_id (int) identifiant d'une intensité (classe <Intensity>).
gymnast_id (int) identifiant d'un gymnaste (classe <Gymnast>).
2023-04-25 17:06:14 +02:00
"""
if intensity_id:
intensity = get_object_or_404(Intensity, pk=intensity_id)
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
2024-02-03 17:20:42 +01:00
receivers = []
2023-05-15 13:58:02 +02:00
date = form.cleaned_data["date"]
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="intensity")
2024-02-03 17:20:42 +01:00
for notification in intensity.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
2024-02-03 18:51:57 +01:00
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.mean_quantity_of_skill_by_time:.2f}min</li>
<li>Skill/passe: {intensity.mean_quantity_of_skill_by_passe:.2f}</li>
<li>Diff/passe: {intensity.mean_difficulty_by_passe:.2f}</li>
<li>Diff/skill: {intensity.mean_difficulty_by_skill:.2f}</li>
</ul>"""
2024-02-03 17:20:42 +01:00
Email.objects.create(
receivers=receivers,
2024-02-03 18:51:57 +01:00
title=title,
body=body,
)
2024-02-04 07:45:45 +01:00
send_mail(
title,
f"Une nouvelle information de saison enregistrée pour {intensity.gymnast}",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-04 07:45:45 +01:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse(
2024-02-03 19:56:07 +01:00
"intensity_details",
args=(intensity.id,),
2023-04-25 17:06:14 +02:00
)
)
2024-01-14 14:48:31 +01:00
return render(request, "intensities/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = IntensityForm(instance=intensity, initial=data)
context = {"form": form, "intensity_id": intensity_id}
return render(request, "intensities/create.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def season_information_create_or_update(
request, season_information_id=None, gymnast_id=None
):
"""Création d'un record de la class Intentity.
Args:
season_information_id (int): identifiant d'un plan (classe <SeasonInformation>).
gymnast_id (int): identifiant d'un gymnaste (classe <Gymnast>).
"""
if season_information_id:
season_information = get_object_or_404(
SeasonInformation, pk=season_information_id
)
data = {
"gymnast": season_information.gymnast.id,
"gymnast_related": str(season_information.gymnast),
"club": season_information.club.id,
"club_related": str(season_information.club),
}
else:
season_information = 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 = SeasonInformationForm(request.POST, instance=season_information)
if form.is_valid():
season_information = form.save()
# notification
2024-02-03 17:20:42 +01:00
receivers = []
2023-04-28 12:20:55 +02:00
functionality = ContentType.objects.get(model="seasoninformation")
2024-02-03 17:20:42 +01:00
for notification in season_information.gymnast.notifications.filter(
2023-04-28 12:20:55 +02:00
functionality=functionality
):
2024-02-03 17:20:42 +01:00
receivers.append(notification.user.email)
title = f"{season_information.gymnast} : Nouvelle information de saison"
body = f"""<p>Bonjour,</p>
<p>Une nouvelle information de saison enregistrée pour {season_information.gymnast}.</p>"""
2024-02-03 17:20:42 +01:00
Email.objects.create(receivers=receivers, title=title, body=body)
2024-02-03 17:39:12 +01:00
send_mail(
title,
f"Une nouvelle information de saison enregistrée pour {gymnast}",
settings.EMAIL_HOST_USER,
receivers,
fail_silently=False,
2024-02-04 14:12:02 +01:00
html_message=body + MAIL_FOOTER,
2024-02-03 17:39:12 +01:00
)
2023-04-25 17:06:14 +02:00
return HttpResponseRedirect(
reverse(
"gymnast_details",
args=(form.cleaned_data["gymnast"].id,),
)
)
2024-01-14 14:48:31 +01:00
return render(request, "seasoninformations/create.html", {"form": form})
2023-04-25 17:06:14 +02:00
form = SeasonInformationForm(instance=season_information, initial=data)
context = {"form": form, "season_information_id": season_information_id}
return render(request, "seasoninformations/create.html", context)
@login_required
@require_http_methods(["GET"])
def season_information_listing(request, gymnast_id=None):
"""Liste toutes les informations de saison pour un gymnaste.
Args:
gymnast_id (int) identifiant d'un gymnaste
"""
2023-04-25 17:06:14 +02:00
if gymnast_id is not None:
gymnast = Gymnast.objects.get(pk=gymnast_id)
season_information_list = SeasonInformation.objects.filter(gymnast=gymnast_id)
else:
season_information_list = SeasonInformation.objects.all()
context = {"season_information_list": season_information_list, "gymnast": gymnast}
return render(request, "seasoninformations/list.html", context)