Here we go again...

This commit is contained in:
Fred Pauchet 2021-10-04 21:39:37 +02:00
parent fa6c6d531a
commit e56660e626
354 changed files with 1744 additions and 1908 deletions

View File

@ -232,7 +232,7 @@ single-line-if-stmt=no
[VARIABLES] [VARIABLES]
django-settings-module=khana.settings django-settings-module=config.settings
# List of additional names supposed to be defined in builtins. Remember that # List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible. # you should avoid defining new builtins when possible.

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
# Makefile for khana
ifeq ($(shell which coverage >/dev/null 2>&1; echo $$?), 1)
$(error The 'coverage' command was not found. Make sure you have coverage installed)
endif
.PHONY: help coverage
help:
@echo " coverage to run coverage check of the source files."
coverage:
coverage run --source='.' manage.py test; coverage report; coverage html;
@echo "Testing of coverage in the sources finished."

View File

@ -16,19 +16,17 @@ import environ
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
env = environ.Env( env = environ.Env(DEBUG=(bool, False))
DEBUG=(bool, False)
)
environ.Env.read_env() environ.Env.read_env()
DEBUG = env('DEBUG', default=True) DEBUG = env("DEBUG", default=True)
SECRET_KEY = env('SECRET_KEY', default="6@9p0g-5ebcttbt$^*s4rda5!piezt6b7wj35g(+$mgz52k#d=") SECRET_KEY = env(
"SECRET_KEY", default="6@9p0g-5ebcttbt$^*s4rda5!piezt6b7wj35g(+$mgz52k#d="
)
DATABASES = { DATABASES = {"default": env.db("DATABASE_URL", default="sqlite:///db.sqlite3")}
'default': env.db('DATABASE_URL', default='sqlite:///db.sqlite3')
}
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
@ -45,14 +43,14 @@ INSTALLED_APPS = (
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django_extensions", "django_extensions",
"people", "khana.people",
"location", "khana.location",
"planning", "khana.planning",
"objective", "khana.objective",
"competition", "khana.competition",
"profile", "khana.profile",
"tools", "khana.tools",
"communication", "khana.communication",
"rest_framework", "rest_framework",
) )
@ -66,7 +64,7 @@ MIDDLEWARE = [
#'django.middleware.clickjacking.XFrameOptionsMiddleware', #'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = "khana.urls" ROOT_URLCONF = "config.urls"
TEMPLATES = [ TEMPLATES = [
{ {
@ -122,4 +120,4 @@ DEBUG_TOOLBAR_CONFIG = {
"JQUERY_URL": STATIC_URL + "js/jquery-2.1.4.min.js", "JQUERY_URL": STATIC_URL + "js/jquery-2.1.4.min.js",
} }
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@ -246,7 +246,8 @@ def search(request):
name__icontains=pattern name__icontains=pattern
) # ou gymnaste qui y participe ! ) # ou gymnaste qui y participe !
gymnast_list = Gymnast.objects.filter( gymnast_list = Gymnast.objects.filter(
Q(user__last_name__icontains=pattern) | Q(user__first_name__icontains=pattern) Q(user__last_name__icontains=pattern)
| Q(user__first_name__icontains=pattern)
) )
accident_list = Accident.objects.filter( accident_list = Accident.objects.filter(
Q(gymnast__user__last_name__icontains=pattern) Q(gymnast__user__last_name__icontains=pattern)

View File

@ -9,6 +9,7 @@ from .models import Message
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):
"""La classe `MessageAdmin` contrôle la gestion des messages """La classe `MessageAdmin` contrôle la gestion des messages
""" """
list_display = ("sender", "recipient", "written_at", "is_read", "read_at") list_display = ("sender", "recipient", "written_at", "is_read", "read_at")
ordering = ("written_at", "sender") ordering = ("written_at", "sender")
search_fields = ("sender", "recipient", "message_title") search_fields = ("sender", "recipient", "message_title")

View File

@ -2,4 +2,4 @@ from django.apps import AppConfig
class CommunicationConfig(AppConfig): class CommunicationConfig(AppConfig):
name = "communication" name = "khana.communication"

View File

@ -8,6 +8,7 @@ from .models import Message
class MessageForm(forms.ModelForm): class MessageForm(forms.ModelForm):
"""Formulaire de base pour la création et la modification de messages """Formulaire de base pour la création et la modification de messages
""" """
class Meta: class Meta:
model = Message model = Message
fields = ( fields = (

View File

@ -0,0 +1,63 @@
# Generated by Django 3.2.2 on 2021-05-13 10:58
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("communication", "0002_auto_20190413_1028"),
]
operations = [
migrations.RenameField(
model_name="message", old_name="message_body", new_name="body",
),
migrations.RenameField(
model_name="message", old_name="date_of_reading", new_name="read_at",
),
migrations.RenameField(
model_name="message", old_name="message_title", new_name="title",
),
migrations.RenameField(
model_name="message", old_name="date_of_writing", new_name="written_at",
),
migrations.RemoveField(model_name="message", name="is_read",),
migrations.RemoveField(model_name="message", name="reader",),
migrations.RemoveField(model_name="message", name="writer",),
migrations.AddField(
model_name="message",
name="content",
field=models.TextField(
blank=True,
help_text="Seul le MarkDown simple est accepté",
null=True,
verbose_name="Comments",
),
),
migrations.AddField(
model_name="message",
name="recipient",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
related_name="received_messages",
to="auth.user",
),
preserve_default=False,
),
migrations.AddField(
model_name="message",
name="sender",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
related_name="sent_messages",
to="auth.user",
),
preserve_default=False,
),
]

View File

@ -12,7 +12,7 @@ from datetime import datetime
from django.db import models from django.db import models
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from base.models import Markdownizable from khana.base.models import Markdownizable
User = get_user_model() User = get_user_model()
@ -33,13 +33,11 @@ class Message(Markdownizable):
sender = models.ForeignKey( sender = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="sent_messages" User, on_delete=models.CASCADE, related_name="sent_messages"
) )
written_at = models.DateTimeField( written_at = models.DateTimeField(auto_now_add=True, verbose_name="Date of writing")
auto_now_add=True, verbose_name="Date of writing" recipient = models.ForeignKey(
) User, on_delete=models.CASCADE, related_name="received_messages"
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name="received_messages")
read_at = models.DateTimeField(
auto_now=True, verbose_name="Date of reading"
) )
read_at = models.DateTimeField(auto_now=True, verbose_name="Date of reading")
title = models.CharField(max_length=255, verbose_name="Title") title = models.CharField(max_length=255, verbose_name="Title")
body = models.TextField(null=True, blank=True, verbose_name="Message") body = models.TextField(null=True, blank=True, verbose_name="Message")

View File

@ -14,6 +14,6 @@ def test_message_to_string():
"""Vérifie la représentation textuelle d'un message """Vérifie la représentation textuelle d'un message
""" """
timing = datetime.now() timing = datetime.now()
user = User(username='fred', password='fredpassword') user = User(username="fred", password="fredpassword")
message = Message(sender=user, written_at=timing, title="test") message = Message(sender=user, written_at=timing, title="test")
assert str(message) == "fred - " + str(timing) + " : test" assert str(message) == "fred - " + str(timing) + " : test"

View File

@ -0,0 +1,92 @@
"""Vues et fonctions pour tout ce qui touche à la communication entre plusieurs utilisateurs."""
from datetime import datetime
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods
from django.urls import reverse
from .forms import MessageForm
from .models import Message
@login_required
def get_number_of_unread_message(request):
"""Récupère le nombre de messages non lus associés à l'utilisateur en session.
"""
return (
Message.objects.filter(recipient=request.user)
.filter(read_at__isnull=True)
.count()
)
@login_required
@require_http_methods(["GET"])
def get_received_messages(request):
"""Récupère des messages recus pour l'utilisateur connecté.
"""
return request.user.received_messages.all()
@login_required
@require_http_methods(["GET"])
def get_sent_messages(request):
"""Récupère des messages envoyés par l'utilisateur connecté.
"""
return request.user.sent_messages.all()
@login_required
@require_http_methods(["GET"])
def get_message_details(request, messageid):
"""Récupère les détails (l'affichage ?) d'un message.
"""
message = get_object_or_404(Message, pk=messageid)
if not message.read_at and message.recipient == request.user.id:
message.read_at = datetime.now()
message.save()
context = {"message": message, "type": None}
return render(request, "message_details.html", context)
@login_required
@require_http_methods(["POST"])
def delete_message(request, messageid):
"""Supprime le message dont la clé est passée en paramètre.
"""
try:
message = Message.objects.get(pk=messageid)
if message.sender == request.user or message.recipient == request.user:
message.delete()
else:
return HttpResponse(401)
except:
return HttpResponse(400)
return HttpResponse(200)
@login_required
@require_http_methods(["GET", "POST"])
def compose_message(request):
"""Permet à l'utilisateur connecté de rédiger un nouveau message.
"""
if request.method == "POST":
form = MessageForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("sent_messages"))
print("Invalid form")
else:
form = MessageForm()
context = {"form": form, "writer": request.user.id}
return render(request, "message_create.html", context)

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CompetitionConfig(AppConfig):
name = "khana.competition"

