[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.urls import reverse
from jarvis.objective.models import Routine
from jarvis.objective.models import Combination
from jarvis.profiles.models import Profile
from jarvis.followup.models import Skill, Point, Chrono
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_event = Event.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_club = Club.objects.all().count()
# 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.people.models import Gymnast, GENDER_CHOICES
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
User = get_user_model()
@ -311,9 +311,10 @@ class GymnastHasRoutine(models.Model):
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
Combination,
verbose_name="Routine",
related_name="done_by_gymnast",
limit_choices_to={"is_routine": True},
on_delete=models.CASCADE,
)
routine_type = models.PositiveSmallIntegerField(
@ -343,10 +344,11 @@ class NumberOfRoutineDone(Seasonisable):
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
Combination,
verbose_name="Routine",
related_name="number_of_try",
on_delete=models.SET_NULL,
limit_choices_to={"is_routine": True},
null=True,
blank=True,
)

View File

@ -11,8 +11,8 @@ from django_admin_listfilter_dropdown.filters import (
from .models import (
TouchPosition,
Skill,
Routine,
RoutineSkill,
Combination,
CombinationSkill,
PrerequisiteClosure,
)
@ -98,8 +98,8 @@ class SkillAdmin(admin.ModelAdmin):
)
class RoutineAdmin(admin.ModelAdmin):
model = Routine
class CombinationAdmin(admin.ModelAdmin):
model = Combination
fields = (
"long_label",
@ -145,7 +145,7 @@ class RoutineAdmin(admin.ModelAdmin):
class Media:
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
@ -155,19 +155,19 @@ class RoutineAdmin(admin.ModelAdmin):
# return super(Skill, self).get_related_filter(model, request)
class RoutineSkillAdmin(admin.ModelAdmin):
model = RoutineSkill
class CombinationSkillAdmin(admin.ModelAdmin):
model = CombinationSkill
list_display = ("routine", "skill", "rank")
list_display = ("combination", "skill", "rank")
list_filter = (("rank", DropdownFilter),)
search_fields = (
"routine__long_label",
"routine__short_label",
"combination__long_label",
"combination__short_label",
"skill__long_label",
"skill__short_label",
)
ordering = ("routine",)
autocomplete_fields = ("routine", "skill")
ordering = ("combination",)
autocomplete_fields = ("combination", "skill")
class PrerequisiteClosureAdmin(admin.ModelAdmin):
@ -188,6 +188,6 @@ class PrerequisiteClosureAdmin(admin.ModelAdmin):
admin.site.register(TouchPosition, TouchPositionAdmin)
admin.site.register(Skill, SkillAdmin)
admin.site.register(Routine, RoutineAdmin)
admin.site.register(RoutineSkill, RoutineSkillAdmin)
admin.site.register(Combination, CombinationAdmin)
admin.site.register(CombinationSkill, CombinationSkillAdmin)
admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin)

View File

