Jarvis/jarvis/followup/models.py

929 lines
33 KiB
Python
Raw Normal View History

2024-01-14 14:48:31 +01:00
from datetime import date
2023-04-25 17:06:14 +02:00
from django.db import models
from django.contrib.auth import get_user_model
2023-07-05 09:33:03 +02:00
from django.core.validators import MaxValueValidator, MinValueValidator
2023-04-25 17:06:14 +02:00
2024-03-06 15:13:09 +01:00
from jarvis.core.global_vars import (
INJURY_MECHANISM_CHOICE,
INJURY_BODY_SIDE_CHOICE,
INJURY_TYPE_CHOICE,
INJURY_LOCATION_CHOICE,
ROUTINE_TYPE_CHOICE,
LEARNING_STEP_CHOICES,
CHRONO_TYPE_CHOICE,
SCORE_TYPE_CHOICE,
CATEGORY_CHOICES,
AGE_CATOGORY_CHOICES,
NOTE_STATUS_CHOICES,
)
2023-04-25 17:06:14 +02:00
from jarvis.tools.models import Markdownizable, Seasonisable
from jarvis.people.models import Gymnast, GENDER_CHOICES
from jarvis.planning.models import Event
from jarvis.objective.models import Educative, Skill, Routine
from jarvis.location.models import Club
User = get_user_model()
class Chrono(Seasonisable):
"""
Représente les chronos (de chandelles ou de série) enregistrés pour un(e) gymnaste.
"""
class Meta:
verbose_name = "Chrono"
verbose_name_plural = "Chronos"
ordering = ["date", "gymnast"]
gymnast = models.ForeignKey(
Gymnast,
verbose_name="gymnast",
related_name="chronos",
on_delete=models.CASCADE,
)
chrono_type = models.PositiveSmallIntegerField(
choices=CHRONO_TYPE_CHOICE, verbose_name="Routine type"
)
score_type = models.PositiveSmallIntegerField(
choices=SCORE_TYPE_CHOICE, verbose_name="Score type"
)
score = models.DecimalField(max_digits=5, decimal_places=3)
tof = models.DecimalField(max_digits=5, decimal_places=3, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
2024-01-21 13:53:57 +01:00
return f"{self.gymnast} - {self.tof} ({self.date} - {self.chrono_type})"
2023-04-25 17:06:14 +02:00
def timeline_representation(self):
2024-01-14 14:48:31 +01:00
return f"<li>{self.date:%d %b %Y} - New personel best {CHRONO_TYPE_CHOICE[self.chrono_type][1]}: {self.score}' ({self.tof}')</li>" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
@staticmethod
def compute_tof(value):
tof = round((value * 13) / 15, 3) * 1000
return tof / 1000
class ChronoDetails(models.Model):
"""
Class permettant d'enregistrer des détails d'un chrono : le temps de chaque saut.
"""
class Meta:
verbose_name = "Chronos Details"
verbose_name_plural = "Chronos Details"
ordering = ["chrono", "order"]
unique_together = ["chrono", "order"]
chrono = models.ForeignKey(Chrono, on_delete=models.CASCADE, related_name="details")
order = models.SmallIntegerField()
value = models.DecimalField(max_digits=5, decimal_places=3)
2023-10-11 16:32:09 +02:00
class Injury(Markdownizable, Seasonisable):
"""
La classe `Injury` permet d'indiquer qu'un gymnaste a eu un blessure, en liaison avec un
skill ou non.
"""
class Meta:
verbose_name = "Injury"
verbose_name_plural = "Injuries"
# unique_together = ("gymnast", "skill", "date")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="injuries",
on_delete=models.CASCADE,
)
skill = models.ForeignKey(
"objective.Skill",
verbose_name="Skill",
related_name="injuries",
on_delete=models.SET_NULL,
default=None,
blank=True,
null=True,
)
location = models.SmallIntegerField(
choices=INJURY_LOCATION_CHOICE, verbose_name="Location"
)
injury_type = models.SmallIntegerField(
choices=INJURY_TYPE_CHOICE, verbose_name="Type"
)
body_side = models.PositiveSmallIntegerField(
choices=INJURY_BODY_SIDE_CHOICE, verbose_name="Body side"
)
mechanism = models.PositiveSmallIntegerField(
choices=INJURY_MECHANISM_CHOICE, verbose_name="Mechanism"
)
2023-11-14 13:50:25 +01:00
nb_week_off = models.SmallIntegerField(default=0, verbose_name="# week off")
2023-10-11 16:32:09 +02:00
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.gymnast} ({self.date})"
def timeline_representation(self):
2024-01-14 14:48:31 +01:00
return f"<li>{self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)</li>" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
class LearnedSkill(Seasonisable):
"""
Représente la capacité d'un gymnaste à savori faire un skill de la ligne d'apprentissage.
"""
class Meta:
verbose_name = "Learned Skill"
verbose_name_plural = "Learned Skills"
unique_together = ("gymnast", "skill", "date", "learning_step")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="gymnast",
related_name="known_skills",
on_delete=models.CASCADE,
)
skill = models.ForeignKey(
Skill,
verbose_name="Skill",
related_name="known_by",
on_delete=models.CASCADE,
)
learning_step = models.PositiveSmallIntegerField(
choices=LEARNING_STEP_CHOICES, verbose_name="Can do type"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} - {self.date} - {self.learning_step} - {self.skill}"
2023-04-25 17:06:14 +02:00
def timeline_representation(self):
2024-01-14 14:48:31 +01:00
return f"<li>{self.date:%d %b %Y} - learning of {self.skill.long_label} ({self.skill.short_label}): {LEARNING_STEP_CHOICES[self.learning_step][1]}</li>" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
class Plan(Seasonisable, Markdownizable):
"""
Classe représentant les objectifs qu'un gymnaste devra savoir faire pour une date donnée.
"""
class Meta:
verbose_name = "Plan"
verbose_name_plural = "Plans"
ordering = ["date", "educative", "gymnast"]
unique_together = ("gymnast", "educative")
gymnast = models.ForeignKey(
"people.Gymnast",
verbose_name="Gymnast",
related_name="todo",
on_delete=models.CASCADE,
)
educative = models.ForeignKey(
Educative,
verbose_name="Educative",
related_name="plan",
on_delete=models.CASCADE,
)
learning_step = models.PositiveSmallIntegerField(
choices=LEARNING_STEP_CHOICES, verbose_name="Can do type", default=3
)
is_done = models.BooleanField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} - {self.educative.short_label} - {self.date}"
2023-04-25 17:06:14 +02:00
class Point(models.Model):
"""
Représente les points obtenus lors d'une compétition.
"""
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, default=None, related_name="points"
)
event = models.ForeignKey(Event, on_delete=models.CASCADE, default=None)
routine_type = models.PositiveSmallIntegerField(choices=ROUTINE_TYPE_CHOICE)
point_execution = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="Execution"
)
point_difficulty = models.DecimalField(
max_digits=3, decimal_places=1, verbose_name="Difficulty"
)
point_time_of_flight = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="ToF"
)
point_horizontal_displacement = models.DecimalField(
max_digits=4, decimal_places=3, verbose_name="HD"
)
penality = models.DecimalField(max_digits=3, decimal_places=1)
total = models.DecimalField(max_digits=6, decimal_places=3)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated")
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} - {self.total}"
2023-04-25 17:06:14 +02:00
2023-07-05 10:51:49 +02:00
class WellBeing(Markdownizable, Seasonisable):
2023-04-25 17:06:14 +02:00
"""
2023-07-05 09:33:03 +02:00
Représente l'état psychologique/physique d'un gymnaste
2023-04-25 17:06:14 +02:00
"""
2024-04-11 08:42:03 +02:00
class Meta:
ordering = ["date", ]
2023-04-25 17:06:14 +02:00
gymnast = models.ForeignKey(
2023-07-05 09:33:03 +02:00
Gymnast, on_delete=models.CASCADE, default=None, related_name="wellbeings"
2023-04-25 17:06:14 +02:00
)
event = models.ForeignKey(
Event,
on_delete=models.SET_NULL,
default=None,
blank=True,
null=True,
2023-07-05 09:33:03 +02:00
related_name="wellbeings",
)
2023-07-05 10:51:49 +02:00
mindstate = models.PositiveSmallIntegerField(
2023-07-05 09:33:03 +02:00
verbose_name="Mindstate",
validators=[MinValueValidator(1), MaxValueValidator(10)],
)
sleep = models.PositiveSmallIntegerField(
verbose_name="Sleep",
2023-07-05 10:51:49 +02:00
null=True,
blank=True,
default=None,
2023-07-05 09:33:03 +02:00
validators=[MinValueValidator(1), MaxValueValidator(10)],
)
stress = models.PositiveSmallIntegerField(
verbose_name="Stress",
validators=[MinValueValidator(1), MaxValueValidator(10)],
)
fatigue = models.PositiveSmallIntegerField(
verbose_name="Fatigue",
2023-07-05 10:51:49 +02:00
null=True,
blank=True,
default=None,
2023-07-05 09:33:03 +02:00
validators=[MinValueValidator(1), MaxValueValidator(10)],
)
muscle_soreness = models.PositiveSmallIntegerField(
verbose_name="Muscle Soreness",
2023-07-05 10:51:49 +02:00
null=True,
blank=True,
default=None,
2023-07-05 09:33:03 +02:00
validators=[MinValueValidator(1), MaxValueValidator(10)],
2023-04-25 17:06:14 +02:00
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
2024-01-14 14:48:31 +01:00
return f"{self.gymnast} - {self.date} : {self.mindstate} | {self.sleep} | {self.stress} | {self.fatigue} | {self.muscle_soreness}" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
2023-10-11 12:12:01 +02:00
@property
def get_inversed_stress(self):
return 10 - self.stress
@property
def get_inversed_fatigue(self):
return 10 - self.fatigue
@property
def get_inversed_muscle_soreness(self):
return 10 - self.muscle_soreness
2023-04-25 17:06:14 +02:00
class GymnastHasRoutine(models.Model):
"""Représente le lien entre les gymnastes et leurs séries.
2023-04-25 17:06:14 +02:00
TODO: il y a un champ "date_begin" et un champ "date_end" --> peut hérité de Temporizable
"""
class Meta:
verbose_name = "Gymnast Has Routine"
verbose_name_plural = "Gymnast Has Routines"
# unique_together()
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="has_routine",
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
verbose_name="Routine",
related_name="done_by_gymnast",
on_delete=models.CASCADE,
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
date_begin = models.DateField(default=date.today, verbose_name="Date begin")
date_end = models.DateField(verbose_name="Date end", null=True, blank=True)
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} - {self.routine_type} : {self.routine}"
2023-04-25 17:06:14 +02:00
class NumberOfRoutineDone(Seasonisable):
"""
Classe permettant de suivre le nombre de séries faites par le gymnaste.
"""
class Meta:
verbose_name = "Number of routine done"
verbose_name_plural = "Number of routines done"
unique_together = ("gymnast", "date", "routine_type")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="number_of_routine_done",
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
verbose_name="Routine",
related_name="number_of_try",
on_delete=models.SET_NULL, # TODO: analyser la pertinence de cette option
null=True, # TODO: analyser la pertinence de cette option
blank=True, # TODO: analyser la pertinence de cette option
2023-04-25 17:06:14 +02:00
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
number_of_try = models.PositiveSmallIntegerField(
verbose_name="Number of try", default=0
)
number_of_successes = models.PositiveSmallIntegerField(
verbose_name="number of successes", default=0
)
def __str__(self):
2024-01-14 14:48:31 +01:00
return f"{self.gymnast} - {self.routine_type} ({self.routine}) : {self.number_of_try} | {self.number_of_successes}" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
class HeightWeight(Seasonisable):
"""
Classe permettant de suivre le poids et la taille d'un gymnaste
"""
class Meta:
verbose_name = "Height & weight"
verbose_name_plural = "Heights & weights"
unique_together = ("gymnast", "date")
2024-04-10 12:15:20 +02:00
ordering = ["date",]
2023-04-25 17:06:14 +02:00
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="height_weight",
on_delete=models.CASCADE,
)
height = models.DecimalField(max_digits=4, decimal_places=1, verbose_name="Height")
hips_height = models.DecimalField(
max_digits=4,
decimal_places=1,
verbose_name="Hips height",
null=True,
blank=True,
)
weight = models.DecimalField(max_digits=4, decimal_places=1, verbose_name="Weight")
2023-11-21 09:10:28 +01:00
@property
def bmi(self):
2023-11-21 09:17:18 +01:00
height = self.height / 100
2023-11-21 09:18:25 +01:00
return self.weight / (height * height)
2023-11-21 09:10:28 +01:00
2023-04-25 17:06:14 +02:00
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} : {self.height}/{self.hips_height} - {self.weight}"
2023-04-25 17:06:14 +02:00
class Note(Markdownizable, Seasonisable):
"""
Notes relatives à un gymnaste
"""
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="remarks"
)
coach = models.ForeignKey(
User, on_delete=models.SET_NULL, blank=True, null=True, related_name="notes"
)
status = models.PositiveSmallIntegerField(
2023-06-19 20:41:14 +02:00
choices=NOTE_STATUS_CHOICES, verbose_name="Status", default=0
)
title = models.CharField(
default="Note of the week", verbose_name="Title", max_length=255
2023-04-25 17:06:14 +02:00
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
2023-04-29 15:31:14 +02:00
return f"{self.gymnast} - {self.created_at} : {self.coach}"
2023-04-25 17:06:14 +02:00
class Intensity(Markdownizable, Seasonisable):
"""Classe représentant l'intensité d'un entraînement
L'intensité va prendre 9 valeurs :
- le temps (en minute)
- le temps théorique (en minute)
- la difficulté (en 10ème)
- la difficulté demandé (en 10ème)
- la quantité de figure
- la quantité de figure demandée
- le nombre de passage
- le nombre de passage demandé
- le nombre de gymnaste par trampoline
- qualité de temps (calculé automatiquement)
- qualité de difficulté (calculé automatiquement)
- qualité du nombre de figure (calculé automatiquement)
- qualité du nombre de passage (calculé automatiquement)
- qualité moyenne (calculé automatiquement)
Elle va également calculer 6 statistiques (et/ou leur corollaire) :
- la difficulté moyenne par minute (d/T)
- la quantité moyenne de figure par minute (S/T)
- le temps moyen par passage (T/p)
- le temps moyen par figure (T/S) (corollaire de la 2ème)
- la difficulté moyenne par passage (d/p)
- la difficulté moyenne par figure (d/S)
- la quantité moyenne de figures par passage (S/p)
2023-04-25 17:06:14 +02:00
"""
class Meta:
verbose_name = "Intensity"
verbose_name_plural = "Intensities"
2023-10-18 12:53:03 +02:00
# unique_together = ("gymnast", "date")
2023-04-25 17:06:14 +02:00
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="intensities"
)
time = models.PositiveSmallIntegerField(verbose_name="Time (in minutes)")
2024-01-25 17:54:21 +01:00
theorical_time = models.PositiveSmallIntegerField(
verbose_name="Theorical time (in minutes)"
)
2023-04-25 17:06:14 +02:00
difficulty = models.PositiveSmallIntegerField(verbose_name="Difficulty (in tenths)")
2024-01-25 17:54:21 +01:00
difficulty_asked = models.PositiveSmallIntegerField(
verbose_name="Difficulty asked (in tenths)"
)
2023-04-25 17:06:14 +02:00
quantity_of_skill = models.PositiveSmallIntegerField()
2024-01-25 17:54:21 +01:00
quantity_of_skill_asked = models.PositiveSmallIntegerField()
2023-04-25 17:06:14 +02:00
number_of_passes = models.PositiveSmallIntegerField()
2024-01-25 17:54:21 +01:00
number_of_passes_asked = models.PositiveSmallIntegerField()
number_of_gymnast = models.PositiveSmallIntegerField()
2024-02-16 06:19:28 +01:00
time_quality = models.DecimalField(max_digits=6, decimal_places=3)
difficulty_quality = models.DecimalField(max_digits=6, decimal_places=3)
quantity_of_skill_quality = models.DecimalField(max_digits=6, decimal_places=3)
number_of_passes_quality = models.DecimalField(max_digits=6, decimal_places=3)
average_training_quality = models.DecimalField(max_digits=6, decimal_places=3)
average_time_by_passe = models.DecimalField(max_digits=4, decimal_places=3)
def compute_average_training_quality(self):
"""
Calcul de la qualité d'un entrainement sur base des 4 données pratiques encodées : Temps,
# de passage, # de saut et Difficulté.
Si les 4 données pratiques sont inférieures ou égales aux données théoriques, une moyenne
pondérée (D*4, p*3, S*2 et T) est calculée.
Si une (ou plusieurs) données pratiques sont supérieures aux données théorique... ?
Pour les cas non traités, une moyenne arithmétique est calculée.
TODO:
2024-02-16 06:19:28 +01:00
- trouver un calcul d'efficacité qui tienne compte des statistiques (notamment le temps par
passage)
"""
# Si les 4 données pratiques sont inférieures ou égales aux données théoriques, une moyenne
# pondérée (D*4, p*3, S*2 et T) est calculée.
if (
self.time <= self.theorical_time
and self.number_of_passes <= self.number_of_passes_asked
and self.difficulty <= self.difficulty_asked
and self.quantity_of_skill <= self.quantity_of_skill_asked
):
return (
self.time_quality
+ (self.quantity_of_skill_quality * 2)
+ (self.number_of_passes_quality * 3)
+ (self.difficulty_quality * 4)
) / 10
# if self.difficulty > self.difficulty_asked:
# if (
# self.time <= self.theorical_time
# and self.number_of_passes <= self.number_of_passes_asked
# and self.quantity_of_skill <= self.quantity_of_skill_asked
# ):
# return self.difficulty_quality
if (
self.time <= self.theorical_time
and self.number_of_passes <= self.number_of_passes_asked
):
if (
self.difficulty >= self.difficulty_asked
and self.quantity_of_skill >= self.quantity_of_skill_asked
):
return (
(self.difficulty_quality * 2) + self.quantity_of_skill_quality
) / 3
# Pour les cas non traités, une moyenne arithmétique est calculée.
2024-01-28 16:06:47 +01:00
return (
self.time_quality
+ self.difficulty_quality
+ self.quantity_of_skill_quality
+ self.number_of_passes_quality
) / 4
2024-02-16 06:19:28 +01:00
def save(self, *args, **kwargs):
"""Calcule les informations de qualité de l'intensité de entraînement et sauve les informations."""
# self.average_time_by_skill = self.time / self.quantity_of_skill
2024-02-17 19:27:35 +01:00
self.time_quality = round((self.time / self.theorical_time) * 100, 3)
self.difficulty_quality = round(
(self.difficulty / self.difficulty_asked) * 100, 3
)
self.quantity_of_skill_quality = round(
(self.quantity_of_skill / self.quantity_of_skill_asked) * 100, 3
)
self.number_of_passes_quality = round(
(self.number_of_passes / self.number_of_passes_asked) * 100, 3
)
self.average_training_quality = round(
self.compute_average_training_quality(), 3
)
self.average_time_by_passe = round(self.time / self.number_of_passes, 3)
2024-02-16 06:19:28 +01:00
super().save(*args, **kwargs)
2023-11-22 09:50:34 +01:00
2024-02-16 06:19:28 +01:00
def __str__(self):
2024-02-17 19:27:35 +01:00
return f"{self.gymnast} - {self.date} : {self.time_quality} - {self.difficulty_quality} - {self.quantity_of_skill_quality} - {self.number_of_passes_quality} - {self.average_training_quality} - {self.average_time_by_passe}" # pylint: disable=line-too-long
2024-02-16 06:19:28 +01:00
#
2023-11-14 09:34:33 +01:00
@property
2024-02-16 06:19:28 +01:00
def passes_quality_for_gymnast(self):
"""Calcule la qualité de passage pour un entraînement. On calcule le temps pour un gymnaste
en additionnant le passage théorique optimale d'un passage (90 secondes) et un temps de
fonctionnement (pour monter, descendre, communiquer, ) équivalent racine cubique du nombre
de gymnaste) ; le tout multiplié par le nombre de gymnaste du groupe. Le tout calculé en
seconde puis ramener en minute.
La qualité de passage représente donc le temps nécessaire pour que tous les gymnastes du
groupe ait fait chacun un passage.
"""
optimal_time_by_gymnast = 90
average_passe_time = (
(optimal_time_by_gymnast + pow(100, 1 / self.number_of_gymnast))
* self.number_of_gymnast
) / 60
2023-11-14 09:34:33 +01:00
2024-02-17 19:00:27 +01:00
if self.average_time_by_passe <= average_passe_time:
2024-02-16 06:19:28 +01:00
return 1
2024-02-17 19:00:27 +01:00
if self.average_time_by_passe <= (average_passe_time * 1.05):
2024-02-16 06:19:28 +01:00
return 2
2024-02-17 19:00:27 +01:00
if self.average_time_by_passe >= (average_passe_time * 1.05):
2024-02-16 06:19:28 +01:00
return 3
2024-02-16 06:19:28 +01:00
# Theorical statistics
# @property
# def average_time_by_passe_theorical(self):
# return self.theorical_time / self.number_of_passes_asked
2024-02-16 06:19:28 +01:00
# @property
# def average_quantity_of_skill_by_time_theorical(self):
# return self.quantity_of_skill_asked / self.theorical_time
# @property
# def average_time_by_skill_theorical(self):
# return self.theorical_time / self.quantity_of_skill_asked
# @property
# def average_difficulty_by_passe_theorical(self):
# return self.difficulty_asked / self.number_of_passes_asked
# @property
# def average_quantity_of_skill_by_passe_theorical(self):
# return self.quantity_of_skill_asked / self.number_of_passes_asked
# @property
# def average_difficulty_by_skill_theorical(self):
# return self.difficulty_asked / self.quantity_of_skill_asked
# Real statistics
@property
2024-02-16 06:19:28 +01:00
def average_time_by_skill(self):
return self.time / self.quantity_of_skill
2024-01-28 16:06:47 +01:00
2023-04-25 17:06:14 +02:00
@property
2024-02-16 06:19:28 +01:00
def average_difficulty_by_passe(self):
2023-04-25 17:06:14 +02:00
return self.difficulty / self.number_of_passes
2023-11-12 19:22:52 +01:00
@property
2024-02-16 06:19:28 +01:00
def average_difficulty_by_passe_in_unit(self):
return self.average_difficulty_by_passe / 10
2023-11-12 19:22:52 +01:00
2023-04-25 17:06:14 +02:00
@property
2024-02-16 06:19:28 +01:00
def average_quantity_of_skill_by_time(self):
2023-04-25 17:06:14 +02:00
return self.quantity_of_skill / self.time
@property
2024-02-16 06:19:28 +01:00
def average_quantity_of_skill_by_passe(self):
2023-04-25 17:06:14 +02:00
return self.quantity_of_skill / self.number_of_passes
@property
2024-02-16 06:19:28 +01:00
def average_difficulty_by_skill(self):
2023-04-25 17:06:14 +02:00
return self.difficulty / self.quantity_of_skill
2023-11-14 09:34:33 +01:00
@property
2024-02-16 06:19:28 +01:00
def average_difficulty_by_skill_in_unit(self):
return self.average_difficulty_by_skill / 10
2024-02-05 16:51:55 +01:00
# Human readeable scores
2024-02-06 16:29:30 +01:00
@property
2024-02-16 06:19:28 +01:00
def average_time_by_passe_readable(self):
2024-02-17 19:00:27 +01:00
average_time_in_sec = round((self.average_time_by_passe % 1) * 60)
2024-02-06 16:29:30 +01:00
if average_time_in_sec < 10:
average_time_in_sec = "0" + str(average_time_in_sec)
else:
average_time_in_sec = str(average_time_in_sec)
2024-02-17 19:00:27 +01:00
return str(int(self.average_time_by_passe)) + "min" + average_time_in_sec
2024-02-06 16:29:30 +01:00
@property
def difficulty_in_unit(self):
return self.difficulty / 10
@property
def difficulty_asked_in_unit(self):
return self.difficulty_asked / 10
2023-11-14 09:34:33 +01:00
2023-04-25 17:06:14 +02:00
class SeasonInformation(models.Model):
"""Classe représentant l'intensité d'un entraînement"""
class Meta:
verbose_name = "Season Information"
verbose_name_plural = "Season Informations"
unique_together = ("gymnast", "season")
2024-01-14 14:48:31 +01:00
CATEGORY_CHOICES_ARRAY = list(CATEGORY_CHOICES.items())
2023-04-25 17:06:14 +02:00
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="season_informations"
)
season = models.CharField(max_length=9)
number_of_training_sessions_per_week = models.PositiveSmallIntegerField(
verbose_name="# Training/w"
)
number_of_hours_per_week = models.DecimalField(
verbose_name="# Hours/w", max_digits=3, decimal_places=1
2023-04-25 17:06:14 +02:00
)
number_of_s_and_c_sessions_per_week = models.PositiveSmallIntegerField(
verbose_name="# S&C training/w",
blank=True,
null=True,
)
number_of_s_and_c_hours_per_week = models.DecimalField(
2023-04-25 17:06:14 +02:00
verbose_name="# S&C hours/w",
blank=True,
null=True,
max_digits=3,
decimal_places=1,
2023-04-25 17:06:14 +02:00
)
category = models.PositiveSmallIntegerField(
choices=CATEGORY_CHOICES_ARRAY,
verbose_name="Category",
)
club = models.ForeignKey(
Club, null=True, on_delete=models.SET_NULL, related_name="season_informations"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
2024-01-14 14:48:31 +01:00
return f"{self.gymnast} - {self.season} : {self.number_of_training_sessions_per_week} - {self.number_of_hours_per_week} - {self.category} - {self.club}" # pylint: disable=line-too-long
2023-04-25 17:06:14 +02:00
class CompetitivePointsStats(Markdownizable, Seasonisable):
"""Class représentant des points de références de compétitions"""
TYPE_OF_STAT = (
(0, "precise"),
(1, "mean + 4 * standard deviation"),
(2, "mean + 2 * standard deviation"),
(3, "mean + standard deviation"),
(4, "mean + ½ standard deviation"),
(5, "mean + ¼ standard deviation"),
(6, "mean"),
(7, "mean - ¼ standard deviation"),
(8, "mean - ½ standard deviation"),
(9, "mean - standard deviation"),
(10, "mean - 2 * standard deviation"),
(11, "mean - 4 * standard deviation"),
)
label = models.CharField(max_length=40, null=False, blank=False)
gender = models.PositiveSmallIntegerField(
choices=GENDER_CHOICES, verbose_name="Gender"
)
age_category = models.PositiveSmallIntegerField(
choices=AGE_CATOGORY_CHOICES, verbose_name="Age category"
)
statistic_type = models.PositiveSmallIntegerField(
choices=TYPE_OF_STAT, verbose_name="Type of statistic"
)
point_execution = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="Execution"
)
point_difficulty = models.DecimalField(
max_digits=3, decimal_places=1, verbose_name="Difficulty"
)
point_time_of_flight = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="ToF"
)
point_horizontal_displacement = models.DecimalField(
max_digits=4, decimal_places=3, verbose_name="HD"
)
total = models.DecimalField(max_digits=6, decimal_places=3)
place = models.PositiveSmallIntegerField(verbose_name="Place")
event = models.ForeignKey(
Event, on_delete=models.SET_NULL, default=None, blank=True, null=True
)
routine_type = models.PositiveSmallIntegerField(choices=ROUTINE_TYPE_CHOICE)
informations = models.TextField(
blank=True,
null=True,
help_text="Information about even or statistics (mean, standard deviation, …).",
)
def __str__(self):
2024-01-14 14:48:31 +01:00
return f"{self.age_category} - {self.gender} - {self.routine_type} : {self.total} ({self.statistic_type})" # pylint: disable=line-too-long
2023-10-12 12:31:34 +02:00
# class Stability(Seasonisable):
# """Classe représentant la stabilité (Stability)"""
# class Meta:
# verbose_name = "Stability"
# verbose_name_plural = "Stabilities"
# unique_together = ["gymnast", "date"]
# gymnast = models.ForeignKey(
# Gymnast, on_delete=models.CASCADE, related_name="stabilitytests"
# )
# anterior_chain = models.PositiveSmallIntegerField(verbose_name="Anterior Chain")
# posterior_chain_left = models.PositiveSmallIntegerField(
# verbose_name="Posterior Chain Left"
# )
# posterior_chain_right = models.PositiveSmallIntegerField(
# verbose_name="Posterior Chain Right"
# )
# leg_lowering = models.PositiveSmallIntegerField(
# choices=LUMBAR_STABILITY_CHOICE,
# verbose_name="Leg Lowering: lumbar stability",
# )
# sl_bridge = models.ManyToManyField(
# SLBridge, related_name="stabilitytests", symmetrical=False
# )
# side_plank_leg_raise = models.ManyToManyField(
# SidePlankLegRaise, related_name="stabilitytests", symmetrical=False
# )
# class QualityOfMovement(Seasonisable):
# """Classe représentant les tests des qualité de mouvement (Quality of Movement)"""
# class Meta:
# verbose_name = "Quality Of Movement"
# verbose_name_plural = "Qualities Of Movement"
# unique_together = ["gymnast", "date"]
# gymnast = models.ForeignKey(
# Gymnast, on_delete=models.CASCADE, related_name="qualities_of_movement"
# )
# overhead_squat = models.ManyToManyField(
# OverheadSquat, related_name="qualities_of_movement", symmetrical=False
# )
# single_leg_drop_jump = models.ManyToManyField(
# DropJump, related_name="qualities_of_movement", symmetrical=False
# )
# single_leg_stability_right = models.PositiveSmallIntegerField(
# verbose_name="Single leg stability right"
# )
# single_leg_stability_left = models.PositiveSmallIntegerField(
# verbose_name="Single leg stability right"
# )
# def __str__(self):
# return f"{self.gymnast} - {self.date}: {self.overhead_squat}"
# class Strength(Seasonisable):
# """Classe représentant les tests de force (Strength)"""
# class Meta:
# verbose_name = "Strength"
# verbose_name_plural = "Strength"
# unique_together = ["gymnast", "date"]
# gymnast = models.ForeignKey(
# Gymnast, on_delete=models.CASCADE, related_name="strength"
# )
# harmstring_left_prone = models.PositiveSmallIntegerField(null=True, blank=True)
# harmstring_right_prone = models.PositiveSmallIntegerField(null=True, blank=True)
# quadriceps_left_seated = models.PositiveSmallIntegerField(null=True, blank=True)
# quadriceps_right_seated = models.PositiveSmallIntegerField(null=True, blank=True)
# hip_adductor_left = models.PositiveSmallIntegerField(null=True, blank=True)
# hip_adductor_right = models.PositiveSmallIntegerField(null=True, blank=True)
# hip_abductor_left = models.PositiveSmallIntegerField(null=True, blank=True)
# hip_abductor_right = models.PositiveSmallIntegerField(null=True, blank=True)
# shoulder_external_rotator_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_external_rotator_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_internal_rotator_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_internal_rotator_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# def __str__(self):
# return f"{self.gymnast} - {self.date}"
# class MobilityFlexibility(Seasonisable):
# """Classe représentant les tests de Mobilité et flexibilité (Mobility and Flexibility)"""
# class Meta:
# verbose_name = "Mobility & Flexibility"
# verbose_name_plural = "Mobility & Flexibility"
# unique_together = ["gymnast", "date"]
# gymnast = models.ForeignKey(
# Gymnast, on_delete=models.CASCADE, related_name="mobility_fexibility"
# )
# hip_internal_rotation_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# hip_internal_rotation_left = models.PositiveSmallIntegerField(null=True, blank=True)
# harmstring_aket_right = models.PositiveSmallIntegerField(null=True, blank=True)
# harmstring_aket_left = models.PositiveSmallIntegerField(null=True, blank=True)
# elys_test_right = models.PositiveSmallIntegerField(null=True, blank=True)
# elys_test_left = models.PositiveSmallIntegerField(null=True, blank=True)
# bent_knee_fall_out_test_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# bent_knee_fall_out_test_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# knee_to_wall_test_right = models.PositiveSmallIntegerField(null=True, blank=True)
# knee_to_wall_test_left = models.PositiveSmallIntegerField(null=True, blank=True)
# shoulder_anteversion_supine_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_anteversion_supine_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_external_rotation_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_external_rotation_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_internal_rotation_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# shoulder_internal_rotation_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# functional_external_rotation_right = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# functional_external_rotation_left = models.PositiveSmallIntegerField(
# null=True, blank=True
# )
# mod_thomas_test_right = models.PositiveSmallIntegerField(
# choices=MOD_THOMAS_TEST_CHOICES, default=0
# )
# mod_thomas_test_left = models.PositiveSmallIntegerField(
# choices=MOD_THOMAS_TEST_CHOICES, default=0
# )
# lombo_pelvic_control = models.PositiveSmallIntegerField(
# choices=LOMBO_PELVIC_AND_CERVICAL_CONTROL_CHOICES, default=0
# )
# cervical_control = models.PositiveSmallIntegerField(
# choices=LOMBO_PELVIC_AND_CERVICAL_CONTROL_CHOICES, default=0
# )
# def __str__(self):
# return f"{self.gymnast} - {self.date}"