[WIP] Routine -> Combination

This commit is contained in:
Gregory Trullemans 2023-04-30 12:25:13 +02:00
parent 982f44b38b
commit a8019b6e8d
14 changed files with 172 additions and 75 deletions

View File

@ -10,7 +10,7 @@ from django.views.decorators.http import require_http_methods
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from jarvis.objective.models import Routine from jarvis.objective.models import Combination
from jarvis.profiles.models import Profile from jarvis.profiles.models import Profile
from jarvis.followup.models import Skill, Point, Chrono from jarvis.followup.models import Skill, Point, Chrono
from jarvis.location.models import Place, Club from jarvis.location.models import Place, Club
@ -120,7 +120,7 @@ def home(request):
nb_active_gymnast = Gymnast.objects.filter(is_active=True).count() nb_active_gymnast = Gymnast.objects.filter(is_active=True).count()
nb_event = Event.objects.all().count() nb_event = Event.objects.all().count()
nb_skill = Skill.objects.all().count() nb_skill = Skill.objects.all().count()
nb_routine = Routine.objects.all().count() nb_routine = Combination.objects.filter(is_routine=True).count()
nb_score = Point.objects.all().count() nb_score = Point.objects.all().count()
nb_club = Club.objects.all().count() nb_club = Club.objects.all().count()
# percentage_week = int( # percentage_week = int(

View File

@ -0,0 +1,39 @@
# Generated by Django 4.2 on 2023-04-30 10:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0017_combination_combinationskill_and_more"),
("followup", "0044_alter_seasoninformation_number_of_hours_per_week_and_more"),
]
operations = [
migrations.AlterField(
model_name="gymnasthasroutine",
name="routine",
field=models.ForeignKey(
limit_choices_to={"is_routine": True},
on_delete=django.db.models.deletion.CASCADE,
related_name="done_by_gymnast",
to="objective.combination",
verbose_name="Routine",
),
),
migrations.AlterField(
model_name="numberofroutinedone",
name="routine",
field=models.ForeignKey(
blank=True,
limit_choices_to={"is_routine": True},
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="number_of_try",
to="objective.combination",
verbose_name="Routine",
),
),
]

View File

