303 lines
10 KiB
Python
303 lines
10 KiB
Python
""" Modelisation des gymnastes """
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.db.models import Count
|
|
from django.db import models
|
|
User = get_user_model()
|
|
|
|
from datetime import date
|
|
import pendulum
|
|
|
|
from ultron.location.models import Club
|
|
from ultron.objective.models import Skill
|
|
from ultron.tools.models import Markdownizable
|
|
from ultron.objective.tools import (
|
|
nb_skill_by_type,
|
|
nb_skill_lte_type,
|
|
compute_completude,
|
|
compute_statistics_by_type,
|
|
)
|
|
|
|
|
|
class Gymnast(Markdownizable):
|
|
"""Représente un gymnaste.
|
|
Un gymnaste peut être actif ou inactif.
|
|
"""
|
|
|
|
class Meta:
|
|
verbose_name = "Gymnast"
|
|
verbose_name_plural = "Gymnasts"
|
|
|
|
GENDER_CHOICES = ((0, "Male"), (1, "Female"))
|
|
# CATEGORY_CHOICES = ((9, "I9"), (10, "I10"), (11, "A11"), (12, "A12"), (13, "A13-14"), (15, "A15-16"), (18, "Senior"))
|
|
|
|
user = models.OneToOneField(
|
|
User,
|
|
on_delete=models.SET_NULL,
|
|
related_name="gymnast",
|
|
blank=True,
|
|
null=True
|
|
)
|
|
last_name = models.CharField(max_length=40, null=False, blank=False)
|
|
first_name = models.CharField(max_length=25, null=False, blank=False)
|
|
birthdate = models.DateField(verbose_name="Date de naissance")
|
|
gender = models.PositiveSmallIntegerField(
|
|
choices=GENDER_CHOICES, verbose_name="Sexe"
|
|
)
|
|
is_active = models.BooleanField(default=1, verbose_name="Active")
|
|
club = models.ForeignKey(
|
|
Club, null=True, on_delete=models.SET_NULL, related_name="gymnast"
|
|
)
|
|
# category = models.PositiveSmallIntegerField(
|
|
# choices=CATEGORY_CHOICES, verbose_name="Category"
|
|
# )
|
|
email_trainer = models.EmailField(max_length=254, null=True, blank=True, verbose_name="Trainer's email")
|
|
trainings_by_week = models.PositiveSmallIntegerField(
|
|
verbose_name="# Training by week"
|
|
)
|
|
hours_by_week = models.PositiveSmallIntegerField(verbose_name="# Hours by week")
|
|
|
|
def __str__(self):
|
|
return "%s %s" % (self.first_name, self.last_name)
|
|
|
|
@property
|
|
def next_birthday(self):
|
|
"""Définit la prochaine date (de fête) d'anniversaire pour cette personne.
|
|
|
|
Returns:
|
|
Soit le jour/mois pour cette année
|
|
Soit le jour/mois pour l'année prochaine.
|
|
|
|
Examples: en supposant qu'on soit le 23/05/2019
|
|
>>> from datetime import date
|
|
>>> gregg = Gymnast(name='Tru', firstname='Gregg', birthdate=date(1982, 2, 5))
|
|
>>> gregg.next_birthday()
|
|
Date(2020, 2, 5)
|
|
"""
|
|
|
|
now = pendulum.now()
|
|
|
|
this_year_birthday = pendulum.date(
|
|
now.year, self.birthdate.month, self.birthdate.day
|
|
)
|
|
|
|
if this_year_birthday.is_past():
|
|
return pendulum.date(now.year + 1, self.birthdate.month, self.birthdate.day)
|
|
return this_year_birthday
|
|
|
|
@property
|
|
def next_birthday_in_days(self):
|
|
now = pendulum.now()
|
|
return self.next_birthday.diff(now).in_days()
|
|
|
|
@property
|
|
def age(self):
|
|
"""Renvoie l'âge d'un gymnaste."""
|
|
today = date.today()
|
|
return (
|
|
today.year
|
|
- self.birthdate.year
|
|
- ((today.month, today.day) < (self.birthdate.month, self.birthdate.day))
|
|
)
|
|
|
|
@property
|
|
def next_age(self):
|
|
"""Renvoie l'âge prochain du gymnaste."""
|
|
return (self.age) + 1
|
|
|
|
def min_rank_skill(self):
|
|
tmp = (
|
|
Skill.objects.filter(known_by__gymnast=self.id)
|
|
.order_by("rank")
|
|
.values("rank")[:1]
|
|
)
|
|
|
|
if tmp:
|
|
min_rank = tmp[0]["rank"]
|
|
else:
|
|
min_rank = 0
|
|
|
|
return min_rank
|
|
|
|
def max_rank_skill(self):
|
|
tmp = (
|
|
Skill.objects.filter(known_by__gymnast=self.id)
|
|
.order_by("-rank")
|
|
.values("rank")[:1]
|
|
)
|
|
if tmp:
|
|
max_rank = tmp[0]["rank"]
|
|
else:
|
|
max_rank = 0
|
|
|
|
return max_rank
|
|
|
|
def nb_known_skill_by_rank(self):
|
|
"""
|
|
Renvoie le nombre de Skill qu'un gymnast sait faire par rang
|
|
"""
|
|
nb_known_skill_by_rank = (
|
|
Skill.objects.values("rank")
|
|
.filter(known_by__gymnast=self.id)
|
|
.order_by("rank")
|
|
.annotate(nb_known_skill=Count("id"))
|
|
)
|
|
return nb_known_skill_by_rank
|
|
|
|
def max_level_skill(self):
|
|
"""
|
|
Retourne le niveau maximum des skill que le gymnaste sait faire.
|
|
"""
|
|
tmp = (
|
|
Skill.objects.filter(known_by__gymnast=self.id)
|
|
.order_by("-level")
|
|
.values("level")[:1]
|
|
)
|
|
if tmp:
|
|
max_level = tmp[0]["level"]
|
|
else:
|
|
max_level = 0
|
|
|
|
return max_level
|
|
|
|
def nb_known_skill_by_level(self):
|
|
"""
|
|
Renvoie le nombre de Skill qu'un gymnast sait faire par niveau
|
|
"""
|
|
nb_known_skill_by_level = (
|
|
Skill.objects.values("level")
|
|
.filter(known_by__gymnast=self.id)
|
|
.order_by("level")
|
|
.annotate(nb_known_skill=Count("id"))
|
|
)
|
|
return nb_known_skill_by_level
|
|
|
|
def unknown_skill_lte_level(self, max_level_skill):
|
|
"""
|
|
Liste des Skill que le gymnaste ne sait PAS faire/
|
|
|
|
Ces skills sont classé par niveau.
|
|
"""
|
|
return Skill.objects.filter(level__lte=max_level_skill).exclude(
|
|
known_by__gymnast=self.id
|
|
)
|
|
|
|
def unknown_skill_gt_level(self, max_level_skill):
|
|
"""
|
|
Liste des Skill que le gymnaste ne sait PAS faire,
|
|
classé par niveau (ayant un niveau inférieur ou égal au niveau max du gym)
|
|
"""
|
|
return Skill.objects.filter(level__gt=max_level_skill).exclude(
|
|
known_by__gymnast=self.id
|
|
)
|
|
|
|
def unknown_skill_lte_rank(self, max_rank_skill):
|
|
# Liste des Skill que le gymnaste ne sait PAS faire, classé par niveau
|
|
# (ayant un niveau inférieur ou égal au niveau max du gym)
|
|
return Skill.objects.filter(level__lte=max_rank_skill).exclude(
|
|
known_by__gymnast=self.id
|
|
)
|
|
|
|
def unknown_skill_gt_rank(self, max_rank_skill):
|
|
"""
|
|
Liste des Skill que le gymnaste ne sais PAS faire
|
|
(ayant un niveau plus grand que le niveau max du gym)
|
|
"""
|
|
return Skill.objects.filter(level__gt=max_rank_skill).exclude(
|
|
known_by__gymnast=self.id
|
|
)
|
|
|
|
def get_informations_from_level(self):
|
|
"""
|
|
Calcule toutes les statistiques par rapport au niveau.
|
|
|
|
1. on va chercher le niveau maximum de skill que le gymnast sait faire
|
|
2. on va chercher le nombre de skill par niveau que le gymnast sait faire
|
|
nb_known_skill_by_level = [
|
|
{
|
|
'level': 1,
|
|
'nb_known_skill': 1
|
|
},
|
|
{
|
|
'level': 2,
|
|
'nb_known_skill': 2
|
|
}
|
|
]
|
|
3. si le niveau max est supérieur à 0
|
|
OUI:
|
|
a. on va chercher le nombre total des skills dont le niveau
|
|
est inférieur ou égale au niveau max (cf. 1.)
|
|
b. on va chercher le nombre de skill qu'il y a par niveau :
|
|
[
|
|
{
|
|
'level': 1, 'nb_skill': 1
|
|
},
|
|
{
|
|
'level': 2, 'nb_skill': 1
|
|
}
|
|
]
|
|
|
|
c. Pour chaque élément da la liste obtenue en b :
|
|
- si le niveau du skill est le même que le niveau courant ...
|
|
je sais pas, je comprends plus...
|
|
- on incrémente j
|
|
NON:
|
|
|
|
"""
|
|
|
|
context = {}
|
|
max_level_skill = self.max_level_skill()
|
|
nb_known_skill_by_level = self.nb_known_skill_by_level()
|
|
gymnast_nb_known_skills = self.known_skills.distinct("skill").count()
|
|
|
|
if max_level_skill > 0:
|
|
cpt_skill_by_level = nb_skill_by_type(max_level_skill, "level")
|
|
context["total_skill"] = nb_skill_lte_type(max_level_skill, "level")
|
|
context["percentages"] = compute_statistics_by_type(
|
|
cpt_skill_by_level, nb_known_skill_by_level, "level"
|
|
)
|
|
context["skill_by_level"] = self.unknown_skill_lte_level(max_level_skill)
|
|
context["unknown_skill"] = self.unknown_skill_gt_level(max_level_skill)
|
|
else:
|
|
tmp = Skill.objects.all()
|
|
context["total_skill"] = tmp.count()
|
|
context["unknown_skill"] = tmp
|
|
|
|
context["completude"], context["evaluated_level"] = compute_completude(
|
|
context["total_skill"], gymnast_nb_known_skills, max_level_skill
|
|
)
|
|
|
|
context["max_level_skill"] = max_level_skill
|
|
return context
|
|
|
|
def get_informations_from_rank(self):
|
|
"""
|
|
Calcule toutes les statistiques des skills par rapport au rang du gymnaste.
|
|
"""
|
|
|
|
context = {}
|
|
# min_rank_skill = self.min_rank_skill()
|
|
max_rank_skill = self.max_rank_skill()
|
|
context["max_rank_skill"] = max_rank_skill
|
|
nb_known_skill_by_rank = self.nb_known_skill_by_rank()
|
|
gymnast_nb_known_skills = self.known_skills.distinct("skill").count()
|
|
|
|
if max_rank_skill > 0:
|
|
cpt_skill_by_rank = nb_skill_by_type(max_rank_skill, "rank")
|
|
context["total_skill"] = nb_skill_lte_type(max_rank_skill, "rank")
|
|
context["percentages"] = compute_statistics_by_type(
|
|
cpt_skill_by_rank, nb_known_skill_by_rank, "rank"
|
|
)
|
|
context["skill_by_rank"] = self.unknown_skill_lte_rank(max_rank_skill)
|
|
context["unknown_skill"] = self.unknown_skill_gt_rank(max_rank_skill)
|
|
else:
|
|
tmp = Skill.objects.all()
|
|
context["unknown_skill"] = tmp
|
|
context["total_skill"] = tmp.count()
|
|
|
|
context["completude"], context["evaluated_level"] = compute_completude(
|
|
context["total_skill"], gymnast_nb_known_skills, max_rank_skill
|
|
)
|
|
|
|
return context
|