305 lines
9.4 KiB
Python
305 lines
9.4 KiB
Python
from django.db import models
|
|
from ultron.tools.models import Markdownizable
|
|
from django.db.models import Q, Count
|
|
from datetime import date
|
|
|
|
|
|
class Educative(Markdownizable):
|
|
"""
|
|
Classe `mère`.
|
|
"""
|
|
|
|
class Meta:
|
|
verbose_name = "Educatif"
|
|
verbose_name_plural = "Educatifs"
|
|
ordering = ["long_label", "short_label"] # 'level',
|
|
|
|
long_label = models.CharField(max_length=255, verbose_name="Long Name")
|
|
short_label = models.CharField(max_length=255, verbose_name="Short Name")
|
|
difficulty = models.DecimalField(
|
|
max_digits=3, decimal_places=1, verbose_name="Difficulty", default=0.000
|
|
)
|
|
level = models.PositiveSmallIntegerField(verbose_name="Level", default=0)
|
|
rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0)
|
|
educatives = models.ManyToManyField(
|
|
"self", related_name="educatives_of", blank=True, symmetrical=False
|
|
)
|
|
prerequisites = models.ManyToManyField(
|
|
"self", related_name="prerequisite_of", blank=True, symmetrical=False
|
|
)
|
|
age_boy = models.PositiveSmallIntegerField(
|
|
blank=True, null=True, verbose_name="Boy's age"
|
|
)
|
|
age_girl = models.PositiveSmallIntegerField(
|
|
blank=True, null=True, verbose_name="Girl's age"
|
|
)
|
|
|
|
def __str__(self):
|
|
return "%s - %s (level: %s | diff: %s)" % (
|
|
self.rank,
|
|
self.long_label,
|
|
self.level,
|
|
self.difficulty,
|
|
)
|
|
|
|
|
|
class TouchPosition(models.Model):
|
|
"""
|
|
Classe représentant les différentes position d'arrivée/départ (landing position) en trampoline.
|
|
"""
|
|
|
|
class Meta:
|
|
verbose_name = "Landing"
|
|
verbose_name_plural = "Landings"
|
|
ordering = ["long_label", "short_label", "is_default", "allowed_in_competition"]
|
|
|
|
long_label = models.CharField(max_length=30, verbose_name="Long label")
|
|
short_label = models.CharField(max_length=15, verbose_name="Short label")
|
|
allowed_in_competition = models.BooleanField(verbose_name="Allowed in competition")
|
|
is_default = models.BooleanField(verbose_name="Défault ?")
|
|
|
|
def __str__(self):
|
|
return "%s" % (self.long_label)
|
|
|
|
|
|
def get_default_position():
|
|
"""
|
|
Renvoie la position d'arrivée/départ par définie par défaut si elle existe. Sinon, renvoie None.
|
|
"""
|
|
try:
|
|
return TouchPosition.objects.get(default=True).id
|
|
except:
|
|
return None
|
|
|
|
|
|
class Skill(Educative):
|
|
"""
|
|
Classe représentant une figure (aka un saut acrobatique).
|
|
"""
|
|
|
|
# SELECT * FROM `objective_skill` WHERE educative_ptr_id NOT IN (SELECT DISTINCT(from_educative_id) FROM `objective_educative_prerequisite`)
|
|
# SELECT * FROM `objective_skill`, `objective_educative` WHERE `objective_educative`.id = `objective_skill`.educative_ptr_id
|
|
|
|
class Meta:
|
|
verbose_name = "Skill"
|
|
verbose_name_plural = "Skills"
|
|
|
|
POSITION_CHOICES = (
|
|
("0", "None"),
|
|
("o", "Tuck"),
|
|
("c", "Puck"),
|
|
("<", "Pike"),
|
|
# ("L", "Half pike"),
|
|
("/", "Straight"),
|
|
("//", "Straddle"),
|
|
)
|
|
|
|
ROTATION_CHOICES = (
|
|
(0, "None"),
|
|
(1, "Frontward"),
|
|
(2, "Backward"),
|
|
)
|
|
|
|
position = models.CharField(max_length=2, choices=POSITION_CHOICES)
|
|
departure = models.ForeignKey(
|
|
TouchPosition,
|
|
related_name="depart_of",
|
|
default=get_default_position,
|
|
verbose_name="Take-off position",
|
|
on_delete=models.CASCADE,
|
|
)
|
|
landing = models.ForeignKey(
|
|
TouchPosition,
|
|
related_name="landing_of",
|
|
default=get_default_position,
|
|
verbose_name="Landing position",
|
|
on_delete=models.CASCADE,
|
|
)
|
|
rotation_type = models.PositiveSmallIntegerField(
|
|
choices=ROTATION_CHOICES, verbose_name="Type de rotation"
|
|
)
|
|
rotation = models.PositiveSmallIntegerField(verbose_name="1/4 de rotation")
|
|
twist = models.PositiveSmallIntegerField(verbose_name="1/2 Vrille")
|
|
notation = models.CharField(max_length=25)
|
|
simplified_notation = models.CharField(
|
|
max_length=25, verbose_name="Notation simplifiée"
|
|
)
|
|
is_competitive = models.BooleanField(default=False)
|
|
# importance = models.PositiveSmallIntegerField(default = 1)
|
|
|
|
def __str__(self):
|
|
return "%s (%s)" % (self.long_label, self.notation)
|
|
|
|
@staticmethod
|
|
def nb_skill_by_level(max_level_skill):
|
|
"""
|
|
Nombre de Skill qui ont un level inférieur ou égal, groupé par niveau
|
|
"""
|
|
nb_skill_by_level = (
|
|
Skill.objects.values("level")
|
|
.filter(level__lte=max_level_skill)
|
|
.order_by("level")
|
|
.annotate(nb_skill=Count("id"))
|
|
)
|
|
return nb_skill_by_level
|
|
|
|
@staticmethod
|
|
def nb_skill_by_rank(max_rank_skill):
|
|
"""
|
|
Nombre de Skill qui ont un rang inférieur ou égal, groupé par niveau
|
|
"""
|
|
nb_skill_by_rank = (
|
|
Skill.objects.values("rank")
|
|
.filter(level__lte=max_rank_skill)
|
|
.order_by("rank")
|
|
.annotate(nb_skill=Count("id"))
|
|
)
|
|
return nb_skill_by_rank
|
|
|
|
@staticmethod
|
|
def nb_skill_lte_rank(max_rank_skill):
|
|
"""
|
|
Nombre de Skill qui ont un rang inférieur ou égal à un rang passé en paramètre
|
|
"""
|
|
nb_skill = Skill.objects.filter(rank__lte=max_rank_skill).count()
|
|
return nb_skill
|
|
|
|
@staticmethod
|
|
def nb_skill_lte_level(max_rank_skill):
|
|
"""
|
|
Nombre de Skill qui ont un niveau inférieur ou égal à un niveau passé en paramètre
|
|
"""
|
|
nb_skill = Skill.objects.filter(level__lte=max_rank_skill).count()
|
|
return nb_skill
|
|
|
|
|
|
class Routine(Educative):
|
|
"""
|
|
Classe représentant une série (enchainement de plusieurs figures).
|
|
"""
|
|
|
|
class Meta:
|
|
verbose_name = "Routine"
|
|
verbose_name_plural = "Routines"
|
|
|
|
active = models.BooleanField()
|
|
jumps = models.ManyToManyField(
|
|
Skill, through="RoutineSkill", verbose_name="routine"
|
|
)
|
|
is_competitive = models.BooleanField(default=False)
|
|
|
|
def __str__(self):
|
|
return "%s (%s)" % (self.long_label, self.short_label)
|
|
|
|
def contains_basic_jumps(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un saut de base, False sinon.
|
|
"""
|
|
return self.skill_links.filter(skill__notation__in=["//", "<", "o"]).exists()
|
|
|
|
def contains_basic_fall(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un tomber de base, False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
skill__landing__long_label__in=["Assis", "Dos", "Ventre"]
|
|
).exists()
|
|
|
|
def contains_basic_salto(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un salto/barani de base,
|
|
False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
Q(skill__notation__icontains=".4 1") | Q(skill__notation__icontains="4. -")
|
|
).exists()
|
|
|
|
def contains_basic_three_quarters(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un 3/4 de salto, False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
Q(skill__notation__icontains=".3") | Q(skill__notation__icontains="3.")
|
|
).exists()
|
|
|
|
def contains_basic_twist(self):
|
|
"""
|
|
Renvoie True si la série contient au moins une vrille de base, False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
Q(skill__notation__icontains=".4 3") | Q(skill__notation__icontains="4. 2")
|
|
).exists()
|
|
|
|
def contains_double(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un double, False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
Q(skill__notation__icontains=".8") | Q(skill__notation__icontains="8.")
|
|
).exists()
|
|
|
|
def contains_triple(self):
|
|
"""
|
|
Renvoie True si la série contient au moins un triple, False sinon.
|
|
"""
|
|
return self.skill_links.filter(
|
|
Q(skill__notation__icontains=".12") | Q(skill__notation__icontains="12.")
|
|
).exists()
|
|
|
|
|
|
class RoutineSkill(models.Model):
|
|
"""
|
|
Classe de liaison permettant de liée une figure à une série. (relation n-n)
|
|
"""
|
|
|
|
class Meta:
|
|
ordering = ("rank",)
|
|
|
|
routine = models.ForeignKey(
|
|
Routine, on_delete=models.CASCADE, default=None, related_name="skill_links"
|
|
)
|
|
skill = models.ForeignKey(
|
|
Skill, on_delete=models.CASCADE, default=None, related_name="routine_links"
|
|
)
|
|
rank = models.PositiveSmallIntegerField()
|
|
|
|
def __str__(self):
|
|
return "%s - %s : %s" % (
|
|
self.rank,
|
|
self.routine.short_label,
|
|
self.skill.short_label,
|
|
)
|
|
|
|
|
|
class Plan(models.Model):
|
|
"""
|
|
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,
|
|
)
|
|
date = models.DateField(default=date.today, verbose_name="Date")
|
|
|
|
def __str__(self):
|
|
return "%s - %s - %s" % (
|
|
self.gymnast,
|
|
self.educative.short_label,
|
|
self.date,
|
|
)
|