Refactoring Skill, templates reorganisation, new jquery, ...
This commit is contained in:
parent
318bbe8f67
commit
b9bff531e3
|
@ -49,6 +49,7 @@ INSTALLED_APPS = [
|
|||
'tools',
|
||||
'profiles',
|
||||
'planning',
|
||||
'objective',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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)
|
|
@ -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'),
|
||||
|
|
|
@ -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,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)
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ObjectiveConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'objective'
|
|
@ -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',),
|
||||
),
|
||||
]
|
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -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"),
|
||||
]
|
|
@ -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)
|
|
@ -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
|
@ -0,0 +1,11 @@
|
|||
if (!$) {
|
||||
$ = django.jQuery;
|
||||
}
|
||||
|
||||
$(function(){
|
||||
|
||||
$('#id_label').keyup(function(){
|
||||
$('#id_short_label').val($('#id_label').val());
|
||||
});
|
||||
|
||||
});
|
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.
|
@ -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> 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> 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>
|
||||
|
|
|
@ -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 %} <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 %}">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
{% for skill in skill_list %}
|
||||
<tr>
|
||||
<td class="text-left"> <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>
|
|
@ -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 %}
|
|
@ -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>
|
Loading…
Reference in New Issue