Compare commits

..

No commits in common. "4a8bead7a0c7538cea8a379fc7cbaf5970346c88" and "dcf587e2f94faf0bfef354b38b5c04392f70ff25" have entirely different histories.

13 changed files with 101 additions and 451 deletions

View File

@ -23,7 +23,7 @@
</div>
<div class="card-body pt-0">
<div class="table-responsive">
{% if season_information_list %}
{% if gymnast_list %}
<table class="table tablesorter table-striped" data-sort="table" id="gymnast_table">
<thead>
<tr>
@ -34,26 +34,24 @@
<th class="header text-left" style="width: 20%">Firstname</th>
<th class="header text-left" style="width: 10%">Gender</th>
<th class="header text-left" style="width: 10%">Age</th>
<th class="header text-left" style="width: 10%">Category</th>
<th class="header text-left" style="width: 25%">Club</th>
</tr>
</thead>
<tbody>
{% for season_information in season_information_list %}
{% for gymnast in gymnast_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
{% if request.user|has_group:"trainer" %}
<td>
<a href="{% url 'gymnast_update' season_information.gymnast.id %}">
<a href="{% url 'gymnast_update' gymnast.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
{% endif %}
<td class="text-left"><a href="{% url 'gymnast_details' season_information.gymnast.id %}">{{ season_information.gymnast.last_name }}</a></td>
<td class="text-left"><a href="{% url 'gymnast_details' season_information.gymnast.id %}">{{ season_information.gymnast.first_name }}</a></td>
<td class="text-left">{{ season_information.gymnast.get_gender_display }}</td>
<td class="text-left">{{ season_information.gymnast.age }}</td>
<td class="text-left">{{ season_information.get_category_display }}</td>
<td class="text-left">{{ season_information.club.name }}</td>
<td class="text-left"><a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast.last_name }}</a></td>
<td class="text-left"><a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast.first_name }}</a></td>
<td class="text-left">{{ gymnast.get_gender_display }}</td>
<td class="text-left">{{ gymnast.age }}</td>
<td class="text-left">{{ gymnast.club.name }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -18,7 +18,6 @@ from .models import (
LearnedSkill,
ChronoDetails,
GymnastHasRoutine,
SeasonInformation,
NumberOfRoutineDone,
)
@ -212,25 +211,6 @@ class IntensityAdmin(admin.ModelAdmin):
autocomplete_fields = ("gymnast",)
class SeasonInformationAdmin(admin.ModelAdmin):
model = SeasonInformation
list_display = (
"gymnast",
"season",
"category",
"number_of_training_sessions_per_week",
"number_of_hours_per_week",
# "club",
)
list_filter = (("gymnast", RelatedDropdownFilter),)
search_fields = (
"gymnast__firstname",
"gymnast__lastname",
)
autocomplete_fields = ("gymnast",)
admin.site.register(Plan, PlanAdmin)
admin.site.register(Note, NoteAdmin)
admin.site.register(Point, PointAdmin)
@ -241,6 +221,5 @@ admin.site.register(Intensity, IntensityAdmin)
admin.site.register(LearnedSkill, LearnedSkillAdmin)
admin.site.register(HeightWeight, HeightWeightAdmin)
admin.site.register(ChronoDetails, ChronoDetailsAdmin)
admin.site.register(SeasonInformation, SeasonInformationAdmin)
admin.site.register(GymnastHasRoutine, GymnastHasRoutineAdmin)
admin.site.register(NumberOfRoutineDone, NumberOfRoutineDoneAdmin)

View File

@ -1,85 +0,0 @@
# Generated by Django 4.1.1 on 2022-11-01 14:31
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("location", "0003_auto_20220109_1001"),
("people", "0006_gymnast_created_at"),
(
"followup",
"0033_alter_intensity_options_alter_intensity_difficulty_and_more",
),
]
operations = [
migrations.CreateModel(
name="SeasonInformation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("season", models.CharField(editable=False, max_length=9)),
(
"trainings_by_week",
models.PositiveSmallIntegerField(verbose_name="# Training by week"),
),
(
"hours_by_week",
models.PositiveSmallIntegerField(verbose_name="# Hours by week"),
),
(
"category",
models.PositiveSmallIntegerField(
choices=[
(9, "I9"),
(10, "I10"),
(11, "A11"),
(12, "A12"),
(13, "A13-14"),
(15, "A Junior"),
(18, "A Senior"),
(21, "B11"),
(22, "B12"),
(23, "B13-14"),
(24, "B Junior"),
(25, "B Senior"),
],
verbose_name="Category",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"club",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="season_informations",
to="location.club",
),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="season_informations",
to="people.gymnast",
),
),
],
options={
"verbose_name": "Season Information",
"verbose_name_plural": "Season Informations",
"unique_together": {("gymnast", "season")},
},
),
]

