Merge branch 'master' of grimbox.be:Sulley/khana

This commit is contained in:
Trullemans Gregory 2021-06-20 14:30:03 +02:00
commit 787ba706aa
16 changed files with 234 additions and 154 deletions

View File

@ -3,17 +3,22 @@
# A comma-separated list of package or module names from where C extensions may # A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may # be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. # run arbitrary code.
extension-pkg-allow-list=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist= extension-pkg-whitelist=
# Specify a score threshold to be exceeded before program exits with error. # Specify a score threshold to be exceeded before program exits with error.
fail-under=10.0 fail-under=10.0
# Add files or directories to the blacklist. They should be base names, not # Files or directories to be skipped. They should be base names, not paths.
# paths.
ignore=CVS ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The # Files or directories matching the regex patterns are skipped. The regex
# regex matches against base names, not paths. # matches against base names, not paths.
ignore-patterns= ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as # Python code to execute, usually for sys.path manipulation such as
@ -181,7 +186,7 @@ max-nested-blocks=5
# inconsistent-return-statements if a never returning function is called then # inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be # it will be considered as an explicit return statement and no message will be
# printed. # printed.
never-returning-functions=sys.exit never-returning-functions=sys.exit,argparse.parse_error
[STRING] [STRING]
@ -227,6 +232,8 @@ single-line-if-stmt=no
[VARIABLES] [VARIABLES]
django-settings-module=khana.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.
additional-builtins= additional-builtins=
@ -234,6 +241,9 @@ additional-builtins=
# Tells whether unused global variables should be treated as a violation. # Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of strings which can identify a callback function by name. A callback # List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings. # name must start or end with one of those strings.
callbacks=cb_, callbacks=cb_,
@ -367,6 +377,13 @@ class-attribute-naming-style=any
# attribute-naming-style. # attribute-naming-style.
#class-attribute-rgx= #class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style.
#class-const-rgx=
# Naming style matching correct class names. # Naming style matching correct class names.
class-naming-style=PascalCase class-naming-style=PascalCase
@ -455,9 +472,13 @@ variable-naming-style=snake_case
max-spelling-suggestions=4 max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work, # Spelling dictionary name. Available dictionaries: none. To make it work,
# install the python-enchant package. # install the 'python-enchant' package.
spelling-dict= spelling-dict=
# List of comma separated words that should be considered directives if they
# appear and the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
# List of comma separated words that should not be checked. # List of comma separated words that should not be checked.
spelling-ignore-words= spelling-ignore-words=
@ -519,6 +540,9 @@ min-public-methods=2
[CLASSES] [CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes. # List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__, defining-attr-methods=__init__,
__new__, __new__,
@ -557,16 +581,17 @@ analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma. # Deprecated modules which should not be used, separated by a comma.
deprecated-modules=optparse,tkinter.tix deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must # Output a graph (.gv or any supported image format) of external dependencies
# not be disabled). # to the given file (report RP0402 must not be disabled).
ext-import-graph= ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the # Output a graph (.gv or any supported image format) of all (i.e. internal and
# given file (report RP0402 must not be disabled). # external) dependencies to the given file (report RP0402 must not be
# disabled).
import-graph= import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must # Output a graph (.gv or any supported image format) of internal dependencies
# not be disabled). # to the given file (report RP0402 must not be disabled).
int-import-graph= int-import-graph=
# Force import order to recognize a module as part of the standard # Force import order to recognize a module as part of the standard

View File

@ -3,7 +3,9 @@
black==19.10b0 black==19.10b0
coverage==5.5 coverage==5.5
flake8==3.9.1 flake8==3.9.1
pylint==2.8.2
pylint-django==2.4.4
django-spaghetti-and-meatballs==0.4.2 django-spaghetti-and-meatballs==0.4.2
docutils==0.16 docutils==0.16
pytest==6.2.4 pytest==6.2.4
pytest-django==4.2.0 pytest-django==4.2.0

3
setup.cfg Normal file
View File

@ -0,0 +1,3 @@
[flake8]
max-line-length=100
max-complexity=10

View File

@ -20,5 +20,5 @@ class Markdownizable(models.Model):
def to_markdown(self): def to_markdown(self):
"""Convertit le champ `content` en (Github-flavored) Markdown.""" """Convertit le champ `content` en (Github-flavored) Markdown."""
return markdown.markdown(self.content) return markdown.markdown(self.content)

View File

@ -11,6 +11,6 @@ class TestMarkdownizable(TestCase):
def test_to_markdown(self): def test_to_markdown(self):
"""Vérifie qu'un contenu Markdown est correctement convertit en HTML.""" """Vérifie qu'un contenu Markdown est correctement convertit en HTML."""
m = Markdownizable(information="# Title") markdown_content = Markdownizable(information="# Title")
self.assertEqual(m.to_markdown(), "<h1>Title</h1>") self.assertEqual(markdown_content.to_markdown(), "<h1>Title</h1>")

View File

@ -7,6 +7,8 @@ from .models import Message
@admin.register(Message) @admin.register(Message)
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):
"""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

@ -1,14 +1,13 @@
"""Configuration et représentation des forms liés aux messages.""" """Configuration et représentation des forms liés aux messages."""
from datetime import date
from django import forms from django import forms
from people.models import Gymnast
from .models import Message from .models import Message
class MessageForm(forms.ModelForm): class MessageForm(forms.ModelForm):
"""Formulaire de base pour la création et la modification de messages
"""
class Meta: class Meta:
model = Message model = Message
fields = ( fields = (

View File

@ -1,10 +1,23 @@
"""Modelisation de tout ce qui touche à la communication entre utilisateurs.
Cette application gère:
* Les messages
* Ah, c'est tout en fait :-)
"""
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime from datetime import datetime
from django.db import models
from django.contrib.auth import get_user_model
from base.models import Markdownizable from base.models import Markdownizable
User = get_user_model()
class Message(Markdownizable): class Message(Markdownizable):
"""Représente un message échangé entre deux utilisateurs. """Représente un message échangé entre deux utilisateurs.

View File

@ -1,12 +1,19 @@
# coding=UTF-8 """Tests liés au modèle de l'application Communication"""
from datetime import datetime from datetime import datetime
from .models import Message
from django.contrib.auth.models import User
import pytest
def test_message_tostring(): from django.contrib.auth import get_user_model
from .models import Message
User = get_user_model()
def test_message_to_string():
"""Vérifie la représentation textuelle d'un message
"""
timing = datetime.now() timing = datetime.now()
u = User(username='fred', password='fredpassword') user = User(username='fred', password='fredpassword')
m = Message(sender=u, written_at=timing, title="test") message = Message(sender=user, written_at=timing, title="test")
assert str(m) == "fred - " + str(timing) + " : test" assert str(message) == "fred - " + str(timing) + " : test"

