From 97ff0edbb3a46198aa86c6d3f86be3355dbabafb Mon Sep 17 00:00:00 2001 From: Gregory Trullemans Date: Sat, 9 Mar 2024 08:06:21 +0100 Subject: [PATCH] Check passe.regexp improvement --- .coverage | Bin 53248 -> 53248 bytes jarvis/objective/admin.py | 10 +- jarvis/objective/forms.py | 9 + jarvis/objective/models.py | 273 ++++++++++++++---- .../objective/templates/passes/details.html | 22 +- .../templates/trainingprograms/details.html | 4 +- jarvis/objective/tests.py | 2 +- jarvis/objective/urls.py | 6 +- jarvis/objective/views.py | 53 +++- 9 files changed, 310 insertions(+), 69 deletions(-) diff --git a/.coverage b/.coverage index f732434ccabe2202044306c5fe76223c8a8ec534..15170c79956b9366be43f783d0bb4e6ce18064b2 100644 GIT binary patch delta 3325 zcmaJ@3s98T6~6!bvHSo3K6tD=b{B!=&GOQa0$p+BAp*8B$>0MPlw~2q?&5-2Yh;%+ zF_~)XpbM@Dkl9%Ij8(UStdV{&&ol$NV+AxBlSpG`s?~5 z`fh!e?z--mx-MO&cuhPkZWJ?wtHRrYTS(+D@;mrS?g4k4>*q??8TNfP$R;wsXL=bc z{RzF9UQB&T^-zdBLHdvFqe-^5m`1}!b-2cg(`# zcs)Lkf55pZ-~+9*9}9h320ZQ_|A5Ee-!rhyy?IMVFYJYV+dPF3zB(3O_N4HB&wzWN z+Y<=(Z0hKJoCJcuQvku4T5w&hS>C3Ep94N57rArhI*%| zP(9cvsuQ54&ug}o0{5>g_uecjvodjh=yFPCW8aMU+pU6BMzIog8_d4tSgehD#fgd$Jf!*>mKX~;^Hb7sm{hSb#f(7 zx-3xHQ|eR$PgW*sXeyMOpn)R| z4=ygZi3XZReGLm}n)56TEY+r5Cjm>1O!jj$urz|$h%8lVSqEG#YAqGCCk@Fm)tOdj zl&L)MHm2ED^1$2a4bCW2dEjkSlVY)HwKNbFvKu1=c}!8e<{M!sPuS))U?^jaHddA# ziTWlvQyXaDCX6^SVqeJ1lsXz{3AVV1ZJv_JH8e014D#35n4}hNSfWm@6`+E0%tXSX zA*moGYS_rSC=sK3+WKlCMntn#H}G&CF+p?{VT?}61cbyhnP@ZuA*VMi12Pu1Rsb1i zT8n^&Mj#}f=Y&Scnbv9`Bi4b2@;rrv`x(n*u?P^XqJ+T=XyzW8>&KceTB(Kzs%%x0R2S<4T+Iw34XM@_kvA zL$Y6PmDk9b@*?Tq(hcd7bWu7fy)Uhj^!mg4{ra8yM*UJfsk^DWqI+KVlrBg7TpSbM z5ci6&ibLX{=o34|_2M(5M#GE{;tFU&FcdViTs{`^LCC{G-hP71#X|0Wg3SRz&Kc*j zF_2wIaamZ%Dx_Ekwm8NenXor!-k+XZA|P0=QzYWB5MAZkP7|gK@N;C0hh~t!^1`{R zA=7`ZDQnUhLKt?W55?0{2=%EuciAds2*6)R!XZFWm_qIFn?Q1khH4pE{bIaSrOsCHr?TglJLsy0dl^d0nUJD+3vn3Zz%{^3I_d@@A)m3gmXMHZ$ zL-n7#%R^}HtGjr8;+lKWIy*(2_8oN1RwY837ZcCiyGew9Lcw)K?nyfo9}WdaI@H=k zV^C8J#fFXqeCYCo*`u~Fx(>+Hf|@mNJV0%Qa0}6;WyQJ1Ffi|;jYx9|&pU@HJ|0Rm z#gCtRWQqgH5qG}=t{@ssiDtkO%pc#~J29HgRB-fgT6QSc{_m&W6m-(Sd^Tin`uFNT%v-rDP7)?9df=5@;X^0(WR|}=ZCz* zZo4jgb`-HwU4ev?fAhMF9x$WfbX|KVabZs3CGa*$U29oI50YI^p4PKhet7GxH_VC- z10h|>NhkZ|VN2g2VG;pxh=mssd3$5{)X0jl4_Z9}9%X~jhGsmc1KU3;JyZJT^yG<& zji;wCo31`M)4KaZpY#0e?X71gOGP$M;cvfKxmKN&H}WYT@Fv| ztG^FwgzBNjy`Ap414)U30&UCB*S5SoZOhHowwxSo%g)xetgO*{Z}G+d1y9zXIRF3v delta 2343 zcmZ8idu&tJ89(Rxz8~k>&dVi^WAZQ=OPmlg5r#<|2zio_ZKP#kfXNbvj5u*hN5?4G zX6mH2k!Uy*Q>%(bf~pTFZN!$KQ9-IwrbdjFekqo@hFx-al>jF-X_q$ZpskbJYXthrW3XKxRBngt+9u?`+wZ~TzBYzH+<*^ z1>oP9^rz8Z)jDxL#I)i(>fX|VSJM71+XXj-lq9mgu_c`fb!kE_Sk}?embJ94E}wT< z(BV{3^zn-(UL~bwUk0v3JtWVSWZ}91M8aB8m>m9k6LSlDgjnS2iwcd&_GvrAKOeb52ozo z%0>E4s{>o0BOmAI5(&yStMt;gM&L>XGRlM(%&S6$yibp-@0iDAL;XrWuX>F0YLDJ0 zpCQkYa`}$#Ro)}x(zw*Ae5uVTMaI0+tDVzU8-3b-a^I|#(#2239?>CO8y%kJ>A-jE zg`!3th%(sz%@k4aDthyK-@zXG`Swk=CkaxxiwAx{L{uA>Z13aUazp%%Jz{1T8-fJ^DzrV z7H9PB?eFjH?vEMdl!yq!K^ zi7cco=gnxg3if5~Y0zxT+Ao18R#p)hV*+=eb6|`N+<}6?7+Ek9u`sZnVvPR8&_Ea& zYjCU-2xF&bca<#>M!`zL`Wjfvc5kLNdrGOhUM0&s5v%zahK-azHVIpKU{2y?Fy>@o z9y;3y{9);VI2SJMX>_-FG!B{f&6DP!xy|&Fd2*Y4MlOhEa}w6C;D?IZ1o)~_{aer=uR)jV2`mab_Ucfn>ouSO&fj}@z@Rg?-DCWPT(;b=E+t4OSc^88?%_$r}ub+)Eccn99VB0?;0haZ%>pu;Ao-V1ZE!XlCjNl4c&e7z!Ye$ERVjNg>IiDHYAI-7pEuX|Mx(+T}P`j5LJdpJg% zD(;JCWF9KIk=5#7cm)4U$Pu6V;%`H7{qEv;XxtHB9J{)3c_8bD15>YzURk^yAHP2I z@xL#ZTw8oN_R_t}SL%jBJ9TtpcMxRu*HeebJ8^VsZ0-mrVO~+}!E?L{op{IsG8fH% bm~-YG^N;2&lV-eH4L>Vq$c2Sd@|ynv;IdsY diff --git a/jarvis/objective/admin.py b/jarvis/objective/admin.py index 365a837..aa27276 100644 --- a/jarvis/objective/admin.py +++ b/jarvis/objective/admin.py @@ -221,7 +221,15 @@ class TrainingProgramAdmin(admin.ModelAdmin): "difficulty", # "score", ) - list_display = ("date", "gymnast", "passe", "rank") + list_display = ( + "date", + "gymnast", + "passe", + "rank", + "repetition", + "number_of_skill", + "difficulty", + ) list_filter = ( ("gymnast", RelatedDropdownFilter), ("date", DropdownFilter), diff --git a/jarvis/objective/forms.py b/jarvis/objective/forms.py index 5f72522..e066699 100644 --- a/jarvis/objective/forms.py +++ b/jarvis/objective/forms.py @@ -110,3 +110,12 @@ class PasseForm(forms.ModelForm): # "step": "1", # } # ), + + def clean_regexp(self): + """ """ + cleaned_data = self.clean() + regexp = cleaned_data.get("regexp") + if not Passe.is_valid_regexp(regexp, None, None): + self.add_error("regexp", "Entered regexp not valid.") + + return regexp diff --git a/jarvis/objective/models.py b/jarvis/objective/models.py index a5244df..f1cb435 100644 --- a/jarvis/objective/models.py +++ b/jarvis/objective/models.py @@ -477,54 +477,82 @@ class Passe(Markdownizable): 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.""" + """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") - 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 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 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 + 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 += educative.jumps.all().count() + 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) @@ -532,7 +560,63 @@ class Passe(Markdownizable): return f"{self.label} ({self.number_of_skill} | {self.difficulty})" @staticmethod - def check_regexp(regexp, label, educatives_list): + 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. + """ + last_place = len(pattern) - 1 + if re.search("\.", pattern): + 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 une liste.""" + 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 + - [-] False + - [1-] False + - [-1] False + - [4] False + - [6-6] False + """ + if re.match(r"^\[[2-9]*\-[2-9]*\]$", subset): + value = subset.replace("[", "").replace("]", "") + if len(value) > 1: + 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 Type de string pris en compte : @@ -546,14 +630,84 @@ class Passe(Markdownizable): - Q1R1 True (si educatives.count() vide) - Q1R2 [2-8] True (si educatives.count() vide) - Q1R1 [8-2] False - - Q2R3 [-5] True (si educatives.count() vide) + - Q1R1 [-] False (not working) + - Q2R1 [-5] True (si educatives.count() vide) - SF [6-] True (si educatives.count() vide) - FS [3-7] True (si educatives.count() vide) + - Q1R1. True (si educatives.count() == 1) (NOT WOKRING) + - .Q1R2 True (si educatives.count() == 1) (NOT WOKRING) - WC True (si educatives.count() >= 2) - - 1| True (si educatives.count() >= 2) + - 1| True (si educatives.count() >= 1) """ + operation_list = regexp.split(" ") + + if len(operation_list) >= 3: + return False + + if len(operation_list) == 2: + if not Passe.is_valid_dot(operation_list[0]): + return False + + value = operation_list[0].replace(".", "") + is_valid_routine = Passe.is_valid_routine_type(value) + + if is_valid_routine: + return Passe.is_valid_subset(operation_list[1]) + else: + if operation_list[0] == "WC": + return True + + if re.match(r"[1-9]+\|", operation_list[0]): + return True + + if not Passe.is_valid_dot(operation_list[0]): + return False + + value = operation_list[0].replace(".", "") + is_valid_routine = Passe.is_valid_routine_type(value) + + if is_valid_routine: + return True + + return Passe.is_valid_subset(operation_list[0]) + + return False + + @staticmethod + def is_valid_regexp_extended(regexp, label, educatives_list): + """Vérifie le champ regexp + + Type de string pris en compte : + - Toutes les valeurs de ROUTINE_TYPE_CHOICE (Educative vide !) + - avec [x-y] (avec X ou Y vide mais pas les deux en même temps) + - [x-y] (avec X ou Y vide mais pas les deux en même temps) (EDUCATIVE non vide !!!) + - WC + - x| (x entier) + + Exemples : + - Q1R1 True (si educatives.count() vide) + - Q1R2 [2-8] True (si educatives.count() vide) + - Q1R1 [8-2] False + - Q1R1 [-] False + - Q2R1 [-5] True (si educatives.count() vide) + - SF [6-] True (si educatives.count() vide) + - FS [3-7] True (si educatives.count() vide) + - Q1R1. True (si educatives.count() vide) + - .Q1R2 True (si educatives.count() vide) + - Q1R1. [-4] True (si educatives.count() vide) + - .Q1R2 [4-] True (si educatives.count() vide) + - .FS [3-7] True (si educatives.count() vide) + - [2-8] True (si educative.count() == 1 et educative est une routine >= 8 sauts) + - [8-2] False + - [-] False + - [-5] True (si educative.count() == 1 et educative est une routine >= 8 sauts) + - WC True (si educatives.count() >= 2) + - 1| True (si educatives.count() >= 1) + """ + # devrait être en dehors de cette fonction --> if label is None and educatives_list is None and regexp is None: return False + # <-- operation_list = regexp.split(" ") @@ -561,27 +715,36 @@ class Passe(Markdownizable): return False if len(operation_list) == 2: - if operation_list[0] in ROUTINE_TYPE_CHOICE: - if re.match(r"\[[1-9]*\-[1-9]*\]", operation_list[1]): - content = operation_list[1].replace("[", "").replace("]", "") - ranks = content.split("-") - if ranks.count() > 1: - if ranks[1] > ranks[1]: - return True - else: - return False - return True - else: - return False - else: + if not Passe.is_valid_dot(operation_list[0]): return False + + value = operation_list[0].replace(".", "") + is_valid_routine = Passe.is_valid_routine_type(value) + + if is_valid_routine: + return Passe.is_valid_subset(operation_list[1]) else: - if operation_list[0] == "WC" and len(educatives_list) == 2: + if ( + operation_list[0] == "WC" + and educatives_list is not None + and len(educatives_list) == 2 + ): return True if re.match(r"[1-9]+\|", operation_list[0]) and len(educatives_list) >= 1: return True + if not Passe.is_valid_dot(operation_list[0]): + return False + + value = operation_list[0].replace(".", "") + is_valid_routine = Passe.is_valid_routine_type(value) + + if is_valid_routine: + return True + + return Passe.is_valid_subset(operation_list[0]) + return False diff --git a/jarvis/objective/templates/passes/details.html b/jarvis/objective/templates/passes/details.html index 187065d..4a83193 100644 --- a/jarvis/objective/templates/passes/details.html +++ b/jarvis/objective/templates/passes/details.html @@ -10,7 +10,7 @@

Passe details

-
{{ passe.label }}{% if passe.regexp %}{{ passe.regexp }}{% endif %}   {{ passe.repetition }}
+
{{ passe.label }} {% if passe.regexp %}{{ passe.regexp }}{% endif %}   {{ passe.repetition }}
@@ -27,16 +27,24 @@
- {% if passe.regexp %} + {% if passe.regexp and passe.regexp != passe.label %}
- +
{{ passe.regexp }}
@@ -46,7 +54,7 @@
- {{ number_of_skill }} + {{ number_of_educative }}
diff --git a/jarvis/objective/templates/trainingprograms/details.html b/jarvis/objective/templates/trainingprograms/details.html index 17dc15f..36a69d1 100644 --- a/jarvis/objective/templates/trainingprograms/details.html +++ b/jarvis/objective/templates/trainingprograms/details.html @@ -12,14 +12,14 @@ - + {% for trainingprogram in trainingprogram_list %} - + {% if request.user|has_group:"trainer" %} diff --git a/jarvis/objective/tests.py b/jarvis/objective/tests.py index 92189e9..64793a5 100644 --- a/jarvis/objective/tests.py +++ b/jarvis/objective/tests.py @@ -132,7 +132,7 @@ class PasseTestCase(TestCase): educ_1 = Educative.objects.get(long_label="1/2 vrille") educatives_list.append(educ_1) regexp = "1|" - self.assertEqual(Passe.check_regexp(regexp, label, educatives_list), False) + self.assertEqual(Passe.check_regexp(regexp, label, educatives_list), True) educ_3 = Educative.objects.get(long_label="4 pattes") educatives_list.append(educ_3) self.assertEqual(Passe.check_regexp(regexp, label, educatives_list), True) diff --git a/jarvis/objective/urls.py b/jarvis/objective/urls.py index bec4be4..d3c550e 100644 --- a/jarvis/objective/urls.py +++ b/jarvis/objective/urls.py @@ -92,7 +92,11 @@ urlpatterns = [ # # PASSES # - path(r"passe//", views.passe_details, name="passe_details"), + path( + r"passe//gymnast//date//", + views.passe_details, + name="passe_details", + ), path(r"passe/", views.passe_listing, name="passe_listing"), path( r"passe/add/", diff --git a/jarvis/objective/views.py b/jarvis/objective/views.py index d7d2532..c0824ea 100644 --- a/jarvis/objective/views.py +++ b/jarvis/objective/views.py @@ -7,7 +7,9 @@ from django.urls import reverse import pendulum +from jarvis.core.global_vars import ROUTINE_TYPE_CHOICE from jarvis.people.models import Gymnast +from jarvis.followup.models import GymnastHasRoutine from .forms import ( SkillForm, @@ -521,19 +523,66 @@ def passe_listing(request): @login_required @require_http_methods(["GET"]) -def passe_details(request, passe_id): +def passe_details(request, passe_id, gymnast_id, date): """Détails d'un passage.""" is_skill = False passe = get_object_or_404(Passe, pk=passe_id) educative_list = passe.educatives.all() # TODO: décryptage de la regexp - number_of_educative = educative_list.count() + regexp = passe.regexp + routine = None + skill_link_list = None + + if regexp is not None: + operation_list = regexp.split(" ") + routine_type = None + + for item in ROUTINE_TYPE_CHOICE: + if item[1] == operation_list[0]: + routine_type = item[0] + break + + if routine_type is not None: + # Récupération de la série + ghr = GymnastHasRoutine.objects.filter( + gymnast=gymnast_id, + date_begin__lte=date, + # date_end__gte=date, + routine_type=routine_type, + ) + + print(ghr.query) + + if ghr.count() > 1: + print("Plus d'une série trouvée...") + + print(ghr) + + routine = ghr.first().routine + skill_link_list = routine.skill_links.all() + if len(operation_list) == 2: + content = operation_list[1].replace("[", "").replace("]", "") + ranks = content.split("-") + + if ranks[0] != "": + skill_link_list = skill_link_list.filter(rank__gte=ranks[0]) + + if ranks[1] != "": + skill_link_list = skill_link_list.filter(rank__lte=ranks[1]) + + number_of_educative = skill_link_list.count() + print(number_of_educative) + + else: + number_of_educative = educative_list.count() context = { "passe": passe, "is_skill": is_skill, "educative_list": educative_list, + "routine": routine, + "skill_link_list": skill_link_list, "difficulty": passe.difficulty, "number_of_skill": passe.number_of_skill, "number_of_educative": number_of_educative,
{% if gymnast %}{{ gymnast}} - {% endif %}{{ date|date:"l j F Y" }}{% if gymnast %}{{ gymnast }} - {% endif %}{{ date|date:"l j F Y" }}
{{ trainingprogram.rank }}{{ trainingprogram.passe.label }}{% if trainingprogram.repetition != 1 %}   {{ trainingprogram.repetition }}{% endif %}{{ trainingprogram.passe.label }}{% if trainingprogram.repetition != 1 %}   {{ trainingprogram.repetition }}{% endif %} {{ trainingprogram.number_of_skill}} {{ trainingprogram.difficulty }}