Update models

This commit is contained in:
Gregory Trullemans 2024-03-15 14:01:27 +01:00
parent e57e414f27
commit 6a63776145
8 changed files with 628 additions and 593 deletions

View File

@ -9,10 +9,10 @@ from django_admin_listfilter_dropdown.filters import (
from .models import ( from .models import (
TouchPosition, TouchPosition,
Skill, Skill,
Passe, # Passe,
Routine, Routine,
RoutineSkill, RoutineSkill,
TrainingProgram, # TrainingProgram,
PrerequisiteClosure, PrerequisiteClosure,
) )
@ -194,57 +194,57 @@ class PrerequisiteClosureAdmin(admin.ModelAdmin):
) )
@admin.register(Passe) # @admin.register(Passe)
class PasseAdmin(admin.ModelAdmin): # class PasseAdmin(admin.ModelAdmin):
model = Passe # model = Passe
fields = ( # fields = (
"label", # "label",
"educatives", # "educatives",
"regexp", # "regexp",
"number_of_skill", # "number_of_skill",
"difficulty", # "difficulty",
"informations", # "informations",
) # )
list_display = ("label", "regexp", "number_of_skill", "difficulty") # list_display = ("label", "regexp", "number_of_skill", "difficulty")
list_filter = ( # list_filter = (
("number_of_skill", DropdownFilter), # ("number_of_skill", DropdownFilter),
("difficulty", DropdownFilter), # ("difficulty", DropdownFilter),
) # )
filter_horizontal = ("educatives",) # filter_horizontal = ("educatives",)
class Media: # class Media:
js = ( # js = (
"js/core/jquery-3.6.0.min.js", # "js/core/jquery-3.6.0.min.js",
"js/admin/passe.js", # "js/admin/passe.js",
) # )
@admin.register(TrainingProgram) # @admin.register(TrainingProgram)
class TrainingProgramAdmin(admin.ModelAdmin): # class TrainingProgramAdmin(admin.ModelAdmin):
model = TrainingProgram # model = TrainingProgram
fields = ( # fields = (
"gymnast", # "gymnast",
"date", # "date",
"rank", # "rank",
"passe", # "passe",
"repetition", # "repetition",
"number_of_skill", # "number_of_skill",
"difficulty", # "difficulty",
# "score", # # "score",
) # )
list_display = ( # list_display = (
"date", # "date",
"gymnast", # "gymnast",
"passe", # "passe",
"rank", # "rank",
"repetition", # "repetition",
"number_of_skill", # "number_of_skill",
"difficulty", # "difficulty",
) # )
list_filter = ( # list_filter = (
("gymnast", RelatedDropdownFilter), # ("gymnast", RelatedDropdownFilter),
("date", DropdownFilter), # ("date", DropdownFilter),
) # )

View File