View File

@ -1,6 +1,6 @@
"""Définition des routes d'actions permettant de contrôler les messages et la communication.""" """Définition des routes d'actions permettant de contrôler les messages et la communication."""
from django.urls import path, re_path from django.urls import path
from . import views from . import views

View File

@ -1,17 +1,18 @@
"""Vues et fonctions pour tout ce qui touche à la communication entre plusieurs utilisateurs.""" """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.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.urls import reverse from django.urls import reverse
from datetime import datetime
from .forms import MessageForm from .forms import MessageForm
from .models import Message from .models import Message
@login_required @login_required
def get_number_of_unread_message(request): def get_number_of_unread_message(request):
"""Récupère le nombre de messages non lus associés à l'utilisateur en session. """Récupère le nombre de messages non lus associés à l'utilisateur en session.
@ -80,7 +81,7 @@ def delete_message(request, messageid):
""" """
try: try:
message = Message.objects.get(pk=messageid) message = Message.objects.get(pk=messageid)
if message.sender == request.user or message.recipient == request.user : if message.sender == request.user or message.recipient == request.user :
message.delete() message.delete()
else: else:
@ -103,8 +104,8 @@ def compose_message(request):
if form.is_valid(): if form.is_valid():
form.save() form.save()
return HttpResponseRedirect(reverse("sent_messages")) return HttpResponseRedirect(reverse("sent_messages"))
else:
print("Invalid form") print("Invalid form")
else: else:
form = MessageForm() form = MessageForm()