@ -6,7 +6,7 @@ from datetime import date
from jarvis.tools.models import Markdownizable, Seasonisable from jarvis.tools.models import Markdownizable, Seasonisable
from jarvis.people.models import Gymnast, GENDER_CHOICES from jarvis.people.models import Gymnast, GENDER_CHOICES
from jarvis.planning.models import Event from jarvis.planning.models import Event
from jarvis.objective.models import Educative, Skill, Routine from jarvis.objective.models import Educative, Skill, Combination
from jarvis.location.models import Club from jarvis.location.models import Club
User = get_user_model() User = get_user_model()
@ -311,9 +311,10 @@ class GymnastHasRoutine(models.Model):
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
routine = models.ForeignKey( routine = models.ForeignKey(
Routine, Combination,
verbose_name="Routine", verbose_name="Routine",
related_name="done_by_gymnast", related_name="done_by_gymnast",
limit_choices_to={"is_routine": True},
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
routine_type = models.PositiveSmallIntegerField( routine_type = models.PositiveSmallIntegerField(
@ -343,10 +344,11 @@ class NumberOfRoutineDone(Seasonisable):
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
routine = models.ForeignKey( routine = models.ForeignKey(
Routine, Combination,
verbose_name="Routine", verbose_name="Routine",
related_name="number_of_try", related_name="number_of_try",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
limit_choices_to={"is_routine": True},
null=True, null=True,
blank=True, blank=True,
) )

View File

@ -11,8 +11,8 @@ from django_admin_listfilter_dropdown.filters import (
from .models import ( from .models import (
TouchPosition, TouchPosition,
Skill, Skill,
Routine, Combination,
RoutineSkill, CombinationSkill,
PrerequisiteClosure, PrerequisiteClosure,
) )
@ -98,8 +98,8 @@ class SkillAdmin(admin.ModelAdmin):
) )
class RoutineAdmin(admin.ModelAdmin): class CombinationAdmin(admin.ModelAdmin):
model = Routine model = Combination
fields = ( fields = (
"long_label", "long_label",
@ -145,7 +145,7 @@ class RoutineAdmin(admin.ModelAdmin):
class Media: class Media:
js = ( js = (
"js/core/jquery-3.6.0.min.js", "js/core/jquery-3.6.0.min.js",
"js/admin/routine.js", "js/admin/combination.js",
) )
# TODO: ne proposer QUE les SKILL comme educatif # TODO: ne proposer QUE les SKILL comme educatif
@ -155,19 +155,19 @@ class RoutineAdmin(admin.ModelAdmin):
# return super(Skill, self).get_related_filter(model, request) # return super(Skill, self).get_related_filter(model, request)
class RoutineSkillAdmin(admin.ModelAdmin): class CombinationSkillAdmin(admin.ModelAdmin):
model = RoutineSkill model = CombinationSkill
list_display = ("routine", "skill", "rank") list_display = ("combination", "skill", "rank")
list_filter = (("rank", DropdownFilter),) list_filter = (("rank", DropdownFilter),)
search_fields = ( search_fields = (
"routine__long_label", "combination__long_label",
"routine__short_label", "combination__short_label",
"skill__long_label", "skill__long_label",
"skill__short_label", "skill__short_label",
) )
ordering = ("routine",) ordering = ("combination",)
autocomplete_fields = ("routine", "skill") autocomplete_fields = ("combination", "skill")
class PrerequisiteClosureAdmin(admin.ModelAdmin): class PrerequisiteClosureAdmin(admin.ModelAdmin):
@ -188,6 +188,6 @@ class PrerequisiteClosureAdmin(admin.ModelAdmin):
admin.site.register(TouchPosition, TouchPositionAdmin) admin.site.register(TouchPosition, TouchPositionAdmin)
admin.site.register(Skill, SkillAdmin) admin.site.register(Skill, SkillAdmin)
admin.site.register(Routine, RoutineAdmin) admin.site.register(Combination, CombinationAdmin)
admin.site.register(RoutineSkill, RoutineSkillAdmin) admin.site.register(CombinationSkill, CombinationSkillAdmin)
admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin) admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin)

View File

@ -1,6 +1,6 @@
from django import forms from django import forms
from .models import Skill, Routine, RoutineSkill from .models import Skill, Combination, CombinationSkill
class SkillForm(forms.ModelForm): class SkillForm(forms.ModelForm):
@ -17,9 +17,9 @@ class SkillForm(forms.ModelForm):
} }
class RoutineForm(forms.ModelForm): class CombinationForm(forms.ModelForm):
class Meta: class Meta:
model = Routine model = Combination
fields = ( fields = (
"long_label", "long_label",
"short_label", "short_label",
@ -30,10 +30,16 @@ class RoutineForm(forms.ModelForm):
) )
widgets = { widgets = {
"long_label": forms.TextInput( "long_label": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Routine's long name"} attrs={
"class": "form-control",
"placeholder": "Combination's long name",
}
), ),
"short_label": forms.TextInput( "short_label": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Routine's short name"} attrs={
"class": "form-control",
"placeholder": "Combination's short name",
}
), ),
"informations": forms.Textarea( "informations": forms.Textarea(
attrs={ attrs={
@ -47,16 +53,16 @@ class RoutineForm(forms.ModelForm):
} }
class RoutineSkillForm(forms.ModelForm): class CombinationSkillForm(forms.ModelForm):
class Meta: class Meta:
model = RoutineSkill model = CombinationSkill
fields = ( fields = (
"routine", "combination",
"skill", "skill",
"rank", "rank",
) )
widgets = { widgets = {
"routine": forms.HiddenInput(), "combination": forms.HiddenInput(),
"skill": forms.HiddenInput(), "skill": forms.HiddenInput(),
"rank": forms.NumberInput(), "rank": forms.NumberInput(),
} }

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2023-04-29 15:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("objective", "0015_alter_skill_position"),
]
operations = [
migrations.AddField(
model_name="routine",
name="is_routine",
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 4.2 on 2023-04-30 10:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0016_routine_is_routine"),
]
operations = [
migrations.RenameModel("Routine", "Combination"),
migrations.RenameModel("RoutineSkill", "CombinationSkill"),
]

View File

@ -1,5 +1,5 @@
from django.db import models from django.db import models
from django.db.models import Q, Count from django.db.models import Q
from jarvis.tools.models import Markdownizable, max_even_if_none from jarvis.tools.models import Markdownizable, max_even_if_none
@ -244,20 +244,22 @@ class Skill(Educative):
return f"{self.long_label} ({self.notation})" return f"{self.long_label} ({self.notation})"
class Routine(Educative): class Combination(Educative):
""" """
Classe représentant une série (enchainement de plusieurs figures). Elle hérite de la classe Classe représentant une série (c-à-d. un enchainement de plusieurs figures). Elle hérite de la classe
`Educative`. `Educative`.
TODO: il faudrait changer le nom de la classe en "Combination".
""" """
class Meta: class Meta:
verbose_name = "Routine" verbose_name = "Combination"
verbose_name_plural = "Routines" verbose_name_plural = "Combinations"
jumps = models.ManyToManyField( jumps = models.ManyToManyField(
Skill, through="RoutineSkill", verbose_name="routine" Skill, through="CombinationSkill", verbose_name="combination"
) )
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
is_routine = models.BooleanField(default=False)
is_competitive = models.BooleanField(default=False) is_competitive = models.BooleanField(default=False)
def __str__(self): def __str__(self):
@ -267,15 +269,15 @@ class Routine(Educative):
"""Calcule les informations (rank, niveau, ages, …) d'une routine.""" """Calcule les informations (rank, niveau, ages, …) d'une routine."""
rank = 0 rank = 0
level = 0 level = 0
age_boy_with_help = 0 difficulty = 0
age_girl_with_help = 0
age_boy_without_help = 0
age_girl_without_help = 0
age_boy_chained = 0 age_boy_chained = 0
age_girl_chained = 0 age_girl_chained = 0
age_boy_with_help = 0
age_girl_with_help = 0
age_boy_masterised = 0 age_boy_masterised = 0
age_girl_masterised = 0 age_girl_masterised = 0
difficulty = 0 age_boy_without_help = 0
age_girl_without_help = 0
is_competitive = True is_competitive = True
for skill_link in self.skill_links.all(): for skill_link in self.skill_links.all():
@ -392,21 +394,27 @@ class Routine(Educative):
).exists() ).exists()
class RoutineSkill(models.Model): class CombinationSkill(models.Model):
""" """
Classe de liaison permettant de liée une figure à une série. (relation n-n) Classe de liaison permettant de liée une figure à une série. (relation n-n)
TODO: devrait être renommée en "CombinationSkill".
""" """
class Meta: class Meta:
ordering = ("rank",) ordering = ("rank",)
routine = models.ForeignKey( combination = models.ForeignKey(
Routine, on_delete=models.CASCADE, default=None, related_name="skill_links" Combination,
on_delete=models.CASCADE,
default=None,
related_name="skill_links",
) )
skill = models.ForeignKey( skill = models.ForeignKey(
Skill, on_delete=models.CASCADE, default=None, related_name="routine_links" Skill, on_delete=models.CASCADE, default=None, related_name="combination_links"
) )
rank = models.PositiveSmallIntegerField() rank = models.PositiveSmallIntegerField()
def __str__(self): def __str__(self):
return f"{self.rank} - {self.routine.short_label} : {self.skill.short_label}" return (
f"{self.rank} - {self.combination.short_label} : {self.skill.short_label}"
)