View File

@ -1,12 +1,7 @@
# coding=UTF-8 # coding=UTF-8
import pytest import pytest
from .models import ( from .models import Point, Competition, Division, Level
Point,
Competition,
Division,
Level
)
# class TestModelCompetition(TestCase): # class TestModelCompetition(TestCase):
@ -14,6 +9,7 @@ from .models import (
# Tests relatifs à la classe `Compétition`. # Tests relatifs à la classe `Compétition`.
# """ # """
def test_competition_str_(): def test_competition_str_():
""" Vérifie la représentation textuelle de la classe. """ """ Vérifie la représentation textuelle de la classe. """
competition = Competition(name="Belgian Open Trampoline", acronym="BOT") competition = Competition(name="Belgian Open Trampoline", acronym="BOT")
@ -25,6 +21,7 @@ def test_competition_str_():
# Tests relatifs à la classe `Division`. # Tests relatifs à la classe `Division`.
# """ # """
def test_division_str_(): def test_division_str_():
""" Vérifie la représentation textuelle de la classe. """ """ Vérifie la représentation textuelle de la classe. """
competition = Competition(name="Belgian Open Trampoline", acronym="BOT") competition = Competition(name="Belgian Open Trampoline", acronym="BOT")
@ -37,6 +34,7 @@ def test_division_str_():
# Tests relatifs à la classe `Level`. # Tests relatifs à la classe `Level`.
# """ # """
def test_level_str_(): def test_level_str_():
""" Vérifie la représentation textuelle de la classe. """ """ Vérifie la représentation textuelle de la classe. """
competition = Competition(name="Belgian Open Trampoline", acronym="BOT") competition = Competition(name="Belgian Open Trampoline", acronym="BOT")

5
khana/location/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class LocationConfig(AppConfig):
name = "khana.location"

View File

@ -8,10 +8,7 @@ from ..models import Club, Country, Place
class TestCountry(TestCase): class TestCountry(TestCase):
def test_str_should_contain_name_and_iso2(self): def test_str_should_contain_name_and_iso2(self):
country = Country.objects.create( country = Country.objects.create(
nameus="Belgium", nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
namefr="Belgique",
isonum=56,
iso2="BE"
) )
self.assertEqual(str(country), "Belgique (BE)") self.assertEqual(str(country), "Belgique (BE)")
@ -23,11 +20,8 @@ class TestPlace(TestCase):
name="Heaven", name="Heaven",
postal=1080, postal=1080,
country=Country.objects.create( country=Country.objects.create(
nameus="Belgium", nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
namefr="Belgique", ),
isonum=56,
iso2="BE"
)
) )
self.assertEqual(str(place), "Heaven (?)") self.assertEqual(str(place), "Heaven (?)")
@ -41,12 +35,9 @@ class TestClub(TestCase):
name="Heaven", name="Heaven",
postal=1080, postal=1080,
country=Country.objects.create( country=Country.objects.create(
nameus="Belgium", nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
namefr="Belgique", ),
isonum=56, ),
iso2="BE"
)
)
) )
self.assertEqual(str(club), "RSCA (à ?)") self.assertEqual(str(club), "RSCA (à ?)")

