Season information management and many other lil' things

This commit is contained in:
Gregory Trullemans 2022-11-04 10:49:57 +01:00
parent 8cb3551f13
commit 81f45a681d
12 changed files with 453 additions and 39 deletions

View File

@ -4,14 +4,14 @@
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-10 col-xl-8">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if intensity_id %}Edit{% else %}Add{% endif %} intensity</h4>
</div>
<div class="card-body">
<form
action="{% if intensity_id %}{% url 'intensity_update' note_id %}{% else %}{% url 'intensity_create' %}{% endif %}"
action="{% if intensity_id %}{% url 'intensity_update' intensity_id %}{% else %}{% url 'intensity_create' %}{% endif %}"
method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
@ -97,6 +97,49 @@
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
$('#id_club_related').autocomplete({
source: function(request, response) {
$.ajax({
url: club_lookup,
method: "POST",
data: {
pattern: $('#id_club_related').val(),
csrfmiddlewaretoken: csrf_token
},
dataType: "json",
success: function(data) {
if(data.length != 0) {
response($.map(data, function(item) {
return {
label: item.Name,
value: item.Name,
clubid: 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.clubid);
},
{% 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>
<script src="{% static " js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}

View File

@ -7,7 +7,7 @@
<div class="card">
<div class="card-header row">
<div class="col-8">
<h4 class="">Routine done list {% if gymnast %}for <i>{{ gymnast }}</i>{% endif %}</h4>
<h4 class="">Routine done list {% if gymnast %}for <a href="{% url 'gymnast_details_tab' gymnast.id 'routine' %}"><i>{{ gymnast }}</i></a>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
@ -46,8 +46,7 @@
<td class="text-center">{{ routine_done.get_routine_type_display }}</td>
<td class="text-left">
{% if routine_done.routine %}
<a href="{% url 'routine_details' routine_done.routine.id %}">{{
routine_done.routine.long_label }}</a>
<a href="{% url 'routine_details' routine_done.routine.id %}">{{ routine_done.routine.long_label }}</a>
{% else %}
-
{% endif %}

View File

@ -7,9 +7,7 @@
<div class="card">
<div class="card-header row">
<div class="col-8">
<h4 class="">Scores listing {% if gymnast %}for <a
href="{% url 'gymnast_details_tab' gymnast.id 'scores' %}"><i>{{ gymnast }}</i></a>{% endif
%}</h4>
<h4 class="">Scores listing {% if gymnast %}for <a href="{% url 'gymnast_details_tab' gymnast.id 'scores' %}"><i>{{ gymnast }}</i></a>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
@ -57,15 +55,17 @@
<td class="text-right">{{ score.point_difficulty }}</td>
<td class="text-right">{{ score.point_time_of_flight }}</td>
<td class="text-right">{{ score.point_horizontal_displacement }}</td>
<td class="text-right">{% if score.penality > 0 %}-{{ score.penality }}{% else %}-{% endif
%}</td>
<td class="text-right">
{% if score.penality > 0 %}-{{ score.penality }}
{% else %}-
{% endif %}</td>
<td class="text-right"><b>{{ score.total }}</b></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
There are no scores corresponding to your criterias
There are no scores corresponding to your criterias
{% endif %}
</div>
</div>

View File

@ -0,0 +1,142 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if season_information_id %}Edit{% else %}Add{% endif %} season informations</h4>
</div>
<div class="card-body">
<form
action="{% if season_information_id %}{% url 'season_information_update' note_id %}{% else %}{% url 'season_information_create' %}{% endif %}"
method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_date" class="col-sm-3 col-md-3 col-lg-3 col-xl-3 col-form-label">Gymnast
<span class="text-danger"><b>*</b></span></label>
<div
class="col-sm-9 col-md-9 col-lg-6 {% if form.jumper.errors %}has-danger{% endif %}">
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_season" class="col-sm-3 col-md-3 col-lg-3 col-xl-3 col-form-label">Season <span class="text-danger"><b>*</b></span></label>
<div
class="col-sm-3 col-md-4 col-lg-4 col-xl-3 {% if form.season.errors %}has-danger{% endif %}">
{{ form.season }}
{% if form.season.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.season.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_club" class="col-4 col-sm-3 col-form-label">Club <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-md-9 col-lg-6 {% if form.club.errors %}has-danger{% endif %}">
{{ form.club }}
{{ form.club_related }}
{% if form.club.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.club.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_number_of_training_sessions_per_week" class="col-sm-3 col-md-3 col-lg-3 col-xl-3 col-form-label">Training/week <span class="text-danger"><b>*</b></span></label>
<div class="col-sm-3 col-md-3 col-lg-2 {% if form.number_of_training_sessions_per_week.errors %}has-danger{% endif %}">
{{ form.number_of_training_sessions_per_week }}
{% if form.number_of_training_sessions_per_week.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.number_of_training_sessions_per_week.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_number_of_hours_per_week" class="col-sm-3 col-md-3 col-lg-3 col-xl-3 col-form-label">Hours/week <span class="text-danger"><b>*</b></span></label>
<div class="col-sm-3 col-md-3 col-lg-2 {% if form.number_of_hours_per_week.errors %}has-danger{% endif %}">
{{ form.number_of_hours_per_week }}
{% if form.number_of_hours_per_week.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.number_of_hours_per_week.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_category" class="col-sm-3 col-md-3 col-lg-3 col-xl-3 col-form-label">Category <span class="text-danger"><b>*</b></span></label>
<div class="col-sm-3 col-md-3 col-lg-3 {% if form.category.errors %}has-danger{% endif %}">
{{ form.category }}
{% if form.category.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.category.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if note_id %}Save{% else %}Add{% endif %}"
class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(function () {
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
const club_lookup = "{% url 'club_lookup' %}";
$('#id_season').keyup(function(){
if($(this).val().length == 4 && $(this).val().indexOf(',') == -1) {
var value = parseInt($(this).val());
var res = value.toString() + '-' + (value + 1).toString();
$(this).val(res);
}
});
$('#id_club_related').autocomplete({
source: function(request, response) {
$.ajax({
url: club_lookup,
method: "POST",
data: {
pattern: $('#id_club_related').val(),
csrfmiddlewaretoken: csrf_token
},
dataType: "json",
success: function(data) {
if(data.length != 0) {
response($.map(data, function(item) {
return {
label: item.Name,
value: item.Name,
clubid: 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.clubid);
},
{% 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>
{% if request.session.template == 0 %}
<script src="{% static "js/template_users/gymnast_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static "js/template_users/gymnast_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,86 @@
{% extends "listing.html" %}
{% load has_group %}
{% block datacontent %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">
<div class="card">
<div class="card-header row">
<div class="col-8">
<h4 class="">Season informations listing {% if gymnast %}for <a href="{% url 'gymnast_details_tab' gymnast.id 'scores' %}"><i>{{ gymnast }}</i></a>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
{% if request.user|has_group:"trainer" %}
<a href="{% url 'season_information_create' %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
{% if season_information_list %}
<table class="table tablesorter table-striped mb-0" data-sort="table" id="score_table">
<thead>
<tr>
<th></th>
<th class="header text-left">Gymnast</th>
<th class="header text-left">Season</th>
<th class="header text-left"># training/week</th>
<th class="header text-left"># hours/week</th>
<th class="header text-left">category</th>
<th class="header text-center">club</th>
</tr>
</thead>
<tbody>
{% for season_information in season_information_list %}
<tr>
<td>
<a href="{% url 'season_information_update' season_information.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td><a href="{% url 'gymnast_details' season_information.gymnast.id %}">{{ season_information.gymnast }}</a></td>
<td>{{ season_information.season }}</td>
<td>{{ season_information.number_of_training_sessions_per_week }}</td>
<td>{{ season_information.number_of_hours_per_week }}</td>
<td class="text-right">{{ season_information.get_category_display }}</td>
<td class="text-right">{{ season_information.club.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
There are no scores corresponding to your criterias
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('[data-sort="table"]').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[2, 1]]
});
$('#score_table').DataTable({
scrollY: 500,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

View File

@ -24,7 +24,8 @@
<h4 class="title">{{ gymnast.first_name }} {{ gymnast.last_name }}</h4>
</a>
<p class="description">
{{ last_season_information.club.name }}
{{ last_season_information.club.name }}<br />
{{ last_season_information.get_category_display }}
</p>
</div>
<div class="card-description">
@ -47,6 +48,10 @@
<b>Routine</b> : {% if best_routine %}<b>{{ best_routine.0.tof }}</b> ({{ best_routine.0.date | date:"d-m-Y" }}){% else %} (no information){% endif %}
</li>
<a href="{% url 'gymnast_report_export' gymnast.id %}">PDF</a>
{% if user_is_trainer %} -
<a href="{% url 'add_season_information_for_gymnast' gymnast.id %}">Add season details</a> -
<a href="{% url 'season_information_for_gymnast' gymnast.id %}">Seasons details</a>
{% endif %}
</div>
</div>
</div>

View File

@ -11,8 +11,8 @@
<h4 class=""><i class="icon-primary fal fa-laugh-wink"></i> Hi {{ user.username }} !</h4>
</div>
<div class="card-body">
Welcome to Ultron v0.63 <span class="text-muted">(last update : 31-10-2022)</span><br />
This application is here to help us to manage the gymnasts (evolution, evaluation, routines, scores, …).
Welcome to Ultron v0.70 <span class="text-muted">(last update : 4-11-2022)</span><br />
This application is here to help coaches to manage the gymnasts (evolution, evaluation, routines, scores, …).
This tool is not perfect so feel free to make improvement proposals, bug reports, … by sending me an <a href="mailto:gregory@flyingacrobaticstrampoline.be">email</a>.<br />
<br />
</div>

View File

@ -13,6 +13,7 @@ from .models import (
HeightWeight,
LearnedSkill,
GymnastHasRoutine,
SeasonInformation,
NumberOfRoutineDone,
)
@ -600,23 +601,49 @@ class IntensityForm(forms.ModelForm):
)
# class SeasonInformationForm():
class SeasonInformationForm(forms.ModelForm):
class Meta:
model = SeasonInformation
fields = (
"gymnast",
"season",
"number_of_training_sessions_per_week",
"number_of_hours_per_week",
"category",
"club",
)
widgets = {
"gymnast": forms.HiddenInput(),
"season": forms.TextInput(
attrs={"class": "form-control", "placeholder": "202x-202y"}
),
"number_of_training_sessions_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "5"}
),
"number_of_hours_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "11.5"}
),
"category": forms.Select(attrs={"class": "form-control selectpicker"}),
"club": forms.HiddenInput(),
}
# "trainings_by_week": forms.TextInput(
# attrs={"class": "form-control", "placeholder": "5"}
# ),
# "hours_by_week": forms.TextInput(
# attrs={"class": "form-control", "placeholder": "11,5"}
# ),
# "club": forms.HiddenInput(),
# club_related = forms.CharField(
# required=False,
# widget=forms.TextInput(
# attrs={
# "class": "form-control",
# "placeholder": "Searching club…",
# "data-ref": "#id_club",
# }
# ),
# )
club_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching club…",
"data-ref": "#id_club",
}
),
)
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)

View File

@ -12,7 +12,7 @@ from ultron.planning.models import Event
from ultron.objective.models import Educative, Skill, Routine
from ultron.location.models import Club
ROUTINE_CHOICE = (
ROUTINE_TYPE_CHOICE = (
(0, "Other"),
(1, "R1"),
(2, "R2"),
@ -319,7 +319,7 @@ class GymnastHasRoutine(models.Model):
on_delete=models.CASCADE,
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_CHOICE, verbose_name="Type", default="1"
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
date_begin = models.DateField(default=date.today, verbose_name="Date begin")
date_end = models.DateField(verbose_name="Date end", null=True, blank=True)
@ -336,6 +336,7 @@ class NumberOfRoutineDone(Seasonisable):
class Meta:
verbose_name = "Number of routine done"
verbose_name_plural = "Number of routines done"
unique_together = ("gymnast", "date", "routine_type")
gymnast = models.ForeignKey(
Gymnast,
@ -352,7 +353,7 @@ class NumberOfRoutineDone(Seasonisable):
blank=True,
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_CHOICE, verbose_name="Type", default="1"
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
number_of_try = models.PositiveSmallIntegerField(
verbose_name="Number of try", default=0

View File

@ -102,6 +102,9 @@ urlpatterns = [
name="learnedskill_create",
),
path(r"learnedskill/new/", views.gymnast_learn_skill, name="gymnast_learn_skill"),
#
#
# Score
path(r"score/", views.score_listing, name="score_listing"),
path(
r"score/gymnast/<int:gymnast_id>/",
@ -117,6 +120,9 @@ urlpatterns = [
path(
r"score/edit/<int:score_id>/", views.score_create_or_update, name="score_update"
),
#
#
# Accident
path(r"accident/search/", views.accident_listing, name="accident_search"),
path(r"accident/", views.accident_listing, name="accident_list"),
path(r"accident/add/", views.accident_create_or_update, name="accident_create"),
@ -133,6 +139,9 @@ urlpatterns = [
path(
r"accident/<int:accident_id>/", views.accident_detail, name="accident_details"
),
#
#
# Mindstate
path(r"mindstate/", views.mindstate_listing, name="mindstate_list"),
path(
r"mindstate/gymnast/<int:gymnast_id>/",
@ -155,6 +164,9 @@ urlpatterns = [
views.mindstate_detail,
name="mindstate_details",
),
#
#
# Height/Weight
path(
r"heightweight/gymnast/<int:gymnast_id>/",
views.heightweight_listing,
@ -175,6 +187,9 @@ urlpatterns = [
views.heightweight_create_or_update,
name="heightweight_update",
),
#
#
# Routine Done
path(
r"routinedone/add/",
views.routinedone_create_or_update,
@ -201,6 +216,9 @@ urlpatterns = [
name="routinedone_list_for_gymnast",
),
path(r"routinedone/", views.routine_done_listing, name="routinedone_list"),
#
#
# Plan
path(r"plan/add/", views.plan_create_or_update, name="plan_create"),
path(r"plan/<int:plan_id>/edit/", views.plan_create_or_update, name="plan_update"),
path(
@ -218,4 +236,27 @@ urlpatterns = [
views.plan_create_or_update,
name="plan_update",
),
#
#
# Season Informations
path(
r"season-information/gymnast/<int:gymnast_id>/",
views.season_information_listing,
name="season_information_for_gymnast",
),
path(
r"season-information/add/",
views.season_information_create_or_update,
name="season_information_create",
),
path(
r"season-information/<int:season_information_id>/edit/",
views.season_information_create_or_update,
name="season_information_update",
),
path(
r"season-information/add/<int:gymnast_id>/",
views.season_information_create_or_update,
name="add_season_information_for_gymnast",
),
]

View File

@ -24,6 +24,7 @@ from .models import (
LearnedSkill,
HeightWeight,
ChronoDetails,
SeasonInformation,
NumberOfRoutineDone,
)
@ -37,6 +38,7 @@ from .forms import (
IntensityForm,
HeightWeightForm,
LearnedSkillForm,
SeasonInformationForm,
NumberOfRoutineDoneForm,
)
@ -926,7 +928,10 @@ def routinedone_create_or_update(request, routinedone_id=None, gymnast_id=None):
if form.is_valid():
form.save()
return HttpResponseRedirect(
reverse("gymnast_details", args=(form.cleaned_data["gymnast"].id,))
reverse(
"gymnast_details_tab",
args=(form.cleaned_data["gymnast"].id, "routine"),
)
)
else:
return render(request, "followup/routinedone/create.html", {"form": form})
@ -1057,3 +1062,68 @@ def intensity_create_or_update(request, intensity_id=None, gymnast_id=None):
form = IntensityForm(instance=intensity, initial=data)
context = {"form": form, "intensity_id": intensity_id}
return render(request, "followup/intensities/create.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def season_information_create_or_update(
request, season_information_id=None, gymnast_id=None
):
"""Création d'un record de la class Intentity.
Args:
season_information_id (int): identifiant d'un plan (classe <Intensity>).
gymnast_id (int): identifiant d'un gymnaste (classe <Gymnast>).
"""
if season_information_id:
season_information = get_object_or_404(
SeasonInformation, pk=season_information_id
)
data = {
"gymnast": season_information_id.gymnast.id,
"gymnast_related": str(season_information_id.gymnast),
}
else:
season_information = None
data = {}
if gymnast_id:
gymnast = get_object_or_404(Gymnast, pk=gymnast_id)
data["gymnast"] = gymnast_id
data["gymnast_related"] = str(gymnast)
if request.method == "POST":
form = SeasonInformationForm(request.POST, instance=season_information)
if form.is_valid():
season_information = form.save()
return HttpResponseRedirect(
reverse(
"gymnast_details",
args=(form.cleaned_data["gymnast"].id),
)
)
else:
return render(
request, "followup/seasoninformations/create.html", {"form": form}
)
form = SeasonInformationForm(instance=season_information, initial=data)
context = {"form": form, "season_information_id": season_information_id}
return render(request, "followup/seasoninformations/create.html", context)
@login_required
@require_http_methods(["GET"])
def season_information_listing(request, gymnast_id=None):
"""Liste toutes les informations de saison pour un gymnaste"""
if gymnast_id is not None:
gymnast = Gymnast.objects.get(pk=gymnast_id)
season_information_list = SeasonInformation.objects.filter(gymnast=gymnast_id)
else:
season_information_list = SeasonInformation.objects.all()
context = {"season_information_list": season_information_list, "gymnast": gymnast}
return render(request, "followup/seasoninformations/list.html", context)

View File

@ -11,10 +11,10 @@ class Educative(Markdownizable):
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
Par défaut, le niveau d'une figure est son coéfficient de difficulté (exprimé en 10ème
pour avoir des nombres entiers) auquel on ajoute 1 pour les positions tendue.
Exemple :
Exemples :
- 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