@ -1,6 +1,6 @@
from django import forms
from .models import Skill, Routine, RoutineSkill
from .models import Skill, Combination, CombinationSkill
class SkillForm(forms.ModelForm):
@ -17,9 +17,9 @@ class SkillForm(forms.ModelForm):
}
class RoutineForm(forms.ModelForm):
class CombinationForm(forms.ModelForm):
class Meta:
model = Routine
model = Combination
fields = (
"long_label",
"short_label",
@ -30,10 +30,16 @@ class RoutineForm(forms.ModelForm):
)
widgets = {
"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(
attrs={"class": "form-control", "placeholder": "Routine's short name"}
attrs={
"class": "form-control",
"placeholder": "Combination's short name",
}
),
"informations": forms.Textarea(
attrs={
@ -47,16 +53,16 @@ class RoutineForm(forms.ModelForm):
}
class RoutineSkillForm(forms.ModelForm):
class CombinationSkillForm(forms.ModelForm):
class Meta:
model = RoutineSkill
model = CombinationSkill
fields = (
"routine",
"combination",
"skill",
"rank",
)
widgets = {
"routine": forms.HiddenInput(),
"combination": forms.HiddenInput(),
"skill": forms.HiddenInput(),
"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.models import Q, Count
from django.db.models import Q
from jarvis.tools.models import Markdownizable, max_even_if_none
@ -244,20 +244,22 @@ class Skill(Educative):
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`.
TODO: il faudrait changer le nom de la classe en "Combination".
"""
class Meta:
verbose_name = "Routine"
verbose_name_plural = "Routines"
verbose_name = "Combination"
verbose_name_plural = "Combinations"
jumps = models.ManyToManyField(
Skill, through="RoutineSkill", verbose_name="routine"
Skill, through="CombinationSkill", verbose_name="combination"
)
is_active = models.BooleanField(default=True)
is_routine = models.BooleanField(default=False)
is_competitive = models.BooleanField(default=False)
def __str__(self):
@ -267,15 +269,15 @@ class Routine(Educative):
"""Calcule les informations (rank, niveau, ages, …) d'une routine."""
rank = 0
level = 0
age_boy_with_help = 0
age_girl_with_help = 0
age_boy_without_help = 0
age_girl_without_help = 0
difficulty = 0
age_boy_chained = 0
age_girl_chained = 0
age_boy_with_help = 0
age_girl_with_help = 0
age_boy_masterised = 0
age_girl_masterised = 0
difficulty = 0
age_boy_without_help = 0
age_girl_without_help = 0
is_competitive = True
for skill_link in self.skill_links.all():
@ -392,21 +394,27 @@ class Routine(Educative):
).exists()
class RoutineSkill(models.Model):
class CombinationSkill(models.Model):
"""
Classe de liaison permettant de liée une figure à une série. (relation n-n)
TODO: devrait être renommée en "CombinationSkill".
"""
class Meta:
ordering = ("rank",)
routine = models.ForeignKey(
Routine, on_delete=models.CASCADE, default=None, related_name="skill_links"
combination = models.ForeignKey(
Combination,
on_delete=models.CASCADE,
default=None,
related_name="skill_links",
)
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()
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 (
SkillForm,
RoutineForm,
RoutineSkillForm,
CombinationForm,
CombinationSkillForm,
)
from .models import (
Skill,
Routine,
RoutineSkill,
Combination,
CombinationSkill,
PrerequisiteClosure,
)
@ -182,22 +182,24 @@ def routine_listing(request, gymnast_id=None):
gymnast = None
pattern = request.GET.get("pattern", None)
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)
)
else:
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)
else:
routine_list = Routine.objects.all()
routine_list = Combination.objects.filter(is_routine=True)
context = {
"routine_list": routine_list,
"gymnast_id": gymnast_id,
"gymnast": gymnast,
}
return render(request, "routines/list.html", context)
return render(request, "combinations/list.html", context)
@login_required
@ -209,8 +211,12 @@ def routine_lookup(request):
pattern = request.POST.get("pattern", None)
if pattern is not None and len(pattern) >= 3:
results = Routine.objects.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
results = (
Combination()
.objects.filter(is_routine=True)
.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
)
place_list = [{"id": x.id, "label": str(x)} for x in results]
@ -226,42 +232,44 @@ def routine_details(request, routine_id):
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()
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
@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.
Args:
routine_id (int): identifiant d'un object de classe <routine>.
combination_id (int): identifiant d'un object de classe <routine>.
"""
if routine_id:
routine = get_object_or_404(Routine, pk=routine_id)
if combination_id:
combination = get_object_or_404(Combination, pk=combination_id)
else:
routine = None
combination = None
if request.method == "POST":
form = RoutineForm(request.POST, instance=routine)
form = CombinationForm(request.POST, instance=combination)
if form.is_valid():
routine = form.save()
combination = form.save()
# ici faire un FOR skill in form_skills_list:
# record.save() # ca sauve le record dans la table RoutineSkill
# something like this : http://stackoverflow.com/questions/3074938/django-m2m-form-save-through-table
# TO_FRED : can you help me ?
return HttpResponseRedirect(reverse("routine_details", args=(routine.pk,)))
return HttpResponseRedirect(
reverse("routine_details", args=(combination.pk,))
)
else:
return render(request, "routines/create.html", {"form": form})
return render(request, "combinations/create.html", {"form": form})
form = RoutineForm(instance=routine)
context = {"form": form, "routine_id": routine_id}
return render(request, "routines/create.html", context)
form = CombinationForm(instance=combination)
context = {"form": form, "routine_id": combination_id}
return render(request, "combinations/create.html", context)
@login_required
@ -271,7 +279,7 @@ def compose_routine(request, routine_id):
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_list = Skill.objects.all()
context = {
@ -280,7 +288,7 @@ def compose_routine(request, routine_id):
"number_of_skill": skill_link_list.count(),
"skill_list": skill_list,
}
return render(request, "routines/compose.html", context)
return render(request, "combinations/compose.html", context)
@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
"""
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)),
"rank": request.POST.get("rank", 0),
}
form = RoutineSkillForm(data)
form = CombinationSkillForm(data)
if form.is_valid():
link, created = RoutineSkill.objects.get_or_create(
link, created = CombinationSkill.objects.get_or_create(
routine=form.cleaned_data["routine"],
skill=form.cleaned_data["skill"],
rank=form.cleaned_data["rank"],
@ -313,9 +321,9 @@ def unlink_skill_from_routine(request):
"""
order = request.POST.get("order", 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:
RoutineSkill.objects.get(routine=routine, rank=order).delete()
CombinationSkill.objects.get(routine=routine, rank=order).delete()
except Exception:
return HttpResponse(409)