Check passe.regexp improvement

This commit is contained in:
Gregory Trullemans 2024-03-09 08:06:21 +01:00
parent 74fc13f5cd
commit 97ff0edbb3
9 changed files with 310 additions and 69 deletions

BIN
.coverage

Binary file not shown.

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -10,7 +10,7 @@
<div class="card-header">
<h4 class="mb-0">Passe details</h3>
<h5 class="card-category mb-0">{{ passe.label }}{% if passe.regexp %}{{ passe.regexp }}{% endif %}</a>&nbsp;&nbsp;&nbsp;{{ passe.repetition }}</h4>
<h5 class="card-category mb-0">{{ passe.label }} {% if passe.regexp %}{{ passe.regexp }}{% endif %}</a>&nbsp;&nbsp;&nbsp;{{ passe.repetition }}</h4>
</div>
<div class="card-body">
@ -27,16 +27,24 @@
<label for="id_educative" class="col-4 col-sm-6 col-md-5 col-lg-4 col-xl-3 col-form-label">Content</label>
<div class="col-8 col-sm-8 col-md-7 col-lg-8 col-xl-9 pt-2">
<ul class="mb-0 ml-4 pl-0">
{% for educative in passe.educatives.all %}
<li><a href="{% url 'educative_details' educative.id %}">{{ educative }}</a></li>
{% endfor %}
{% if routine and not skill_link_list %}
<li><a href="{% url 'educative_details' routine.id %}">{{ routine }}</a></li>
{% elif routine and skill_link_list %}
{% for routine_skill in skill_link_list %}
<li><a href="{% url 'educative_details' routine_skill.skill.id %}">{{ routine_skill.rank }} - {{ routine_skill.skill.notation }}</a></li>
{% endfor %}
{% else %}
{% for educative in passe.educatives.all %}
<li><a href="{% url 'educative_details' educative.id %}">{{ educative }}</a></li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
{% if passe.regexp %}
{% if passe.regexp and passe.regexp != passe.label %}
<div class="form-group row mb-0">
<label for="id_regexp" class="col-4 col-sm-6 col-md-5 col-lg-4 col-xl-3 col-form-label">Regexp<span class="text-danger"><b>*</b></span></label>
<label for="id_regexp" class="col-4 col-sm-6 col-md-5 col-lg-4 col-xl-3 col-form-label">Regexp</label>
<div class="col-8 col-sm-8 col-md-7 col-lg-8 col-xl-9 pt-2">
{{ passe.regexp }}
</div>
@ -46,7 +54,7 @@
<div class="form-group row mb-0">
<label for="id_regexp" class="col-4 col-sm-6 col-md-5 col-lg-4 col-xl-3 col-form-label">Number of Skill</label>
<div class="col-8 col-sm-8 col-md-7 col-lg-8 col-xl-9 pt-2">
{{ number_of_skill }}
{{ number_of_educative }}
</div>
</div>

View File

@ -12,14 +12,14 @@
<table class="table table-striped tablesorter" id="trainingprogram_table">
<thead>
<tr>
<th colspan="5" class="text-center">{% if gymnast %}{{ gymnast}} - {% endif %}{{ date|date:"l j F Y" }}</th>
<th colspan="5" class="text-center">{% if gymnast %}{{ gymnast }} - {% endif %}{{ date|date:"l j F Y" }}</th>
</tr>
</thead>
<tbody>
{% for trainingprogram in trainingprogram_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td><b>{{ trainingprogram.rank }}</b></td>
<td class="text-center"><a href="{% url 'passe_details' trainingprogram.passe.id %}">{{ trainingprogram.passe.label }}</a>{% if trainingprogram.repetition != 1 %}&nbsp;&nbsp;&nbsp;{{ trainingprogram.repetition }}{% endif %}</td>
<td class="text-center"><a href="{% url 'passe_details' trainingprogram.passe.id gymnast.id date %}">{{ trainingprogram.passe.label }}</a>{% if trainingprogram.repetition != 1 %}&nbsp;&nbsp;&nbsp;{{ trainingprogram.repetition }}{% endif %}</td>
<td class="text-center">{{ trainingprogram.number_of_skill}}</td>
<td class="text-center">{{ trainingprogram.difficulty }}</td>
{% if request.user|has_group:"trainer" %}

View File

@ -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)

View File

@ -92,7 +92,11 @@ urlpatterns = [
#
# PASSES
#
path(r"passe/<int:passe_id>/", views.passe_details, name="passe_details"),
path(
r"passe/<int:passe_id>/gymnast/<int:gymnast_id>/date/<str:date>/",
views.passe_details,
name="passe_details",
),
path(r"passe/", views.passe_listing, name="passe_listing"),
path(
r"passe/add/",

View File

@ -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,