Jarvis/jarvis/followup/models.py

949 lines
35 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,
)
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"<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)
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}"