View File

@ -1,10 +1,6 @@
# coding=UTF-8 # coding=UTF-8
from .models import ( from .models import Club, Place, Country
Club,
Place,
Country
)
import pytest import pytest
# class GymnastTestCase(): # class GymnastTestCase():
@ -12,11 +8,13 @@ def test_country_tostring():
c = Country(namefr="Belgique", iso2="56") c = Country(namefr="Belgique", iso2="56")
assert str(c) == "Belgique (56)" assert str(c) == "Belgique (56)"
def test_place_tostring(): def test_place_tostring():
p = Place(name="FATC", city="Lillois") p = Place(name="FATC", city="Lillois")
assert str(p) == "FATC (Lillois)" assert str(p) == "FATC (Lillois)"
def test_club_tostring(): def test_club_tostring():
p = Place(name="FATC", city="Lillois") p = Place(name="FATC", city="Lillois")
club = Club(place=p, name="FATC2") club = Club(place=p, name="FATC2")
assert str(club) == "FATC2 (à Lillois)" assert str(club) == "FATC2 (à Lillois)"

5
khana/objective/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ObjectiveConfig(AppConfig):
name = "khana.objective"

View File

@ -6,13 +6,11 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('objective', '0015_auto_20190524_1211'), ("objective", "0015_auto_20190524_1211"),
] ]
operations = [ operations = [
migrations.RenameField( migrations.RenameField(
model_name='educative', model_name="educative", old_name="information", new_name="content",
old_name='information',
new_name='content',
), ),
] ]

