Refactoring Skill, templates reorganisation, new jquery, ...

This commit is contained in:
Trullemans Gregory 2021-11-24 13:24:03 +01:00
parent 318bbe8f67
commit b9bff531e3
59 changed files with 65897 additions and 160 deletions

View File

@ -49,6 +49,7 @@ INSTALLED_APPS = [
'tools',
'profiles',
'planning',
'objective',
]
MIDDLEWARE = [

View File

@ -22,6 +22,7 @@ import people.urls
import Ultron.views
import profiles.urls
import planning.urls
import objective.urls
urlpatterns = [
# Global search
@ -43,7 +44,9 @@ urlpatterns = [
path(r"learnedskill/", include(followup.urls.learnedskill_urlpatterns)),
path(r"score/", include(followup.urls.score_urlpatterns)),
path(r"accident/", include(followup.urls.accident_urlpatterns)),
path(r"skill/", include(followup.urls.skill_urlpatterns)),
# Objective management
path(r"skill/", include(objective.urls.skill_urlpatterns)),
# Planning management
path(r"event/", include(planning.urls.event_urlpatterns)),

View File

@ -50,7 +50,7 @@ def login(request):
else:
context = {}
return render(request, "login.html", context)
return render(request, "ultron/login.html", context)
@login_required
@ -72,7 +72,7 @@ def home(request):
event_list = Event.objects.all().order_by('datebegin')[:5]
context = {'event_list': event_list}
return render(request, "dashboard/dashboard.html", context)
return render(request, "ultron/dashboard/dashboard.html", context)
@login_required
@ -104,4 +104,4 @@ def search(request):
else:
context = {}
return render(request, "search/results.html", context)
return render(request, "ultron/search/results.html", context)

View File

@ -1,5 +1,5 @@
from django.contrib import admin
from .models import Chrono, LearnedSkill, Skill, Point, Accident
from .models import Chrono, LearnedSkill, Point, Accident
from django_extensions.admin import ForeignKeyAutocompleteAdmin
@ -16,18 +16,6 @@ class ChronoAdmin(ForeignKeyAutocompleteAdmin):
}
class SkillAdmin(admin.ModelAdmin):
model = Skill
list_display = ('short_label', 'numeric_notation', 'difficulty', 'level', 'rank')
list_filter = ('difficulty', 'level', 'rank')
search_fields = ('short_label', )
# autocomplete_fields = ['ancestor']
# related_search_fields = {
# 'gymnast': ('last_name', 'first_name')
# }
class LearnedSkillAdmin(admin.ModelAdmin):
model = LearnedSkill
@ -66,4 +54,3 @@ admin.site.register(Accident, AccidentAdmin)
admin.site.register(Point, PointAdmin)
admin.site.register(Chrono, ChronoAdmin)
admin.site.register(LearnedSkill, LearnedSkillAdmin)
admin.site.register(Skill, SkillAdmin)

View File

@ -0,0 +1,79 @@
# Generated by Django 3.2.8 on 2021-11-24 07:39
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('objective', '0001_initial'),
('followup', '0002_mindstate'),
]
operations = [
migrations.AddField(
model_name='accident',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='accident',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='chrono',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='chrono',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='learnedskill',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='learnedskill',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='mindstate',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='mindstate',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='point',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='point',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='learnedskill',
name='skill',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='done_by_gymnasts', to='objective.skill', verbose_name='Skill'),
),
migrations.DeleteModel(
name='Skill',
),
]

View File

