forked from ScoDoc/ScoDoc
docstrings API
This commit is contained in:
parent
71639606fa
commit
d37ce3f8d9
@ -6,6 +6,11 @@
|
||||
|
||||
"""
|
||||
API : billets d'absences
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Billets d'absence
|
||||
|
||||
"""
|
||||
|
||||
from flask import g, request
|
||||
@ -29,7 +34,7 @@ from app.scodoc.sco_permissions import Permission
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
return [billet.to_dict() for billet in billets]
|
||||
|
||||
@ -41,7 +46,20 @@ def billets_absence_etudiant(etudid: int):
|
||||
@permission_required(Permission.AbsAddBillet)
|
||||
@as_json
|
||||
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
|
||||
}
|
||||
```
|
||||
"""
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
etudid = data.get("etudid")
|
||||
abs_begin = data.get("abs_begin")
|
||||
|
@ -9,6 +9,11 @@
|
||||
|
||||
Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/),
|
||||
mais évidemment pas sur l'API web (/ScoDoc/<dept>/api).
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Département
|
||||
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
@ -27,24 +32,13 @@ from app.scodoc.sco_permissions import Permission
|
||||
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")
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def departements_list():
|
||||
"""Liste les départements"""
|
||||
"""Liste tous les départements."""
|
||||
return [dept.to_dict(with_dept_name=True) for dept in Departement.query]
|
||||
|
||||
|
||||
@ -54,7 +48,7 @@ def departements_list():
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def departements_ids():
|
||||
"""Liste des ids de départements"""
|
||||
"""Liste des ids de tous les départements."""
|
||||
return [dept.id for dept in Departement.query]
|
||||
|
||||
|
||||
@ -68,6 +62,7 @@ def departement_by_acronym(acronym: str):
|
||||
Info sur un département. Accès par acronyme.
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"acronym": "TAPI",
|
||||
@ -76,6 +71,7 @@ def departement_by_acronym(acronym: str):
|
||||
"visible": true,
|
||||
"date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
|
||||
}
|
||||
```
|
||||
"""
|
||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||
return dept.to_dict(with_dept_name=True)
|
||||
@ -102,11 +98,15 @@ def departement_by_id(dept_id: int):
|
||||
def departement_create():
|
||||
"""
|
||||
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,
|
||||
"visible": bool,
|
||||
}
|
||||
```
|
||||
"""
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
acronym = str(data.get("acronym", ""))
|
||||
@ -130,8 +130,10 @@ def departement_create():
|
||||
@as_json
|
||||
def departement_edit(acronym):
|
||||
"""
|
||||
Edition d'un département: seul visible peut être modifié
|
||||
The request content type should be "application/json":
|
||||
Édition d'un département: seul le champ `visible` peut être modifié.
|
||||
|
||||
DATA
|
||||
----
|
||||
{
|
||||
"visible": bool,
|
||||
}
|
||||
@ -155,7 +157,7 @@ def departement_edit(acronym):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
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()
|
||||
acronym = dept.acronym
|
||||
@ -172,11 +174,14 @@ def departement_delete(acronym):
|
||||
@as_json
|
||||
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
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
[
|
||||
{
|
||||
"civilite": "M",
|
||||
@ -191,6 +196,7 @@ def departement_etudiants(acronym: str):
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
"""
|
||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||
return [etud.to_dict_short() for etud in dept.etudiants]
|
||||
@ -215,7 +221,7 @@ def departement_etudiants_by_id(dept_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def departement_formsemestres_ids(acronym: str):
|
||||
"""liste des ids formsemestre du département"""
|
||||
"""Liste des ids de tous les formsemestres du département."""
|
||||
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
|
||||
return [formsemestre.id for formsemestre in dept.formsemestres]
|
||||
|
||||
@ -226,7 +232,7 @@ def departement_formsemestres_ids(acronym: str):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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."""
|
||||
dept = Departement.query.get_or_404(dept_id)
|
||||
return [formsemestre.id for formsemestre in dept.formsemestres]
|
||||
|
||||
@ -239,7 +245,7 @@ def departement_formsemestres_ids_by_id(dept_id: int):
|
||||
@as_json
|
||||
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
|
||||
(au format ISO).
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
"""
|
||||
API : accès aux étudiants
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Étudiants
|
||||
"""
|
||||
from datetime import datetime
|
||||
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 import sco_etud
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc import sco_photos
|
||||
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
|
||||
|
||||
# Un exemple:
|
||||
@ -103,6 +106,7 @@ def etudiants_courants(long: bool = False):
|
||||
date_courante:<string:date_courante>
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1234,
|
||||
@ -115,6 +119,7 @@ def etudiants_courants(long: bool = False):
|
||||
}
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
En format "long": voir documentation.
|
||||
|
||||
@ -160,10 +165,13 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
|
||||
"""
|
||||
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
|
||||
|
||||
PARAMS
|
||||
------
|
||||
etudid : l'etudid de l'étudiant
|
||||
nip : le code nip 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.
|
||||
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
|
||||
"""
|
||||
@ -197,6 +205,8 @@ def etudiant_get_photo_image(etudid: int = None, nip: str = None, ine: str = Non
|
||||
-----
|
||||
size:<string:size>
|
||||
|
||||
PARAMS
|
||||
------
|
||||
etudid : l'etudid de l'étudiant
|
||||
nip : le code nip de l'étudiant
|
||||
ine : le code ine de l'étudiant
|
||||
@ -269,9 +279,12 @@ def etudiant_set_photo_image(etudid: int = None):
|
||||
@as_json
|
||||
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||
"""
|
||||
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
|
||||
toujours une liste.
|
||||
Info sur le ou les étudiants correspondants.
|
||||
|
||||
Comme `/etudiant` mais renvoie toujours une liste.
|
||||
|
||||
Si non trouvé, liste vide, pas d'erreur.
|
||||
|
||||
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.).
|
||||
"""
|
||||
@ -304,8 +317,9 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
||||
"""Liste des étudiants dont le nom débute par start.
|
||||
Si start fait moins de min_len=3 caractères, liste vide.
|
||||
"""Liste des étudiants dont le nom débute par `start`.
|
||||
|
||||
Si `start` fait moins de `min_len=3` caractères, liste vide.
|
||||
La casse et les accents sont ignorés.
|
||||
"""
|
||||
if len(start) < min_len:
|
||||
@ -340,13 +354,13 @@ def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
||||
@as_json
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
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:
|
||||
q_etud = Identite.query.filter_by(id=etudid)
|
||||
@ -475,10 +489,13 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
||||
"""
|
||||
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué
|
||||
|
||||
PARAMS
|
||||
------
|
||||
formsemestre_id : l'id d'un formsemestre
|
||||
etudid : l'etudid d'un étudiant
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
[
|
||||
{
|
||||
"partition_id": 1,
|
||||
@ -503,6 +520,7 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
||||
"group_name": "A"
|
||||
}
|
||||
]
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -530,9 +548,12 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
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.
|
||||
|
||||
L'étudiant créé n'est pas inscrit à un semestre.
|
||||
|
||||
Champs requis: nom, prenom (sauf si config sans prénom), dept (string:acronyme)
|
||||
"""
|
||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||
@ -602,7 +623,10 @@ def etudiant_edit(
|
||||
):
|
||||
"""Édition des données étudiant (identité, admission, adresses).
|
||||
|
||||
`code_type`: `etudid`, `ine` ou `nip`.
|
||||
PARAMS
|
||||
------
|
||||
`code_type`: le type du code, `etudid`, `ine` ou `nip`.
|
||||
`code`: la valeur du code
|
||||
"""
|
||||
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
||||
if not ok:
|
||||
@ -642,7 +666,23 @@ def etudiant_annotation(
|
||||
code_type: str = "etudid",
|
||||
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
|
||||
}
|
||||
```
|
||||
"""
|
||||
if not current_user.has_permission(Permission.ViewEtudData):
|
||||
return json_error(403, "non autorisé (manque ViewEtudData)")
|
||||
ok, etud = _get_etud_by_code(code_type, code, g.scodoc_dept)
|
||||
@ -679,7 +719,13 @@ def etudiant_annotation_delete(
|
||||
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)
|
||||
if not ok:
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux évaluations
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Évaluations
|
||||
"""
|
||||
from flask import g, request
|
||||
from flask_json import as_json
|
||||
@ -32,11 +36,14 @@ import app.scodoc.sco_utils as scu
|
||||
def get_evaluation(evaluation_id: int):
|
||||
"""Description d'une évaluation.
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
{
|
||||
'coefficient': 1.0,
|
||||
'date_debut': '2016-01-04T08:30:00',
|
||||
'date_fin': '2016-01-04T12:30:00',
|
||||
'description': 'TP NI9219 Température',
|
||||
'description': 'TP Température',
|
||||
'evaluation_type': 0,
|
||||
'id': 15797,
|
||||
'moduleimpl_id': 1234,
|
||||
@ -50,6 +57,7 @@ def get_evaluation(evaluation_id: int):
|
||||
'publish_incomplete': False,
|
||||
'visibulletin': True
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||
if g.scodoc_dept:
|
||||
@ -70,11 +78,13 @@ def get_evaluation(evaluation_id: int):
|
||||
@as_json
|
||||
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
|
||||
|
||||
Exemple de résultat : voir /evaluation
|
||||
Exemple de résultat : voir `/evaluation`.
|
||||
"""
|
||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||
return [evaluation.to_dict_api() for evaluation in modimpl.evaluations]
|
||||
@ -88,8 +98,10 @@ def moduleimpl_evaluations(moduleimpl_id: int):
|
||||
@as_json
|
||||
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
|
||||
|
||||
Exemple de résultat :
|
||||
@ -145,13 +157,18 @@ def evaluation_notes(evaluation_id: int):
|
||||
@as_json
|
||||
def evaluation_set_notes(evaluation_id: int): # evaluation-notes-set
|
||||
"""Écriture de notes dans une évaluation.
|
||||
The request content type should be "application/json",
|
||||
and contains:
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
{
|
||||
'notes' : [ [etudid, value], ... ],
|
||||
'comment' : optional string
|
||||
}
|
||||
Result:
|
||||
```
|
||||
|
||||
Résultat:
|
||||
|
||||
- nb_changed: nombre de notes changées
|
||||
- nb_suppress: nombre de notes effacées
|
||||
- etudids_with_decision: liste des etudiants dont la note a changé
|
||||
@ -186,8 +203,9 @@ def evaluation_set_notes(evaluation_id: int): # evaluation-notes-set
|
||||
@as_json
|
||||
def evaluation_create(moduleimpl_id: int):
|
||||
"""Création d'une évaluation.
|
||||
The request content type should be "application/json",
|
||||
and contains:
|
||||
|
||||
DATA
|
||||
----
|
||||
{
|
||||
"description" : str,
|
||||
"evaluation_type" : int, // {0,1,2} default 0 (normale)
|
||||
@ -200,7 +218,8 @@ def evaluation_create(moduleimpl_id: int):
|
||||
"coefficient" : float, // si non spécifié, 1.0
|
||||
"poids" : { ue_id : poids } // optionnel
|
||||
}
|
||||
Result: l'évaluation créée.
|
||||
|
||||
Résultat: l'évaluation créée.
|
||||
"""
|
||||
moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
||||
if not moduleimpl.can_edit_evaluation(current_user):
|
||||
@ -250,7 +269,7 @@ def evaluation_create(moduleimpl_id: int):
|
||||
@as_json
|
||||
def evaluation_delete(evaluation_id: int):
|
||||
"""Suppression d'une évaluation.
|
||||
Efface aussi toutes ses notes
|
||||
Efface aussi toutes ses notes.
|
||||
"""
|
||||
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||
if g.scodoc_dept:
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux formations
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Formations
|
||||
"""
|
||||
|
||||
from flask import flash, g, request
|
||||
@ -38,7 +42,8 @@ from app.scodoc.sco_permissions import Permission
|
||||
@as_json
|
||||
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).
|
||||
"""
|
||||
query = Formation.query
|
||||
if g.scodoc_dept:
|
||||
@ -58,7 +63,7 @@ def formations_ids():
|
||||
Retourne la liste de toutes les id de formations
|
||||
(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 ]`.
|
||||
"""
|
||||
query = Formation.query
|
||||
if g.scodoc_dept:
|
||||
@ -74,11 +79,12 @@ def formations_ids():
|
||||
@as_json
|
||||
def formation_by_id(formation_id: int):
|
||||
"""
|
||||
La formation d'id donné
|
||||
La formation d'id donné.
|
||||
|
||||
formation_id : l'id d'une formation
|
||||
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"acronyme": "BUT R&T",
|
||||
@ -92,6 +98,7 @@ def formation_by_id(formation_id: int):
|
||||
"referentiel_competence_id": null,
|
||||
"formation_id": 1
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = Formation.query.filter_by(id=formation_id)
|
||||
if g.scodoc_dept:
|
||||
@ -123,10 +130,14 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
||||
"""
|
||||
Retourne la formation, avec UE, matières, modules
|
||||
|
||||
PARAMS
|
||||
------
|
||||
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 :
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"acronyme": "BUT R&T",
|
||||
@ -214,6 +225,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = Formation.query.filter_by(id=formation_id)
|
||||
if g.scodoc_dept:
|
||||
@ -236,11 +248,8 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
||||
@as_json
|
||||
def referentiel_competences(formation_id: int):
|
||||
"""
|
||||
Retourne le référentiel de compétences
|
||||
|
||||
formation_id : l'id d'une formation
|
||||
|
||||
return null si pas de référentiel associé.
|
||||
Retourne le référentiel de compétences de la formation
|
||||
ou null si pas de référentiel associé.
|
||||
"""
|
||||
query = Formation.query.filter_by(id=formation_id)
|
||||
if g.scodoc_dept:
|
||||
@ -259,8 +268,14 @@ def referentiel_competences(formation_id: int):
|
||||
@as_json
|
||||
def ue_set_parcours(ue_id: int):
|
||||
"""Associe UE et parcours BUT.
|
||||
|
||||
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)
|
||||
if g.scodoc_dept:
|
||||
@ -293,7 +308,7 @@ def ue_set_parcours(ue_id: int):
|
||||
@permission_required(Permission.EditFormation)
|
||||
@as_json
|
||||
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)
|
||||
if g.scodoc_dept:
|
||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||
@ -323,7 +338,7 @@ def ue_assoc_niveau(ue_id: int, niveau_id: int):
|
||||
@as_json
|
||||
def ue_desassoc_niveau(ue_id: int):
|
||||
"""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)
|
||||
if g.scodoc_dept:
|
||||
@ -345,7 +360,7 @@ def ue_desassoc_niveau(ue_id: int):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def get_ue(ue_id: int):
|
||||
"""Renvoie l'UE"""
|
||||
"""Renvoie l'UE."""
|
||||
query = UniteEns.query.filter_by(id=ue_id)
|
||||
if g.scodoc_dept:
|
||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||
@ -359,7 +374,7 @@ def get_ue(ue_id: int):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formation_module_get(module_id: int):
|
||||
"""Renvoie le module"""
|
||||
"""Renvoie le module."""
|
||||
query = Module.query.filter_by(id=module_id)
|
||||
if g.scodoc_dept:
|
||||
query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
|
||||
@ -390,13 +405,15 @@ def formation_module_get(module_id: int):
|
||||
@permission_required(Permission.EditFormation)
|
||||
def ue_set_code_apogee(ue_id: int | None = None, code_apogee: str = ""):
|
||||
"""Change le code Apogée de l'UE.
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur formation verrouillée)
|
||||
|
||||
Ce changement peut être fait sur formation verrouillée.
|
||||
|
||||
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
|
||||
utilise l'argument value du POST.
|
||||
|
||||
Le retour est une chaîne (le code enregistré), pas json.
|
||||
"""
|
||||
@ -444,9 +461,11 @@ def ue_set_code_apogee(ue_id: int | None = None, code_apogee: str = ""):
|
||||
@permission_required(Permission.EditFormation)
|
||||
def ue_set_code_apogee_rcue(ue_id: int, code_apogee: str = ""):
|
||||
"""Change le code Apogée du RCUE de l'UE.
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
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,
|
||||
utilise l'argument value du POST (utilisé par jinplace.js)
|
||||
@ -497,9 +516,11 @@ def formation_module_set_code_apogee(
|
||||
module_id: int | None = None, code_apogee: str = ""
|
||||
):
|
||||
"""Change le code Apogée du module.
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur formation verrouillée)
|
||||
|
||||
Ce changement peut être fait sur formation verrouillée.
|
||||
|
||||
Si module_id n'est pas spécifié, utilise l'argument oid du POST.
|
||||
Si code_apogee n'est pas spécifié ou vide,
|
||||
|
@ -6,6 +6,12 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux formsemestres
|
||||
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
FormSemestre
|
||||
|
||||
"""
|
||||
from operator import attrgetter, itemgetter
|
||||
|
||||
@ -55,6 +61,7 @@ def formsemestre_infos(formsemestre_id: int):
|
||||
formsemestre_id : l'id du formsemestre
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
{
|
||||
"block_moyennes": false,
|
||||
"bul_bgcolor": "white",
|
||||
@ -84,7 +91,7 @@ def formsemestre_infos(formsemestre_id: int):
|
||||
"titre_num": "BUT GEA semestre 1",
|
||||
"titre": "BUT GEA",
|
||||
}
|
||||
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -101,8 +108,8 @@ def formsemestre_infos(formsemestre_id: int):
|
||||
@as_json
|
||||
def formsemestres_query():
|
||||
"""
|
||||
Retourne les formsemestres filtrés par
|
||||
étape Apogée ou année scolaire ou département (acronyme ou id) ou état ou code étudiant
|
||||
Retourne les formsemestres filtrés par étape Apogée ou année scolaire
|
||||
ou département (acronyme ou id) ou état ou code étudiant.
|
||||
|
||||
PARAMS
|
||||
------
|
||||
@ -192,7 +199,36 @@ def formsemestres_query():
|
||||
@permission_required(Permission.EditFormSemestre)
|
||||
@as_json
|
||||
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)
|
||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||
editable_keys = {
|
||||
@ -230,13 +266,19 @@ def formsemestre_edit(formsemestre_id: int):
|
||||
@permission_required(Permission.EditApogee)
|
||||
def formsemestre_set_apo_etapes():
|
||||
"""Change les codes étapes du semestre indiqué.
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur un semestre verrouillé)
|
||||
|
||||
Args:
|
||||
oid=int, le formsemestre_id
|
||||
value=chaine "V1RT, V1RT2", codes séparés par des virgules
|
||||
Ce changement peut être fait sur un semestre verrouillé
|
||||
|
||||
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"))
|
||||
etapes_apo_str = request.form.get("value")
|
||||
@ -267,13 +309,20 @@ def formsemestre_set_apo_etapes():
|
||||
@permission_required(Permission.EditApogee)
|
||||
def formsemestre_set_elt_sem_apo():
|
||||
"""Change les codes étapes du semestre indiqué.
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur un semestre verrouillé)
|
||||
|
||||
Args:
|
||||
oid=int, le formsemestre_id
|
||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||
Ce changement peut être fait sur un semestre verrouillé.
|
||||
|
||||
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"))
|
||||
value = (request.form.get("value") or "").strip()
|
||||
@ -295,13 +344,20 @@ def formsemestre_set_elt_sem_apo():
|
||||
@permission_required(Permission.EditApogee)
|
||||
def formsemestre_set_elt_annee_apo():
|
||||
"""Change les codes étapes du semestre indiqué (par le champ oid).
|
||||
|
||||
Le code est une chaîne, avec éventuellement plusieurs valeurs séparées
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur un semestre verrouillé)
|
||||
|
||||
Args:
|
||||
oid=int, le formsemestre_id
|
||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||
Ce changement peut être fait sur un semestre verrouillé.
|
||||
|
||||
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"))
|
||||
value = (request.form.get("value") or "").strip()
|
||||
@ -323,13 +379,20 @@ def formsemestre_set_elt_annee_apo():
|
||||
@permission_required(Permission.EditApogee)
|
||||
def formsemestre_set_elt_passage_apo():
|
||||
"""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
|
||||
par des virgules.
|
||||
(Ce changement peut être fait sur un semestre verrouillé)
|
||||
|
||||
Args:
|
||||
oid=int, le formsemestre_id
|
||||
value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
|
||||
Ce changement peut être fait sur un semestre verrouillé.
|
||||
|
||||
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"))
|
||||
value = (request.form.get("value") or "").strip()
|
||||
@ -355,9 +418,12 @@ def formsemestre_set_elt_passage_apo():
|
||||
@as_json
|
||||
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
|
||||
"""
|
||||
@ -389,9 +455,9 @@ def formsemestre_programme(formsemestre_id: int):
|
||||
"""
|
||||
Retourne la liste des UEs, ressources et SAEs d'un semestre
|
||||
|
||||
formsemestre_id : l'id d'un formsemestre
|
||||
|
||||
Exemple de résultat :
|
||||
```json
|
||||
{
|
||||
"ues": [
|
||||
{
|
||||
@ -449,6 +515,7 @@ def formsemestre_programme(formsemestre_id: int):
|
||||
],
|
||||
"modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ]
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -567,9 +634,9 @@ def formsemestre_etat_evaluations(formsemestre_id: int):
|
||||
"""
|
||||
Informations sur l'état des évaluations d'un formsemestre.
|
||||
|
||||
formsemestre_id : l'id d'un semestre
|
||||
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1, // moduleimpl_id
|
||||
@ -597,6 +664,7 @@ def formsemestre_etat_evaluations(formsemestre_id: int):
|
||||
]
|
||||
},
|
||||
]
|
||||
```
|
||||
"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
app.set_sco_dept(formsemestre.departement.acronym)
|
||||
@ -671,7 +739,8 @@ def formsemestre_etat_evaluations(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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.
|
||||
|
||||
Si `format=raw`, ne converti pas les valeurs.
|
||||
@ -726,7 +795,7 @@ def formsemestre_resultat(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
if g.scodoc_dept:
|
||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
@ -747,12 +816,17 @@ def groups_get_auto_assignment(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
if g.scodoc_dept:
|
||||
query = query.filter_by(dept_id=g.scodoc_dept_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:
|
||||
return json_error(413, "data too large")
|
||||
formsemestre.groups_auto_assignment_data = request.data
|
||||
@ -767,17 +841,16 @@ def groups_save_auto_assignment(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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.
|
||||
|
||||
group_ids permet de filtrer sur les groupes ScoDoc.
|
||||
show_modules_titles affiche le titre complet du module (défaut), sinon juste le code.
|
||||
Expérimental, ne pas utiliser hors ScoDoc.
|
||||
|
||||
QUERY
|
||||
-----
|
||||
group_ids:<string:group_ids>
|
||||
show_modules_titles:<bool:show_modules_titles>
|
||||
|
||||
group_ids : string (optionnel) filtre sur les groupes ScoDoc.
|
||||
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)
|
||||
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
|
||||
@ -91,7 +96,7 @@ def _news_delete_jury_etud(etud: Identite, detail: str = ""):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def validation_ue_delete(etudid: int, validation_id: int):
|
||||
"Efface cette validation"
|
||||
"Efface cette validation d'UE."
|
||||
return _validation_ue_delete(etudid, validation_id)
|
||||
|
||||
|
||||
@ -108,7 +113,7 @@ def validation_ue_delete(etudid: int, validation_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
return _validation_ue_delete(etudid, validation_id)
|
||||
|
||||
@ -160,7 +165,7 @@ def _validation_ue_delete(etudid: int, validation_id: int):
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
def autorisation_inscription_delete(etudid: int, validation_id: int):
|
||||
"Efface cette validation"
|
||||
"Efface cette autorisation d'inscription."
|
||||
etud = tools.get_etud(etudid)
|
||||
if etud is None:
|
||||
return "étudiant inconnu", 404
|
||||
@ -189,8 +194,13 @@ def autorisation_inscription_delete(etudid: int, validation_id: int):
|
||||
@as_json
|
||||
def validation_rcue_record(etudid: int):
|
||||
"""Enregistre une validation de RCUE.
|
||||
|
||||
Si une validation existe déjà pour ce RCUE, la remplace.
|
||||
The request content type should be "application/json":
|
||||
|
||||
DATA
|
||||
----
|
||||
|
||||
```json
|
||||
{
|
||||
"code" : str,
|
||||
"ue1_id" : int,
|
||||
@ -200,6 +210,7 @@ def validation_rcue_record(etudid: int):
|
||||
"date" : date_iso, // si non spécifié, now()
|
||||
"parcours_id" :int,
|
||||
}
|
||||
```
|
||||
"""
|
||||
etud = tools.get_etud(etudid)
|
||||
if etud is None:
|
||||
@ -314,7 +325,7 @@ def validation_rcue_record(etudid: int):
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
def validation_rcue_delete(etudid: int, validation_id: int):
|
||||
"Efface cette validation"
|
||||
"Efface cette validation de RCUE."
|
||||
etud = tools.get_etud(etudid)
|
||||
if etud is None:
|
||||
return "étudiant inconnu", 404
|
||||
@ -342,7 +353,7 @@ def validation_rcue_delete(etudid: int, validation_id: int):
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
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)
|
||||
if etud is None:
|
||||
return "étudiant inconnu", 404
|
||||
@ -371,7 +382,7 @@ def validation_annee_but_delete(etudid: int, validation_id: int):
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
def validation_dut120_delete(etudid: int, validation_id: int):
|
||||
"Efface cette validation"
|
||||
"Efface cette validation de DUT120."
|
||||
etud = tools.get_etud(etudid)
|
||||
if etud is None:
|
||||
return "étudiant inconnu", 404
|
||||
|
@ -50,7 +50,7 @@ from app.scodoc.sco_utils import json_error
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
def logo_list_globals():
|
||||
"""Liste tous les logos"""
|
||||
"""Liste des noms des logos définis pour le site ScoDoc."""
|
||||
logos = list_logos()[None]
|
||||
return list(logos.keys())
|
||||
|
||||
@ -59,6 +59,11 @@ def logo_list_globals():
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
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.
|
||||
"""
|
||||
logo = find_logo(logoname=logoname)
|
||||
if logo is None:
|
||||
return json_error(404, message="logo not found")
|
||||
@ -80,6 +85,9 @@ def _core_get_logos(dept_id) -> list:
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
def logo_get_local_by_acronym(departement):
|
||||
"""Liste des noms des logos définis pour le département
|
||||
désigné par son acronyme.
|
||||
"""
|
||||
dept_id = Departement.from_acronym(departement).id
|
||||
return _core_get_logos(dept_id)
|
||||
|
||||
@ -89,6 +97,9 @@ def logo_get_local_by_acronym(departement):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
def logo_get_local_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)
|
||||
|
||||
|
||||
@ -108,6 +119,12 @@ def _core_get_logo(dept_id, logoname) -> Response:
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
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
|
||||
return _core_get_logo(dept_id, logoname)
|
||||
|
||||
@ -116,4 +133,10 @@ def logo_get_local_dept_by_acronym(departement, logoname):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
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)
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux moduleimpl
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
ModuleImpl
|
||||
"""
|
||||
|
||||
from flask_json import as_json
|
||||
@ -28,11 +32,15 @@ from app.scodoc.sco_permissions import Permission
|
||||
@as_json
|
||||
def moduleimpl(moduleimpl_id: int):
|
||||
"""
|
||||
Retourne un moduleimpl en fonction de son id
|
||||
Retourne le moduleimpl.
|
||||
|
||||
PARAMS
|
||||
------
|
||||
moduleimpl_id : l'id d'un moduleimpl
|
||||
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"formsemestre_id": 1,
|
||||
@ -60,6 +68,7 @@ def moduleimpl(moduleimpl_id: int):
|
||||
"module_id": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
"""
|
||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||
return modimpl.to_dict(convert_objects=True)
|
||||
@ -72,8 +81,11 @@ def moduleimpl(moduleimpl_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def moduleimpl_inscriptions(moduleimpl_id: int):
|
||||
"""Liste des inscriptions à ce moduleimpl
|
||||
"""Liste des inscriptions à ce moduleimpl.
|
||||
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
@ -82,6 +94,7 @@ def moduleimpl_inscriptions(moduleimpl_id: int):
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
"""
|
||||
modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
||||
return [i.to_dict() for i in modimpl.inscriptions]
|
||||
|
@ -6,6 +6,11 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : partitions
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Groupes et Partitions
|
||||
|
||||
"""
|
||||
from operator import attrgetter
|
||||
|
||||
@ -41,7 +46,8 @@ def partition_info(partition_id: int):
|
||||
"""Info sur une partition.
|
||||
|
||||
Exemple de résultat :
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
'bul_show_rank': False,
|
||||
'formsemestre_id': 39,
|
||||
@ -71,10 +77,11 @@ def partition_info(partition_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
{
|
||||
partition_id : {
|
||||
"bul_show_rank": False,
|
||||
@ -88,7 +95,7 @@ def formsemestre_partitions(formsemestre_id: int):
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -112,9 +119,14 @@ def group_etudiants(group_id: int):
|
||||
"""
|
||||
Retourne la liste des étudiants dans un groupe
|
||||
(inscrits au groupe et inscrits au semestre).
|
||||
|
||||
PARAMS
|
||||
------
|
||||
group_id : l'id d'un groupe
|
||||
|
||||
Exemple de résultat :
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
'civilite': 'M',
|
||||
@ -127,6 +139,7 @@ def group_etudiants(group_id: int):
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
"""
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
@ -152,11 +165,11 @@ def group_etudiants(group_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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
|
||||
-----
|
||||
etat:<string:etat>
|
||||
etat : string
|
||||
|
||||
"""
|
||||
etat = request.args.get("etat")
|
||||
@ -186,7 +199,7 @@ def group_etudiants_query(group_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
@ -248,7 +261,8 @@ def group_remove_etud(group_id: int, etudid: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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)
|
||||
"""
|
||||
etud = Identite.query.get_or_404(etudid)
|
||||
@ -293,12 +307,15 @@ def partition_remove_etud(partition_id: int, etudid: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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,
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = Partition.query.filter_by(id=partition_id)
|
||||
if g.scodoc_dept:
|
||||
@ -345,7 +362,7 @@ def group_create(partition_id: int): # partition-group-create
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def group_delete(group_id: int):
|
||||
"""Suppression d'un groupe"""
|
||||
"""Suppression d'un groupe."""
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
query = (
|
||||
@ -374,7 +391,7 @@ def group_delete(group_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def group_edit(group_id: int):
|
||||
"""Edit a group"""
|
||||
"""Édition d'un groupe."""
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
query = (
|
||||
@ -415,9 +432,10 @@ def group_edit(group_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def group_set_edt_id(group_id: int, edt_id: str):
|
||||
"""Set edt_id for this group.
|
||||
Contrairement à /edit, peut-être changé pour toute partition
|
||||
ou formsemestre non verrouillé.
|
||||
"""Set edt_id du groupe.
|
||||
|
||||
Contrairement à `/edit`, peut-être changé pour toute partition
|
||||
d'un formsemestre non verrouillé.
|
||||
"""
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
@ -443,9 +461,11 @@ def group_set_edt_id(group_id: int, edt_id: str):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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,
|
||||
"numero": int,
|
||||
@ -453,6 +473,7 @@ def partition_create(formsemestre_id: int):
|
||||
"show_in_lists": bool,
|
||||
"groups_editable": bool
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -508,8 +529,13 @@ def partition_create(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def formsemestre_set_partitions_order(formsemestre_id: int):
|
||||
"""Modifie l'ordre des partitions du formsemestre
|
||||
JSON args: [partition_id1, partition_id2, ...]
|
||||
"""Modifie l'ordre des partitions du formsemestre.
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
[ partition_id1, partition_id2, ... ]
|
||||
```
|
||||
"""
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
if g.scodoc_dept:
|
||||
@ -520,7 +546,7 @@ def formsemestre_set_partitions_order(formsemestre_id: int):
|
||||
if not formsemestre.can_change_groups():
|
||||
return json_error(401, "opération non autorisée")
|
||||
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
|
||||
):
|
||||
return json_error(
|
||||
@ -549,8 +575,13 @@ def formsemestre_set_partitions_order(formsemestre_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def partition_order_groups(partition_id: int):
|
||||
"""Modifie l'ordre des groupes de la partition
|
||||
JSON args: [group_id1, group_id2, ...]
|
||||
"""Modifie l'ordre des groupes de la partition.
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
[ group_id1, group_id2, ... ]
|
||||
```
|
||||
"""
|
||||
query = Partition.query.filter_by(id=partition_id)
|
||||
if g.scodoc_dept:
|
||||
@ -561,7 +592,7 @@ def partition_order_groups(partition_id: int):
|
||||
if not partition.formsemestre.can_change_groups():
|
||||
return json_error(401, "opération non autorisée")
|
||||
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
|
||||
):
|
||||
return json_error(
|
||||
@ -586,10 +617,13 @@ def partition_order_groups(partition_id: int):
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
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"
|
||||
All fields are optional:
|
||||
Tous les champs sont optionnels.
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
{
|
||||
"partition_name": str,
|
||||
"numero":int,
|
||||
@ -597,6 +631,7 @@ def partition_edit(partition_id: int):
|
||||
"show_in_lists":bool,
|
||||
"groups_editable":bool
|
||||
}
|
||||
```
|
||||
"""
|
||||
query = Partition.query.filter_by(id=partition_id)
|
||||
if g.scodoc_dept:
|
||||
@ -660,9 +695,9 @@ def partition_edit(partition_id: int):
|
||||
def partition_delete(partition_id: int):
|
||||
"""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.
|
||||
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.
|
||||
"""
|
||||
query = Partition.query.filter_by(id=partition_id)
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux formsemestres
|
||||
|
||||
"""
|
||||
# from flask import g, jsonify, request
|
||||
# from flask_login import login_required
|
||||
|
@ -3,12 +3,18 @@ from app import db, log
|
||||
from app.api import api_bp as bp
|
||||
from app.auth.logic import basic_auth, token_auth
|
||||
|
||||
"""
|
||||
CATEGORY
|
||||
--------
|
||||
Authentification API
|
||||
"""
|
||||
|
||||
|
||||
@bp.route("/tokens", methods=["POST"])
|
||||
@basic_auth.login_required
|
||||
@as_json
|
||||
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()
|
||||
log(f"API: giving token to {basic_auth.current_user()}")
|
||||
db.session.commit()
|
||||
@ -18,7 +24,7 @@ def token_get():
|
||||
@bp.route("/tokens", methods=["DELETE"])
|
||||
@token_auth.login_required
|
||||
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.revoke_token()
|
||||
db.session.commit()
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : accès aux utilisateurs
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Utilisateurs
|
||||
"""
|
||||
|
||||
from flask import g, request
|
||||
@ -32,7 +36,7 @@ from app.scodoc.sco_utils import json_error
|
||||
@as_json
|
||||
def user_info(uid: int):
|
||||
"""
|
||||
Info sur un compte utilisateur scodoc
|
||||
Info sur un compte utilisateur ScoDoc.
|
||||
"""
|
||||
user: User = db.session.get(User, uid)
|
||||
if user is None:
|
||||
@ -53,7 +57,11 @@ def user_info(uid: int):
|
||||
@as_json
|
||||
def users_info_query():
|
||||
"""Utilisateurs, filtrés par dept, active ou début nom
|
||||
|
||||
Exemple:
|
||||
```
|
||||
/users/query?departement=dept_acronym&active=1&starts_with=<string:nom>
|
||||
```
|
||||
|
||||
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
|
||||
@ -61,9 +69,9 @@ def users_info_query():
|
||||
|
||||
QUERY
|
||||
-----
|
||||
active:<bool:active>
|
||||
departement:<string:departement>
|
||||
starts_with:<string:starts_with>
|
||||
active: bool
|
||||
departement: string
|
||||
starts_with: string
|
||||
|
||||
"""
|
||||
query = User.query
|
||||
@ -113,7 +121,10 @@ def _is_allowed_user_edit(args: dict) -> tuple[bool, str]:
|
||||
@as_json
|
||||
def user_create():
|
||||
"""Création d'un utilisateur
|
||||
The request content type should be "application/json":
|
||||
|
||||
DATA
|
||||
----
|
||||
```json
|
||||
{
|
||||
"active":bool (default True),
|
||||
"dept": str or null,
|
||||
@ -122,6 +133,7 @@ def user_create():
|
||||
"user_name": str,
|
||||
...
|
||||
}
|
||||
```
|
||||
"""
|
||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||
user_name = args.get("user_name")
|
||||
@ -158,8 +170,10 @@ def user_create():
|
||||
@permission_required(Permission.UsersAdmin)
|
||||
@as_json
|
||||
def user_edit(uid: int):
|
||||
"""Modification d'un utilisateur
|
||||
"""Modification d'un utilisateur.
|
||||
|
||||
Champs modifiables:
|
||||
```json
|
||||
{
|
||||
"dept": str or null,
|
||||
"nom": str,
|
||||
@ -167,6 +181,7 @@ def user_edit(uid: int):
|
||||
"active":bool
|
||||
...
|
||||
}
|
||||
```
|
||||
"""
|
||||
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||
user: User = User.query.get_or_404(uid)
|
||||
@ -205,11 +220,15 @@ def user_edit(uid: int):
|
||||
@permission_required(Permission.UsersAdmin)
|
||||
@as_json
|
||||
def user_password(uid: int):
|
||||
"""Modification du mot de passe d'un utilisateur
|
||||
"""Modification du mot de passe d'un utilisateur.
|
||||
|
||||
Champs modifiables:
|
||||
```json
|
||||
{
|
||||
"password": str
|
||||
}
|
||||
```.
|
||||
|
||||
Si le mot de passe ne convient pas, erreur 400.
|
||||
"""
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
@ -243,7 +262,7 @@ def user_password(uid: int):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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)
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
if dept is not None: # check
|
||||
@ -272,7 +291,7 @@ def user_role_add(uid: int, role_name: str, dept: str = None):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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)
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
if dept is not None: # check
|
||||
@ -299,7 +318,7 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
|
||||
@permission_required(Permission.UsersView)
|
||||
@as_json
|
||||
def permissions_list():
|
||||
"""Liste des noms de permissions définies"""
|
||||
"""Liste des noms de permissions définies."""
|
||||
return list(Permission.permission_by_name.keys())
|
||||
|
||||
|
||||
@ -321,7 +340,7 @@ def role_get(role_name: str):
|
||||
@permission_required(Permission.UsersView)
|
||||
@as_json
|
||||
def roles_list():
|
||||
"""Tous les rôles définis"""
|
||||
"""Tous les rôles définis."""
|
||||
return [role.to_dict() for role in Role.query]
|
||||
|
||||
|
||||
@ -338,7 +357,7 @@ def roles_list():
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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()
|
||||
permission = Permission.get_by_name(perm_name)
|
||||
if permission is None:
|
||||
@ -363,7 +382,7 @@ def role_permission_add(role_name: str, perm_name: str):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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()
|
||||
permission = Permission.get_by_name(perm_name)
|
||||
if permission is None:
|
||||
@ -382,10 +401,15 @@ def role_permission_remove(role_name: str, perm_name: str):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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', ... ]
|
||||
}
|
||||
```
|
||||
"""
|
||||
role: Role = Role.query.filter_by(name=role_name).first()
|
||||
if role:
|
||||
@ -410,11 +434,16 @@ def role_create(role_name: str):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
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
|
||||
"permissions" : [ 'ScoView', ... ]
|
||||
}
|
||||
```
|
||||
"""
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
@ -442,7 +471,7 @@ def role_edit(role_name: str):
|
||||
@permission_required(Permission.ScoSuperAdmin)
|
||||
@as_json
|
||||
def role_delete(role_name: str):
|
||||
"""Delete a role"""
|
||||
"""Suprression d'un rôle."""
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
db.session.delete(role)
|
||||
db.session.commit()
|
||||
|
@ -52,6 +52,17 @@ class Departement(db.Model):
|
||||
def __repr__(self):
|
||||
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):
|
||||
data = {
|
||||
"id": self.id,
|
||||
|
Loading…
Reference in New Issue
Block a user