View File

@ -9,13 +9,13 @@ from jarvis.people.models import Gymnast
from .forms import ( from .forms import (
SkillForm, SkillForm,
RoutineForm, CombinationForm,
RoutineSkillForm, CombinationSkillForm,
) )
from .models import ( from .models import (
Skill, Skill,
Routine, Combination,
RoutineSkill, CombinationSkill,
PrerequisiteClosure, PrerequisiteClosure,
) )
@ -182,22 +182,24 @@ def routine_listing(request, gymnast_id=None):
gymnast = None gymnast = None
pattern = request.GET.get("pattern", None) pattern = request.GET.get("pattern", None)
if pattern: if pattern:
routine_list = Routine.objects.filter( routine_list = Combination.objects.filter(is_routine=True).filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern) Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
) )
else: else:
if gymnast_id: if gymnast_id:
routine_list = Routine.objects.filter(done_by_gymnast__gymnast=gymnast_id) routine_list = Combination.objects.filter(is_routine=True).filter(
done_by_gymnast__gymnast=gymnast_id
)
gymnast = Gymnast.objects.get(pk=gymnast_id) gymnast = Gymnast.objects.get(pk=gymnast_id)
else: else:
routine_list = Routine.objects.all() routine_list = Combination.objects.filter(is_routine=True)
context = { context = {
"routine_list": routine_list, "routine_list": routine_list,
"gymnast_id": gymnast_id, "gymnast_id": gymnast_id,
"gymnast": gymnast, "gymnast": gymnast,
} }
return render(request, "routines/list.html", context) return render(request, "combinations/list.html", context)
@login_required @login_required
@ -209,9 +211,13 @@ def routine_lookup(request):
pattern = request.POST.get("pattern", None) pattern = request.POST.get("pattern", None)
if pattern is not None and len(pattern) >= 3: if pattern is not None and len(pattern) >= 3:
results = Routine.objects.filter( results = (
Combination()
.objects.filter(is_routine=True)
.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern) Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
) )
)
place_list = [{"id": x.id, "label": str(x)} for x in results] place_list = [{"id": x.id, "label": str(x)} for x in results]
return JsonResponse(place_list, safe=False) return JsonResponse(place_list, safe=False)
@ -226,42 +232,44 @@ def routine_details(request, routine_id):
routine_id int identifiant d'une routine routine_id int identifiant d'une routine
""" """
routine = get_object_or_404(Routine, pk=routine_id) routine = get_object_or_404(Combination, pk=routine_id)
routine.compute_informations() routine.compute_informations()
context = {"routine": routine, "skill_link_list": routine.skill_links.all()} context = {"routine": routine, "skill_link_list": routine.skill_links.all()}
return render(request, "routines/details.html", context) return render(request, "combinations/details.html", context)
@login_required @login_required
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
def routine_create_or_update(request, routine_id=None): def routine_create_or_update(request, combination_id=None):
"""Création d'une série. """Création d'une série.
Args: Args:
routine_id (int): identifiant d'un object de classe <routine>. combination_id (int): identifiant d'un object de classe <routine>.
""" """
if routine_id: if combination_id:
routine = get_object_or_404(Routine, pk=routine_id) combination = get_object_or_404(Combination, pk=combination_id)
else: else:
routine = None combination = None
if request.method == "POST": if request.method == "POST":
form = RoutineForm(request.POST, instance=routine) form = CombinationForm(request.POST, instance=combination)
if form.is_valid(): if form.is_valid():
routine = form.save() combination = form.save()
# ici faire un FOR skill in form_skills_list: # ici faire un FOR skill in form_skills_list:
# record.save() # ca sauve le record dans la table RoutineSkill # record.save() # ca sauve le record dans la table RoutineSkill
# something like this : http://stackoverflow.com/questions/3074938/django-m2m-form-save-through-table # something like this : http://stackoverflow.com/questions/3074938/django-m2m-form-save-through-table
# TO_FRED : can you help me ? # TO_FRED : can you help me ?
return HttpResponseRedirect(reverse("routine_details", args=(routine.pk,))) return HttpResponseRedirect(
reverse("routine_details", args=(combination.pk,))
)
else: else:
return render(request, "routines/create.html", {"form": form}) return render(request, "combinations/create.html", {"form": form})
form = RoutineForm(instance=routine) form = CombinationForm(instance=combination)
context = {"form": form, "routine_id": routine_id} context = {"form": form, "routine_id": combination_id}
return render(request, "routines/create.html", context) return render(request, "combinations/create.html", context)
@login_required @login_required
@ -271,7 +279,7 @@ def compose_routine(request, routine_id):
Récupère une routine et les sauts associés. Récupère une routine et les sauts associés.
""" """
routine = get_object_or_404(Routine, pk=routine_id) routine = get_object_or_404(Combination, pk=routine_id)
skill_link_list = routine.skill_links.all() skill_link_list = routine.skill_links.all()
skill_list = Skill.objects.all() skill_list = Skill.objects.all()
context = { context = {
@ -280,7 +288,7 @@ def compose_routine(request, routine_id):
"number_of_skill": skill_link_list.count(), "number_of_skill": skill_link_list.count(),
"skill_list": skill_list, "skill_list": skill_list,
} }
return render(request, "routines/compose.html", context) return render(request, "combinations/compose.html", context)
@require_http_methods(["POST"]) @require_http_methods(["POST"])
@ -289,14 +297,14 @@ def link_skill_to_routine(request):
Recoit trois informations permettant de lier complètement un saut à une routine Recoit trois informations permettant de lier complètement un saut à une routine
""" """
data = { data = {
"routine": get_object_or_404(Routine, pk=request.POST.get("routine_id", 0)), "routine": get_object_or_404(Combination, pk=request.POST.get("routine_id", 0)),
"skill": get_object_or_404(Skill, pk=request.POST.get("skill_id", 0)), "skill": get_object_or_404(Skill, pk=request.POST.get("skill_id", 0)),
"rank": request.POST.get("rank", 0), "rank": request.POST.get("rank", 0),
} }
form = RoutineSkillForm(data) form = CombinationSkillForm(data)
if form.is_valid(): if form.is_valid():
link, created = RoutineSkill.objects.get_or_create( link, created = CombinationSkill.objects.get_or_create(
routine=form.cleaned_data["routine"], routine=form.cleaned_data["routine"],
skill=form.cleaned_data["skill"], skill=form.cleaned_data["skill"],
rank=form.cleaned_data["rank"], rank=form.cleaned_data["rank"],
@ -313,9 +321,9 @@ def unlink_skill_from_routine(request):
""" """
order = request.POST.get("order", None) order = request.POST.get("order", None)
routine_id = request.POST.get("routine_id", None) routine_id = request.POST.get("routine_id", None)
routine = get_object_or_404(Routine, pk=routine_id) routine = get_object_or_404(Combination, pk=routine_id)
try: try:
RoutineSkill.objects.get(routine=routine, rank=order).delete() CombinationSkill.objects.get(routine=routine, rank=order).delete()
except Exception: except Exception:
return HttpResponse(409) return HttpResponse(409)