@ -5,6 +5,7 @@ from django.db.models.deletion import CASCADE
from tools.models import Markdownizable
from people.models import Gymnast
from planning.models import Event
from objective.models import Skill
class Chrono(models.Model):
@ -36,6 +37,8 @@ class Chrono(models.Model):
score = models.DecimalField(max_digits=5, decimal_places=3)
tof = models.DecimalField(max_digits=5, decimal_places=3, blank=True, null=True)
date = models.DateField(default=date.today, verbose_name="Date")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s (%s - %s)" % (
@ -69,6 +72,8 @@ class Accident(Markdownizable):
# on_delete=models.CASCADE,
# )
date = models.DateField(verbose_name="Date")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s(%s)" % (
@ -77,36 +82,6 @@ class Accident(Markdownizable):
)
class Skill(models.Model):
"""
Représente la ligne d'apprentissage.
"""
class Meta:
verbose_name = "Skill"
verbose_name_plural = 'Skills'
ordering = ['short_label']
short_label = models.CharField(verbose_name="Short label", max_length=50, null=False, blank=False)
long_label = models.CharField(verbose_name="Long label", max_length=255, null=False, blank=False)
difficulty = models.DecimalField(
max_digits=3,
decimal_places=1,
verbose_name="Difficulty"
)
level = models.PositiveSmallIntegerField(default=0)
rank = models.PositiveSmallIntegerField(default=0)
numeric_notation = models.CharField(max_length=25)
ancestor = models.ManyToManyField("self")
def __str__(self):
return "%s (%s) - %s" % (
self.short_label,
self.numeric_notation,
self.difficulty
)
class LearnedSkill(models.Model):
"""
Représente la capacité d'un gymnaste à savori faire un skill de la ligne d'apprentissage.
@ -136,6 +111,8 @@ class LearnedSkill(models.Model):
choices=TYPE_CHOICES, verbose_name="Can do type"
)
date = models.DateField(default=date.today, verbose_name="Date")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Point(models.Model):
@ -154,6 +131,8 @@ class Point(models.Model):
point_horizontal_displacement = models.DecimalField(max_digits=4, decimal_places=3)
penality = models.DecimalField(max_digits=3, decimal_places=1)
total = models.DecimalField(max_digits=6, decimal_places=3)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s" % (
@ -169,4 +148,6 @@ class MindState(Markdownizable):
gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE, default=None)
date = models.DateField(default=date.today, verbose_name="Date")
score = models.PositiveSmallIntegerField(verbose_name="Chrono type")
score = models.PositiveSmallIntegerField(verbose_name="Chrono type")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@ -9,17 +9,6 @@ chrono_urlpatterns = [
path(r"edit/<int:chronoid>/", views.chrono_create_or_update, name="chrono_update"),
]
skill_urlpatterns = [
path(r"", views.skill_listing, name='skill_list'),
path(r"<int:skillid>/", views.skill_details, name="skill_details"),
path(r"lookup/", views.skill_lookup),
re_path(
r"^(?P<field>(level|rank|difficulty))/(?P<expression>[\w]+)/(?P<value>[\w]+)$",
views.skill_listing,
name="skill_listing_by_key",
),
]
learnedskill_urlpatterns = [
path(r"create/", views.learnedskill_create_or_update, name="learnedskill_create"),
path(r"create/<int:gymnastid>/", views.learnedskill_create_or_update, name='learnedskill_create'),

View File

@ -5,7 +5,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.db.models import Q
from people.models import Gymnast
from .models import Chrono, Skill, Point, Accident
from .models import Chrono, Point, Accident
from .forms import ChronoForm, LearnedSkillForm, ScoreForm, AccidentForm
import simplejson
@ -62,71 +62,6 @@ def chrono_create_or_update(request, chronoid=None, gymnastid=None):
return render(request, "followup/chronos/create.html", context)
@login_required
@require_http_methods(["GET"])
def skill_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.GET.get("pattern", None)
# Ignore queries shorter than length 3
if pattern is not None and len(pattern) > 3:
model_results = Skill.objects.filter(
Q(short_label__icontains=pattern) | Q(long_label__icontains=pattern)
)
results = [{"ID": x.id, "Name": str(x)} for x in model_results]
json = simplejson.dumps(results)
return HttpResponse(json, content_type="application/json")
@login_required
@require_http_methods(["GET"])
def skill_listing(request, field=None, expression=None, value=None, level=None):
"""
Récupère la liste des skills suivant un pattern si celui-ci est définit.
"""
pattern = None
if not field or not value or not expression:
pattern = request.GET.get("pattern", None)
if pattern:
skill_list = Skill.objects.filter(
Q(longLabel__icontains=pattern) | Q(shortLabel__icontains=pattern)
)
elif field and expression and value:
kwargs = {"{0}__{1}".format(field, expression): value}
skill_list = Skill.objects.filter(**kwargs)
elif level is not None:
skill_list = Skill.objects.filter
else:
skill_list = Skill.objects.all()
context = {'skill_list': skill_list}
return render(request, 'skills/list.html', context)
@login_required
@require_http_methods(["GET"])
def skill_details(request, skillid):
"""
Récupère toutes les informations d'un skill.
:param skillig: id d'un `skill`
:type skillid: int
:return: skill
"""
context = {"skill": get_object_or_404(Skill, pk=skillid)}
return render(request, "skills/details.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def learnedskill_create_or_update(request, gymnastid=None):
@ -183,13 +118,15 @@ def score_create_or_update(request, scoreid=None, gymnastid=None):
form = ScoreForm(request.POST, instance=score)
if form.is_valid():
# print(form.cleaned_data)
print(form.cleaned_data)
form.save()
return HttpResponseRedirect(
"/gymnast/" + str(form.cleaned_data["gymnast"].id) + "/tab/scores/"
# "/gymnast/" + str(form.cleaned_data["gymnast"].id) + "/tab/scores/"
"/gymnast/details/" + str(form.cleaned_data["gymnast"].id) + "/"
)
# return HttpResponseRedirect("/score/")
else:
print(form.errors)
else:
form = ScoreForm(instance=score, initial=data)

0
objective/__init__.py Normal file
View File

88
objective/admin.py Normal file
View File

@ -0,0 +1,88 @@
from django.contrib import admin
from .models import (
Educative,
TouchPosition,
Skill,
)
from django_extensions.admin import ForeignKeyAutocompleteAdmin
class TouchPositionAdmin(admin.ModelAdmin):
model = TouchPosition
list_display = ("label", "short_label", "allowed_in_competition", "is_default")
ordering = ("label", "short_label")
search_fields = ("label", "short_label")
list_filter = ("allowed_in_competition",)
def duplicate_skill(self, SkillAdmin, request, queryset):
for object in queryset:
object.id = None
object.save()
class SkillAdmin(ForeignKeyAutocompleteAdmin):
model = Skill
fields = (
"label",
"short_label",
"informations",
"departure",
"landing",
"rotation_type",
"position",
"rotation",
"twist",
"difficulty",
"level",
"rank",
"notation",
"simplified_notation",
"is_competitive",
"age_boy",
"age_girl",
"prerequisites",
"educatives",
)
list_display = (
"label",
"difficulty",
"is_competitive",
"level",
"rank",
"notation",
"age_boy",
"age_girl",
)
ordering = ("label", "short_label")
search_fields = ("rank", "label", "short_label")
list_filter = (
"is_competitive",
"departure",
"landing",
"rotation_type",
"rank",
"rotation",
"level",
"difficulty",
"age_boy",
"age_girl",
)
filter_horizontal = ("educatives", "prerequisites")
duplicate_skill.short_description = "Duplicate selected record"
class Media:
js = ("js/admin/skill.js",)
# admin.site.register(Educative) # a commenter je crois
admin.site.register(TouchPosition, TouchPositionAdmin)
admin.site.register(Skill, SkillAdmin)

6
objective/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ObjectiveConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'objective'

View File

@ -0,0 +1,72 @@
# Generated by Django 3.2.8 on 2021-11-24 07:39
from django.db import migrations, models
import django.db.models.deletion
import objective.models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Educative',
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(max_length=255, verbose_name='Long Name')),
('short_label', models.CharField(max_length=255, verbose_name='Short Name')),
('difficulty', models.DecimalField(decimal_places=1, max_digits=3, verbose_name='Difficulty')),
('level', models.PositiveSmallIntegerField(default=0, verbose_name='Level')),
('rank', models.PositiveSmallIntegerField(default=0, verbose_name='Rank')),
('age_boy', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name="Boy's age")),
('age_girl', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name="Girl's age")),
('educatives', models.ManyToManyField(blank=True, related_name='educatives_of', to='objective.Educative')),
('prerequisites', models.ManyToManyField(blank=True, related_name='prerequisite_of', to='objective.Educative')),
],
options={
'verbose_name': 'Educatif',
'verbose_name_plural': 'Educatifs',
'ordering': ['label', 'short_label'],
},
),
migrations.CreateModel(
name='TouchPosition',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', models.CharField(max_length=30, verbose_name='Nom long')),
('short_label', models.CharField(max_length=15, verbose_name='Nom court')),
('allowed_in_competition', models.BooleanField(verbose_name='Compétition')),
('is_default', models.BooleanField(verbose_name='Défaut')),
],
options={
'verbose_name': 'Landing',
'verbose_name_plural': 'Landings',
'ordering': ['label', 'short_label', 'is_default', 'allowed_in_competition'],
},
),
migrations.CreateModel(
name='Skill',
fields=[
('educative_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='objective.educative')),
('position', models.CharField(choices=[('0', 'None'), ('o', 'Tuck'), ('<', 'Pike'), ('/', 'Straight'), ('//', 'Straddle')], max_length=2)),
('rotation_type', models.PositiveSmallIntegerField(choices=[(0, 'None'), (1, 'Frontward'), (2, 'Backward')], verbose_name='Type de rotation')),
('rotation', models.PositiveSmallIntegerField(verbose_name='1/4 de rotation')),
('twist', models.PositiveSmallIntegerField(verbose_name='1/2 Vrille')),
('notation', models.CharField(max_length=25)),
('simplified_notation', models.CharField(max_length=25, verbose_name='Notation simplifiée')),
('is_competitive', models.BooleanField(default=False)),
('departure', models.ForeignKey(default=objective.models.get_default_position, on_delete=django.db.models.deletion.CASCADE, related_name='depart_of', to='objective.touchposition', verbose_name='Take-off position')),
('landing', models.ForeignKey(default=objective.models.get_default_position, on_delete=django.db.models.deletion.CASCADE, related_name='landing_of', to='objective.touchposition', verbose_name='Landing position')),
],
options={
'verbose_name': 'Skill',
'verbose_name_plural': 'Skills',
},
bases=('objective.educative',),
),
]

View File

130
objective/models.py Normal file
View File

@ -0,0 +1,130 @@
from django.db import models
from tools.models import Markdownizable
class Educative(Markdownizable):
"""
Classe `mère`.
"""
class Meta:
verbose_name = "Educatif"
verbose_name_plural = "Educatifs"
ordering = ["label", "short_label"] # 'level',
label = models.CharField(max_length=255, verbose_name="Long Name")
short_label = models.CharField(max_length=255, verbose_name="Short Name")
difficulty = models.DecimalField(
max_digits=3, decimal_places=1, verbose_name="Difficulty"
)
level = models.PositiveSmallIntegerField(verbose_name="Level", default=0)
rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0)
educatives = models.ManyToManyField(
"self", related_name="educatives_of", blank=True, symmetrical=False
)
prerequisites = models.ManyToManyField(
"self", related_name="prerequisite_of", blank=True, symmetrical=False
)
age_boy = models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Boy's age"
)
age_girl = models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Girl's age"
)
def __str__(self):
return "%s - %s (level: %s | diff: %s)" % (
self.rank,
self.label,
self.level,
self.difficulty,
)
class TouchPosition(models.Model):
"""
Classe représentant les différentes position d'arrivée/départ (landing position) en trampoline.
"""
class Meta:
verbose_name = "Landing"
verbose_name_plural = "Landings"
ordering = ["label", "short_label", "is_default", "allowed_in_competition"]
label = models.CharField(max_length=30, verbose_name="Long label")
short_label = models.CharField(max_length=15, verbose_name="Short label")
allowed_in_competition = models.BooleanField(verbose_name="Allowed in competition")
is_default = models.BooleanField(verbose_name="Défault ?")
def __str__(self):
return "%s" % (self.label)
def get_default_position():
"""
Renvoie la position d'arrivée/départ par définie par défaut si elle existe. Sinon, renvoie None.
"""
try:
return TouchPosition.objects.get(default=True).id
except:
return None
class Skill(Educative):
"""
Classe représentant une figure (aka un saut acrobatique).
"""
# SELECT * FROM `objective_skill` WHERE educative_ptr_id NOT IN (SELECT DISTINCT(from_educative_id) FROM `objective_educative_prerequisite`)
# SELECT * FROM `objective_skill`, `objective_educative` WHERE `objective_educative`.id = `objective_skill`.educative_ptr_id
class Meta:
verbose_name = "Skill"
verbose_name_plural = "Skills"
POSITION_CHOICES = (
("0", "None"),
("o", "Tuck"),
# ("c", "Puck"),
("<", "Pike"),
# ("L", "Half pike"),
("/", "Straight"),
("//", "Straddle"),
)
ROTATION_CHOICES = (
(0, "None"),
(1, "Frontward"),
(2, "Backward"),
)
position = models.CharField(max_length=2, choices=POSITION_CHOICES)
departure = models.ForeignKey(
TouchPosition,
related_name="depart_of",
default=get_default_position,
verbose_name="Take-off position",
on_delete=models.CASCADE,
)
landing = models.ForeignKey(
TouchPosition,
related_name="landing_of",
default=get_default_position,
verbose_name="Landing position",
on_delete=models.CASCADE,
)
rotation_type = models.PositiveSmallIntegerField(
choices=ROTATION_CHOICES, verbose_name="Type de rotation"
)
rotation = models.PositiveSmallIntegerField(verbose_name="1/4 de rotation")
twist = models.PositiveSmallIntegerField(verbose_name="1/2 Vrille")
notation = models.CharField(max_length=25)
simplified_notation = models.CharField(
max_length=25, verbose_name="Notation simplifiée"
)
is_competitive = models.BooleanField(default=False)
# importance = models.PositiveSmallIntegerField(default = 1)
def __str__(self):
return "%s (%s)" % (self.short_label, self.notation)

3
objective/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

15
objective/urls.py Normal file
View File

@ -0,0 +1,15 @@
from django.urls import path, re_path
from . import views
# Skills
skill_urlpatterns = [
re_path(
r"^(?P<field>(level|rank|difficulty))/(?P<expression>[\w]+)/(?P<value>[\w]+)$",
views.skill_listing,
name="skill_listing_by_key",
),
path(r"lookup/", views.skill_lookup),
path(r"search/", views.skill_listing),
path(r"<int:skillid>/", views.skill_details, name="skill_details"),
path(r"", views.skill_listing, name="skill_list"),
]

75
objective/views.py Normal file
View File

@ -0,0 +1,75 @@
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.http import HttpResponse, HttpResponseRedirect
from django.db.models import Q
from people.models import Gymnast
from .models import Skill
import simplejson
@login_required
@require_http_methods(["GET"])
def skill_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.GET.get("pattern", None)
# Ignore queries shorter than length 3
if pattern is not None and len(pattern) > 3:
model_results = Skill.objects.filter(
Q(short_label__icontains=pattern) | Q(long_label__icontains=pattern)
)
results = [{"ID": x.id, "Name": str(x)} for x in model_results]
json = simplejson.dumps(results)
return HttpResponse(json, content_type="application/json")
@login_required
@require_http_methods(["GET"])
def skill_listing(request, field=None, expression=None, value=None, level=None):
"""
Récupère la liste des skills suivant un pattern si celui-ci est définit.
"""
pattern = None
if not field or not value or not expression:
pattern = request.GET.get("pattern", None)
if pattern:
skill_list = Skill.objects.filter(
Q(longLabel__icontains=pattern) | Q(shortLabel__icontains=pattern)
)
elif field and expression and value:
kwargs = {"{0}__{1}".format(field, expression): value}
skill_list = Skill.objects.filter(**kwargs)
elif level is not None:
skill_list = Skill.objects.filter
else:
skill_list = Skill.objects.all()
context = {'skill_list': skill_list}
return render(request, 'objectives/skills/list.html', context)
@login_required
@require_http_methods(["GET"])
def skill_details(request, skillid):
"""
Récupère toutes les informations d'un skill.
:param skillig: id d'un `skill`
:type skillid: int
:return: skill
"""
context = {"skill": get_object_or_404(Skill, pk=skillid)}
return render(request, "objectives/skills/details.html", context)

View File

@ -7,7 +7,7 @@ from django.db.models import Q
from django.contrib import messages
from .models import Gymnast
from followup.models import Chrono, LearnedSkill, Skill
from followup.models import Chrono, LearnedSkill, Skill, Point
from .forms import GymnastForm
import simplejson
@ -48,7 +48,8 @@ def gymnast_listing(request):
@login_required
@require_http_methods(["GET"])
def gymnast_details(request, gymnastid):
"""Récupère toutes les informations d'un gymnaste.
"""
Récupère toutes les informations d'un gymnaste.
"""
gymnast = get_object_or_404(Gymnast, pk=gymnastid)
learnedskills_list = LearnedSkill.objects.filter(gymnast=gymnastid).order_by('-date')[:10]
@ -59,6 +60,14 @@ def gymnast_details(request, gymnastid):
best_straightjump = Chrono.objects.filter(gymnast=gymnastid).filter(type=0).order_by('-score')[:1]
best_routine = Chrono.objects.filter(gymnast=gymnastid).filter(type=1).order_by('-score')[:1]
routine_score = Chrono.objects.filter(gymnast=gymnastid).filter(type=1).order_by('-date')
points_routine_1_list = Point.objects.filter(gymnast=gymnastid).filter(type=0).order_by('-date')
points_routine_2_list = Point.objects.filter(gymnast=gymnastid).filter(type=1).order_by('-date')
points_routine_final_list = Point.objects.filter(gymnast=gymnastid).filter(type=2).order_by('-date')
for point in points_routine_1_list:
print(point.point_execution)
context = {
'gymnast': gymnast,
'learnedskills_list': learnedskills_list,
@ -68,10 +77,13 @@ def gymnast_details(request, gymnastid):
'best_routine': best_routine,
'best_straightjump': best_straightjump,
'nb_skill': nb_skill,
'known_skill': known_skill
'known_skill': known_skill,
'points_routine_1_list': points_routine_1_list,
'points_routine_2_list': points_routine_2_list,
'points_routine_final_list': points_routine_final_list,
}
return render(request, "gymnasts/details.html", context)
return render(request, "peoples/gymnasts/details.html", context)
@login_required
@ -100,4 +112,4 @@ def gymnast_create_or_update(request, gymnastid=None):
form = GymnastForm(instance=gymnast, initial=data)
context = {"form": form, "gymnastid": gymnastid}
return render(request, "gymnasts/create.html", context)
return render(request, "peoples/gymnasts/create.html", context)

