344 lines
12 KiB
Python
344 lines
12 KiB
Python
from django.db import models
|
|
from django.db.models import Q, Count, Min
|
|
|
|
import pendulum
|
|
from datetime import date
|
|
|
|
from ultron.location.models import Club
|
|
from ultron.objective.models import Skill
|
|
from ultron.tools.models import Markdownizable
|
|
|
|
|
|
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"))
|
|
|
|
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"
|
|
)
|
|
trainings_by_week = models.PositiveSmallIntegerField(
|
|
verbose_name="# Training by week"
|
|
)
|
|
hours_by_week = models.PositiveSmallIntegerField(verbose_name="# Hours by week")
|
|
|
|
def __str__(self):
|
|
return u"%s, %s" % (self.last_name, self.first_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 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):
|
|
"""
|
|
Renvoie la liste des skill inférieurs ou égaux au niveau max que le gymnaste ne sait *pas* faire.
|
|
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_level_skill).exclude(
|
|
known_by__gymnast=self.id
|
|
)
|
|
|
|
def unknown_skill_gt_level(self, max_level_skill):
|
|
"""
|
|
Renvoie la liste des skill inférieurs ou égaux au niveau max que le gymnaste ne sait *pas* faire.
|
|
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
|
|
)
|
|
|
|
@staticmethod
|
|
def compute_completude(total_skill, gymnast_nb_known_skills, max_skill):
|
|
"""
|
|
Calcule la complétude et le niveau estimé du gymnaste
|
|
"""
|
|
if total_skill:
|
|
completude = "%s%%" % (int((gymnast_nb_known_skills / total_skill) * 100))
|
|
evaluated_level = int(max_skill * (gymnast_nb_known_skills / total_skill))
|
|
else:
|
|
completude = 0
|
|
evaluated_level = 0
|
|
return completude, evaluated_level
|
|
|
|
@staticmethod
|
|
def compute_level_statistics(nb_skill_by_level, nb_known_skill_by_level):
|
|
"""
|
|
(A COMPLETER QUAND J'AURAI COMPRIS CE QUE CA FAIT)
|
|
"""
|
|
j = 0
|
|
percentages = []
|
|
for skill in nb_skill_by_level:
|
|
tmp = None
|
|
if skill["level"] == nb_known_skill_by_level[j]["level"]:
|
|
if skill["nb_skill"] != nb_known_skill_by_level[j]["nb_known_skill"]:
|
|
tmp = (
|
|
skill["level"],
|
|
skill["nb_skill"],
|
|
nb_known_skill_by_level[j]["nb_known_skill"],
|
|
int(
|
|
(
|
|
nb_known_skill_by_level[j]["nb_known_skill"]
|
|
/ skill["nb_skill"]
|
|
)
|
|
* 100
|
|
),
|
|
)
|
|
|
|
j += 1
|
|
else:
|
|
tmp = (skill["level"], skill["nb_skill"], 0, 0)
|
|
|
|
if tmp:
|
|
percentages.append(tmp)
|
|
|
|
return percentages
|
|
|
|
@staticmethod
|
|
def compute_rank_statistics(nb_skill_by_rank, nb_known_skill_by_rank):
|
|
"""
|
|
(A COMPLETER QUAND J'AURAI COMPRIS CE QUE CA FAIT)
|
|
"""
|
|
j = 0
|
|
percentages = []
|
|
for skill in nb_skill_by_rank:
|
|
# les deux lignes ci-dessous doivent partir
|
|
if j >= nb_known_skill_by_rank.count():
|
|
break
|
|
|
|
tmp = None
|
|
if skill["rank"] == nb_known_skill_by_rank[j]["rank"]:
|
|
if skill["nb_skill"] != nb_known_skill_by_rank[j]["nb_known_skill"]:
|
|
tmp = (
|
|
skill["rank"],
|
|
skill["nb_skill"],
|
|
nb_known_skill_by_rank[j]["nb_known_skill"],
|
|
int(
|
|
(
|
|
nb_known_skill_by_rank[j]["nb_known_skill"]
|
|
/ skill["nb_skill"]
|
|
)
|
|
* 100
|
|
),
|
|
)
|
|
|
|
j += 1
|
|
else:
|
|
tmp = (skill["rank"], skill["nb_skill"], 0, 0)
|
|
|
|
if tmp:
|
|
percentages.append(tmp)
|
|
|
|
return percentages
|
|
|
|
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:
|
|
nb_skill_by_level = Skill.nb_skill_by_level(max_level_skill)
|
|
context["total_skill"] = Skill.nb_skill_lte_level(max_level_skill)
|
|
context["percentages"] = self.compute_level_statistics(
|
|
nb_skill_by_level, nb_known_skill_by_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"] = self.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.known_skill_by_rank()
|
|
gymnast_nb_known_skills = self.known_skills.distinct("skill").count()
|
|
|
|
if max_rank_skill > 0:
|
|
nb_skill_by_rank = Skill.nb_skill_by_rank(max_rank_skill)
|
|
context["total_skill"] = Skill.nb_skill_lte_rank(max_rank_skill)
|
|
context["percentages"] = self.compute_rank_statistics(
|
|
nb_skill_by_rank, nb_known_skill_by_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"] = self.compute_completude(
|
|
context["total_skill"], gymnast_nb_known_skills, max_rank_skill
|
|
)
|
|
|
|
return context
|