Compare commits

..

No commits in common. "71e0a0270cfdb3590990bba5cdc3c06407768aae" and "5f44559b769cfa2415d7fea856487e1aa5786212" have entirely different histories.

2 changed files with 6 additions and 323 deletions

View File

@ -1,255 +0,0 @@
# Module Assiduité
!!! warning
Attention vous vous trouvez sur la documentation **_Développeur_** du module Assiduité.
La documentation utilisateur est [ici](Assiduites.md).
Le module Assiduité est le nouveau module introduit en 9.6 permettant la saisie précise de l'assiduité des étudiants et étudiantes.
Ce module a été développé par Matthias HARTMANN, pendant son alternance entre 2022 et 2024 (alternance financée par l'association ScoDoc) et remplace l'ancien module _Absence_.
## Représentation en BDD
Le module Assiduité introduit deux nouveaux objets à la base ScoDoc :
- `Assiduite` : l'objet représentant une assiduité (absent,retard,present)
- `Justificatif` : l'objet représentant un justificatif (permet de justifier ou non une assiduité / plusieurs assiduités)
La modélisation est gérée dans le fichier [app/models/assiduites.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/models/assiduites.py).
### Représentation de l'Assiduité
Le tableau si dessous reprend la représation des données d'une assiduité.
!!! warning
Dans ScoDoc, l'heure affichée doit être celle affichée sur la montre des étudiants.
Assurez vous que la **timezone** du serveur soit la bonne.
| champs | type | commentaire |
| ----------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| id / assiduite_id | Integer | L'identifiant de l'assiduité |
| date_debut | DateTimeTZ | La date (+heure) de début de l'assiduité. Doit avoir une TIMEZONE |
| date_fin | DateTimeTZ | La date (+heure) de fin de l'assiduité. Doit avoir une TIMEZONE |
| etudid | Integer | L'identifiant de l'étudiant concerné par l'assiduité **(clé externe)** |
| moduleimpl_id | Integer | L'identifiant du moduleimpl concerné par l'assiduité. ([plus d'infos](#moduleimpl)) **Peut être None** **(clé externe)** |
| etat | Integer | L'état de l'assiduité ([plus d'infos](#etats-dassiduite)) |
| description | Text | La description de l'assiduité **Peut être None** |
| entry_date | DateTimeTZ | La date (+heure) de saisie de l'assiduité. Doit avoir une TIMEZONE. Par défaut à la date actuelle |
| user_id | Integer | L'identifiant de l'utilisateur qui a saisie l'assiduité. **Peut être None** **(clé externe)** |
| est_just | Boolean | Indique si l'assiduité est justifiée |
| external_data | JSON | Champs de données JSON peu utilisées dans ScoDoc même mais facilite la vie aux utilisateurs de l'API **Peut être None** |
!!! tip
Il est conseillé d'utiliser les méthodes du modèle Assiduité afin de créer / éditer / supprimer une assiduité.
#### Etats d'assiduité
!!! info
Une classe EtatAssiduite simplifiant la traduction ETAT ↔ VALEUR est disponible dans [app/scodoc/sco_utils.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_utils.py)
| état | valeur |
| ------- | ------ |
| PRESENT | 0 |
| RETARD | 1 |
| ABSENT | 2 |
!!! warning
Seules les assiduités `RETARD` et `ABSENT` peuvent être justifiées.
#### Etudiants
Chaque assiduité est obligatoirement associée à un étudiant (Identite).
Il est possible de récupérer toutes les assiduités d'un étudiant en utilisant la relation à partir du modèle Identite.
```py
etudiant : Identite = Identite.get_etud(1234)
assiduites_etud : Query = etudiant.assiduites
# Identite.assiduites est une query et permet donc les filtrages etc.
# Première absence
prem_abs: Assiduite = assiduites_etud.filter(
Assiduite.etat == scu.EtatAssiduite.ABSENT
).first()
```
#### ModuleImpl
Une assiduité peut être reliée à un ModuleImpl à l'aide du `moduleimpl_id`.
Cependant suite à une demande des utilisateurs, il existe un **FAUX** module nommé `autre`. Ce module permet de contourner la sécurité de la préférence `forcer la saisie d'un module` dans le cas où la saisie d'assiduité n'est pas directement en lien avec un module enregistré dans ScoDoc.
!!! warning
Ce **FAUX** module n'est pas inscrit dans le `moduleimpl_id` mais dans `external_data`.
Des fonctions ont été rédigées pour faciliter la gestion de ce **FAUX** module. Elles sont toutes disponibles dans la classe de l'objet Assiduité. ([voir le code](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/models/assiduites.py))
- `get_module` : récupère (si existant) le module associé à l'assiduité
- `get_moduleimpl_id` : récupère (si existant) le moduleimpl_id associé à l'assiduité (comprend le module autre)
- `set_moduleimpl_id` : permet de mettre à jour le moduleimpl_id (prend en compte le module autre)
### Représentation du Justificatif
Le tableau si dessous reprend la représation des données d'un justificatif.
!!! warning
Dans ScoDoc, l'heure affichée doit être celle affichée sur la montre des étudiants.
Assurez vous que la **timezone** du serveur soit la bonne.
| champs | type | commentaire | |
| -------------- | ---------- | ---------------------------------------------------------------------------------------------------------------- | - |
| id / justif_id | Integer | L'identifiant du justificatif | |
| date_debut | DateTimeTZ | La date (+heure) de début du justificatif. Doit avoir une TIMEZONE | |
| date_fin | DateTimeTZ | La date (+heure) de fin du justificatif. Doit avoir une TIMEZONE | |
| etudid | Integer | L'identifiant de l'étudiant concerné par le justificatif **(clé externe)** | |
| etat | Integer | L'état du justificatif([plus d'infos](#etats-de-justificatif)) | |
| raison | Text | La raison du justificatif**Peut être None** | |
| entry_date | DateTimeTZ | La date (+heure) de saisie du justificatif. Doit avoir une TIMEZONE. Par défaut à la date actuelle | |
| user_id | Integer | L'identifiant de l'utilisateur qui a saisie le justificatif. **Peut être None** **(clé externe)** | |
| fichier | Text | l'identifiant de l'archive justificatif si existante ([plus d'infos](#gestion-des-archives-justificatives)) | |
| external_data | JSON | Champs de données JSON non utilisé dans ScoDoc mais facilite la vie aux utilisateurs de l'API **Peut être None** | |
!!! tip
Il est conseillé d'utiliser les méthodes du modèle Justificatif afin de créer / éditer / supprimer un justificatif.
#### Etats de Justificatif
!!! info
Une classe EtatJustificatif simplifiant la traduction ETAT ↔ VALEUR est disponible dans [app/scodoc/sco_utils.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_utils.py)
| état | valeur |
| ---------- | ------ |
| VALIDE | 0 |
| NON_VALIDE | 1 |
| ATTENTE | 2 |
| MODIFIE | 3 |
!!! warning
Seul l'état `VALIDE` permet de justifier des assiduités. Les autres états permettent d'organiser le travail administratif.
#### Etudiants
Chaque justificatif est obligatoirement associée à un étudiant (Identite).
Il est possible de récupérer toutes les justificatifs d'un étudiant en utilisant la relation à partir du modèle Identite.
```py
etudiant : Identite = Identite.get_etud(1234)
justificatifs_etud : Query = etudiant.justificatifs
# Identite.justificatifs est une query et permet donc les filtrages etc.
# Première justificatif valide
prem_abs: Justificatif = justificatifs_etud.filter(
Justificatif.etat == scu.EtatJustificatif.VALIDE
).first()
```
## Fonctionnements des Assiduités
### Cache
### Métriques
Les métriques d'assiduités permettent le suivi de l'assiduité d'un étudiant avec plusieurs niveaux de précisions.
Il existe 4 métriques :
- `compte` : un simple comptage du nombre d'assiduités sans autre calcul
- `journee` : le calcul du nombre de journées concernées par les assiduités (chaque journée n'est comptée qu'une fois)
- `demi` : le calcul du nombre de demi-journées concernées par les assiduités (chaque demi-journée n'est comptée qu'une fois)
- `heure` : le calcul du nombre d'heures concernées par les assiduités.
Ces calculs sont réalisés grâce à la classe `CountCalculator` trouvable dans [app/scodoc/sco_assiduites.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_assiduites.py).
Il n'est pas nécessaire de créer un objet CountCalculator à chaque fois que vous voulez un calcul. Vous pouvez utiliser directement la fonction `sco_assiduites.py#get_assiduites_stats`.
!!! warning
Les métriques sont utilisées dans les bulletins, sidebar et bilan.
Ces valeurs utilisent le [cache](#cache).
### Permissions
Les fonctions relatives aux assiduités utilisent les permissions suivantes :
- `AbsChange` : permet de créer / éditer / supprimer les assiduités
- `AbsJustifView` : permet de voir les informations (descriptions) des assiduités (données personnelles cachées par défaut)
## Fonctionnements des Justificatifs
### Permissions
Les fonctions relatives aux justificatifs utilisent les permissions suivantes :
- `AbsChange` : permet de créer / éditer / supprimer les justificatifs
- `AbsJustifView` : permet de voir les informations (raison, fichiers) des justificatifs (données personnelles cachées par défaut)
- `JustifValidate` : permet de changer l'état d'un justificatif de `Attente` à `Valide` / `Non Valide` / `Modifie`
### Gestion des archives justificatives
L'ensemble des fonctionnalités relatives aux archives justificatives (fichiers importés) est disponible dans le fichier [app/scodoc/sco_archives_justificatifs.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_archives_justificatifs.py)
!!! note
Le système global d'archive est disponible [ici](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_archives.py)
Les archives sont enregistrées dans le dossier `/opt/scodoc-data/archives/`.
Voici la représentation de l'arboressence des fichiers justificatifs:
```text
justificatif/
└── <dept_id>/
└── <etudid/oid>/
└── <archive_id>/
├── [_description.txt]
├── [<filename.ext>]
└── [_trace.csv]
```
Les archives sont organisées par département, puis par étudiant.
#### Archive
L'`archive_id` est une chaîne de caractère générée à l'importation d'un fichier. Il s'agit de la date courante au format `yyyy-mm-dd-hh-MM-ss`.
!!! info
Il existe une archive par justificatif (ayant au moins un fichier importé).
Une archive peut contenir plusieurs fichiers. Chaque fichier est renommé de façon à avoir un nom `serveur` (ascii sans espacement).
##### _description.txt
Le fichier `_description.txt` est généré par l'archiveur global mais n'est pas utilisé ici. (le fichier est vide)
##### _traces.csv
Le fichier `_trace.csv` est un fichier de log généré par l'archiveur. Il permet de tenir un registre des actions qui ont été effectuées sur l'archive. (Il est géré par sa propre classe `Trace` dans [app/scodoc/sco_archives_justificatifs.py](https://git.scodoc.org/ScoDoc/ScoDoc/src/branch/master/app/scodoc/sco_archives_justificatifs.py))
Son contenu est un CSV de la forme :
```csv
nom_fichier_srv,datetime_depot,datetime_suppr,user_id
```
- `nom_fichier_srv` : nom du fichier tel qu'il est enregistré sur le serveur
- `datetime_depot` : datetime au format iso indiquant la date de dépot du fichier
- `datetime_suppr` : datetime au format iso indiquant la date de suppression du fichier **(par défaut None)**
- `user_id` : identifiant de l'utilisateur ayant déposé le fichier.
!!! info
le champs `user_id` est utilisé par ScoDoc pour afficher au non le fichier aux utilisateurs.
Seuls l'utilisateur qui a importé le fichier et ceux ayant la permission `JustifView` peuvent voir le fichier.
#### Gestion des fichiers
La classe `JustificatifArchiver` possède plusieurs fonctions pour simplifier la gestion des fichiers.
- `save_justificatif` : enregistre un fichier pour un étudiant. Génère l'archive si elle n'existe pas. Génère le fichier trace s'il n'existe pas et log le fichier. Renvoie le nom de l'archive et le nom du fichier (traduit en nom serveur).
- `delete_justificatif` : supprime un fichier d'une archive d'un étudiant. (Ajoute à la trace l'action). Si on ne donne pas de nom de fichier, supprime l'archive (et donc tous ses fichiers).
- `list_justificatifs` : retourne la liste des fichiers avec l'user_id de l'importateur.
- `get_justificatif_file` : renvoie une réponse de téléchargement (pour les vues) du fichier demandé.
- `remove_dept_archive` : supprime toutes les archives d'un département, supprime aussi toutes les traces

View File

@ -16,22 +16,20 @@ Pour les nouveaux codes, respecter les principes suivants:
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
## 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
## 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)
## Annotations de type
On annote les paramètres et résultats des fonctions.
@ -42,9 +40,9 @@ def _upload_justificatif_files(
...
```
## Pages et templates
## Vues et templates
Les vues utilisent un template *Jinja2*, fichier avec extension j2.
Les vues utilisent un template *Jinja*, fichier avec extension j2.
On s'astreint, sauf cas particulier, à un nommage identique de la route, la vue et du template.
@ -55,70 +53,10 @@ 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 %}
<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 variable `content` est placé comme HTML
brut (*safe*) dans la partie contenu.
- `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=...
)
```
### 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
accès à la base se font à travers l'ORM *SQLAlchemy*. Mes modèles sont définis
dans `app/models`, sauf les comptes utilisateurs qui sont dans
`/app/auth/models.py`.