File diff suppressed because it is too large Load Diff

11
static/js/admin/skill.js Normal file
View File

@ -0,0 +1,11 @@
if (!$) {
$ = django.jQuery;
}
$(function(){
$('#id_label').keyup(function(){
$('#id_short_label').val($('#id_label').val());
});
});

2
static/js/core/jquery-3.6.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -26,17 +26,17 @@
<!-- JQuery UI CSS -->
<link href="{% static "js/plugins/jqueryui/jquery-ui.theme.min.css" %}" rel="stylesheet" />
<link href="{% static "js/plugins/jqueryui/jquery-ui.min.css" %}" rel="stylesheet" />
<!-- Font Awesome Pro -->
<link href="{% static "css/font_awesome_5133.css" %}" rel="stylesheet" />
<!-- Nucleo Icons -->
<link href="{% static "css/nucleo-icons.css" %}" rel="stylesheet" />
<!-- CSS Files -->
<link href="{% static "css/black-dashboard.css" %}" rel="stylesheet" />
<link href="{% static "css/app2.css" %}" rel="stylesheet" />
<!-- CSS Just for demo purpose, don't include it in your project -->
<!-- <link href="../assets/demo/demo.css" rel="stylesheet" /> -->
<script src="{% static "js/core/jquery.min.js" %}"></script>
<script src="{% static "js/plugins/moment.min.js" %}"></script>
<!-- Chart JS -->
<script src="{% static "js/plugins/chartjs.min.js" %}"></script>
<!-- Maps by mapbox -->
<script src='https://api.mapbox.com/mapbox.js/v3.2.0/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.2.0/mapbox.css' rel='stylesheet' />
@ -57,18 +57,22 @@
<a href="javascript:void(0)" class="simple-text logo-normal">Trampoline</a>
</div>
<ul class="nav">
{% menuitem 'home' 'tim-icons icon-chart-pie-36' 'Dashboard' %}
<!-- {% menuitem 'home' 'tim-icons icon-chart-pie-36' 'Dashboard' %} -->
{% menuitem 'home' 'fal fa-chart-pie' 'Dashboard' %}
{% menuitem 'gymnast_list' 'tim-icons icon-badge' 'Gymnasts' %}
{% menuitem 'skill_list' 'tim-icons icon-molecule-40' 'Skills' %}
{% menuitem 'event_list' 'tim-icons icon-calendar-60' 'Events' %}
<!-- {% menuitem 'event_list' 'tim-icons icon-calendar-60' 'Events' %} -->
{% menuitem 'event_list' 'fal fa-calendar-alt' 'Events' %}
{% menuitem 'accident_list' 'tim-icons icon-notes' 'Accidents' %}
<!-- {% menuitem 'club_list' 'tim-icons icon-square-pin' 'Clubs' %} -->
{% menuitem 'place_list' 'tim-icons icon-square-pin' 'Places' %}
{% menuitem 'chrono_list' 'far fa-stopwatch' 'Chronos' %}
<!-- {% menuitem 'place_list' 'tim-icons icon-square-pin' 'Places' %} -->
{% menuitem 'place_list' 'fal fa-map-marked-alt' 'Places' %}
{% menuitem 'chrono_list' 'fal fa-stopwatch' 'Chronos' %}
{% if request.user.is_staff %}
<li>
<a href="/admin/" target="_blank">
<i class="tim-icons icon-settings"></i>
<!-- <i class="tim-icons icon-settings"></i> -->
<i class="fal fa-tools"></i>
<p>Administration</p>
</a>
</li>
@ -122,11 +126,11 @@
</a>
<ul class="dropdown-menu dropdown-navbar">
<li class="nav-link">
<a href="{% url 'profile_update' %}" class="nav-item dropdown-item">Profile</a>
<a href="{% url 'profile_update' %}" class="nav-item dropdown-item"><i class="fal fa-id-card-alt"></i>&nbsp;Profile</a>
</li>
<li class="dropdown-divider"></li>
<li class="nav-link">
<a href="{% url 'logout' %}" class="nav-item dropdown-item">Log out</a>
<a href="{% url 'logout' %}" class="nav-item dropdown-item"><i class="fal fa-sign-out-alt"></i>&nbsp;Log out</a>
</li>
</ul>
</li>
@ -207,6 +211,11 @@
<!-- <script src="{% static "js/tablesorter.min.js" %}"></script> -->
<!-- Core JS Files -->
<script src="{% static "js/core/jquery-3.6.0.min.js" %}"></script>
<script src="{% static "js/plugins/moment.min.js" %}"></script>
<!-- Chart JS -->
<script src="{% static "js/plugins/chartjs.min.js" %}"></script>
<script src="{% static "js/core/popper.min.js" %}"></script>
<script src="{% static "js/core/bootstrap.min.js" %}"></script>
<script src="{% static "js/plugins/perfect-scrollbar.jquery.min.js" %}"></script>