@ -9,7 +9,7 @@ from .models import (
Skill, Skill,
Routine, Routine,
RoutineSkill, RoutineSkill,
Passe, # Passe,
) )
@ -74,87 +74,87 @@ class CombinationSkillForm(forms.ModelForm):
} }
class PasseForm(forms.ModelForm): # class PasseForm(forms.ModelForm):
educatives = forms.ModelMultipleChoiceField( # educatives = forms.ModelMultipleChoiceField(
required=False, # required=False,
queryset=Educative.objects.all(), # queryset=Educative.objects.all(),
widget=FilteredSelectMultiple("Educatives", is_stacked=False), # widget=FilteredSelectMultiple("Educatives", is_stacked=False),
# widget=customFilteredSelectMultiple(verbose_name='test2',is_stacked=False) # # widget=customFilteredSelectMultiple(verbose_name='test2',is_stacked=False)
) # )
class Media: # class Media:
css = { # css = {
"all": ["admin/css/widgets.css"], # "all": ["admin/css/widgets.css"],
} # }
# Adding this javascript is crucial # # Adding this javascript is crucial
js = ["/admin/jsi18n/"] # js = ["/admin/jsi18n/"]
class Meta: # class Meta:
model = Passe # model = Passe
fields = ("label", "regexp", "educatives", "informations") # fields = ("label", "regexp", "educatives", "informations")
widgets = { # widgets = {
"label": forms.TextInput( # "label": forms.TextInput(
attrs={ # attrs={
"class": "form-control", # "class": "form-control",
"placeholder": "Label (not mandatory)", # "placeholder": "Label (not mandatory)",
"maxlength": 30, # "maxlength": 30,
} # }
), # ),
"regexp": forms.TextInput( # "regexp": forms.TextInput(
attrs={"class": "form-control", "placeholder": "[2-8]"} # attrs={"class": "form-control", "placeholder": "[2-8]"}
), # ),
"informations": forms.Textarea( # "informations": forms.Textarea(
attrs={ # attrs={
"class": "form-control", # "class": "form-control",
"placeholder": "Informations about the passe…", # pylint: disable=line-too-long # "placeholder": "Informations about the passe…", # pylint: disable=line-too-long
} # }
), # ),
} # }
def clean_regexp(self): # def clean_regexp(self):
"""Vérifie que la regexp entrée par l'utilisateur est valide.""" # """Vérifie que la regexp entrée par l'utilisateur est valide."""
regexp = self.cleaned_data["regexp"] # regexp = self.cleaned_data["regexp"]
if not Passe.is_valid_regexp(regexp): # if not Passe.is_valid_regexp(regexp):
raise ValidationError("Entered regexp not valid.") # raise ValidationError("Entered regexp not valid.")
return regexp # return regexp
def clean(self): # def clean(self):
"""Vérifie le contenu des champs `educatives` par rapport à la valeur de `regexp`. Si # """Vérifie le contenu des champs `educatives` par rapport à la valeur de `regexp`. Si
`regexp` est définie par : # `regexp` est définie par :
- valeurs de ROUTINE_TYPE_CHOICE il faut que Educatives soit VIDE # - valeurs de ROUTINE_TYPE_CHOICE il faut que Educatives soit VIDE
- avec [x-y] # - avec [x-y]
- [x-y] " Educatives soit NON VIDE # - [x-y] " Educatives soit NON VIDE
- WC " il y ait 2+ Educatives # - WC " il y ait 2+ Educatives
- x| " il y ait 1! Educatives # - x| " il y ait 1! Educatives
""" # """
cleaned_data = super().clean() # cleaned_data = super().clean()
regexp = ["regexp"] # regexp = ["regexp"]
if regexp is not None: # if regexp is not None:
arguments = regexp.split(" ") # arguments = regexp.split(" ")
educatives = cleaned_data["educatives"] # educatives = cleaned_data["educatives"]
if Passe.is_valid_routine_type(arguments[0]) and educatives is not None: # if Passe.is_valid_routine_type(arguments[0]) and educatives is not None:
raise ValidationError( # raise ValidationError(
"Educatives must be empty with the entered Regexp." # "Educatives must be empty with the entered Regexp."
) # )
if Passe.is_valid_subset(arguments[0]) and educatives is None: # if Passe.is_valid_subset(arguments[0]) and educatives is None:
raise ValidationError( # raise ValidationError(
"Educatives can't be empty with the entered Regexp." # "Educatives can't be empty with the entered Regexp."
) # )
if re.match(r"[1-9]+\|", arguments[0]) and len(educatives) != 1: # if re.match(r"[1-9]+\|", arguments[0]) and len(educatives) != 1:
raise ValidationError( # raise ValidationError(
"One and only one Educatives allowed with the entered Regexp." # "One and only one Educatives allowed with the entered Regexp."
) # )
if arguments[0] == "WC" and (educatives is None or len(educatives) < 2): # if arguments[0] == "WC" and (educatives is None or len(educatives) < 2):
raise ValidationError( # raise ValidationError(
"At least two Educatives with the entered Regexp." # "At least two Educatives with the entered Regexp."
) # )
return cleaned_data # return cleaned_data

View File

@ -0,0 +1,31 @@
# Generated by Django 4.2 on 2024-03-13 11:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("objective", "0021_alter_passe_educatives"),
]
operations = [
migrations.AlterUniqueTogether(
name="trainingprogram",
unique_together=None,
),
migrations.RemoveField(
model_name="trainingprogram",
name="gymnast",
),
migrations.RemoveField(
model_name="trainingprogram",
name="passe",
),
migrations.DeleteModel(
name="Passe",
),
migrations.DeleteModel(
name="TrainingProgram",
),
]

View File

