2022-07-19 22:17:10 +02:00
|
|
|
##############################################################################
|
|
|
|
# ScoDoc
|
2023-01-02 13:16:27 +01:00
|
|
|
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
2022-07-19 22:17:10 +02:00
|
|
|
# See LICENSE
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
"""
|
|
|
|
ScoDoc 9 API : partitions
|
|
|
|
"""
|
2023-04-03 17:46:31 +02:00
|
|
|
from operator import attrgetter
|
|
|
|
|
2023-04-06 16:10:32 +02:00
|
|
|
from flask import g, request
|
|
|
|
from flask_json import as_json
|
2022-07-27 16:03:14 +02:00
|
|
|
from flask_login import login_required
|
2023-07-13 20:01:42 +02:00
|
|
|
import sqlalchemy as sa
|
2023-07-05 19:15:33 +02:00
|
|
|
from sqlalchemy.exc import IntegrityError
|
2022-03-02 16:45:47 +01:00
|
|
|
|
2022-07-20 22:03:29 +02:00
|
|
|
import app
|
2022-07-20 22:33:41 +02:00
|
|
|
from app import db, log
|
2023-02-22 02:13:06 +01:00
|
|
|
from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR
|
2022-07-27 16:03:14 +02:00
|
|
|
from app.decorators import scodoc, permission_required
|
2022-08-07 19:56:25 +02:00
|
|
|
from app.scodoc.sco_utils import json_error
|
2022-07-19 22:17:10 +02:00
|
|
|
from app.models import FormSemestre, FormSemestreInscription, Identite
|
2023-02-17 20:40:57 +01:00
|
|
|
from app.models import GroupDescr, Partition, Scolog
|
2022-07-19 22:17:10 +02:00
|
|
|
from app.models.groups import group_membership
|
2022-07-20 15:07:31 +02:00
|
|
|
from app.scodoc import sco_cache
|
2023-01-22 22:15:56 +01:00
|
|
|
from app.scodoc import sco_groups
|
2023-07-05 19:15:33 +02:00
|
|
|
from app.scodoc.sco_exceptions import ScoValueError
|
2022-03-04 17:16:08 +01:00
|
|
|
from app.scodoc.sco_permissions import Permission
|
2022-07-20 08:19:24 +02:00
|
|
|
from app.scodoc import sco_utils as scu
|
2022-03-02 16:45:47 +01:00
|
|
|
|
|
|
|
|
2022-07-27 16:03:14 +02:00
|
|
|
@bp.route("/partition/<int:partition_id>")
|
2022-08-03 16:05:01 +02:00
|
|
|
@api_web_bp.route("/partition/<int:partition_id>")
|
2022-07-27 16:03:14 +02:00
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 08:19:24 +02:00
|
|
|
def partition_info(partition_id: int):
|
2022-07-23 09:07:53 +02:00
|
|
|
"""Info sur une partition.
|
|
|
|
|
2022-07-20 08:19:24 +02:00
|
|
|
Exemple de résultat :
|
|
|
|
```
|
|
|
|
{
|
|
|
|
'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
|
|
|
|
}
|
|
|
|
```
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition = query.first_or_404()
|
2023-04-06 16:10:32 +02:00
|
|
|
return partition.to_dict(with_groups=True)
|
2022-07-20 08:19:24 +02:00
|
|
|
|
|
|
|
|
2022-07-27 16:03:14 +02:00
|
|
|
@bp.route("/formsemestre/<int:formsemestre_id>/partitions")
|
|
|
|
@api_web_bp.route("/formsemestre/<int:formsemestre_id>/partitions")
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 08:19:24 +02:00
|
|
|
def formsemestre_partitions(formsemestre_id: int):
|
2022-07-23 09:07:53 +02:00
|
|
|
"""Liste de toutes les partitions d'un formsemestre
|
2022-03-02 16:45:47 +01:00
|
|
|
|
|
|
|
formsemestre_id : l'id d'un formsemestre
|
2022-04-27 14:11:06 +02:00
|
|
|
|
2022-07-23 09:07:53 +02:00
|
|
|
{
|
|
|
|
partition_id : {
|
|
|
|
"bul_show_rank": False,
|
|
|
|
"formsemestre_id": 1063,
|
|
|
|
"groups" :
|
|
|
|
group_id : {
|
|
|
|
"id" : 12,
|
|
|
|
"name" : "A",
|
|
|
|
"partition_id" : partition_id,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2022-03-02 16:45:47 +01:00
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
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)
|
2023-04-03 17:46:31 +02:00
|
|
|
partitions = sorted(formsemestre.partitions, key=attrgetter("numero"))
|
2023-04-06 16:10:32 +02:00
|
|
|
return {
|
2023-04-12 13:21:13 +02:00
|
|
|
str(partition.id): partition.to_dict(with_groups=True, str_keys=True)
|
2023-04-06 16:10:32 +02:00
|
|
|
for partition in partitions
|
|
|
|
if partition.partition_name is not None
|
|
|
|
}
|
2022-03-02 16:45:47 +01:00
|
|
|
|
|
|
|
|
2022-07-27 16:03:14 +02:00
|
|
|
@bp.route("/group/<int:group_id>/etudiants")
|
|
|
|
@api_web_bp.route("/group/<int:group_id>/etudiants")
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 08:19:24 +02:00
|
|
|
def etud_in_group(group_id: int):
|
2022-03-02 16:45:47 +01:00
|
|
|
"""
|
|
|
|
Retourne la liste des étudiants dans un groupe
|
2023-07-26 15:59:31 +02:00
|
|
|
(inscrits au groupe et inscrits au semestre).
|
2022-03-02 16:45:47 +01:00
|
|
|
group_id : l'id d'un groupe
|
2022-04-27 14:11:06 +02:00
|
|
|
|
|
|
|
Exemple de résultat :
|
2022-07-19 22:17:10 +02:00
|
|
|
[
|
|
|
|
{
|
|
|
|
'civilite': 'M',
|
|
|
|
'id': 123456,
|
|
|
|
'ine': None,
|
|
|
|
'nip': '987654321',
|
|
|
|
'nom': 'MARTIN',
|
|
|
|
'nom_usuel': null,
|
|
|
|
'prenom': 'JEAN'}
|
|
|
|
},
|
|
|
|
...
|
|
|
|
]
|
2022-03-02 16:45:47 +01:00
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
)
|
|
|
|
group = query.first_or_404()
|
2023-07-26 15:59:31 +02:00
|
|
|
|
|
|
|
query = (
|
|
|
|
Identite.query.join(group_membership)
|
|
|
|
.filter_by(group_id=group_id)
|
|
|
|
.join(FormSemestreInscription)
|
|
|
|
.filter_by(formsemestre_id=group.partition.formsemestre_id)
|
|
|
|
)
|
|
|
|
|
|
|
|
return [etud.to_dict_short() for etud in query]
|
2022-07-20 08:19:24 +02:00
|
|
|
|
|
|
|
|
2022-07-27 16:03:14 +02:00
|
|
|
@bp.route("/group/<int:group_id>/etudiants/query")
|
|
|
|
@api_web_bp.route("/group/<int:group_id>/etudiants/query")
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 08:19:24 +02:00
|
|
|
def etud_in_group_query(group_id: int):
|
2022-09-03 11:41:56 +02:00
|
|
|
"""Étudiants du groupe, filtrés par état"""
|
2022-07-20 16:12:20 +02:00
|
|
|
etat = request.args.get("etat")
|
2022-08-10 07:24:54 +02:00
|
|
|
if etat not in {None, scu.INSCRIT, scu.DEMISSION, scu.DEF}:
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "etat: valeur invalide")
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
)
|
2022-08-10 07:24:54 +02:00
|
|
|
group = query.first_or_404() # just to ckeck that group exists in accessible dept
|
|
|
|
|
|
|
|
query = Identite.query.join(FormSemestreInscription).filter_by(
|
|
|
|
formsemestre_id=group.partition.formsemestre_id
|
2022-07-20 08:19:24 +02:00
|
|
|
)
|
2022-08-10 07:24:54 +02:00
|
|
|
if etat is not None:
|
|
|
|
query = query.filter_by(etat=etat)
|
|
|
|
|
|
|
|
query = query.join(group_membership).filter_by(group_id=group_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return [etud.to_dict_short() for etud in query]
|
2022-07-20 09:50:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 09:50:02 +02:00
|
|
|
def set_etud_group(etudid: int, group_id: int):
|
|
|
|
"""Affecte l'étudiant au groupe indiqué"""
|
|
|
|
etud = Identite.query.get_or_404(etudid)
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
2022-07-27 17:42:58 +02:00
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
2022-07-27 16:03:14 +02:00
|
|
|
)
|
|
|
|
group = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not group.partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not group.partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-20 09:50:02 +02:00
|
|
|
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
|
2022-08-07 19:56:25 +02:00
|
|
|
return json_error(404, "etud non inscrit au formsemestre du groupe")
|
2023-01-22 22:15:56 +01:00
|
|
|
|
2023-07-05 19:15:33 +02:00
|
|
|
try:
|
|
|
|
sco_groups.change_etud_group_in_partition(etudid, group)
|
|
|
|
except ScoValueError as exc:
|
|
|
|
return json_error(404, exc.args[0])
|
|
|
|
except IntegrityError:
|
|
|
|
return json_error(404, "échec de l'enregistrement")
|
2023-04-06 16:10:32 +02:00
|
|
|
return {"group_id": group_id, "etudid": etudid}
|
2022-07-25 08:46:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/group/<int:group_id>/remove_etudiant/<int:etudid>", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route(
|
|
|
|
"/group/<int:group_id>/remove_etudiant/<int:etudid>", methods=["POST"]
|
|
|
|
)
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-25 08:46:24 +02:00
|
|
|
def group_remove_etud(group_id: int, etudid: int):
|
|
|
|
"""Retire l'étudiant de ce groupe. S'il n'y est pas, ne fait rien."""
|
|
|
|
etud = Identite.query.get_or_404(etudid)
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
)
|
|
|
|
group = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not group.partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not group.partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2023-08-31 13:38:17 +02:00
|
|
|
|
|
|
|
group.remove_etud(etud)
|
|
|
|
|
2023-04-06 16:10:32 +02:00
|
|
|
return {"group_id": group_id, "etudid": etudid}
|
2022-07-20 09:50:02 +02:00
|
|
|
|
|
|
|
|
2022-07-23 15:29:12 +02:00
|
|
|
@bp.route(
|
|
|
|
"/partition/<int:partition_id>/remove_etudiant/<int:etudid>", methods=["POST"]
|
|
|
|
)
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route(
|
|
|
|
"/partition/<int:partition_id>/remove_etudiant/<int:etudid>", methods=["POST"]
|
|
|
|
)
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-23 15:29:12 +02:00
|
|
|
def partition_remove_etud(partition_id: int, etudid: int):
|
2022-07-25 08:46:24 +02:00
|
|
|
"""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)
|
|
|
|
"""
|
2022-07-23 15:29:12 +02:00
|
|
|
etud = Identite.query.get_or_404(etudid)
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2023-07-13 20:01:42 +02:00
|
|
|
db.session.execute(
|
|
|
|
sa.text(
|
|
|
|
"""DELETE FROM group_membership
|
|
|
|
WHERE etudid=:etudid
|
|
|
|
and group_id IN (
|
|
|
|
SELECT id FROM group_descr WHERE partition_id = :partition_id
|
|
|
|
);
|
|
|
|
"""
|
|
|
|
),
|
|
|
|
{"etudid": etudid, "partition_id": partition_id},
|
|
|
|
)
|
|
|
|
|
|
|
|
Scolog.logdb(
|
|
|
|
method="partition_remove_etud",
|
|
|
|
etudid=etud.id,
|
|
|
|
msg=f"Retrait de la partition {partition.partition_name}",
|
|
|
|
commit=False,
|
2022-07-23 15:29:12 +02:00
|
|
|
)
|
|
|
|
db.session.commit()
|
2023-02-17 20:40:57 +01:00
|
|
|
# Update parcours
|
2023-07-31 18:46:41 +02:00
|
|
|
partition.formsemestre.update_inscriptions_parcours_from_groups(etudid=etudid)
|
2022-07-26 09:00:48 +02:00
|
|
|
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
2022-07-25 08:46:24 +02:00
|
|
|
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return {"partition_id": partition_id, "etudid": etudid}
|
2022-07-23 15:29:12 +02:00
|
|
|
|
|
|
|
|
2022-07-20 09:50:02 +02:00
|
|
|
@bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2023-07-16 19:59:45 +02:00
|
|
|
def group_create(partition_id: int): # partition-group-create
|
2022-07-20 09:50:02 +02:00
|
|
|
"""Création d'un groupe dans une partition
|
|
|
|
|
|
|
|
The request content type should be "application/json":
|
|
|
|
{
|
|
|
|
"group_name" : nom_du_groupe,
|
|
|
|
}
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition: Partition = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2022-07-20 15:07:31 +02:00
|
|
|
if not partition.groups_editable:
|
2023-01-24 12:12:24 +01:00
|
|
|
return json_error(403, "partition non editable")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2023-11-22 17:54:16 +01:00
|
|
|
|
|
|
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
group_name = args.get("group_name")
|
|
|
|
if not isinstance(group_name, str):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "missing group name or invalid data format")
|
2023-11-22 17:54:16 +01:00
|
|
|
args["group_name"] = args["group_name"].strip()
|
|
|
|
if not GroupDescr.check_name(partition, args["group_name"]):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid group_name")
|
2023-11-22 17:54:16 +01:00
|
|
|
args["partition_id"] = partition_id
|
|
|
|
try:
|
|
|
|
group = GroupDescr(**args)
|
|
|
|
except TypeError:
|
|
|
|
return json_error(API_CLIENT_ERROR, "invalid arguments")
|
2022-07-20 09:50:02 +02:00
|
|
|
db.session.add(group)
|
|
|
|
db.session.commit()
|
2022-07-20 22:33:41 +02:00
|
|
|
log(f"created group {group}")
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return group.to_dict(with_partition=True)
|
2022-07-20 09:50:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/group/<int:group_id>/delete", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/group/<int:group_id>/delete", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 09:50:02 +02:00
|
|
|
def group_delete(group_id: int):
|
2022-07-20 15:07:31 +02:00
|
|
|
"""Suppression d'un groupe"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
)
|
|
|
|
group: GroupDescr = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not group.partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2022-07-20 15:07:31 +02:00
|
|
|
if not group.partition.groups_editable:
|
2023-01-24 12:12:24 +01:00
|
|
|
return json_error(403, "partition non editable")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not group.partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-20 15:07:31 +02:00
|
|
|
formsemestre_id = group.partition.formsemestre_id
|
2022-07-20 22:33:41 +02:00
|
|
|
log(f"deleting {group}")
|
2022-07-20 09:50:02 +02:00
|
|
|
db.session.delete(group)
|
|
|
|
db.session.commit()
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return {"OK": True}
|
2022-07-20 09:50:02 +02:00
|
|
|
|
|
|
|
|
2022-07-20 15:07:31 +02:00
|
|
|
@bp.route("/group/<int:group_id>/edit", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/group/<int:group_id>/edit", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 09:50:02 +02:00
|
|
|
def group_edit(group_id: int):
|
|
|
|
"""Edit a group"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = GroupDescr.query.filter_by(id=group_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = (
|
|
|
|
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
)
|
|
|
|
group: GroupDescr = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not group.partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2022-07-20 15:07:31 +02:00
|
|
|
if not group.partition.groups_editable:
|
2023-01-24 12:12:24 +01:00
|
|
|
return json_error(403, "partition non editable")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not group.partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2023-11-22 17:54:16 +01:00
|
|
|
|
|
|
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
if "group_name" in args:
|
|
|
|
if not isinstance(args["group_name"], str):
|
|
|
|
return json_error(API_CLIENT_ERROR, "invalid data format for group_name")
|
|
|
|
args["group_name"] = args["group_name"].strip() if args["group_name"] else ""
|
|
|
|
if not GroupDescr.check_name(
|
|
|
|
group.partition, args["group_name"], existing=True
|
|
|
|
):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid group_name")
|
2023-11-22 17:54:16 +01:00
|
|
|
|
|
|
|
group.from_dict(args)
|
|
|
|
db.session.add(group)
|
|
|
|
db.session.commit()
|
|
|
|
log(f"modified {group}")
|
|
|
|
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return group.to_dict(with_partition=True)
|
2022-07-20 15:07:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route(
|
|
|
|
"/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"]
|
|
|
|
)
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 15:07:31 +02:00
|
|
|
def partition_create(formsemestre_id: int):
|
|
|
|
"""Création d'une partition dans un semestre
|
|
|
|
|
|
|
|
The request content type should be "application/json":
|
|
|
|
{
|
|
|
|
"partition_name": str,
|
|
|
|
"numero":int,
|
|
|
|
"bul_show_rank":bool,
|
|
|
|
"show_in_lists":bool,
|
|
|
|
"groups_editable":bool
|
|
|
|
}
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
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)
|
2023-01-24 12:12:24 +01:00
|
|
|
if not formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-20 15:07:31 +02:00
|
|
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
partition_name = data.get("partition_name")
|
|
|
|
if partition_name is None:
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(
|
|
|
|
API_CLIENT_ERROR, "missing partition_name or invalid data format"
|
|
|
|
)
|
2022-08-03 21:42:53 +02:00
|
|
|
if partition_name == scu.PARTITION_PARCOURS:
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(
|
|
|
|
API_CLIENT_ERROR, f"invalid partition_name {scu.PARTITION_PARCOURS}"
|
|
|
|
)
|
2022-07-20 15:07:31 +02:00
|
|
|
if not Partition.check_name(formsemestre, partition_name):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid partition_name")
|
2022-07-20 15:07:31 +02:00
|
|
|
numero = data.get("numero", 0)
|
|
|
|
if not isinstance(numero, int):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid type for numero")
|
2022-07-20 15:07:31 +02:00
|
|
|
args = {
|
|
|
|
"formsemestre_id": formsemestre_id,
|
|
|
|
"partition_name": partition_name.strip(),
|
|
|
|
"numero": numero,
|
|
|
|
}
|
|
|
|
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
|
|
|
|
value = data.get(
|
|
|
|
boolean_field, False if boolean_field != "groups_editable" else True
|
|
|
|
)
|
2022-07-20 22:33:41 +02:00
|
|
|
if not isinstance(value, bool):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, f"invalid type for {boolean_field}")
|
2022-07-20 15:07:31 +02:00
|
|
|
args[boolean_field] = value
|
|
|
|
|
|
|
|
partition = Partition(**args)
|
|
|
|
db.session.add(partition)
|
|
|
|
db.session.commit()
|
2022-07-20 22:33:41 +02:00
|
|
|
log(f"created partition {partition}")
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return partition.to_dict(with_groups=True)
|
2022-07-20 15:07:31 +02:00
|
|
|
|
|
|
|
|
2022-07-24 22:04:37 +02:00
|
|
|
@bp.route("/formsemestre/<int:formsemestre_id>/partitions/order", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route(
|
|
|
|
"/formsemestre/<int:formsemestre_id>/partitions/order", methods=["POST"]
|
|
|
|
)
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-24 22:04:37 +02:00
|
|
|
def formsemestre_order_partitions(formsemestre_id: int):
|
|
|
|
"""Modifie l'ordre des partitions du formsemestre
|
|
|
|
JSON args: [partition_id1, partition_id2, ...]
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
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)
|
2023-01-24 12:12:24 +01:00
|
|
|
if not formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-24 22:04:37 +02:00
|
|
|
partition_ids = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
if not isinstance(partition_ids, int) and not all(
|
|
|
|
isinstance(x, int) for x in partition_ids
|
|
|
|
):
|
2022-08-07 19:56:25 +02:00
|
|
|
return json_error(
|
2023-02-22 02:13:06 +01:00
|
|
|
API_CLIENT_ERROR,
|
2022-07-24 22:04:37 +02:00
|
|
|
message="paramètre liste des partitions invalide",
|
|
|
|
)
|
|
|
|
for p_id, numero in zip(partition_ids, range(len(partition_ids))):
|
2023-04-03 17:46:31 +02:00
|
|
|
partition = Partition.query.get_or_404(p_id)
|
|
|
|
partition.numero = numero
|
|
|
|
db.session.add(partition)
|
2022-07-24 22:04:37 +02:00
|
|
|
db.session.commit()
|
2022-07-26 09:00:48 +02:00
|
|
|
app.set_sco_dept(formsemestre.departement.acronym)
|
2022-07-25 08:46:24 +02:00
|
|
|
sco_cache.invalidate_formsemestre(formsemestre_id)
|
2023-04-06 16:10:32 +02:00
|
|
|
return [
|
|
|
|
partition.to_dict()
|
|
|
|
for partition in formsemestre.partitions.order_by(Partition.numero)
|
|
|
|
if partition.partition_name is not None
|
|
|
|
]
|
2022-07-24 22:04:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-24 22:04:37 +02:00
|
|
|
def partition_order_groups(partition_id: int):
|
|
|
|
"""Modifie l'ordre des groupes de la partition
|
|
|
|
JSON args: [group_id1, group_id2, ...]
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition: Partition = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-24 22:04:37 +02:00
|
|
|
group_ids = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
if not isinstance(group_ids, int) and not all(
|
|
|
|
isinstance(x, int) for x in group_ids
|
|
|
|
):
|
2022-08-07 19:56:25 +02:00
|
|
|
return json_error(
|
2023-02-22 02:13:06 +01:00
|
|
|
API_CLIENT_ERROR,
|
2022-07-24 22:04:37 +02:00
|
|
|
message="paramètre liste de groupe invalide",
|
|
|
|
)
|
|
|
|
for group_id, numero in zip(group_ids, range(len(group_ids))):
|
|
|
|
group = GroupDescr.query.get_or_404(group_id)
|
|
|
|
group.numero = numero
|
|
|
|
db.session.add(group)
|
|
|
|
db.session.commit()
|
2022-07-26 09:00:48 +02:00
|
|
|
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
2022-07-25 08:46:24 +02:00
|
|
|
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
2022-07-27 17:42:58 +02:00
|
|
|
log(f"partition_order_groups: {partition} : {group_ids}")
|
2023-04-06 16:10:32 +02:00
|
|
|
return partition.to_dict(with_groups=True)
|
2022-07-24 22:04:37 +02:00
|
|
|
|
|
|
|
|
2022-07-20 15:07:31 +02:00
|
|
|
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 15:07:31 +02:00
|
|
|
def partition_edit(partition_id: int):
|
|
|
|
"""Modification d'une partition dans un semestre
|
|
|
|
|
|
|
|
The request content type should be "application/json"
|
|
|
|
All fields are optional:
|
|
|
|
{
|
|
|
|
"partition_name": str,
|
|
|
|
"numero":int,
|
|
|
|
"bul_show_rank":bool,
|
|
|
|
"show_in_lists":bool,
|
|
|
|
"groups_editable":bool
|
|
|
|
}
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition: Partition = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-20 15:07:31 +02:00
|
|
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
|
|
|
modified = False
|
|
|
|
partition_name = data.get("partition_name")
|
2022-08-03 21:42:53 +02:00
|
|
|
#
|
2022-07-20 15:07:31 +02:00
|
|
|
if partition_name is not None and partition_name != partition.partition_name:
|
2022-08-03 21:42:53 +02:00
|
|
|
if partition.is_parcours():
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(
|
|
|
|
API_CLIENT_ERROR, f"can't rename {scu.PARTITION_PARCOURS}"
|
|
|
|
)
|
2022-07-20 15:07:31 +02:00
|
|
|
if not Partition.check_name(
|
|
|
|
partition.formsemestre, partition_name, existing=True
|
|
|
|
):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid partition_name")
|
2022-07-20 15:07:31 +02:00
|
|
|
partition.partition_name = partition_name.strip()
|
|
|
|
modified = True
|
|
|
|
|
|
|
|
numero = data.get("numero")
|
|
|
|
if numero is not None and numero != partition.numero:
|
|
|
|
if not isinstance(numero, int):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, "invalid type for numero")
|
2022-07-20 15:07:31 +02:00
|
|
|
partition.numero = numero
|
|
|
|
modified = True
|
|
|
|
|
|
|
|
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
|
|
|
|
value = data.get(boolean_field)
|
|
|
|
if value is not None and value != getattr(partition, boolean_field):
|
2022-07-20 22:33:41 +02:00
|
|
|
if not isinstance(value, bool):
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(API_CLIENT_ERROR, f"invalid type for {boolean_field}")
|
2022-08-03 21:42:53 +02:00
|
|
|
if boolean_field == "groups_editable" and partition.is_parcours():
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(
|
|
|
|
API_CLIENT_ERROR, f"can't change {scu.PARTITION_PARCOURS}"
|
|
|
|
)
|
2022-07-20 15:07:31 +02:00
|
|
|
setattr(partition, boolean_field, value)
|
|
|
|
modified = True
|
|
|
|
|
|
|
|
if modified:
|
|
|
|
db.session.add(partition)
|
|
|
|
db.session.commit()
|
2022-07-20 22:33:41 +02:00
|
|
|
log(f"modified partition {partition}")
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
|
|
|
|
2023-04-06 16:10:32 +02:00
|
|
|
return partition.to_dict(with_groups=True)
|
2022-07-20 15:07:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
|
2022-07-27 16:03:14 +02:00
|
|
|
@api_web_bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-09-02 23:12:41 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-04-06 16:10:32 +02:00
|
|
|
@as_json
|
2022-07-20 15:07:31 +02:00
|
|
|
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
|
|
|
|
pas être supprimée.
|
|
|
|
Note 2: Si la partition de parcours est supprimée, les étudiants
|
|
|
|
sont désinscrits des parcours.
|
|
|
|
"""
|
2022-07-27 16:03:14 +02:00
|
|
|
query = Partition.query.filter_by(id=partition_id)
|
|
|
|
if g.scodoc_dept:
|
|
|
|
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
|
|
partition: Partition = query.first_or_404()
|
2023-01-24 12:12:24 +01:00
|
|
|
if not partition.formsemestre.etat:
|
|
|
|
return json_error(403, "formsemestre verrouillé")
|
2023-09-02 23:12:41 +02:00
|
|
|
if not partition.formsemestre.can_change_groups():
|
|
|
|
return json_error(401, "opération non autorisée")
|
2022-07-20 15:07:31 +02:00
|
|
|
if not partition.partition_name:
|
2023-02-22 02:13:06 +01:00
|
|
|
return json_error(
|
|
|
|
API_CLIENT_ERROR, "ne peut pas supprimer la partition par défaut"
|
|
|
|
)
|
2022-07-20 15:07:31 +02:00
|
|
|
is_parcours = partition.is_parcours()
|
|
|
|
formsemestre: FormSemestre = partition.formsemestre
|
2022-07-20 22:33:41 +02:00
|
|
|
log(f"deleting partition {partition}")
|
2022-07-20 15:07:31 +02:00
|
|
|
db.session.delete(partition)
|
2022-07-20 22:33:41 +02:00
|
|
|
db.session.commit()
|
2022-07-20 22:03:29 +02:00
|
|
|
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
2022-07-20 15:07:31 +02:00
|
|
|
sco_cache.invalidate_formsemestre(formsemestre.id)
|
|
|
|
if is_parcours:
|
|
|
|
formsemestre.update_inscriptions_parcours_from_groups()
|
2023-04-06 16:10:32 +02:00
|
|
|
return {"OK": True}
|