View File

@ -0,0 +1,75 @@
# Generated by Django 3.2.2 on 2021-06-20 16:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("objective", "0016_rename_information_educative_content"),
]
operations = [
migrations.RenameModel(old_name="Routine_Skill", new_name="RoutineSkill",),
migrations.AlterModelOptions(
name="educative",
options={
"ordering": ["label", "short_label"],
"verbose_name": "Educatif",
"verbose_name_plural": "Educatifs",
},
),
migrations.AlterModelOptions(
name="touchposition",
options={
"ordering": [
"label",
"short_label",
"is_default",
"allowed_in_competition",
],
"verbose_name": "Landing",
"verbose_name_plural": "Landings",
},
),
migrations.RenameField(
model_name="educative", old_name="ageBoy", new_name="age_boy",
),
migrations.RenameField(
model_name="educative", old_name="ageGirl", new_name="age_girl",
),
migrations.RenameField(
model_name="educative", old_name="educative", new_name="educatives",
),
migrations.RenameField(
model_name="educative", old_name="longLabel", new_name="label",
),
migrations.RenameField(
model_name="educative", old_name="prerequisite", new_name="prerequisites",
),
migrations.RenameField(
model_name="educative", old_name="shortLabel", new_name="short_label",
),
migrations.RenameField(
model_name="skill", old_name="rotationType", new_name="rotation_type",
),
migrations.RenameField(
model_name="skill",
old_name="simplyNotation",
new_name="simplified_notation",
),
migrations.RenameField(
model_name="touchposition",
old_name="competition",
new_name="allowed_in_competition",
),
migrations.RenameField(
model_name="touchposition", old_name="default", new_name="is_default",
),
migrations.RenameField(
model_name="touchposition", old_name="longLabel", new_name="label",
),
migrations.RenameField(
model_name="touchposition", old_name="shortLabel", new_name="short_label",
),
]

View File

