diff --git a/docs/DevConventions.md b/docs/DevConventions.md index a2af78b..5e2032d 100644 --- a/docs/DevConventions.md +++ b/docs/DevConventions.md @@ -1,7 +1,7 @@ # Conventions de codage pour ScoDoc Le projet étant très ancien, le code est passé par différentes conventions et -frameworks. Le style de python et celui du *frontend* web ont considérablement +frameworks. Le style de python et celui du _frontend_ web ont considérablement évolués ces dernières décennies. Pour les nouveaux codes, respecter les principes suivants: @@ -22,7 +22,7 @@ Pour les nouveaux codes, respecter les principes suivants: - l'usage de **[black](https://black.readthedocs.io/en/stable/)** est obligatoire - l'usage de pylint et mypy est fortement recommandé -- Pour le code JS, indentation avec 2 espaces (sous VS Code, utiliser *Prettier*). +- Pour le code JS, indentation avec 2 espaces (sous VS Code, utiliser _Prettier_). ### Conventions standard Python @@ -44,7 +44,7 @@ def _upload_justificatif_files( ## Pages et templates -Les vues utilisent un template *Jinja2*, fichier avec extension j2. +Les vues utilisent un template _Jinja2_, fichier avec extension j2. On s'astreint, sauf cas particulier, à un nommage identique de la route, la vue et du template. @@ -76,7 +76,7 @@ Les templates suivants sont les plus utilisés pour les pages: - `sco_page.j2` page standard dans un formsemestre: avec sidebar et barre de menu du semestre. Le contenu de la variable `content` est placé comme HTML - brut (*safe*) dans la partie contenu. Passer `sco=ScoData(formsemestre=formsemestre)` + brut (_safe_) dans la partie contenu. Passer `sco=ScoData(formsemestre=formsemestre)` en argument au template. - `sco_page_dept.j2` page dans un département (avec sidebar) mais sans formsemestre (pas de @@ -120,6 +120,7 @@ html_sco_header.sco_footer() ``` La migration la plus simple consiste à utilise `sco_page.j2`: + ```py render_template( "sco_page.j2", @@ -163,7 +164,6 @@ Mais si on souhaite générer le contenu dans un template, cela prendra la forme {% endblock %} ``` - ### Classes css `scodoc.css` (et à partir de 9.7, `scodoc97.css`) définissent quelques classes à usage général: @@ -179,12 +179,12 @@ Mais si on souhaite générer le contenu dans un template, cela prendra la forme ## Accès à la base de donnée Sauf exception (requêtes exotiques, problèmes de performance) à discuter, les -accès à la base se font à travers l'ORM *SQLAlchemy*. Les modèles sont définis +accès à la base se font à travers l'ORM _SQLAlchemy_. Les modèles sont définis dans `app/models`, sauf les comptes utilisateurs qui sont dans `/app/auth/models.py`. Au moment de la définition de nouveaux modèles, éviter si possible les champs -*nullables*, penser à créer là où ce sera utile des index. +_nullables_, penser à créer là où ce sera utile des index. ## Tableaux @@ -192,19 +192,76 @@ ScoDoc génère beaucoup de tableaux, sauf exception destinés à l'affichage We On utilise la super-classe `Table` de `app/tables/table_builder.py`. -Le rendu HTML utilise *DataTables*. +Le rendu HTML utilise _DataTables_. ## Dates et heures ScoDoc, contrairement à de nombreuses applications, est centré sur les -*étudiants* et les enseignements, qui sont censés opérer dans le fuseau horaire +_étudiants_ et les enseignements, qui sont censés opérer dans le fuseau horaire du serveur. Cela signifie que, quelle que soit l'emplacement de l'utilisateur, les heures affichées et saisies par ScoDoc seront données dans le fuseau horaire du serveur. -L'API publie et reçoit des heures au format ISO avec fuseau horaire (*timezone*) et n'est pas +L'API publie et reçoit des heures au format ISO avec fuseau horaire (_timezone_) et n'est pas concernée par cette remarque. +## Sélection de groupes + +De nombreuses pages ont besoin d'offrir un moyen de sélectionner un ou plusieurs +groupes d'étudiants, comme dans cet exemple: + +![menu multiselect des groupes](screens/multiselect_groups.png) + +La vue doit récupérer la liste des groupes sélectionnés ainsi: + +```py +group_ids = request.args.getlist("group_ids") +``` + +ou s'il s'agit un `POST`: + +```py +group_ids = request.form.getlist("group_ids") +``` + +On converti ensuite les arguments en entiers: + +```py +try: + group_ids = [int(gid) for gid in group_ids] +except ValueError as exc: + raise ScoValueError("group_ids invalide") from exc +``` + +et on peut utiliser le code ancien `sco_groups_view`: + +```py +groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, + formsemestre_id=formsemestre.id, + select_all_when_unspecified=True, +) +``` + +`groups_infos` est un objet qui permet de récupérer les informations et de générer le menu. + +Par exemple: + +```py +Choix des groupes : +{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True) + if groups_infos else ''} +``` + +La fonction `form_groups_choice` génère un formulaire complet. + +Autres attributs/méthodes utiles de `groups_infos`: + +- `get_etudids():list[int]` : les ids des étudiants des groupes sélectionnés; +- `groups_titles:str` : titres des groupes; +- `groups_filename:str`: un (morceau de) nom de fichier pour ces groupes. + + !!! note "Voir aussi" diff --git a/docs/screens/multiselect_groups.png b/docs/screens/multiselect_groups.png new file mode 100644 index 0000000..8a63e86 Binary files /dev/null and b/docs/screens/multiselect_groups.png differ