View File

@ -1,7 +1,9 @@
# Application `Planning` # Application `Planning`
## Saison ## Saison
Une saison est déinie par : Une saison est déinie par :
- un id, - un id,
- un label, - un label,
- une date de début et - une date de début et
@ -11,9 +13,12 @@ La date de début est très souvent le : 01/09/xxxx
La date de fin est très souvent le : 31/08/xxxy La date de fin est très souvent le : 31/08/xxxy
Exemple : 1/9/2015 - 31/8/2016 Exemple : 1/9/2015 - 31/8/2016
NOTE: Le fait que la date de début soit **très souvent** le 01 septembre indique sans doute une date par défaut (modifiable) au niveau du modèle. Idem pour la date de fin.
NOTE: je ne comprends pas la méthode `week_number_from_begin`. Si cela fait référence à la date de début, alors il faut le mentionner dans le nom de la fonction.
## Course ## Course
Un cours est un ensemble d'entraînements (`training`) (récurrents ?) défini par : Un cours est un ensemble d'entraînements (`training`) (récurrents ?) défini par :
- une heure de début et une heure de fin, - une heure de début et une heure de fin,
- une date de début et une date de fin - une date de début et une date de fin
@ -26,16 +31,32 @@ Réflexions/questions :
- les cours devraient-ils être liés à une saison ? - les cours devraient-ils être liés à une saison ?
- un cours est considéré comme donné hebdomadairement entre la date de début et la date de fin (hérite de la classe `Temporizable`), mais est-ce une bonne idée ? Est-ce une bonne manière de faire ? - un cours est considéré comme donné hebdomadairement entre la date de début et la date de fin (hérite de la classe `Temporizable`), mais est-ce une bonne idée ? Est-ce une bonne manière de faire ?
NOTE: Je dirais que oui. D'un côté, tu n'aurais pas de possibilité de déduction entre un cours et le moment où il y a réellement lieu - de ce que je comprends, le *cours* correspond en fait à quelque chose qui est prévu selon une récurrence donnée - eg. "tous les mardis (deuxième jour de la semaine), entre 10h et 12h, avec Machin, Chose et Brol".
La *saison* va juste indiquer la date de début et de fin des cours qui y sont liés.
Même s'il y a moyen de le représenter différement, je pense surtout que le concept de saison parle à beaucoup de monde.
NOTE: la réflexion va surtout être "est-ce qu'un cours est différent entre deux saison ?" A priori, oui, puisque Bidule peut devenir entraineur pour la saison 2020-2021.
L'avantage, c'est que Machin pourrait se connecter sur son profil et dire "ah ouais, cette année, je donne cours le jeudi et le samedi."
## Training ## Training
Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents. Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents.
Un objet de cette classe lie donc : Un objet de cette classe lie donc :
- un cours, - un cours,
- gymnaste et - des gymnastes présents et
- une date. - une date.
NOTE: Techniquement, tu peux ici mettre une contrainte ou un avertissement si l'entrainement est situé à une date différente de ce que la saison devrait autoriser.
NOTE: dans la classe Training, il est question d'une `ForeignKey` vers Gymnast, mais ce devrait être un ManyToManyField.
NOTE: De la même manière, je reprendrais aussi l'heure de début et de fin. Entre ce qui est prévu (le cours) et la réalité (l'entraintement), il pourrait y avoir des différences.
Cela permettrait aussi de planifier les cours - dire en gros que, en début d'année, tu (l'appli) planifies les jours fériés, et *projette* les entrainements pour la saison, sur base de ce qui est prévu.
## Round ## Round
@ -43,6 +64,10 @@ Un objet de cette classe lie donc :
Classe représentant les passages des élèves lors d'un entrainement. Classe représentant les passages des élèves lors d'un entrainement.
Chaque record représente un passage. Il est donc lié à un record de la classe `Training`. Chaque record représente un passage. Il est donc lié à un record de la classe `Training`.
NOTE: Est-ce qu'il est important de savoir qui est l'entraineur qui a donné une évaluation ?
NOTE: au niveau du round, il y a un ensemble d'informations chronologiques: `nb_of_realisations` (au pluriel...), `nb_of_success`, ... mais c'est incohérent avec le `round_number`, puisque je suppose qu'il pourrait faire un tour de A, puis B, puis revenir à A.
Cette partie-ci me semble très complexe - sans oublier qu'il va falloir la remplir: si tes entraineurs chipotent sur une tablette ou sur un écran pour chaque action que réalise un gymnaste, ça va pas être sympa pour eux.
## Group ## Group
@ -50,6 +75,11 @@ Chaque record représente un passage. Il est donc lié à un record de la classe
Classe représentant les groupes (Loisir, D1, D2, A, B, …). Classe représentant les groupes (Loisir, D1, D2, A, B, …).
Un groupe appartient à un club. Un groupe appartient à un club.
NOTE: pourquoi garder un champ `active` ? Il y a un risque qu'un groupe soit désactivé ? Si oui, ne vaut-il pas mieux garder le moment où il l'a été ?
NOTE: est-ce que le champ `name` n'est pas un dictionnaire fini ? Loisir, D1, D2, ... ?
NOTE: est-ce que tu n'as pas une contrainte sur le nom, le club et la saison ?
## Subgroup ## Subgroup
@ -61,28 +91,33 @@ Un sous-groupe appartient à un groupe (pour rappel, lui-même lié à un club).
De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe, De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe,
nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison. nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison.
NOTE: re-question sur le `name`. A mon avis, si le nom du groupe est fini, tu peux te passer d'une des classes `Group` ou `Subgroup`, et cela simplifierait pas mal la gestion du club.
## Unavailability ## Unavailability
Classe représentant les indisponibilités. Classe représentant les indisponibilités.
NOTE: avec la réflexion ci-dessous, cela pourrait ne plus être utile. Les *Courses* correspondent à la modélisation tandis que les entrainements représentent le planifié/réalisé. Du coup, il suffit qu'un entrainenemnts n'existe pas pour qu'il ne soit pas planifié.
## PlanningLine ## PlanningLine
Classe représentant les passages prévisionnels (incubating idea). Classe représentant les passages prévisionnels (incubating idea).
NOTE: en gros, tu veux proposer un entrainement personnalisé pour chaque gymnaste ;) Je ne vois pas la valeur ajoutée. Le mieux serait d'avoir une forme de proposition au niveau des Rounds et des Trainings, quitte à la modifier pendant l'entrainement. Sinon, je ne vois pas trop l'idée.
## EventType ## EventType
Classe représentant les types d'évènements. Classe représentant les types d'évènements.
C'est un dictionnaire fini : C'est un dictionnaire fini :
- compétiton qualificative, - compétiton qualificative,
- compétition finale, - compétition finale,
- démonstration, - démonstration,
- … - …
NOTE: tu peux utiliser un champ de type Choice, si le dictionnaire est fini. Cela te fera gagner une jointure. Si le dictionnaire a ***une*** chance d'avoir une nouvelle valeur, garde la table.
## Event ## Event
@ -97,6 +132,8 @@ Un évènement est caractérisé par :
Je ne me rapelle plus à quoi sert le club. Je ne me rapelle plus à quoi sert le club.
NOTE: alors, retire le club :-p
## Event_Participation ## Event_Participation
NOTE: Dans Event, tu as déjà un lien avec des gymnastes, que tu reprends dans la classe EventParticipation (pas de "*_*"). Autant ne garder qu'une seule liaison entre un évènement et des gymnastes, et compléter ces enregistrements après (ou pendant) pour dire si Choupidou étant bien placé ou pas (quitte à laisser le `rank` vide si Choupidou n'est finalement pas venu ou s'il a sauté comme une bouse - oui, ça arrive).

