Compare commits

...

3 Commits

Author SHA1 Message Date
Gregory Trullemans 1756d78e89 Add clean names to people model 2024-02-06 12:43:08 +01:00
Gregory Trullemans 350c6549e7 Add clean names to people model 2024-02-06 12:42:31 +01:00
Gregory Trullemans 70c306f546 Minors improvements 2024-02-06 11:00:21 +01:00
11 changed files with 121 additions and 14 deletions

View File

@ -41,6 +41,12 @@ puis, **pour les mac M1** exécuter les commandes :
sudo ln -s /opt/homebrew/opt/pango/lib/libpangoft2-1.0.dylib /usr/local/lib/pangoft2-1.0
```
### Tests
### Pylint
Dans le répertoire racine, tapez la commande suivante :

View File

@ -42,7 +42,7 @@
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td class="text-left"><a href="{% url 'injury_details' injury.id %}">{{ injury.date | date:"d-m-Y" }}</a></td>
<td class="text-left"><a href="{% url 'injury_details' injury.id %}">{{ injury.date | date:"j-n-Y" }}</a></td>
<td class="text-left"><a href="{% url 'gymnast_details_tab' injury.gymnast.id 'physiological' %}">{{ injury.gymnast }}</a></td>
<td class="text-left">{{ injury.get_mechanism_display }}</td>
<td class="text-left">{{ injury.get_location_display }}</td>

View File

@ -26,6 +26,8 @@ class CombinationForm(forms.ModelForm):
"difficulty",
"level",
"is_active",
"is_routine",
"is_competitive",
"informations",
)
widgets = {
@ -35,6 +37,8 @@ class CombinationForm(forms.ModelForm):
"short_label": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Routine's short name"}
),
# "is_routine": form.,
# "is_competitive": form.,
"informations": forms.Textarea(
attrs={
"class": "form-control",

View File

@ -250,6 +250,12 @@ class Routine(Educative):
"""
Classe représentant une série (enchainement de plusieurs figures). Elle hérite de la classe
`Educative`.
Cette classe permet donc de représenter tout enchaînement de figures. Il existe 4 types
d'enchaînements :
- les enchaînements de compétition (série de compétition) : is_routine = True & is_competitive = True
- les enchaînements (non autorisé en compétition) : is_routine = True
- les éducatifs (enchainements courts en vue d'apprendre une figure précise) : is_routine = False
- les combinaisons : regroupe les 3 précédentes catégorie sans distinction
"""
class Meta:

View File

@ -27,6 +27,21 @@
{{ form.short_label }}&nbsp;{% if form.short_label.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.short_label.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row {% if form.is_routine.errors %}has-error has-feedback{% endif %}">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Is routine ? <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-sm-10 col-md-10 col-lg-10 col-xl-10">
{{ form.is_routine }}&nbsp;{% if form.is_routine.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.is_routine.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row {% if form.is_competitive.errors %}has-error has-feedback{% endif %}">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Is Competitive ? <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-sm-10 col-md-10 col-lg-10 col-xl-10">
{{ form.is_competitive }}&nbsp;{% if form.is_competitive.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.is_competitive.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_information" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Informations</label>
<div class="col-8 col-sm-9 col-md-9 col-lg-9 col-xl-9 {% if form.id_information.errors %}has-danger{% endif %}">

View File

@ -241,18 +241,17 @@ def routine_listing(request, gymnast_id=None):
gymnast = None
pattern = request.GET.get("pattern", None)
base_queryset = Routine.objects.filter(is_routine=True)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(is_routine=True).filter(
routine_list = base_queryset.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
else:
if gymnast_id:
routine_list = Routine.objects.filter(is_routine=True).filter(
done_by_gymnast__gymnast=gymnast_id
)
routine_list = base_queryset.filter(done_by_gymnast__gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.filter(is_routine=True)
routine_list = base_queryset
context = {
"title": "Routines",
@ -274,18 +273,17 @@ def educative_combination_listing(request, gymnast_id=None):
gymnast = None
pattern = request.GET.get("pattern", None)
base_queryset = Routine.objects.filter(is_routine=False)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(is_routine=False).filter(
routine_list = base_queryset.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
else:
if gymnast_id:
routine_list = Routine.objects.filter(is_routine=False).filter(
done_by_gymnast__gymnast=gymnast_id
)
routine_list = base_queryset.filter(done_by_gymnast__gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.filter(is_routine=False)
routine_list = base_queryset
context = {
"title": "Educative",

View File

@ -11,7 +11,7 @@ class GymnastAdmin(admin.ModelAdmin):
def first_name(self, obj):
return obj.user.first_name
@admin.display(ordering='user__email', description='Email')
@admin.display(ordering="user__email", description="Email")
def email(self, obj):
if obj.user:
return obj.user.email
@ -23,7 +23,9 @@ class GymnastAdmin(admin.ModelAdmin):
fields = (
"last_name",
"cleaned_last_name",
"first_name",
"cleaned_first_name",
"user",
"birthdate",
"gender",
@ -35,6 +37,8 @@ class GymnastAdmin(admin.ModelAdmin):
"informations",
)
readonly_fields = ["cleaned_last_name", "cleaned_first_name"]
list_display = ("last_name", "first_name", "age", "email", "is_active")
list_filter = ("gender", "user__is_active")
search_fields = ("last_name", "first_name")

View File

@ -0,0 +1,30 @@
# Generated by Django 4.2 on 2024-02-06 11:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("people", "0009_alter_gymnast_gender"),
]
operations = [
migrations.AddField(
model_name="gymnast",
name="cleaned_first_name",
field=models.CharField(default="GREGORY", max_length=25),
preserve_default=False,
),
migrations.AddField(
model_name="gymnast",
name="cleaned_last_name",
field=models.CharField(default="TRULLEMANS", max_length=40),
preserve_default=False,
),
migrations.AlterField(
model_name="gymnast",
name="birthdate",
field=models.DateField(verbose_name="Birth date"),
),
]

View File

@ -14,6 +14,7 @@ from jarvis.objective.tools import (
compute_completude,
compute_statistics_by_type,
)
from jarvis.tools.clean_name import clean_name
User = get_user_model()
@ -35,9 +36,9 @@ class Gymnast(Markdownizable):
User, on_delete=models.SET_NULL, related_name="gymnast", blank=True, null=True
)
last_name = models.CharField(max_length=40, null=False, blank=False)
# cleaned_last_name = models.CharField(max_length=40, null=False, blank=False)
cleaned_last_name = models.CharField(max_length=40, null=False, blank=False)
first_name = models.CharField(max_length=25, null=False, blank=False)
# cleaned_first_name = models.CharField(max_length=25, null=False, blank=False)
cleaned_first_name = models.CharField(max_length=25, null=False, blank=False)
birthdate = models.DateField(verbose_name="Birth date")
gender = models.PositiveSmallIntegerField(
choices=GENDER_CHOICES, verbose_name="Sexe"
@ -55,6 +56,12 @@ class Gymnast(Markdownizable):
)
created_at = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
"""Sauve les informations de la personne et initialise les champs nettoyés."""
self.cleaned_last_name = clean_name(self.last_name)
self.cleaned_first_name = clean_name(self.first_name)
super().save(*args, **kwargs)
def __str__(self):
return f"{self.first_name} {self.last_name}"

View File

@ -0,0 +1,21 @@
"""Outils et fonctions utiles pour la gestion des personnes"""
import unicodedata
TRANSTABLE = str.maketrans(
dict((ord(char), None) for char in " \"/-.,;+_*:=~''`\\()!$")
)
def clean_name(name):
"""Nettoie les (pré)noms en supprimant les caractère accentués, les espaces, … permettant des
recherches plus faciles.
"""
tmp_str = name.strip().upper().translate(TRANSTABLE)
compressed_name = "".join(
c
for c in unicodedata.normalize("NFKD", tmp_str)
if unicodedata.category(c) != "Mn"
)
return compressed_name

View File

@ -0,0 +1,16 @@
""" Test du module cleane_name """
from django.test import TestCase
from .clean_name import clean_name
class FunctionTestCase(TestCase):
def test_cleane_name(self):
name = "Gregory"
self.assertEqual(clean_name(name), "GREGORY")
name = "Naël"
self.assertEqual(clean_name(name), "NAEL")
name = "Félix"
self.assertEqual(clean_name(name), "FELIX")
name = "Jean-Luc"
self.assertEqual(clean_name(name), "JEANLUC")