Jarvis/jarvis/followup/models.py

915 lines
34 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
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"<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
@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"<li>{self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)</li>" # 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"<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
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 dune quarantaine dannées et de 1,70 cm, on obtient 66,6 kg.
- La formule de Monnerot-Dumaine prend en compte lossature 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... ?
"""
return (
self.time_quality
+ (self.quantity_of_skill_quality * 2)
+ (self.number_of_passes_quality * 3)
+ (self.difficulty_quality * 4)
) / 10
def save(self, *args, **kwargs):
"""Calculate quality metrics for training intensity and save the information."""
def calculate_quality(actual, expected):
"""Helper function to calculate quality percentage."""
if expected > 0:
return round((actual / expected) * 100, 3)
return 0 # Return 0 or some other appropriate value if expected is zero
# Calculate quality metrics
self.time_quality = calculate_quality(self.time, self.theorical_time)
self.difficulty_quality = calculate_quality(self.difficulty, self.difficulty_asked)
self.quantity_of_skill_quality = calculate_quality(self.quantity_of_skill, self.quantity_of_skill_asked)
self.number_of_passes_quality = calculate_quality(self.number_of_passes, self.number_of_passes_asked)
# Calculate average training quality and time per pass
self.average_training_quality = round(self.compute_average_training_quality(), 3)
if self.number_of_passes > 0:
self.average_time_by_passe = round(self.time / self.number_of_passes, 3)
else:
self.average_time_by_passe = 0 # Handle zero passes appropriately even if it should not append.
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):
"""Calculate the quality of passes for a training session. This calculates the time for a gymnast
by adding the optimal theoretical time for a pass (90 seconds) and a operational time (for getting on and off, communicating, etc.) (calculated as the cube root of the number of gymnasts), multiplied by the number of gymnasts in the group, and then converted from seconds to minutes.
The quality of passes represents the time needed for all gymnasts in the group to each make a pass.
"""
if self.number_of_gymnast == 0:
return 0 # Handle case where there are no gymnasts to avoid division by zero
optimal_time_by_gymnast = 90
operational_time = pow(100, 1 / self.number_of_gymnast)
total_time_per_gymnast = optimal_time_by_gymnast + operational_time
total_group_time_seconds = total_time_per_gymnast * self.number_of_gymnast
average_passe_time = total_group_time_seconds / 60
# Calculate the threshold for a 5% increase
threshold_time = average_passe_time * 1.05
# Determine the quality based on the average time per pass
if self.average_time_by_passe <= average_passe_time:
return 1
elif self.average_time_by_passe <= threshold_time:
return 2
else:
return 3
# 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}"