View File

@ -7,5 +7,5 @@ from . import views
profile_urlpatterns = [ profile_urlpatterns = [
path(r"lookup/", views.user_lookup, name="user_lookup"), path(r"lookup/", views.user_lookup, name="user_lookup"),
path(r"edit/<int:profileid>/", views.profile_update, name="profile_update"), path(r"edit/", views.profile_update, name="profile_update"),
] ]

View File

@ -60,17 +60,12 @@ def user_lookup(request):
@login_required @login_required
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
def profile_update(request, profileid): def profile_update(request):
"""Modification d'un profil utilisateur. """Modification du profil de l'utilisateur connecté
Args:
profileid (int): L'identifiant du profil utilisateur à modifier.
""" """
profile = get_object_or_404(Profile, pk=profileid) profile = request.user.profile
if profile.user != request.user:
raise PermissionDenied("Permission denied : you don't have the permission to update this profile.")
if request.method == "POST": if request.method == "POST":
form = ProfileForm(request.POST, instance=profile) form = ProfileForm(request.POST, instance=profile)
@ -78,7 +73,6 @@ def profile_update(request, profileid):
if form.is_valid(): if form.is_valid():
form.save() form.save()
request.session["profileid"] = profileid
request.session["template"] = profile.template_color request.session["template"] = profile.template_color
request.session["sidebar"] = profile.sidebar_color request.session["sidebar"] = profile.sidebar_color
request.session["is_sidebar_minified"] = profile.is_sidebar_minified request.session["is_sidebar_minified"] = profile.is_sidebar_minified
@ -88,5 +82,5 @@ def profile_update(request, profileid):
else: else:
form = ProfileForm(instance=profile) form = ProfileForm(instance=profile)
context = {"form": form, "profileid": profileid} context = {"form": form,}
return render(request, "profile_create.html", context) return render(request, "profile_create.html", context)