View File

@ -1,28 +0,0 @@
# Generated by Django 4.1.1 on 2022-11-02 12:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0034_seasoninformation"),
]
operations = [
migrations.RenameField(
model_name="seasoninformation",
old_name="hours_by_week",
new_name="number_of_hours_per_week",
),
migrations.RenameField(
model_name="seasoninformation",
old_name="trainings_by_week",
new_name="number_of_training_sessions_per_week",
),
migrations.AlterField(
model_name="seasoninformation",
name="season",
field=models.CharField(max_length=9),
),
]

View File

@ -10,7 +10,6 @@ from ultron.tools.models import Markdownizable, Seasonisable
from ultron.people.models import Gymnast
from ultron.planning.models import Event
from ultron.objective.models import Educative, Skill, Routine
from ultron.location.models import Club
ROUTINE_CHOICE = (
(0, "Other"),
@ -32,6 +31,12 @@ LEARNING_STEP_CHOICES = (
(4, "Masterised"),
)
class Chrono(Seasonisable):
"""
Représente les chronos (de chandelles ou de série) enregistrés pour un(e) gymnaste.
"""
CHRONO_TYPE_CHOICE = (
(0, "10 |"),
(1, "Routine 1"),
@ -46,27 +51,6 @@ SCORE_TYPE_CHOICE = (
(1, "ToF"),
)
CATEGORY_CHOICES = {
9: "I9",
10: "I10",
11: "A11",
12: "A12",
13: "A13-14",
15: "A Junior",
18: "A Senior",
21: "B11",
22: "B12",
23: "B13-14",
24: "B Junior",
25: "B Senior",
}
class Chrono(Seasonisable):
"""
Représente les chronos (de chandelles ou de série) enregistrés pour un(e) gymnaste.
"""
class Meta:
verbose_name = "Chrono"
verbose_name_plural = "Chronos"
@ -448,16 +432,6 @@ class Intensity(Markdownizable, Seasonisable):
quantity_of_skill = models.PositiveSmallIntegerField()
number_of_passes = models.PositiveSmallIntegerField()
def __str__(self):
return "%s - %s : %s - %s - %s - %s" % (
self.gymnast,
self.date,
self.time,
self.difficulty,
self.quantity_of_skill,
self.number_of_passes,
)
@property
def mean_difficulty_by_passe(self):
return self.difficulty / self.number_of_passes
@ -473,43 +447,3 @@ class Intensity(Markdownizable, Seasonisable):
@property
def mean_difficulty_by_skill(self):
return self.difficulty / self.quantity_of_skill
class SeasonInformation(models.Model):
"""Classe représentant l'intensité d'un entraînement"""
class Meta:
verbose_name = "Season Information"
verbose_name_plural = "Season Informations"
unique_together = ("gymnast", "season")
CATEGORY_CHOICES_ARRAY = [(key, value) for key, value in CATEGORY_CHOICES.items()]
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="season_informations"
)
season = models.CharField(max_length=9)
number_of_training_sessions_per_week = models.PositiveSmallIntegerField(
verbose_name="# Training by week"
)
number_of_hours_per_week = models.PositiveSmallIntegerField(
verbose_name="# Hours by week"
)
category = models.PositiveSmallIntegerField(
choices=CATEGORY_CHOICES_ARRAY,
verbose_name="Category",
)
club = models.ForeignKey(
Club, null=True, on_delete=models.SET_NULL, related_name="season_informations"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "%s - %s : %s - %s - %s - %s" % (
self.gymnast,
self.season,
self.number_of_training_sessions_per_week,
self.number_of_hours_per_week,
self.category,
self.club,
)

View File

@ -7,21 +7,18 @@ This way, it's relatively easy to fetch an entire tree with just one tiny reques
from django.core.management.base import BaseCommand
from ultron.objective.models import Routine, PrerequisiteClosure
from ultron.objective.models import Educative, PrerequisiteClosure
class Command(BaseCommand):
def handle(self, *args, **options):
routine_list = Routine.objects.all()
educative_list = Educative.objects.all()
count = 0
for routine in routine_list:
for educative in educative_list:
count += 1
updated = False
max_level = routine.difficulty * 10
# if educative.
max_level = 0
max_rank = 0
age_boy_with_help = 0
age_boy_without_help = 0
@ -33,9 +30,9 @@ class Command(BaseCommand):
age_girl_chained = 0
age_girl_masterised = 0
print(str(count) + " - Traitement de " + str(routine.long_label))
print(str(count) + " - Traitement de " + str(educative.long_label))
breadcrumb = routine.breadcrumb()
breadcrumb = educative.breadcrumb()
for path in range(0, len(breadcrumb)):
tree = set(
PrerequisiteClosure.objects.filter(descendant=educative, path=path)
@ -78,45 +75,45 @@ class Command(BaseCommand):
for tree_path in tree:
tree_path.delete()
if routine.level != max_level:
if educative.level != max_level:
updated = True
routine.level = max_level
educative.level = max_level
if routine.rank != max_rank:
if educative.rank != max_rank:
updated = True
routine.rank = max_rank
educative.rank = max_rank
if routine.age_boy_with_help < age_boy_with_help:
if educative.age_boy_with_help < age_boy_with_help:
updated = True
routine.age_boy_with_help = age_boy_with_help
educative.age_boy_with_help = age_boy_with_help
if routine.age_boy_without_help < age_boy_without_help:
if educative.age_boy_without_help < age_boy_without_help:
updated = True
routine.age_boy_without_help = age_boy_without_help
educative.age_boy_without_help = age_boy_without_help
if routine.age_boy_chained < age_boy_chained:
if educative.age_boy_chained < age_boy_chained:
updated = True
routine.age_boy_chained = age_boy_chained
educative.age_boy_chained = age_boy_chained
if routine.age_boy_masterised < age_boy_masterised:
if educative.age_boy_masterised < age_boy_masterised:
updated = True
routine.age_boy_masterised = age_boy_masterised
educative.age_boy_masterised = age_boy_masterised
if routine.age_girl_with_help < age_girl_with_help:
if educative.age_girl_with_help < age_girl_with_help:
updated = True
routine.age_girl_with_help = age_girl_with_help
educative.age_girl_with_help = age_girl_with_help
if routine.age_girl_without_help < age_girl_without_help:
if educative.age_girl_without_help < age_girl_without_help:
updated = True
routine.age_girl_without_help = age_girl_without_help
educative.age_girl_without_help = age_girl_without_help
if routine.age_girl_chained < age_girl_chained:
if educative.age_girl_chained < age_girl_chained:
updated = True
routine.age_girl_chained = age_girl_chained
educative.age_girl_chained = age_girl_chained
if routine.age_girl_masterised < age_girl_masterised:
if educative.age_girl_masterised < age_girl_masterised:
updated = True
routine.age_girl_masterised = age_girl_masterised
educative.age_girl_masterised = age_girl_masterised
if updated:
routine.save()
educative.save()

View File

@ -1,123 +0,0 @@
"""This command manages Closure Tables implementation
It adds new levels and cleans links between Educatives.
This way, it's relatively easy to fetch an entire tree with just one tiny request.
"""
from django.core.management.base import BaseCommand
from ultron.objective.models import Skill, PrerequisiteClosure
class Command(BaseCommand):
def handle(self, *args, **options):
skill_list = Skill.objects.all()
count = 0
for skill in skill_list:
count += 1
updated = False
max_level = skill.difficulty * 10
if skill.position == "/":
max_level += 1
max_rank = 0
age_boy_with_help = 0
age_boy_without_help = 0
age_boy_chained = 0
age_boy_masterised = 0
age_girl_with_help = 0
age_girl_without_help = 0
age_girl_chained = 0
age_girl_masterised = 0
print(str(count) + " - Traitement de " + str(skill.long_label))
breadcrumb = skill.breadcrumb()
for path in range(0, len(breadcrumb)):
tree = set(
PrerequisiteClosure.objects.filter(descendant=skill, path=path)
)
for position, ancestor in enumerate(breadcrumb[path]):
tree_path, _ = PrerequisiteClosure.objects.get_or_create(
ancestor=ancestor,
descendant=skill,
level=position,
path=path,
)
max_level = max(max_level, position)
max_rank = max(max_rank, max_level, ancestor.rank + 1)
age_boy_with_help = max(
age_boy_with_help, ancestor.age_boy_with_help
)
age_boy_without_help = max(
age_boy_without_help, ancestor.age_boy_without_help
)
age_boy_chained = max(age_boy_chained, ancestor.age_boy_chained)
age_boy_masterised = max(
age_boy_masterised, ancestor.age_boy_masterised
)
age_girl_with_help = max(
age_girl_with_help, ancestor.age_girl_with_help
)
age_girl_without_help = max(
age_girl_without_help, ancestor.age_girl_without_help
)
age_girl_chained = max(age_girl_chained, ancestor.age_girl_chained)
age_girl_masterised = max(
age_girl_masterised, ancestor.age_girl_masterised
)
if tree_path in tree:
tree.remove(tree_path)
for tree_path in tree:
tree_path.delete()
if skill.level != max_level:
updated = True
skill.level = max_level
if skill.rank != max_rank:
updated = True
skill.rank = max_rank
if skill.age_boy_with_help < age_boy_with_help:
updated = True
skill.age_boy_with_help = age_boy_with_help
if skill.age_boy_without_help < age_boy_without_help:
updated = True
skill.age_boy_without_help = age_boy_without_help
if skill.age_boy_chained < age_boy_chained:
updated = True
skill.age_boy_chained = age_boy_chained
if skill.age_boy_masterised < age_boy_masterised:
updated = True
skill.age_boy_masterised = age_boy_masterised
if skill.age_girl_with_help < age_girl_with_help:
updated = True
skill.age_girl_with_help = age_girl_with_help
if skill.age_girl_without_help < age_girl_without_help:
updated = True
skill.age_girl_without_help = age_girl_without_help
if skill.age_girl_chained < age_girl_chained:
updated = True
skill.age_girl_chained = age_girl_chained
if skill.age_girl_masterised < age_girl_masterised:
updated = True
skill.age_girl_masterised = age_girl_masterised
if updated:
skill.save()

View File

@ -1,29 +0,0 @@
# Generated by Django 4.1.1 on 2022-11-01 14:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("objective", "0014_alter_skill_notation_alter_skill_rotation_and_more"),
]
operations = [
migrations.AlterField(
model_name="skill",
name="position",
field=models.CharField(
choices=[
("0", "none"),
("o", "tuck"),
("c", "puck"),
("<", "pike"),
("L", "half pike"),
("/", "straight"),
("//", "straddle"),
],
max_length=2,
),
),
]

View File

@ -10,29 +10,25 @@ class Educative(Markdownizable):
série de compétition, .
Level (skill) :
Toutes les figures appartiennent à un niveau. Un niveau peut contenir plusieurs figures.
Par défaut, le niveau d'une figure est son coéficient de difficulté (exprimé en 10ème
Toutes les figures appartiennent à un niveau. Un niveau peut contenir plusieurs
figures. Par défaut, le niveau d'une figure est son coéficient de difficulté (exprimé en 10ème
pour avoir des nombres entiers) auquel on ajoute 1 pour les positions tendue.
Exemple :
- saut groupé, saut carpé joint et saut écart ==> niveau 0
- salto avant groupé, salto arrière groupé ==> niveau 5
- salto avant carpé, barani groupé, salto arrière carpé ==> niveau 6
- salto avant tendu, salto arrière tendu, barani tendu ==> niveau 7
- saut groupé, saut carpé joint et saut écart ==> niveau 5
- salto avant groupé, salto arrière groupé ==> niveau 10
- salto avant carpé, barani groupé, salto arrière carpé ==> niveau 11
- salto avant tendu, barani carpé, salto arrière tendu ==> niveau 12
En plus de cela, il y a une limite minimum : le niveau dune figure ne peut pas être plus
petit que le niveau maximum de ses prérequis.
Le niveau, avec le rang, ont pour but daider les coaches à planifier lévolution et l
apprentissage des figures les unes par rapport aux autres.
En plus de cela, il y a une limite minimum pour un niveau : le niveau dune figure ne peut pas
être plus petit que le niveau maximum de ses prérequis.
Le niveau, avec le rang, ont pour but daider les coaches à planifier lévolution et
lapprentissage des figures les unes par rapport aux autres.
Level (routine) :
Toutes les séries ont également un niveau. Par défaut le niveau d'une série est le niveau
maximum des figures qui composent la série.
Rank (skill) :
Le rang permet, en plus du `level` (niveau), de classer les figures entre elles, de leur
donner un ordre (informatif). Le rang dune figure est calculé par rapport aux prérequis et
au niveau : par défaut le rang dune figure est le maximum entre le niveau maximum de ses
donner un ordre (informatif). Le rang dune figure est calculé par rapport aux prérequis et au
niveau : par défaut le rang dune figure est le maximum entre le niveau maximum de ses
prérequis plus un et le niveau de la figure.
Le rang, avec le niveau, ont pour but daider les coaches à planifier lévolution et
lapprentissage des figures les unes par rapport aux autres.
@ -211,7 +207,7 @@ class Skill(Educative):
("o", "tuck"),
("c", "puck"),
("<", "pike"),
("L", "half pike"),
("L", "Half pike"),
("/", "straight"),
("//", "straddle"),
)

