from django.db import models from django.contrib.auth import get_user_model from datetime import date 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() ROUTINE_TYPE_CHOICE = ( (0, "Other"), (1, "Q1R1"), (2, "Q1R2"), (3, "Q2R1"), (4, "SF"), (5, "F"), (6, "Q1R1S"), (7, "Q1R2S"), (8, "Q2R1S"), (9, "SFS"), (9, "FS"), (99, "Other"), ) LEARNING_STEP_CHOICES = ( (0, "No"), (1, "With help"), (2, "Without help"), (3, "Chained"), (4, "Masterised"), ) CHRONO_TYPE_CHOICE = ( (0, "10 |"), (1, "Q1R1"), (2, "Q1R2"), (3, "Q2R1"), (4, "SF"), (5, "F"), (99, "Other"), ) SCORE_TYPE_CHOICE = ( (0, "Chrono"), (1, "ToF"), ) CATEGORY_CHOICES = { 9: "I9", 10: "I10", 11: "A11", 12: "A12", 13: "A13-14", 15: "A Junior", 18: "A Senior", 21: "B11", 22: "B12", 23: "B13-14", 24: "B Junior", 25: "B Senior", } AGE_CATOGORY_CHOICES = ( (11, "11-12"), (13, "13-14"), (15, "15-16"), (17, "17-21"), (22, "Senior"), ) 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.score} ({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}')
  • " @staticmethod def compute_tof(value): tof = round((value * 13) / 15, 3) * 1000 tof = tof - (tof % 5) 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 Accident(Markdownizable, Seasonisable): """ La classe `Accident` permet d'indiquer qu'un gymnaste a eu un accident, en liaison avec un skill ou non. """ class Meta: verbose_name = "Accident" verbose_name_plural = "Accidents" # unique_together = ("gymnast", "skill", "date") gymnast = models.ForeignKey( Gymnast, verbose_name="Gymnast", related_name="accident", on_delete=models.CASCADE, ) skill = models.ForeignKey( "objective.Skill", verbose_name="Skill", related_name="accident", on_delete=models.SET_NULL, default=None, blank=True, null=True, ) nb_week_off = models.SmallIntegerField( blank=True, null=True, 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)
  • " 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]}
  • " 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 MindState(Markdownizable, Seasonisable): """ Représente l'état d'esprit psychologique d'un gymnaste """ gymnast = models.ForeignKey( Gymnast, on_delete=models.CASCADE, default=None, related_name="mindstate" ) event = models.ForeignKey( Event, on_delete=models.SET_NULL, default=None, blank=True, null=True, related_name="mindstate", ) score = models.PositiveSmallIntegerField(verbose_name="Score") 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.score}" class GymnastHasRoutine(models.Model): """ Classe représentant 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, null=True, blank=True, ) 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}" 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") 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") def __str__(self): return f"{self.gymnast} : {self.height}/{self.hips_height} - {self.weight}" class Note(Markdownizable, Seasonisable): """ Notes relatives à un gymnaste """ STATUS_CHOICES = ( (0, "Draft"), (1, "Published"), ) 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=STATUS_CHOICES, verbose_name="Status", 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.created_at} : {self.coach}" class Intensity(Markdownizable, Seasonisable): """Classe représentant l'intensité d'un entraînement L'intensité va prendre 4 valeurs : - la temps (en minute), - la difficulté (en 10 ème), - la quantité de figure et - le nombre de passafe. Avec ces 4 informations, la classe va en calculer 4 autres : - la difficulté moyenne par passage - la difficulté moyenne par figure - la quantité moyene de figures par passage - la quantité moyenne de figure par minute """ 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)") difficulty = models.PositiveSmallIntegerField(verbose_name="Difficulty (in tenths)") quantity_of_skill = models.PositiveSmallIntegerField() number_of_passes = models.PositiveSmallIntegerField() def __str__(self): return f"{self.gymnast} - {self.date} : {self.time} - {self.difficulty} - {self.quantity_of_skill} - {self.number_of_passes}" @property def mean_difficulty_by_passe(self): return self.difficulty / self.number_of_passes @property def mean_quantity_of_skill(self): return self.quantity_of_skill / self.time @property def quantity_of_skill_by_passe(self): return self.quantity_of_skill / self.number_of_passes @property def mean_difficulty_by_skill(self): return self.difficulty / self.quantity_of_skill 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") CATEGORY_CHOICES_ARRAY = [(key, value) for key, value in 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.PositiveSmallIntegerField( verbose_name="# Hours/w" ) 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.PositiveSmallIntegerField( verbose_name="# S&C hours/w", blank=True, null=True, ) 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): return f"{self.gymnast} - {self.season} : {self.number_of_training_sessions_per_week} - {self.number_of_hours_per_week} - {self.category} - {self.club}" 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})"