View File

@ -36,6 +36,13 @@
</div>
</div>
<div class="form-group row ">
<label for="id_routine_type" class="col-4 col-sm-3 col-md-3 col-lg-3 col-xl-2 col-form-label">Routine</label>
<div class="col-8 col-sm-4 col-md-3 {% if form.routine_type.errors %}has-danger{% endif %}">
{{ form.routine_type }}
{% if form.routine_type.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.routine_type.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_point_difficulty" class="col-4 col-sm-3 col-md-3 col-lg-3 col-xl-2 col-form-label">Difficulty</label>
<div class="col-sm-6 col-md-3 {% if form.date.errors %}has-danger{% endif %}">

View File

@ -1,7 +1,5 @@
{% extends "listing.html" %}
{% load format %}
{% block datacontent %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">

View File

@ -30,7 +30,7 @@
{% for skill in skill_list %}
<tr>
<td class="text-left">&nbsp;<a href="{% url 'skill_details' skill.id %}">{{ skill.short_label }}</a></td>
<td class="text-center">{{ skill.numeric_notation }}</td>
<td class="text-center">{{ skill.notation }}</td>
<td class="text-center">{{ skill.difficulty }}</td>
<td class="text-center">{{ skill.level }}</td>
<td class="text-center">{{ skill.rank }}</td>

View File

@ -40,12 +40,15 @@
{{ known_skill }} known skill on {{ nb_skill }} skills.
</div>
<div class="row">
<div class="col-6">
<div class="col-4">
<a href="{% url 'chrono_create' gymnast.id %}" class="nav-item dropdown-item">New <i class="far fa-stopwatch"></i></a>
</div>
<div class="col-6">
<div class="col-4">
<a href="{% url 'learnedskill_create' gymnast.id %}" class="nav-item dropdown-item">New <i class="tim-icons icon-molecule-40"></i></a>
</div>
<div class="col-4">
<a href="{% url 'score_create_for_gymnast' gymnast.id %}" class="nav-item dropdown-item">New <i class="fal fa-star"></i></a>
</div>
</div>
</div>
</div>
@ -115,7 +118,12 @@
{% if chronos_list %}
<div class="row">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 card">
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6 card">
<div class="card-body pb-0">
<canvas id="chartjs_chrono" class="chartjs" width="350" height="150"></canvas>
</div>
</div>
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6 card">
<div class="card-body pb-0">
<canvas id="chartjs_routine" class="chartjs" width="350" height="150"></canvas>
</div>
@ -136,13 +144,13 @@
})
});
new Chart(document.getElementById("chartjs_routine"),{
new Chart(document.getElementById("chartjs_chrono"),{
type: 'line',
data:{
datasets:[
{% if straightjump_score %}
{
label: '10|',
label: 'Straightjump',
backgroundColor: 'rgb(255, 99, 132, 0.25)',
borderColor: 'rgb(255, 99, 132)',
fill: true,
@ -198,6 +206,105 @@
}
},
});
new Chart(document.getElementById("chartjs_routine"),{
type: 'line',
data:{
datasets:[
{% if points_list %}
{
label: 'Execution',
backgroundColor: 'rgb(255, 99, 132, 0.25)',
borderColor: 'rgb(255, 99, 132)',
fill: true,
data: [
{% for point in points_list %}
{
x: '{{ point.event.datebegin | date:"d-m-Y" }}',
y: '{{ point.point_execution }}'
},
{% endfor %}
]
},
{% endif %}
{% if points_list %}
{
label: 'Difficulty',
backgroundColor: 'rgb(255, 159, 64, 0.25)',
borderColor: 'rgb(255, 159, 64)',
fill: true,
data: [
{% for point in points_list %}
{
x: '{{ point.event.datebegin | date:"d-m-Y" }}',
y: '{{ point.point_difficulty }}'
},
{% endfor %}
]
},
{% endif %}
{% if points_list %}
{
label: 'ToF',
backgroundColor: 'rgb(0, 250, 147, 0.25)',
borderColor: 'rgb(0, 250, 147)',
fill: true,
data: [
{% for point in points_list %}
{
x: '{{ point.event.datebegin | date:"d-m-Y" }}',
y: '{{ point.point_time_of_flight }}'
},
{% endfor %}
]
},
{% endif %}
{% if points_list %}
{
label: 'HD',
backgroundColor: 'rgb(0, 151, 255, 0.25)',
borderColor: 'rgb(0, 151, 255)',
fill: true,
data: [
{% for point in points_list %}
{
x: '{{ point.event.datebegin | date:"d-m-Y" }}',
y: '{{ point.point_horizontal_displacement }}'
},
{% endfor %}
]
},
{% endif %}
],
},
options: {
scales: {
xAxes: [{
type: "time",
time: {
unit: 'day',
format: 'DD-MM-YYYY'
},
scaleLabel: {
display: true,
}
}, ],
yAxes: [{
scaleLabel: {
display: true,
}
}]
},
legend: {
display: true,
position: 'bottom',
}
},
});
</script>
{% endblock %}

View File

@ -13,7 +13,8 @@
<h4>Hi {{ user.username }} !</h4>
</div>
<div class="card-body">
Welcome to Ultron v0.21<br />
This application is there to help us manage the gymnasts (evolution, evaluation, ...). It is not perfect so feel free to make improvement proposals, bug reports, … by sending me an <a href="mailto:gregory@flyingacrobaticstrampoline.be">email</a>.
</div>
</div>
</div>