View File

@ -224,8 +224,9 @@ def routine_details(request, routine_id):
"""
Récupère toutes les informations d'une routine (série).
Args:
routine_id int identifiant d'une routine
:param routine_id: id d'une `routine`
:type routine_id: int
:return: routine_id
"""
routine = get_object_or_404(Routine, pk=routine_id)

View File

@ -54,10 +54,30 @@ class Gymnast(Markdownizable):
)
created_at = models.DateTimeField(auto_now_add=True)
#
#
# TODO: traingin_by_week and hours_by_week --> classe de saison
# CATEGORY_CHOICES = (
# (9, "I9"),
# (10, "I10"),
# (11, "A11"),
# (12, "A12"),
# (13, "A13-14"),
# (15, "A Junior"),
# (18, "A Senior"),
# (21, "B11"),
# (22, "B12"),
# (23, "B13-14"),
# (24, "B Junior"),
# (25, "B Senior"),
# )
trainings_by_week = models.PositiveSmallIntegerField(
verbose_name="# Training by week"
)
hours_by_week = models.PositiveSmallIntegerField(verbose_name="# Hours by week")
# category = models.PositiveSmallIntegerField(
# choices=CATEGORY_CHOICES, verbose_name="Category"
# )
club = models.ForeignKey(
Club, null=True, on_delete=models.SET_NULL, related_name="gymnast"
)

