Here we go again...
This commit is contained in:
parent
fa6c6d531a
commit
e56660e626
|
@ -232,7 +232,7 @@ single-line-if-stmt=no
|
|||
|
||||
[VARIABLES]
|
||||
|
||||
django-settings-module=khana.settings
|
||||
django-settings-module=config.settings
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
|
|
|
@ -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."
|
|
@ -16,19 +16,17 @@ import environ
|
|||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
env = environ.Env(
|
||||
DEBUG=(bool, False)
|
||||
)
|
||||
env = environ.Env(DEBUG=(bool, False))
|
||||
|
||||
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 = {
|
||||
'default': env.db('DATABASE_URL', default='sqlite:///db.sqlite3')
|
||||
}
|
||||
DATABASES = {"default": env.db("DATABASE_URL", default="sqlite:///db.sqlite3")}
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
@ -45,14 +43,14 @@ INSTALLED_APPS = (
|
|||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"django_extensions",
|
||||
"people",
|
||||
"location",
|
||||
"planning",
|
||||
"objective",
|
||||
"competition",
|
||||
"profile",
|
||||
"tools",
|
||||
"communication",
|
||||
"khana.people",
|
||||
"khana.location",
|
||||
"khana.planning",
|
||||
"khana.objective",
|
||||
"khana.competition",
|
||||
"khana.profile",
|
||||
"khana.tools",
|
||||
"khana.communication",
|
||||
"rest_framework",
|
||||
)
|
||||
|
||||
|
@ -66,7 +64,7 @@ MIDDLEWARE = [
|
|||
#'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "khana.urls"
|
||||
ROOT_URLCONF = "config.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
|
@ -122,4 +120,4 @@ DEBUG_TOOLBAR_CONFIG = {
|
|||
"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"
|
|
@ -246,7 +246,8 @@ def search(request):
|
|||
name__icontains=pattern
|
||||
) # ou gymnaste qui y participe !
|
||||
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(
|
||||
Q(gymnast__user__last_name__icontains=pattern)
|
|
@ -9,6 +9,7 @@ from .models import Message
|
|||
class MessageAdmin(admin.ModelAdmin):
|
||||
"""La classe `MessageAdmin` contrôle la gestion des messages
|
||||
"""
|
||||
|
||||
list_display = ("sender", "recipient", "written_at", "is_read", "read_at")
|
||||
ordering = ("written_at", "sender")
|
||||
search_fields = ("sender", "recipient", "message_title")
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class CommunicationConfig(AppConfig):
|
||||
name = "communication"
|
||||
name = "khana.communication"
|
|
@ -8,6 +8,7 @@ from .models import Message
|
|||
class MessageForm(forms.ModelForm):
|
||||
"""Formulaire de base pour la création et la modification de messages
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = (
|
|
@ -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,
|
||||
),
|
||||
]
|
|
@ -12,7 +12,7 @@ from datetime import datetime
|
|||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from base.models import Markdownizable
|
||||
from khana.base.models import Markdownizable
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
|
@ -33,13 +33,11 @@ class Message(Markdownizable):
|
|||
sender = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, related_name="sent_messages"
|
||||
)
|
||||
written_at = models.DateTimeField(
|
||||
auto_now_add=True, verbose_name="Date of writing"
|
||||
)
|
||||
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name="received_messages")
|
||||
read_at = models.DateTimeField(
|
||||
auto_now=True, verbose_name="Date of reading"
|
||||
written_at = models.DateTimeField(auto_now_add=True, verbose_name="Date of writing")
|
||||
recipient = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, related_name="received_messages"
|
||||
)
|
||||
read_at = models.DateTimeField(auto_now=True, verbose_name="Date of reading")
|
||||
title = models.CharField(max_length=255, verbose_name="Title")
|
||||
body = models.TextField(null=True, blank=True, verbose_name="Message")
|
||||
|
|
@ -14,6 +14,6 @@ def test_message_to_string():
|
|||
"""Vérifie la représentation textuelle d'un message
|
||||
"""
|
||||
timing = datetime.now()
|
||||
user = User(username='fred', password='fredpassword')
|
||||
user = User(username="fred", password="fredpassword")
|
||||
message = Message(sender=user, written_at=timing, title="test")
|
||||
assert str(message) == "fred - " + str(timing) + " : test"
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CompetitionConfig(AppConfig):
|
||||
name = "khana.competition"
|
|
@ -1,12 +1,7 @@
|
|||
# coding=UTF-8
|
||||
|
||||
import pytest
|
||||
from .models import (
|
||||
Point,
|
||||
Competition,
|
||||
Division,
|
||||
Level
|
||||
)
|
||||
from .models import Point, Competition, Division, Level
|
||||
|
||||
|
||||
# class TestModelCompetition(TestCase):
|
||||
|
@ -14,6 +9,7 @@ from .models import (
|
|||
# Tests relatifs à la classe `Compétition`.
|
||||
# """
|
||||
|
||||
|
||||
def test_competition_str_():
|
||||
""" Vérifie la représentation textuelle de la classe. """
|
||||
competition = Competition(name="Belgian Open Trampoline", acronym="BOT")
|
||||
|
@ -25,6 +21,7 @@ def test_competition_str_():
|
|||
# Tests relatifs à la classe `Division`.
|
||||
# """
|
||||
|
||||
|
||||
def test_division_str_():
|
||||
""" Vérifie la représentation textuelle de la classe. """
|
||||
competition = Competition(name="Belgian Open Trampoline", acronym="BOT")
|
||||
|
@ -37,6 +34,7 @@ def test_division_str_():
|
|||
# Tests relatifs à la classe `Level`.
|
||||
# """
|
||||
|
||||
|
||||
def test_level_str_():
|
||||
""" Vérifie la représentation textuelle de la classe. """
|
||||
competition = Competition(name="Belgian Open Trampoline", acronym="BOT")
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LocationConfig(AppConfig):
|
||||
name = "khana.location"
|
|
@ -8,10 +8,7 @@ from ..models import Club, Country, Place
|
|||
class TestCountry(TestCase):
|
||||
def test_str_should_contain_name_and_iso2(self):
|
||||
country = Country.objects.create(
|
||||
nameus="Belgium",
|
||||
namefr="Belgique",
|
||||
isonum=56,
|
||||
iso2="BE"
|
||||
nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
|
||||
)
|
||||
|
||||
self.assertEqual(str(country), "Belgique (BE)")
|
||||
|
@ -23,11 +20,8 @@ class TestPlace(TestCase):
|
|||
name="Heaven",
|
||||
postal=1080,
|
||||
country=Country.objects.create(
|
||||
nameus="Belgium",
|
||||
namefr="Belgique",
|
||||
isonum=56,
|
||||
iso2="BE"
|
||||
)
|
||||
nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual(str(place), "Heaven (?)")
|
||||
|
@ -41,12 +35,9 @@ class TestClub(TestCase):
|
|||
name="Heaven",
|
||||
postal=1080,
|
||||
country=Country.objects.create(
|
||||
nameus="Belgium",
|
||||
namefr="Belgique",
|
||||
isonum=56,
|
||||
iso2="BE"
|
||||
)
|
||||
)
|
||||
nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual(str(club), "RSCA (à ?)")
|
||||
self.assertEqual(str(club), "RSCA (à ?)")
|
|
@ -1,10 +1,6 @@
|
|||
# coding=UTF-8
|
||||
|
||||
from .models import (
|
||||
Club,
|
||||
Place,
|
||||
Country
|
||||
)
|
||||
from .models import Club, Place, Country
|
||||
import pytest
|
||||
|
||||
# class GymnastTestCase():
|
||||
|
@ -12,11 +8,13 @@ def test_country_tostring():
|
|||
c = Country(namefr="Belgique", iso2="56")
|
||||
assert str(c) == "Belgique (56)"
|
||||
|
||||
|
||||
def test_place_tostring():
|
||||
p = Place(name="FATC", city="Lillois")
|
||||
assert str(p) == "FATC (Lillois)"
|
||||
|
||||
|
||||
def test_club_tostring():
|
||||
p = Place(name="FATC", city="Lillois")
|
||||
club = Club(place=p, name="FATC2")
|
||||
assert str(club) == "FATC2 (à Lillois)"
|
||||
assert str(club) == "FATC2 (à Lillois)"
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ObjectiveConfig(AppConfig):
|
||||
name = "khana.objective"
|
|
@ -6,13 +6,11 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objective', '0015_auto_20190524_1211'),
|
||||
("objective", "0015_auto_20190524_1211"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='educative',
|
||||
old_name='information',
|
||||
new_name='content',
|
||||
model_name="educative", old_name="information", new_name="content",
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
]
|
|
@ -3,7 +3,7 @@
|
|||
from datetime import date
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from base.models import Markdownizable
|
||||
from khana.base.models import Markdownizable
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
|
@ -17,24 +17,24 @@ class Educative(Markdownizable):
|
|||
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")
|
||||
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"
|
||||
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)
|
||||
level = models.PositiveSmallIntegerField(verbose_name="Level", default=0)
|
||||
rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0)
|
||||
educatives = models.ManyToManyField(
|
||||
"self", related_name = "educativeOf", blank = True, symmetrical = False
|
||||
"self", related_name="educativeOf", blank=True, symmetrical=False
|
||||
)
|
||||
prerequisites = models.ManyToManyField(
|
||||
"self", related_name = "prerequisiteOf", blank = True, symmetrical = False
|
||||
"self", related_name="prerequisiteOf", blank=True, symmetrical=False
|
||||
)
|
||||
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(
|
||||
blank = True, null = True, verbose_name = "Girl's age"
|
||||
blank=True, null=True, verbose_name="Girl's age"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -56,10 +56,10 @@ class TouchPosition(models.Model):
|
|||
verbose_name_plural = "Landings"
|
||||
ordering = ["label", "short_label", "is_default", "allowed_in_competition"]
|
||||
|
||||
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")
|
||||
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")
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self.longLabel)
|
||||
|
@ -104,29 +104,31 @@ class Skill(Educative):
|
|||
(2, "Backward"),
|
||||
)
|
||||
|
||||
position = models.CharField(max_length = 2, choices = POSITION_CHOICES)
|
||||
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,
|
||||
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,
|
||||
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"
|
||||
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)
|
||||
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):
|
||||
|
@ -144,9 +146,9 @@ class Routine(Educative):
|
|||
|
||||
active = models.BooleanField()
|
||||
jumps = models.ManyToManyField(
|
||||
Skill, through = "RoutineSkill", verbose_name = "routine"
|
||||
Skill, through="RoutineSkill", verbose_name="routine"
|
||||
) # ceci n'est pas un vrai champ
|
||||
is_competitive = models.BooleanField(default = False)
|
||||
is_competitive = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return "%s (%s)" % (self.label, self.short_label)
|
||||
|
@ -216,10 +218,10 @@ class RoutineSkill(models.Model):
|
|||
ordering = ("rank",)
|
||||
|
||||
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, on_delete = models.CASCADE, default = None, related_name = "routine_links"
|
||||
Skill, on_delete=models.CASCADE, default=None, related_name="routine_links"
|
||||
)
|
||||
rank = models.PositiveSmallIntegerField()
|
||||
|
||||
|
@ -247,13 +249,13 @@ class Chrono(models.Model):
|
|||
(99, "Other"),
|
||||
)
|
||||
|
||||
routine = models.ForeignKey(Routine, on_delete = models.CASCADE, default = None)
|
||||
routine_type = models.PositiveSmallIntegerField(choices = ROUTINETYPE_CHOICE)
|
||||
routine = models.ForeignKey(Routine, on_delete=models.CASCADE, default=None)
|
||||
routine_type = models.PositiveSmallIntegerField(choices=ROUTINETYPE_CHOICE)
|
||||
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")
|
||||
score = models.DecimalField(max_digits = 5, decimal_places = 2)
|
||||
date = models.DateField(default=date.today, verbose_name="Date")
|
||||
score = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
|
||||
def __str__(self):
|
||||
return "%s le %s : %s pour %s" % (
|
||||
|
@ -278,13 +280,13 @@ class Evaluation(models.Model):
|
|||
Classe permettant l'évaluation des éducatifs.
|
||||
"""
|
||||
|
||||
date = models.DateField(default = date.today, verbose_name = "Date")
|
||||
value = models.PositiveSmallIntegerField(default = 0)
|
||||
date = models.DateField(default=date.today, verbose_name="Date")
|
||||
value = models.PositiveSmallIntegerField(default=0)
|
||||
educative = models.ForeignKey(
|
||||
Educative,
|
||||
related_name = "depart_of",
|
||||
verbose_name = "Take-off position",
|
||||
on_delete = models.CASCADE,
|
||||
related_name="depart_of",
|
||||
verbose_name="Take-off position",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
type_of_evaluator = models.BooleanField(default = 0)
|
||||
evaluator = models.ForeignKey(User, null = True, on_delete = models.SET_NULL)
|
||||
type_of_evaluator = models.BooleanField(default=0)
|
||||
evaluator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
|
|
@ -42,11 +42,7 @@ routine_urlpatterns = [
|
|||
views.delete_skill_from_routine,
|
||||
name="delete_skill_from_routine",
|
||||
),
|
||||
path(
|
||||
r"suggest/",
|
||||
views.suggest_routine,
|
||||
name="suggest_routine",
|
||||
)
|
||||
path(r"suggest/", views.suggest_routine, name="suggest_routine",),
|
||||
]
|
||||
|
||||
# Chrono
|
|
@ -338,7 +338,9 @@ def random_skill(request, skill_quantity=20, is_competitive=True):
|
|||
number_of_skill = skillid_list.count()
|
||||
selected_skillid_list = [
|
||||
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)
|
||||
|
@ -387,12 +389,14 @@ def __construct_routine(
|
|||
|
||||
if current_routine:
|
||||
skill_list = Skill.objects.filter(
|
||||
departure = current_routine[-1].landing
|
||||
) # , difficulty__lte = total_difficulty_score
|
||||
departure=current_routine[-1].landing
|
||||
) # , difficulty__lte = total_difficulty_score
|
||||
if len(current_routine) == (max_routine_length - 1):
|
||||
skill_list = skill_list.filter(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:
|
||||
skill_list = Skill.objects.filter(departure__longLabel="Debout")
|
||||
|
||||
|
@ -404,23 +408,31 @@ def __construct_routine(
|
|||
min_diff_skill = total_difficulty_score
|
||||
max_diff_skill = total_difficulty_score + 3
|
||||
else:
|
||||
if 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))
|
||||
if (
|
||||
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:
|
||||
return
|
||||
|
||||
if (math.ceil(total_difficulty_score / max_routine_length) + 2) <= max_skill_difficulty:
|
||||
max_diff_skill = math.ceil(total_difficulty_score / max_routine_length) + 2
|
||||
if (
|
||||
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:
|
||||
return
|
||||
|
||||
skill_list = skill_list.filter(
|
||||
difficulty__gte = (min_diff_skill / 10),
|
||||
difficulty__lte = (max_diff_skill / 10)
|
||||
difficulty__gte=(min_diff_skill / 10), difficulty__lte=(max_diff_skill / 10)
|
||||
)
|
||||
|
||||
if gymnast:
|
||||
skill_list = skill_list.filter(cando__gymnast = gymnast)
|
||||
skill_list = skill_list.filter(cando__gymnast=gymnast)
|
||||
|
||||
for skill in skill_list:
|
||||
current_routine.append(skill)
|
||||
|
@ -429,7 +441,9 @@ def __construct_routine(
|
|||
current_routine,
|
||||
max_routine_length,
|
||||
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,
|
||||
logic,
|
||||
gymnast,
|
||||
|
@ -442,7 +456,7 @@ def __construct_routine(
|
|||
|
||||
def suggest_routine(
|
||||
request,
|
||||
max_routine_length = 2,
|
||||
max_routine_length=2,
|
||||
total_difficulty_score=None,
|
||||
competition=True,
|
||||
logic=True,
|
||||
|
@ -471,24 +485,32 @@ def suggest_routine(
|
|||
total_difficulty_score = 26
|
||||
|
||||
if gymnast:
|
||||
max_skill_difficulty = Educative.objects.values('difficulty').filter(cando__gymnast=gymnast).order_by(
|
||||
"-difficulty"
|
||||
)[:1][0]["difficulty"] * 10
|
||||
max_skill_difficulty = (
|
||||
Educative.objects.values("difficulty")
|
||||
.filter(cando__gymnast=gymnast)
|
||||
.order_by("-difficulty")[:1][0]["difficulty"]
|
||||
* 10
|
||||
)
|
||||
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)
|
||||
# for total_difficulty_score in difficulty_scores:
|
||||
# print("===============================================================================================")
|
||||
__construct_routine(
|
||||
routine,
|
||||
max_routine_length,
|
||||
max_skill_difficulty,
|
||||
total_difficulty_score,
|
||||
competition,
|
||||
logic,
|
||||
gymnast,
|
||||
)
|
||||
routine,
|
||||
max_routine_length,
|
||||
max_skill_difficulty,
|
||||
total_difficulty_score,
|
||||
competition,
|
||||
logic,
|
||||
gymnast,
|
||||
)
|
||||
# print(routines)
|
||||
|
||||
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PeopleConfig(AppConfig):
|
||||
name = "khana.people"
|
|
@ -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
Loading…
Reference in New Issue