from datetime import date from django.db import models from django.contrib.auth import get_user_model from django.core.validators import MaxValueValidator, MinValueValidator 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, GENDER_CHOICES, ) 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): return f"{self.gymnast} - {self.tof} ({self.date} - {self.chrono_type})" def timeline_representation(self): return f"
  • {self.date:%d %b %Y} - New personel best {CHRONO_TYPE_CHOICE[self.chrono_type][1]}: {self.score}' ({self.tof}')
  • " # pylint: disable=line-too-long @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) 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" ) nb_week_off = models.SmallIntegerField(default=0, verbose_name="# week off") 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): return f"
  • {self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)
  • " # pylint: disable=line-too-long 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): return f"{self.gymnast} - {self.date} - {self.learning_step} - {self.skill}" def timeline_representation(self): return f"
  • {self.date:%d %b %Y} - learning of {self.skill.long_label} ({self.skill.short_label}): {LEARNING_STEP_CHOICES[self.learning_step][1]}
  • " # pylint: disable=line-too-long 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): return f"{self.gymnast} - {self.educative.short_label} - {self.date}" 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): return f"{self.gymnast} - {self.total}" class WellBeing(Markdownizable, Seasonisable): """ Représente l'état psychologique/physique d'un gymnaste """ class Meta: ordering = ["date", ] gymnast = models.ForeignKey( Gymnast, on_delete=models.CASCADE, default=None, related_name="wellbeings" ) event = models.ForeignKey( Event, on_delete=models.SET_NULL, default=None, blank=True, null=True, related_name="wellbeings", ) mindstate = models.PositiveSmallIntegerField( verbose_name="Mindstate", validators=[MinValueValidator(1), MaxValueValidator(10)], ) sleep = models.PositiveSmallIntegerField( verbose_name="Sleep", null=True, blank=True, default=None, validators=[MinValueValidator(1), MaxValueValidator(10)], ) stress = models.PositiveSmallIntegerField( verbose_name="Stress", validators=[MinValueValidator(1), MaxValueValidator(10)], ) fatigue = models.PositiveSmallIntegerField( verbose_name="Fatigue", null=True, blank=True, default=None, validators=[MinValueValidator(1), MaxValueValidator(10)], ) muscle_soreness = models.PositiveSmallIntegerField( verbose_name="Muscle Soreness", null=True, blank=True, default=None, validators=[MinValueValidator(1), MaxValueValidator(10)], ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"{self.gymnast} - {self.date} : {self.mindstate} | {self.sleep} | {self.stress} | {self.fatigue} | {self.muscle_soreness}" # pylint: disable=line-too-long @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 class GymnastHasRoutine(models.Model): """Représente le lien entre les gymnastes et leurs séries. 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): return f"{self.gymnast} - {self.routine_type} : {self.routine}" 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 ) 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): return f"{self.gymnast} - {self.routine_type} ({self.routine}) : {self.number_of_try} | {self.number_of_successes}" # pylint: disable=line-too-long class HeightWeight(Seasonisable): """ Classe permettant de suivre le poids et la taille d'un gymnaste. Voici les autres formules mathématiques les plus connues, valables pour les adultes : - La formule de Broca est très simple. Le poids idéal correspond à la différence entre votre taille en cm et 100. Vous mesurez 170 cm et votre poids idéal est de 70 kg. - La formule de Lorentz tient compte du sexe. Pour une femme, on calcule taille en cm – 100 – [(taille – 150)/2,5]. On divisera par 4 pour un homme. Si vous mesurez 170 cm, le résultat est 62 kg. - La formule de Creff classe la morphologie en trois catégories, très subjectives : normal, large et gracile. Le poids idéal est augmenté de 10 % pour les larges et réduit de 10 % pour les graciles. Pour une silhouette normale, la formule est taille en cm – 100 + (âge/10) X 0,9. Pour une femme d’une quarantaine d’années et de 1,70 cm, on obtient 66,6 kg. - La formule de Monnerot-Dumaine prend en compte l’ossature en se basant sur la circonférence du poignet. Le calcul : (Taille en cm – 100) + (4 x Circonférence du poignet en cm) / 2. Pour notre femme de 170 cm avec un poignet de 16 cm, le résultat est 67 kg. """ class Meta: verbose_name = "Height & weight" verbose_name_plural = "Heights & weights" unique_together = ("gymnast", "date") ordering = ["date",] 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") @property def bmi(self): height = self.height / 100 return self.weight / (height * height) @property def bmi_quality(self): bmi = self.bmi if self.gymnast.gender == 0: if bmi < 19: return 2 elif bmi < 21: return 1 elif bmi < 23: return 0 elif bmi < 25: return 1 else: return 2 else: if bmi < 21: return 2 elif bmi < 23: return 1 elif bmi < 25: return 0 elif bmi < 27: return 1 else: return 2 def __str__(self): return f"{self.gymnast} : {self.height}/{self.hips_height} - {self.weight}" 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( choices=NOTE_STATUS_CHOICES, verbose_name="Status", default=0 ) title = models.CharField( default="Note of the week", verbose_name="Title", max_length=255 ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"{self.gymnast} - {self.created_at} : {self.coach}" 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) """ class Meta: verbose_name = "Intensity" verbose_name_plural = "Intensities" # unique_together = ("gymnast", "date") gymnast = models.ForeignKey( Gymnast, on_delete=models.CASCADE, related_name="intensities" ) time = models.PositiveSmallIntegerField(verbose_name="Time (in minutes)") theorical_time = models.PositiveSmallIntegerField( verbose_name="Theorical time (in minutes)" ) difficulty = models.PositiveSmallIntegerField(verbose_name="Difficulty (in tenths)") difficulty_asked = models.PositiveSmallIntegerField( verbose_name="Difficulty asked (in tenths)" ) quantity_of_skill = models.PositiveSmallIntegerField() quantity_of_skill_asked = models.PositiveSmallIntegerField() number_of_passes = models.PositiveSmallIntegerField() number_of_passes_asked = models.PositiveSmallIntegerField() number_of_gymnast = models.PositiveSmallIntegerField() 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: - 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. return ( self.time_quality + self.difficulty_quality + self.quantity_of_skill_quality + self.number_of_passes_quality ) / 4 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 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) super().save(*args, **kwargs) def __str__(self): 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 # @property 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 if self.average_time_by_passe <= average_passe_time: return 1 if self.average_time_by_passe <= (average_passe_time * 1.05): return 2 if self.average_time_by_passe >= (average_passe_time * 1.05): return 3 # Theorical statistics # @property # def average_time_by_passe_theorical(self): # return self.theorical_time / self.number_of_passes_asked # @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 def average_time_by_skill(self): return self.time / self.quantity_of_skill @property def average_difficulty_by_passe(self): return self.difficulty / self.number_of_passes @property def average_difficulty_by_passe_in_unit(self): return self.average_difficulty_by_passe / 10 @property def average_quantity_of_skill_by_time(self): return self.quantity_of_skill / self.time @property def average_quantity_of_skill_by_passe(self): return self.quantity_of_skill / self.number_of_passes @property def average_difficulty_by_skill(self): return self.difficulty / self.quantity_of_skill @property def average_difficulty_by_skill_in_unit(self): return self.average_difficulty_by_skill / 10 # Human readeable scores @property def average_time_by_passe_readable(self): average_time_in_sec = round((self.average_time_by_passe % 1) * 60) 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) return str(int(self.average_time_by_passe)) + "min" + average_time_in_sec @property def difficulty_in_unit(self): return self.difficulty / 10 @property def difficulty_asked_in_unit(self): return self.difficulty_asked / 10 class CompetitionCategory(Markdownizable): """Classe représentant les catégories de compétitions""" class Meta: verbose_name = "Competition Category" verbose_name_plural = "Competition Categories" long_label = models.CharField(max_length=100) short_label = models.CharField(max_length=10) age_min = models.PositiveSmallIntegerField(blank=True, null=True) age_max = models.PositiveSmallIntegerField(blank=True, null=True) def __str__(self): return f"{self.short_label}" class SeasonInformation(Markdownizable): """Classe représentant l'intensité d'un entraînement""" class Meta: verbose_name = "Season Information" verbose_name_plural = "Season Informations" unique_together = ("gymnast", "season") CATEGORY_CHOICES_ARRAY = list(CATEGORY_CHOICES.items()) 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 ) 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( verbose_name="# S&C hours/w", blank=True, null=True, max_digits=3, decimal_places=1, ) category = models.ForeignKey( CompetitionCategory, on_delete=models.CASCADE, related_name="season_informations" ) 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): 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 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): return f"{self.age_category} - {self.gender} - {self.routine_type} : {self.total} ({self.statistic_type})" # pylint: disable=line-too-long # 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}"