View File

@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
User = get_user_model()
from django.db.models import BooleanField, ExpressionWrapper, Q, F, Avg, IntegerField
from django.db.models import BooleanField, ExpressionWrapper, Q, F, Avg
from django.db.models.functions import TruncDay
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
@ -28,14 +28,9 @@ from ultron.followup.models import (
MindState,
LearnedSkill,
HeightWeight,
SeasonInformation,
NumberOfRoutineDone,
)
from ultron.followup.models import CATEGORY_CHOICES
from ultron.tools.models import Season
from ultron.tools.pdf_generator import GymnastReportDocument
from .models import Gymnast
@ -69,12 +64,10 @@ def gymnast_listing(request):
Si la personne connectée est un entraîneur : liste tous les gymnasts connus.
Si la personne connectée est un gymnaste : renvoie sa fiche détaillée.
"""
season = Season()
if request.user.groups.filter(name="trainer").exists():
season_information_list = SeasonInformation.objects.filter(
season=season.label
).select_related("gymnast")
context = {"season_information_list": season_information_list}
gymnast_list = Gymnast.objects.all()
context = {"gymnast_list": gymnast_list}
return render(request, "people/gymnasts/list.html", context)
else:
gymnast = Gymnast.objects.get(user=request.user)

View File

@ -18,7 +18,7 @@ class Season:
def __init__(self, label=None):
self.label = label
if self.label is None or not self.is_valid():
if not self.is_valid():
the_date = pendulum.today().date()
if the_date.month >= 9: # nouvelle saison
self.label = str(the_date.year) + "-" + str(the_date.year + 1)
@ -70,9 +70,6 @@ class Season:
return True
def __str__(self):
return "%s" % (self.label)
def get_default_date():
return pendulum.now().date()