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, ) 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 datetime import date, timedelta from statistics import mean from jarvis.followup.models import Event from jarvis.followup.forms import GymnastHasRoutineForm from jarvis.followup.models import ( Note, Plan, Skill, Point, Chrono, Accident, MindState, 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 .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) > 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): """ Si la personne connectée est un entraîneur : liste tous les gymnasts 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) else: 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. """ 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): """ """ context = { "gymnast_id": gymnast_id, } return render(request, "gymnasts/tabs/tab_documents.html", context) @login_required @require_http_methods(["GET"]) def gymnast_report_evaluation(request, gymnast_id): """ """ 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_evaluation.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("people/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 gymnast_display_events_and_notes(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, 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[:5] 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_accident(request, gymnast_id): """ Renvoie la liste des accidents d'un gymnaste. """ accident_list = Accident.objects.filter(gymnast=gymnast_id) context = {"accident_list": accident_list, "gymnast_id": gymnast_id} return render(request, "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, "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. """ 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_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, "gymnasts/list_mindstate.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. """ 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): """ """ 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="""

Bonjour,

Une nouvelle série vous a été associée.


Excellente journée

Jarvis

""", ) 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 creation et modification d'un gymnaste. """ 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,))) else: 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 """ 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 = list() skill_whith_help = list() skill_without_help = list() skill_chained = list() skill_masterised = list() 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 value (value) par rapport à la moyenne de value_list et à la dernière valeur de value_list. Args: value float valeur value_list array 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 __get_distinct_followup_season_for_gymnast(gymnast_id): """Recupère les saisons pour lesquelles le gymnastes à des followup.""" 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.accident.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.mindstate.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.""" 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.accident.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.mindstate.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): """ """ 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.""" 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) @login_required @require_http_methods(["GET"]) def generate_week_report(request, gymnast_id, season=None, week_number=None): 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() # # PHYSIOLOGICAL INFORMATIONS # # Mindstate Score mindstate_week_score = ( gymnast.mindstate.filter(season=season) .filter(week_number=week_number) .aggregate(mean_mindstate_value=Avg("score")) ) mindstate_queryset = MindState.objects.filter(gymnast=gymnast).order_by("-date") have_physiological = False if mindstate_week_score["mean_mindstate_value"]: lasts_mindstate = list(mindstate_queryset.values_list("score", flat=True)[1:6]) mindstate_analyse = analyse_score( mindstate_week_score["mean_mindstate_value"], lasts_mindstate ) else: mindstate_analyse = None height_weight_week_value = ( gymnast.height_weight.filter(season=season) .filter(week_number=week_number) .aggregate(mean_height_value=Avg("height"), mean_weight_value=Avg("weight")) ) height_weight_queryset = HeightWeight.objects.filter(gymnast=gymnast).order_by( "-date" ) if height_weight_week_value["mean_height_value"]: lasts_height = list( height_weight_queryset.values_list("height", flat=True)[1:6] ) height_analyse = analyse_score( height_weight_week_value["mean_height_value"], lasts_height ) else: height_analyse = None if height_weight_week_value["mean_weight_value"]: lasts_weight = list( height_weight_queryset.values_list("weight", flat=True)[1:6] ) weight_analyse = analyse_score( height_weight_week_value["mean_weight_value"], lasts_weight ) else: weight_analyse = None intensity_week_value = ( gymnast.intensities.filter(season=season) .filter(week_number=week_number) .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"), ) ) # # BEST TOF # best_tof_straightjump = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=0) .order_by("-score") .first() ) best_tof_routine_1 = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=1) .order_by("-score") .first() ) best_tof_routine_2 = ( Chrono.objects.filter(gymnast=gymnast) .filter(chrono_type=2) .order_by("-score") .first() ) # # BEST SCORES # best_point_routine_1 = ( Point.objects.filter(gymnast=gymnast) .filter(routine_type=1) .order_by("-total") .first() ) best_point_routine_2 = ( Point.objects.filter(gymnast=gymnast) .filter(routine_type=2) .order_by("-total") .first() ) best_point_routine_3 = ( Point.objects.filter(gymnast=gymnast) .filter(routine_type=3) .order_by("-total") .first() ) best_point_routine_4 = ( Point.objects.filter(gymnast=gymnast) .filter(routine_type=4) .order_by("-total") .first() ) best_point_routine_5 = ( Point.objects.filter(gymnast=gymnast) .filter(routine_type=5) .order_by("-total") .first() ) # # ROUTINES # routine_1 = ( 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) .filter(season=season) .filter(week_number=week_number) .aggregate( total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") ) ) routine_2 = ( 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) .filter(season=season) .filter(week_number=week_number) .aggregate( total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") ) ) routine_3 = ( 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) .filter(season=season) .filter(week_number=week_number) .aggregate( total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") ) ) routine_4 = ( 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) .filter(season=season) .filter(week_number=week_number) .aggregate( total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") ) ) routine_5 = ( 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) .filter(season=season) .filter(week_number=week_number) .aggregate( total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") ) ) # # LAST LEARNED SKILLS # # TODO: il faudrait que ce soit classer d'abord par DATE et pas par skill. # # learned_skills = ( # LearnedSkill.objects.filter(gymnast=gymnast.id) # .annotate(skill_notation=F("skill__notation")) # .order_by("skill_notation", "-date") # .distinct("skill_notation")[:6] # ) learned_skills = LearnedSkill.objects.filter(gymnast=gymnast.id).order_by("-date")[ :6 ] # # 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("educative", "-date") .distinct()[:6] ) # # NEXT EVENTS # next_event_list = Event.objects.filter( gymnasts=gymnast.id, 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=begin_of_the_week) .filter(status=1) .order_by("date") ) 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, "last_mindstate": mindstate_week_score["mean_mindstate_value"], "mindstate_analyse": mindstate_analyse, "last_height": height_weight_week_value["mean_height_value"], "last_weigth": height_weight_week_value["mean_weight_value"], "height_analyse": height_analyse, "weight_analyse": weight_analyse, "best_tof_straightjump": best_tof_straightjump, "best_tof_routine_1": best_tof_routine_1, "best_tof_routine_2": best_tof_routine_2, "best_point_routine_1": best_point_routine_1, "best_point_routine_2": best_point_routine_2, "best_point_routine_3": best_point_routine_3, "best_point_routine_4": best_point_routine_4, "best_point_routine_5": best_point_routine_5, "routine_1": routine_1, "routine_2": routine_2, "routine_3": routine_3, "routine_4": routine_4, "routine_5": routine_5, "routine_1_done_stat": routine_1_done_stat, "routine_2_done_stat": routine_2_done_stat, "routine_3_done_stat": routine_3_done_stat, "routine_4_done_stat": routine_4_done_stat, "routine_5_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_week.html", context) response = HttpResponse(content_type="application/pdf") response[ "Content-Disposition" ] = "attachment; filename={lastname}-{firstname}-report.pdf".format( lastname=gymnast.last_name, firstname=gymnast.first_name, ) html = render_to_string("people/gymnasts/reports/report_week.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_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) - accident - GymnastHasRoutine eton les trie par 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.accident.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