@ -466,299 +466,281 @@ class RoutineSkill(models.Model):
return f"{self.rank} - {self.routine.short_label} : {self.skill.short_label}" return f"{self.rank} - {self.routine.short_label} : {self.skill.short_label}"
class Passe(Markdownizable): # class TrainingRound(Markdownizable):
"""Classe représentant les passages (à faire pendant un entraînement).""" # """Classe représentant les passages (à faire pendant un entraînement)."""
class Meta: # class Meta:
ordering = [ # ordering = [
"label", # "label",
] # ]
label = models.CharField(max_length=30) # label = models.CharField(max_length=30)
educatives = models.ManyToManyField( # educatives = models.ManyToManyField(
Educative, related_name="passes", blank=True, symmetrical=False # Educative, related_name="passes", blank=True, symmetrical=False
)
regexp = models.CharField(max_length=50, null=True, blank=True)
number_of_skill = models.PositiveSmallIntegerField(default=0)
difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
# TODO: number_of_skill doit être calculé correctement dans tous les cas.
def save(self, *args, **kwargs):
"""Sauve les informations de la personne et initialise les champs nettoyés.
On part du principe que self.regexp est correct.
"""
self.difficulty = 0
self.number_of_skill = 0
super().save(*args, **kwargs)
# print("Dans le save")
if self.educatives.count() == 0:
# print("educative is none")
present = False
operation_list = self.regexp.split(" ")
for item in ROUTINE_TYPE_CHOICE:
if item[1] == operation_list[0]:
present = True
break
if present and len(operation_list) == 2:
# print("present")
content = operation_list[1].replace("[", "").replace("]", "")
ranks = content.split("-")
if ranks[0] == "":
self.number_of_skill += int(ranks[1])
elif ranks[1] == "":
self.number_of_skill += (10 - int(ranks[0])) + 1
else:
self.number_of_skill += (int(ranks[1]) - int(ranks[0])) + 1
else:
self.number_of_skill += 10
else:
for educative in self.educatives.all():
is_skill = False
try:
educative = Routine.objects.get(pk=educative)
except Routine.DoesNotExist:
educative = Skill.objects.get(pk=educative)
is_skill = True
if is_skill:
self.difficulty += educative.difficulty
self.number_of_skill += 1
else:
if self.regexp is not None:
regexp = self.regexp.replace("[", "").replace("]", "")
position = regexp.find("-")
start = regexp[:position]
if start == "":
start = 0
else:
start = int(start)
end = regexp[position + 1 :]
if end == "":
end = educative.jumps.all().count()
else:
end = int(end)
self.number_of_skill += end - (start - 1)
list_of_skill = educative.skill_links.filter(
rank__gte=start, rank__lte=end
)
# .aggregate(total=Sum("value"))
tmp_difficulty = 0
for routine_skill in list_of_skill:
tmp_difficulty += routine_skill.skill.difficulty
self.difficulty += tmp_difficulty
else:
self.number_of_skill += educative.jumps.all().count()
self.difficulty += educative.difficulty
super().save(*args, **kwargs)
def __str__(self):
return f"{self.label} ({self.number_of_skill} | {self.difficulty})"
@staticmethod
def is_valid_regexp_one_arg(arg):
"""Vérifie une regexp avec un paramètre."""
if arg == "WC":
return True
if re.match(r"[1-9]+\|", arg):
return True
if not Passe.is_valid_dot(arg):
return False
value = arg.replace(".", "")
is_valid_routine = Passe.is_valid_routine_type(value)
if is_valid_routine:
return True
return Passe.is_valid_subset(arg)
@staticmethod
def is_valid_regexp_two_args(arg1, arg2):
"""Vérifie une regexp avec deux paramètres."""
if not Passe.is_valid_dot(arg1):
return False
value = arg1.replace(".", "")
is_valid_routine = Passe.is_valid_routine_type(value)
if is_valid_routine:
return Passe.is_valid_subset(arg2)
return False
@staticmethod
def is_valid_dot(pattern):
"""Reçoit une chaine de caratère et vérifie que si elle contient un point (.), il se trouve
soit à la première position soit à la dernière position.
"""
if len(re.findall("\.", pattern)) > 1:
return False
if re.search("\.", pattern):
last_place = len(pattern) - 1
if pattern[0] != "." and pattern[last_place] != ".":
return False
return True
@staticmethod
def is_valid_routine_type(routine_type):
"""Recoit une chaine de caractère et vérifie si elle est présente dans la liste
ROUTINE_TYPE_CHOICE (Educative vide !)
"""
is_valid = False
for item in ROUTINE_TYPE_CHOICE:
if item[1] == routine_type:
is_valid = True
break
return is_valid
@staticmethod
def is_valid_subset(subset):
"""Reçoit la description d'un subset sous forme de string et vérifie qu'elle est conforme.
Format attendu : [X-Y]
X ou Y peuvent être vide mais pas en même temps.
X est un entier >= 2
Y est un entier >= 2 OU Y > X si X est non vide
Exemples :
- [2-8] True
- [-5] True
- [3-] True
- [8-2] False
- [4--8] False
- [-] False
- [1-] False
- [-1] False
- [4] False
- [6-6] False
"""
if re.match(r"^\[(([2-9]+\-{1})|([2-9]+\-{1}[2-9]+)|(\-{1}[2-9]+))\]$", subset):
value = subset.replace("[", "").replace("]", "")
ranks = value.split("-")
if ranks[0] == "" or ranks[1] == "":
return True
if int(ranks[0]) < int(ranks[1]):
return True
return False
@staticmethod
def is_valid_regexp(regexp):
"""Vérifie le champ regexp
Exemples :
- Q1R1 True
- Q1R2 [2-8] True
- Q2R1 [-5] True
- SF [6-] True
- FS [3-7] True
- Q1R1. True
- .Q1R2 True
- Q1R1. [-4] True
- .Q1R2 [4-] True
- .FS [3-7] True
- [2-8] True
- [-5] True
- WC True
- 1| True
"""
argument_list = regexp.split(" ")
if len(argument_list) >= 3:
return False
if len(argument_list) == 2:
return Passe.is_valid_regexp_two_args(argument_list[0], argument_list[1])
else:
return Passe.is_valid_regexp_one_arg(argument_list[0])
return False
class TrainingProgram(Seasonisable, Markdownizable):
"""Classe représentant ?????
TODO:
- renommer (supprimer/remettre) TrainingProgram en TrainingPasse
- supprimer Seasonisable
- supprimer Markdownizable
- supprimer le champ Gymnast
- supprimer score
- supprimer rank ??? (--> dans la M2M)
- supprimer difficulty ??? (--> dans la M2M)
"""
class Meta:
verbose_name = "Training Program"
verbose_name_plural = "Trainings Programs"
ordering = [
"rank",
]
unique_together = ["date", "gymnast", "rank"]
gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) # TO DELETE
passe = models.ForeignKey(Passe, on_delete=models.CASCADE)
repetition = models.PositiveSmallIntegerField(default=1)
number_of_skill = models.PositiveSmallIntegerField(default=0)
difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
rank = models.PositiveSmallIntegerField(default=1) # TO DELETE
score = models.PositiveSmallIntegerField(blank=True, null=True) # TO DELETE
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return (
f"{self.gymnast} {self.date} - {self.rank} : {self.passe} {self.repetition}"
)
def save(self, *args, **kwargs):
"""Sauve les informations de la personne et initialise les champs nettoyés."""
super().save(*args, **kwargs)
self.difficulty = self.passe.difficulty * self.repetition
self.number_of_skill = self.passe.number_of_skill * self.repetition
super().save(*args, **kwargs)
# class TrainingPasseLink(models.Model):
# """Modèle M2M entre Training et TrainingPasse."""
# training = models.ForeignKey(
# Training,
# on_delete=models.CASCADE,
# default=None,
# related_name="program_passe_links",
# ) # )
# program_passe = models.ForeignKey( # regexp = models.CharField(max_length=50, null=True, blank=True)
# TrainingPasse,
# on_delete=models.CASCADE,
# default=None,
# related_name="training_links",
# )
# rank = models.PositiveSmallIntegerField(default=1)
# number_of_skill = models.PositiveSmallIntegerField(default=0) # number_of_skill = models.PositiveSmallIntegerField(default=0)
# difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0) # difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
# score = models.PositiveSmallIntegerField(blank=True, null=True) # # TODO: number_of_skill doit être calculé correctement dans tous les cas.
# def save(self, *args, **kwargs):
# """Sauve les informations de la personne et initialise les champs nettoyés.
# On part du principe que self.regexp est correct.
# """
# self.difficulty = 0
# self.number_of_skill = 0
# super().save(*args, **kwargs)
# # print("Dans le save")
# if self.educatives.count() == 0:
# # print("educative is none")
# present = False
# operation_list = self.regexp.split(" ")
# for item in ROUTINE_TYPE_CHOICE:
# if item[1] == operation_list[0]:
# present = True
# break
# if present and len(operation_list) == 2:
# # print("present")
# content = operation_list[1].replace("[", "").replace("]", "")
# ranks = content.split("-")
# if ranks[0] == "":
# self.number_of_skill += int(ranks[1])
# elif ranks[1] == "":
# self.number_of_skill += (10 - int(ranks[0])) + 1
# else:
# self.number_of_skill += (int(ranks[1]) - int(ranks[0])) + 1
# else:
# self.number_of_skill += 10
# else:
# for educative in self.educatives.all():
# is_skill = False
# try:
# educative = Routine.objects.get(pk=educative)
# except Routine.DoesNotExist:
# educative = Skill.objects.get(pk=educative)
# is_skill = True
# if is_skill:
# self.difficulty += educative.difficulty
# self.number_of_skill += 1
# else:
# if self.regexp is not None:
# regexp = self.regexp.replace("[", "").replace("]", "")
# position = regexp.find("-")
# start = regexp[:position]
# if start == "":
# start = 0
# else:
# start = int(start)
# end = regexp[position + 1 :]
# if end == "":
# end = educative.jumps.all().count()
# else:
# end = int(end)
# self.number_of_skill += end - (start - 1)
# list_of_skill = educative.skill_links.filter(
# rank__gte=start, rank__lte=end
# )
# # .aggregate(total=Sum("value"))
# tmp_difficulty = 0
# for routine_skill in list_of_skill:
# tmp_difficulty += routine_skill.skill.difficulty
# self.difficulty += tmp_difficulty
# else:
# self.number_of_skill += educative.jumps.all().count()
# self.difficulty += educative.difficulty
# super().save(*args, **kwargs)
# def __str__(self):
# return f"{self.label} ({self.number_of_skill} | {self.difficulty})"
# @staticmethod
# def is_valid_regexp_one_arg(arg):
# """Vérifie une regexp avec un paramètre."""
# if arg == "WC":
# return True
# if re.match(r"[1-9]+\|", arg):
# return True
# if not TrainingRound.is_valid_dot(arg):
# return False
# value = arg.replace(".", "")
# is_valid_routine = TrainingRound.is_valid_routine_type(value)
# if is_valid_routine:
# return True
# return TrainingRound.is_valid_subset(arg)
# @staticmethod
# def is_valid_regexp_two_args(arg1, arg2):
# """Vérifie une regexp avec deux paramètres."""
# if not TrainingRound.is_valid_dot(arg1):
# return False
# value = arg1.replace(".", "")
# is_valid_routine = TrainingRound.is_valid_routine_type(value)
# if is_valid_routine:
# return TrainingRound.is_valid_subset(arg2)
# return False
# @staticmethod
# def is_valid_dot(pattern):
# """Reçoit une chaine de caratère et vérifie que si elle contient un point (.), il se trouve
# soit à la première position soit à la dernière position.
# """
# if len(re.findall("\.", pattern)) > 1:
# return False
# if re.search("\.", pattern):
# last_place = len(pattern) - 1
# if pattern[0] != "." and pattern[last_place] != ".":
# return False
# return True
# @staticmethod
# def is_valid_routine_type(routine_type):
# """Recoit une chaine de caractère et vérifie si elle est présente dans la liste
# ROUTINE_TYPE_CHOICE (Educative vide !)
# """
# is_valid = False
# for item in ROUTINE_TYPE_CHOICE:
# if item[1] == routine_type:
# is_valid = True
# break
# return is_valid
# @staticmethod
# def is_valid_subset(subset):
# """Reçoit la description d'un subset sous forme de string et vérifie qu'elle est conforme.
# Format attendu : [X-Y]
# X ou Y peuvent être vide mais pas en même temps.
# X est un entier >= 2
# Y est un entier >= 2 OU Y > X si X est non vide
# Exemples :
# - [2-8] True
# - [-5] True
# - [3-] True
# - [8-2] False
# - [4--8] False
# - [-] False
# - [1-] False
# - [-1] False
# - [4] False
# - [6-6] False
# """
# if re.match(r"^\[(([2-9]+\-{1})|([2-9]+\-{1}[2-9]+)|(\-{1}[2-9]+))\]$", subset):
# value = subset.replace("[", "").replace("]", "")
# ranks = value.split("-")
# if ranks[0] == "" or ranks[1] == "":
# return True
# if int(ranks[0]) < int(ranks[1]):
# return True
# return False
# @staticmethod
# def is_valid_regexp(regexp):
# """Vérifie le champ regexp
# Exemples :
# - Q1R1 True
# - Q1R2 [2-8] True
# - Q2R1 [-5] True
# - SF [6-] True
# - FS [3-7] True
# - Q1R1. True
# - .Q1R2 True
# - Q1R1. [-4] True
# - .Q1R2 [4-] True
# - .FS [3-7] True
# - [2-8] True
# - [-5] True
# - WC True
# - 1| True
# """
# argument_list = regexp.split(" ")
# if len(argument_list) >= 3:
# return False
# if len(argument_list) == 2:
# return TrainingRound.is_valid_regexp_two_args(
# argument_list[0], argument_list[1]
# )
# else:
# return TrainingRound.is_valid_regexp_one_arg(argument_list[0])
# return False
# class Training(Seasonisable, Markdownizable): # class GymnastTraining(Seasonisable, Markdownizable):
# """Classe représentant un entraînement.""" # """Classe représentant un entraînement."""
# label = models.CharField()
# gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) # gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE)
# training_rounds = models.ManyToManyField(
# TrainingRound, through="GymnastTrainingRound", verbose_name="trainings"
# )
# difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0) # difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
# number_of_skill = models.PositiveSmallIntegerField(default=0) # number_of_skill = models.PositiveSmallIntegerField(default=0)
# score = models.PositiveSmallIntegerField(default=1) # score = models.PositiveSmallIntegerField(default=1)
# def __str__(self):
# return f"Training on {self.date} for {self.gymnast}: {self.number_of_skill} | {self.difficulty}"
# class GymnastTrainingRound(Markdownizable):
# """Classe représentant un passage."""
# class Meta:
# verbose_name = "Gymnast Training Round"
# verbose_name_plural = "Gymnast Training Rounds"
# ordering = [
# "rank",
# ]
# unique_together = ["gymnast_training", "rank"]
# gymnast_training = models.ForeignKey(
# GymnastTraining,
# on_delete=models.CASCADE,
# related_name="training_rounds",
# )
# training_round = models.ForeignKey(TrainingRound, on_delete=models.CASCADE)
# repetition = models.PositiveSmallIntegerField(default=1)
# rank = models.PositiveSmallIntegerField(default=1)
# updated_at = models.DateTimeField(auto_now=True)
# number_of_skill = models.PositiveSmallIntegerField(default=0)
# score = models.PositiveSmallIntegerField(blank=True, null=True)
# difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
# def __str__(self):
# return (
# f"{self.gymnast} {self.date} - {self.rank} : {self.passe} {self.repetition}"
# )
# def save(self, *args, **kwargs):
# """Sauve les informations de la personne et initialise les champs nettoyés."""
# super().save(*args, **kwargs)
# self.difficulty = self.round.difficulty * self.repetition
# self.number_of_skill = self.round.number_of_skill * self.repetition
# super().save(*args, **kwargs)