View File

@ -87,93 +87,93 @@
<i class="tim-icons icon-bullet-list-67 visible-on-sidebar-mini"></i> <i class="tim-icons icon-bullet-list-67 visible-on-sidebar-mini"></i>
</button> </button>
</div> </div>
<div class="navbar-toggle d-inline"> <div class="navbar-toggle d-inline">
<button type="button" class="navbar-toggler"> <button type="button" class="navbar-toggler">
<span class="navbar-toggler-bar bar1"></span> <span class="navbar-toggler-bar bar1"></span>
<span class="navbar-toggler-bar bar2"></span> <span class="navbar-toggler-bar bar2"></span>
<span class="navbar-toggler-bar bar3"></span> <span class="navbar-toggler-bar bar3"></span>
</button>
</div>
<a class="navbar-brand" href="/">Dashboard</a>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
</button>
<div class="collapse navbar-collapse" id="navigation">
<ul class="navbar-nav ml-auto">
<li class="search-bar input-group">
<button class="btn btn-link" id="search-button" data-toggle="modal" data-target="#searchModal">
<i class="tim-icons icon-zoom-split"></i>
<span class="d-lg-none d-md-block">Search</span>
</button> </button>
</li> </div>
<li class="dropdown nav-item"> <a class="navbar-brand" href="/">Dashboard</a>
<a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown"> </div>
<i class="tim-icons icon-sound-wave"></i> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-expanded="false" aria-label="Toggle navigation">
<p class="d-lg-none">Flash access</p> <span class="navbar-toggler-bar navbar-kebab"></span>
</a> <span class="navbar-toggler-bar navbar-kebab"></span>
<ul class="dropdown-menu dropdown-menu-right dropdown-navbar"> <span class="navbar-toggler-bar navbar-kebab"></span>
<li class="nav-link"> </button>
<a href="{% url 'calendar' %}" class="nav-item dropdown-item"><i class="far fa-calendar-alt"></i> Calendrier</a> <div class="collapse navbar-collapse" id="navigation">
</li> <ul class="navbar-nav ml-auto">
<li class="nav-link"> <li class="search-bar input-group">
<a href="{% url 'chrono_create' %}" class="nav-item dropdown-item"><i class="far fa-stopwatch"></i> New chrono</a> <button class="btn btn-link" id="search-button" data-toggle="modal" data-target="#searchModal">
</li> <i class="tim-icons icon-zoom-split"></i>
<li class="nav-link"> <span class="d-lg-none d-md-block">Search</span>
<a href="{% url 'score_create' %}" class="nav-item dropdown-item"><i class="far fa-share-alt"></i> New score</a> </button>
</li> </li>
<li class="nav-link"> <li class="dropdown nav-item">
<a href="#" class="nav-item dropdown-item"><i class="far fa-bezier-curve"></i> Program</a> <a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown">
</li> <i class="tim-icons icon-sound-wave"></i>
</ul> <p class="d-lg-none">Flash access</p>
</li> </a>
<li class="dropdown nav-item"> <ul class="dropdown-menu dropdown-menu-right dropdown-navbar">
<a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown"> <li class="nav-link">
{% if number_unreaded_message %} <a href="{% url 'calendar' %}" class="nav-item dropdown-item"><i class="far fa-calendar-alt"></i> Calendrier</a>
<div class="notification d-none d-lg-block d-xl-block"></div> </li>
{% endif %} <li class="nav-link">
<i class="tim-icons icon-email-85"></i> <a href="{% url 'chrono_create' %}" class="nav-item dropdown-item"><i class="far fa-stopwatch"></i> New chrono</a>
<p class="d-lg-none">Notifications</p> </li>
</a> <li class="nav-link">
<ul class="dropdown-menu dropdown-menu-right dropdown-navbar"> <a href="{% url 'score_create' %}" class="nav-item dropdown-item"><i class="far fa-share-alt"></i> New score</a>
<li class="nav-link"> </li>
<a href="{% url 'compose_message' %}" class="nav-item dropdown-item"><i class="tim-icons icon-pencil"></i> Compose message</a> <li class="nav-link">
</li> <a href="#" class="nav-item dropdown-item"><i class="far fa-bezier-curve"></i> Program</a>
<li class="nav-link"> </li>
<a href="{% url 'received_messages' %}" class="nav-item dropdown-item"><i class="tim-icons icon-email-85"></i> Received messages</a> </ul>
</li> </li>
<li class="nav-link"> <li class="dropdown nav-item">
<a href="{% url 'sent_messages' %}" class="nav-item dropdown-item"><i class="tim-icons icon-send"></i> Sent messages</a> <a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown">
</li> {% if number_unreaded_message %}
</ul> <div class="notification d-none d-lg-block d-xl-block"></div>
</li> {% endif %}
<li class="dropdown nav-item"> <i class="tim-icons icon-email-85"></i>
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown"> <p class="d-lg-none">Notifications</p>
<div class="photo"> </a>
<img src="{% static '/img/mike.jpg' %}" alt="Profile Photo"> <ul class="dropdown-menu dropdown-menu-right dropdown-navbar">
</div> <li class="nav-link">
<b class="caret d-none d-lg-block d-xl-block"></b> <a href="{% url 'compose_message' %}" class="nav-item dropdown-item"><i class="tim-icons icon-pencil"></i> Compose message</a>
<p class="d-lg-none">Log out</p> </li>
</a> <li class="nav-link">
<ul class="dropdown-menu dropdown-navbar"> <a href="{% url 'received_messages' %}" class="nav-item dropdown-item"><i class="tim-icons icon-email-85"></i> Received messages</a>
<li class="nav-link"> </li>
<a href="{% url 'profile_update' request.session.profileid %}" class="nav-item dropdown-item">Profile</a> <li class="nav-link">
</li> <a href="{% url 'sent_messages' %}" class="nav-item dropdown-item"><i class="tim-icons icon-send"></i> Sent messages</a>
<!-- <li class="nav-link"> --> </li>
</ul>
</li>
<li class="dropdown nav-item">
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">
<div class="photo">
<img src="{% static '/img/mike.jpg' %}" alt="Profile Photo">
</div>
<b class="caret d-none d-lg-block d-xl-block"></b>
<p class="d-lg-none">Log out</p>
</a>
<ul class="dropdown-menu dropdown-navbar">
<li class="nav-link">
<a href="{% url 'profile_update' %}" class="nav-item dropdown-item">Profile</a>
</li>
<!-- <li class="nav-link"> -->
<!-- <a href="javascript:void(0)" class="nav-item dropdown-item">Settings</a> --> <!-- <a href="javascript:void(0)" class="nav-item dropdown-item">Settings</a> -->
<!-- </li> --> <!-- </li> -->
<li class="dropdown-divider"></li> <li class="dropdown-divider"></li>
<li class="nav-link"> <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">Log out</a>
</li> </li>
</ul> </ul>
</li> </li>
<li class="separator d-lg-none"></li> <li class="separator d-lg-none"></li>
</ul> </ul>
</div>
</div> </div>
</div>
</nav> </nav>
<div class="modal modal-search fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModal" aria-hidden="true"> <div class="modal modal-search fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModal" aria-hidden="true">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
@ -237,7 +237,7 @@
</div> </div>
<div class="togglebutton switch-change-color mt-3"> <div class="togglebutton switch-change-color mt-3">
<span class="label-switch">LIGHT MODE</span> <span class="label-switch">LIGHT MODE</span>
<input type="checkbox" name="checkbox" {% if request.session.template == 0 %}checked{% endif %} class="bootstrap-switch" data-on-label="" data-off-label="" /> <input type="checkbox" name="checkbox" {% if request.session.template == 0 %}checked{% endif %} class="bootstrap-switch" data-on-label="" data-off-label="" />
<span class="label-switch label-right">DARK MODE</span> <span class="label-switch label-right">DARK MODE</span>
</div> </div>
</li> </li>
@ -301,26 +301,22 @@
$full_page = $('.full-page'); $full_page = $('.full-page');
$sidebar_responsive = $('body > .navbar-collapse'); $sidebar_responsive = $('body > .navbar-collapse');
{% if request.session.is_sidebar_minified %}sidebar_mini_active = true; {% if request.session.is_sidebar_minified %}
{% else %}sidebar_mini_active = false;{% endif %} sidebar_mini_active = true; {% else %}
sidebar_mini_active = false; {% endif %}
{% if request.session.template == 0 %}
{% if request.session.template == 0%}white_color = false; white_color = false; {% else %}
{% else %}white_color = true;{% endif %} white_color = true; {% endif %}
window_width = $(window).width(); window_width = $(window).width();
fixed_plugin_open = $('.sidebar .sidebar-wrapper .nav li.active a p').html(); fixed_plugin_open = $('.sidebar .sidebar-wrapper .nav li.active a p').html();
{% if request.session.sidebar == 1 %} {% if request.session.sidebar == 1 %}
color = "blue"; color = "blue"; {% elif request.session.sidebar == 2 %}
{% elif request.session.sidebar == 2 %} color = "green"; {% elif request.session.sidebar == 3 %}
color = "green"; color = "orange"; {% elif request.session.sidebar == 4 %}
{% elif request.session.sidebar == 3 %} color = "red"; {% endif %}
color = "orange";
{% elif request.session.sidebar == 4 %}
color = "red";
{% endif %}
$sidebar.attr('data', color); $sidebar.attr('data', color);
$main_panel.attr('data', color); $main_panel.attr('data', color);
$full_page.attr('filter-color', color); $full_page.attr('filter-color', color);
@ -417,5 +413,6 @@
{% block footerscript %}{% endblock %} {% block footerscript %}{% endblock %}
</body> </body>
</html> </html>

View File

@ -12,7 +12,7 @@
<h4 class="card-title">Edit User Profile</h4> <h4 class="card-title">Edit User Profile</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<form action="{% url 'profile_update' profileid %}" method="post" class="form-horizontal" id="formulaire" name="formulaire"> <form action="{% url 'profile_update' %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %} {% csrf_token %}
<div class="form-group row"> <div class="form-group row">
<label for="id_birthdate" class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4 col-form-label">Birthdate</label> <label for="id_birthdate" class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4 col-form-label">Birthdate</label>
@ -59,10 +59,10 @@
</div> </div>
</div> </div>
<script type="text/javascript" > <script type="text/javascript">
$(function(){ $(function() {
blackDashboard.initDateTimePicker(); blackDashboard.initDateTimePicker();
}); });
</script> </script>
{% endblock %} {% endblock %}