@ -3,7 +3,7 @@
from datetime import date from datetime import date
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from base.models import Markdownizable from khana.base.models import Markdownizable
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -17,24 +17,24 @@ class Educative(Markdownizable):
verbose_name_plural = "Educatifs" verbose_name_plural = "Educatifs"
ordering = ["label", "short_label"] # 'level', ordering = ["label", "short_label"] # 'level',
label = models.CharField(max_length = 255, verbose_name = "Long Name") label = models.CharField(max_length=255, verbose_name="Long Name")
short_label = models.CharField(max_length = 255, verbose_name = "Short Name") short_label = models.CharField(max_length=255, verbose_name="Short Name")
difficulty = models.DecimalField( difficulty = models.DecimalField(
max_digits = 3, decimal_places = 1, verbose_name = "Difficulty" max_digits=3, decimal_places=1, verbose_name="Difficulty"
) )
level = models.PositiveSmallIntegerField(verbose_name = "Level", default = 0) level = models.PositiveSmallIntegerField(verbose_name="Level", default=0)
rank = models.PositiveSmallIntegerField(verbose_name = "Rank", default = 0) rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0)
educatives = models.ManyToManyField( educatives = models.ManyToManyField(
"self", related_name = "educativeOf", blank = True, symmetrical = False "self", related_name="educativeOf", blank=True, symmetrical=False
) )
prerequisites = models.ManyToManyField( prerequisites = models.ManyToManyField(
"self", related_name = "prerequisiteOf", blank = True, symmetrical = False "self", related_name="prerequisiteOf", blank=True, symmetrical=False
) )
age_boy = models.PositiveSmallIntegerField( age_boy = models.PositiveSmallIntegerField(
blank = True, null = True, verbose_name = "Boy's age" blank=True, null=True, verbose_name="Boy's age"
) )
age_girl = models.PositiveSmallIntegerField( age_girl = models.PositiveSmallIntegerField(
blank = True, null = True, verbose_name = "Girl's age" blank=True, null=True, verbose_name="Girl's age"
) )
def __str__(self): def __str__(self):
@ -56,10 +56,10 @@ class TouchPosition(models.Model):
verbose_name_plural = "Landings" verbose_name_plural = "Landings"
ordering = ["label", "short_label", "is_default", "allowed_in_competition"] ordering = ["label", "short_label", "is_default", "allowed_in_competition"]
label = models.CharField(max_length = 30, verbose_name = "Nom long") label = models.CharField(max_length=30, verbose_name="Nom long")
short_label = models.CharField(max_length = 15, verbose_name = "Nom court") short_label = models.CharField(max_length=15, verbose_name="Nom court")
allowed_in_competition = models.BooleanField(verbose_name = "Compétition") allowed_in_competition = models.BooleanField(verbose_name="Compétition")
is_default = models.BooleanField(verbose_name = "Défaut") is_default = models.BooleanField(verbose_name="Défaut")
def __str__(self): def __str__(self):
return "%s" % (self.longLabel) return "%s" % (self.longLabel)
@ -104,29 +104,31 @@ class Skill(Educative):
(2, "Backward"), (2, "Backward"),
) )
position = models.CharField(max_length = 2, choices = POSITION_CHOICES) position = models.CharField(max_length=2, choices=POSITION_CHOICES)
departure = models.ForeignKey( departure = models.ForeignKey(
TouchPosition, TouchPosition,
related_name = "depart_of", related_name="depart_of",
default = get_default_position, default=get_default_position,
verbose_name = "Take-off position", verbose_name="Take-off position",
on_delete = models.CASCADE, on_delete=models.CASCADE,
) )
landing = models.ForeignKey( landing = models.ForeignKey(
TouchPosition, TouchPosition,
related_name = "landing_of", related_name="landing_of",
default = get_default_position, default=get_default_position,
verbose_name = "Landing position", verbose_name="Landing position",
on_delete = models.CASCADE, on_delete=models.CASCADE,
) )
rotation_type = models.PositiveSmallIntegerField( rotation_type = models.PositiveSmallIntegerField(
choices = ROTATION_CHOICES, verbose_name = "Type de rotation" choices=ROTATION_CHOICES, verbose_name="Type de rotation"
) )
rotation = models.PositiveSmallIntegerField(verbose_name = "1/4 de rotation") rotation = models.PositiveSmallIntegerField(verbose_name="1/4 de rotation")
twist = models.PositiveSmallIntegerField(verbose_name = "1/2 Vrille") twist = models.PositiveSmallIntegerField(verbose_name="1/2 Vrille")
notation = models.CharField(max_length = 25) notation = models.CharField(max_length=25)
simplified_notation = models.CharField(max_length = 25, verbose_name = "Notation simplifiée") simplified_notation = models.CharField(
is_competitive = models.BooleanField(default = False) max_length=25, verbose_name="Notation simplifiée"
)
is_competitive = models.BooleanField(default=False)
# importance = models.PositiveSmallIntegerField(default = 1) # importance = models.PositiveSmallIntegerField(default = 1)
def __str__(self): def __str__(self):
@ -144,9 +146,9 @@ class Routine(Educative):
active = models.BooleanField() active = models.BooleanField()
jumps = models.ManyToManyField( jumps = models.ManyToManyField(
Skill, through = "RoutineSkill", verbose_name = "routine" Skill, through="RoutineSkill", verbose_name="routine"
) # ceci n'est pas un vrai champ ) # ceci n'est pas un vrai champ
is_competitive = models.BooleanField(default = False) is_competitive = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return "%s (%s)" % (self.label, self.short_label) return "%s (%s)" % (self.label, self.short_label)
@ -216,10 +218,10 @@ class RoutineSkill(models.Model):
ordering = ("rank",) ordering = ("rank",)
routine = models.ForeignKey( routine = models.ForeignKey(
Routine, on_delete = models.CASCADE, default = None, related_name = "skill_links" Routine, on_delete=models.CASCADE, default=None, related_name="skill_links"
) )
skill = models.ForeignKey( skill = models.ForeignKey(
Skill, on_delete = models.CASCADE, default = None, related_name = "routine_links" Skill, on_delete=models.CASCADE, default=None, related_name="routine_links"
) )
rank = models.PositiveSmallIntegerField() rank = models.PositiveSmallIntegerField()
@ -247,13 +249,13 @@ class Chrono(models.Model):
(99, "Other"), (99, "Other"),
) )
routine = models.ForeignKey(Routine, on_delete = models.CASCADE, default = None) routine = models.ForeignKey(Routine, on_delete=models.CASCADE, default=None)
routine_type = models.PositiveSmallIntegerField(choices = ROUTINETYPE_CHOICE) routine_type = models.PositiveSmallIntegerField(choices=ROUTINETYPE_CHOICE)
gymnast = models.ForeignKey( gymnast = models.ForeignKey(
"people.gymnast", on_delete = models.CASCADE, default = None "people.gymnast", on_delete=models.CASCADE, default=None
) )
date = models.DateField(default = date.today, verbose_name = "Date") date = models.DateField(default=date.today, verbose_name="Date")
score = models.DecimalField(max_digits = 5, decimal_places = 2) score = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self): def __str__(self):
return "%s le %s : %s pour %s" % ( return "%s le %s : %s pour %s" % (
@ -278,13 +280,13 @@ class Evaluation(models.Model):
Classe permettant l'évaluation des éducatifs. Classe permettant l'évaluation des éducatifs.
""" """
date = models.DateField(default = date.today, verbose_name = "Date") date = models.DateField(default=date.today, verbose_name="Date")
value = models.PositiveSmallIntegerField(default = 0) value = models.PositiveSmallIntegerField(default=0)
educative = models.ForeignKey( educative = models.ForeignKey(
Educative, Educative,
related_name = "depart_of", related_name="depart_of",
verbose_name = "Take-off position", verbose_name="Take-off position",
on_delete = models.CASCADE, on_delete=models.CASCADE,
) )
type_of_evaluator = models.BooleanField(default = 0) type_of_evaluator = models.BooleanField(default=0)
evaluator = models.ForeignKey(User, null = True, on_delete = models.SET_NULL) evaluator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)

View File

@ -42,11 +42,7 @@ routine_urlpatterns = [
views.delete_skill_from_routine, views.delete_skill_from_routine,
name="delete_skill_from_routine", name="delete_skill_from_routine",
), ),
path( path(r"suggest/", views.suggest_routine, name="suggest_routine",),
r"suggest/",
views.suggest_routine,
name="suggest_routine",
)
] ]
# Chrono # Chrono

