Merge branch 'master' into propal/pep8-pylint

This commit is contained in:
Fred 2021-06-19 08:28:03 +02:00
commit e944ecdb34
5 changed files with 147 additions and 119 deletions

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 (un cours est considéré comme donné hebdomadairement entre - une date de début et une date de fin (un cours est considéré comme donné hebdomadairement entre
@ -24,16 +29,32 @@ Un cours est un ensemble d'entraînements (`training`) (récurrents ?) défini p
Réflexion : les cours devraient-ils être liés à une saison ? Réflexion : les cours devraient-ils être liés à une saison ?
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
@ -41,6 +62,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
@ -48,6 +73,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
@ -59,28 +89,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
@ -95,6 +130,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 %}