View File

@ -92,33 +92,33 @@ urlpatterns = [
# #
# PASSES # PASSES
# #
path( # path(
r"passe/<int:passe_id>/gymnast/<int:gymnast_id>/date/<str:date>/", # r"passe/<int:passe_id>/gymnast/<int:gymnast_id>/date/<str:date>/",
views.passe_details, # views.passe_details,
name="passe_details", # name="passe_details",
), # ),
path(r"passe/", views.passe_listing, name="passe_listing"), # path(r"passe/", views.passe_listing, name="passe_listing"),
path( # path(
r"passe/add/", # r"passe/add/",
views.passe_create_or_update, # views.passe_create_or_update,
name="passe_create", # name="passe_create",
), # ),
path( # path(
r"passe/edit/<int:passe_id>/", # r"passe/edit/<int:passe_id>/",
views.passe_create_or_update, # views.passe_create_or_update,
name="passe_update", # name="passe_update",
), # ),
# #
# TRAININGPROGRAM # TRAININGPROGRAM
# #
path( # path(
r"trainingprogram/detail/date/<str:date>/gymnast/<int:gymnast_id>/", # r"trainingprogram/detail/date/<str:date>/gymnast/<int:gymnast_id>/",
views.trainingprogram_details, # views.trainingprogram_details,
name="trainingprogram_details", # name="trainingprogram_details",
), # ),
path( # path(
r"trainingprogram/switch_trainingprogram_line/", # r"trainingprogram/switch_trainingprogram_line/",
views.switch_trainingprogram_line, # views.switch_trainingprogram_line,
name="switch_trainingprogram_line", # name="switch_trainingprogram_line",
), # ),
] ]

