# 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 évolués ces dernières décennies. Pour les nouveaux codes, respecter les principes suivants: - ScoDoc est avant tout une application serveur écrite en Python, qui offre des services via une interface Web, une API, et accessoirement la ligne de commande unix. - Les pages Web générées doivent l'être en Python côté serveur. On peut utiliser du JS pour les pages dynamiques complexes (eg éditeur de partition) mais éviter d'utiliser du JS pour générer des éléments statiques: l'abus de JS conduit à dupliquer du code (qui existe en général dans le serveur) et augmente les coûts de maintenance. ## Conventions de codage ### Formatage du code - 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_). ### Conventions standard Python On se tient à la PEP8, c'est à dire en résumé: `UneClasse`, `une_fonction`, `_une_fonction_privee`, `une_variable`, `UNE_CONSTANTE_GLOBALE`. Les noms de fichiers sont en minuscules sans accents ni espaces, `comme_ceci.py`. ### Annotations de type (typehints) On annote les paramètres et résultats des fonctions. ```py def _upload_justificatif_files( just: Justificatif, form: AjoutJustificatifEtudForm ) -> bool: ... ``` ## Pages et templates 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. ```py @bp.route("/un_exemple") def un_exemple(...): ... return render_template(".../un_exemple.j2") ``` Le template aura typiquement avec la structure suivante: ```jinja {% extends "sco_page.j2" %} {% block app_content %}

Titre

{% endblock app_content %} ``` Pour les pages dans un semestre (avec barre menu etc.), on utilisera `

` au lieu de `

`. ### Templates habituels Les templates suivants sont les plus utilisés pour les pages: - `base.j2` pages hors département (accueil, configuration, ...) - `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)` en argument au template. - `sco_page_dept.j2` page dans un département (avec sidebar) mais sans formsemestre (pas de barre de menu) ### Constantes définies dans les templates ScoDoc Le `context_processor` `inject_sco_utils()` ajoute des objets pour accès à tous les templates ScoDoc: - `scu` : le module `sco_utils.py` (diverses fonctions utilitaires et variables de config.) - `sco` : des données sur le contexte, notamment les préférences, le formsemestre et l'étudiant courants (resp. `sco.formsemestre` et `sco.etud`) ### Appel d'un template page Pour mémoire, l'appel d'un template dans une vue se fait ainsi: ```py render_template( "votre_template.j2", title="Changer de référentiel de compétences", autre_variable=... ) ``` ### Passage des anciennes pages ScoDoc 7 aux templates Jinja Les anciennes pages étaient générées en python selon la structure: ```py html_sco_header.sco_header( cssstyles=["css/....css"], javascripts=[ "js/....js", ], page_title="titre de la page", ) ... contenu ... html_sco_header.sco_footer() ``` La migration la plus simple consiste à utilise `sco_page.j2`: ```py render_template( "sco_page.j2", content=contenu, title="titre de la page", cssstyles=["..."], javascripts=["..."], ) ``` Mais si on souhaite générer le contenu dans un template, cela prendra la forme: ```jinja-html {% extends "sco_page.j2" %} {% block title %} ...titre... {% endblock title %} {% block styles %} {{ super() }} {% endblock styles %} {% block app_content %}

titre page

... contenu ...
{% endblock %} {% block scripts %} {{ super() }} {% endblock %} ``` ### Classes css `scodoc.css` (et à partir de 9.7, `scodoc97.css`) définissent quelques classes à usage général: - `div.scobox`: boite (bords arrondis, fond uni) regroupant des éléments. - `div.scobox-title` le titre de la boite - `div.scobox-buttons` boutons en bas de boite. - `p.help`, `div.help`: explications, aide. - ... ## 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 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. ## Tableaux ScoDoc génère beaucoup de tableaux, sauf exception destinés à l'affichage Web et à l'export xlsx. On utilise la super-classe `Table` de `app/tables/table_builder.py`. 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 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 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" - [Introduction au développement ScoDoc](DevInternals.md) - [Guide développeurs](GuideDeveloppeurs.md) - [API ScoDoc 9](ScoDoc9API.md) - [Modélisation du BUT](ModelisationParcoursBUT.md) - [Contacts](Contact.md)