View File

@ -338,7 +338,9 @@ def random_skill(request, skill_quantity=20, is_competitive=True):
number_of_skill = skillid_list.count() number_of_skill = skillid_list.count()
selected_skillid_list = [ selected_skillid_list = [
skillid_list[x]["id"] skillid_list[x]["id"]
for x in [random.randrange(0, number_of_skill, 1) for i in range(skill_quantity)] for x in [
random.randrange(0, number_of_skill, 1) for i in range(skill_quantity)
]
] ]
skill_list = Skill.objects.filter(id__in=selected_skillid_list) skill_list = Skill.objects.filter(id__in=selected_skillid_list)
@ -387,12 +389,14 @@ def __construct_routine(
if current_routine: if current_routine:
skill_list = Skill.objects.filter( skill_list = Skill.objects.filter(
departure = current_routine[-1].landing departure=current_routine[-1].landing
) # , difficulty__lte = total_difficulty_score ) # , difficulty__lte = total_difficulty_score
if len(current_routine) == (max_routine_length - 1): if len(current_routine) == (max_routine_length - 1):
skill_list = skill_list.filter(landing__longLabel="Debout") skill_list = skill_list.filter(landing__longLabel="Debout")
if logic and current_routine[-1].landing.longLabel == "Debout": if logic and current_routine[-1].landing.longLabel == "Debout":
skill_list = skill_list.exclude(rotationType = current_routine[-1].rotationType) skill_list = skill_list.exclude(
rotationType=current_routine[-1].rotationType
)
else: else:
skill_list = Skill.objects.filter(departure__longLabel="Debout") skill_list = Skill.objects.filter(departure__longLabel="Debout")
@ -404,23 +408,31 @@ def __construct_routine(
min_diff_skill = total_difficulty_score min_diff_skill = total_difficulty_score
max_diff_skill = total_difficulty_score + 3 max_diff_skill = total_difficulty_score + 3
else: else:
if math.ceil(total_difficulty_score / max_routine_length) <= max_skill_difficulty: if (
min_diff_skill = math.ceil(max((total_difficulty_score / max_routine_length) - 5, 0)) math.ceil(total_difficulty_score / max_routine_length)
<= max_skill_difficulty
):
min_diff_skill = math.ceil(
max((total_difficulty_score / max_routine_length) - 5, 0)
)
else: else:
return return
if (math.ceil(total_difficulty_score / max_routine_length) + 2) <= max_skill_difficulty: if (
max_diff_skill = math.ceil(total_difficulty_score / max_routine_length) + 2 math.ceil(total_difficulty_score / max_routine_length) + 2
) <= max_skill_difficulty:
max_diff_skill = (
math.ceil(total_difficulty_score / max_routine_length) + 2
)
else: else:
return return
skill_list = skill_list.filter( skill_list = skill_list.filter(
difficulty__gte = (min_diff_skill / 10), difficulty__gte=(min_diff_skill / 10), difficulty__lte=(max_diff_skill / 10)
difficulty__lte = (max_diff_skill / 10)
) )
if gymnast: if gymnast:
skill_list = skill_list.filter(cando__gymnast = gymnast) skill_list = skill_list.filter(cando__gymnast=gymnast)
for skill in skill_list: for skill in skill_list:
current_routine.append(skill) current_routine.append(skill)
@ -429,7 +441,9 @@ def __construct_routine(
current_routine, current_routine,
max_routine_length, max_routine_length,
max_skill_difficulty, max_skill_difficulty,
total_difficulty_score - (skill.difficulty * 10) if total_difficulty_score is not None else None, total_difficulty_score - (skill.difficulty * 10)
if total_difficulty_score is not None
else None,
competition, competition,
logic, logic,
gymnast, gymnast,
@ -442,7 +456,7 @@ def __construct_routine(
def suggest_routine( def suggest_routine(
request, request,
max_routine_length = 2, max_routine_length=2,
total_difficulty_score=None, total_difficulty_score=None,
competition=True, competition=True,
logic=True, logic=True,
@ -471,24 +485,32 @@ def suggest_routine(
total_difficulty_score = 26 total_difficulty_score = 26
if gymnast: if gymnast:
max_skill_difficulty = Educative.objects.values('difficulty').filter(cando__gymnast=gymnast).order_by( max_skill_difficulty = (
"-difficulty" Educative.objects.values("difficulty")
)[:1][0]["difficulty"] * 10 .filter(cando__gymnast=gymnast)
.order_by("-difficulty")[:1][0]["difficulty"]
* 10
)
else: else:
max_skill_difficulty = Skill.objects.values('difficulty').order_by("-difficulty")[:1][0]["difficulty"] * 10 max_skill_difficulty = (
Skill.objects.values("difficulty").order_by("-difficulty")[:1][0][
"difficulty"
]
* 10
)
# difficulty_scores = range(5, 45, 5) # difficulty_scores = range(5, 45, 5)
# for total_difficulty_score in difficulty_scores: # for total_difficulty_score in difficulty_scores:
# print("===============================================================================================") # print("===============================================================================================")
__construct_routine( __construct_routine(
routine, routine,
max_routine_length, max_routine_length,
max_skill_difficulty, max_skill_difficulty,
total_difficulty_score, total_difficulty_score,
competition, competition,
logic, logic,
gymnast, gymnast,
) )
# print(routines) # print(routines)

127
khana/people/admin.py Normal file
View File

@ -0,0 +1,127 @@
"""Administration des gymnastes et des personnes.
Remarks:
* Je ne pense pas qu'il faille encore passer par `ForeignKeyAutocompleteAdmin`.
https://docs.djangoproject.com/fr/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields
"""
from django.contrib import admin
from django_extensions.admin import ForeignKeyAutocompleteAdmin
from .models import (
Gymnast,
Accident,
CanDoRelation,
ToDoRelation,
GymnastHasRoutine,
)
class CanDoRelationAdmin(ForeignKeyAutocompleteAdmin):
model = CanDoRelation
list_display = ("date", "gymnast", "educative")
list_filter = ("gymnast",)
search_fields = ("gymnast", "educative")
autocomplete_fields = ("gymnast",)
# related_search_fields = {
# 'gymnast': ('lastname', 'firstname'),
# }
class InlineCanDoRelation(admin.TabularInline):
model = CanDoRelation
class ToDoRelationAdmin(ForeignKeyAutocompleteAdmin):
model = ToDoRelation
list_display = ("date", "gymnast", "educative")
list_filter = ("gymnast",)
search_fields = ("gymnast", "educative")
autocomplete_fields = ("gymnast",)
# related_search_fields = {
# 'gymnast': ('lastname', 'firstname'),
# # 'educative': ('longLabel', 'shortLabel'), # TO_FRED : Pq ca marche pas ca ?
# }
class GymnastHasRoutineAdmin(ForeignKeyAutocompleteAdmin):
model = GymnastHasRoutine
list_display = ("gymnast", "routine", "routine_type", "datebegin", "dateend")
list_filter = ("gymnast", "routine_type")
search_fields = ("gymnast", "routine")
autocomplete_fields = ("gymnast", "routine")
class InlineToDoRelation(admin.TabularInline):
model = ToDoRelation
class GymnastAdmin(admin.ModelAdmin):
model = Gymnast
def lastname(self, obj):
return obj.user.last_name
def firstname(self, obj):
return obj.user.first_name
def email(self, obj):
return obj.user.email
def is_active(self, obj):
return obj.user.is_active
fields = (
# "lastname",
# "firstname",
"birthdate",
"gender",
"club",
"niss",
"address",
"postal",
"city",
"phone",
"gsm",
# "user__email",
"fedid",
"year_of_practice",
"gsm_mother",
"email_mother",
"gsm_father",
"email_father",
# "user.is_active",
"orientation",
"trainer",
"picture",
"content",
)
list_display = ("lastname", "firstname", "birthdate", "age", "is_active")
list_filter = ("gender", "user__is_active")
search_fields = ("lastname", "firstname", "email")
inlines = [InlineToDoRelation, InlineCanDoRelation]
autocomplete_fields = ("club",)
class AccidentAdmin(admin.ModelAdmin):
model = Accident
fields = ("date", "gymnast", "educative", "information")
list_display = ("date", "gymnast", "educative")
list_filter = ("date",)
search_fields = ("date", "gymnast", "educative")
autocomplete_fields = ["gymnast", "educative"]
admin.site.register(Gymnast, GymnastAdmin)
admin.site.register(Accident, AccidentAdmin)
admin.site.register(CanDoRelation, CanDoRelationAdmin)
admin.site.register(ToDoRelation, ToDoRelationAdmin)
admin.site.register(GymnastHasRoutine, GymnastHasRoutineAdmin)

5
khana/people/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class PeopleConfig(AppConfig):
name = "khana.people"

174
khana/people/forms.py Normal file
View File

@ -0,0 +1,174 @@
"""Formulaires de gestion des données entrantes pour les gymnastes et accidents."""
from django import forms
from .models import (
Accident,
Gymnast,
GymnastHasRoutine,
)
from django.contrib.auth.models import User
class AccidentForm(forms.ModelForm):
class Meta:
model = Accident
fields = ("gymnast", "educative", "date", "content")
widgets = {
"date": forms.DateInput(
attrs={
"class": "form-control datepicker",
# "value": date.today().strftime("%Y-%m-%d"),
}
),
"gymnast": forms.HiddenInput(),
"educative": forms.HiddenInput(),
"content": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about accident: context (why, where, …), consequencies, …",
}
),
}
gymnast_related = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
)
)
educative_related = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching skill…",
"data-ref": "#id_educative",
}
)
)
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = (
"last_name",
"first_name",
"email",
"is_active",
"username",
)
class GymnastForm(forms.ModelForm):
lastname = forms.CharField(
widget=forms.TextInput(
attrs={"class": "form-control", "placeholder": "Lastname"}
)
)
firstname = forms.CharField(
widget=forms.TextInput(
attrs={"class": "form-control", "placeholder": "Firstname"}
)
)
email = forms.EmailField()
# is_active = forms.CheckboxInput()
class Meta:
model = Gymnast
fields = (
"id",
"birthdate",
"gender",
"address",
"postal",
"city",
"phone",
"gsm",
"gsm_main_responsible",
"email_main_responsible",
"gsm_second_responsible",
"email_second_responsible",
"orientation",
"picture",
"content",
)
widgets = {
"id": forms.HiddenInput(),
"lastname": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Lastname"}
),
"firstname": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Firstname"}
),
"birthdate": forms.DateInput(attrs={"class": "form-control datepicker"}),
"gender": forms.Select(attrs={"class": "form-control"}),
"address": forms.TextInput(attrs={"class": "form-control"}),
"postal": forms.TextInput(attrs={"class": "form-control"}),
"city": forms.TextInput(attrs={"class": "form-control"}),
"phone": forms.TextInput(attrs={"class": "form-control"}),
"gsm": forms.TextInput(attrs={"class": "form-control"}),
"email": forms.EmailInput(attrs={"class": "form-control"}),
"gsm_main_responsible": forms.TextInput(attrs={"class": "form-control"}),
"email_main_responsible": forms.EmailInput(attrs={"class": "form-control"}),
"gsm_second_responsible": forms.TextInput(attrs={"class": "form-control"}),
"email_second_responsible": forms.EmailInput(
attrs={"class": "form-control"}
),
# "is_active": forms.CheckboxInput(
# attrs={
# "class": "bootstrap-switch mt-0",
# "data-on-label": "<i class='tim-icons icon-check-2 text-success'></i>",
# "data-off-label": "<i class='tim-icons icon-simple-remove text-danger'></i>",
# }
# ),
"orientation": forms.Select(attrs={"class": "form-control"}),
"picture": forms.Select(attrs={"class": "form-control"}),
"content": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about the gymnast.",
}
),
}
class GymnastHasRoutineForm(forms.ModelForm):
"""
"""
class Meta:
model = GymnastHasRoutine
fields = ("gymnast", "routine", "routine_type", "datebegin", "dateend")
widgets = {
"gymnast": forms.HiddenInput(),
"routine": forms.HiddenInput(),
"routine_type": forms.Select(attrs={"class": "form-control"}),
"datebegin": forms.DateInput(attrs={"class": "form-control datepicker",}),
"dateend": forms.DateInput(attrs={"class": "form-control datepicker",}),
}
gymnast_related = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
)
)
routine_related = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching routine…",
"data-ref": "#id_routine",
}
)
)

Some files were not shown because too many files have changed in this diff Show More