View File

@ -13,17 +13,17 @@ from jarvis.followup.models import GymnastHasRoutine
from .forms import ( from .forms import (
SkillForm, SkillForm,
PasseForm, # PasseForm,
CombinationForm, CombinationForm,
CombinationSkillForm, CombinationSkillForm,
) )
from .models import ( from .models import (
Skill, Skill,
Passe, # Passe,
Routine, Routine,
Educative, Educative,
RoutineSkill, RoutineSkill,
TrainingProgram, # TrainingProgram,
PrerequisiteClosure, PrerequisiteClosure,
) )
@ -512,189 +512,189 @@ def unlink_skill_from_combination(request):
return HttpResponse(200) return HttpResponse(200)
@login_required # @login_required
@require_http_methods(["GET"]) # @require_http_methods(["GET"])
def passe_listing(request): # def passe_listing(request):
"""Liste des passages.""" # """Liste des passages."""
passe_listing = Passe.objects.all() # passe_listing = Passe.objects.all()
context = {"passe_listing": passe_listing} # context = {"passe_listing": passe_listing}
return render(request, "passes/list.html", context) # return render(request, "passes/list.html", context)
@login_required # @login_required
@require_http_methods(["GET"]) # @require_http_methods(["GET"])
def passe_details(request, passe_id, gymnast_id, date): # def passe_details(request, passe_id, gymnast_id, date):
"""Détails d'un passage.""" # """Détails d'un passage."""
is_skill = False # is_skill = False
passe = get_object_or_404(Passe, pk=passe_id) # passe = get_object_or_404(Passe, pk=passe_id)
educative_list = passe.educatives.all() # educative_list = passe.educatives.all()
# TODO: décryptage de la regexp # # TODO: décryptage de la regexp
regexp = passe.regexp # regexp = passe.regexp
routine = None # routine = None
skill_link_list = None # skill_link_list = None
if regexp is not None: # if regexp is not None:
operation_list = regexp.split(" ") # operation_list = regexp.split(" ")
routine_type = None # routine_type = None
for item in ROUTINE_TYPE_CHOICE: # for item in ROUTINE_TYPE_CHOICE:
if item[1] == operation_list[0]: # if item[1] == operation_list[0]:
routine_type = item[0] # routine_type = item[0]
break # break
if routine_type is not None: # if routine_type is not None:
# Récupération de la série # # Récupération de la série
ghr = GymnastHasRoutine.objects.filter( # ghr = GymnastHasRoutine.objects.filter(
gymnast=gymnast_id, # gymnast=gymnast_id,
date_begin__lte=date, # date_begin__lte=date,
# date_end__gte=date, # # date_end__gte=date,
routine_type=routine_type, # routine_type=routine_type,
) # )
print(ghr.query) # print(ghr.query)
if ghr.count() > 1: # if ghr.count() > 1:
print("Plus d'une série trouvée...") # print("Plus d'une série trouvée...")
print(ghr) # print(ghr)
routine = ghr.first().routine # routine = ghr.first().routine
skill_link_list = routine.skill_links.all() # skill_link_list = routine.skill_links.all()
if len(operation_list) == 2: # if len(operation_list) == 2:
content = operation_list[1].replace("[", "").replace("]", "") # content = operation_list[1].replace("[", "").replace("]", "")
ranks = content.split("-") # ranks = content.split("-")
if ranks[0] != "": # if ranks[0] != "":
skill_link_list = skill_link_list.filter(rank__gte=ranks[0]) # skill_link_list = skill_link_list.filter(rank__gte=ranks[0])
if ranks[1] != "": # if ranks[1] != "":
skill_link_list = skill_link_list.filter(rank__lte=ranks[1]) # skill_link_list = skill_link_list.filter(rank__lte=ranks[1])
number_of_educative = skill_link_list.count() # number_of_educative = skill_link_list.count()
print(number_of_educative) # print(number_of_educative)
else: # else:
number_of_educative = educative_list.count() # number_of_educative = educative_list.count()
context = { # context = {
"passe": passe, # "passe": passe,
"is_skill": is_skill, # "is_skill": is_skill,
"educative_list": educative_list, # "educative_list": educative_list,
"routine": routine, # "routine": routine,
"skill_link_list": skill_link_list, # "skill_link_list": skill_link_list,
"difficulty": passe.difficulty, # "difficulty": passe.difficulty,
"number_of_skill": passe.number_of_skill, # "number_of_skill": passe.number_of_skill,
"number_of_educative": number_of_educative, # "number_of_educative": number_of_educative,
} # }
return render(request, "passes/details.html", context) # return render(request, "passes/details.html", context)
@login_required # @login_required
@require_http_methods(["GET", "POST"]) # @require_http_methods(["GET", "POST"])
def passe_create_or_update(request, passe_id=None): # def passe_create_or_update(request, passe_id=None):
"""Création d'un passage. # """Création d'un passage.
Args: # Args:
passe_id (int) identifiant d'un object de classe <Passe>. # passe_id (int) identifiant d'un object de classe <Passe>.
""" # """
if passe_id: # if passe_id:
passe = get_object_or_404(Passe, pk=passe_id) # passe = get_object_or_404(Passe, pk=passe_id)
else: # else:
passe = None # passe = None
if request.method == "POST": # if request.method == "POST":
form = PasseForm(request.POST, instance=passe) # form = PasseForm(request.POST, instance=passe)
if form.is_valid(): # if form.is_valid():
passe = form.save() # passe = form.save()
return HttpResponseRedirect(reverse("passe_details", args=(passe.pk,))) # return HttpResponseRedirect(reverse("passe_details", args=(passe.pk,)))
return render(request, "passes/create.html", {"form": form}) # return render(request, "passes/create.html", {"form": form})
form = PasseForm(instance=passe) # form = PasseForm(instance=passe)
context = {"form": form, "passe_id": passe_id} # context = {"form": form, "passe_id": passe_id}
return render(request, "passes/create.html", context) # return render(request, "passes/create.html", context)
@login_required # @login_required
@require_http_methods(["GET"]) # @require_http_methods(["GET"])
def trainingprogram_details(request, date=None, gymnast_id=None): # def trainingprogram_details(request, date=None, gymnast_id=None):
"""Détails d'un entraînement.""" # """Détails d'un entraînement."""
gymnast = None # gymnast = None
trainingprogram_id = None # trainingprogram_id = None
if trainingprogram_id is not None: # if trainingprogram_id is not None:
trainingprogram = get_object_or_404(TrainingProgram, pk=trainingprogram_id) # trainingprogram = get_object_or_404(TrainingProgram, pk=trainingprogram_id)
trainingprogram_list = None # trainingprogram_list = None
else: # else:
trainingprogram = None # trainingprogram = None
trainingprogram_list = TrainingProgram.objects.all() # trainingprogram_list = TrainingProgram.objects.all()
parsed_date = pendulum.parse(date).date() # parsed_date = pendulum.parse(date).date()
if date is not None: # if date is not None:
trainingprogram_list = trainingprogram_list.filter(date=parsed_date) # trainingprogram_list = trainingprogram_list.filter(date=parsed_date)
if gymnast_id is not None: # if gymnast_id is not None:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id) # gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
trainingprogram_list = trainingprogram_list.filter(gymnast=gymnast_id) # trainingprogram_list = trainingprogram_list.filter(gymnast=gymnast_id)
difficulty = 0 # difficulty = 0
number_of_skill = 0 # number_of_skill = 0
for trainingprogram in trainingprogram_list: # for trainingprogram in trainingprogram_list:
difficulty += trainingprogram.difficulty # difficulty += trainingprogram.difficulty
number_of_skill += trainingprogram.number_of_skill # number_of_skill += trainingprogram.number_of_skill
context = { # context = {
"gymnast": gymnast, # "gymnast": gymnast,
"date": parsed_date, # "date": parsed_date,
"difficulty": difficulty, # "difficulty": difficulty,
"number_of_skill": number_of_skill, # "number_of_skill": number_of_skill,
"trainingprogram": trainingprogram, # "trainingprogram": trainingprogram,
"trainingprogram_list": trainingprogram_list, # "trainingprogram_list": trainingprogram_list,
} # }
return render(request, "trainingprograms/details.html", context) # return render(request, "trainingprograms/details.html", context)
@require_http_methods(["POST"]) # @require_http_methods(["POST"])
def switch_trainingprogram_line(request): # def switch_trainingprogram_line(request):
""" # """
Recoit dans request deux identifiants de trainingprogram qu'il faut échanger () : # Recoit dans request deux identifiants de trainingprogram qu'il faut échanger () :
- tp_id (int) identifiant d'une instance de TraiingProgram # - tp_id (int) identifiant d'une instance de TraiingProgram
- direction (str) la direction du changement (0: haut, 1: bas) # - direction (str) la direction du changement (0: haut, 1: bas)
J'utilise `32767` comme valeur intermédiaire pour le `rank` car c'est la limite supérieure d'un # J'utilise `32767` comme valeur intermédiaire pour le `rank` car c'est la limite supérieure d'un
PositiveSmallIntegerField. # PositiveSmallIntegerField.
""" # """
try: # try:
target_trainingprogram_id = request.POST.get("tpid", None) # target_trainingprogram_id = request.POST.get("tpid", None)
direction = int(request.POST.get("direction", 0)) # direction = int(request.POST.get("direction", 0))
target_trainingprogram = get_object_or_404( # target_trainingprogram = get_object_or_404(
TrainingProgram, pk=target_trainingprogram_id # TrainingProgram, pk=target_trainingprogram_id
) # )
if direction == 0: # if direction == 0:
source_trainingprogram = ( # source_trainingprogram = (
TrainingProgram.objects.filter(rank__lt=target_trainingprogram.rank) # TrainingProgram.objects.filter(rank__lt=target_trainingprogram.rank)
.order_by("-id") # .order_by("-id")
.first() # .first()
) # )
else: # else:
source_trainingprogram = ( # source_trainingprogram = (
TrainingProgram.objects.filter(rank__gt=target_trainingprogram.rank) # TrainingProgram.objects.filter(rank__gt=target_trainingprogram.rank)
.order_by("id") # .order_by("id")
.first() # .first()
) # )
saved_source_rank = source_trainingprogram.rank # saved_source_rank = source_trainingprogram.rank
saved_target_rank = target_trainingprogram.rank # saved_target_rank = target_trainingprogram.rank
source_trainingprogram.rank = 32767 # source_trainingprogram.rank = 32767
source_trainingprogram.save() # source_trainingprogram.save()
target_trainingprogram.rank = saved_source_rank # target_trainingprogram.rank = saved_source_rank
target_trainingprogram.save() # target_trainingprogram.save()
source_trainingprogram.rank = saved_target_rank # source_trainingprogram.rank = saved_target_rank
source_trainingprogram.save() # source_trainingprogram.save()
return HttpResponse(200) # return HttpResponse(200)
except Exception: # except Exception:
return HttpResponse(409) # return HttpResponse(409)

