7.5 KiB
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 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.
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.
@bp.route("/un_exemple")
def un_exemple(...):
...
return render_template(".../un_exemple.j2")
Le template aura typiquement avec la structure suivante:
{% extends "sco_page.j2" %}
{% block app_content %}
<h1>Titre</h1>
{% endblock app_content %}
Pour les pages dans un semestre (avec barre menu etc.), on utilisera <h2>
au
lieu de <h1>
.
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 variablecontent
est placé comme HTML brut (safe) dans la partie contenu. Passersco=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 modulesco_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
etsco.etud
)
Appel d'un template page
Pour mémoire, l'appel d'un template dans une vue se fait ainsi:
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:
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
:
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:
{% extends "sco_page.j2" %}
{% block title %}
...titre...
{% endblock title %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/un_style_scodoc.css">
<style>
... styles locaux ...
</style>
{% endblock styles %}
{% block app_content %}
<div class="pageContent">
<h2>titre page</h2>
... contenu ...
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{scu.STATIC_DIR}}/js/un_script_scodoc.js"></script>
<script>
... script js local ...
</script>
{% 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 boitediv.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:
La vue doit récupérer la liste des groupes sélectionnés ainsi:
group_ids = request.args.getlist("group_ids")
ou s'il s'agit un POST
:
group_ids = request.form.getlist("group_ids")
On converti ensuite les arguments en entiers:
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
:
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:
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)