Compare commits
7 Commits
51b6598747
...
5140ddce3a
Author | SHA1 | Date |
---|---|---|
Gregory Trullemans | 5140ddce3a | |
Gregory Trullemans | 1d99ed6568 | |
Gregory Trullemans | 23e00ef131 | |
Gregory Trullemans | a798507754 | |
Gregory Trullemans | 51ac7fc5fb | |
Gregory Trullemans | b026edbb27 | |
Gregory Trullemans | fccb416728 |
|
@ -87,7 +87,7 @@
|
|||
{% submenuitem 'competition_routine_listing' 'CR' 'Competition Routine' %}
|
||||
{% submenuitem 'routine_listing' 'R' 'Routine' %}
|
||||
{% submenuitem 'educative_combination_listing' 'E' 'Educative' %}
|
||||
{% submenuitem 'combination_list' 'C' 'Combination' %}
|
||||
{% submenuitem 'combination_listing' 'C' 'Combination' %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -9,8 +9,10 @@ from django_admin_listfilter_dropdown.filters import (
|
|||
from .models import (
|
||||
TouchPosition,
|
||||
Skill,
|
||||
Passe,
|
||||
Routine,
|
||||
RoutineSkill,
|
||||
TrainingProgram,
|
||||
PrerequisiteClosure,
|
||||
)
|
||||
|
||||
|
@ -172,7 +174,7 @@ class RoutineSkillAdmin(admin.ModelAdmin):
|
|||
|
||||
|
||||
class PrerequisiteClosureAdmin(admin.ModelAdmin):
|
||||
model = PrerequisiteClosure()
|
||||
model = PrerequisiteClosure
|
||||
|
||||
list_display = ("descendant", "ancestor", "level", "path")
|
||||
search_fields = (
|
||||
|
@ -187,8 +189,49 @@ class PrerequisiteClosureAdmin(admin.ModelAdmin):
|
|||
)
|
||||
|
||||
|
||||
admin.site.register(TouchPosition, TouchPositionAdmin)
|
||||
class PasseAdmin(admin.ModelAdmin):
|
||||
model = Passe
|
||||
|
||||
fields = (
|
||||
"label",
|
||||
"educatives",
|
||||
"regexp",
|
||||
"number_of_skill",
|
||||
"difficulty",
|
||||
"informations",
|
||||
)
|
||||
|
||||
list_display = ("label", "number_of_skill", "difficulty")
|
||||
list_filter = (
|
||||
("number_of_skill", DropdownFilter),
|
||||
("difficulty", DropdownFilter),
|
||||
)
|
||||
|
||||
|
||||
class TrainingProgramAdmin(admin.ModelAdmin):
|
||||
model = TrainingProgram
|
||||
|
||||
fields = (
|
||||
"gymnast",
|
||||
"date",
|
||||
"rank",
|
||||
"passe",
|
||||
"repetition",
|
||||
"number_of_skill",
|
||||
"difficulty",
|
||||
# "score",
|
||||
)
|
||||
list_display = ("date", "gymnast", "passe", "rank")
|
||||
list_filter = (
|
||||
("gymnast", RelatedDropdownFilter),
|
||||
("date", DropdownFilter),
|
||||
)
|
||||
|
||||
|
||||
admin.site.register(Skill, SkillAdmin)
|
||||
admin.site.register(Passe, PasseAdmin)
|
||||
admin.site.register(Routine, RoutineAdmin)
|
||||
admin.site.register(RoutineSkill, RoutineSkillAdmin)
|
||||
admin.site.register(TouchPosition, TouchPositionAdmin)
|
||||
admin.site.register(TrainingProgram, TrainingProgramAdmin)
|
||||
admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django import forms
|
||||
|
||||
from .models import Skill, Routine, RoutineSkill
|
||||
from .models import Skill, Routine, RoutineSkill, Passe
|
||||
|
||||
|
||||
class SkillForm(forms.ModelForm):
|
||||
|
@ -64,3 +64,49 @@ class CombinationSkillForm(forms.ModelForm):
|
|||
"skill": forms.HiddenInput(),
|
||||
"rank": forms.NumberInput(),
|
||||
}
|
||||
|
||||
|
||||
class PasseForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Passe
|
||||
fields = ("label", "educatives", "regexp", "informations")
|
||||
widgets = {
|
||||
"label": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Label (not mandatory)",
|
||||
"maxlength": 30,
|
||||
}
|
||||
),
|
||||
"regexp": forms.TextInput(
|
||||
attrs={"class": "form-control", "placeholder": "[2-8]"}
|
||||
),
|
||||
"informations": forms.Textarea(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Informations about the passe…", # pylint: disable=line-too-long
|
||||
}
|
||||
),
|
||||
"educatives": forms.HiddenInput(),
|
||||
}
|
||||
|
||||
educative_related = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Searching educative",
|
||||
"data-ref": "#id_educative",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
# "repetition": forms.NumberInput(
|
||||
# attrs={
|
||||
# "class": "form-control",
|
||||
# "placeholder": "x",
|
||||
# "min": "0",
|
||||
# "max": "1000",
|
||||
# "step": "1",
|
||||
# }
|
||||
# ),
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# Generated by Django 4.2 on 2024-03-01 06:49
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import jarvis.tools.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("people", "0011_gymnast_trainers"),
|
||||
("objective", "0016_routine_is_routine"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Passe",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"informations",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Only MarkDown is authorized",
|
||||
null=True,
|
||||
verbose_name="Comments",
|
||||
),
|
||||
),
|
||||
("label", models.CharField(blank=True, max_length=30, null=True)),
|
||||
("regexp", models.CharField(blank=True, max_length=50, null=True)),
|
||||
("number_of_skill", models.PositiveSmallIntegerField(default=0)),
|
||||
(
|
||||
"difficulty",
|
||||
models.DecimalField(decimal_places=1, default=0.0, max_digits=4),
|
||||
),
|
||||
("educatives", models.ManyToManyField(to="objective.educative")),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TrainingProgram",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date",
|
||||
models.DateField(
|
||||
default=jarvis.tools.models.get_default_date,
|
||||
verbose_name="Date",
|
||||
),
|
||||
),
|
||||
("season", models.CharField(editable=False, max_length=9)),
|
||||
("week_number", models.PositiveSmallIntegerField(editable=False)),
|
||||
("repetition", models.PositiveSmallIntegerField(default=1)),
|
||||
("number_of_skill", models.PositiveSmallIntegerField(default=0)),
|
||||
(
|
||||
"difficulty",
|
||||
models.DecimalField(decimal_places=1, default=0.0, max_digits=4),
|
||||
),
|
||||
("rank", models.PositiveSmallIntegerField(default=1)),
|
||||
("score", models.PositiveSmallIntegerField(blank=True, null=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"gymnast",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="people.gymnast"
|
||||
),
|
||||
),
|
||||
(
|
||||
"passe",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="objective.passe",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,7 +1,11 @@
|
|||
from django.db import models
|
||||
from django.db.models import Q
|
||||
|
||||
from jarvis.tools.models import Markdownizable, max_even_if_none
|
||||
from jarvis.tools.models import (
|
||||
Markdownizable,
|
||||
Seasonisable,
|
||||
max_even_if_none,
|
||||
)
|
||||
|
||||
|
||||
class Educative(Markdownizable):
|
||||
|
@ -288,6 +292,15 @@ class Routine(Educative):
|
|||
def __str__(self):
|
||||
return f"{self.long_label} ({self.short_label})"
|
||||
|
||||
def inline_str(self):
|
||||
tmp_string = ""
|
||||
for skill in self.jumps.all():
|
||||
if self.is_routine:
|
||||
tmp_string = tmp_string + " - " + str(skill)
|
||||
else:
|
||||
tmp_string = tmp_string + " & " + str(skill)
|
||||
return tmp_string[3:]
|
||||
|
||||
def compute_informations(self):
|
||||
"""Cette fonction a pour but d'assurer la cohérence des informations d'une combinaison.
|
||||
La fonction vérifie :
|
||||
|
@ -350,18 +363,9 @@ class Routine(Educative):
|
|||
skill.age_girl_masterised, age_girl_masterised
|
||||
)
|
||||
|
||||
# Je ne sais plus pourquoi j'ai mis ce bout de code.
|
||||
# if self.skill_links.all().count() < 5:
|
||||
# is_routine = False
|
||||
# print("Not a routine.")
|
||||
|
||||
if not self.is_routine:
|
||||
is_competitive = False
|
||||
|
||||
if self.skill_links.all().count() != 10:
|
||||
is_competitive = False
|
||||
|
||||
# self.is_routine = is_routine
|
||||
self.is_competitive = is_competitive
|
||||
|
||||
self.difficulty = difficulty
|
||||
|
@ -456,3 +460,102 @@ class RoutineSkill(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return f"{self.rank} - {self.routine.short_label} : {self.skill.short_label}"
|
||||
|
||||
|
||||
class Passe(Markdownizable):
|
||||
"""Classe représentant les passages (à faire pendant un entraînement)."""
|
||||
|
||||
label = models.CharField(max_length=30, null=True, blank=True)
|
||||
educatives = models.ManyToManyField(Educative)
|
||||
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)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Sauve les informations de la personne et initialise les champs nettoyés."""
|
||||
self.difficulty = 0
|
||||
self.number_of_skill = 0
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
if self.label:
|
||||
return f"{self.label} ({self.number_of_skill} | {self.difficulty})"
|
||||
else:
|
||||
return f"- ({self.number_of_skill} | {self.difficulty})"
|
||||
|
||||
|
||||
class TrainingProgram(Seasonisable):
|
||||
"""Classe représentant un entraînement (ensemble de passage)."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Training Program"
|
||||
verbose_name_plural = "Trainings Programs"
|
||||
ordering = [
|
||||
"rank",
|
||||
]
|
||||
unique_together = ["date", "rank"]
|
||||
|
||||
gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE)
|
||||
passe = models.ForeignKey(Passe, on_delete=models.CASCADE)
|
||||
repetition = models.PositiveSmallIntegerField(default=1)
|
||||
number_of_skill = models.PositiveSmallIntegerField(default=0)
|
||||
difficulty = models.DecimalField(max_digits=4, decimal_places=1, default=0.0)
|
||||
rank = models.PositiveSmallIntegerField(default=1)
|
||||
score = models.PositiveSmallIntegerField(blank=True, null=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
f"{self.gymnast} {self.date} - {self.rank} : {self.passe} {self.repetition}"
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Sauve les informations de la personne et initialise les champs nettoyés."""
|
||||
super().save(*args, **kwargs)
|
||||
self.difficulty = self.passe.difficulty * self.repetition
|
||||
self.number_of_skill = self.passe.number_of_skill * self.repetition
|
||||
super().save(*args, **kwargs)
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
const csrf_token = "{{ csrf_token|escapejs }}";
|
||||
const skill_lookup = "{% url 'skill_lookup' %}";
|
||||
// var landing_position = 0
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#id_skill').focus();
|
||||
|
@ -92,6 +93,7 @@
|
|||
label: item.Name,
|
||||
value: item.Name,
|
||||
skillid: item.ID,
|
||||
// landingid: item.landing.ID,
|
||||
notation: item.Notation,
|
||||
}
|
||||
}))
|
||||
|
@ -118,6 +120,7 @@
|
|||
csrfmiddlewaretoken: '{{ csrf_token }}'
|
||||
},
|
||||
}).done(function() {
|
||||
// landing_position_id = ui.item.landingid;
|
||||
insert_selected_skill(ui.item.notation);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -14,30 +14,30 @@
|
|||
{{ hidden }}
|
||||
{% endfor %}
|
||||
<div class="form-group row {% if form.long_label.errors %}has-error has-feedback{% endif %}">
|
||||
<label for="id_date" class="col-4 col-sm-2 col-md-3 col-lg-3 col-xl-3 col-form-label">Long label <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-8 col-sm-9 col-md-9 col-lg-9 col-xl-9">
|
||||
<label for="id_long_label" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Long label <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.long_label }} {% if form.long_label.errors %}
|
||||
{% for error in form.long_label.errors %}{{ error }}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row {% if form.short_label.errors %}has-error has-feedback{% endif %}">
|
||||
<label for="id_date" class="col-4 col-sm-2 col-md-3 col-lg-3 col-xl-3 col-form-label">Short label <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-8 col-sm-6 col-md-6 col-lg-6 col-xl-6">
|
||||
<label for="id_short_label" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Short label <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.short_label }} {% 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-3 col-lg-3 col-xl-3 col-form-label">Is routine ? <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-6 col-sm-5 col-md-4 col-lg-3 col-xl-2">
|
||||
<label for="id_is_routine" 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 }} {% 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-3 col-lg-3 col-xl-3 col-form-label">Is Competitive ? <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-6 col-sm-5 col-md-4 col-lg-3 col-xl-2">
|
||||
<label for="id_is_competitive" 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 }} {% 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>
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
{% load has_group %}
|
||||
|
||||
{% block page_title %}{{ routine.short_label }}{% endblock %}
|
||||
{% block page_title %}{{ combination.short_label }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">{{ routine.short_label }}</h3>
|
||||
<h5 class="card-category mb-0">{{ routine.long_label }}</h4>
|
||||
<h4 class="mb-0">{{ combination.short_label }}</h3>
|
||||
<h5 class="card-category mb-0">{{ combination.long_label }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if skill_link_list %}
|
||||
|
@ -24,21 +24,23 @@
|
|||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-right"></td>
|
||||
<td class="text-center"><b>{{ routine.difficulty }}</b></td>
|
||||
<td class="text-center"><b>{{ combination.difficulty }}</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-3 text-center">Niveau : <a href="#">{{ routine.level }}</a></div>
|
||||
<div class="col-3 text-center">Rank : <a href="#">{{ routine.rank }}</a></div>
|
||||
<div class="col-3 text-center">Age Boy : <a href="#">{{ routine.age_boy_masterised }}</a></div>
|
||||
<div class="col-3 text-center">Age Girl : <a href="#">{{ routine.age_girl_masterised }}</a></div>
|
||||
<div class="col-3 text-center">Niveau : <a href="#">{{ combination.level }}</a></div>
|
||||
<div class="col-3 text-center">Rank : <a href="#">{{ combination.rank }}</a></div>
|
||||
<div class="col-3 text-center">Age Boy : <a href="#">{{ combination.age_boy_masterised }}</a></div>
|
||||
<div class="col-3 text-center">Age Girl : <a href="#">{{ combination.age_girl_masterised }}</a></div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-6 text-center">Routine : <a href="#">{% if routine.is_routine %}Yes{% else %}No{% endif %}</a></div>
|
||||
<div class="col-6 text-center">Competition : <a href="#">{% if routine.is_competitive %}Yes{% else %}No{% endif %}</a></div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<p>No skill defined for this routine.</p>
|
||||
<p>No skill defined for this combination.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if routine.informations %}
|
||||
|
@ -52,9 +54,21 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if combination.informations %}
|
||||
<div class="col-md-12">
|
||||
<hr class="m-t">
|
||||
<h4>Informations</h4>
|
||||
<!-- <p>{{ skill.educative }}</p> -->
|
||||
<span id="comment">
|
||||
{{ combination.to_markdown | safe }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-footer row pt-0">
|
||||
<div class="col-6">
|
||||
<a href="{% url 'routine_listing' %}">
|
||||
<a href="{% url 'combination_listing' %}">
|
||||
<button type="submit" value="add" class="btn btn-icon btn-warning ">
|
||||
<i class="tim-icons icon-double-left"></i>
|
||||
</button>
|
||||
|
@ -62,12 +76,12 @@
|
|||
</div>
|
||||
<div class="col-6 text-right">
|
||||
{% if request.user|has_group:"trainer" %}
|
||||
<a href="{% url 'compose_combination' routine.id %}">
|
||||
<a href="{% url 'compose_combination' combination.id %}">
|
||||
<button type="submit" value="add" class="btn btn-icon btn-warning ">
|
||||
<i class="tim-icons icon-molecule-40"></i>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'combination_update' routine.id %}">
|
||||
<a href="{% url 'combination_update' combination.id %}">
|
||||
<button type="submit" value="add" class="btn btn-icon btn-warning ">
|
||||
<i class="tim-icons icon-pencil"></i>
|
||||
</button>
|
||||
|
@ -78,15 +92,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" >
|
||||
$(function(){
|
||||
// $('#list_skill_table').DataTable({
|
||||
// paging: false,
|
||||
// searching: false,
|
||||
// ordering: false,
|
||||
// "bInfo" : false,
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,117 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-sm-12 col-md-8 col-lg-7 col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="">{% if passe_id %}Edit{% else %}Add{% endif %} Passe</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{% if passe_id %}{% url 'passe_update' passe_id %}{% else %}{% url 'passe_create' %}{% endif %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
|
||||
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{ hidden }}
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-group row {% if form.long_label.errors %}has-error has-feedback{% endif %}">
|
||||
<label for="id_label" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Label</label>
|
||||
<div class="col-8 col-sm-10 col-md-10 col-lg-10 col-xl-10">
|
||||
{{ form.label }}
|
||||
{% if form.label.errors %} <span class="btn btn-sm btn-danger-outline">{% for error in form.label.errors %}{{ error }}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row ">
|
||||
<label for="id_educative" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Educative <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-8 col-sm-8 col-md-6 col-lg-6 col-xl-6 {% if form.educative.errors %}has-danger{% endif %}">
|
||||
{{ form.educative_related }}
|
||||
{% if form.educative.errors %} <span class="btn btn-sm btn-danger-outline">{% for error in form.educative.errors %}{{ error }}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row {% if form.repetition.errors %}has-error has-feedback{% endif %}">
|
||||
<label for="id_repetition" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">{{ form.repetition.label }} <span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-3">
|
||||
{{ form.repetition }}
|
||||
{% if form.repetition.errors %} <span class="btn btn-sm btn-danger-outline">{% for error in form.repetition.errors %}{{ error }}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row {% if form.regexp.errors %}has-error has-feedback{% endif %}">
|
||||
<label for="id_regexp" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">{{ form.regexp.label }}<span class="text-danger"><b>*</b></span></label>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-3">
|
||||
{{ form.regexp }}
|
||||
{% if form.regexp.errors %} <span class="btn btn-sm btn-danger-outline">{% for error in form.regexp.errors %}{{ error }}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row ">
|
||||
<label for="id_informations" 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 %}">
|
||||
{{ form.informations }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" value="Save" class="btn btn-warning" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block footerscript %}
|
||||
<script type="text/javascript" >
|
||||
$(function(){
|
||||
const csrf_token = "{{ csrf_token|escapejs }}";
|
||||
const educative_lookup = "{% url 'educative_lookup' %}";
|
||||
|
||||
$('#id_educative_related').autocomplete({
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: educative_lookup,
|
||||
method: "POST",
|
||||
data: {
|
||||
pattern: $('#id_educative_related').val(),
|
||||
csrfmiddlewaretoken: csrf_token
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if(data.length != 0) {
|
||||
response($.map(data, function(item) {
|
||||
return {
|
||||
label: item.long_label,
|
||||
value: item.short_label,
|
||||
educativeid: item.ID
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
response([{ label: 'No result found.', value: '' }]);
|
||||
};
|
||||
},
|
||||
|
||||
error: function (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
},
|
||||
minLength: 3,
|
||||
select: function (event, ui) {
|
||||
$($(this).data('ref')).val(ui.item.educativeid);
|
||||
},
|
||||
{% if request.session.template == 0 %}
|
||||
classes: {
|
||||
"ui-widget-content": "custom_autocomplete_ul",
|
||||
"ui-autocomplete": "custom_autocomplete_ul",
|
||||
"ui-menu-item-wrapper": "custom_autocomplete_li",
|
||||
"ui-menu-item": "custom_autocomplete_li",
|
||||
},
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% load has_group %}
|
||||
|
||||
{% block page_title %}{{ combination.short_label }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<b><u>Label :</b></u> {{ passe.label }}<br />
|
||||
<b><u>Content :</b></u> {% for educative in passe.educative.all %}{{ educative }}{% if not forloop.last %} & {% endif %}{% endfor %}<br />
|
||||
<b><u>Répétition :</b></u> {{ passe.repetition }}<br />
|
||||
{% if passe.regexp %}<b><u>RegExp :</b></u> {{ passe.regexp }}<br />{% endif %}
|
||||
<br />
|
||||
<a href="#">{{ passe.label }}{% if passe.regexp %}{{ passe.regexp }}{% endif %}</a> {{ passe.repetition }}<br /><br />
|
||||
{{ number_of_skill }} - {{ difficulty }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,70 @@
|
|||
{% extends "listing.html" %}
|
||||
|
||||
{% block datacontent %}
|
||||
<div class="card mb-0">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<h4 class=""> Passes Listing</h4>
|
||||
</div>
|
||||
<div class="col-1 ml-auto">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
{% if passes_list %}
|
||||
<table class="table tablesorter table-striped" data-sort="table" id="routine_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" style="width: 10%">label</th>
|
||||
<th class="header text-center" style="width: 7%">educative</th>
|
||||
<th class="header text-center" style="width: 7%">repetition</th>
|
||||
<th class="header text-center" style="width: 7%">regexp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for passe in passes_list %}
|
||||
<tr role="row" class="{% cycle 'odd' 'even' %}">
|
||||
<td class="text-center">{{ passe.label }}</td>
|
||||
<td class="text-center">{{ passe.educative }}</td>
|
||||
<td class="text-center">{{ passe.repetition }}</td>
|
||||
<td class="text-center">{{ passe.regexp }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted-text">There are no passe corresponding to your criterias.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footerscript %}
|
||||
<script type="text/javascript">
|
||||
// $(document).ready(function () {
|
||||
// $('routine_table').tablesorter({
|
||||
// {% if request.user|has_group:"trainer" %}
|
||||
// headers: {
|
||||
// 0: { sorter: false },
|
||||
// },
|
||||
// sortList: [[1, 0]],
|
||||
// {% else %}
|
||||
// sortList: [[0, 0]],
|
||||
// {% endif %}
|
||||
// });
|
||||
|
||||
// $('#routine_table').DataTable({
|
||||
// scrollY: '50vh',
|
||||
// scrollCollapse: true,
|
||||
// paging: false,
|
||||
// searching: false,
|
||||
// ordering: false,
|
||||
// "bInfo": false,
|
||||
// });
|
||||
// });
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
{% load has_group %}
|
||||
|
||||
{% block page_title %}{{ combination.short_label }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if trainingprogram_list %}
|
||||
<table class="table table-striped tablesorter" id="trainingprogram_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4" class="text-center">{{ date|date:"l d F Y" }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for trainingprogram in trainingprogram_list %}
|
||||
<tr role="row" class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ trainingprogram.rank }}</td>
|
||||
<td>{{ trainingprogram.passe.label }} {{ trainingprogram.passe.repetition }}</td>
|
||||
<td class="text-center"><b>{{ trainingprogram.passe.number_of_skill}}</b></td>
|
||||
<td class="text-center">{{ trainingprogram.passe.difficulty }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tr>
|
||||
<td colspan="2" class="text-right"><b>TOTAL</b></td>
|
||||
<td class="text-center"><b>{{ number_of_skill }}</b></td>
|
||||
<td class="text-center"><b>{{ difficulty }}</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -3,6 +3,13 @@ from . import views
|
|||
|
||||
|
||||
urlpatterns = [
|
||||
#
|
||||
#
|
||||
#
|
||||
path(r"educative/lookup/", views.educative_lookup, name="educative_lookup"),
|
||||
#
|
||||
# SKILLS
|
||||
#
|
||||
re_path(
|
||||
r"skill/(?P<field>(level|rank|difficulty))/(?P<expression>[\w]+)/(?P<value>[\w]+)/",
|
||||
views.skill_listing,
|
||||
|
@ -21,7 +28,9 @@ urlpatterns = [
|
|||
name="skill_without_prerequisite",
|
||||
),
|
||||
path(r"skill/", views.skill_listing, name="skill_list"),
|
||||
# Combination
|
||||
#
|
||||
# COMBINATION
|
||||
#
|
||||
path(
|
||||
r"combination/lookup/<str:search_type>",
|
||||
views.combination_lookup,
|
||||
|
@ -63,7 +72,6 @@ urlpatterns = [
|
|||
views.routine_listing,
|
||||
name="routine_list_for_gymnast",
|
||||
),
|
||||
path(r"combination/", views.combination_listing, name="combination_list"),
|
||||
path(
|
||||
r"combination/educative/",
|
||||
views.educative_combination_listing,
|
||||
|
@ -75,4 +83,28 @@ urlpatterns = [
|
|||
views.competition_routine_listing,
|
||||
name="competition_routine_listing",
|
||||
),
|
||||
path(r"combination/", views.combination_listing, name="combination_listing"),
|
||||
#
|
||||
# PASSES
|
||||
#
|
||||
path(r"passe/<int:passe_id>/", views.passe_details, name="passe_details"),
|
||||
path(r"passe/", views.passe_listing, name="passe_listing"),
|
||||
path(
|
||||
r"passe/add/",
|
||||
views.passe_create_or_update,
|
||||
name="passe_create",
|
||||
),
|
||||
path(
|
||||
r"passe/edit/<int:passe_id>/",
|
||||
views.passe_create_or_update,
|
||||
name="passe_update",
|
||||
),
|
||||
#
|
||||
# TRAININGPROGRAM
|
||||
#
|
||||
path(
|
||||
r"trainingprogram/detail/date/<str:date>/gymnast/<int:gymnast_id>/",
|
||||
views.trainingprogram_details,
|
||||
name="trainingprogram_details",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,21 +5,50 @@ from django.shortcuts import render, get_object_or_404
|
|||
from django.views.decorators.http import require_http_methods
|
||||
from django.urls import reverse
|
||||
|
||||
import pendulum
|
||||
|
||||
from jarvis.people.models import Gymnast
|
||||
|
||||
from .forms import (
|
||||
SkillForm,
|
||||
PasseForm,
|
||||
CombinationForm,
|
||||
CombinationSkillForm,
|
||||
)
|
||||
from .models import (
|
||||
Skill,
|
||||
Passe,
|
||||
Routine,
|
||||
Educative,
|
||||
RoutineSkill,
|
||||
TrainingProgram,
|
||||
PrerequisiteClosure,
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def educative_lookup(request):
|
||||
"""
|
||||
Récupère la liste des skill à la volée suivant des caractères de recherche entrés (min 3
|
||||
caractères).
|
||||
"""
|
||||
results = []
|
||||
pattern = request.POST.get("pattern", None)
|
||||
|
||||
# Ignore queries shorter than length 2
|
||||
if pattern is not None and len(pattern) > 2:
|
||||
model_results = Educative.objects.filter(
|
||||
Q(short_label__icontains=pattern) | Q(long_label__icontains=pattern)
|
||||
)
|
||||
results = [
|
||||
{"ID": x.id, "long_label": x.long_label, "short_label": x.short_label}
|
||||
for x in model_results
|
||||
]
|
||||
|
||||
return JsonResponse(results, safe=False)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def skill_lookup(request):
|
||||
|
@ -359,9 +388,14 @@ def combination_details(request, combination_id):
|
|||
combination_id (int) identifiant d'une routine
|
||||
"""
|
||||
|
||||
routine = get_object_or_404(Routine, pk=combination_id)
|
||||
routine.compute_informations()
|
||||
context = {"routine": routine, "skill_link_list": routine.skill_links.all()}
|
||||
combination = get_object_or_404(Routine, pk=combination_id)
|
||||
combination.compute_informations()
|
||||
combination_string = combination.inline_str()
|
||||
context = {
|
||||
"combination": combination,
|
||||
"combination_string": combination_string,
|
||||
"skill_link_list": combination.skill_links.all(),
|
||||
}
|
||||
return render(request, "combinations/details.html", context)
|
||||
|
||||
|
||||
|
@ -463,3 +497,96 @@ def unlink_skill_from_combination(request):
|
|||
return HttpResponse(409)
|
||||
|
||||
return HttpResponse(200)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def passe_listing(request):
|
||||
"""Liste des passages."""
|
||||
passe_listing = Passe.objects.all()
|
||||
context = {"passe_listing": passe_listing}
|
||||
return render(request, "passes/list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def passe_details(request, passe_id):
|
||||
"""Détails d'un passage."""
|
||||
|
||||
is_skill = False
|
||||
passe = get_object_or_404(Passe, pk=passe_id)
|
||||
educative_list = passe.educatives.all()
|
||||
number_of_educative = educative_list.count()
|
||||
|
||||
context = {
|
||||
"passe": passe,
|
||||
"is_skill": is_skill,
|
||||
"educative_list": educative_list,
|
||||
"difficulty": passe.difficulty,
|
||||
"number_of_skill": passe.number_of_skill,
|
||||
"number_of_educative": number_of_educative,
|
||||
}
|
||||
return render(request, "passes/details.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def passe_create_or_update(request, passe_id=None):
|
||||
"""Création d'un passage.
|
||||
|
||||
Args:
|
||||
passe_id (int) identifiant d'un object de classe <Passe>.
|
||||
"""
|
||||
if passe_id:
|
||||
passe = get_object_or_404(Passe, pk=passe_id)
|
||||
else:
|
||||
passe = None
|
||||
|
||||
if request.method == "POST":
|
||||
form = PasseForm(request.POST, instance=passe)
|
||||
|
||||
if form.is_valid():
|
||||
passe = form.save()
|
||||
return HttpResponseRedirect(reverse("passe_details", args=(passe.pk,)))
|
||||
|
||||
return render(request, "passes/create.html", {"form": form})
|
||||
|
||||
form = PasseForm(instance=passe)
|
||||
context = {"form": form, "passe_id": passe_id}
|
||||
return render(request, "passes/create.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def trainingprogram_details(request, date=None, gymnast_id=None):
|
||||
"""Détails d'un entraînement."""
|
||||
trainingprogram_id = None
|
||||
if trainingprogram_id is not None:
|
||||
trainingprogram = get_object_or_404(TrainingProgram, pk=trainingprogram_id)
|
||||
trainingprogram_list = None
|
||||
else:
|
||||
trainingprogram = None
|
||||
trainingprogram_list = TrainingProgram.objects.all()
|
||||
parsed_date = pendulum.parse(date).date()
|
||||
if date is not None:
|
||||
trainingprogram_list = trainingprogram_list.filter(date=parsed_date)
|
||||
|
||||
if gymnast_id is not None:
|
||||
trainingprogram_list = trainingprogram_list.filter(gymnast=gymnast_id)
|
||||
|
||||
# trainingprogram_list = trainingprogram_list.order_by("rank")
|
||||
|
||||
difficulty = 0
|
||||
number_of_skill = 0
|
||||
for trainingprogram in trainingprogram_list:
|
||||
difficulty += trainingprogram.difficulty
|
||||
number_of_skill += trainingprogram.number_of_skill
|
||||
|
||||
context = {
|
||||
"date": parsed_date,
|
||||
"difficulty": difficulty,
|
||||
"number_of_skill": number_of_skill,
|
||||
"trainingprogram": trainingprogram,
|
||||
"trainingprogram_list": trainingprogram_list,
|
||||
}
|
||||
return render(request, "trainingprograms/details.html", context)
|
||||
|
|
|
@ -75,6 +75,27 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center ml-1">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Training Program</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if date_list %}
|
||||
{% for date in date_list %}
|
||||
<a href="{% url 'trainingprogram_details' date gymnast_id %}">{{ date }}</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
Pas de training planifié.
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer text-right text-muted pt-0">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var timeFormat = 'DD-MM-YYYY';
|
||||
|
|
|
@ -20,6 +20,7 @@ import pendulum
|
|||
|
||||
from jarvis.followup.models import Event
|
||||
from jarvis.followup.forms import GymnastHasRoutineForm
|
||||
from jarvis.objective.models import TrainingProgram
|
||||
from jarvis.followup.models import (
|
||||
Note,
|
||||
Plan,
|
||||
|
@ -255,6 +256,12 @@ def gymnast_display_scores_chrono(request, gymnast_id):
|
|||
gymnast=gymnast_id, date__gte=start_date
|
||||
).order_by("date")
|
||||
base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof"))
|
||||
today = pendulum.now().date()
|
||||
date_list = (
|
||||
TrainingProgram.objects.filter(gymnast=gymnast_id, date__gte=today)
|
||||
.values_list("date", flat=True)
|
||||
.distinct()
|
||||
)
|
||||
|
||||
context = {
|
||||
"intensity_list": intensity_list,
|
||||
|
@ -263,6 +270,7 @@ def gymnast_display_scores_chrono(request, gymnast_id):
|
|||
"chrono_r1": base_queryset.filter(chrono_type=1),
|
||||
"chrono_r2": base_queryset.filter(chrono_type=2),
|
||||
"chrono_rf": base_queryset.filter(chrono_type=3),
|
||||
"date_list": date_list,
|
||||
"gymnast_id": gymnast_id,
|
||||
}
|
||||
return render(request, "gymnasts/tabs/tab_intensity_and_chronos.html", context)
|
||||
|
|
Loading…
Reference in New Issue