View File

@ -0,0 +1,21 @@
# Generated by Django 4.2 on 2024-03-13 11:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("people", "0011_gymnast_trainers"),
]
operations = [
migrations.AlterModelOptions(
name="gymnast",
options={
"ordering": ["first_name", "last_name"],
"verbose_name": "Gymnast",
"verbose_name_plural": "Gymnasts",
},
),
]

View File

@ -20,7 +20,8 @@ import pendulum
from jarvis.followup.models import Event from jarvis.followup.models import Event
from jarvis.followup.forms import GymnastHasRoutineForm from jarvis.followup.forms import GymnastHasRoutineForm
from jarvis.objective.models import TrainingProgram
# from jarvis.objective.models import TrainingProgram
from jarvis.followup.models import ( from jarvis.followup.models import (
Note, Note,
Plan, Plan,
@ -257,12 +258,12 @@ def gymnast_display_scores_chrono(request, gymnast_id):
).order_by("date") ).order_by("date")
base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof")) base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof"))
today = pendulum.now().date() today = pendulum.now().date()
date_list = ( # date_list = (
TrainingProgram.objects.filter(gymnast=gymnast_id, date__gte=today) # TrainingProgram.objects.filter(gymnast=gymnast_id, date__gte=today)
.values_list("date", flat=True) # .values_list("date", flat=True)
.order_by("date") # .order_by("date")
.distinct() # .distinct()
) # )
context = { context = {
"intensity_list": intensity_list, "intensity_list": intensity_list,
@ -271,7 +272,7 @@ def gymnast_display_scores_chrono(request, gymnast_id):
"chrono_r1": base_queryset.filter(chrono_type=1), "chrono_r1": base_queryset.filter(chrono_type=1),
"chrono_r2": base_queryset.filter(chrono_type=2), "chrono_r2": base_queryset.filter(chrono_type=2),
"chrono_rf": base_queryset.filter(chrono_type=3), "chrono_rf": base_queryset.filter(chrono_type=3),
"date_list": date_list, # "date_list": date_list,
"gymnast_id": gymnast_id, "gymnast_id": gymnast_id,
} }
return render(request, "gymnasts/tabs/tab_intensity_and_chronos.html", context) return render(request, "gymnasts/tabs/tab_intensity_and_chronos.html", context)