forked from ScoDoc/ScoDoc
Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into revamp
This commit is contained in:
commit
7ff0fd39fb
@ -19,7 +19,8 @@ import app.scodoc.sco_assiduites as scass
|
|||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.api import api_bp as bp
|
from app.api import api_bp as bp
|
||||||
from app.api import api_web_bp, get_model_api_object, tools
|
from app.api import api_web_bp, get_model_api_object, tools
|
||||||
from app.decorators import permission_required, scodoc
|
from app.api import api_permission_required as permission_required
|
||||||
|
from app.decorators import scodoc
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Assiduite,
|
Assiduite,
|
||||||
Evaluation,
|
Evaluation,
|
||||||
@ -47,6 +48,8 @@ def assiduite(assiduite_id: int = None):
|
|||||||
"""Retourne un objet assiduité à partir de son id
|
"""Retourne un objet assiduité à partir de son id
|
||||||
|
|
||||||
Exemple de résultat:
|
Exemple de résultat:
|
||||||
|
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"assiduite_id": 1,
|
"assiduite_id": 1,
|
||||||
"etudid": 2,
|
"etudid": 2,
|
||||||
@ -55,11 +58,17 @@ def assiduite(assiduite_id: int = None):
|
|||||||
"date_fin": "2022-10-31T10:00+01:00",
|
"date_fin": "2022-10-31T10:00+01:00",
|
||||||
"etat": "retard",
|
"etat": "retard",
|
||||||
"desc": "une description",
|
"desc": "une description",
|
||||||
"user_id: 1 or null,
|
"user_id": 1 or null,
|
||||||
"user_name" : login scodoc or null
|
"user_name" : login scodoc or null,
|
||||||
"user_nom_complet": "Marie Dupont"
|
"user_nom_complet": "Marie Dupont",
|
||||||
"est_just": False or True,
|
"est_just": False or True,
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduite/1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return get_model_api_object(Assiduite, assiduite_id, Identite)
|
return get_model_api_object(Assiduite, assiduite_id, Identite)
|
||||||
@ -77,15 +86,23 @@ def assiduite(assiduite_id: int = None):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def assiduite_justificatifs(assiduite_id: int = None, long: bool = False):
|
def assiduite_justificatifs(assiduite_id: int = None, long: bool = False):
|
||||||
"""Retourne la liste des justificatifs qui justifie cette assiduitée
|
"""Retourne la liste des justificatifs qui justifient cette assiduité.
|
||||||
|
|
||||||
Exemple de résultat:
|
Exemple de résultat:
|
||||||
|
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
3,
|
3,
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduite/1/justificatifs;
|
||||||
|
/assiduite/1/justificatifs/long;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return get_assiduites_justif(assiduite_id, long)
|
return get_assiduites_justif(assiduite_id, long)
|
||||||
@ -123,45 +140,7 @@ def assiduites_count(
|
|||||||
etudid: int = None, nip: str = None, ine: str = None, with_query: bool = False
|
etudid: int = None, nip: str = None, ine: str = None, with_query: bool = False
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Retourne le nombre d'assiduités d'un étudiant
|
Retourne le nombre d'assiduités d'un étudiant.
|
||||||
chemin : /assiduites/<int:etudid>/count
|
|
||||||
|
|
||||||
Un filtrage peut être donné avec une query
|
|
||||||
chemin : /assiduites/<int:etudid>/count/query?
|
|
||||||
|
|
||||||
Les différents filtres :
|
|
||||||
Type (type de comptage -> journee, demi, heure, nombre d'assiduite):
|
|
||||||
query?type=(journee, demi, heure) -> une seule valeur parmis les trois
|
|
||||||
ex: .../query?type=heure
|
|
||||||
Comportement par défaut : compte le nombre d'assiduité enregistrée
|
|
||||||
|
|
||||||
Etat (etat de l'étudiant -> absent, present ou retard):
|
|
||||||
query?etat=[- liste des états séparé par une virgule -]
|
|
||||||
ex: .../query?etat=present,retard
|
|
||||||
Date debut
|
|
||||||
(date de début de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de début est supérieur ou égale à la valeur donnée):
|
|
||||||
query?date_debut=[- date au format iso -]
|
|
||||||
ex: query?date_debut=2022-11-03T08:00+01:00
|
|
||||||
Date fin
|
|
||||||
(date de fin de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de fin est inférieure ou égale à la valeur donnée):
|
|
||||||
query?date_fin=[- date au format iso -]
|
|
||||||
ex: query?date_fin=2022-11-03T10:00+01:00
|
|
||||||
Moduleimpl_id (l'id du module concerné par l'assiduité):
|
|
||||||
query?moduleimpl_id=[- int ou vide -]
|
|
||||||
ex: query?moduleimpl_id=1234
|
|
||||||
query?moduleimpl_od=
|
|
||||||
Formsemstre_id (l'id du formsemestre concerné par l'assiduité)
|
|
||||||
query?formsemestre_id=[int]
|
|
||||||
ex query?formsemestre_id=3
|
|
||||||
user_id (l'id de l'auteur de l'assiduité)
|
|
||||||
query?user_id=[int]
|
|
||||||
ex query?user_id=3
|
|
||||||
est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard))
|
|
||||||
query?est_just=[bool]
|
|
||||||
query?est_just=f
|
|
||||||
query?est_just=t
|
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
@ -175,6 +154,25 @@ def assiduites_count(
|
|||||||
metric:<array[string]:metric>
|
metric:<array[string]:metric>
|
||||||
split:<bool:split>
|
split:<bool:split>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur de l'assiduité
|
||||||
|
est_just:si l'assiduité est justifiée (fait aussi filtre abs/retard)
|
||||||
|
moduleimpl_id:l'id du module concerné par l'assiduité
|
||||||
|
date_debut:date de début de l'assiduité (supérieur ou égal)
|
||||||
|
date_fin:date de fin de l'assiduité (inférieur ou égal)
|
||||||
|
etat:etat de l'étudiant → absent, present ou retard
|
||||||
|
formsemestre_id:l'identifiant du formsemestre concerné par l'assiduité
|
||||||
|
metric: la/les métriques de comptage (journee, demi, heure, compte)
|
||||||
|
split: divise le comptage par état
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/1/count;
|
||||||
|
/assiduites/1/count/query?etat=retard;
|
||||||
|
/assiduites/1/count/query?split;
|
||||||
|
/assiduites/1/count/query?etat=present,retard&metric=compte,heure;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération de l'étudiant
|
# Récupération de l'étudiant
|
||||||
@ -230,39 +228,6 @@ def assiduites_count(
|
|||||||
def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False):
|
def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False):
|
||||||
"""
|
"""
|
||||||
Retourne toutes les assiduités d'un étudiant
|
Retourne toutes les assiduités d'un étudiant
|
||||||
chemin : /assiduites/<int:etudid>
|
|
||||||
|
|
||||||
Un filtrage peut être donné avec une query
|
|
||||||
chemin : /assiduites/<int:etudid>/query?
|
|
||||||
|
|
||||||
Les différents filtres :
|
|
||||||
Etat (etat de l'étudiant -> absent, present ou retard):
|
|
||||||
query?etat=[- liste des états séparé par une virgule -]
|
|
||||||
ex: .../query?etat=present,retard
|
|
||||||
Date debut
|
|
||||||
(date de début de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de début est supérieur ou égale à la valeur donnée):
|
|
||||||
query?date_debut=[- date au format iso -]
|
|
||||||
ex: query?date_debut=2022-11-03T08:00+01:00
|
|
||||||
Date fin
|
|
||||||
(date de fin de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de fin est inférieure ou égale à la valeur donnée):
|
|
||||||
query?date_fin=[- date au format iso -]
|
|
||||||
ex: query?date_fin=2022-11-03T10:00+01:00
|
|
||||||
Moduleimpl_id (l'id du module concerné par l'assiduité):
|
|
||||||
query?moduleimpl_id=[- int ou vide -]
|
|
||||||
ex: query?moduleimpl_id=1234
|
|
||||||
query?moduleimpl_od=
|
|
||||||
Formsemstre_id (l'id du formsemestre concerné par l'assiduité)
|
|
||||||
query?formsemstre_id=[int]
|
|
||||||
ex query?formsemestre_id=3
|
|
||||||
user_id (l'id de l'auteur de l'assiduité)
|
|
||||||
query?user_id=[int]
|
|
||||||
ex query?user_id=3
|
|
||||||
est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard))
|
|
||||||
query?est_just=[bool]
|
|
||||||
query?est_just=f
|
|
||||||
query?est_just=t
|
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
@ -273,6 +238,25 @@ def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False)
|
|||||||
date_fin:<string:date_fin_iso>
|
date_fin:<string:date_fin_iso>
|
||||||
etat:<array[string]:etat>
|
etat:<array[string]:etat>
|
||||||
formsemestre_id:<int:formsemestre_id>
|
formsemestre_id:<int:formsemestre_id>
|
||||||
|
with_justifs:<bool:with_justifs>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur de l'assiduité
|
||||||
|
est_just:si l'assiduité est justifiée (fait aussi filtre abs/retard)
|
||||||
|
moduleimpl_id:l'id du module concerné par l'assiduité
|
||||||
|
date_debut:date de début de l'assiduité (supérieur ou égal)
|
||||||
|
date_fin:date de fin de l'assiduité (inférieur ou égal)
|
||||||
|
etat:etat de l'étudiant → absent, present ou retard
|
||||||
|
formsemestre_id:l'identifiant du formsemestre concerné par l'assiduité
|
||||||
|
with_justif:ajoute les justificatifs liés à l'assiduité
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/1;
|
||||||
|
/assiduites/1/query?etat=retard;
|
||||||
|
/assiduites/1/query?moduleimpl_id=1;
|
||||||
|
/assiduites/1/query?with_justifs=;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -328,7 +312,9 @@ def assiduites_evaluations(etudid: int = None, nip=None, ine=None):
|
|||||||
Pour chaque évaluation, retourne la liste des objets assiduités
|
Pour chaque évaluation, retourne la liste des objets assiduités
|
||||||
sur la plage de l'évaluation
|
sur la plage de l'évaluation
|
||||||
|
|
||||||
Présentation du retour :
|
Exemple de résultat:
|
||||||
|
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"evaluation_id": 1234,
|
"evaluation_id": 1234,
|
||||||
@ -341,6 +327,12 @@ def assiduites_evaluations(etudid: int = None, nip=None, ine=None):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/1/evaluations;
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
@ -367,7 +359,10 @@ def assiduites_evaluations(etudid: int = None, nip=None, ine=None):
|
|||||||
def evaluation_assiduites(evaluation_id):
|
def evaluation_assiduites(evaluation_id):
|
||||||
"""
|
"""
|
||||||
Retourne les objets assiduités de chaque étudiant sur la plage de l'évaluation
|
Retourne les objets assiduités de chaque étudiant sur la plage de l'évaluation
|
||||||
Présentation du retour :
|
|
||||||
|
Exemple de résultat:
|
||||||
|
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"<etudid>" : [
|
"<etudid>" : [
|
||||||
{
|
{
|
||||||
@ -376,6 +371,11 @@ def evaluation_assiduites(evaluation_id):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Évaluations
|
||||||
"""
|
"""
|
||||||
# Récupération de l'évaluation
|
# Récupération de l'évaluation
|
||||||
try:
|
try:
|
||||||
@ -404,37 +404,6 @@ def assiduites_group(with_query: bool = False):
|
|||||||
Retourne toutes les assiduités d'un groupe d'étudiants
|
Retourne toutes les assiduités d'un groupe d'étudiants
|
||||||
chemin : /assiduites/group/query?etudids=1,2,3
|
chemin : /assiduites/group/query?etudids=1,2,3
|
||||||
|
|
||||||
Un filtrage peut être donné avec une query
|
|
||||||
chemin : /assiduites/group/query?etudids=1,2,3
|
|
||||||
|
|
||||||
Les différents filtres :
|
|
||||||
Etat (etat de l'étudiant -> absent, present ou retard):
|
|
||||||
query?etat=[- liste des états séparé par une virgule -]
|
|
||||||
ex: .../query?etat=present,retard
|
|
||||||
Date debut
|
|
||||||
(date de début de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de début est supérieur ou égale à la valeur donnée):
|
|
||||||
query?date_debut=[- date au format iso -]
|
|
||||||
ex: query?date_debut=2022-11-03T08:00+01:00
|
|
||||||
Date fin
|
|
||||||
(date de fin de l'assiduité, sont affichés les assiduités
|
|
||||||
dont la date de fin est inférieure ou égale à la valeur donnée):
|
|
||||||
query?date_fin=[- date au format iso -]
|
|
||||||
ex: query?date_fin=2022-11-03T10:00+01:00
|
|
||||||
Moduleimpl_id (l'id du module concerné par l'assiduité):
|
|
||||||
query?moduleimpl_id=[- int ou vide -]
|
|
||||||
ex: query?moduleimpl_id=1234
|
|
||||||
query?moduleimpl_od=
|
|
||||||
Formsemstre_id (l'id du formsemestre concerné par l'assiduité)
|
|
||||||
query?formsemstre_id=[int]
|
|
||||||
ex query?formsemestre_id=3
|
|
||||||
user_id (l'id de l'auteur de l'assiduité)
|
|
||||||
query?user_id=[int]
|
|
||||||
ex query?user_id=3
|
|
||||||
est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard))
|
|
||||||
query?est_just=[bool]
|
|
||||||
query?est_just=f
|
|
||||||
query?est_just=t
|
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
@ -444,8 +413,25 @@ def assiduites_group(with_query: bool = False):
|
|||||||
date_debut:<string:date_debut_iso>
|
date_debut:<string:date_debut_iso>
|
||||||
date_fin:<string:date_fin_iso>
|
date_fin:<string:date_fin_iso>
|
||||||
etat:<array[string]:etat>
|
etat:<array[string]:etat>
|
||||||
etudids:<array[int]:etudids
|
etudids:<array[int]:etudids>
|
||||||
formsemestre_id:<int:formsemestre_id>
|
formsemestre_id:<int:formsemestre_id>
|
||||||
|
with_justif:<bool:with_justif>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur de l'assiduité
|
||||||
|
est_just:si l'assiduité est justifiée (fait aussi filtre abs/retard)
|
||||||
|
moduleimpl_id:l'id du module concerné par l'assiduité
|
||||||
|
date_debut:date de début de l'assiduité (supérieur ou égal)
|
||||||
|
date_fin:date de fin de l'assiduité (inférieur ou égal)
|
||||||
|
etat:etat de l'étudiant → absent, present ou retard
|
||||||
|
etudids:liste des ids des étudiants concernés par la recherche
|
||||||
|
formsemestre_id:l'identifiant du formsemestre concerné par l'assiduité
|
||||||
|
with_justifs:ajoute les justificatifs liés à l'assiduité
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/group/query?etudids=1,2,3;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -506,6 +492,7 @@ def assiduites_group(with_query: bool = False):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
||||||
"""Retourne toutes les assiduités du formsemestre
|
"""Retourne toutes les assiduités du formsemestre
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
user_id:<int:user_id>
|
user_id:<int:user_id>
|
||||||
@ -514,6 +501,23 @@ def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
|||||||
date_debut:<string:date_debut_iso>
|
date_debut:<string:date_debut_iso>
|
||||||
date_fin:<string:date_fin_iso>
|
date_fin:<string:date_fin_iso>
|
||||||
etat:<array[string]:etat>
|
etat:<array[string]:etat>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur de l'assiduité
|
||||||
|
est_just:si l'assiduité est justifiée (fait aussi filtre abs/retard)
|
||||||
|
moduleimpl_id:l'id du module concerné par l'assiduité
|
||||||
|
date_debut:date de début de l'assiduité (supérieur ou égal)
|
||||||
|
date_fin:date de fin de l'assiduité (inférieur ou égal)
|
||||||
|
etat:etat de l'étudiant → absent, present ou retard
|
||||||
|
formsemestre_id:l'identifiant du formsemestre concerné par l'assiduité
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/formsemestre/1;
|
||||||
|
/assiduites/formsemestre/1/query?etat=retard;
|
||||||
|
/assiduites/formsemestre/1/query?moduleimpl_id=1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du formsemestre à partir du formsemestre_id
|
# Récupération du formsemestre à partir du formsemestre_id
|
||||||
@ -577,6 +581,25 @@ def assiduites_formsemestre_count(
|
|||||||
formsemestre_id:<int:formsemestre_id>
|
formsemestre_id:<int:formsemestre_id>
|
||||||
metric:<array[string]:metric>
|
metric:<array[string]:metric>
|
||||||
split:<bool:split>
|
split:<bool:split>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur de l'assiduité
|
||||||
|
est_just:si l'assiduité est justifiée (fait aussi filtre abs/retard)
|
||||||
|
moduleimpl_id:l'id du module concerné par l'assiduité
|
||||||
|
date_debut:date de début de l'assiduité (supérieur ou égal)
|
||||||
|
date_fin:date de fin de l'assiduité (inférieur ou égal)
|
||||||
|
etat:etat de l'étudiant → absent, present ou retard
|
||||||
|
formsemestre_id:l'identifiant du formsemestre concerné par l'assiduité
|
||||||
|
metric: la/les métriques de comptage (journee, demi, heure, compte)
|
||||||
|
split: divise le comptage par état
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/formsemestre/1/count;
|
||||||
|
/assiduites/formsemestre/1/count/query?etat=retard;
|
||||||
|
/assiduites/formsemestre/1/count/query?etat=present,retard&metric=compte,heure;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du formsemestre à partir du formsemestre_id
|
# Récupération du formsemestre à partir du formsemestre_id
|
||||||
@ -628,7 +651,10 @@ def assiduites_formsemestre_count(
|
|||||||
def assiduite_create(etudid: int = None, nip=None, ine=None):
|
def assiduite_create(etudid: int = None, nip=None, ine=None):
|
||||||
"""
|
"""
|
||||||
Enregistrement d'assiduités pour un étudiant (etudid)
|
Enregistrement d'assiduités pour un étudiant (etudid)
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"date_debut": str,
|
"date_debut": str,
|
||||||
@ -644,6 +670,12 @@ def assiduite_create(etudid: int = None, nip=None, ine=None):
|
|||||||
}
|
}
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduite/1/create;[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]
|
||||||
|
/assiduite/1/create;[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
# Récupération de l'étudiant
|
||||||
@ -697,7 +729,10 @@ def assiduite_create(etudid: int = None, nip=None, ine=None):
|
|||||||
def assiduites_create():
|
def assiduites_create():
|
||||||
"""
|
"""
|
||||||
Création d'une assiduité ou plusieurs assiduites
|
Création d'une assiduité ou plusieurs assiduites
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"date_debut": str,
|
"date_debut": str,
|
||||||
@ -710,12 +745,17 @@ def assiduites_create():
|
|||||||
"date_fin": str,
|
"date_fin": str,
|
||||||
"etat": str,
|
"etat": str,
|
||||||
"etudid":int,
|
"etudid":int,
|
||||||
|
|
||||||
"moduleimpl_id": int,
|
"moduleimpl_id": int,
|
||||||
"desc":str,
|
"desc":str,
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/create;[{""etudid"":1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]
|
||||||
|
/assiduites/create;[{""etudid"":-1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -886,13 +926,18 @@ def assiduite_delete():
|
|||||||
"""
|
"""
|
||||||
Suppression d'une assiduité à partir de son id
|
Suppression d'une assiduité à partir de son id
|
||||||
|
|
||||||
Forme des données envoyées :
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
<assiduite_id:int>,
|
<assiduite_id:int>,
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduite/delete;[2,2,3]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupération des ids envoyés dans la liste
|
# Récupération des ids envoyés dans la liste
|
||||||
@ -967,13 +1012,24 @@ def _delete_one(assiduite_id: int) -> tuple[int, str]:
|
|||||||
def assiduite_edit(assiduite_id: int):
|
def assiduite_edit(assiduite_id: int):
|
||||||
"""
|
"""
|
||||||
Edition d'une assiduité à partir de son id
|
Edition d'une assiduité à partir de son id
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"etat"?: str,
|
"etat"?: str,
|
||||||
"moduleimpl_id"?: int
|
"moduleimpl_id"?: int
|
||||||
"desc"?: str
|
"desc"?: str
|
||||||
"est_just"?: bool
|
"est_just"?: bool
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduite/1/edit;{""etat"":""absent""}
|
||||||
|
/assiduite/1/edit;{""moduleimpl_id"":2}
|
||||||
|
/assiduite/1/edit;{""etat"": ""retard"",""moduleimpl_id"":3}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération de l'assiduité à modifier
|
# Récupération de l'assiduité à modifier
|
||||||
@ -1015,7 +1071,10 @@ def assiduite_edit(assiduite_id: int):
|
|||||||
def assiduites_edit():
|
def assiduites_edit():
|
||||||
"""
|
"""
|
||||||
Edition de plusieurs assiduités
|
Edition de plusieurs assiduités
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"assiduite_id" : int,
|
"assiduite_id" : int,
|
||||||
@ -1025,6 +1084,13 @@ def assiduites_edit():
|
|||||||
"est_just"?: bool
|
"est_just"?: bool
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/assiduites/edit;[{""etat"":""absent"",""assiduite_id"":1}]
|
||||||
|
/assiduites/edit;[{""moduleimpl_id"":2,""assiduite_id"":1}]
|
||||||
|
/assiduites/edit;[{""etat"": ""retard"",""moduleimpl_id"":3,""assiduite_id"":1}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
edit_list: list[object] = request.get_json(force=True)
|
edit_list: list[object] = request.get_json(force=True)
|
||||||
|
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
API : billets d'absences
|
API : billets d'absences
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Billets d'absence
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
@ -20,6 +25,7 @@ from app.models import BilletAbsence
|
|||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.scodoc import sco_abs_billets
|
from app.scodoc import sco_abs_billets
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/billets_absence/etudiant/<int:etudid>")
|
@bp.route("/billets_absence/etudiant/<int:etudid>")
|
||||||
@ -29,7 +35,7 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def billets_absence_etudiant(etudid: int):
|
def billets_absence_etudiant(etudid: int):
|
||||||
"""Liste des billets d'absence pour cet étudiant"""
|
"""Liste des billets d'absence pour cet étudiant."""
|
||||||
billets = sco_abs_billets.query_billets_etud(etudid)
|
billets = sco_abs_billets.query_billets_etud(etudid)
|
||||||
return [billet.to_dict() for billet in billets]
|
return [billet.to_dict() for billet in billets]
|
||||||
|
|
||||||
@ -41,13 +47,30 @@ def billets_absence_etudiant(etudid: int):
|
|||||||
@permission_required(Permission.AbsAddBillet)
|
@permission_required(Permission.AbsAddBillet)
|
||||||
@as_json
|
@as_json
|
||||||
def billets_absence_create():
|
def billets_absence_create():
|
||||||
"""Ajout d'un billet d'absence"""
|
"""Ajout d'un billet d'absence. Renvoie le billet créé en json.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"etudid" : int,
|
||||||
|
"abs_begin" : date_iso,
|
||||||
|
"abs_end" : date_iso,
|
||||||
|
"description" : string,
|
||||||
|
"justified" : bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/billets_absence/create;{""etudid"":""1"",""abs_begin"":""2023-10-27T10:00"",""abs_end"":""2023-10-28T10:00"",""description"":""grave malade"",""justified"":""1""}
|
||||||
|
"""
|
||||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
etudid = data.get("etudid")
|
etudid = data.get("etudid")
|
||||||
abs_begin = data.get("abs_begin")
|
abs_begin = data.get("abs_begin")
|
||||||
abs_end = data.get("abs_end")
|
abs_end = data.get("abs_end")
|
||||||
description = data.get("description", "")
|
description = data.get("description", "")
|
||||||
justified = data.get("justified", False)
|
justified = scu.to_bool(data.get("justified", False))
|
||||||
if None in (etudid, abs_begin, abs_end):
|
if None in (etudid, abs_begin, abs_end):
|
||||||
return json_error(
|
return json_error(
|
||||||
404, message="Paramètre manquant: etudid, abs_begin, abs_end requis"
|
404, message="Paramètre manquant: etudid, abs_begin, abs_end requis"
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/),
|
Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/),
|
||||||
mais évidemment pas sur l'API web (/ScoDoc/<dept>/api).
|
mais évidemment pas sur l'API web (/ScoDoc/<dept>/api).
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Département
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -27,24 +32,19 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
from app.scodoc.sco_utils import json_error
|
from app.scodoc.sco_utils import json_error
|
||||||
|
|
||||||
|
|
||||||
def get_departement(dept_ident: str) -> Departement:
|
|
||||||
"Le departement, par id ou acronyme. Erreur 404 si pas trouvé."
|
|
||||||
try:
|
|
||||||
dept_id = int(dept_ident)
|
|
||||||
except ValueError:
|
|
||||||
dept_id = None
|
|
||||||
if dept_id is None:
|
|
||||||
return Departement.query.filter_by(acronym=dept_ident).first_or_404()
|
|
||||||
return Departement.query.get_or_404(dept_id)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/departements")
|
@bp.route("/departements")
|
||||||
@login_required
|
@login_required
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def departements_list():
|
def departements_list():
|
||||||
"""Liste les départements"""
|
"""Liste tous les départements.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departements;
|
||||||
|
|
||||||
|
"""
|
||||||
return [dept.to_dict(with_dept_name=True) for dept in Departement.query]
|
return [dept.to_dict(with_dept_name=True) for dept in Departement.query]
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +54,13 @@ def departements_list():
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def departements_ids():
|
def departements_ids():
|
||||||
"""Liste des ids de départements"""
|
"""Liste des ids de tous les départements.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departements_ids;
|
||||||
|
|
||||||
|
"""
|
||||||
return [dept.id for dept in Departement.query]
|
return [dept.id for dept in Departement.query]
|
||||||
|
|
||||||
|
|
||||||
@ -67,15 +73,10 @@ def departement_by_acronym(acronym: str):
|
|||||||
"""
|
"""
|
||||||
Info sur un département. Accès par acronyme.
|
Info sur un département. Accès par acronyme.
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"id": 1,
|
/departement/TAPI;
|
||||||
"acronym": "TAPI",
|
|
||||||
"dept_name" : "TEST",
|
|
||||||
"description": null,
|
|
||||||
"visible": true,
|
|
||||||
"date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||||
return dept.to_dict(with_dept_name=True)
|
return dept.to_dict(with_dept_name=True)
|
||||||
@ -86,9 +87,14 @@ def departement_by_acronym(acronym: str):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def departement_by_id(dept_id: int):
|
def departement_get(dept_id: int):
|
||||||
"""
|
"""
|
||||||
Info sur un département. Accès par id.
|
Info sur un département. Accès par id.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/id/1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
dept = Departement.query.get_or_404(dept_id)
|
dept = Departement.query.get_or_404(dept_id)
|
||||||
return dept.to_dict()
|
return dept.to_dict()
|
||||||
@ -102,11 +108,19 @@ def departement_by_id(dept_id: int):
|
|||||||
def departement_create():
|
def departement_create():
|
||||||
"""
|
"""
|
||||||
Création d'un département.
|
Création d'un département.
|
||||||
The request content type should be "application/json":
|
Le content type doit être `application/json`.
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"acronym": str,
|
"acronym": str,
|
||||||
"visible": bool,
|
"visible": bool,
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/create;{""acronym"":""MYDEPT"",""visible"":""1""}
|
||||||
"""
|
"""
|
||||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
acronym = str(data.get("acronym", ""))
|
acronym = str(data.get("acronym", ""))
|
||||||
@ -130,8 +144,10 @@ def departement_create():
|
|||||||
@as_json
|
@as_json
|
||||||
def departement_edit(acronym):
|
def departement_edit(acronym):
|
||||||
"""
|
"""
|
||||||
Edition d'un département: seul visible peut être modifié
|
Édition d'un département: seul le champ `visible` peut être modifié.
|
||||||
The request content type should be "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
{
|
{
|
||||||
"visible": bool,
|
"visible": bool,
|
||||||
}
|
}
|
||||||
@ -155,7 +171,7 @@ def departement_edit(acronym):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
def departement_delete(acronym):
|
def departement_delete(acronym):
|
||||||
"""
|
"""
|
||||||
Suppression d'un département.
|
Suppression d'un département identifié par son acronyme.
|
||||||
"""
|
"""
|
||||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||||
acronym = dept.acronym
|
acronym = dept.acronym
|
||||||
@ -172,25 +188,16 @@ def departement_delete(acronym):
|
|||||||
@as_json
|
@as_json
|
||||||
def departement_etudiants(acronym: str):
|
def departement_etudiants(acronym: str):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des étudiants d'un département
|
Retourne la liste des étudiants d'un département.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
acronym : l'acronyme d'un département
|
acronym : l'acronyme d'un département
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
[
|
-------
|
||||||
{
|
/departement/TAPI/etudiants;
|
||||||
"civilite": "M",
|
|
||||||
"code_ine": "7899X61616",
|
|
||||||
"code_nip": "F6777H88",
|
|
||||||
"date_naissance": null,
|
|
||||||
"email": "toto@toto.fr",
|
|
||||||
"emailperso": null,
|
|
||||||
"etudid": 18,
|
|
||||||
"nom": "MOREL",
|
|
||||||
"prenom": "JACQUES"
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||||
return [etud.to_dict_short() for etud in dept.etudiants]
|
return [etud.to_dict_short() for etud in dept.etudiants]
|
||||||
@ -215,7 +222,13 @@ def departement_etudiants_by_id(dept_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def departement_formsemestres_ids(acronym: str):
|
def departement_formsemestres_ids(acronym: str):
|
||||||
"""liste des ids formsemestre du département"""
|
"""Liste des ids de tous les formsemestres du département.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/TAPI/formsemestres_ids;
|
||||||
|
|
||||||
|
"""
|
||||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||||
return [formsemestre.id for formsemestre in dept.formsemestres]
|
return [formsemestre.id for formsemestre in dept.formsemestres]
|
||||||
|
|
||||||
@ -226,7 +239,13 @@ def departement_formsemestres_ids(acronym: str):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def departement_formsemestres_ids_by_id(dept_id: int):
|
def departement_formsemestres_ids_by_id(dept_id: int):
|
||||||
"""liste des ids formsemestre du département"""
|
"""Liste des ids de tous les formsemestres du département.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/id/1/formsemestres_ids;
|
||||||
|
|
||||||
|
"""
|
||||||
dept = Departement.query.get_or_404(dept_id)
|
dept = Departement.query.get_or_404(dept_id)
|
||||||
return [formsemestre.id for formsemestre in dept.formsemestres]
|
return [formsemestre.id for formsemestre in dept.formsemestres]
|
||||||
|
|
||||||
@ -239,7 +258,7 @@ def departement_formsemestres_ids_by_id(dept_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def departement_formsemestres_courants(acronym: str = "", dept_id: int | None = None):
|
def departement_formsemestres_courants(acronym: str = "", dept_id: int | None = None):
|
||||||
"""
|
"""
|
||||||
Liste les semestres du département indiqué (par son acronyme ou son id)
|
Liste les formsemestres du département indiqué (par son acronyme ou son id)
|
||||||
contenant la date courante, ou à défaut celle indiquée en argument
|
contenant la date courante, ou à défaut celle indiquée en argument
|
||||||
(au format ISO).
|
(au format ISO).
|
||||||
|
|
||||||
@ -247,6 +266,9 @@ def departement_formsemestres_courants(acronym: str = "", dept_id: int | None =
|
|||||||
-----
|
-----
|
||||||
date_courante:<string:date_courante>
|
date_courante:<string:date_courante>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/id/1/formsemestres_courants?date_courante=2022-01-01
|
||||||
"""
|
"""
|
||||||
dept = (
|
dept = (
|
||||||
Departement.query.filter_by(acronym=acronym).first_or_404()
|
Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
API : accès aux étudiants
|
API : accès aux étudiants
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Étudiants
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
@ -38,9 +42,8 @@ from app.scodoc import sco_groups
|
|||||||
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc import sco_photos
|
||||||
from app.scodoc.sco_utils import json_error, suppress_accents
|
from app.scodoc.sco_utils import json_error, suppress_accents
|
||||||
|
|
||||||
import app.scodoc.sco_photos as sco_photos
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
# Un exemple:
|
# Un exemple:
|
||||||
@ -98,25 +101,16 @@ def etudiants_courants(long: bool = False):
|
|||||||
et les formsemestres contenant la date courante,
|
et les formsemestres contenant la date courante,
|
||||||
ou à défaut celle indiquée en argument (au format ISO).
|
ou à défaut celle indiquée en argument (au format ISO).
|
||||||
|
|
||||||
|
En format "long": voir l'exemple.
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
date_courante:<string:date_courante>
|
date_courante:<string:date_courante>
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
[
|
-------
|
||||||
{
|
/etudiants/courants?date_courante=2022-05-01;
|
||||||
"id": 1234,
|
/etudiants/courants/long?date_courante=2022-05-01;
|
||||||
"code_nip": "12345678",
|
|
||||||
"code_ine": null,
|
|
||||||
"nom": "JOHN",
|
|
||||||
"nom_usuel": None,
|
|
||||||
"prenom": "DEUF",
|
|
||||||
"civilite": "M",
|
|
||||||
}
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
En format "long": voir documentation.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
allowed_depts = current_user.get_depts_with_permission(Permission.ScoView)
|
allowed_depts = current_user.get_depts_with_permission(Permission.ScoView)
|
||||||
@ -160,10 +154,13 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
|
|||||||
"""
|
"""
|
||||||
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
|
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
etudid : l'etudid de l'étudiant
|
etudid : l'etudid de l'étudiant
|
||||||
nip : le code nip de l'étudiant
|
nip : le code nip de l'étudiant
|
||||||
ine : le code ine de l'étudiant
|
ine : le code ine de l'étudiant
|
||||||
|
|
||||||
|
`etudid` est unique dans la base (tous départements).
|
||||||
Les codes INE et NIP sont uniques au sein d'un département.
|
Les codes INE et NIP sont uniques au sein d'un département.
|
||||||
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
|
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
|
||||||
"""
|
"""
|
||||||
@ -197,6 +194,8 @@ def etudiant_get_photo_image(etudid: int = None, nip: str = None, ine: str = Non
|
|||||||
-----
|
-----
|
||||||
size:<string:size>
|
size:<string:size>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
etudid : l'etudid de l'étudiant
|
etudid : l'etudid de l'étudiant
|
||||||
nip : le code nip de l'étudiant
|
nip : le code nip de l'étudiant
|
||||||
ine : le code ine de l'étudiant
|
ine : le code ine de l'étudiant
|
||||||
@ -269,9 +268,12 @@ def etudiant_set_photo_image(etudid: int = None):
|
|||||||
@as_json
|
@as_json
|
||||||
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||||
"""
|
"""
|
||||||
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
|
Info sur le ou les étudiants correspondants.
|
||||||
toujours une liste.
|
|
||||||
|
Comme `/etudiant` mais renvoie toujours une liste.
|
||||||
|
|
||||||
Si non trouvé, liste vide, pas d'erreur.
|
Si non trouvé, liste vide, pas d'erreur.
|
||||||
|
|
||||||
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
|
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
|
||||||
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
|
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
|
||||||
"""
|
"""
|
||||||
@ -304,8 +306,9 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
||||||
"""Liste des étudiants dont le nom débute par start.
|
"""Liste des étudiants dont le nom débute par `start`.
|
||||||
Si start fait moins de min_len=3 caractères, liste vide.
|
|
||||||
|
Si `start` fait moins de `min_len=3` caractères, liste vide.
|
||||||
La casse et les accents sont ignorés.
|
La casse et les accents sont ignorés.
|
||||||
"""
|
"""
|
||||||
if len(start) < min_len:
|
if len(start) < min_len:
|
||||||
@ -340,13 +343,13 @@ def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
|||||||
@as_json
|
@as_json
|
||||||
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
||||||
"""
|
"""
|
||||||
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
|
Liste des formsemestres qu'un étudiant a suivi, triés par ordre chronologique.
|
||||||
Accès par etudid, nip ou ine.
|
Accès par etudid, nip ou ine.
|
||||||
|
|
||||||
Attention, si accès via NIP ou INE, les semestres peuvent être de départements
|
Attention, si accès via NIP ou INE, les formsemestres peuvent être de départements
|
||||||
différents (si l'étudiant a changé de département). L'id du département est `dept_id`.
|
différents (si l'étudiant a changé de département). L'id du département est `dept_id`.
|
||||||
|
|
||||||
Si accès par département, ne retourne que les formsemestre suivis dans le département.
|
Si accès par département, ne retourne que les formsemestres suivis dans le département.
|
||||||
"""
|
"""
|
||||||
if etudid is not None:
|
if etudid is not None:
|
||||||
q_etud = Identite.query.filter_by(id=etudid)
|
q_etud = Identite.query.filter_by(id=etudid)
|
||||||
@ -414,13 +417,18 @@ def bulletin(
|
|||||||
"""
|
"""
|
||||||
Retourne le bulletin d'un étudiant dans un formsemestre.
|
Retourne le bulletin d'un étudiant dans un formsemestre.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
formsemestre_id : l'id d'un formsemestre
|
formsemestre_id : l'id d'un formsemestre
|
||||||
code_type : "etudid", "nip" ou "ine"
|
code_type : "etudid", "nip" ou "ine"
|
||||||
code : valeur du code INE, NIP ou etudid, selon code_type.
|
code : valeur du code INE, NIP ou etudid, selon code_type.
|
||||||
version : type de bulletin (par défaut, "selectedevals"): short, long, selectedevals, butcourt
|
version : type de bulletin (par défaut, "selectedevals"): short, long, selectedevals, butcourt
|
||||||
pdf : si spécifié, bulletin au format PDF (et non JSON).
|
pdf : si spécifié, bulletin au format PDF (et non JSON).
|
||||||
|
|
||||||
Exemple de résultat : voir https://scodoc.org/ScoDoc9API/#bulletin
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/etudiant/etudid/1/formsemestre/1/bulletin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if version == "pdf":
|
if version == "pdf":
|
||||||
version = "long"
|
version = "long"
|
||||||
@ -474,34 +482,14 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
|||||||
"""
|
"""
|
||||||
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué
|
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
formsemestre_id : l'id d'un formsemestre
|
formsemestre_id : l'id d'un formsemestre
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid d'un étudiant
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
[
|
-------
|
||||||
{
|
/etudiant/etudid/1/formsemestre/1/groups
|
||||||
"partition_id": 1,
|
|
||||||
"id": 1,
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"partition_name": null,
|
|
||||||
"numero": 0,
|
|
||||||
"bul_show_rank": false,
|
|
||||||
"show_in_lists": true,
|
|
||||||
"group_id": 1,
|
|
||||||
"group_name": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partition_id": 2,
|
|
||||||
"id": 2,
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"partition_name": "TD",
|
|
||||||
"numero": 1,
|
|
||||||
"bul_show_rank": false,
|
|
||||||
"show_in_lists": true,
|
|
||||||
"group_id": 2,
|
|
||||||
"group_name": "A"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -529,9 +517,12 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
|||||||
@permission_required(Permission.EtudInscrit)
|
@permission_required(Permission.EtudInscrit)
|
||||||
@as_json
|
@as_json
|
||||||
def etudiant_create(force=False):
|
def etudiant_create(force=False):
|
||||||
"""Création d'un nouvel étudiant
|
"""Création d'un nouvel étudiant.
|
||||||
|
|
||||||
Si force, crée même si homonymie détectée.
|
Si force, crée même si homonymie détectée.
|
||||||
|
|
||||||
L'étudiant créé n'est pas inscrit à un semestre.
|
L'étudiant créé n'est pas inscrit à un semestre.
|
||||||
|
|
||||||
Champs requis: nom, prenom (sauf si config sans prénom), dept (string:acronyme)
|
Champs requis: nom, prenom (sauf si config sans prénom), dept (string:acronyme)
|
||||||
"""
|
"""
|
||||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
@ -599,7 +590,17 @@ def etudiant_edit(
|
|||||||
code_type: str = "etudid",
|
code_type: str = "etudid",
|
||||||
code: str = None,
|
code: str = None,
|
||||||
):
|
):
|
||||||
"""Edition des données étudiant (identité, admission, adresses)"""
|
"""Édition des données étudiant (identité, admission, adresses).
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
|
`code_type`: le type du code, `etudid`, `ine` ou `nip`.
|
||||||
|
`code`: la valeur du code
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/etudiant/ine/INE1/edit;{""prenom"":""Nouveau Prénom"", ""adresses"":[{""email"":""nouvelle@adresse.fr""}]}
|
||||||
|
"""
|
||||||
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
||||||
if not ok:
|
if not ok:
|
||||||
return etud # json error
|
return etud # json error
|
||||||
@ -638,7 +639,27 @@ def etudiant_annotation(
|
|||||||
code_type: str = "etudid",
|
code_type: str = "etudid",
|
||||||
code: str = None,
|
code: str = None,
|
||||||
):
|
):
|
||||||
"""Ajout d'une annotation sur un étudiant"""
|
"""Ajout d'une annotation sur un étudiant.
|
||||||
|
|
||||||
|
Renvoie l'annotation créée.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
|
`code_type`: le type du code, `etudid`, `ine` ou `nip`.
|
||||||
|
`code`: la valeur du code
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"comment" : string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/etudiant/etudid/1/annotation;{""comment"":""une annotation sur l'étudiant""}
|
||||||
|
"""
|
||||||
if not current_user.has_permission(Permission.ViewEtudData):
|
if not current_user.has_permission(Permission.ViewEtudData):
|
||||||
return json_error(403, "non autorisé (manque ViewEtudData)")
|
return json_error(403, "non autorisé (manque ViewEtudData)")
|
||||||
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
||||||
@ -675,7 +696,13 @@ def etudiant_annotation_delete(
|
|||||||
code_type: str = "etudid", code: str = None, annotation_id: int = None
|
code_type: str = "etudid", code: str = None, annotation_id: int = None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Suppression d'une annotation
|
Suppression d'une annotation. On spécifie l'étudiant et l'id de l'annotation.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
|
`code_type`: le type du code, `etudid`, `ine` ou `nip`.
|
||||||
|
`code`: la valeur du code
|
||||||
|
`annotation_id` : id de l'annotation
|
||||||
"""
|
"""
|
||||||
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
||||||
if not ok:
|
if not ok:
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : accès aux évaluations
|
ScoDoc 9 API : accès aux évaluations
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Évaluations
|
||||||
"""
|
"""
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_json import as_json
|
from flask_json import as_json
|
||||||
@ -32,11 +36,14 @@ import app.scodoc.sco_utils as scu
|
|||||||
def get_evaluation(evaluation_id: int):
|
def get_evaluation(evaluation_id: int):
|
||||||
"""Description d'une évaluation.
|
"""Description d'une évaluation.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
'coefficient': 1.0,
|
'coefficient': 1.0,
|
||||||
'date_debut': '2016-01-04T08:30:00',
|
'date_debut': '2016-01-04T08:30:00',
|
||||||
'date_fin': '2016-01-04T12:30:00',
|
'date_fin': '2016-01-04T12:30:00',
|
||||||
'description': 'TP NI9219 Température',
|
'description': 'TP Température',
|
||||||
'evaluation_type': 0,
|
'evaluation_type': 0,
|
||||||
'id': 15797,
|
'id': 15797,
|
||||||
'moduleimpl_id': 1234,
|
'moduleimpl_id': 1234,
|
||||||
@ -50,6 +57,7 @@ def get_evaluation(evaluation_id: int):
|
|||||||
'publish_incomplete': False,
|
'publish_incomplete': False,
|
||||||
'visibulletin': True
|
'visibulletin': True
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -70,11 +78,15 @@ def get_evaluation(evaluation_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def moduleimpl_evaluations(moduleimpl_id: int):
|
def moduleimpl_evaluations(moduleimpl_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des évaluations d'un moduleimpl
|
Retourne la liste des évaluations d'un moduleimpl.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
moduleimpl_id : l'id d'un moduleimpl
|
moduleimpl_id : l'id d'un moduleimpl
|
||||||
|
|
||||||
Exemple de résultat : voir /evaluation
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/moduleimpl/1/evaluations
|
||||||
"""
|
"""
|
||||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||||
return [evaluation.to_dict_api() for evaluation in modimpl.evaluations]
|
return [evaluation.to_dict_api() for evaluation in modimpl.evaluations]
|
||||||
@ -88,30 +100,15 @@ def moduleimpl_evaluations(moduleimpl_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def evaluation_notes(evaluation_id: int):
|
def evaluation_notes(evaluation_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des notes de l'évaluation
|
Retourne la liste des notes de l'évaluation.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
evaluation_id : l'id de l'évaluation
|
evaluation_id : l'id de l'évaluation
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"11": {
|
/evaluation/2/notes;
|
||||||
"etudid": 11,
|
|
||||||
"evaluation_id": 1,
|
|
||||||
"value": 15.0,
|
|
||||||
"comment": "",
|
|
||||||
"date": "Wed, 20 Apr 2022 06:49:05 GMT",
|
|
||||||
"uid": 2
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"etudid": 12,
|
|
||||||
"evaluation_id": 1,
|
|
||||||
"value": 12.0,
|
|
||||||
"comment": "",
|
|
||||||
"date": "Wed, 20 Apr 2022 06:49:06 GMT",
|
|
||||||
"uid": 2
|
|
||||||
},
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -145,17 +142,26 @@ def evaluation_notes(evaluation_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def evaluation_set_notes(evaluation_id: int): # evaluation-notes-set
|
def evaluation_set_notes(evaluation_id: int): # evaluation-notes-set
|
||||||
"""Écriture de notes dans une évaluation.
|
"""Écriture de notes dans une évaluation.
|
||||||
The request content type should be "application/json",
|
|
||||||
and contains:
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
'notes' : [ [etudid, value], ... ],
|
'notes' : [ [etudid, value], ... ],
|
||||||
'comment' : optional string
|
'comment' : optional string
|
||||||
}
|
}
|
||||||
Result:
|
```
|
||||||
- nb_changed: nombre de notes changées
|
|
||||||
- nb_suppress: nombre de notes effacées
|
Résultat:
|
||||||
|
|
||||||
|
- etudids_changed: étudiants dont la note est modifiée
|
||||||
- etudids_with_decision: liste des etudiants dont la note a changé
|
- etudids_with_decision: liste des etudiants dont la note a changé
|
||||||
alors qu'ils ont une décision de jury enregistrée.
|
alors qu'ils ont une décision de jury enregistrée.
|
||||||
|
- history_menu: un fragment de HTML expliquant l'historique de la note de chaque étudiant modifié.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/evaluation/1/notes/set;{""notes"": [[1, 17], [2, ""SUPR""]], ""comment"" : ""sample test""}
|
||||||
"""
|
"""
|
||||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -186,8 +192,9 @@ def evaluation_set_notes(evaluation_id: int): # evaluation-notes-set
|
|||||||
@as_json
|
@as_json
|
||||||
def evaluation_create(moduleimpl_id: int):
|
def evaluation_create(moduleimpl_id: int):
|
||||||
"""Création d'une évaluation.
|
"""Création d'une évaluation.
|
||||||
The request content type should be "application/json",
|
|
||||||
and contains:
|
DATA
|
||||||
|
----
|
||||||
{
|
{
|
||||||
"description" : str,
|
"description" : str,
|
||||||
"evaluation_type" : int, // {0,1,2} default 0 (normale)
|
"evaluation_type" : int, // {0,1,2} default 0 (normale)
|
||||||
@ -200,7 +207,13 @@ def evaluation_create(moduleimpl_id: int):
|
|||||||
"coefficient" : float, // si non spécifié, 1.0
|
"coefficient" : float, // si non spécifié, 1.0
|
||||||
"poids" : { ue_id : poids } // optionnel
|
"poids" : { ue_id : poids } // optionnel
|
||||||
}
|
}
|
||||||
Result: l'évaluation créée.
|
|
||||||
|
Résultat: l'évaluation créée.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/moduleimpl/1/evaluation/create;{""description"":""Exemple éval.""}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
||||||
if not moduleimpl.can_edit_evaluation(current_user):
|
if not moduleimpl.can_edit_evaluation(current_user):
|
||||||
@ -250,7 +263,7 @@ def evaluation_create(moduleimpl_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def evaluation_delete(evaluation_id: int):
|
def evaluation_delete(evaluation_id: int):
|
||||||
"""Suppression d'une évaluation.
|
"""Suppression d'une évaluation.
|
||||||
Efface aussi toutes ses notes
|
Efface aussi toutes ses notes.
|
||||||
"""
|
"""
|
||||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : accès aux formations
|
ScoDoc 9 API : accès aux formations
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Formations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import flash, g, request
|
from flask import flash, g, request
|
||||||
@ -38,7 +42,13 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
@as_json
|
@as_json
|
||||||
def formations():
|
def formations():
|
||||||
"""
|
"""
|
||||||
Retourne la liste de toutes les formations (tous départements)
|
Retourne la liste de toutes les formations (tous départements,
|
||||||
|
sauf si route départementale).
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formations;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = Formation.query
|
query = Formation.query
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -58,7 +68,12 @@ def formations_ids():
|
|||||||
Retourne la liste de toutes les id de formations
|
Retourne la liste de toutes les id de formations
|
||||||
(tous départements, ou du département indiqué dans la route)
|
(tous départements, ou du département indiqué dans la route)
|
||||||
|
|
||||||
Exemple de résultat : [ 17, 99, 32 ]
|
Exemple de résultat : `[ 17, 99, 32 ]`.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formations_ids;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = Formation.query
|
query = Formation.query
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -72,26 +87,14 @@ def formations_ids():
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formation_by_id(formation_id: int):
|
def formation_get(formation_id: int):
|
||||||
"""
|
"""
|
||||||
La formation d'id donné
|
La formation d'id donné.
|
||||||
|
|
||||||
formation_id : l'id d'une formation
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formation/1;
|
||||||
|
|
||||||
Exemple de résultat :
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"acronyme": "BUT R&T",
|
|
||||||
"titre_officiel": "Bachelor technologique réseaux et télécommunications",
|
|
||||||
"formation_code": "V1RET",
|
|
||||||
"code_specialite": null,
|
|
||||||
"dept_id": 1,
|
|
||||||
"titre": "BUT R&T",
|
|
||||||
"version": 1,
|
|
||||||
"type_parcours": 700,
|
|
||||||
"referentiel_competence_id": null,
|
|
||||||
"formation_id": 1
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
query = Formation.query.filter_by(id=formation_id)
|
query = Formation.query.filter_by(id=formation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -123,97 +126,14 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
|||||||
"""
|
"""
|
||||||
Retourne la formation, avec UE, matières, modules
|
Retourne la formation, avec UE, matières, modules
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
formation_id : l'id d'une formation
|
formation_id : l'id d'une formation
|
||||||
export_ids : True ou False, si l'on veut ou non exporter les ids
|
export_with_ids : si présent, exporte aussi les ids des objets ScoDoc de la formation.
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"id": 1,
|
/formation/1/export
|
||||||
"acronyme": "BUT R&T",
|
|
||||||
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
|
||||||
"formation_code": "V1RET",
|
|
||||||
"code_specialite": null,
|
|
||||||
"dept_id": 1,
|
|
||||||
"titre": "BUT R&T",
|
|
||||||
"version": 1,
|
|
||||||
"type_parcours": 700,
|
|
||||||
"referentiel_competence_id": null,
|
|
||||||
"formation_id": 1,
|
|
||||||
"ue": [
|
|
||||||
{
|
|
||||||
"acronyme": "RT1.1",
|
|
||||||
"numero": 1,
|
|
||||||
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
|
||||||
"type": 0,
|
|
||||||
"ue_code": "UCOD11",
|
|
||||||
"ects": 12.0,
|
|
||||||
"is_external": false,
|
|
||||||
"code_apogee": "",
|
|
||||||
"coefficient": 0.0,
|
|
||||||
"semestre_idx": 1,
|
|
||||||
"color": "#B80004",
|
|
||||||
"reference": 1,
|
|
||||||
"matiere": [
|
|
||||||
{
|
|
||||||
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
|
||||||
"numero": 1,
|
|
||||||
"module": [
|
|
||||||
{
|
|
||||||
"titre": "Initiation aux r\u00e9seaux informatiques",
|
|
||||||
"abbrev": "Init aux r\u00e9seaux informatiques",
|
|
||||||
"code": "R101",
|
|
||||||
"heures_cours": 0.0,
|
|
||||||
"heures_td": 0.0,
|
|
||||||
"heures_tp": 0.0,
|
|
||||||
"coefficient": 1.0,
|
|
||||||
"ects": "",
|
|
||||||
"semestre_id": 1,
|
|
||||||
"numero": 10,
|
|
||||||
"code_apogee": "",
|
|
||||||
"module_type": 2,
|
|
||||||
"coefficients": [
|
|
||||||
{
|
|
||||||
"ue_reference": "1",
|
|
||||||
"coef": "12.0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ue_reference": "2",
|
|
||||||
"coef": "4.0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ue_reference": "3",
|
|
||||||
"coef": "4.0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique...",
|
|
||||||
"abbrev": "Hygi\u00e8ne informatique",
|
|
||||||
"code": "SAE11",
|
|
||||||
"heures_cours": 0.0,
|
|
||||||
"heures_td": 0.0,
|
|
||||||
"heures_tp": 0.0,
|
|
||||||
"coefficient": 1.0,
|
|
||||||
"ects": "",
|
|
||||||
"semestre_id": 1,
|
|
||||||
"numero": 10,
|
|
||||||
"code_apogee": "",
|
|
||||||
"module_type": 3,
|
|
||||||
"coefficients": [
|
|
||||||
{
|
|
||||||
"ue_reference": "1",
|
|
||||||
"coef": "16.0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
query = Formation.query.filter_by(id=formation_id)
|
query = Formation.query.filter_by(id=formation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -236,11 +156,13 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
|||||||
@as_json
|
@as_json
|
||||||
def referentiel_competences(formation_id: int):
|
def referentiel_competences(formation_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne le référentiel de compétences
|
Retourne le référentiel de compétences de la formation
|
||||||
|
ou null si pas de référentiel associé.
|
||||||
|
|
||||||
formation_id : l'id d'une formation
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formation/1/referentiel_competences;
|
||||||
|
|
||||||
return null si pas de référentiel associé.
|
|
||||||
"""
|
"""
|
||||||
query = Formation.query.filter_by(id=formation_id)
|
query = Formation.query.filter_by(id=formation_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -259,8 +181,14 @@ def referentiel_competences(formation_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def ue_set_parcours(ue_id: int):
|
def ue_set_parcours(ue_id: int):
|
||||||
"""Associe UE et parcours BUT.
|
"""Associe UE et parcours BUT.
|
||||||
|
|
||||||
La liste des ids de parcours est passée en argument JSON.
|
La liste des ids de parcours est passée en argument JSON.
|
||||||
JSON arg: [parcour_id1, parcour_id2, ...]
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
[ parcour_id1, parcour_id2, ... ]
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
query = UniteEns.query.filter_by(id=ue_id)
|
query = UniteEns.query.filter_by(id=ue_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -293,7 +221,7 @@ def ue_set_parcours(ue_id: int):
|
|||||||
@permission_required(Permission.EditFormation)
|
@permission_required(Permission.EditFormation)
|
||||||
@as_json
|
@as_json
|
||||||
def ue_assoc_niveau(ue_id: int, niveau_id: int):
|
def ue_assoc_niveau(ue_id: int, niveau_id: int):
|
||||||
"""Associe l'UE au niveau de compétence"""
|
"""Associe l'UE au niveau de compétence."""
|
||||||
query = UniteEns.query.filter_by(id=ue_id)
|
query = UniteEns.query.filter_by(id=ue_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
@ -323,7 +251,7 @@ def ue_assoc_niveau(ue_id: int, niveau_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def ue_desassoc_niveau(ue_id: int):
|
def ue_desassoc_niveau(ue_id: int):
|
||||||
"""Désassocie cette UE de son niveau de compétence
|
"""Désassocie cette UE de son niveau de compétence
|
||||||
(si elle n'est pas associée, ne fait rien)
|
(si elle n'est pas associée, ne fait rien).
|
||||||
"""
|
"""
|
||||||
query = UniteEns.query.filter_by(id=ue_id)
|
query = UniteEns.query.filter_by(id=ue_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -345,7 +273,12 @@ def ue_desassoc_niveau(ue_id: int):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def get_ue(ue_id: int):
|
def get_ue(ue_id: int):
|
||||||
"""Renvoie l'UE"""
|
"""Renvoie l'UE.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formation/ue/1;
|
||||||
|
"""
|
||||||
query = UniteEns.query.filter_by(id=ue_id)
|
query = UniteEns.query.filter_by(id=ue_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
@ -359,7 +292,12 @@ def get_ue(ue_id: int):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def formation_module_get(module_id: int):
|
def formation_module_get(module_id: int):
|
||||||
"""Renvoie le module"""
|
"""Renvoie le module.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formation/module/1;
|
||||||
|
"""
|
||||||
query = Module.query.filter_by(id=module_id)
|
query = Module.query.filter_by(id=module_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
@ -390,15 +328,17 @@ def formation_module_get(module_id: int):
|
|||||||
@permission_required(Permission.EditFormation)
|
@permission_required(Permission.EditFormation)
|
||||||
def ue_set_code_apogee(ue_id: int | None = None, code_apogee: str = ""):
|
def ue_set_code_apogee(ue_id: int | None = None, code_apogee: str = ""):
|
||||||
"""Change le code Apogée de l'UE.
|
"""Change le code Apogée de l'UE.
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur formation verrouillée)
|
|
||||||
|
|
||||||
Si ue_id n'est pas spécifié, utilise l'argument oid du POST.
|
Ce changement peut être fait sur formation verrouillée.
|
||||||
Si code_apogee n'est pas spécifié ou vide,
|
|
||||||
utilise l'argument value du POST
|
|
||||||
|
|
||||||
Le retour est une chaîne (le code enregistré), pas json.
|
Si `ue_id` n'est pas spécifié, utilise l'argument oid du POST.
|
||||||
|
Si `code_apogee` n'est pas spécifié ou vide,
|
||||||
|
utilise l'argument value du POST.
|
||||||
|
|
||||||
|
Le retour est une chaîne (le code enregistré), pas du json.
|
||||||
"""
|
"""
|
||||||
if ue_id is None:
|
if ue_id is None:
|
||||||
ue_id = request.form.get("oid")
|
ue_id = request.form.get("oid")
|
||||||
@ -444,14 +384,16 @@ def ue_set_code_apogee(ue_id: int | None = None, code_apogee: str = ""):
|
|||||||
@permission_required(Permission.EditFormation)
|
@permission_required(Permission.EditFormation)
|
||||||
def ue_set_code_apogee_rcue(ue_id: int, code_apogee: str = ""):
|
def ue_set_code_apogee_rcue(ue_id: int, code_apogee: str = ""):
|
||||||
"""Change le code Apogée du RCUE de l'UE.
|
"""Change le code Apogée du RCUE de l'UE.
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur formation verrouillée)
|
|
||||||
|
Ce changement peut être fait sur formation verrouillée.
|
||||||
|
|
||||||
Si code_apogee n'est pas spécifié ou vide,
|
Si code_apogee n'est pas spécifié ou vide,
|
||||||
utilise l'argument value du POST (utilisé par jinplace.js)
|
utilise l'argument value du POST (utilisé par `jinplace.js`)
|
||||||
|
|
||||||
Le retour est une chaîne (le code enregistré), pas json.
|
Le retour est une chaîne (le code enregistré), pas du json.
|
||||||
"""
|
"""
|
||||||
if not code_apogee:
|
if not code_apogee:
|
||||||
code_apogee = request.form.get("value", "")
|
code_apogee = request.form.get("value", "")
|
||||||
@ -497,15 +439,17 @@ def formation_module_set_code_apogee(
|
|||||||
module_id: int | None = None, code_apogee: str = ""
|
module_id: int | None = None, code_apogee: str = ""
|
||||||
):
|
):
|
||||||
"""Change le code Apogée du module.
|
"""Change le code Apogée du module.
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur formation verrouillée)
|
|
||||||
|
|
||||||
Si module_id n'est pas spécifié, utilise l'argument oid du POST.
|
Ce changement peut être fait sur formation verrouillée.
|
||||||
Si code_apogee n'est pas spécifié ou vide,
|
|
||||||
|
Si `module_id` n'est pas spécifié, utilise l'argument `oid` du POST.
|
||||||
|
Si `code_apogee` n'est pas spécifié ou vide,
|
||||||
utilise l'argument value du POST (utilisé par jinplace.js)
|
utilise l'argument value du POST (utilisé par jinplace.js)
|
||||||
|
|
||||||
Le retour est une chaîne (le code enregistré), pas json.
|
Le retour est une chaîne (le code enregistré), pas du json.
|
||||||
"""
|
"""
|
||||||
if module_id is None:
|
if module_id is None:
|
||||||
module_id = request.form.get("oid")
|
module_id = request.form.get("oid")
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : accès aux formsemestres
|
ScoDoc 9 API : accès aux formsemestres
|
||||||
|
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
FormSemestre
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter, itemgetter
|
||||||
|
|
||||||
@ -48,43 +54,15 @@ from app.tables.recap import TableRecap, RowRecap
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_infos(formsemestre_id: int):
|
def formsemestre_get(formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
Information sur le formsemestre indiqué.
|
Information sur le formsemestre indiqué.
|
||||||
|
|
||||||
formsemestre_id : l'id du formsemestre
|
formsemestre_id : l'id du formsemestre
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"block_moyennes": false,
|
/formsemestre/1
|
||||||
"bul_bgcolor": "white",
|
|
||||||
"bul_hide_xml": false,
|
|
||||||
"date_debut_iso": "2021-09-01",
|
|
||||||
"date_debut": "01/09/2021",
|
|
||||||
"date_fin_iso": "2022-08-31",
|
|
||||||
"date_fin": "31/08/2022",
|
|
||||||
"dept_id": 1,
|
|
||||||
"elt_annee_apo": null,
|
|
||||||
"elt_passage_apo" : null,
|
|
||||||
"elt_sem_apo": null,
|
|
||||||
"ens_can_edit_eval": false,
|
|
||||||
"etat": true,
|
|
||||||
"formation_id": 1,
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"gestion_compensation": false,
|
|
||||||
"gestion_semestrielle": false,
|
|
||||||
"id": 1,
|
|
||||||
"modalite": "FI",
|
|
||||||
"resp_can_change_ens": true,
|
|
||||||
"resp_can_edit": false,
|
|
||||||
"responsables": [1, 99], // uids
|
|
||||||
"scodoc7_id": null,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"titre_formation" : "BUT GEA",
|
|
||||||
"titre_num": "BUT GEA semestre 1",
|
|
||||||
"titre": "BUT GEA",
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -101,9 +79,11 @@ def formsemestre_infos(formsemestre_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def formsemestres_query():
|
def formsemestres_query():
|
||||||
"""
|
"""
|
||||||
Retourne les formsemestres filtrés par
|
Retourne les formsemestres filtrés par étape Apogée ou année scolaire
|
||||||
étape Apogée ou année scolaire ou département (acronyme ou id) ou état ou code étudiant
|
ou département (acronyme ou id) ou état ou code étudiant.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
etape_apo : un code étape apogée
|
etape_apo : un code étape apogée
|
||||||
annee_scolaire : année de début de l'année scolaire
|
annee_scolaire : année de début de l'année scolaire
|
||||||
dept_acronym : acronyme du département (eg "RT")
|
dept_acronym : acronyme du département (eg "RT")
|
||||||
@ -190,7 +170,36 @@ def formsemestres_query():
|
|||||||
@permission_required(Permission.EditFormSemestre)
|
@permission_required(Permission.EditFormSemestre)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_edit(formsemestre_id: int):
|
def formsemestre_edit(formsemestre_id: int):
|
||||||
"""Modifie les champs d'un formsemestre."""
|
"""Modifie les champs d'un formsemestre.
|
||||||
|
|
||||||
|
On peut spécifier un ou plusieurs champs.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
---
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"semestre_id" : string,
|
||||||
|
"titre" : string,
|
||||||
|
"date_debut" : date iso,
|
||||||
|
"date_fin" : date iso,
|
||||||
|
"edt_id" : string,
|
||||||
|
"etat" : string,
|
||||||
|
"modalite" : string,
|
||||||
|
"gestion_compensation" : bool,
|
||||||
|
"bul_hide_xml" : bool,
|
||||||
|
"block_moyennes" : bool,
|
||||||
|
"block_moyenne_generale" : bool,
|
||||||
|
"mode_calcul_moyennes" : string,
|
||||||
|
"gestion_semestrielle" : string,
|
||||||
|
"bul_bgcolor" : string,
|
||||||
|
"resp_can_edit" : bool,
|
||||||
|
"resp_can_change_ens" : bool,
|
||||||
|
"ens_can_edit_eval" : bool,
|
||||||
|
"elt_sem_apo" : string,
|
||||||
|
"elt_annee_apo : string,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
editable_keys = {
|
editable_keys = {
|
||||||
@ -228,13 +237,19 @@ def formsemestre_edit(formsemestre_id: int):
|
|||||||
@permission_required(Permission.EditApogee)
|
@permission_required(Permission.EditApogee)
|
||||||
def formsemestre_set_apo_etapes():
|
def formsemestre_set_apo_etapes():
|
||||||
"""Change les codes étapes du semestre indiqué.
|
"""Change les codes étapes du semestre indiqué.
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur un semestre verrouillé)
|
|
||||||
|
|
||||||
Args:
|
Ce changement peut être fait sur un semestre verrouillé.
|
||||||
oid=int, le formsemestre_id
|
|
||||||
value=chaine "V1RT, V1RT2", codes séparés par des virgules
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
oid : int, le formsemestre_id
|
||||||
|
value : string, eg "V1RT, V1RT2", codes séparés par des virgules
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
formsemestre_id = int(request.form.get("oid"))
|
formsemestre_id = int(request.form.get("oid"))
|
||||||
etapes_apo_str = request.form.get("value")
|
etapes_apo_str = request.form.get("value")
|
||||||
@ -265,13 +280,20 @@ def formsemestre_set_apo_etapes():
|
|||||||
@permission_required(Permission.EditApogee)
|
@permission_required(Permission.EditApogee)
|
||||||
def formsemestre_set_elt_sem_apo():
|
def formsemestre_set_elt_sem_apo():
|
||||||
"""Change les codes étapes du semestre indiqué.
|
"""Change les codes étapes du semestre indiqué.
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur un semestre verrouillé)
|
|
||||||
|
|
||||||
Args:
|
Ce changement peut être fait sur un semestre verrouillé.
|
||||||
oid=int, le formsemestre_id
|
|
||||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
oid : int, le formsemestre_id
|
||||||
|
value : string, eg "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||||
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
oid = int(request.form.get("oid"))
|
oid = int(request.form.get("oid"))
|
||||||
value = (request.form.get("value") or "").strip()
|
value = (request.form.get("value") or "").strip()
|
||||||
@ -293,13 +315,20 @@ def formsemestre_set_elt_sem_apo():
|
|||||||
@permission_required(Permission.EditApogee)
|
@permission_required(Permission.EditApogee)
|
||||||
def formsemestre_set_elt_annee_apo():
|
def formsemestre_set_elt_annee_apo():
|
||||||
"""Change les codes étapes du semestre indiqué (par le champ oid).
|
"""Change les codes étapes du semestre indiqué (par le champ oid).
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur un semestre verrouillé)
|
|
||||||
|
|
||||||
Args:
|
Ce changement peut être fait sur un semestre verrouillé.
|
||||||
oid=int, le formsemestre_id
|
|
||||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
oid : int, le formsemestre_id
|
||||||
|
value : string, eg "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||||
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
oid = int(request.form.get("oid"))
|
oid = int(request.form.get("oid"))
|
||||||
value = (request.form.get("value") or "").strip()
|
value = (request.form.get("value") or "").strip()
|
||||||
@ -320,14 +349,21 @@ def formsemestre_set_elt_annee_apo():
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.EditApogee)
|
@permission_required(Permission.EditApogee)
|
||||||
def formsemestre_set_elt_passage_apo():
|
def formsemestre_set_elt_passage_apo():
|
||||||
"""Change les codes apogée de passage du semestre indiqué (par le champ oid).
|
"""Change les codes Apogée de passage du semestre indiqué (par le champ oid).
|
||||||
|
|
||||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||||
par des virgules.
|
par des virgules.
|
||||||
(Ce changement peut être fait sur un semestre verrouillé)
|
|
||||||
|
|
||||||
Args:
|
Ce changement peut être fait sur un semestre verrouillé.
|
||||||
oid=int, le formsemestre_id
|
|
||||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
oid : int, le formsemestre_id
|
||||||
|
value : string, eg "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||||
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
oid = int(request.form.get("oid"))
|
oid = int(request.form.get("oid"))
|
||||||
value = (request.form.get("value") or "").strip()
|
value = (request.form.get("value") or "").strip()
|
||||||
@ -353,11 +389,16 @@ def formsemestre_set_elt_passage_apo():
|
|||||||
@as_json
|
@as_json
|
||||||
def bulletins(formsemestre_id: int, version: str = "long"):
|
def bulletins(formsemestre_id: int, version: str = "long"):
|
||||||
"""
|
"""
|
||||||
Retourne les bulletins d'un formsemestre donné
|
Retourne les bulletins d'un formsemestre.
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formesemestre
|
PARAMS
|
||||||
|
------
|
||||||
|
formsemestre_id : int
|
||||||
|
version : string ("long", "short", "selectedevals")
|
||||||
|
|
||||||
Exemple de résultat : liste, voir https://scodoc.org/ScoDoc9API/#bulletin
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/bulletins
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -387,66 +428,9 @@ def formsemestre_programme(formsemestre_id: int):
|
|||||||
"""
|
"""
|
||||||
Retourne la liste des UEs, ressources et SAEs d'un semestre
|
Retourne la liste des UEs, ressources et SAEs d'un semestre
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
SAMPLES
|
||||||
|
-------
|
||||||
Exemple de résultat :
|
/formsemestre/1/programme
|
||||||
{
|
|
||||||
"ues": [
|
|
||||||
{
|
|
||||||
"type": 0,
|
|
||||||
"formation_id": 1,
|
|
||||||
"ue_code": "UCOD11",
|
|
||||||
"id": 1,
|
|
||||||
"ects": 12.0,
|
|
||||||
"acronyme": "RT1.1",
|
|
||||||
"is_external": false,
|
|
||||||
"numero": 1,
|
|
||||||
"code_apogee": "",
|
|
||||||
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
|
||||||
"coefficient": 0.0,
|
|
||||||
"semestre_idx": 1,
|
|
||||||
"color": "#B80004",
|
|
||||||
"ue_id": 1
|
|
||||||
},
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"ressources": [
|
|
||||||
{
|
|
||||||
"ens": [ 10, 18 ],
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"id": 15,
|
|
||||||
"module": {
|
|
||||||
"abbrev": "Programmer",
|
|
||||||
"code": "SAE15",
|
|
||||||
"code_apogee": "V7GOP",
|
|
||||||
"coefficient": 1.0,
|
|
||||||
"formation_id": 1,
|
|
||||||
"heures_cours": 0.0,
|
|
||||||
"heures_td": 0.0,
|
|
||||||
"heures_tp": 0.0,
|
|
||||||
"id": 15,
|
|
||||||
"matiere_id": 3,
|
|
||||||
"module_id": 15,
|
|
||||||
"module_type": 3,
|
|
||||||
"numero": 50,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"titre": "Programmer en Python",
|
|
||||||
"ue_id": 3
|
|
||||||
},
|
|
||||||
"module_id": 15,
|
|
||||||
"moduleimpl_id": 15,
|
|
||||||
"responsable_id": 2
|
|
||||||
},
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"saes": [
|
|
||||||
{
|
|
||||||
...
|
|
||||||
},
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ]
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -519,6 +503,10 @@ def formsemestre_etudiants(
|
|||||||
-----
|
-----
|
||||||
etat:<string:etat>
|
etat:<string:etat>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/etudiants/query;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -565,36 +553,9 @@ def formsemestre_etat_evaluations(formsemestre_id: int):
|
|||||||
"""
|
"""
|
||||||
Informations sur l'état des évaluations d'un formsemestre.
|
Informations sur l'état des évaluations d'un formsemestre.
|
||||||
|
|
||||||
formsemestre_id : l'id d'un semestre
|
SAMPLES
|
||||||
|
-------
|
||||||
Exemple de résultat :
|
/formsemestre/1/etat_evals
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 1, // moduleimpl_id
|
|
||||||
"titre": "Initiation aux réseaux informatiques",
|
|
||||||
"evaluations": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"description": null,
|
|
||||||
"datetime_epreuve": null,
|
|
||||||
"heure_fin": "09:00:00",
|
|
||||||
"coefficient": "02.00"
|
|
||||||
"is_complete": true,
|
|
||||||
"nb_inscrits": 16,
|
|
||||||
"nb_manquantes": 0,
|
|
||||||
"ABS": 0,
|
|
||||||
"ATT": 0,
|
|
||||||
"EXC": 0,
|
|
||||||
"saisie_notes": {
|
|
||||||
"datetime_debut": "2021-09-11T00:00:00+02:00",
|
|
||||||
"datetime_fin": "2022-08-25T00:00:00+02:00",
|
|
||||||
"datetime_mediane": "2022-03-19T00:00:00+01:00"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
app.set_sco_dept(formsemestre.departement.acronym)
|
app.set_sco_dept(formsemestre.departement.acronym)
|
||||||
@ -669,7 +630,8 @@ def formsemestre_etat_evaluations(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_resultat(formsemestre_id: int):
|
def formsemestre_resultat(formsemestre_id: int):
|
||||||
"""Tableau récapitulatif des résultats
|
"""Tableau récapitulatif des résultats.
|
||||||
|
|
||||||
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules.
|
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules.
|
||||||
|
|
||||||
Si `format=raw`, ne converti pas les valeurs.
|
Si `format=raw`, ne converti pas les valeurs.
|
||||||
@ -678,6 +640,9 @@ def formsemestre_resultat(formsemestre_id: int):
|
|||||||
-----
|
-----
|
||||||
format:<string:format>
|
format:<string:format>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/resultats;
|
||||||
"""
|
"""
|
||||||
format_spec = request.args.get("format", None)
|
format_spec = request.args.get("format", None)
|
||||||
if format_spec is not None and format_spec != "raw":
|
if format_spec is not None and format_spec != "raw":
|
||||||
@ -724,7 +689,7 @@ def formsemestre_resultat(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def groups_get_auto_assignment(formsemestre_id: int):
|
def groups_get_auto_assignment(formsemestre_id: int):
|
||||||
"""rend les données stockées par"""
|
"""Rend les données stockées par `groups_save_auto_assignment`."""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
@ -745,12 +710,17 @@ def groups_get_auto_assignment(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def groups_save_auto_assignment(formsemestre_id: int):
|
def groups_save_auto_assignment(formsemestre_id: int):
|
||||||
"""enregistre les données"""
|
"""Enregistre les données, associées à ce formsemestre.
|
||||||
|
Usage réservé aux fonctions de gestion des groupes, ne pas utiliser ailleurs.
|
||||||
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
|
formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
|
||||||
|
|
||||||
|
if not formsemestre.can_change_groups():
|
||||||
|
return json_error(403, "non autorisé (can_change_groups)")
|
||||||
|
|
||||||
if len(request.data) > GROUPS_AUTO_ASSIGNMENT_DATA_MAX:
|
if len(request.data) > GROUPS_AUTO_ASSIGNMENT_DATA_MAX:
|
||||||
return json_error(413, "data too large")
|
return json_error(413, "data too large")
|
||||||
formsemestre.groups_auto_assignment_data = request.data
|
formsemestre.groups_auto_assignment_data = request.data
|
||||||
@ -765,17 +735,16 @@ def groups_save_auto_assignment(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_edt(formsemestre_id: int):
|
def formsemestre_edt(formsemestre_id: int):
|
||||||
"""l'emploi du temps du semestre.
|
"""L'emploi du temps du semestre.
|
||||||
|
|
||||||
Si ok, une liste d'évènements. Sinon, une chaine indiquant un message d'erreur.
|
Si ok, une liste d'évènements. Sinon, une chaine indiquant un message d'erreur.
|
||||||
|
|
||||||
group_ids permet de filtrer sur les groupes ScoDoc.
|
Expérimental, ne pas utiliser hors ScoDoc.
|
||||||
show_modules_titles affiche le titre complet du module (défaut), sinon juste le code.
|
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
group_ids:<string:group_ids>
|
group_ids : string (optionnel) filtre sur les groupes ScoDoc.
|
||||||
show_modules_titles:<bool:show_modules_titles>
|
show_modules_titles: show_modules_titles affiche le titre complet du module (défaut), sinon juste le code.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
|
@ -5,7 +5,12 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : jury WIP à compléter avec enregistrement décisions
|
ScoDoc 9 API : jury WIP à compléter avec enregistrement décisions.
|
||||||
|
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Jury
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
@ -48,7 +53,12 @@ from app.scodoc.sco_utils import json_error
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def decisions_jury(formsemestre_id: int):
|
def decisions_jury(formsemestre_id: int):
|
||||||
"""Décisions du jury des étudiants du formsemestre."""
|
"""Décisions du jury des étudiants du formsemestre.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/decisions_jury
|
||||||
|
"""
|
||||||
# APC, pair:
|
# APC, pair:
|
||||||
formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
|
formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
|
||||||
if formsemestre is None:
|
if formsemestre is None:
|
||||||
@ -91,7 +101,7 @@ def _news_delete_jury_etud(etud: Identite, detail: str = ""):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def validation_ue_delete(etudid: int, validation_id: int):
|
def validation_ue_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette validation d'UE."
|
||||||
return _validation_ue_delete(etudid, validation_id)
|
return _validation_ue_delete(etudid, validation_id)
|
||||||
|
|
||||||
|
|
||||||
@ -108,7 +118,7 @@ def validation_ue_delete(etudid: int, validation_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def validation_formsemestre_delete(etudid: int, validation_id: int):
|
def validation_formsemestre_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette validation de semestre."
|
||||||
# c'est la même chose (formations classiques)
|
# c'est la même chose (formations classiques)
|
||||||
return _validation_ue_delete(etudid, validation_id)
|
return _validation_ue_delete(etudid, validation_id)
|
||||||
|
|
||||||
@ -160,7 +170,7 @@ def _validation_ue_delete(etudid: int, validation_id: int):
|
|||||||
@permission_required(Permission.EtudInscrit)
|
@permission_required(Permission.EtudInscrit)
|
||||||
@as_json
|
@as_json
|
||||||
def autorisation_inscription_delete(etudid: int, validation_id: int):
|
def autorisation_inscription_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette autorisation d'inscription."
|
||||||
etud = tools.get_etud(etudid)
|
etud = tools.get_etud(etudid)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return "étudiant inconnu", 404
|
return "étudiant inconnu", 404
|
||||||
@ -189,8 +199,12 @@ def autorisation_inscription_delete(etudid: int, validation_id: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def validation_rcue_record(etudid: int):
|
def validation_rcue_record(etudid: int):
|
||||||
"""Enregistre une validation de RCUE.
|
"""Enregistre une validation de RCUE.
|
||||||
|
|
||||||
Si une validation existe déjà pour ce RCUE, la remplace.
|
Si une validation existe déjà pour ce RCUE, la remplace.
|
||||||
The request content type should be "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"code" : str,
|
"code" : str,
|
||||||
"ue1_id" : int,
|
"ue1_id" : int,
|
||||||
@ -200,6 +214,7 @@ def validation_rcue_record(etudid: int):
|
|||||||
"date" : date_iso, // si non spécifié, now()
|
"date" : date_iso, // si non spécifié, now()
|
||||||
"parcours_id" :int,
|
"parcours_id" :int,
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
etud = tools.get_etud(etudid)
|
etud = tools.get_etud(etudid)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
@ -314,7 +329,7 @@ def validation_rcue_record(etudid: int):
|
|||||||
@permission_required(Permission.EtudInscrit)
|
@permission_required(Permission.EtudInscrit)
|
||||||
@as_json
|
@as_json
|
||||||
def validation_rcue_delete(etudid: int, validation_id: int):
|
def validation_rcue_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette validation de RCUE."
|
||||||
etud = tools.get_etud(etudid)
|
etud = tools.get_etud(etudid)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return "étudiant inconnu", 404
|
return "étudiant inconnu", 404
|
||||||
@ -342,7 +357,7 @@ def validation_rcue_delete(etudid: int, validation_id: int):
|
|||||||
@permission_required(Permission.EtudInscrit)
|
@permission_required(Permission.EtudInscrit)
|
||||||
@as_json
|
@as_json
|
||||||
def validation_annee_but_delete(etudid: int, validation_id: int):
|
def validation_annee_but_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette validation d'année BUT."
|
||||||
etud = tools.get_etud(etudid)
|
etud = tools.get_etud(etudid)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return "étudiant inconnu", 404
|
return "étudiant inconnu", 404
|
||||||
@ -371,7 +386,7 @@ def validation_annee_but_delete(etudid: int, validation_id: int):
|
|||||||
@permission_required(Permission.EtudInscrit)
|
@permission_required(Permission.EtudInscrit)
|
||||||
@as_json
|
@as_json
|
||||||
def validation_dut120_delete(etudid: int, validation_id: int):
|
def validation_dut120_delete(etudid: int, validation_id: int):
|
||||||
"Efface cette validation"
|
"Efface cette validation de DUT120."
|
||||||
etud = tools.get_etud(etudid)
|
etud = tools.get_etud(etudid)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return "étudiant inconnu", 404
|
return "étudiant inconnu", 404
|
||||||
|
@ -38,9 +38,11 @@ from app.scodoc.sco_groups import get_group_members
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatif(justif_id: int = None):
|
def justificatif(justif_id: int = None):
|
||||||
"""Retourne un objet justificatif à partir de son id
|
"""Retourne un objet justificatif à partir de son id.
|
||||||
|
|
||||||
Exemple de résultat:
|
Exemple de résultat:
|
||||||
|
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"justif_id": 1,
|
"justif_id": 1,
|
||||||
"etudid": 2,
|
"etudid": 2,
|
||||||
@ -52,6 +54,11 @@ def justificatif(justif_id: int = None):
|
|||||||
"entry_date": "2022-10-31T08:00+01:00",
|
"entry_date": "2022-10-31T08:00+01:00",
|
||||||
"user_id": 1 or null,
|
"user_id": 1 or null,
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -92,38 +99,32 @@ def justificatif(justif_id: int = None):
|
|||||||
def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = False):
|
def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = False):
|
||||||
"""
|
"""
|
||||||
Retourne toutes les assiduités d'un étudiant
|
Retourne toutes les assiduités d'un étudiant
|
||||||
chemin : /justificatifs/<int:etudid>
|
|
||||||
|
|
||||||
Un filtrage peut être donné avec une query
|
|
||||||
chemin : /justificatifs/<int:etudid>/query?
|
|
||||||
|
|
||||||
Les différents filtres :
|
|
||||||
Etat (etat du justificatif -> validé, non validé, modifé, en attente):
|
|
||||||
query?etat=[- liste des états séparé par une virgule -]
|
|
||||||
ex: .../query?etat=validé,modifié
|
|
||||||
Date debut
|
|
||||||
(date de début du justificatif, sont affichés les justificatifs
|
|
||||||
dont la date de début est supérieur ou égale à la valeur donnée):
|
|
||||||
query?date_debut=[- date au format iso -]
|
|
||||||
ex: query?date_debut=2022-11-03T08:00+01:00
|
|
||||||
Date fin
|
|
||||||
(date de fin du justificatif, sont affichés les justificatifs
|
|
||||||
dont la date de fin est inférieure ou égale à la valeur donnée):
|
|
||||||
query?date_fin=[- date au format iso -]
|
|
||||||
ex: query?date_fin=2022-11-03T10:00+01:00
|
|
||||||
user_id (l'id de l'auteur du justificatif)
|
|
||||||
query?user_id=[int]
|
|
||||||
ex query?user_id=3
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
user_id:<int:user_id>
|
user_id:<int:user_id>
|
||||||
est_just:<bool:est_just>
|
|
||||||
date_debut:<string:date_debut_iso>
|
date_debut:<string:date_debut_iso>
|
||||||
date_fin:<string:date_fin_iso>
|
date_fin:<string:date_fin_iso>
|
||||||
etat:<array[string]:etat>
|
etat:<array[string]:etat>
|
||||||
order:<bool:order>
|
order:<bool:order>
|
||||||
courant:<bool:courant>
|
courant:<bool:courant>
|
||||||
group_id:<int:group_id>
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur du justificatif
|
||||||
|
date_debut:date de début du justificatif (supérieur ou égal)
|
||||||
|
date_fin:date de fin du justificatif (inférieur ou égal)
|
||||||
|
etat:etat du justificatif → valide, non_valide, attente, modifie
|
||||||
|
order:retourne les justificatifs dans l'ordre décroissant (non vide = True)
|
||||||
|
courant:retourne les justificatifs de l'année courante (bool : v/t ou f)
|
||||||
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatifs/1;
|
||||||
|
/justificatifs/1/query?etat=attente;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
@ -164,7 +165,7 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
|||||||
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
||||||
"""
|
"""
|
||||||
Renvoie tous les justificatifs d'un département
|
Renvoie tous les justificatifs d'un département
|
||||||
(en ajoutant un champ "formsemestre" si possible)
|
(en ajoutant un champ "`formsemestre`" si possible).
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
@ -176,6 +177,21 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
|||||||
order:<bool:order>
|
order:<bool:order>
|
||||||
courant:<bool:courant>
|
courant:<bool:courant>
|
||||||
group_id:<int:group_id>
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur du justificatif
|
||||||
|
date_debut:date de début du justificatif (supérieur ou égal)
|
||||||
|
date_fin:date de fin du justificatif (inférieur ou égal)
|
||||||
|
etat:etat du justificatif → valide, non_valide, attente, modifie
|
||||||
|
order:retourne les justificatifs dans l'ordre décroissant (non vide = True)
|
||||||
|
courant:retourne les justificatifs de l'année courante (bool : v/t ou f)
|
||||||
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatifs/dept/1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du département et des étudiants du département
|
# Récupération du département et des étudiants du département
|
||||||
@ -204,9 +220,9 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
|||||||
|
|
||||||
def _set_sems(justi: Justificatif, restrict: bool) -> dict:
|
def _set_sems(justi: Justificatif, restrict: bool) -> dict:
|
||||||
"""
|
"""
|
||||||
_set_sems Ajoute le formsemestre associé au justificatif s'il existe
|
_set_sems Ajoute le formsemestre associé au justificatif s'il existe.
|
||||||
|
|
||||||
Si le formsemestre n'existe pas, renvoie la simple représentation du justificatif
|
Si le formsemestre n'existe pas, renvoie la simple représentation du justificatif.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
justi (Justificatif): Le justificatif
|
justi (Justificatif): Le justificatif
|
||||||
@ -247,7 +263,7 @@ def _set_sems(justi: Justificatif, restrict: bool) -> dict:
|
|||||||
@as_json
|
@as_json
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
||||||
"""Retourne tous les justificatifs du formsemestre
|
"""Retourne tous les justificatifs du formsemestre.
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
@ -259,6 +275,21 @@ def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
|||||||
order:<bool:order>
|
order:<bool:order>
|
||||||
courant:<bool:courant>
|
courant:<bool:courant>
|
||||||
group_id:<int:group_id>
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
-----
|
||||||
|
user_id:l'id de l'auteur du justificatif
|
||||||
|
date_debut:date de début du justificatif (supérieur ou égal)
|
||||||
|
date_fin:date de fin du justificatif (inférieur ou égal)
|
||||||
|
etat:etat du justificatif → valide, non_valide, attente, modifie
|
||||||
|
order:retourne les justificatifs dans l'ordre décroissant (non vide = True)
|
||||||
|
courant:retourne les justificatifs de l'année courante (bool : v/t ou f)
|
||||||
|
group_id:<int:group_id>
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatifs/formsemestre/1;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du formsemestre
|
# Récupération du formsemestre
|
||||||
@ -306,8 +337,11 @@ def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_create(etudid: int = None, nip=None, ine=None):
|
def justif_create(etudid: int = None, nip=None, ine=None):
|
||||||
"""
|
"""
|
||||||
Création d'un justificatif pour l'étudiant (etudid)
|
Création d'un justificatif pour l'étudiant.
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"date_debut": str,
|
"date_debut": str,
|
||||||
@ -322,6 +356,10 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
}
|
}
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/1/create;[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""attente""}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -451,15 +489,24 @@ def _create_one(
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_edit(justif_id: int):
|
def justif_edit(justif_id: int):
|
||||||
"""
|
"""
|
||||||
Edition d'un justificatif à partir de son id
|
Édition d'un justificatif à partir de son id.
|
||||||
La requête doit avoir un content type "application/json":
|
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"etat"?: str,
|
"etat"?: str,
|
||||||
"raison"?: str
|
"raison"?: str
|
||||||
"date_debut"?: str
|
"date_debut"?: str
|
||||||
"date_fin"?: str
|
"date_fin"?: str
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/1/edit;{""etat"":""valide""}
|
||||||
|
/justificatif/1/edit;{""raison"":""MEDIC""}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du justificatif à modifier
|
# Récupération du justificatif à modifier
|
||||||
@ -564,15 +611,20 @@ def justif_edit(justif_id: int):
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_delete():
|
def justif_delete():
|
||||||
"""
|
"""
|
||||||
Suppression d'un justificatif à partir de son id
|
Suppression d'un justificatif à partir de son id.
|
||||||
|
|
||||||
Forme des données envoyées :
|
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
[
|
[
|
||||||
<justif_id:int>,
|
<justif_id:int>,
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/delete;[2, 2, 3]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -647,7 +699,9 @@ def _delete_one(justif_id: int) -> tuple[int, str]:
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_import(justif_id: int = None):
|
def justif_import(justif_id: int = None):
|
||||||
"""
|
"""
|
||||||
Importation d'un fichier (création d'archive)
|
Importation d'un fichier (création d'archive).
|
||||||
|
|
||||||
|
> Procédure d'importation de fichier : [importer un justificatif](FichiersJustificatifs.md#importer-un-fichier)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# On vérifie qu'un fichier a bien été envoyé
|
# On vérifie qu'un fichier a bien été envoyé
|
||||||
@ -698,7 +752,10 @@ def justif_import(justif_id: int = None):
|
|||||||
def justif_export(justif_id: int | None = None, filename: str | None = None):
|
def justif_export(justif_id: int | None = None, filename: str | None = None):
|
||||||
"""
|
"""
|
||||||
Retourne un fichier d'une archive d'un justificatif.
|
Retourne un fichier d'une archive d'un justificatif.
|
||||||
La permission est ScoView + (AbsJustifView ou être l'auteur du justifcatif)
|
|
||||||
|
La permission est `ScoView` + (`AbsJustifView` ou être l'auteur du justificatif).
|
||||||
|
|
||||||
|
> Procédure de téléchargement de fichier : [télécharger un justificatif](FichiersJustificatifs.md#télécharger-un-fichier)
|
||||||
"""
|
"""
|
||||||
# On récupère le justificatif concerné
|
# On récupère le justificatif concerné
|
||||||
justificatif_unique = Justificatif.get_justificatif(justif_id)
|
justificatif_unique = Justificatif.get_justificatif(justif_id)
|
||||||
@ -735,15 +792,21 @@ def justif_export(justif_id: int | None = None, filename: str | None = None):
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_remove(justif_id: int = None):
|
def justif_remove(justif_id: int = None):
|
||||||
"""
|
"""
|
||||||
Supression d'un fichier ou d'une archive
|
Supression d'un fichier ou d'une archive.
|
||||||
{
|
|
||||||
"remove": <"all"/"list">
|
|
||||||
|
|
||||||
|
> Procédure de suppression de fichier : [supprimer un justificatif](FichiersJustificatifs.md#supprimer-un-fichier)
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"remove": <"all"/"list">,
|
||||||
"filenames"?: [
|
"filenames"?: [
|
||||||
<filename:str>,
|
<filename:str>,
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# On récupère le dictionnaire
|
# On récupère le dictionnaire
|
||||||
@ -808,7 +871,12 @@ def justif_remove(justif_id: int = None):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justif_list(justif_id: int = None):
|
def justif_list(justif_id: int = None):
|
||||||
"""
|
"""
|
||||||
Liste les fichiers du justificatif
|
Liste les fichiers du justificatif.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/1/list;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Récupération du justificatif concerné
|
# Récupération du justificatif concerné
|
||||||
@ -850,7 +918,12 @@ def justif_list(justif_id: int = None):
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def justif_justifies(justif_id: int = None):
|
def justif_justifies(justif_id: int = None):
|
||||||
"""
|
"""
|
||||||
Liste assiduite_id justifiées par le justificatif
|
Liste `assiduite_id` justifiées par le justificatif.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/justificatif/1/justifies;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# On récupère le justificatif concerné
|
# On récupère le justificatif concerné
|
||||||
|
@ -50,7 +50,12 @@ from app.scodoc.sco_utils import json_error
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def logo_list_globals():
|
def logo_list_globals():
|
||||||
"""Liste tous les logos"""
|
"""Liste des noms des logos définis pour le site ScoDoc.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/logos
|
||||||
|
"""
|
||||||
logos = list_logos()[None]
|
logos = list_logos()[None]
|
||||||
return list(logos.keys())
|
return list(logos.keys())
|
||||||
|
|
||||||
@ -59,6 +64,15 @@ def logo_list_globals():
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
def logo_get_global(logoname):
|
def logo_get_global(logoname):
|
||||||
|
"""Renvoie le logo global de nom donné.
|
||||||
|
|
||||||
|
L'image est au format png ou jpg; le format retourné dépend du format sous lequel
|
||||||
|
l'image a été initialement enregistrée.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/logo/B
|
||||||
|
"""
|
||||||
logo = find_logo(logoname=logoname)
|
logo = find_logo(logoname=logoname)
|
||||||
if logo is None:
|
if logo is None:
|
||||||
return json_error(404, message="logo not found")
|
return json_error(404, message="logo not found")
|
||||||
@ -75,12 +89,19 @@ def _core_get_logos(dept_id) -> list:
|
|||||||
return list(logos.keys())
|
return list(logos.keys())
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/departement/<string:departement>/logos")
|
@bp.route("/departement/<string:dept_acronym>/logos")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def logo_get_local_by_acronym(departement):
|
def departement_logos(dept_acronym: str):
|
||||||
dept_id = Departement.from_acronym(departement).id
|
"""Liste des noms des logos définis pour le département
|
||||||
|
désigné par son acronyme.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/departement/TAPI/logos
|
||||||
|
"""
|
||||||
|
dept_id = Departement.from_acronym(dept_acronym).id
|
||||||
return _core_get_logos(dept_id)
|
return _core_get_logos(dept_id)
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +109,10 @@ def logo_get_local_by_acronym(departement):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def logo_get_local_by_id(dept_id):
|
def departement_logos_by_id(dept_id):
|
||||||
|
"""Liste des noms des logos définis pour le département
|
||||||
|
désigné par son id.
|
||||||
|
"""
|
||||||
return _core_get_logos(dept_id)
|
return _core_get_logos(dept_id)
|
||||||
|
|
||||||
|
|
||||||
@ -108,6 +132,12 @@ def _core_get_logo(dept_id, logoname) -> Response:
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
def logo_get_local_dept_by_acronym(departement, logoname):
|
def logo_get_local_dept_by_acronym(departement, logoname):
|
||||||
|
"""Le logo: image (format png ou jpg).
|
||||||
|
|
||||||
|
**Exemple d'utilisation:**
|
||||||
|
|
||||||
|
* `/ScoDoc/api/departement/MMI/logo/header`
|
||||||
|
"""
|
||||||
dept_id = Departement.from_acronym(departement).id
|
dept_id = Departement.from_acronym(departement).id
|
||||||
return _core_get_logo(dept_id, logoname)
|
return _core_get_logo(dept_id, logoname)
|
||||||
|
|
||||||
@ -116,4 +146,10 @@ def logo_get_local_dept_by_acronym(departement, logoname):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
def logo_get_local_dept_by_id(dept_id, logoname):
|
def logo_get_local_dept_by_id(dept_id, logoname):
|
||||||
|
"""Le logo: image (format png ou jpg).
|
||||||
|
|
||||||
|
**Exemple d'utilisation:**
|
||||||
|
|
||||||
|
* `/ScoDoc/api/departement/id/3/logo/header`
|
||||||
|
"""
|
||||||
return _core_get_logo(dept_id, logoname)
|
return _core_get_logo(dept_id, logoname)
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : accès aux moduleimpl
|
ScoDoc 9 API : accès aux moduleimpl
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
ModuleImpl
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask_json import as_json
|
from flask_json import as_json
|
||||||
@ -28,38 +32,15 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
@as_json
|
@as_json
|
||||||
def moduleimpl(moduleimpl_id: int):
|
def moduleimpl(moduleimpl_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne un moduleimpl en fonction de son id
|
Retourne le moduleimpl.
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
moduleimpl_id : l'id d'un moduleimpl
|
moduleimpl_id : l'id d'un moduleimpl
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"id": 1,
|
/moduleimpl/1
|
||||||
"formsemestre_id": 1,
|
|
||||||
"module_id": 1,
|
|
||||||
"responsable_id": 2,
|
|
||||||
"moduleimpl_id": 1,
|
|
||||||
"ens": [],
|
|
||||||
"module": {
|
|
||||||
"heures_tp": 0,
|
|
||||||
"code_apogee": "",
|
|
||||||
"titre": "Initiation aux réseaux informatiques",
|
|
||||||
"coefficient": 1,
|
|
||||||
"module_type": 2,
|
|
||||||
"id": 1,
|
|
||||||
"ects": null,
|
|
||||||
"abbrev": "Init aux réseaux informatiques",
|
|
||||||
"ue_id": 1,
|
|
||||||
"code": "R101",
|
|
||||||
"formation_id": 1,
|
|
||||||
"heures_cours": 0,
|
|
||||||
"matiere_id": 1,
|
|
||||||
"heures_td": 0,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"numero": 10,
|
|
||||||
"module_id": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||||
return modimpl.to_dict(convert_objects=True)
|
return modimpl.to_dict(convert_objects=True)
|
||||||
@ -72,16 +53,11 @@ def moduleimpl(moduleimpl_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def moduleimpl_inscriptions(moduleimpl_id: int):
|
def moduleimpl_inscriptions(moduleimpl_id: int):
|
||||||
"""Liste des inscriptions à ce moduleimpl
|
"""Liste des inscriptions à ce moduleimpl.
|
||||||
Exemple de résultat :
|
|
||||||
[
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"id": 1,
|
/moduleimpl/1/inscriptions
|
||||||
"etudid": 666,
|
|
||||||
"moduleimpl_id": 1234,
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||||
return [i.to_dict() for i in modimpl.inscriptions]
|
return [i.to_dict() for i in modimpl.inscriptions]
|
||||||
@ -93,22 +69,11 @@ def moduleimpl_inscriptions(moduleimpl_id: int):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def moduleimpl_notes(moduleimpl_id: int):
|
def moduleimpl_notes(moduleimpl_id: int):
|
||||||
"""Liste des notes dans ce moduleimpl
|
"""Liste des notes dans ce moduleimpl.
|
||||||
Exemple de résultat :
|
|
||||||
[
|
SAMPLES
|
||||||
{
|
-------
|
||||||
"etudid": 17776, // code de l'étudiant
|
/moduleimpl/1/notes
|
||||||
"nom": "DUPONT",
|
|
||||||
"prenom": "Luz",
|
|
||||||
"38411": 16.0, // Note dans l'évaluation d'id 38411
|
|
||||||
"38410": 15.0,
|
|
||||||
"moymod": 15.5, // Moyenne INDICATIVE module
|
|
||||||
"moy_ue_2875": 15.5, // Moyenne vers l'UE 2875
|
|
||||||
"moy_ue_2876": 15.5, // Moyenne vers l'UE 2876
|
|
||||||
"moy_ue_2877": 15.5 // Moyenne vers l'UE 2877
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||||
app.set_sco_dept(modimpl.formsemestre.departement.acronym)
|
app.set_sco_dept(modimpl.formsemestre.departement.acronym)
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : partitions
|
ScoDoc 9 API : partitions
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Groupes et Partitions
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
@ -40,22 +45,9 @@ from app.scodoc import sco_utils as scu
|
|||||||
def partition_info(partition_id: int):
|
def partition_info(partition_id: int):
|
||||||
"""Info sur une partition.
|
"""Info sur une partition.
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
```
|
-------
|
||||||
{
|
/partition/1
|
||||||
'bul_show_rank': False,
|
|
||||||
'formsemestre_id': 39,
|
|
||||||
'groups': [
|
|
||||||
{'id': 268, 'name': 'A', 'partition_id': 100},
|
|
||||||
{'id': 269, 'name': 'B', 'partition_id': 100}
|
|
||||||
],
|
|
||||||
'groups_editable': True,
|
|
||||||
'id': 100,
|
|
||||||
'numero': 100,
|
|
||||||
'partition_name': 'TD',
|
|
||||||
'show_in_lists': True
|
|
||||||
}
|
|
||||||
```
|
|
||||||
"""
|
"""
|
||||||
query = Partition.query.filter_by(id=partition_id)
|
query = Partition.query.filter_by(id=partition_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -71,24 +63,11 @@ def partition_info(partition_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_partitions(formsemestre_id: int):
|
def formsemestre_partitions(formsemestre_id: int):
|
||||||
"""Liste de toutes les partitions d'un formsemestre
|
"""Liste de toutes les partitions d'un formsemestre.
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
|
||||||
|
|
||||||
{
|
|
||||||
partition_id : {
|
|
||||||
"bul_show_rank": False,
|
|
||||||
"formsemestre_id": 1063,
|
|
||||||
"groups" :
|
|
||||||
group_id : {
|
|
||||||
"id" : 12,
|
|
||||||
"name" : "A",
|
|
||||||
"partition_id" : partition_id,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/partitions
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -112,21 +91,14 @@ def group_etudiants(group_id: int):
|
|||||||
"""
|
"""
|
||||||
Retourne la liste des étudiants dans un groupe
|
Retourne la liste des étudiants dans un groupe
|
||||||
(inscrits au groupe et inscrits au semestre).
|
(inscrits au groupe et inscrits au semestre).
|
||||||
|
|
||||||
|
PARAMS
|
||||||
|
------
|
||||||
group_id : l'id d'un groupe
|
group_id : l'id d'un groupe
|
||||||
|
|
||||||
Exemple de résultat :
|
SAMPLES
|
||||||
[
|
-------
|
||||||
{
|
/group/1/etudiants
|
||||||
'civilite': 'M',
|
|
||||||
'id': 123456,
|
|
||||||
'ine': None,
|
|
||||||
'nip': '987654321',
|
|
||||||
'nom': 'MARTIN',
|
|
||||||
'nom_usuel': null,
|
|
||||||
'prenom': 'JEAN'}
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
query = GroupDescr.query.filter_by(id=group_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -152,11 +124,11 @@ def group_etudiants(group_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_etudiants_query(group_id: int):
|
def group_etudiants_query(group_id: int):
|
||||||
"""Étudiants du groupe, filtrés par état (aucun, I, D, DEF)
|
"""Étudiants du groupe, filtrés par état (aucun, `I`, `D`, `DEF`)
|
||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
etat:<string:etat>
|
etat : string
|
||||||
|
|
||||||
"""
|
"""
|
||||||
etat = request.args.get("etat")
|
etat = request.args.get("etat")
|
||||||
@ -186,7 +158,7 @@ def group_etudiants_query(group_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_set_etudiant(group_id: int, etudid: int):
|
def group_set_etudiant(group_id: int, etudid: int):
|
||||||
"""Affecte l'étudiant au groupe indiqué"""
|
"""Affecte l'étudiant au groupe indiqué."""
|
||||||
etud = Identite.query.get_or_404(etudid)
|
etud = Identite.query.get_or_404(etudid)
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
query = GroupDescr.query.filter_by(id=group_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -248,7 +220,8 @@ def group_remove_etud(group_id: int, etudid: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def partition_remove_etud(partition_id: int, etudid: int):
|
def partition_remove_etud(partition_id: int, etudid: int):
|
||||||
"""Enlève l'étudiant de tous les groupes de cette partition
|
"""Enlève l'étudiant de tous les groupes de cette partition.
|
||||||
|
|
||||||
(NB: en principe, un étudiant ne doit être que dans 0 ou 1 groupe d'une partition)
|
(NB: en principe, un étudiant ne doit être que dans 0 ou 1 groupe d'une partition)
|
||||||
"""
|
"""
|
||||||
etud = Identite.query.get_or_404(etudid)
|
etud = Identite.query.get_or_404(etudid)
|
||||||
@ -293,12 +266,19 @@ def partition_remove_etud(partition_id: int, etudid: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_create(partition_id: int): # partition-group-create
|
def group_create(partition_id: int): # partition-group-create
|
||||||
"""Création d'un groupe dans une partition
|
"""Création d'un groupe dans une partition.
|
||||||
|
|
||||||
The request content type should be "application/json":
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"group_name" : nom_du_groupe,
|
"group_name" : nom_du_groupe,
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/partition/1/group/create;{""group_name"" : ""Nouveau Groupe""}
|
||||||
"""
|
"""
|
||||||
query = Partition.query.filter_by(id=partition_id)
|
query = Partition.query.filter_by(id=partition_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -345,7 +325,7 @@ def group_create(partition_id: int): # partition-group-create
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_delete(group_id: int):
|
def group_delete(group_id: int):
|
||||||
"""Suppression d'un groupe"""
|
"""Suppression d'un groupe."""
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
query = GroupDescr.query.filter_by(id=group_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = (
|
query = (
|
||||||
@ -374,7 +354,19 @@ def group_delete(group_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_edit(group_id: int):
|
def group_edit(group_id: int):
|
||||||
"""Edit a group"""
|
"""Édition d'un groupe.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"group_name" : "A1"
|
||||||
|
}
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/group/1/edit;{""group_name"":""A1""}
|
||||||
|
"""
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
query = GroupDescr.query.filter_by(id=group_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = (
|
query = (
|
||||||
@ -415,9 +407,14 @@ def group_edit(group_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def group_set_edt_id(group_id: int, edt_id: str):
|
def group_set_edt_id(group_id: int, edt_id: str):
|
||||||
"""Set edt_id for this group.
|
"""Set edt_id du groupe.
|
||||||
Contrairement à /edit, peut-être changé pour toute partition
|
|
||||||
ou formsemestre non verrouillé.
|
Contrairement à `/edit`, peut-être changé pour toute partition
|
||||||
|
d'un formsemestre non verrouillé.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/group/1/set_edt_id/EDT_GR1
|
||||||
"""
|
"""
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
query = GroupDescr.query.filter_by(id=group_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -443,9 +440,11 @@ def group_set_edt_id(group_id: int, edt_id: str):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def partition_create(formsemestre_id: int):
|
def partition_create(formsemestre_id: int):
|
||||||
"""Création d'une partition dans un semestre
|
"""Création d'une partition dans un semestre.
|
||||||
|
|
||||||
The request content type should be "application/json":
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"partition_name": str,
|
"partition_name": str,
|
||||||
"numero": int,
|
"numero": int,
|
||||||
@ -453,6 +452,7 @@ def partition_create(formsemestre_id: int):
|
|||||||
"show_in_lists": bool,
|
"show_in_lists": bool,
|
||||||
"groups_editable": bool
|
"groups_editable": bool
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -508,8 +508,13 @@ def partition_create(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def formsemestre_set_partitions_order(formsemestre_id: int):
|
def formsemestre_set_partitions_order(formsemestre_id: int):
|
||||||
"""Modifie l'ordre des partitions du formsemestre
|
"""Modifie l'ordre des partitions du formsemestre.
|
||||||
JSON args: [partition_id1, partition_id2, ...]
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
[ partition_id1, partition_id2, ... ]
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -520,7 +525,7 @@ def formsemestre_set_partitions_order(formsemestre_id: int):
|
|||||||
if not formsemestre.can_change_groups():
|
if not formsemestre.can_change_groups():
|
||||||
return json_error(401, "opération non autorisée")
|
return json_error(401, "opération non autorisée")
|
||||||
partition_ids = request.get_json(force=True) # may raise 400 Bad Request
|
partition_ids = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
if not isinstance(partition_ids, int) and not all(
|
if not isinstance(partition_ids, list) and not all(
|
||||||
isinstance(x, int) for x in partition_ids
|
isinstance(x, int) for x in partition_ids
|
||||||
):
|
):
|
||||||
return json_error(
|
return json_error(
|
||||||
@ -549,8 +554,13 @@ def formsemestre_set_partitions_order(formsemestre_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def partition_order_groups(partition_id: int):
|
def partition_order_groups(partition_id: int):
|
||||||
"""Modifie l'ordre des groupes de la partition
|
"""Modifie l'ordre des groupes de la partition.
|
||||||
JSON args: [group_id1, group_id2, ...]
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
|
[ group_id1, group_id2, ... ]
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
query = Partition.query.filter_by(id=partition_id)
|
query = Partition.query.filter_by(id=partition_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -561,7 +571,7 @@ def partition_order_groups(partition_id: int):
|
|||||||
if not partition.formsemestre.can_change_groups():
|
if not partition.formsemestre.can_change_groups():
|
||||||
return json_error(401, "opération non autorisée")
|
return json_error(401, "opération non autorisée")
|
||||||
group_ids = request.get_json(force=True) # may raise 400 Bad Request
|
group_ids = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
if not isinstance(group_ids, int) and not all(
|
if not isinstance(group_ids, list) and not all(
|
||||||
isinstance(x, int) for x in group_ids
|
isinstance(x, int) for x in group_ids
|
||||||
):
|
):
|
||||||
return json_error(
|
return json_error(
|
||||||
@ -586,10 +596,13 @@ def partition_order_groups(partition_id: int):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def partition_edit(partition_id: int):
|
def partition_edit(partition_id: int):
|
||||||
"""Modification d'une partition dans un semestre
|
"""Modification d'une partition dans un semestre.
|
||||||
|
|
||||||
The request content type should be "application/json"
|
Tous les champs sont optionnels.
|
||||||
All fields are optional:
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"partition_name": str,
|
"partition_name": str,
|
||||||
"numero":int,
|
"numero":int,
|
||||||
@ -597,6 +610,11 @@ def partition_edit(partition_id: int):
|
|||||||
"show_in_lists":bool,
|
"show_in_lists":bool,
|
||||||
"groups_editable":bool
|
"groups_editable":bool
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/partition/1/edit;{""bul_show_rank"":1}
|
||||||
"""
|
"""
|
||||||
query = Partition.query.filter_by(id=partition_id)
|
query = Partition.query.filter_by(id=partition_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
@ -631,9 +649,8 @@ def partition_edit(partition_id: int):
|
|||||||
|
|
||||||
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
|
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
|
||||||
value = data.get(boolean_field)
|
value = data.get(boolean_field)
|
||||||
|
value = scu.to_bool(value) if value is not None else None
|
||||||
if value is not None and value != getattr(partition, boolean_field):
|
if value is not None and value != getattr(partition, boolean_field):
|
||||||
if not isinstance(value, bool):
|
|
||||||
return json_error(API_CLIENT_ERROR, f"invalid type for {boolean_field}")
|
|
||||||
if boolean_field == "groups_editable" and partition.is_parcours():
|
if boolean_field == "groups_editable" and partition.is_parcours():
|
||||||
return json_error(
|
return json_error(
|
||||||
API_CLIENT_ERROR, f"can't change {scu.PARTITION_PARCOURS}"
|
API_CLIENT_ERROR, f"can't change {scu.PARTITION_PARCOURS}"
|
||||||
@ -660,9 +677,9 @@ def partition_edit(partition_id: int):
|
|||||||
def partition_delete(partition_id: int):
|
def partition_delete(partition_id: int):
|
||||||
"""Suppression d'une partition (et de tous ses groupes).
|
"""Suppression d'une partition (et de tous ses groupes).
|
||||||
|
|
||||||
Note 1: La partition par défaut (tous les étudiants du sem.) ne peut
|
* Note 1: La partition par défaut (tous les étudiants du sem.) ne peut
|
||||||
pas être supprimée.
|
pas être supprimée.
|
||||||
Note 2: Si la partition de parcours est supprimée, les étudiants
|
* Note 2: Si la partition de parcours est supprimée, les étudiants
|
||||||
sont désinscrits des parcours.
|
sont désinscrits des parcours.
|
||||||
"""
|
"""
|
||||||
query = Partition.query.filter_by(id=partition_id)
|
query = Partition.query.filter_by(id=partition_id)
|
||||||
|
@ -3,12 +3,18 @@ from app import db, log
|
|||||||
from app.api import api_bp as bp
|
from app.api import api_bp as bp
|
||||||
from app.auth.logic import basic_auth, token_auth
|
from app.auth.logic import basic_auth, token_auth
|
||||||
|
|
||||||
|
"""
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Authentification API
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/tokens", methods=["POST"])
|
@bp.route("/tokens", methods=["POST"])
|
||||||
@basic_auth.login_required
|
@basic_auth.login_required
|
||||||
@as_json
|
@as_json
|
||||||
def token_get():
|
def token_get():
|
||||||
"renvoie un jeton jwt pour l'utilisateur courant"
|
"Renvoie un jeton jwt pour l'utilisateur courant."
|
||||||
token = basic_auth.current_user().get_token()
|
token = basic_auth.current_user().get_token()
|
||||||
log(f"API: giving token to {basic_auth.current_user()}")
|
log(f"API: giving token to {basic_auth.current_user()}")
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -18,7 +24,7 @@ def token_get():
|
|||||||
@bp.route("/tokens", methods=["DELETE"])
|
@bp.route("/tokens", methods=["DELETE"])
|
||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
def token_revoke():
|
def token_revoke():
|
||||||
"révoque le jeton de l'utilisateur courant"
|
"Révoque le jeton de l'utilisateur courant."
|
||||||
user = token_auth.current_user()
|
user = token_auth.current_user()
|
||||||
user.revoke_token()
|
user.revoke_token()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
100
app/api/users.py
100
app/api/users.py
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc 9 API : accès aux utilisateurs
|
ScoDoc 9 API : accès aux utilisateurs
|
||||||
|
|
||||||
|
CATEGORY
|
||||||
|
--------
|
||||||
|
Utilisateurs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
@ -32,7 +36,11 @@ from app.scodoc.sco_utils import json_error
|
|||||||
@as_json
|
@as_json
|
||||||
def user_info(uid: int):
|
def user_info(uid: int):
|
||||||
"""
|
"""
|
||||||
Info sur un compte utilisateur scodoc
|
Info sur un compte utilisateur ScoDoc.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/user/2
|
||||||
"""
|
"""
|
||||||
user: User = db.session.get(User, uid)
|
user: User = db.session.get(User, uid)
|
||||||
if user is None:
|
if user is None:
|
||||||
@ -53,7 +61,11 @@ def user_info(uid: int):
|
|||||||
@as_json
|
@as_json
|
||||||
def users_info_query():
|
def users_info_query():
|
||||||
"""Utilisateurs, filtrés par dept, active ou début nom
|
"""Utilisateurs, filtrés par dept, active ou début nom
|
||||||
|
|
||||||
|
Exemple:
|
||||||
|
```
|
||||||
/users/query?departement=dept_acronym&active=1&starts_with=<string:nom>
|
/users/query?departement=dept_acronym&active=1&starts_with=<string:nom>
|
||||||
|
```
|
||||||
|
|
||||||
Seuls les utilisateurs "accessibles" (selon les permissions) sont retournés.
|
Seuls les utilisateurs "accessibles" (selon les permissions) sont retournés.
|
||||||
Si accès via API web, le département de l'URL est ignoré, seules
|
Si accès via API web, le département de l'URL est ignoré, seules
|
||||||
@ -61,9 +73,9 @@ def users_info_query():
|
|||||||
|
|
||||||
QUERY
|
QUERY
|
||||||
-----
|
-----
|
||||||
active:<bool:active>
|
active: bool
|
||||||
departement:<string:departement>
|
departement: string
|
||||||
starts_with:<string:starts_with>
|
starts_with: string
|
||||||
|
|
||||||
"""
|
"""
|
||||||
query = User.query
|
query = User.query
|
||||||
@ -113,7 +125,10 @@ def _is_allowed_user_edit(args: dict) -> tuple[bool, str]:
|
|||||||
@as_json
|
@as_json
|
||||||
def user_create():
|
def user_create():
|
||||||
"""Création d'un utilisateur
|
"""Création d'un utilisateur
|
||||||
The request content type should be "application/json":
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"active":bool (default True),
|
"active":bool (default True),
|
||||||
"dept": str or null,
|
"dept": str or null,
|
||||||
@ -122,6 +137,7 @@ def user_create():
|
|||||||
"user_name": str,
|
"user_name": str,
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
user_name = args.get("user_name")
|
user_name = args.get("user_name")
|
||||||
@ -158,8 +174,10 @@ def user_create():
|
|||||||
@permission_required(Permission.UsersAdmin)
|
@permission_required(Permission.UsersAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def user_edit(uid: int):
|
def user_edit(uid: int):
|
||||||
"""Modification d'un utilisateur
|
"""Modification d'un utilisateur.
|
||||||
|
|
||||||
Champs modifiables:
|
Champs modifiables:
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"dept": str or null,
|
"dept": str or null,
|
||||||
"nom": str,
|
"nom": str,
|
||||||
@ -167,6 +185,7 @@ def user_edit(uid: int):
|
|||||||
"active":bool
|
"active":bool
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
user: User = User.query.get_or_404(uid)
|
user: User = User.query.get_or_404(uid)
|
||||||
@ -205,12 +224,21 @@ def user_edit(uid: int):
|
|||||||
@permission_required(Permission.UsersAdmin)
|
@permission_required(Permission.UsersAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def user_password(uid: int):
|
def user_password(uid: int):
|
||||||
"""Modification du mot de passe d'un utilisateur
|
"""Modification du mot de passe d'un utilisateur.
|
||||||
Champs modifiables:
|
|
||||||
|
Si le mot de passe ne convient pas, erreur 400.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"password": str
|
"password": str
|
||||||
}
|
}
|
||||||
Si le mot de passe ne convient pas, erreur 400.
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/user/3/password;{""password"" : ""rePlaCemeNT456averylongandcomplicated""}
|
||||||
"""
|
"""
|
||||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
user: User = User.query.get_or_404(uid)
|
user: User = User.query.get_or_404(uid)
|
||||||
@ -243,7 +271,7 @@ def user_password(uid: int):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def user_role_add(uid: int, role_name: str, dept: str = None):
|
def user_role_add(uid: int, role_name: str, dept: str = None):
|
||||||
"""Add a role in the given dept to the user"""
|
"""Ajoute un rôle à l'utilisateur dans le département donné."""
|
||||||
user: User = User.query.get_or_404(uid)
|
user: User = User.query.get_or_404(uid)
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
if dept is not None: # check
|
if dept is not None: # check
|
||||||
@ -272,7 +300,7 @@ def user_role_add(uid: int, role_name: str, dept: str = None):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def user_role_remove(uid: int, role_name: str, dept: str = None):
|
def user_role_remove(uid: int, role_name: str, dept: str = None):
|
||||||
"""Remove the role (in the given dept) from the user"""
|
"""Retire le rôle (dans le département donné) à cet utilisateur."""
|
||||||
user: User = User.query.get_or_404(uid)
|
user: User = User.query.get_or_404(uid)
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
if dept is not None: # check
|
if dept is not None: # check
|
||||||
@ -299,7 +327,12 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
|
|||||||
@permission_required(Permission.UsersView)
|
@permission_required(Permission.UsersView)
|
||||||
@as_json
|
@as_json
|
||||||
def permissions_list():
|
def permissions_list():
|
||||||
"""Liste des noms de permissions définies"""
|
"""Liste des noms de permissions définies.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/permissions
|
||||||
|
"""
|
||||||
return list(Permission.permission_by_name.keys())
|
return list(Permission.permission_by_name.keys())
|
||||||
|
|
||||||
|
|
||||||
@ -310,7 +343,12 @@ def permissions_list():
|
|||||||
@permission_required(Permission.UsersView)
|
@permission_required(Permission.UsersView)
|
||||||
@as_json
|
@as_json
|
||||||
def role_get(role_name: str):
|
def role_get(role_name: str):
|
||||||
"""Un rôle"""
|
"""Un rôle.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/role/Ens
|
||||||
|
"""
|
||||||
return Role.query.filter_by(name=role_name).first_or_404().to_dict()
|
return Role.query.filter_by(name=role_name).first_or_404().to_dict()
|
||||||
|
|
||||||
|
|
||||||
@ -321,7 +359,12 @@ def role_get(role_name: str):
|
|||||||
@permission_required(Permission.UsersView)
|
@permission_required(Permission.UsersView)
|
||||||
@as_json
|
@as_json
|
||||||
def roles_list():
|
def roles_list():
|
||||||
"""Tous les rôles définis"""
|
"""Tous les rôles définis.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/roles
|
||||||
|
"""
|
||||||
return [role.to_dict() for role in Role.query]
|
return [role.to_dict() for role in Role.query]
|
||||||
|
|
||||||
|
|
||||||
@ -338,7 +381,7 @@ def roles_list():
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def role_permission_add(role_name: str, perm_name: str):
|
def role_permission_add(role_name: str, perm_name: str):
|
||||||
"""Add permission to role"""
|
"""Ajoute une permission à un rôle."""
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
permission = Permission.get_by_name(perm_name)
|
permission = Permission.get_by_name(perm_name)
|
||||||
if permission is None:
|
if permission is None:
|
||||||
@ -363,7 +406,7 @@ def role_permission_add(role_name: str, perm_name: str):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def role_permission_remove(role_name: str, perm_name: str):
|
def role_permission_remove(role_name: str, perm_name: str):
|
||||||
"""Remove permission from role"""
|
"""Retire une permission d'un rôle."""
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
permission = Permission.get_by_name(perm_name)
|
permission = Permission.get_by_name(perm_name)
|
||||||
if permission is None:
|
if permission is None:
|
||||||
@ -382,10 +425,19 @@ def role_permission_remove(role_name: str, perm_name: str):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def role_create(role_name: str):
|
def role_create(role_name: str):
|
||||||
"""Create a new role with permissions.
|
"""Création d'un nouveau rôle avec les permissions données.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"permissions" : [ 'ScoView', ... ]
|
"permissions" : [ 'ScoView', ... ]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/role/create/customRole;{""permissions"": [""ScoView"", ""UsersView""]}
|
||||||
"""
|
"""
|
||||||
role: Role = Role.query.filter_by(name=role_name).first()
|
role: Role = Role.query.filter_by(name=role_name).first()
|
||||||
if role:
|
if role:
|
||||||
@ -410,11 +462,16 @@ def role_create(role_name: str):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def role_edit(role_name: str):
|
def role_edit(role_name: str):
|
||||||
"""Edit a role. On peut spécifier un nom et/ou des permissions.
|
"""Édition d'un rôle. On peut spécifier un nom et/ou des permissions.
|
||||||
|
|
||||||
|
DATA
|
||||||
|
----
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"name" : name
|
"name" : name
|
||||||
"permissions" : [ 'ScoView', ... ]
|
"permissions" : [ 'ScoView', ... ]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
@ -442,7 +499,12 @@ def role_edit(role_name: str):
|
|||||||
@permission_required(Permission.ScoSuperAdmin)
|
@permission_required(Permission.ScoSuperAdmin)
|
||||||
@as_json
|
@as_json
|
||||||
def role_delete(role_name: str):
|
def role_delete(role_name: str):
|
||||||
"""Delete a role"""
|
"""Suppression d'un rôle.
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/role/customRole/delete
|
||||||
|
"""
|
||||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||||
db.session.delete(role)
|
db.session.delete(role)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -348,7 +348,7 @@ class Assiduite(ScoDocModel):
|
|||||||
"""
|
"""
|
||||||
Retourne le module associé à l'assiduité
|
Retourne le module associé à l'assiduité
|
||||||
Si traduire est vrai, retourne le titre du module précédé du code
|
Si traduire est vrai, retourne le titre du module précédé du code
|
||||||
Sinon rentourne l'objet Module ou None
|
Sinon retourne l'objet Module ou None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.moduleimpl_id is not None:
|
if self.moduleimpl_id is not None:
|
||||||
@ -358,7 +358,7 @@ class Assiduite(ScoDocModel):
|
|||||||
return f"{mod.code} {mod.titre}"
|
return f"{mod.code} {mod.titre}"
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
elif self.external_data is not None and "module" in self.external_data:
|
if self.external_data is not None and "module" in self.external_data:
|
||||||
return (
|
return (
|
||||||
"Autre module (pas dans la liste)"
|
"Autre module (pas dans la liste)"
|
||||||
if self.external_data["module"] == "Autre"
|
if self.external_data["module"] == "Autre"
|
||||||
|
@ -52,6 +52,17 @@ class Departement(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__}(id={self.id}, acronym='{self.acronym}')>"
|
return f"<{self.__class__.__name__}(id={self.id}, acronym='{self.acronym}')>"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_departement(cls, dept_ident: str | int) -> "Departement":
|
||||||
|
"Le département, par id ou acronyme. Erreur 404 si pas trouvé."
|
||||||
|
try:
|
||||||
|
dept_id = int(dept_ident)
|
||||||
|
except ValueError:
|
||||||
|
dept_id = None
|
||||||
|
if dept_id is None:
|
||||||
|
return cls.query.filter_by(acronym=dept_ident).first_or_404()
|
||||||
|
return cls.query.get_or_404(dept_id)
|
||||||
|
|
||||||
def to_dict(self, with_dept_name=True, with_dept_preferences=False):
|
def to_dict(self, with_dept_name=True, with_dept_preferences=False):
|
||||||
data = {
|
data = {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
|
@ -242,6 +242,16 @@ class GroupDescr(ScoDocModel):
|
|||||||
f"""<{self.__class__.__name__} {self.id} "{self.group_name or '(tous)'}">"""
|
f"""<{self.__class__.__name__} {self.id} "{self.group_name or '(tous)'}">"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_model_attributes(cls, data: dict, excluded: set[str] = None) -> dict:
|
||||||
|
"""Returns a copy of dict with only the keys belonging to the Model and not in excluded.
|
||||||
|
Exclude `partition_id` : a group cannot be moved from a partition to another.
|
||||||
|
"""
|
||||||
|
return super().filter_model_attributes(
|
||||||
|
data,
|
||||||
|
excluded=(excluded or set()) | {"partition_id"},
|
||||||
|
)
|
||||||
|
|
||||||
def get_nom_with_part(self, default="-") -> str:
|
def get_nom_with_part(self, default="-") -> str:
|
||||||
"""Nom avec partition: 'TD A'
|
"""Nom avec partition: 'TD A'
|
||||||
Si groupe par défaut (tous), utilise default ou "-"
|
Si groupe par défaut (tous), utilise default ou "-"
|
||||||
|
280
app/templates/doc/ScoDoc9API.j2
Normal file
280
app/templates/doc/ScoDoc9API.j2
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
{# Documentation de l'API ScoDoc 9 #}
|
||||||
|
# API pour ScoDoc 9
|
||||||
|
|
||||||
|
!!! warning "Attention"
|
||||||
|
*Page générée par la commande `flask gen-api-doc`. Ne pas modifier manuellement.*
|
||||||
|
|
||||||
|
|
||||||
|
L'API ScoDoc permet à des applications tierces d'interroger ScoDoc. Elle offre
|
||||||
|
un accès aux objets de l'application via une API REST.
|
||||||
|
|
||||||
|
Les composants internes de ScoDoc, et notamment le schéma de la base de données,
|
||||||
|
sont susceptibles d'évoluer à tout moment sans préavis: il est vivement
|
||||||
|
déconseillé d'écrire une extension ne passant pas par l'API. Vous ne devez même
|
||||||
|
pas supposer qu'il existe une base de données SQL.
|
||||||
|
|
||||||
|
La version ScoDoc 9 a introduit une nouvelle API avec un nouveau mécanisme d'authentification.
|
||||||
|
**Les clients de l'ancienne API ScoDoc 7 doivent être adaptés pour fonctionner avec ScoDoc 9.**
|
||||||
|
|
||||||
|
Cette API est encore incomplète: n'hésitez pas à demander de nouveaux accès ([contacts](Contact.md))
|
||||||
|
(et canal `#API` du Discord développeurs si vous y avez accès).
|
||||||
|
|
||||||
|
L'API fournit des données JSON, sauf exception (bulletins PDF par exemple).
|
||||||
|
|
||||||
|
Les objets ScoDoc manipulables sont identifiés par des id numériques.
|
||||||
|
|
||||||
|
* `etudid` : étudiant
|
||||||
|
* `formation_id` : un programme de formation (page "programmes");
|
||||||
|
* `ue_id` : une UE dans un programme;
|
||||||
|
* `matiere_id` : une matière dans un programme;
|
||||||
|
* `module_id` : un module dans un programme;
|
||||||
|
* `moduleimpl_id` : un module réalisé dans un semestre;
|
||||||
|
* `formsemestre_id` : un "semestre" de formation.
|
||||||
|
|
||||||
|
(pour plus de précisions, voir le [guide développeurs](GuideDeveloppeurs.md))
|
||||||
|
|
||||||
|
L'URL complète est de la forme:
|
||||||
|
`https://scodoc.example.com/ScoDoc/api/<fonction>`.
|
||||||
|
(<fonction> à choisir dans [Référence](#reference))
|
||||||
|
|
||||||
|
## Configuration de ScoDoc pour utiliser l'API
|
||||||
|
|
||||||
|
Il est nécessaire de disposer d'un compte utilisateur avec les droits adéquats.
|
||||||
|
|
||||||
|
Les droits à accorder dépendent des fonctionnalités nécessaires. la permission
|
||||||
|
`ScoView` est généralement suffisante car elle permet toutes les consultations.
|
||||||
|
Cependant si, par l'API, on veut effectuer des opérations de modification ou
|
||||||
|
encore consulter les comptes utilisateurs, d'autres droits (`ScoChangeGroups`,
|
||||||
|
`UsersView`, `ScoSuperAdmin`, ...) peuvent être requis. La consultation du
|
||||||
|
[tableau récapitulatif](#tableau-recapitulatif-des-entrees-de-lapi) ou la ligne
|
||||||
|
`permission`de chaque entrée vous donnera la permission requise pour chaque
|
||||||
|
opération.
|
||||||
|
|
||||||
|
En général, il est recommandé de créer un rôle, de lui attribuer les permissions
|
||||||
|
que l'on veut utiliser, puis de créer un utilisateur ayant ce rôle.
|
||||||
|
|
||||||
|
En ligne de commande, cela peut se faire comme suit (voir détail des commandes
|
||||||
|
[sur le guide de configuration](GuideConfig.md)).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# se connecter comme utilisateur scodoc
|
||||||
|
su - scodoc
|
||||||
|
|
||||||
|
# Créer un rôle
|
||||||
|
flask create-role LecteurAPI
|
||||||
|
# Lui donner les droits nécessaires: ici ScoView
|
||||||
|
flask edit-role LecteurAPI -a ScoView
|
||||||
|
|
||||||
|
# Créer un nouvel utilisateur avec ce rôle:
|
||||||
|
flask user-create lecteur_api LecteurAPI @all
|
||||||
|
|
||||||
|
# Ou bien, si on veut utiliser un compte existant:
|
||||||
|
# associer notre rôle à un utilisateur
|
||||||
|
flask user-role lecteur_api -a LecteurAPI
|
||||||
|
|
||||||
|
|
||||||
|
# Au besoin, changer le mot de passe de l'utilisateur
|
||||||
|
# (on aura besoin de ce mot de passe dans la configuration du client d'API)
|
||||||
|
flask user-password lecteur_api
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Si vous êtes intéressé par le développement, voir
|
||||||
|
|
||||||
|
* [la section sur les tests unitaires de l'API](TestsScoDoc.md#tests-de-lapi-scodoc9);
|
||||||
|
* [la documentation développeurs](GuideDeveloppeurs.md) et sur les [vues de l'API](DevInternals.md#vues-de-lapi-et-permissions).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
* Si vous utilisez le CAS, pensez à laisser les comptes utilisateurs API se
|
||||||
|
connecter via ScoDoc sans CAS. Pour cela, cocher l'option
|
||||||
|
*Autorise connexion via CAS si CAS est activé*
|
||||||
|
dans leur formulaire de configuration.
|
||||||
|
|
||||||
|
* Si l'utilisateur est associé à un département (cas des comptes créés via l'interface Web),
|
||||||
|
il ne pourra accéder à l'API que via une *route départementale*, c'est à dire une route comprenant
|
||||||
|
l'acronyme de son département, de la forme `https://...//ScoDoc/DEPARTEMENT/api/...`.
|
||||||
|
|
||||||
|
## Essais avec HTTPie
|
||||||
|
|
||||||
|
[HTTPie](https://httpie.io/) est un client universel livre et gratuit très commode, disponible
|
||||||
|
pour Windows, Linux, en ligne de commande ou interface graphique.
|
||||||
|
|
||||||
|
Exemple d'utilisation en ligne de commande et interroger votre ScoDoc pour
|
||||||
|
obtenir la liste des départements:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
http -a USER:PASSWORD POST 'http://localhost:5000/ScoDoc/api/tokens'
|
||||||
|
```
|
||||||
|
|
||||||
|
Qui affiche:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 50
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 05 May 2022 04:29:33 GMT
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": "jS7iVl1234cRDzboAfO5xseE0Ain6Zyz"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(remplacer `USER:PASSWORD` par les identifiants de votre utilisateur et adapter
|
||||||
|
l'URL qui est ici celle d'un client local sur le serveur de test).
|
||||||
|
|
||||||
|
Avec ce jeton (*token*), on peut interroger le serveur:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
http GET http://localhost:5000/ScoDoc/api/departements "Authorization:Bearer jS7iVlH1234cRDzboAfO5xseE0Ain6Zyz"
|
||||||
|
```
|
||||||
|
|
||||||
|
qui affiche par exemple:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 151
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 05 May 2022 05:21:33 GMT
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"acronym": "TAPI",
|
||||||
|
"date_creation": "Wed, 04 May 2022 21:09:25 GMT",
|
||||||
|
"description": null,
|
||||||
|
"id": 1,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fonctions d'API ScoDoc 9
|
||||||
|
|
||||||
|
La documentation ci-dessous concerne la nouvelle API, disponible à partir de la
|
||||||
|
version de ScoDoc 9.3.25.
|
||||||
|
|
||||||
|
### Accès à l'API REST
|
||||||
|
|
||||||
|
L'API est accessible à l'adresse:
|
||||||
|
`https://scodoc.monsite.tld/ScoDoc/api/<fonction>`, et aussi via les *routes
|
||||||
|
départementales* de la forme
|
||||||
|
`https://scodoc.monsite.tld/ScoDoc/<dept_acronyme>/api/<fonction>` pour un accès
|
||||||
|
avec des droits restreints au département indiqué. La liste des `<fonctions>` est
|
||||||
|
donnée ci-dessous dans [Référence](#reference).
|
||||||
|
|
||||||
|
#### Authentification
|
||||||
|
|
||||||
|
Lors de votre authentification (*connexion avec login et mot de passe*) à Scodoc, il
|
||||||
|
vous sera attribué un jeton (token jwt *généré automatiquement*) vous permettant
|
||||||
|
d'utiliser l'api suivant les droits correspondant à votre session.
|
||||||
|
|
||||||
|
Pour obtenir le jeton, il faut un compte sur ScoDoc (`user_name`et `password`).
|
||||||
|
Les autorisations et rôles sont gérés exactement comme pour l'application.
|
||||||
|
|
||||||
|
Exemple avec `curl` (un outil en ligne de commande présent sur la plupart des
|
||||||
|
systèmes, voir plus haut pour la même chose avec la commande `http`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -u user_name:password --request POST https://SERVEUR/ScoDoc/api/tokens
|
||||||
|
```
|
||||||
|
|
||||||
|
où `SERVEUR` est l'adresse (IP ou nom) de votre serveur.
|
||||||
|
La réponse doit ressembler à ceci:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "LuXXxk+i74TXYZZl8MulgbiCGmVHXXX"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous trouverez dans `/opt/scodoc/tests/api/exemple-api-basic.py` un exemple
|
||||||
|
complet en python d'interrogation de l'API.
|
||||||
|
|
||||||
|
#### Codes HTTP
|
||||||
|
|
||||||
|
Chaque appel à l'API donne lieu à une réponse retournant un code spécifique en
|
||||||
|
fonction du résultat obtenu. L'analyse de ce code vous permet de vous assurer
|
||||||
|
que la requête a été traitée avec succès.
|
||||||
|
|
||||||
|
Tous les codes >= 400 indiquent que la requête n'a pas été traitée avec succès
|
||||||
|
par le serveur ScoDoc.
|
||||||
|
|
||||||
|
* [200](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/200) : OK.
|
||||||
|
* [401](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/401) : Authentification nécessaire. (jeton non précisé ou invalide)
|
||||||
|
* [403](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/403) : Action
|
||||||
|
non autorisée pour l'utilisateur associé au jeton.
|
||||||
|
* [404](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/401) : Adresse
|
||||||
|
incorrecte, paramètre manquant ou invalide, ou objet inexistant.
|
||||||
|
* [500](https://developer.mozilla.org/fr/docs/Web/HTTP/Status/500) : Erreur
|
||||||
|
inconnue côté serveur.
|
||||||
|
|
||||||
|
## Règles générales
|
||||||
|
|
||||||
|
* une route s'écrit comme une suite de noms et d'identifiants;
|
||||||
|
* les noms token, département, formation, formsemestre, groupe, etudiant,
|
||||||
|
bulletin, absence, logo, programme, évaluation, résultat, décision désignent
|
||||||
|
des types d'objets;
|
||||||
|
* les noms (verbes ou groupes verbaux): set_etudiant, remove_etudiant, query,
|
||||||
|
create, delete, edit, order sont des actions;
|
||||||
|
* les noms restants (ids, courants, long, ...) sont des options, les autres noms
|
||||||
|
sont des options ou des actions;
|
||||||
|
* le dernier nom apparaissant sur une route donne le type d'objet renvoyé. Ce
|
||||||
|
nom peut apparaître au singulier ou au pluriel.
|
||||||
|
* au singulier un seul objet est renvoyé, si aucun objet n'est trouvé, retourne un 404;
|
||||||
|
* au pluriel une collection d'objets est renvoyée, si aucun objet n'est
|
||||||
|
trouvé, retourne une collection vide.
|
||||||
|
* un type d'objet au singulier est généralement suivi immédiatement de son
|
||||||
|
identifiant (unique). Exception: pour un étudiant, on peut également utiliser
|
||||||
|
le NIP ou l'INE (qui ne sont pas uniques dans la base car un étudiant de même
|
||||||
|
INE/NIP peut passer par plusieurs départements).
|
||||||
|
|
||||||
|
## Référence
|
||||||
|
|
||||||
|
La [carte syntaxique](#carte-syntaxique) vous permet de retrouver une entrée à
|
||||||
|
partir de sa syntaxe (le `?` amène sur la documentation associée).
|
||||||
|
|
||||||
|
Le [tableau récapitulatif](#tableau-recapitulatif-des-entrees-de-lapi) vous
|
||||||
|
permet de rechercher une entrée à partir du résultat attendu.
|
||||||
|
|
||||||
|
### Carte syntaxique
|
||||||
|
|
||||||
|
<div style="overflow: scroll;">
|
||||||
|
<div style="width: 1200px;">
|
||||||
|
![carte_syntaxique](img/API_Chart.svg)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
(carte générée avec `flask gen-api-doc`)
|
||||||
|
|
||||||
|
### Tableau récapitulatif des entrées de l'API
|
||||||
|
|
||||||
|
{{table_api|safe}}
|
||||||
|
|
||||||
|
(table générée avec `flask gen-api-doc`)
|
||||||
|
|
||||||
|
#### Note sur les exemples d'utilisation
|
||||||
|
|
||||||
|
Pour uniformiser les résultats des exemples, ceux sont soumis à quelques post-traitements non réalisés par l'API.
|
||||||
|
|
||||||
|
- les clés sont triées (ce n'est pas toujours garanti);
|
||||||
|
- les listes de plus de 2 éléments sont tronquées à 2 éléments, la fin de la liste étant
|
||||||
|
représentée par la notation en json '...';
|
||||||
|
- les dates (au format ISO) sont systématiquement remplacées par une date fixe et ne sont pas réalistes.
|
||||||
|
|
||||||
|
{{doc_api|safe}}
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
### En savoir plus
|
||||||
|
|
||||||
|
Voir exemples d'utilisation de l'API en Python, dans `tests/api/`.
|
||||||
|
|
||||||
|
|
||||||
|
!!! note "Voir aussi"
|
||||||
|
|
||||||
|
- [Guide configuration et ligne de commande](GuideConfig.md)
|
||||||
|
- [Guide administrateur ScoDoc](GuideAdminSys.md)
|
||||||
|
- [ServicesXml](ServicesXml.md) : anciens web services XML (obsolète)
|
||||||
|
- [FAQ](FAQ.md)
|
||||||
|
- [Contacts](Contact.md)
|
32
app/templates/doc/apidoc.j2
Normal file
32
app/templates/doc/apidoc.j2
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{# Template pour la doc mardown d'un point d'entrée de l'API #}
|
||||||
|
#### **`{{doc.nom}}`**
|
||||||
|
|
||||||
|
{% if doc.routes %}
|
||||||
|
{% if doc.routes|length == 1 %}
|
||||||
|
* **Route:** `{{doc.routes[0]|safe}}`
|
||||||
|
{% else %}
|
||||||
|
* **Routes:**
|
||||||
|
{% for route in doc.routes %}
|
||||||
|
* `{{route|safe}}`
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
* **Méthode:** `{{doc.method}}`
|
||||||
|
* **Permission:** `{{doc.permission}}`
|
||||||
|
{% if doc.params %}
|
||||||
|
* **Paramètres:**
|
||||||
|
{% for param in doc.params %}
|
||||||
|
* `{{param.nom|safe}}` : {{param.description|safe}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if doc.description %}
|
||||||
|
* **Description:** {{doc.description|safe}}
|
||||||
|
{% endif %}
|
||||||
|
{% if doc.data %}
|
||||||
|
* **Data:** {{doc.data|safe}}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if doc.sample %}
|
||||||
|
* **Exemple de résultat:** [{{doc.sample.nom}}](./samples/sample_{{doc.sample.href}})
|
||||||
|
{% else %}
|
||||||
|
{% endif %}
|
10
scodoc.py
10
scodoc.py
@ -744,10 +744,10 @@ def generate_ens_calendars(): # generate-ens-calendars
|
|||||||
@click.option(
|
@click.option(
|
||||||
"-e",
|
"-e",
|
||||||
"--endpoint",
|
"--endpoint",
|
||||||
default="api",
|
default="api.",
|
||||||
help="Endpoint à partir duquel générer la carte des routes",
|
help="Endpoint à partir duquel générer la documentation des routes",
|
||||||
)
|
)
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def gen_api_map(endpoint):
|
def gen_api_doc(endpoint): # gen-api-map
|
||||||
"""Génère la carte des routes de l'API."""
|
"""Génère la documentation des routes de l'API."""
|
||||||
tools.gen_api_map(app, endpoint_start=endpoint)
|
tools.gen_api_doc(app, endpoint_start=endpoint)
|
||||||
|
40
tests/api/api_shell.py
Normal file
40
tests/api/api_shell.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""Script pour tester l'API en mode interactif
|
||||||
|
|
||||||
|
Utilisation:
|
||||||
|
```py
|
||||||
|
python -i tests/api/api_shell.py
|
||||||
|
```
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pdb
|
||||||
|
from pprint import pprint as pp
|
||||||
|
|
||||||
|
from setup_test_api import (
|
||||||
|
API_PASSWORD,
|
||||||
|
API_URL,
|
||||||
|
API_USER,
|
||||||
|
APIError,
|
||||||
|
CHECK_CERTIFICATE,
|
||||||
|
get_auth_headers,
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
SCODOC_URL,
|
||||||
|
set_headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
set_headers(get_auth_headers("admin_api", "admin_api"))
|
||||||
|
|
||||||
|
print(
|
||||||
|
"""
|
||||||
|
Connecté au serveur ScoDoc. Vous pouvez utiliser:
|
||||||
|
GET( route )
|
||||||
|
POST( route, data )
|
||||||
|
Exemple avec pretty print:
|
||||||
|
pp(GET("/departements")[0])
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
pp(GET("/departements")[0])
|
@ -30,7 +30,7 @@ from setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
SCODOC_URL,
|
SCODOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
SCODOC_URL,
|
SCODOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ from setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
SCODOC_URL,
|
SCODOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -127,14 +127,12 @@ group_id = 5315
|
|||||||
POST(f"/group/{group_id}/set_etudiant/{etudid}", headers=HEADERS)
|
POST(f"/group/{group_id}/set_etudiant/{etudid}", headers=HEADERS)
|
||||||
|
|
||||||
|
|
||||||
POST_JSON(
|
POST(f"/partition/{pid}/group/create", data={"group_name": "Omega10"}, headers=HEADERS)
|
||||||
f"/partition/{pid}/group/create", data={"group_name": "Omega10"}, headers=HEADERS
|
|
||||||
)
|
|
||||||
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=HEADERS)
|
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=HEADERS)
|
||||||
pp(partitions)
|
pp(partitions)
|
||||||
|
|
||||||
POST_JSON(f"/group/5559/delete", headers=HEADERS)
|
POST(f"/group/5559/delete", headers=HEADERS)
|
||||||
POST_JSON(f"/group/5327/edit", data={"group_name": "TDXXX"}, headers=HEADERS)
|
POST(f"/group/5327/edit", data={"group_name": "TDXXX"}, headers=HEADERS)
|
||||||
|
|
||||||
# --------- Toutes les bulletins, un à un, et les décisions de jury d'un semestre
|
# --------- Toutes les bulletins, un à un, et les décisions de jury d'un semestre
|
||||||
formsemestre_id = 911
|
formsemestre_id = 911
|
||||||
@ -178,19 +176,19 @@ etud = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=HEADERS)[10]
|
|||||||
etudid = etud["id"]
|
etudid = etud["id"]
|
||||||
|
|
||||||
# 1- Crée une partition, puis la change de nom
|
# 1- Crée une partition, puis la change de nom
|
||||||
js = POST_JSON(
|
js = POST(
|
||||||
f"/formsemestre/{formsemestre_id}/partition/create",
|
f"/formsemestre/{formsemestre_id}/partition/create",
|
||||||
data={"partition_name": "PART"},
|
data={"partition_name": "PART"},
|
||||||
)
|
)
|
||||||
partition_id = js["id"]
|
partition_id = js["id"]
|
||||||
POST_JSON(
|
POST(
|
||||||
f"/partition/{partition_id}/edit",
|
f"/partition/{partition_id}/edit",
|
||||||
data={"partition_name": "PART1", "show_in_lists": True},
|
data={"partition_name": "PART1", "show_in_lists": True},
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2- Crée un groupe
|
# 2- Crée un groupe
|
||||||
js = POST_JSON(
|
js = POST(
|
||||||
f"/partition/{partition_id}/group/create",
|
f"/partition/{partition_id}/group/create",
|
||||||
data={"group_name": "G1"},
|
data={"group_name": "G1"},
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
@ -198,28 +196,28 @@ js = POST_JSON(
|
|||||||
group_1 = js["id"]
|
group_1 = js["id"]
|
||||||
|
|
||||||
# 3- Crée deux autres groupes
|
# 3- Crée deux autres groupes
|
||||||
js = POST_JSON(
|
js = POST(
|
||||||
f"/partition/{partition_id}/group/create",
|
f"/partition/{partition_id}/group/create",
|
||||||
data={"group_name": "G2"},
|
data={"group_name": "G2"},
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
)
|
)
|
||||||
js = POST_JSON(
|
js = POST(
|
||||||
f"/partition/{partition_id}/group/create",
|
f"/partition/{partition_id}/group/create",
|
||||||
data={"group_name": "G3"},
|
data={"group_name": "G3"},
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4- Affecte étudiant au groupe G1
|
# 4- Affecte étudiant au groupe G1
|
||||||
POST_JSON(f"/group/{group_1}/set_etudiant/{etudid}", headers=HEADERS)
|
POST(f"/group/{group_1}/set_etudiant/{etudid}", headers=HEADERS)
|
||||||
|
|
||||||
# 5- retire du groupe
|
# 5- retire du groupe
|
||||||
POST_JSON(f"/group/{group_1}/remove_etudiant/{etudid}", headers=HEADERS)
|
POST(f"/group/{group_1}/remove_etudiant/{etudid}", headers=HEADERS)
|
||||||
|
|
||||||
# 6- affecte au groupe G2
|
# 6- affecte au groupe G2
|
||||||
partition = GET(f"/partition/{partition_id}")
|
partition = GET(f"/partition/{partition_id}")
|
||||||
assert len(partition["groups"]) == 3
|
assert len(partition["groups"]) == 3
|
||||||
group_2 = [g for g in partition["groups"].values() if g["group_name"] == "G2"][0]["id"]
|
group_2 = [g for g in partition["groups"].values() if g["group_name"] == "G2"][0]["id"]
|
||||||
POST_JSON(f"/group/{group_2}/set_etudiant/{etudid}", headers=HEADERS)
|
POST(f"/group/{group_2}/set_etudiant/{etudid}", headers=HEADERS)
|
||||||
|
|
||||||
# 7- Membres du groupe
|
# 7- Membres du groupe
|
||||||
etuds_g2 = GET(f"/group/{group_2}/etudiants", headers=HEADERS)
|
etuds_g2 = GET(f"/group/{group_2}/etudiants", headers=HEADERS)
|
||||||
@ -229,7 +227,7 @@ assert etuds_g2[0]["id"] == etudid
|
|||||||
# 8- Ordres des groupes
|
# 8- Ordres des groupes
|
||||||
group_3 = [g for g in partition["groups"].values() if g["group_name"] == "G3"][0]["id"]
|
group_3 = [g for g in partition["groups"].values() if g["group_name"] == "G3"][0]["id"]
|
||||||
|
|
||||||
POST_JSON(
|
POST(
|
||||||
f"/partition/{partition_id}/groups/order",
|
f"/partition/{partition_id}/groups/order",
|
||||||
data=[group_2, group_1, group_3],
|
data=[group_2, group_1, group_3],
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
@ -242,7 +240,7 @@ new_groups = [
|
|||||||
assert new_groups == [group_2, group_1, group_3]
|
assert new_groups == [group_2, group_1, group_3]
|
||||||
|
|
||||||
# 9- Suppression
|
# 9- Suppression
|
||||||
POST_JSON(f"/partition/{partition_id}/delete")
|
POST(f"/partition/{partition_id}/delete")
|
||||||
# ------
|
# ------
|
||||||
|
|
||||||
# Tests accès API:
|
# Tests accès API:
|
||||||
@ -260,13 +258,13 @@ POST_JSON(f"/partition/{partition_id}/delete")
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#
|
#
|
||||||
POST_JSON(
|
POST(
|
||||||
"/partition/2264/groups/order",
|
"/partition/2264/groups/order",
|
||||||
data=[5563, 5562, 5561, 5560, 5558, 5557, 5316, 5315],
|
data=[5563, 5562, 5561, 5560, 5558, 5557, 5316, 5315],
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
)
|
)
|
||||||
|
|
||||||
POST_JSON(
|
POST(
|
||||||
"/formsemestre/1063/partitions/order",
|
"/formsemestre/1063/partitions/order",
|
||||||
data=[2264, 2263, 2265, 2266, 2267, 2372, 2378],
|
data=[2264, 2263, 2265, 2266, 2267, 2372, 2378],
|
||||||
headers=HEADERS,
|
headers=HEADERS,
|
||||||
|
@ -41,7 +41,7 @@ from setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
SCODOC_URL,
|
SCODOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,170 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- mode: python -*-
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# OBSOLETE - NE PLUS UTILISER CETTE API
|
|
||||||
# VOIR https://scodoc.org/ScoDoc9API/
|
|
||||||
|
|
||||||
"""Exemple connexion sur ScoDoc 9 et utilisation de l'ancienne API ScoDoc 7
|
|
||||||
à la mode "PHP": les gens passaient directement __ac_name et __ac_password
|
|
||||||
dans chaque requête, en POST ou en GET.
|
|
||||||
|
|
||||||
Cela n'a jamais été documenté mais était implicitement supporté. C'est "deprecated"
|
|
||||||
et ne sera plus supporté à partir de juillet 2022.
|
|
||||||
|
|
||||||
Ce script va tester:
|
|
||||||
- Liste semestres
|
|
||||||
- Liste modules
|
|
||||||
- Creation d'une évaluation
|
|
||||||
- Saisie d'une note
|
|
||||||
|
|
||||||
Utilisation: créer les variables d'environnement: (indiquer les valeurs
|
|
||||||
pour le serveur ScoDoc que vous voulez interroger)
|
|
||||||
|
|
||||||
export SCODOC_URL="https://scodoc.xxx.net/"
|
|
||||||
export SCODOC_USER="xxx"
|
|
||||||
export SCODOC_PASSWD="xxx"
|
|
||||||
export CHECK_CERTIFICATE=0 # ou 1 si serveur de production avec certif SSL valide
|
|
||||||
|
|
||||||
(on peut aussi placer ces valeurs dans un fichier .env du répertoire tests/api).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import pdb
|
|
||||||
import requests
|
|
||||||
import urllib3
|
|
||||||
from pprint import pprint as pp
|
|
||||||
|
|
||||||
# --- Lecture configuration (variables d'env ou .env)
|
|
||||||
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
load_dotenv(os.path.join(BASEDIR, ".env"))
|
|
||||||
CHECK_CERTIFICATE = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
|
||||||
SCODOC_URL = os.environ["SCODOC_URL"]
|
|
||||||
SCODOC_DEPT = os.environ["SCODOC_DEPT"]
|
|
||||||
DEPT_URL = SCODOC_URL + "/ScoDoc/" + SCODOC_DEPT + "/Scolarite"
|
|
||||||
SCODOC_USER = os.environ["SCODOC_USER"]
|
|
||||||
SCODOC_PASSWORD = os.environ["SCODOC_PASSWD"]
|
|
||||||
print(f"SCODOC_URL={SCODOC_URL}")
|
|
||||||
|
|
||||||
# ---
|
|
||||||
if not CHECK_CERTIFICATE:
|
|
||||||
urllib3.disable_warnings()
|
|
||||||
|
|
||||||
|
|
||||||
class ScoError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def GET(path: str, params=None, errmsg=None):
|
|
||||||
"""Get and returns as JSON"""
|
|
||||||
# ajoute auth
|
|
||||||
params["__ac_name"] = SCODOC_USER
|
|
||||||
params["__ac_password"] = SCODOC_PASSWORD
|
|
||||||
r = requests.get(
|
|
||||||
DEPT_URL + "/" + path, params=params, verify=CHECK_CERTIFICATE, timeout=10
|
|
||||||
)
|
|
||||||
if r.status_code != 200:
|
|
||||||
raise ScoError(errmsg or "erreur !")
|
|
||||||
return r.json() # decode la reponse JSON
|
|
||||||
|
|
||||||
|
|
||||||
def POST(path: str, data: dict, errmsg=None):
|
|
||||||
"""Post"""
|
|
||||||
data["__ac_name"] = data.get("__ac_name", SCODOC_USER)
|
|
||||||
data["__ac_password"] = data.get("__ac_password", SCODOC_PASSWORD)
|
|
||||||
r = requests.post(
|
|
||||||
DEPT_URL + "/" + path, data=data, verify=CHECK_CERTIFICATE, timeout=10
|
|
||||||
)
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
# ---
|
|
||||||
# pas besoin d'ouvrir une session, on y va directement:
|
|
||||||
|
|
||||||
# --- Recupere la liste de tous les semestres:
|
|
||||||
sems = GET("Notes/formsemestre_list", params={"fmt": "json"})
|
|
||||||
|
|
||||||
# sems est une liste de semestres (dictionnaires)
|
|
||||||
for sem in sems:
|
|
||||||
if sem["etat"]:
|
|
||||||
break
|
|
||||||
|
|
||||||
if sem["etat"] == "0":
|
|
||||||
raise ScoError("Aucun semestre non verrouillé !")
|
|
||||||
|
|
||||||
# Affiche le semestre trouvé:
|
|
||||||
pp(sem)
|
|
||||||
|
|
||||||
# Liste des étudiants dans le 1er semestre non verrouillé:
|
|
||||||
group_list = GET(
|
|
||||||
"groups_view",
|
|
||||||
params={
|
|
||||||
"formsemestre_id": sem["formsemestre_id"],
|
|
||||||
"with_codes": 1,
|
|
||||||
"fmt": "json",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if not group_list:
|
|
||||||
# config inadaptée pour les tests...
|
|
||||||
raise ScoError("aucun étudiant inscrit dans le semestre")
|
|
||||||
|
|
||||||
etud = group_list[0] # le premier étudiant inscrit ici
|
|
||||||
# test un POST
|
|
||||||
r = POST(
|
|
||||||
"Absences/AddBilletAbsence",
|
|
||||||
{
|
|
||||||
"begin": "2021-10-25",
|
|
||||||
"end": "2021-10-26",
|
|
||||||
"description": "test API scodoc7",
|
|
||||||
"etudid": etud["etudid"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
assert r.text.startswith('<?xml version="1.0" encoding="utf-8"?>')
|
|
||||||
assert "billet_id" in r.text
|
|
||||||
# Essai avec un compte invalide
|
|
||||||
r_invalid = POST(
|
|
||||||
"Absences/AddBilletAbsence",
|
|
||||||
{
|
|
||||||
"__ac_name": "xxx",
|
|
||||||
"begin": "2021-10-25",
|
|
||||||
"end": "2021-10-26",
|
|
||||||
"description": "test API scodoc7",
|
|
||||||
"etudid": etud["etudid"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert r_invalid.status_code == 403 # compte invalide => not authorized
|
|
||||||
|
|
||||||
# AddBilletAbsence en json
|
|
||||||
r = POST(
|
|
||||||
"Absences/AddBilletAbsence",
|
|
||||||
{
|
|
||||||
"begin": "2021-10-25",
|
|
||||||
"end": "2021-10-26",
|
|
||||||
"description": "test API scodoc7",
|
|
||||||
"etudid": etud["etudid"],
|
|
||||||
"xml_reply": 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
assert isinstance(json.loads(r.text)[0]["billet_id"], int)
|
|
||||||
|
|
||||||
# Les fonctions ci-dessous ne fonctionnent plus en ScoDoc 9
|
|
||||||
# Voir https://scodoc.org/git/viennet/ScoDoc/issues/149
|
|
||||||
|
|
||||||
# # ---- Liste les modules et prend le premier
|
|
||||||
# mods = GET("/Notes/moduleimpl_list", params={"formsemestre_id": sem["formsemestre_id"]})
|
|
||||||
# print(f"{len(mods)} modules dans le semestre {sem['titre']}")
|
|
||||||
|
|
||||||
# mod = mods[0]
|
|
||||||
|
|
||||||
# # ---- Etudiants inscrits dans ce module
|
|
||||||
# inscrits = GET(
|
|
||||||
# "Notes/do_moduleimpl_inscription_list",
|
|
||||||
# params={"moduleimpl_id": mod["moduleimpl_id"]},
|
|
||||||
# )
|
|
||||||
# print(f"{len(inscrits)} inscrits dans ce module")
|
|
||||||
# # prend le premier inscrit, au hasard:
|
|
||||||
# etudid = inscrits[0]["etudid"]
|
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
Si entry_names est spécifié, la génération est restreinte aux exemples cités.
|
Si entry_names est spécifié, la génération est restreinte aux exemples cités.
|
||||||
Exemple:
|
Exemple:
|
||||||
python make_samples departements departement-formsemestres
|
python make_samples departements departement_formsemestres
|
||||||
|
|
||||||
Doit être exécutée immédiatement apres une initialisation de la base pour test API!
|
Doit être exécutée immédiatement apres une initialisation de la base pour test API!
|
||||||
(car dépendant des identifiants générés lors de la création des objets)
|
(car dépendant des identifiants générés lors de la création des objets)
|
||||||
@ -71,7 +71,7 @@ from setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
SCODOC_URL,
|
SCODOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -98,18 +98,23 @@ class Sample:
|
|||||||
elif permission == "UsersAdmin":
|
elif permission == "UsersAdmin":
|
||||||
HEADERS = get_auth_headers("admin_api", "admin_api")
|
HEADERS = get_auth_headers("admin_api", "admin_api")
|
||||||
else:
|
else:
|
||||||
raise SampleException(f"Bad permission : {permission}")
|
raise SampleException(f"Bad permission : {permission}, url={self.url}")
|
||||||
if self.method == "GET":
|
if self.method == "GET":
|
||||||
self.result = GET(self.url, HEADERS)
|
self.result = GET(self.url, HEADERS)
|
||||||
elif self.method == "POST":
|
elif self.method == "POST":
|
||||||
if self.content == "":
|
if self.content == "":
|
||||||
self.result = POST_JSON(self.url, headers=HEADERS)
|
self.result = POST(self.url, headers=HEADERS)
|
||||||
else:
|
else:
|
||||||
HEADERS["Content-Type"] = "application/json ; charset=utf-8"
|
HEADERS["Content-Type"] = "application/json ; charset=utf-8"
|
||||||
self.result = POST_JSON(self.url, json.loads(self.content), HEADERS)
|
try:
|
||||||
|
data = json.loads(self.content)
|
||||||
|
except json.decoder.JSONDecodeError as exc:
|
||||||
|
raise ValueError(
|
||||||
|
f"JSON invalide: {self.content}\nurl={self.url}"
|
||||||
|
) from exc
|
||||||
|
self.result = POST(self.url, data, HEADERS)
|
||||||
elif self.method[0] != "#":
|
elif self.method[0] != "#":
|
||||||
error = f'Bad method : "{self.method}"'
|
raise SampleException(f'Bad method : "{self.method}", url={self.url}')
|
||||||
raise SampleException(error)
|
|
||||||
self.shorten()
|
self.shorten()
|
||||||
with open("sample_TEST.json.md", "tw", encoding="utf-8") as f:
|
with open("sample_TEST.json.md", "tw", encoding="utf-8") as f:
|
||||||
self.dump(f)
|
self.dump(f)
|
||||||
@ -235,3 +240,5 @@ if not CHECK_CERTIFICATE:
|
|||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
|
|
||||||
make_samples(SAMPLES_FILENAME)
|
make_samples(SAMPLES_FILENAME)
|
||||||
|
|
||||||
|
print(f"Fichiers samples générés dans {DATA_DIR}")
|
||||||
|
@ -79,6 +79,16 @@ if pytest:
|
|||||||
return get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
|
return get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
|
||||||
|
|
||||||
|
|
||||||
|
class _DefaultHeaders:
|
||||||
|
headers = {}
|
||||||
|
|
||||||
|
|
||||||
|
def set_headers(headers: dict):
|
||||||
|
"""Set default headers"""
|
||||||
|
print(f"set_headers: {headers}")
|
||||||
|
_DefaultHeaders.headers = headers
|
||||||
|
|
||||||
|
|
||||||
def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
|
def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
|
||||||
"""Get and optionaly returns as JSON
|
"""Get and optionaly returns as JSON
|
||||||
Special case for non json result (image or pdf):
|
Special case for non json result (image or pdf):
|
||||||
@ -91,7 +101,7 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
|
|||||||
url = API_URL + path
|
url = API_URL + path
|
||||||
reply = requests.get(
|
reply = requests.get(
|
||||||
url,
|
url,
|
||||||
headers=headers or {},
|
headers=_DefaultHeaders.headers if headers is None else headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
timeout=SCO_TEST_API_TIMEOUT,
|
timeout=SCO_TEST_API_TIMEOUT,
|
||||||
)
|
)
|
||||||
@ -119,7 +129,7 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None, raw=False):
|
|||||||
raise APIError("Unknown returned content {r.headers.get('Content-Type', None} !\n")
|
raise APIError("Unknown returned content {r.headers.get('Content-Type', None} !\n")
|
||||||
|
|
||||||
|
|
||||||
def POST_JSON(
|
def POST(
|
||||||
path: str, data: dict = {}, headers: dict = None, errmsg=None, dept=None, raw=False
|
path: str, data: dict = {}, headers: dict = None, errmsg=None, dept=None, raw=False
|
||||||
):
|
):
|
||||||
"""Post
|
"""Post
|
||||||
@ -132,7 +142,7 @@ def POST_JSON(
|
|||||||
r = requests.post(
|
r = requests.post(
|
||||||
url,
|
url,
|
||||||
json=data,
|
json=data,
|
||||||
headers=headers or {},
|
headers=_DefaultHeaders.headers if headers is None else headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
timeout=SCO_TEST_API_TIMEOUT,
|
timeout=SCO_TEST_API_TIMEOUT,
|
||||||
)
|
)
|
||||||
@ -200,7 +210,7 @@ def check_failure_post(path: str, headers: dict, data: dict, err: str = None):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = POST_JSON(path=path, headers=headers, data=data, dept=DEPT_ACRONYM)
|
data = POST(path=path, headers=headers, data=data, dept=DEPT_ACRONYM)
|
||||||
# ^ Renvoie un 404
|
# ^ Renvoie un 404
|
||||||
except APIError as api_err:
|
except APIError as api_err:
|
||||||
if err is not None:
|
if err is not None:
|
||||||
|
@ -10,7 +10,7 @@ from types import NoneType
|
|||||||
|
|
||||||
from tests.api.setup_test_api import (
|
from tests.api.setup_test_api import (
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
DEPT_ACRONYM,
|
DEPT_ACRONYM,
|
||||||
APIError,
|
APIError,
|
||||||
api_headers,
|
api_headers,
|
||||||
@ -260,7 +260,7 @@ def test_route_create(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
data = create_data("present", "03")
|
data = create_data("present", "03")
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create", [data], api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{ETUDID}/create", [data], api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -275,7 +275,7 @@ def test_route_create(api_admin_headers):
|
|||||||
check_fields(data, fields=ASSIDUITES_FIELDS)
|
check_fields(data, fields=ASSIDUITES_FIELDS)
|
||||||
|
|
||||||
data2 = create_data("absent", "04", MODULE, "desc")
|
data2 = create_data("absent", "04", MODULE, "desc")
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create", [data2], api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{ETUDID}/create", [data2], api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -286,7 +286,7 @@ def test_route_create(api_admin_headers):
|
|||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
check_failure_post(f"/assiduite/{FAUX}/create", api_admin_headers, [data])
|
check_failure_post(f"/assiduite/{FAUX}/create", api_admin_headers, [data])
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create", [data], api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{ETUDID}/create", [data], api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -296,7 +296,7 @@ def test_route_create(api_admin_headers):
|
|||||||
== "Duplication: la période rentre en conflit avec une plage enregistrée"
|
== "Duplication: la période rentre en conflit avec une plage enregistrée"
|
||||||
)
|
)
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create",
|
f"/assiduite/{ETUDID}/create",
|
||||||
[create_data("absent", "05", FAUX)],
|
[create_data("absent", "05", FAUX)],
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -316,7 +316,7 @@ def test_route_create(api_admin_headers):
|
|||||||
for d in range(randint(2, 4))
|
for d in range(randint(2, 4))
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create", data, api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{ETUDID}/create", data, api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -334,7 +334,7 @@ def test_route_create(api_admin_headers):
|
|||||||
create_data("absent", "01"),
|
create_data("absent", "01"),
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{ETUDID}/create", data2, api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{ETUDID}/create", data2, api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -359,13 +359,13 @@ def test_route_edit(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
|
|
||||||
data = {"etat": "retard", "moduleimpl_id": MODULE}
|
data = {"etat": "retard", "moduleimpl_id": MODULE}
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{TO_REMOVE[0]}/edit", data, api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{TO_REMOVE[0]}/edit", data, api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
assert res == {"OK": True}
|
assert res == {"OK": True}
|
||||||
|
|
||||||
data["moduleimpl_id"] = None
|
data["moduleimpl_id"] = None
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/assiduite/{TO_REMOVE[1]}/edit", data, api_admin_headers, dept=DEPT_ACRONYM
|
f"/assiduite/{TO_REMOVE[1]}/edit", data, api_admin_headers, dept=DEPT_ACRONYM
|
||||||
)
|
)
|
||||||
assert res == {"OK": True}
|
assert res == {"OK": True}
|
||||||
@ -389,13 +389,13 @@ def test_route_delete(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
data = TO_REMOVE[0]
|
data = TO_REMOVE[0]
|
||||||
|
|
||||||
res = POST_JSON("/assiduite/delete", [data], api_admin_headers, dept=DEPT_ACRONYM)
|
res = POST("/assiduite/delete", [data], api_admin_headers, dept=DEPT_ACRONYM)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
for dat in res["success"]:
|
for dat in res["success"]:
|
||||||
assert dat["message"] == "OK"
|
assert dat["message"] == "OK"
|
||||||
|
|
||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
res = POST_JSON("/assiduite/delete", [data], api_admin_headers, dept=DEPT_ACRONYM)
|
res = POST("/assiduite/delete", [data], api_admin_headers, dept=DEPT_ACRONYM)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["errors"]) == 1
|
assert len(res["errors"]) == 1
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ def test_route_delete(api_admin_headers):
|
|||||||
|
|
||||||
data = TO_REMOVE[1:]
|
data = TO_REMOVE[1:]
|
||||||
|
|
||||||
res = POST_JSON("/assiduite/delete", data, api_admin_headers, dept=DEPT_ACRONYM)
|
res = POST("/assiduite/delete", data, api_admin_headers, dept=DEPT_ACRONYM)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
for dat in res["success"]:
|
for dat in res["success"]:
|
||||||
assert dat["message"] == "OK"
|
assert dat["message"] == "OK"
|
||||||
@ -418,7 +418,7 @@ def test_route_delete(api_admin_headers):
|
|||||||
FAUX + 2,
|
FAUX + 2,
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON("/assiduite/delete", data2, api_admin_headers, dept=DEPT_ACRONYM)
|
res = POST("/assiduite/delete", data2, api_admin_headers, dept=DEPT_ACRONYM)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["errors"]) == 3
|
assert len(res["errors"]) == 3
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ Utilisation :
|
|||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import GET, POST_JSON, api_headers
|
from tests.api.setup_test_api import GET, POST, api_headers
|
||||||
|
|
||||||
ETUDID = 1
|
ETUDID = 1
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ def test_billets(api_headers):
|
|||||||
abs_end="2022-08-01",
|
abs_end="2022-08-01",
|
||||||
description="test 1",
|
description="test 1",
|
||||||
)
|
)
|
||||||
billet_r = POST_JSON("/billets_absence/create", billet_d, headers=api_headers)
|
billet_r = POST("/billets_absence/create", billet_d, headers=api_headers)
|
||||||
assert billet_r["etudid"] == billet_d["etudid"]
|
assert billet_r["etudid"] == billet_d["etudid"]
|
||||||
assert datetime.datetime.fromisoformat(billet_r["abs_begin"]).replace(
|
assert datetime.datetime.fromisoformat(billet_r["abs_begin"]).replace(
|
||||||
tzinfo=None
|
tzinfo=None
|
||||||
@ -43,12 +43,10 @@ def test_billets(api_headers):
|
|||||||
abs_end="2022-08-03",
|
abs_end="2022-08-03",
|
||||||
description="test 2",
|
description="test 2",
|
||||||
)
|
)
|
||||||
billet_r = POST_JSON("/billets_absence/create", billet_d2, headers=api_headers)
|
billet_r = POST("/billets_absence/create", billet_d2, headers=api_headers)
|
||||||
billets = GET("/billets_absence/etudiant/1", headers=api_headers)
|
billets = GET("/billets_absence/etudiant/1", headers=api_headers)
|
||||||
assert len(billets) == 2
|
assert len(billets) == 2
|
||||||
# Suppression
|
# Suppression
|
||||||
for billet in billets:
|
for billet in billets:
|
||||||
reply = POST_JSON(
|
reply = POST(f"/billets_absence/{billet['id']}/delete", headers=api_headers)
|
||||||
f"/billets_absence/{billet['id']}/delete", headers=api_headers
|
|
||||||
)
|
|
||||||
assert reply["OK"] == True
|
assert reply["OK"] == True
|
||||||
|
@ -23,7 +23,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
api_headers,
|
api_headers,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
)
|
)
|
||||||
@ -42,7 +42,7 @@ def test_create_dept(api_admin_headers):
|
|||||||
/departement/<string:dept_acronym>/edit
|
/departement/<string:dept_acronym>/edit
|
||||||
/departement/<string:dept_acronym>/delete
|
/departement/<string:dept_acronym>/delete
|
||||||
"""
|
"""
|
||||||
dept = POST_JSON(
|
dept = POST(
|
||||||
"/departement/create",
|
"/departement/create",
|
||||||
{"acronym": "XTEST", "visible": True},
|
{"acronym": "XTEST", "visible": True},
|
||||||
headers=api_admin_headers,
|
headers=api_admin_headers,
|
||||||
@ -50,14 +50,14 @@ def test_create_dept(api_admin_headers):
|
|||||||
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
|
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
|
||||||
assert dept["acronym"] == dept_r["acronym"]
|
assert dept["acronym"] == dept_r["acronym"]
|
||||||
assert dept_r["visible"] is True
|
assert dept_r["visible"] is True
|
||||||
dept_e = POST_JSON(
|
dept_e = POST(
|
||||||
f"/departement/{dept['acronym']}/edit",
|
f"/departement/{dept['acronym']}/edit",
|
||||||
{"visible": False},
|
{"visible": False},
|
||||||
headers=api_admin_headers,
|
headers=api_admin_headers,
|
||||||
)
|
)
|
||||||
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
|
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
|
||||||
assert dept_r["visible"] is False
|
assert dept_r["visible"] is False
|
||||||
r = POST_JSON(
|
r = POST(
|
||||||
f"/departement/{dept['acronym']}/delete",
|
f"/departement/{dept['acronym']}/delete",
|
||||||
headers=api_admin_headers,
|
headers=api_admin_headers,
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,7 @@ from tests.api.setup_test_api import (
|
|||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
DEPT_ACRONYM,
|
DEPT_ACRONYM,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
)
|
)
|
||||||
from tests.api.setup_test_api import api_headers # pylint: disable=unused-import
|
from tests.api.setup_test_api import api_headers # pylint: disable=unused-import
|
||||||
@ -262,7 +262,7 @@ def test_etudiants_by_name(api_headers):
|
|||||||
"dept": DEPT_ACRONYM,
|
"dept": DEPT_ACRONYM,
|
||||||
"civilite": "X",
|
"civilite": "X",
|
||||||
}
|
}
|
||||||
_ = POST_JSON(
|
_ = POST(
|
||||||
"/etudiant/create",
|
"/etudiant/create",
|
||||||
args,
|
args,
|
||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
@ -292,7 +292,7 @@ def test_etudiant_annotations(api_headers):
|
|||||||
"dept": DEPT_ACRONYM,
|
"dept": DEPT_ACRONYM,
|
||||||
"civilite": "M",
|
"civilite": "M",
|
||||||
}
|
}
|
||||||
etud = POST_JSON(
|
etud = POST(
|
||||||
"/etudiant/create",
|
"/etudiant/create",
|
||||||
args,
|
args,
|
||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
@ -304,7 +304,7 @@ def test_etudiant_annotations(api_headers):
|
|||||||
assert etud["nom"]
|
assert etud["nom"]
|
||||||
assert etud["annotations"] == []
|
assert etud["annotations"] == []
|
||||||
# ajoute annotation
|
# ajoute annotation
|
||||||
annotation = POST_JSON(
|
annotation = POST(
|
||||||
f"/etudiant/etudid/{etudid}/annotation",
|
f"/etudiant/etudid/{etudid}/annotation",
|
||||||
{"comment": "annotation 1"},
|
{"comment": "annotation 1"},
|
||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
@ -318,7 +318,7 @@ def test_etudiant_annotations(api_headers):
|
|||||||
assert etud["annotations"][0]["comment"] == "annotation 1"
|
assert etud["annotations"][0]["comment"] == "annotation 1"
|
||||||
assert etud["annotations"][0]["id"] == annotation_id
|
assert etud["annotations"][0]["id"] == annotation_id
|
||||||
# Supprime annotation
|
# Supprime annotation
|
||||||
POST_JSON(
|
POST(
|
||||||
f"/etudiant/etudid/{etudid}/annotation/{annotation_id}/delete",
|
f"/etudiant/etudid/{etudid}/annotation/{annotation_id}/delete",
|
||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
)
|
)
|
||||||
@ -932,7 +932,7 @@ def test_etudiant_bulletin_semestre(api_headers):
|
|||||||
|
|
||||||
### -------- Modifie publication bulletins
|
### -------- Modifie publication bulletins
|
||||||
admin_header = get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
|
admin_header = get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
|
||||||
formsemestre = POST_JSON(
|
formsemestre = POST(
|
||||||
f"/formsemestre/{1}/edit", {"bul_hide_xml": True}, headers=admin_header
|
f"/formsemestre/{1}/edit", {"bul_hide_xml": True}, headers=admin_header
|
||||||
)
|
)
|
||||||
assert formsemestre["bul_hide_xml"] is True
|
assert formsemestre["bul_hide_xml"] is True
|
||||||
@ -943,7 +943,7 @@ def test_etudiant_bulletin_semestre(api_headers):
|
|||||||
# /ScoDoc/api/etudiant/nip/12345/formsemestre/123/bulletin/long/pdf/nosi
|
# /ScoDoc/api/etudiant/nip/12345/formsemestre/123/bulletin/long/pdf/nosi
|
||||||
# TODO voir forme utilisée par ScoDoc en interne:
|
# TODO voir forme utilisée par ScoDoc en interne:
|
||||||
# formsemestre_bulletinetud?formsemestre_id=1263&etudid=16387
|
# formsemestre_bulletinetud?formsemestre_id=1263&etudid=16387
|
||||||
formsemestre = POST_JSON(
|
formsemestre = POST(
|
||||||
f"/formsemestre/{1}/edit", {"bul_hide_xml": False}, headers=admin_header
|
f"/formsemestre/{1}/edit", {"bul_hide_xml": False}, headers=admin_header
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1000,7 +1000,7 @@ def test_etudiant_create(api_headers):
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
etud = POST_JSON(
|
etud = POST(
|
||||||
"/etudiant/create",
|
"/etudiant/create",
|
||||||
args,
|
args,
|
||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
@ -1023,7 +1023,7 @@ def test_etudiant_create(api_headers):
|
|||||||
# assert etud["adresses"][0]["emailperso"] == args["adresses"][0]["emailperso"]
|
# assert etud["adresses"][0]["emailperso"] == args["adresses"][0]["emailperso"]
|
||||||
|
|
||||||
# Edition
|
# Edition
|
||||||
etud = POST_JSON(
|
etud = POST(
|
||||||
f"/etudiant/etudid/{etudid}/edit",
|
f"/etudiant/etudid/{etudid}/edit",
|
||||||
{
|
{
|
||||||
"civilite": "F",
|
"civilite": "F",
|
||||||
@ -1039,7 +1039,7 @@ def test_etudiant_create(api_headers):
|
|||||||
assert len(etud["adresses"]) == 1
|
assert len(etud["adresses"]) == 1
|
||||||
# assert etud["adresses"][0]["villedomicile"] == args["adresses"][0]["villedomicile"]
|
# assert etud["adresses"][0]["villedomicile"] == args["adresses"][0]["villedomicile"]
|
||||||
# assert etud["adresses"][0]["emailperso"] == args["adresses"][0]["emailperso"]
|
# assert etud["adresses"][0]["emailperso"] == args["adresses"][0]["emailperso"]
|
||||||
etud = POST_JSON(
|
etud = POST(
|
||||||
f"/etudiant/etudid/{etudid}/edit",
|
f"/etudiant/etudid/{etudid}/edit",
|
||||||
{
|
{
|
||||||
"adresses": [
|
"adresses": [
|
||||||
@ -1051,7 +1051,7 @@ def test_etudiant_create(api_headers):
|
|||||||
headers=admin_header,
|
headers=admin_header,
|
||||||
)
|
)
|
||||||
assert etud["adresses"][0]["villedomicile"] == "Barcelona"
|
assert etud["adresses"][0]["villedomicile"] == "Barcelona"
|
||||||
etud = POST_JSON(
|
etud = POST(
|
||||||
f"/etudiant/etudid/{etudid}/edit",
|
f"/etudiant/etudid/{etudid}/edit",
|
||||||
{
|
{
|
||||||
"admission": {
|
"admission": {
|
||||||
|
@ -25,7 +25,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
api_headers,
|
api_headers,
|
||||||
check_failure_post,
|
check_failure_post,
|
||||||
@ -114,7 +114,7 @@ def test_evaluation_create(api_admin_headers):
|
|||||||
)
|
)
|
||||||
nb_evals = len(evaluations)
|
nb_evals = len(evaluations)
|
||||||
#
|
#
|
||||||
e = POST_JSON(
|
e = POST(
|
||||||
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
||||||
{"description": "eval test"},
|
{"description": "eval test"},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -157,7 +157,7 @@ def test_evaluation_create(api_admin_headers):
|
|||||||
"publish_incomplete": True,
|
"publish_incomplete": True,
|
||||||
"note_max": 100.0,
|
"note_max": 100.0,
|
||||||
}
|
}
|
||||||
e = POST_JSON(
|
e = POST(
|
||||||
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
||||||
data,
|
data,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -209,7 +209,7 @@ def test_evaluation_create(api_admin_headers):
|
|||||||
ue_ids = [ue["id"] for ue in ues]
|
ue_ids = [ue["id"] for ue in ues]
|
||||||
poids = {ue_id: float(i) + 0.5 for i, ue_id in enumerate(ue_ids)}
|
poids = {ue_id: float(i) + 0.5 for i, ue_id in enumerate(ue_ids)}
|
||||||
data.update({"description": "eval avec poids", "poids": poids})
|
data.update({"description": "eval avec poids", "poids": poids})
|
||||||
e = POST_JSON(
|
e = POST(
|
||||||
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
f"/moduleimpl/{moduleimpl_id}/evaluation/create",
|
||||||
data,
|
data,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -223,7 +223,7 @@ def test_evaluation_create(api_admin_headers):
|
|||||||
assert new_nb_evals == nb_evals + 1
|
assert new_nb_evals == nb_evals + 1
|
||||||
nb_evals = new_nb_evals
|
nb_evals = new_nb_evals
|
||||||
# Delete
|
# Delete
|
||||||
ans = POST_JSON(
|
ans = POST(
|
||||||
f"/evaluation/{e_ret['id']}/delete",
|
f"/evaluation/{e_ret['id']}/delete",
|
||||||
headers=api_admin_headers,
|
headers=api_admin_headers,
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
)
|
)
|
||||||
from tests.api.tools_test_api import (
|
from tests.api.tools_test_api import (
|
||||||
verify_fields,
|
verify_fields,
|
||||||
@ -338,11 +338,11 @@ def test_api_ue_apo(api_admin_headers):
|
|||||||
ue_id = 1
|
ue_id = 1
|
||||||
ue = GET(path=f"/formation/ue/{ue_id}", headers=api_admin_headers)
|
ue = GET(path=f"/formation/ue/{ue_id}", headers=api_admin_headers)
|
||||||
assert ue["id"] == ue_id
|
assert ue["id"] == ue_id
|
||||||
r = POST_JSON(
|
r = POST(
|
||||||
f"/formation/ue/{ue_id}/set_code_apogee/APOUE", {}, api_admin_headers, raw=True
|
f"/formation/ue/{ue_id}/set_code_apogee/APOUE", {}, api_admin_headers, raw=True
|
||||||
)
|
)
|
||||||
assert r.text == "APOUE"
|
assert r.text == "APOUE"
|
||||||
r = POST_JSON(
|
r = POST(
|
||||||
f"/formation/ue/{ue_id}/set_code_apogee_rcue/APORCUE",
|
f"/formation/ue/{ue_id}/set_code_apogee_rcue/APORCUE",
|
||||||
{},
|
{},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -363,7 +363,7 @@ def test_api_module_apo(api_admin_headers):
|
|||||||
module = GET(path=f"/formation/module/{module_id}", headers=api_admin_headers)
|
module = GET(path=f"/formation/module/{module_id}", headers=api_admin_headers)
|
||||||
assert module["id"] == module_id
|
assert module["id"] == module_id
|
||||||
assert module["code_apogee"] == ""
|
assert module["code_apogee"] == ""
|
||||||
r = POST_JSON(
|
r = POST(
|
||||||
f"/formation/module/{module_id}/set_code_apogee/APOMOD",
|
f"/formation/module/{module_id}/set_code_apogee/APOMOD",
|
||||||
{},
|
{},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -379,7 +379,7 @@ def test_api_module_edit(api_admin_headers):
|
|||||||
/formation/module/<int:module_id>/edit
|
/formation/module/<int:module_id>/edit
|
||||||
"""
|
"""
|
||||||
module_id = 1
|
module_id = 1
|
||||||
module = POST_JSON(
|
module = POST(
|
||||||
f"/formation/module/{module_id}/edit",
|
f"/formation/module/{module_id}/edit",
|
||||||
{"heures_cours": 16, "semestre_id": 1, "abbrev": "ALLO"},
|
{"heures_cours": 16, "semestre_id": 1, "abbrev": "ALLO"},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -390,7 +390,7 @@ def test_api_module_edit(api_admin_headers):
|
|||||||
assert module["abbrev"] == "ALLO"
|
assert module["abbrev"] == "ALLO"
|
||||||
# tente de changer l'UE: ne devrait rien faire:
|
# tente de changer l'UE: ne devrait rien faire:
|
||||||
ue_id = module["ue_id"]
|
ue_id = module["ue_id"]
|
||||||
module = POST_JSON(
|
module = POST(
|
||||||
f"/formation/module/{module_id}/edit",
|
f"/formation/module/{module_id}/edit",
|
||||||
{"ue_id": 666},
|
{"ue_id": 666},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -398,7 +398,7 @@ def test_api_module_edit(api_admin_headers):
|
|||||||
assert module["ue_id"] == ue_id
|
assert module["ue_id"] == ue_id
|
||||||
# tente de changer la formation: ne devrait rien faire:
|
# tente de changer la formation: ne devrait rien faire:
|
||||||
formation_id = module["formation_id"]
|
formation_id = module["formation_id"]
|
||||||
module = POST_JSON(
|
module = POST(
|
||||||
f"/formation/module/{module_id}/edit",
|
f"/formation/module/{module_id}/edit",
|
||||||
{"formation_id": 666},
|
{"formation_id": 666},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -406,7 +406,7 @@ def test_api_module_edit(api_admin_headers):
|
|||||||
assert module["formation_id"] == formation_id
|
assert module["formation_id"] == formation_id
|
||||||
# -- assignation de parcours (ce test suppose que les parcours 1, 2, 3 existent)
|
# -- assignation de parcours (ce test suppose que les parcours 1, 2, 3 existent)
|
||||||
assert module["parcours"] == []
|
assert module["parcours"] == []
|
||||||
module = POST_JSON(
|
module = POST(
|
||||||
f"/formation/module/{module_id}/edit",
|
f"/formation/module/{module_id}/edit",
|
||||||
{"parcours": [1, 2, 3]},
|
{"parcours": [1, 2, 3]},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -419,7 +419,7 @@ def test_api_ue_edit(api_admin_headers):
|
|||||||
/formation/ue/<int:ue_id>/edit
|
/formation/ue/<int:ue_id>/edit
|
||||||
"""
|
"""
|
||||||
ue_id = 1
|
ue_id = 1
|
||||||
ue = POST_JSON(
|
ue = POST(
|
||||||
f"/formation/ue/{ue_id}/edit",
|
f"/formation/ue/{ue_id}/edit",
|
||||||
{"titre": "formation test modifiée", "numero": 22},
|
{"titre": "formation test modifiée", "numero": 22},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -430,7 +430,7 @@ def test_api_ue_edit(api_admin_headers):
|
|||||||
|
|
||||||
# tente de changer le niveau de compétence: ne devrait rien faire:
|
# tente de changer le niveau de compétence: ne devrait rien faire:
|
||||||
niveau_competence_id = ue["niveau_competence_id"]
|
niveau_competence_id = ue["niveau_competence_id"]
|
||||||
ue = POST_JSON(
|
ue = POST(
|
||||||
f"/formation/ue/{ue_id}/edit",
|
f"/formation/ue/{ue_id}/edit",
|
||||||
{"niveau_competence_id": 666},
|
{"niveau_competence_id": 666},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -438,7 +438,7 @@ def test_api_ue_edit(api_admin_headers):
|
|||||||
assert ue["niveau_competence_id"] == niveau_competence_id
|
assert ue["niveau_competence_id"] == niveau_competence_id
|
||||||
# tente de changer la formation: ne devrait rien faire:
|
# tente de changer la formation: ne devrait rien faire:
|
||||||
formation_id = ue["formation_id"]
|
formation_id = ue["formation_id"]
|
||||||
ue = POST_JSON(
|
ue = POST(
|
||||||
f"/formation/ue/{ue_id}/edit",
|
f"/formation/ue/{ue_id}/edit",
|
||||||
{"formation_id": 666},
|
{"formation_id": 666},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
|
@ -23,7 +23,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
api_headers,
|
api_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ def test_jury_decisions(api_headers):
|
|||||||
# ues = export_formation["ue"]
|
# ues = export_formation["ue"]
|
||||||
# # Enregistre une validation d'RCUE
|
# # Enregistre une validation d'RCUE
|
||||||
# etudid = etudiants[0]["id"]
|
# etudid = etudiants[0]["id"]
|
||||||
# validation = POST_JSON(
|
# validation = POST(
|
||||||
# f"/etudiant/{etudid}/jury/validation_rcue/record",
|
# f"/etudiant/{etudid}/jury/validation_rcue/record",
|
||||||
# data={
|
# data={
|
||||||
# "code": "ADM",
|
# "code": "ADM",
|
||||||
|
@ -12,7 +12,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
APIError,
|
APIError,
|
||||||
api_headers,
|
api_headers,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -105,7 +105,7 @@ def check_failure_post(path, headers, data, err=None):
|
|||||||
APIError: Une erreur car la requête a fonctionné (mauvais comportement)
|
APIError: Une erreur car la requête a fonctionné (mauvais comportement)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = POST_JSON(path=path, headers=headers, data=data)
|
data = POST(path=path, headers=headers, data=data)
|
||||||
# ^ Renvoi un 404
|
# ^ Renvoi un 404
|
||||||
except APIError as api_err:
|
except APIError as api_err:
|
||||||
if err is not None:
|
if err is not None:
|
||||||
@ -204,14 +204,14 @@ def test_route_create(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
data = create_data("valide", "01")
|
data = create_data("valide", "01")
|
||||||
|
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data], api_admin_headers)
|
res = POST(f"/justificatif/{ETUDID}/create", [data], api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["success"]) == 1
|
assert len(res["success"]) == 1
|
||||||
|
|
||||||
TO_REMOVE.append(res["success"][0]["message"]["justif_id"])
|
TO_REMOVE.append(res["success"][0]["message"]["justif_id"])
|
||||||
|
|
||||||
data2 = create_data("modifie", "02", "raison")
|
data2 = create_data("modifie", "02", "raison")
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data2], api_admin_headers)
|
res = POST(f"/justificatif/{ETUDID}/create", [data2], api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["success"]) == 1
|
assert len(res["success"]) == 1
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ def test_route_create(api_admin_headers):
|
|||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
check_failure_post(f"/justificatif/{FAUX}/create", api_admin_headers, [data])
|
check_failure_post(f"/justificatif/{FAUX}/create", api_admin_headers, [data])
|
||||||
|
|
||||||
res = POST_JSON(
|
res = POST(
|
||||||
f"/justificatif/{ETUDID}/create",
|
f"/justificatif/{ETUDID}/create",
|
||||||
[create_data("absent", "03")],
|
[create_data("absent", "03")],
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -239,7 +239,7 @@ def test_route_create(api_admin_headers):
|
|||||||
for d in range(randint(3, 5))
|
for d in range(randint(3, 5))
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", data, api_admin_headers)
|
res = POST(f"/justificatif/{ETUDID}/create", data, api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
for dat in res["success"]:
|
for dat in res["success"]:
|
||||||
check_fields(dat["message"], CREATE_FIELD)
|
check_fields(dat["message"], CREATE_FIELD)
|
||||||
@ -253,7 +253,7 @@ def test_route_create(api_admin_headers):
|
|||||||
create_data("valide", 32),
|
create_data("valide", 32),
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", data2, api_admin_headers)
|
res = POST(f"/justificatif/{ETUDID}/create", data2, api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["errors"]) == 3
|
assert len(res["errors"]) == 3
|
||||||
|
|
||||||
@ -270,11 +270,11 @@ def test_route_edit(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
|
|
||||||
data = {"etat": "modifie", "raison": "test"}
|
data = {"etat": "modifie", "raison": "test"}
|
||||||
res = POST_JSON(f"/justificatif/{TO_REMOVE[0]}/edit", data, api_admin_headers)
|
res = POST(f"/justificatif/{TO_REMOVE[0]}/edit", data, api_admin_headers)
|
||||||
assert isinstance(res, dict) and "couverture" in res.keys()
|
assert isinstance(res, dict) and "couverture" in res.keys()
|
||||||
|
|
||||||
data["raison"] = None
|
data["raison"] = None
|
||||||
res = POST_JSON(f"/justificatif/{TO_REMOVE[1]}/edit", data, api_admin_headers)
|
res = POST(f"/justificatif/{TO_REMOVE[1]}/edit", data, api_admin_headers)
|
||||||
assert isinstance(res, dict) and "couverture" in res.keys()
|
assert isinstance(res, dict) and "couverture" in res.keys()
|
||||||
|
|
||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
@ -296,13 +296,13 @@ def test_route_delete(api_admin_headers):
|
|||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
data = TO_REMOVE[0]
|
data = TO_REMOVE[0]
|
||||||
|
|
||||||
res = POST_JSON("/justificatif/delete", [data], api_admin_headers)
|
res = POST("/justificatif/delete", [data], api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
for dat in res["success"]:
|
for dat in res["success"]:
|
||||||
assert dat["message"] == "OK"
|
assert dat["message"] == "OK"
|
||||||
|
|
||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
res = POST_JSON("/justificatif/delete", [data], api_admin_headers)
|
res = POST("/justificatif/delete", [data], api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["errors"]) == 1
|
assert len(res["errors"]) == 1
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ def test_route_delete(api_admin_headers):
|
|||||||
|
|
||||||
data = TO_REMOVE[1:]
|
data = TO_REMOVE[1:]
|
||||||
|
|
||||||
res = POST_JSON("/justificatif/delete", data, api_admin_headers)
|
res = POST("/justificatif/delete", data, api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
for dat in res["success"]:
|
for dat in res["success"]:
|
||||||
assert dat["message"] == "OK"
|
assert dat["message"] == "OK"
|
||||||
@ -325,7 +325,7 @@ def test_route_delete(api_admin_headers):
|
|||||||
FAUX + 2,
|
FAUX + 2,
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON("/justificatif/delete", data2, api_admin_headers)
|
res = POST("/justificatif/delete", data2, api_admin_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["errors"]) == 3
|
assert len(res["errors"]) == 3
|
||||||
|
|
||||||
@ -473,15 +473,13 @@ def test_remove_justificatif(api_admin_headers):
|
|||||||
filename: str = "tests/api/test_api_justificatif2.txt"
|
filename: str = "tests/api/test_api_justificatif2.txt"
|
||||||
_send_file(2, filename, api_admin_headers)
|
_send_file(2, filename, api_admin_headers)
|
||||||
|
|
||||||
res: dict = POST_JSON(
|
res: dict = POST("/justificatif/1/remove", {"remove": "all"}, api_admin_headers)
|
||||||
"/justificatif/1/remove", {"remove": "all"}, api_admin_headers
|
|
||||||
)
|
|
||||||
assert res == {"response": "removed"}
|
assert res == {"response": "removed"}
|
||||||
l = GET("/justificatif/1/list", api_admin_headers)
|
l = GET("/justificatif/1/list", api_admin_headers)
|
||||||
assert isinstance(l, dict)
|
assert isinstance(l, dict)
|
||||||
assert l["total"] == 0
|
assert l["total"] == 0
|
||||||
|
|
||||||
res: dict = POST_JSON(
|
res: dict = POST(
|
||||||
"/justificatif/2/remove",
|
"/justificatif/2/remove",
|
||||||
{"remove": "list", "filenames": ["test_api_justificatif2.txt"]},
|
{"remove": "list", "filenames": ["test_api_justificatif2.txt"]},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
@ -491,7 +489,7 @@ def test_remove_justificatif(api_admin_headers):
|
|||||||
assert isinstance(l, dict)
|
assert isinstance(l, dict)
|
||||||
assert l["total"] == 1
|
assert l["total"] == 1
|
||||||
|
|
||||||
res: dict = POST_JSON(
|
res: dict = POST(
|
||||||
"/justificatif/2/remove",
|
"/justificatif/2/remove",
|
||||||
{"remove": "list", "filenames": ["test_api_justificatif.txt"]},
|
{"remove": "list", "filenames": ["test_api_justificatif.txt"]},
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
|
@ -21,7 +21,7 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
api_headers,
|
api_headers,
|
||||||
)
|
)
|
||||||
from tests.api.tools_test_api import (
|
from tests.api.tools_test_api import (
|
||||||
@ -65,15 +65,15 @@ def test_formsemestre_partition(api_headers):
|
|||||||
headers = api_headers
|
headers = api_headers
|
||||||
formsemestre_id = 1
|
formsemestre_id = 1
|
||||||
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=headers)
|
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=headers)
|
||||||
# au départ, pas de partitions
|
# au départ, une partition (TD avec groupes A et B)
|
||||||
assert partitions == {}
|
assert len(partitions) == 1
|
||||||
# --- Création partition
|
# --- Création partition
|
||||||
partition_d = {
|
partition_d = {
|
||||||
"partition_name": "T&Dé",
|
"partition_name": "T&Dé",
|
||||||
"bul_show_rank": True,
|
"bul_show_rank": True,
|
||||||
"show_in_lists": True,
|
"show_in_lists": True,
|
||||||
}
|
}
|
||||||
partition_r = POST_JSON(
|
partition_r = POST(
|
||||||
f"/formsemestre/{formsemestre_id}/partition/create",
|
f"/formsemestre/{formsemestre_id}/partition/create",
|
||||||
partition_d,
|
partition_d,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@ -83,7 +83,7 @@ def test_formsemestre_partition(api_headers):
|
|||||||
assert partition_r["groups"] == {}
|
assert partition_r["groups"] == {}
|
||||||
# --- Création Groupe
|
# --- Création Groupe
|
||||||
group_d = {"group_name": "Aé-&"}
|
group_d = {"group_name": "Aé-&"}
|
||||||
group_r = POST_JSON(
|
group_r = POST(
|
||||||
f"/partition/{partition_r['id']}/group/create",
|
f"/partition/{partition_r['id']}/group/create",
|
||||||
group_d,
|
group_d,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@ -102,14 +102,14 @@ def test_formsemestre_partition(api_headers):
|
|||||||
|
|
||||||
# --- Ajout d'un groupe avec edt_id
|
# --- Ajout d'un groupe avec edt_id
|
||||||
group_d = {"group_name": "extra", "edt_id": "GEDT"}
|
group_d = {"group_name": "extra", "edt_id": "GEDT"}
|
||||||
group_r = POST_JSON(
|
group_r = POST(
|
||||||
f"/partition/{partition_r['id']}/group/create",
|
f"/partition/{partition_r['id']}/group/create",
|
||||||
group_d,
|
group_d,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
assert group_r["edt_id"] == "GEDT"
|
assert group_r["edt_id"] == "GEDT"
|
||||||
# Edit edt_id
|
# Edit edt_id
|
||||||
group_r = POST_JSON(
|
group_r = POST(
|
||||||
f"/group/{group_r['id']}/edit",
|
f"/group/{group_r['id']}/edit",
|
||||||
{"edt_id": "GEDT2"},
|
{"edt_id": "GEDT2"},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@ -121,7 +121,7 @@ def test_formsemestre_partition(api_headers):
|
|||||||
assert group["edt_id"] == "GEDT2"
|
assert group["edt_id"] == "GEDT2"
|
||||||
|
|
||||||
# Change edt_id via route dédiée:
|
# Change edt_id via route dédiée:
|
||||||
group_t = POST_JSON(
|
group_t = POST(
|
||||||
f"/group/{group_r['id']}/set_edt_id/GEDT3",
|
f"/group/{group_r['id']}/set_edt_id/GEDT3",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
@ -130,7 +130,7 @@ def test_formsemestre_partition(api_headers):
|
|||||||
|
|
||||||
# Place un étudiant dans le groupe
|
# Place un étudiant dans le groupe
|
||||||
etud = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=headers)[0]
|
etud = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=headers)[0]
|
||||||
repl = POST_JSON(f"/group/{group['id']}/set_etudiant/{etud['id']}", headers=headers)
|
repl = POST(f"/group/{group['id']}/set_etudiant/{etud['id']}", headers=headers)
|
||||||
assert isinstance(repl, dict)
|
assert isinstance(repl, dict)
|
||||||
assert repl["group_id"] == group["id"]
|
assert repl["group_id"] == group["id"]
|
||||||
assert repl["etudid"] == etud["id"]
|
assert repl["etudid"] == etud["id"]
|
||||||
@ -139,21 +139,17 @@ def test_formsemestre_partition(api_headers):
|
|||||||
assert len(etuds) == 1
|
assert len(etuds) == 1
|
||||||
assert etuds[0]["id"] == etud["id"]
|
assert etuds[0]["id"] == etud["id"]
|
||||||
# Retire l'étudiant du groupe
|
# Retire l'étudiant du groupe
|
||||||
repl = POST_JSON(
|
repl = POST(f"/group/{group['id']}/remove_etudiant/{etud['id']}", headers=headers)
|
||||||
f"/group/{group['id']}/remove_etudiant/{etud['id']}", headers=headers
|
|
||||||
)
|
|
||||||
assert len(GET(f"/group/{group['id']}/etudiants", headers=headers)) == 0
|
assert len(GET(f"/group/{group['id']}/etudiants", headers=headers)) == 0
|
||||||
|
|
||||||
# Le retire à nouveau ! (bug #465)
|
# Le retire à nouveau ! (bug #465)
|
||||||
repl = POST_JSON(
|
repl = POST(f"/group/{group['id']}/remove_etudiant/{etud['id']}", headers=headers)
|
||||||
f"/group/{group['id']}/remove_etudiant/{etud['id']}", headers=headers
|
|
||||||
)
|
|
||||||
assert repl["group_id"] == group["id"]
|
assert repl["group_id"] == group["id"]
|
||||||
# Avec partition (vérifie encodeur JSON)
|
# Avec partition (vérifie encodeur JSON)
|
||||||
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=headers)
|
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions", headers=headers)
|
||||||
assert partitions
|
assert partitions
|
||||||
# Delete partition
|
# Delete partition
|
||||||
repl = POST_JSON(f"/partition/{partition_r['id']}/delete", headers=headers)
|
repl = POST(f"/partition/{partition_r['id']}/delete", headers=headers)
|
||||||
assert repl["OK"] is True
|
assert repl["OK"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from tests.api.setup_test_api import (
|
|||||||
APIError,
|
APIError,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
GET,
|
GET,
|
||||||
POST_JSON,
|
POST,
|
||||||
api_headers,
|
api_headers,
|
||||||
api_admin_headers,
|
api_admin_headers,
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
@ -76,7 +76,7 @@ def test_edit_users(api_admin_headers):
|
|||||||
"""
|
"""
|
||||||
admin_h = api_admin_headers
|
admin_h = api_admin_headers
|
||||||
nb_users = len(GET("/users/query", headers=admin_h))
|
nb_users = len(GET("/users/query", headers=admin_h))
|
||||||
user = POST_JSON(
|
user = POST(
|
||||||
"/user/create",
|
"/user/create",
|
||||||
{"user_name": "test_edit_users", "nom": "Toto"},
|
{"user_name": "test_edit_users", "nom": "Toto"},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
@ -86,7 +86,7 @@ def test_edit_users(api_admin_headers):
|
|||||||
assert user["active"] is True
|
assert user["active"] is True
|
||||||
assert (nb_users + 1) == len(GET("/users/query", headers=admin_h))
|
assert (nb_users + 1) == len(GET("/users/query", headers=admin_h))
|
||||||
# Change le dept et rend inactif
|
# Change le dept et rend inactif
|
||||||
user = POST_JSON(
|
user = POST(
|
||||||
f"/user/{user['id']}/edit",
|
f"/user/{user['id']}/edit",
|
||||||
{"active": False, "dept": "TAPI", "edt_id": "GGG"},
|
{"active": False, "dept": "TAPI", "edt_id": "GGG"},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
@ -107,37 +107,37 @@ def test_roles(api_admin_headers):
|
|||||||
/user/<int:uid>/edit
|
/user/<int:uid>/edit
|
||||||
"""
|
"""
|
||||||
admin_h = api_admin_headers
|
admin_h = api_admin_headers
|
||||||
user = POST_JSON(
|
user = POST(
|
||||||
"/user/create",
|
"/user/create",
|
||||||
{"user_name": "test_roles", "nom": "Role", "prenom": "Test"},
|
{"user_name": "test_roles", "nom": "Role", "prenom": "Test"},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
uid = user["id"]
|
uid = user["id"]
|
||||||
ans = POST_JSON(f"/user/{uid}/role/Secr/add", headers=admin_h)
|
ans = POST(f"/user/{uid}/role/Secr/add", headers=admin_h)
|
||||||
assert ans["user_name"] == "test_roles"
|
assert ans["user_name"] == "test_roles"
|
||||||
role = POST_JSON("/role/create/Test_X", headers=admin_h)
|
role = POST("/role/create/Test_X", headers=admin_h)
|
||||||
assert role["role_name"] == "Test_X"
|
assert role["role_name"] == "Test_X"
|
||||||
assert role["permissions"] == []
|
assert role["permissions"] == []
|
||||||
role = GET("/role/Test_X", headers=admin_h)
|
role = GET("/role/Test_X", headers=admin_h)
|
||||||
assert role["role_name"] == "Test_X"
|
assert role["role_name"] == "Test_X"
|
||||||
assert role["permissions"] == []
|
assert role["permissions"] == []
|
||||||
role = POST_JSON("/role/Test_X/edit", {"role_name": "Test_Y"}, headers=admin_h)
|
role = POST("/role/Test_X/edit", {"role_name": "Test_Y"}, headers=admin_h)
|
||||||
assert role["role_name"] == "Test_Y"
|
assert role["role_name"] == "Test_Y"
|
||||||
role = GET("/role/Test_Y", headers=admin_h)
|
role = GET("/role/Test_Y", headers=admin_h)
|
||||||
assert role["role_name"] == "Test_Y"
|
assert role["role_name"] == "Test_Y"
|
||||||
role = POST_JSON(
|
role = POST(
|
||||||
"/role/Test_Y/edit",
|
"/role/Test_Y/edit",
|
||||||
{"permissions": ["ScoView", "AbsChange"]},
|
{"permissions": ["ScoView", "AbsChange"]},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
assert set(role["permissions"]) == {"ScoView", "AbsChange"}
|
assert set(role["permissions"]) == {"ScoView", "AbsChange"}
|
||||||
role = POST_JSON("/role/Test_Y/add_permission/AbsAddBillet", headers=admin_h)
|
role = POST("/role/Test_Y/add_permission/AbsAddBillet", headers=admin_h)
|
||||||
assert set(role["permissions"]) == {"ScoView", "AbsChange", "AbsAddBillet"}
|
assert set(role["permissions"]) == {"ScoView", "AbsChange", "AbsAddBillet"}
|
||||||
role = GET("/role/Test_Y", headers=admin_h)
|
role = GET("/role/Test_Y", headers=admin_h)
|
||||||
assert set(role["permissions"]) == {"ScoView", "AbsChange", "AbsAddBillet"}
|
assert set(role["permissions"]) == {"ScoView", "AbsChange", "AbsAddBillet"}
|
||||||
role = POST_JSON("/role/Test_Y/remove_permission/AbsChange", headers=admin_h)
|
role = POST("/role/Test_Y/remove_permission/AbsChange", headers=admin_h)
|
||||||
assert set(role["permissions"]) == {"ScoView", "AbsAddBillet"}
|
assert set(role["permissions"]) == {"ScoView", "AbsAddBillet"}
|
||||||
ans = POST_JSON("/role/Test_Y/delete", headers=admin_h)
|
ans = POST("/role/Test_Y/delete", headers=admin_h)
|
||||||
assert ans["OK"] is True
|
assert ans["OK"] is True
|
||||||
|
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
# On va utiliser les 3 1er dept (TAPI, AA, BB)
|
# On va utiliser les 3 1er dept (TAPI, AA, BB)
|
||||||
# On crée un nouvel utilisateur "chef2", admin dans les 2 premiers dept
|
# On crée un nouvel utilisateur "chef2", admin dans les 2 premiers dept
|
||||||
# puis un utilisateur lambda, dans le dept 2 (AA)
|
# puis un utilisateur lambda, dans le dept 2 (AA)
|
||||||
chef2 = POST_JSON(
|
chef2 = POST(
|
||||||
"/user/create",
|
"/user/create",
|
||||||
{
|
{
|
||||||
"user_name": "chef2",
|
"user_name": "chef2",
|
||||||
@ -163,23 +163,23 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
},
|
},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
role_chef = POST_JSON(
|
role_chef = POST(
|
||||||
"/role/create/chef",
|
"/role/create/chef",
|
||||||
{"permissions": ["ScoView", "UsersAdmin", "UsersView"]},
|
{"permissions": ["ScoView", "UsersAdmin", "UsersView"]},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
_ = POST_JSON(
|
_ = POST(
|
||||||
f"/user/{chef2['id']}/role/chef/add/departement/{dept1['acronym']}",
|
f"/user/{chef2['id']}/role/chef/add/departement/{dept1['acronym']}",
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
_ = POST_JSON(
|
_ = POST(
|
||||||
f"/user/{chef2['id']}/role/chef/add/departement/{dept2['acronym']}",
|
f"/user/{chef2['id']}/role/chef/add/departement/{dept2['acronym']}",
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
# Un mot de passe trop simple:
|
# Un mot de passe trop simple:
|
||||||
ok = False
|
ok = False
|
||||||
try:
|
try:
|
||||||
_ = POST_JSON(
|
_ = POST(
|
||||||
f"/user/{chef2['id']}/password",
|
f"/user/{chef2['id']}/password",
|
||||||
{"password": "123456"},
|
{"password": "123456"},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
@ -190,13 +190,13 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
assert ok
|
assert ok
|
||||||
# Un "vrai" mot de passe:
|
# Un "vrai" mot de passe:
|
||||||
chef2_password = "17HIOPpYhabb8qw'E:/jd7FFddjd"
|
chef2_password = "17HIOPpYhabb8qw'E:/jd7FFddjd"
|
||||||
_ = POST_JSON(
|
_ = POST(
|
||||||
f"/user/{chef2['id']}/password",
|
f"/user/{chef2['id']}/password",
|
||||||
{"password": chef2_password},
|
{"password": chef2_password},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
)
|
)
|
||||||
# Création user lambda:
|
# Création user lambda:
|
||||||
u_lambda = POST_JSON(
|
u_lambda = POST(
|
||||||
"/user/create",
|
"/user/create",
|
||||||
{
|
{
|
||||||
"user_name": "lambda",
|
"user_name": "lambda",
|
||||||
@ -210,7 +210,7 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
# Le chef va modifier u_lambda:
|
# Le chef va modifier u_lambda:
|
||||||
chef_h = get_auth_headers(chef2["user_name"], chef2_password)
|
chef_h = get_auth_headers(chef2["user_name"], chef2_password)
|
||||||
# on utilise une URL avec département car on n'a pas le droit sur tous:
|
# on utilise une URL avec département car on n'a pas le droit sur tous:
|
||||||
u = POST_JSON(
|
u = POST(
|
||||||
f"/user/{u_lambda['id']}/edit",
|
f"/user/{u_lambda['id']}/edit",
|
||||||
{"nom": "toto"},
|
{"nom": "toto"},
|
||||||
headers=chef_h,
|
headers=chef_h,
|
||||||
@ -218,7 +218,7 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
)
|
)
|
||||||
assert u["nom"] == "toto"
|
assert u["nom"] == "toto"
|
||||||
# Dans l'autre ?
|
# Dans l'autre ?
|
||||||
u = POST_JSON(
|
u = POST(
|
||||||
f"/user/{u_lambda['id']}/edit",
|
f"/user/{u_lambda['id']}/edit",
|
||||||
{"nom": "toto"},
|
{"nom": "toto"},
|
||||||
headers=chef_h,
|
headers=chef_h,
|
||||||
@ -227,7 +227,7 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
# mais pas dans le troisième:
|
# mais pas dans le troisième:
|
||||||
ok = False
|
ok = False
|
||||||
try:
|
try:
|
||||||
u = POST_JSON(
|
u = POST(
|
||||||
f"/user/{u_lambda['id']}/edit",
|
f"/user/{u_lambda['id']}/edit",
|
||||||
{"nom": "toto"},
|
{"nom": "toto"},
|
||||||
headers=chef_h,
|
headers=chef_h,
|
||||||
@ -240,7 +240,7 @@ def test_modif_users_depts(api_admin_headers):
|
|||||||
# Nettoyage:
|
# Nettoyage:
|
||||||
# on ne peut pas supprimer l'utilisateur lambda, mais on
|
# on ne peut pas supprimer l'utilisateur lambda, mais on
|
||||||
# le rend inactif et on le retire de son département
|
# le rend inactif et on le retire de son département
|
||||||
u = POST_JSON(
|
u = POST(
|
||||||
f"/user/{u_lambda['id']}/edit",
|
f"/user/{u_lambda['id']}/edit",
|
||||||
{"active": False, "dept": None},
|
{"active": False, "dept": None},
|
||||||
headers=admin_h,
|
headers=admin_h,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import flask
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask_login import login_user
|
from flask_login import login_user
|
||||||
|
|
||||||
|
@ -50,8 +50,9 @@
|
|||||||
"moy_sae_14_3": "17.83",
|
"moy_sae_14_3": "17.83",
|
||||||
"moy_sae_15_3": "~",
|
"moy_sae_15_3": "~",
|
||||||
"ues_validables": "3/3",
|
"ues_validables": "3/3",
|
||||||
"nbabs": 0,
|
"nbabs": 1,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -109,8 +110,9 @@
|
|||||||
"moy_sae_14_3": "10.74",
|
"moy_sae_14_3": "10.74",
|
||||||
"moy_sae_15_3": "~",
|
"moy_sae_15_3": "~",
|
||||||
"ues_validables": "3/3",
|
"ues_validables": "3/3",
|
||||||
"nbabs": 2,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -170,6 +172,7 @@
|
|||||||
"ues_validables": "2/3",
|
"ues_validables": "2/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -229,6 +232,7 @@
|
|||||||
"ues_validables": "2/3",
|
"ues_validables": "2/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -286,8 +290,9 @@
|
|||||||
"moy_sae_14_3": "11.09",
|
"moy_sae_14_3": "11.09",
|
||||||
"moy_sae_15_3": "~",
|
"moy_sae_15_3": "~",
|
||||||
"ues_validables": "1/3",
|
"ues_validables": "1/3",
|
||||||
"nbabs": 3,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -347,6 +352,7 @@
|
|||||||
"ues_validables": "2/3",
|
"ues_validables": "2/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -404,8 +410,9 @@
|
|||||||
"moy_sae_14_3": "05.17",
|
"moy_sae_14_3": "05.17",
|
||||||
"moy_sae_15_3": "~",
|
"moy_sae_15_3": "~",
|
||||||
"ues_validables": "1/3",
|
"ues_validables": "1/3",
|
||||||
"nbabs": 3,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -465,6 +472,7 @@
|
|||||||
"ues_validables": "0/3",
|
"ues_validables": "0/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -524,6 +532,7 @@
|
|||||||
"ues_validables": "0/3",
|
"ues_validables": "0/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -583,6 +592,7 @@
|
|||||||
"ues_validables": "0/3",
|
"ues_validables": "0/3",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -642,6 +652,7 @@
|
|||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 2,
|
"nbabs": 2,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9f.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -699,8 +710,9 @@
|
|||||||
"moy_sae_14_3": "",
|
"moy_sae_14_3": "",
|
||||||
"moy_sae_15_3": "",
|
"moy_sae_15_3": "",
|
||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 2,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9m.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -758,8 +770,9 @@
|
|||||||
"moy_sae_14_3": "",
|
"moy_sae_14_3": "",
|
||||||
"moy_sae_15_3": "",
|
"moy_sae_15_3": "",
|
||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 0,
|
"nbabs": 2,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9f.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -817,8 +830,9 @@
|
|||||||
"moy_sae_14_3": "",
|
"moy_sae_14_3": "",
|
||||||
"moy_sae_15_3": "",
|
"moy_sae_15_3": "",
|
||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 1,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9m.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -878,6 +892,7 @@
|
|||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 0,
|
"nbabs": 0,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9f.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
@ -935,8 +950,9 @@
|
|||||||
"moy_sae_14_3": "",
|
"moy_sae_14_3": "",
|
||||||
"moy_sae_15_3": "",
|
"moy_sae_15_3": "",
|
||||||
"ues_validables": "",
|
"ues_validables": "",
|
||||||
"nbabs": 0,
|
"nbabs": 1,
|
||||||
"nbabsjust": 0,
|
"nbabsjust": 0,
|
||||||
|
"part_2": "D\u00e9m.",
|
||||||
"code_cursus": "S1",
|
"code_cursus": "S1",
|
||||||
"bac": "",
|
"bac": "",
|
||||||
"specialite": "",
|
"specialite": "",
|
||||||
|
@ -1,36 +1,38 @@
|
|||||||
"entry_name";"url";"permission";"method";"content"
|
"entry_name";"url";"permission";"method";"content"
|
||||||
"assiduite";"/assiduite/1";"ScoView";"GET";
|
"assiduite";"/assiduite/1";"ScoView";"GET";
|
||||||
"assiduites";"/assiduites/1";"ScoView";"GET";
|
"assiduite_justificatifs";"/assiduite/1/justificatifs";"ScoView";"GET";
|
||||||
"assiduites";"/assiduites/1/query?etat=retard";"ScoView";"GET";
|
"assiduite_justificatifs";"/assiduite/1/justificatifs/long";"ScoView";"GET";
|
||||||
"assiduites";"/assiduites/1/query?moduleimpl_id=1";"ScoView";"GET";
|
|
||||||
"assiduites";"/assiduites/1/query?with_justifs=";"ScoView";"GET";
|
|
||||||
"assiduites_count";"/assiduites/1/count";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count";"ScoView";"GET";
|
||||||
"assiduites_count";"/assiduites/1/count/query?etat=retard";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?etat=retard";"ScoView";"GET";
|
||||||
"assiduites_count";"/assiduites/1/count/query?split";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?split";"ScoView";"GET";
|
||||||
"assiduites_count";"/assiduites/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
||||||
|
"assiduites";"/assiduites/1";"ScoView";"GET";
|
||||||
|
"assiduites";"/assiduites/1/query?etat=retard";"ScoView";"GET";
|
||||||
|
"assiduites";"/assiduites/1/query?moduleimpl_id=1";"ScoView";"GET";
|
||||||
|
"assiduites";"/assiduites/1/query?with_justifs=";"ScoView";"GET";
|
||||||
|
"assiduites_evaluations";"/assiduites/1/evaluations";"ScoView";"GET";
|
||||||
|
"assiduites_group";"/assiduites/group/query?etudids=1,2,3";"ScoView";"GET";
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1";"ScoView";"GET";
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?etat=retard";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?etat=retard";"ScoView";"GET";
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?moduleimpl_id=1";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?moduleimpl_id=1";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=retard";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=retard";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
||||||
"assiduites_group";"/assiduites/group/query?etudids=1,2,3";"ScoView";"GET";
|
|
||||||
"assiduites_justificatifs";"/assiduite/1/justificatifs";"ScoView";"GET";
|
|
||||||
"assiduites_justificatifs";"/assiduite/1/justificatifs/long";"ScoView";"GET";
|
|
||||||
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
||||||
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
||||||
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
||||||
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":-1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":-1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
||||||
|
"assiduite_delete";"/assiduite/delete";"UsersAdmin";"POST";"[2,2,3]"
|
||||||
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"":""absent""}"
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"":""absent""}"
|
||||||
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""moduleimpl_id"":2}"
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""moduleimpl_id"":2}"
|
||||||
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"": ""retard"",""moduleimpl_id"":3}"
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"": ""retard"",""moduleimpl_id"":3}"
|
||||||
"assiduite_delete";"/assiduite/delete";"UsersAdmin";"POST";"[2,2,3]"
|
|
||||||
"justificatif";"/justificatif/1";"ScoView";"GET";
|
"justificatif";"/justificatif/1";"ScoView";"GET";
|
||||||
"justificatifs";"/justificatifs/1";"ScoView";"GET";
|
"justificatifs";"/justificatifs/1";"ScoView";"GET";
|
||||||
"justificatifs";"/justificatifs/1/query?etat=attente";"ScoView";"GET";
|
"justificatifs";"/justificatifs/1/query?etat=attente";"ScoView";"GET";
|
||||||
"justificatifs_dept";"/justificatifs/dept/1";"ScoView";"GET";
|
"justificatifs_dept";"/justificatifs/dept/1";"ScoView";"GET";
|
||||||
"justificatifs_formsemestre";"/justificatifs/formsemestre/1";"ScoView";"GET";
|
"justificatifs_formsemestre";"/justificatifs/formsemestre/1";"ScoView";"GET";
|
||||||
"justificatif_create";"/justificatif/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""attente""}]"
|
"justif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""etat"":""valide""}"
|
||||||
"justificatif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""etat"":""valide""}"
|
"justif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""raison"":""MEDIC""}"
|
||||||
"justificatif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""raison"":""MEDIC""}"
|
"justif_delete";"/justificatif/delete";"UsersAdmin";"POST";"[2, 2, 3]"
|
||||||
"justificatif_delete";"/justificatif/delete";"UsersAdmin";"POST";"[2,2,3]"
|
"justif_list";"/justificatif/1/list";"ScoView";"GET";
|
||||||
|
"justif_justifies";"/justificatif/1/justifies";"UsersAdmin";"GET";
|
|
@ -1,120 +1,90 @@
|
|||||||
"entry_name";"url";"permission";"method";"content"
|
"entry_name";"url";"permission";"method";"content"
|
||||||
"assiduite_create";"/assiduite/1/create";"ScoView";"POST";"{""date_debut"": ""2022-10-27T08:00"",""date_fin"": ""2022-10-27T10:00"",""etat"": ""absent""}"
|
|
||||||
"assiduite_create";"/assiduite/1/create/batch";"ScoView";"POST";"{""batch"":[{""date_debut"": ""2022-10-27T08:00"",""date_fin"": ""2022-10-27T10:00"",""etat"": ""absent""},{""date_debut"": ""2022-10-27T08:00"",""date_fin"": ""2022-10-27T10:00"",""etat"": ""retard""},{""date_debut"": ""2022-10-27T11:00"",""date_fin"": ""2022-10-27T13:00"",""etat"": ""present""}]}"
|
|
||||||
"assiduite_delete";"/assiduite/delete";"ScoView";"POST";"{""assiduite_id"": 1}"
|
|
||||||
"assiduite_delete";"/assiduite/delete/batch";"ScoView";"POST";"{""batch"":[2,2,3]}"
|
|
||||||
"assiduite_edit";"/assiduite/1/edit";"ScoView";"POST";"{""etat"": ""retard"",""moduleimpl_id"":3}"
|
|
||||||
"assiduite_edit";"/assiduite/1/edit";"ScoView";"POST";"{""etat"":""absent""}"
|
|
||||||
"assiduite_edit";"/assiduite/1/edit";"ScoView";"POST";"{""moduleimpl_id"":2}"
|
|
||||||
"assiduite";"/assiduite/1";"ScoView";"GET";
|
"assiduite";"/assiduite/1";"ScoView";"GET";
|
||||||
|
"assiduite_justificatifs";"/assiduite/1/justificatifs";"ScoView";"GET";
|
||||||
|
"assiduite_justificatifs";"/assiduite/1/justificatifs/long";"ScoView";"GET";
|
||||||
"assiduites_count";"/assiduites/1/count";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count";"ScoView";"GET";
|
||||||
"assiduites_count";"/assiduites/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
|
||||||
"assiduites_count";"/assiduites/1/count/query?etat=retard";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?etat=retard";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?split";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
"assiduites_count";"/assiduites/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
||||||
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=retard";"ScoView";"GET";
|
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1";"ScoView";"GET";
|
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?etat=retard";"ScoView";"GET";
|
|
||||||
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?moduleimpl_id=1";"ScoView";"GET";
|
|
||||||
"assiduites";"/assiduites/1";"ScoView";"GET";
|
"assiduites";"/assiduites/1";"ScoView";"GET";
|
||||||
"assiduites";"/assiduites/1/query?etat=retard";"ScoView";"GET";
|
"assiduites";"/assiduites/1/query?etat=retard";"ScoView";"GET";
|
||||||
"assiduites";"/assiduites/1/query?moduleimpl_id=1";"ScoView";"GET";
|
"assiduites";"/assiduites/1/query?moduleimpl_id=1";"ScoView";"GET";
|
||||||
"departement-create";"/departement/create";"ScoSuperAdmin";"POST";"{""acronym"": ""NEWONE"" , ""visible"": true}"
|
"assiduites";"/assiduites/1/query?with_justifs=";"ScoView";"GET";
|
||||||
"departement-delete";"/departement/NEWONE/delete";"ScoSuperAdmin";"POST";
|
"assiduites_evaluations";"/assiduites/1/evaluations";"ScoView";"GET";
|
||||||
"departement-edit";"/departement/NEWONE/edit";"ScoSuperAdmin";"POST";"{""visible"": false}"
|
"assiduites_group";"/assiduites/group/query?etudids=1,2,3";"ScoView";"GET";
|
||||||
"departement-etudiants";"/departement/id/1/etudiants";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1";"ScoView";"GET";
|
||||||
"departement-etudiants";"/departement/TAPI/etudiants";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?etat=retard";"ScoView";"GET";
|
||||||
"departement-formsemestres_ids";"/departement/id/1/formsemestres_ids";"ScoView";"GET";
|
"assiduites_formsemestre";"/assiduites/formsemestre/1/query?moduleimpl_id=1";"ScoView";"GET";
|
||||||
"departement-formsemestres_ids";"/departement/TAPI/formsemestres_ids";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count";"ScoView";"GET";
|
||||||
"departement-formsemestres-courants";"/departement/id/1/formsemestres_courants";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=retard";"ScoView";"GET";
|
||||||
"departement-formsemestres-courants";"/departement/TAPI/formsemestres_courants";"ScoView";"GET";
|
"assiduites_formsemestre_count";"/assiduites/formsemestre/1/count/query?etat=present,retard&metric=compte,heure";"ScoView";"GET";
|
||||||
"departement-logo";"/departement/id/1/logo/D";"ScoSuperAdmin";"GET";
|
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
||||||
"departement-logo";"/departement/TAPI/logo/D";"ScoSuperAdmin";"GET";
|
"assiduite_create";"/assiduite/1/create";"UsersAdmin";"POST";"[{""date_debut"": ""2023-10-27T08:00"",""date_fin"": ""2023-10-27T10:00"",""etat"": ""absent""}]"
|
||||||
"departement-logos";"/departement/id/1/logos";"ScoSuperAdmin";"GET";
|
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
||||||
"departement-logos";"/departement/TAPI/logos";"ScoSuperAdmin";"GET";
|
"assiduites_create";"/assiduites/create";"UsersAdmin";"POST";"[{""etudid"":-1,""date_debut"": ""2023-10-26T08:00"",""date_fin"": ""2023-10-26T10:00"",""etat"": ""absent""}]"
|
||||||
"departement";"/departement/id/1";"ScoView";"GET";
|
"assiduite_delete";"/assiduite/delete";"UsersAdmin";"POST";"[2,2,3]"
|
||||||
"departement";"/departement/TAPI";"ScoView";"GET";
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"":""absent""}"
|
||||||
"departements-ids";"/departements_ids";"ScoView";"GET";
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""moduleimpl_id"":2}"
|
||||||
"departements";"/departements";"ScoView";"GET";
|
"assiduite_edit";"/assiduite/1/edit";"UsersAdmin";"POST";"{""etat"": ""retard"",""moduleimpl_id"":3}"
|
||||||
"etudiant_formsemestres";"/etudiant/nip/11/formsemestres";"ScoView";"GET";
|
"billets_absence_create";"/billets_absence/create";"UsersAdmin";"POST";"{""etudid"":""1"",""abs_begin"":""2023-10-27T10:00"",""abs_end"":""2023-10-28T10:00"",""description"":""grave malade"",""justified"":""1""}"
|
||||||
"etudiant-formsemestre-bulletin";"/etudiant/etudid/11/formsemestre/1/bulletin";"ScoView";"GET";
|
"departements_list";"/departements";"ScoView";"GET";
|
||||||
#"etudiant-formsemestre-bulletin";"/etudiant/ine/INE11/formsemestre/1/bulletin";"ScoView";"GET";
|
"departements_ids";"/departements_ids";"ScoView";"GET";
|
||||||
#"etudiant-formsemestre-bulletin";"/etudiant/nip/11/formsemestre/1/bulletin";"ScoView";"GET";
|
"departement_by_acronym";"/departement/TAPI";"ScoView";"GET";
|
||||||
#"etudiant-formsemestre-bulletin";"/etudiant/nip/11/formsemestre/1/bulletin/short/pdf";"ScoView";"GET";
|
"departement_get";"/departement/id/1";"ScoView";"GET";
|
||||||
"etudiant-formsemestre-groups";"/etudiant/etudid/11/formsemestre/1/groups";"ScoView";"GET";
|
"departement_create";"/departement/create";"UsersAdmin";"POST";"{""acronym"":""MYDEPT"",""visible"":""1""}"
|
||||||
"etudiant-formsemestres";"/etudiant/etudid/11/formsemestres";"ScoView";"GET";
|
"departement_etudiants";"/departement/TAPI/etudiants";"ScoView";"GET";
|
||||||
"etudiant-formsemestres";"/etudiant/ine/INE11/formsemestres";"ScoView";"GET";
|
"departement_formsemestres_ids";"/departement/TAPI/formsemestres_ids";"ScoView";"GET";
|
||||||
"etudiant";"/etudiant/etudid/11";"ScoView";"GET";
|
"departement_formsemestres_ids_by_id";"/departement/id/1/formsemestres_ids";"ScoView";"GET";
|
||||||
"etudiant";"/etudiant/ine/INE11";"ScoView";"GET";
|
"departement_formsemestres_courants";"/departement/id/1/formsemestres_courants?date_courante=2022-01-01";"ScoView";"GET";
|
||||||
"etudiant";"/etudiant/nip/11";"ScoView";"GET";
|
"etudiants_courants";"/etudiants/courants?date_courante=2022-05-01";"ScoView";"GET";
|
||||||
"etudiants-clef";"/etudiants/etudid/11";"ScoView";"GET";
|
"etudiants_courants";"/etudiants/courants/long?date_courante=2022-05-01";"ScoView";"GET";
|
||||||
"etudiants-clef";"/etudiants/ine/INE11";"ScoView";"GET";
|
"bulletin";"/etudiant/etudid/1/formsemestre/1/bulletin";"ScoView";"GET";
|
||||||
"etudiants-clef";"/etudiants/nip/11";"ScoView";"GET";
|
"etudiant_groups";"/etudiant/etudid/1/formsemestre/1/groups";"ScoView";"GET";
|
||||||
"etudiants-courants";"/etudiants/courants?date_courante=2022-07-20";"ScoView";"GET";
|
"etudiant_edit";"/etudiant/ine/INE1/edit";"UsersAdmin";"POST";"{""prenom"":""Nouveau Prénom"", ""adresses"":[{""email"":""nouvelle@adresse.fr""}]}"
|
||||||
"etudiants-courants";"/etudiants/courants/long?date_courante=2022-07-20";"ScoView";"GET";
|
"etudiant_annotation";"/etudiant/etudid/1/annotation";"UsersAdmin";"POST";"{""comment"":""une annotation sur l'étudiant""}"
|
||||||
"evaluation";"/evaluation/1";"ScoView";"GET";
|
"moduleimpl_evaluations";"/moduleimpl/1/evaluations";"ScoView";"GET";
|
||||||
"evaluation-notes";"/evaluation/1/notes";"ScoView";"GET";
|
"evaluation_notes";"/evaluation/2/notes";"ScoView";"GET";
|
||||||
"formation-export";"/formation/1/export_with_ids";"ScoView";"GET";
|
"evaluation_set_notes";"/evaluation/1/notes/set";"UsersAdmin";"POST";"{""notes"": [[1, 17], [2, ""SUPR""]], ""comment"" : ""sample test""}"
|
||||||
"formation-export";"/formation/1/export";"ScoView";"GET";
|
"evaluation_create";"/moduleimpl/1/evaluation/create";"UsersAdmin";"POST";"{""description"":""Exemple éval.""}"
|
||||||
"formation-referentiel_competences";"/formation/1/referentiel_competences";"ScoView";"GET";
|
|
||||||
"formation";"/formation/1";"ScoView";"GET";
|
|
||||||
"formations_ids";"/formations_ids";"ScoView";"GET";
|
|
||||||
"formations";"/formations";"ScoView";"GET";
|
"formations";"/formations";"ScoView";"GET";
|
||||||
"formsemestre-bulletins";"/formsemestre/1/bulletins";"ScoView";"GET";
|
"formations_ids";"/formations_ids";"ScoView";"GET";
|
||||||
"formsemestre-decisions_jury";"/formsemestre/1/decisions_jury";"ScoView";"GET";
|
"formation_get";"/formation/1";"ScoView";"GET";
|
||||||
"formsemestre-etat_evals";"/formsemestre/1/etat_evals";"ScoView";"GET";
|
"formation_export_by_formation_id";"/formation/1/export";"ScoView";"GET";
|
||||||
"formsemestre-etudiants-query";"/formsemestre/1/etudiants/query?etat=D";"ScoView";"GET";
|
"referentiel_competences";"/formation/1/referentiel_competences";"ScoView";"GET";
|
||||||
"formsemestre-etudiants";"/formsemestre/1/etudiants";"ScoView";"GET";
|
"formation_module_get";"/formation/module/1";"ScoView";"GET";
|
||||||
"formsemestre-etudiants";"/formsemestre/1/etudiants/long";"ScoView";"GET";
|
"formsemestre_get";"/formsemestre/1";"ScoView";"GET";
|
||||||
"formsemestre-partition-create";"/formsemestre/1/partition/create";"ScoSuperAdmin";"POST";"{""partition_name"": ""PART""} "
|
"bulletins";"/formsemestre/1/bulletins";"ScoView";"GET";
|
||||||
"formsemestre-partitions-order";"/formsemestre/1/partitions/order";"ScoSuperAdmin";"POST";"[ 1 ]"
|
"formsemestre_programme";"/formsemestre/1/programme";"ScoView";"GET";
|
||||||
"formsemestre-partitions";"/formsemestre/1/partitions";"ScoView";"GET";
|
"formsemestre_etudiants";"/formsemestre/1/etudiants/query";"ScoView";"GET";
|
||||||
"formsemestre-programme";"/formsemestre/1/programme";"ScoView";"GET";
|
"formsemestre_etat_evaluations";"/formsemestre/1/etat_evals";"ScoView";"GET";
|
||||||
"formsemestre-resultats";"/formsemestre/1/resultats";"ScoView";"GET";
|
"formsemestre_resultat";"/formsemestre/1/resultats";"ScoView";"GET";
|
||||||
"formsemestre";"/formsemestre/1";"ScoView";"GET";
|
"decisions_jury";"/formsemestre/1/decisions_jury";"ScoView";"GET";
|
||||||
"formsemestres-query";"/formsemestres/query?annee_scolaire=2022&etape_apo=A2";"ScoView";"GET";
|
"justificatif";"/justificatif/1";"ScoView";"GET";
|
||||||
"formsemestres-query";"/formsemestres/query?nip=11";"ScoView";"GET";
|
"justificatifs";"/justificatifs/1";"ScoView";"GET";
|
||||||
"group-delete";"/group/2/delete";"ScoSuperAdmin";"POST";
|
"justificatifs";"/justificatifs/1/query?etat=attente";"ScoView";"GET";
|
||||||
"group-edit";"/group/2/edit";"ScoSuperAdmin";"POST";"{""group_name"": ""NEW_GROUP2""}"
|
"justificatifs_dept";"/justificatifs/dept/1";"ScoView";"GET";
|
||||||
"group-etudiants-query";"/group/1/etudiants/query?etat=D";"ScoView";"GET";
|
"justificatifs_formsemestre";"/justificatifs/formsemestre/1";"ScoView";"GET";
|
||||||
"group-etudiants";"/group/1/etudiants";"ScoView";"GET";
|
"justif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""etat"":""valide""}"
|
||||||
"group-remove_etudiant";"/group/1/remove_etudiant/10";"ScoSuperAdmin";"POST";
|
"justif_edit";"/justificatif/1/edit";"UsersAdmin";"POST";"{""raison"":""MEDIC""}"
|
||||||
"group-set_etudiant";"/group/1/set_etudiant/10";"ScoSuperAdmin";"POST";
|
"justif_delete";"/justificatif/delete";"UsersAdmin";"POST";"[2, 2, 3]"
|
||||||
"logo";"/logo/B";"ScoSuperAdmin";"GET";
|
"justif_list";"/justificatif/1/list";"ScoView";"GET";
|
||||||
"logos";"/logos";"ScoSuperAdmin";"GET";
|
"justif_justifies";"/justificatif/1/justifies";"UsersAdmin";"GET";
|
||||||
"moduleimpl-evaluations";"/moduleimpl/1/evaluations";"ScoView";"GET";
|
"logo_list_globals";"/logos";"UsersAdmin";"GET";
|
||||||
"moduleimpl";"/moduleimpl/1";"ScoView";"GET";
|
"logo_get_global";"/logo/B";"UsersAdmin";"GET";
|
||||||
"partition-delete";"/partition/2/delete";"ScoSuperAdmin";"POST";
|
"departement_logos";"/departement/TAPI/logos";"UsersAdmin";"GET";
|
||||||
"partition-edit";"/partition/1/edit";"ScoSuperAdmin";"POST";"{""partition_name"":""P2BIS"", ""numero"":3,""bul_show_rank"":true,""show_in_lists"":false, ""groups_editable"":true}"
|
"moduleimpl_inscriptions";"/moduleimpl/1/inscriptions";"ScoView";"GET";
|
||||||
"partition-group-create";"/partition/1/group/create";"ScoSuperAdmin";"POST";"{""group_name"": ""NEW_GROUP""}"
|
"moduleimpl_notes";"/moduleimpl/1/notes";"ScoView";"GET";
|
||||||
"partition-groups-order";"/partition/1/groups/order";"ScoSuperAdmin";"POST";"[ 1 ]"
|
"partition_info";"/partition/1";"ScoView";"GET";
|
||||||
"partition-remove_etudiant";"/partition/2/remove_etudiant/10";"ScoSuperAdmin";"POST";
|
"formsemestre_partitions";"/formsemestre/1/partitions";"ScoView";"GET";
|
||||||
"partition";"/partition/1";"ScoView";"GET";
|
"group_etudiants";"/group/1/etudiants";"ScoView";"GET";
|
||||||
"permissions";"/permissions";"ScoView";"GET";
|
"group_create";"/partition/1/group/create";"ScoView";"POST";"{""group_name"" : ""Nouveau Groupe""}"
|
||||||
"role-add_permission";"/role/customRole/add_permission/UsersView";"ScoSuperAdmin";"POST";
|
"group_edit";"/group/1/edit";"ScoView";"POST";"{""group_name"":""A1""}"
|
||||||
"role-create";"/role/create/customRole";"ScoSuperAdmin";"POST";"{""permissions"": [""ScoView"", ""UsersView""]}"
|
"group_set_edt_id";"/group/1/set_edt_id/EDT_GR1";"ScoView";"POST";
|
||||||
"role-delete";"/role/customRole/delete";"ScoSuperAdmin";"POST";
|
"partition_edit";"/partition/1/edit";"ScoView";"POST";"{""bul_show_rank"":1}"
|
||||||
"role-edit";"/role/customRole/edit";"ScoSuperAdmin";"POST";"{ ""name"" : ""LaveurDeVitres"", ""permissions"" : [ ""ScoView"" ] }"
|
"user_info";"/user/2";"UsersAdmin";"GET";
|
||||||
"role-remove_permission";"/role/customRole/remove_permission/UsersView";"ScoSuperAdmin";"POST";
|
"user_password";"/user/3/password";"UsersAdmin";"POST";"{""password"" : ""rePlaCemeNT456averylongandcomplicated""}"
|
||||||
"role";"/role/Observateur";"ScoView";"GET";
|
"permissions_list";"/permissions";"UsersAdmin";"GET";
|
||||||
"roles";"/roles";"ScoView";"GET";
|
"role_get";"/role/Ens";"UsersAdmin";"GET";
|
||||||
"test-pdf";"/etudiant/etudid/11/formsemestre/1/bulletin";"ScoView";"GET";
|
"roles_list";"/roles";"UsersAdmin";"GET";
|
||||||
"test-pdf";"/etudiant/etudid/11/formsemestre/1/bulletin/pdf";"ScoView";"GET";
|
"role_create";"/role/create/customRole";"UsersAdmin";"POST";"{""permissions"": [""ScoView"", ""UsersView""]}"
|
||||||
"test-pdf";"/etudiant/etudid/11/formsemestre/1/bulletin/short";"ScoView";"GET";
|
"role_delete";"/role/customRole/delete";"UsersAdmin";"POST";
|
||||||
"test-pdf";"/etudiant/etudid/11/formsemestre/1/bulletin/short/pdf";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/ine/INE11/formsemestre/1/bulletin";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/ine/INE11/formsemestre/1/bulletin/short";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/ine/INE11/formsemestre/1/bulletin/short/pdf";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/nip/11/formsemestre/1/bulletin";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/nip/11/formsemestre/1/bulletin/pdf";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/nip/11/formsemestre/1/bulletin/pdf";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/nip/11/formsemestre/1/bulletin/short";"ScoView";"GET";
|
|
||||||
"test-pdf";"/etudiant/nip/11/formsemestre/1/bulletin/short/pdf";"ScoView";"GET";
|
|
||||||
"user-create";"/user/create";"ScoSuperAdmin";"POST";"{""user_name"": ""alain"", ""dept"": null, ""nom"": ""alain"", ""prenom"": ""bruno"", ""active"": true }"
|
|
||||||
"user-edit";"/user/10/edit";"ScoSuperAdmin";"POST";"{ ""dept"": ""TAPI"", ""nom"": ""alain2"", ""prenom"": ""bruno2"", ""active"": false }"
|
|
||||||
"user-password";"/user/3/password";"ScoSuperAdmin";"POST";"{ ""password"": ""rePlaCemeNT456averylongandcomplicated"" }"
|
|
||||||
"user-password";"/user/3/password";"ScoSuperAdmin";"POST";"{ ""password"": ""too_simple"" }"
|
|
||||||
"user-role-add";"/user/10/role/Observateur/add";"ScoSuperAdmin";"POST";
|
|
||||||
"user-role-remove";"/user/10/role/Observateur/remove";"ScoSuperAdmin";"POST";
|
|
||||||
"user";"/user/1";"ScoView";"GET";
|
|
||||||
"users-query";"/users/query?starts_with=u_";"ScoView";"GET";
|
|
Can't render this file because it contains an unexpected character in line 41 and column 2.
|
@ -10,4 +10,4 @@ from tools.migrate_scodoc7_archives import migrate_scodoc7_dept_archives
|
|||||||
from tools.migrate_scodoc7_logos import migrate_scodoc7_dept_logos
|
from tools.migrate_scodoc7_logos import migrate_scodoc7_dept_logos
|
||||||
from tools.migrate_abs_to_assiduites import migrate_abs_to_assiduites
|
from tools.migrate_abs_to_assiduites import migrate_abs_to_assiduites
|
||||||
from tools.downgrade_assiduites import downgrade_module
|
from tools.downgrade_assiduites import downgrade_module
|
||||||
from tools.create_api_map import gen_api_map
|
from tools.create_api_map import gen_api_map, gen_api_doc
|
||||||
|
@ -4,8 +4,14 @@ Script permettant de générer une carte SVG de l'API de ScoDoc
|
|||||||
Écrit par Matthias HARTMANN
|
Écrit par Matthias HARTMANN
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
import unicodedata
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
|
||||||
class COLORS:
|
class COLORS:
|
||||||
@ -132,7 +138,7 @@ class Token:
|
|||||||
element, x_offset, y_offset
|
element, x_offset, y_offset
|
||||||
)
|
)
|
||||||
# Préparation du lien vers la doc de la route
|
# Préparation du lien vers la doc de la route
|
||||||
href = "#" + self.func_name.replace("_", "-")
|
href = "#" + self.func_name
|
||||||
if self.query and not href.endswith("-query"):
|
if self.query and not href.endswith("-query"):
|
||||||
href += "-query"
|
href += "-query"
|
||||||
question_mark_group = _create_question_mark_group(current_end_coords, href)
|
question_mark_group = _create_question_mark_group(current_end_coords, href)
|
||||||
@ -266,6 +272,13 @@ class Token:
|
|||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
|
def strip_accents(s):
|
||||||
|
"""Retourne la chaîne s séparant les accents et les caractères de base."""
|
||||||
|
return "".join(
|
||||||
|
c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) != "Mn"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _create_svg_element(text, color="rgb(230,156,190)"):
|
def _create_svg_element(text, color="rgb(230,156,190)"):
|
||||||
"""
|
"""
|
||||||
Fonction générale pour créer un élément SVG simple
|
Fonction générale pour créer un élément SVG simple
|
||||||
@ -437,16 +450,15 @@ def _create_question_mark_group(coords, href):
|
|||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
def gen_api_map(app, endpoint_start="api"):
|
def analyze_api_routes(app, endpoint_start: str) -> tuple:
|
||||||
"""
|
"""Parcours de toutes les routes de l'application
|
||||||
Fonction permettant de générer une carte SVG de l'API de ScoDoc
|
analyse docstrings
|
||||||
Elle récupère les routes de l'API et les transforme en un arbre de Token
|
|
||||||
puis génère un fichier SVG à partir de cet arbre
|
|
||||||
"""
|
"""
|
||||||
# Création du token racine
|
# Création du token racine
|
||||||
api_map = Token("")
|
api_map = Token("")
|
||||||
|
|
||||||
# Parcours de toutes les routes de l'application
|
doctable_lines: dict[str, dict] = {}
|
||||||
|
|
||||||
for rule in app.url_map.iter_rules():
|
for rule in app.url_map.iter_rules():
|
||||||
# On ne garde que les routes de l'API / APIWEB
|
# On ne garde que les routes de l'API / APIWEB
|
||||||
if not rule.endpoint.lower().startswith(endpoint_start.lower()):
|
if not rule.endpoint.lower().startswith(endpoint_start.lower()):
|
||||||
@ -461,8 +473,8 @@ def gen_api_map(app, endpoint_start="api"):
|
|||||||
|
|
||||||
# Récupération de la fonction associée à la route
|
# Récupération de la fonction associée à la route
|
||||||
func = app.view_functions[rule.endpoint]
|
func = app.view_functions[rule.endpoint]
|
||||||
func_name = parse_doc_name(func.__doc__ or "") or func.__name__
|
doc_dict = _parse_doc_string(func.__doc__ or "")
|
||||||
|
func_name = doc_dict.get("DOC_ANCHOR", [None])[0] or func.__name__
|
||||||
# Pour chaque segment de la route
|
# Pour chaque segment de la route
|
||||||
for i, segment in enumerate(segments):
|
for i, segment in enumerate(segments):
|
||||||
# On cherche si le segment est déjà un enfant du token courant
|
# On cherche si le segment est déjà un enfant du token courant
|
||||||
@ -500,8 +512,55 @@ def gen_api_map(app, endpoint_start="api"):
|
|||||||
child.method = method
|
child.method = method
|
||||||
current_token.add_child(child)
|
current_token.add_child(child)
|
||||||
|
|
||||||
|
href = func_name
|
||||||
|
if child.query and not href.endswith("-query"):
|
||||||
|
href += "-query"
|
||||||
|
|
||||||
|
# category
|
||||||
|
|
||||||
|
category: str = func.__module__.replace("app.api.", "")
|
||||||
|
mod_doc: str = sys.modules[func.__module__].__doc__ or ""
|
||||||
|
mod_doc_dict: dict = _parse_doc_string(mod_doc)
|
||||||
|
if mod_doc_dict.get("CATEGORY"):
|
||||||
|
category = mod_doc_dict["CATEGORY"][0].strip()
|
||||||
|
|
||||||
|
permissions: str
|
||||||
|
try:
|
||||||
|
permissions: str = ", ".join(
|
||||||
|
sorted(Permission.permissions_names(func.scodoc_permission))
|
||||||
|
)
|
||||||
|
except AttributeError:
|
||||||
|
permissions = "Aucune permission requise"
|
||||||
|
|
||||||
|
if func_name not in doctable_lines:
|
||||||
|
doctable_lines[func_name] = {
|
||||||
|
"method": method,
|
||||||
|
"nom": func_name,
|
||||||
|
"href": href,
|
||||||
|
"query": doc_dict.get("QUERY", "") != "",
|
||||||
|
"permission": permissions,
|
||||||
|
"description": doc_dict.get("", ""),
|
||||||
|
"params": doc_dict.get("PARAMS", ""),
|
||||||
|
"category": doc_dict.get("CATEGORY", [False])[0] or category,
|
||||||
|
"samples": doc_dict.get("SAMPLES"),
|
||||||
|
}
|
||||||
|
|
||||||
# On met à jour le token courant pour le prochain segment
|
# On met à jour le token courant pour le prochain segment
|
||||||
current_token = child
|
current_token = child
|
||||||
|
if func_name in doctable_lines: # endpoint déjà ajouté, ajoute au besoin route
|
||||||
|
doctable_lines[func_name]["routes"] = doctable_lines[func_name].get(
|
||||||
|
"routes", []
|
||||||
|
) + [rule.rule]
|
||||||
|
return api_map, doctable_lines
|
||||||
|
|
||||||
|
|
||||||
|
# point d'entrée de la commande `flask gen-api-map`
|
||||||
|
def gen_api_map(api_map: Token, doctable_lines: dict[str, dict]) -> str:
|
||||||
|
"""
|
||||||
|
Fonction permettant de générer une carte SVG de l'API de ScoDoc
|
||||||
|
Elle récupère les routes de l'API et les transforme en un arbre de Token
|
||||||
|
puis génère un fichier SVG à partir de cet arbre
|
||||||
|
"""
|
||||||
|
|
||||||
# On génère le SVG à partir de l'arbre de Token
|
# On génère le SVG à partir de l'arbre de Token
|
||||||
generate_svg(api_map.to_svg_group(), "/tmp/api_map.svg")
|
generate_svg(api_map.to_svg_group(), "/tmp/api_map.svg")
|
||||||
@ -510,6 +569,11 @@ def gen_api_map(app, endpoint_start="api"):
|
|||||||
+ "Vous pouvez la consulter à l'adresse suivante : /tmp/api_map.svg"
|
+ "Vous pouvez la consulter à l'adresse suivante : /tmp/api_map.svg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On génère le tableau à partir de doctable_lines
|
||||||
|
table = _gen_table(sorted(doctable_lines.values(), key=lambda x: x["nom"]))
|
||||||
|
_write_gen_table(table)
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
def _get_bbox(element, x_offset=0, y_offset=0):
|
def _get_bbox(element, x_offset=0, y_offset=0):
|
||||||
"""
|
"""
|
||||||
@ -614,9 +678,47 @@ def generate_svg(element, fname):
|
|||||||
tree.write(fname, encoding="utf-8", xml_declaration=True)
|
tree.write(fname, encoding="utf-8", xml_declaration=True)
|
||||||
|
|
||||||
|
|
||||||
def _get_doc_lines(keyword, doc) -> list[str]:
|
def _parse_doc_string(doc_string: str) -> dict[str, list[str]]:
|
||||||
|
"""Parse doc string and extract a dict:
|
||||||
|
{
|
||||||
|
"" : description_lines,
|
||||||
|
"keyword" : lines
|
||||||
|
}
|
||||||
|
|
||||||
|
In the docstring, each keyword is associated to a section like
|
||||||
|
|
||||||
|
KEYWORD
|
||||||
|
-------
|
||||||
|
...
|
||||||
|
(blank line)
|
||||||
|
|
||||||
|
All non blank lines not associated to a keyword go to description.
|
||||||
|
"""
|
||||||
|
doc_dict = {}
|
||||||
|
matches = re.finditer(
|
||||||
|
r"^\s*(?P<kw>[A-Z_\-]+)$\n^\s*-+\n(?P<txt>(^(?!\s*$).+$\n?)+)",
|
||||||
|
doc_string,
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
|
description = ""
|
||||||
|
i = 0
|
||||||
|
for match in matches:
|
||||||
|
start, end = match.span()
|
||||||
|
description += doc_string[i:start]
|
||||||
|
doc_dict[match.group("kw")] = [
|
||||||
|
x.strip() for x in match.group("txt").split("\n") if x.strip()
|
||||||
|
]
|
||||||
|
i = end
|
||||||
|
|
||||||
|
description += doc_string[i:]
|
||||||
|
doc_dict[""] = description.split("\n")
|
||||||
|
return doc_dict
|
||||||
|
|
||||||
|
|
||||||
|
def _get_doc_lines(keyword, doc_string: str) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Renvoie les lignes de la doc qui suivent le mot clé keyword
|
Renvoie les lignes de la doc qui suivent le mot clé keyword
|
||||||
|
Attention : s'arrête à la première ligne vide
|
||||||
|
|
||||||
La doc doit contenir des lignes de la forme:
|
La doc doit contenir des lignes de la forme:
|
||||||
|
|
||||||
@ -626,7 +728,7 @@ def _get_doc_lines(keyword, doc) -> list[str]:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupérer les lignes de la doc
|
# Récupérer les lignes de la doc
|
||||||
lines = [line.strip() for line in doc.split("\n")]
|
lines = [line.strip() for line in doc_string.split("\n")]
|
||||||
# On cherche la ligne "KEYWORD" et on vérifie que la ligne suivante est "-----"
|
# On cherche la ligne "KEYWORD" et on vérifie que la ligne suivante est "-----"
|
||||||
# Si ce n'est pas le cas, on renvoie un dictionnaire vide
|
# Si ce n'est pas le cas, on renvoie un dictionnaire vide
|
||||||
try:
|
try:
|
||||||
@ -638,27 +740,18 @@ def _get_doc_lines(keyword, doc) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
# On récupère les lignes de la doc qui correspondent au keyword (enfin on espère)
|
# On récupère les lignes de la doc qui correspondent au keyword (enfin on espère)
|
||||||
kw_lines = lines[kw_index + 2 :]
|
kw_lines = lines[kw_index + 2 :]
|
||||||
|
|
||||||
|
# On s'arrête à la première ligne vide
|
||||||
|
first_empty_line: int
|
||||||
|
try:
|
||||||
|
first_empty_line: int = kw_lines.index("")
|
||||||
|
except ValueError:
|
||||||
|
first_empty_line = len(kw_lines)
|
||||||
|
kw_lines = kw_lines[:first_empty_line]
|
||||||
return kw_lines
|
return kw_lines
|
||||||
|
|
||||||
|
|
||||||
def parse_doc_name(doc):
|
def parse_query_doc(doc_string: str) -> dict[str, str]:
|
||||||
"""
|
|
||||||
renvoie le nom de la route à partir de la docstring
|
|
||||||
|
|
||||||
La doc doit contenir des lignes de la forme:
|
|
||||||
|
|
||||||
DOC_ANCHOR
|
|
||||||
----------
|
|
||||||
nom_de_la_route
|
|
||||||
|
|
||||||
Il ne peut y avoir qu'une seule ligne suivant -----
|
|
||||||
|
|
||||||
"""
|
|
||||||
name_lines: list[str] = _get_doc_lines("DOC_ANCHOR", doc)
|
|
||||||
return name_lines[0] if name_lines else None
|
|
||||||
|
|
||||||
|
|
||||||
def parse_query_doc(doc):
|
|
||||||
"""
|
"""
|
||||||
renvoie un dictionnaire {param: <type:nom_param>} (ex: {assiduite_id : <int:assiduite_id>})
|
renvoie un dictionnaire {param: <type:nom_param>} (ex: {assiduite_id : <int:assiduite_id>})
|
||||||
|
|
||||||
@ -673,7 +766,7 @@ def parse_query_doc(doc):
|
|||||||
Dès qu'une ligne ne respecte pas ce format (voir regex dans la fonction), on arrête de parser
|
Dès qu'une ligne ne respecte pas ce format (voir regex dans la fonction), on arrête de parser
|
||||||
Attention, la ligne ----- doit être collée contre QUERY et contre le premier paramètre
|
Attention, la ligne ----- doit être collée contre QUERY et contre le premier paramètre
|
||||||
"""
|
"""
|
||||||
query_lines: list[str] = _get_doc_lines("QUERY", doc)
|
query_lines: list[str] = _get_doc_lines("QUERY", doc_string)
|
||||||
|
|
||||||
query = {}
|
query = {}
|
||||||
regex = re.compile(r"^(\w+):(<.+>)$")
|
regex = re.compile(r"^(\w+):(<.+>)$")
|
||||||
@ -691,33 +784,190 @@ def parse_query_doc(doc):
|
|||||||
return query
|
return query
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def _gen_table_line(doctable: dict = None):
|
||||||
# Exemple d'utilisation de la classe Token
|
"""
|
||||||
# Exemple simple de création d'un arbre de Token
|
Génère une ligne de tableau markdown
|
||||||
|
|
||||||
root = Token("api")
|
| nom de la route| methode HTTP| Permission |
|
||||||
child1 = Token("assiduites", leaf=True)
|
"""
|
||||||
child1.func_name = "assiduites_get"
|
|
||||||
child2 = Token("count")
|
nom, method, permission = (
|
||||||
child22 = Token("all")
|
doctable.get("nom", ""),
|
||||||
child23 = Token(
|
doctable.get("method", ""),
|
||||||
"query",
|
doctable.get("permission", ""),
|
||||||
query={
|
|
||||||
"etat": "<string:etat>",
|
|
||||||
"moduleimpl_id": "<int:moduleimpl_id>",
|
|
||||||
"count": "<int:count>",
|
|
||||||
"formsemestre_id": "<int:formsemestre_id>",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
child3 = Token("justificatifs", "POST")
|
|
||||||
child3.func_name = "justificatifs_post"
|
|
||||||
|
|
||||||
root.add_child(child1)
|
if doctable is None:
|
||||||
child1.add_child(child2)
|
doctable = {}
|
||||||
child2.add_child(child22)
|
|
||||||
child2.add_child(child23)
|
|
||||||
root.add_child(child3)
|
|
||||||
|
|
||||||
group_element = root.to_svg_group()
|
lien: str = doctable.get("href", nom)
|
||||||
|
|
||||||
generate_svg(group_element, "/tmp/api_map.svg")
|
doctable["query"]: bool
|
||||||
|
if doctable.get("query") and not lien.endswith("-query"):
|
||||||
|
lien += "-query"
|
||||||
|
|
||||||
|
nav: str = f"[{nom}]({'#'+lien})"
|
||||||
|
|
||||||
|
table: str = "|"
|
||||||
|
for string in [nav, method, doctable.get("permissions") or permission]:
|
||||||
|
table += f" {string} |"
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_table_head() -> str:
|
||||||
|
"""
|
||||||
|
Génère la première ligne du tableau markdown
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: str = "| Route | Méthode | Permission |"
|
||||||
|
line: str = "|---|---|---|"
|
||||||
|
|
||||||
|
return f"{headers}\n{line}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_table(lines: list[dict]) -> str:
|
||||||
|
"""
|
||||||
|
Génère un tableau markdown à partir d'une liste de lignes
|
||||||
|
|
||||||
|
lines : liste de dictionnaire au format doc_lines.
|
||||||
|
"""
|
||||||
|
table = _gen_table_head()
|
||||||
|
table += "\n".join([_gen_table_line(line) for line in lines])
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_csv_line(doc_line: dict) -> str:
|
||||||
|
"""
|
||||||
|
Génère les lignes de tableau csv en fonction d'une route (doc_line)
|
||||||
|
|
||||||
|
format :
|
||||||
|
"entry_name";"url";"permission";"method";"content"
|
||||||
|
"""
|
||||||
|
|
||||||
|
entry_name: str = doc_line.get("nom", "")
|
||||||
|
method: str = doc_line.get("method", "GET")
|
||||||
|
permission: str = (
|
||||||
|
"UsersAdmin" if doc_line.get("permission") != "ScoView" else "ScoView"
|
||||||
|
)
|
||||||
|
|
||||||
|
samples: list[str] = doc_line.get("samples", [])
|
||||||
|
csv_lines: list[str] = []
|
||||||
|
for sample in samples:
|
||||||
|
fragments = sample.split(";", maxsplit=1)
|
||||||
|
if len(fragments) == 2:
|
||||||
|
url, content = fragments
|
||||||
|
elif len(fragments) == 1:
|
||||||
|
url, content = fragments[0], ""
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Error: sample invalide: {sample}")
|
||||||
|
csv_line = f'"{entry_name}";"{url}";"{permission}";"{method}";'
|
||||||
|
if content:
|
||||||
|
csv_line += f'"{content}"'
|
||||||
|
csv_lines.append(csv_line)
|
||||||
|
|
||||||
|
return "\n".join(csv_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_csv(lines: list[dict], filename: str = "/tmp/samples.csv") -> str:
|
||||||
|
"""
|
||||||
|
Génère un fichier csv à partir d'une liste de lignes
|
||||||
|
|
||||||
|
lines : liste de dictionnaire au format doc_lines.
|
||||||
|
"""
|
||||||
|
csv = '"entry_name";"url";"permission";"method";"content"\n'
|
||||||
|
csv += "\n".join(
|
||||||
|
[_gen_csv_line(line) for line in lines if line.get("samples") is not None]
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(filename, "w", encoding="UTF-8") as f:
|
||||||
|
f.write(csv)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Les samples ont été générés avec succès. Vous pouvez le consulter à l'adresse suivante : {filename}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_gen_table(table: str, filename: str = "/tmp/api_table.md"):
|
||||||
|
"""Ecriture du fichier md avec la table"""
|
||||||
|
with open(filename, "w", encoding="UTF-8") as f:
|
||||||
|
f.write(table)
|
||||||
|
print(
|
||||||
|
f"Le tableau a été généré avec succès. Vous pouvez le consulter à l'adresse suivante : {filename}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def doc_route(doctable: dict) -> str:
|
||||||
|
"""Generate markdown doc for a route"""
|
||||||
|
jinja_obj: dict = {}
|
||||||
|
jinja_obj.update(doctable)
|
||||||
|
jinja_obj["nom"] = doctable["nom"].strip() # on retire les caractères blancs
|
||||||
|
|
||||||
|
if doctable.get("samples") is not None:
|
||||||
|
jinja_obj["sample"] = {
|
||||||
|
"nom": f"{jinja_obj['nom']}.json",
|
||||||
|
"href": f"{jinja_obj['nom']}.json.md",
|
||||||
|
}
|
||||||
|
|
||||||
|
jinja_obj["query"]: bool
|
||||||
|
if jinja_obj["query"]:
|
||||||
|
jinja_obj["nom"] += "(-query)"
|
||||||
|
|
||||||
|
if doctable.get("params"):
|
||||||
|
jinja_obj["params"] = []
|
||||||
|
for param in doctable["params"]:
|
||||||
|
frags = param.split(":", maxsplit=1)
|
||||||
|
if len(frags) == 2:
|
||||||
|
name, descr = frags
|
||||||
|
jinja_obj["params"].append(
|
||||||
|
{"nom": name.strip(), "description": descr.strip()}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"Warning: {doctable['nom']} : invalid PARAMS {param}")
|
||||||
|
if doctable.get("description"):
|
||||||
|
descr = "\n".join(s for s in doctable["description"])
|
||||||
|
jinja_obj["description"] = descr.strip()
|
||||||
|
|
||||||
|
return render_template("doc/apidoc.j2", doc=jinja_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_api_doc(app, endpoint_start="api."):
|
||||||
|
"commande gen-api-doc"
|
||||||
|
api_map, doctable_lines = analyze_api_routes(app, endpoint_start)
|
||||||
|
|
||||||
|
mddoc: str = ""
|
||||||
|
|
||||||
|
categories: dict = {}
|
||||||
|
for value in doctable_lines.values():
|
||||||
|
category = value["category"].capitalize()
|
||||||
|
if category not in categories:
|
||||||
|
categories[category] = []
|
||||||
|
categories[category].append(value)
|
||||||
|
|
||||||
|
# sort categories by name
|
||||||
|
categories: dict = dict(
|
||||||
|
sorted(categories.items(), key=lambda x: strip_accents(x[0]))
|
||||||
|
)
|
||||||
|
|
||||||
|
category: str
|
||||||
|
routes: list[dict]
|
||||||
|
for category, routes in categories.items():
|
||||||
|
# sort routes by name
|
||||||
|
routes.sort(key=lambda x: strip_accents(x["nom"]))
|
||||||
|
|
||||||
|
mddoc += f"### API {category.capitalize()}\n\n"
|
||||||
|
for route in routes:
|
||||||
|
mddoc += doc_route(route)
|
||||||
|
mddoc += "\n\n"
|
||||||
|
|
||||||
|
table_api = gen_api_map(api_map, doctable_lines)
|
||||||
|
mdpage = render_template("doc/ScoDoc9API.j2", doc_api=mddoc, table_api=table_api)
|
||||||
|
_gen_csv(list(doctable_lines.values()))
|
||||||
|
|
||||||
|
fname = "/tmp/ScoDoc9API.md"
|
||||||
|
with open(fname, "w", encoding="utf-8") as f:
|
||||||
|
f.write(mdpage)
|
||||||
|
print(
|
||||||
|
"La documentation API a été générée avec succès. "
|
||||||
|
f"Vous pouvez la consulter à l'adresse suivante : {fname}"
|
||||||
|
)
|
||||||
|
@ -211,10 +211,17 @@ def create_formsemestre(
|
|||||||
)
|
)
|
||||||
db.session.add(modimpl)
|
db.session.add(modimpl)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
# Partition par défaut (requise):
|
||||||
partition_id = sco_groups.partition_create(
|
partition_id = sco_groups.partition_create(
|
||||||
formsemestre.id, default=True, redirect=False
|
formsemestre.id, default=True, redirect=False
|
||||||
)
|
)
|
||||||
group = sco_groups.create_group(partition_id, default=True)
|
sco_groups.create_group(partition_id, default=True)
|
||||||
|
# Ajoute partition normale, TD avec groupes A et B:
|
||||||
|
partition_id = sco_groups.partition_create(
|
||||||
|
formsemestre.id, partition_name="TD", redirect=False
|
||||||
|
)
|
||||||
|
sco_groups.create_group(partition_id, group_name="A")
|
||||||
|
sco_groups.create_group(partition_id, group_name="B")
|
||||||
return formsemestre
|
return formsemestre
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user