Ordre des groupes et partitions (API)

This commit is contained in:
Emmanuel Viennet 2022-07-24 22:04:37 +02:00
parent 936fe3d716
commit f16f344720
4 changed files with 86 additions and 20 deletions

View File

@ -7,12 +7,13 @@
""" """
ScoDoc 9 API : partitions ScoDoc 9 API : partitions
""" """
from flask import abort, jsonify, request from flask import jsonify, request
import app import app
from app import db, log from app import db, log
from app.api import bp from app.api import bp
from app.api.auth import permission_required_api from app.api.auth import permission_required_api
from app.api.errors import error_response
from app.models import FormSemestre, FormSemestreInscription, Identite from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import GroupDescr, Partition from app.models import GroupDescr, Partition
from app.models.groups import group_membership from app.models.groups import group_membership
@ -112,7 +113,7 @@ def etud_in_group_query(group_id: int):
"""Etudiants du groupe, filtrés par état""" """Etudiants du groupe, filtrés par état"""
etat = request.args.get("etat") etat = request.args.get("etat")
if etat not in {scu.INSCRIT, scu.DEMISSION, scu.DEF}: if etat not in {scu.INSCRIT, scu.DEMISSION, scu.DEF}:
abort(404, "etat invalid") return error_response(404, "etat: valeur invalide")
group = GroupDescr.query.get_or_404(group_id) group = GroupDescr.query.get_or_404(group_id)
query = ( query = (
Identite.query.join(FormSemestreInscription) Identite.query.join(FormSemestreInscription)
@ -131,7 +132,7 @@ def set_etud_group(etudid: int, group_id: int):
etud = Identite.query.get_or_404(etudid) etud = Identite.query.get_or_404(etudid)
group = GroupDescr.query.get_or_404(group_id) group = GroupDescr.query.get_or_404(group_id)
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}: if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
abort(404, "etud non inscrit au formsemestre du groupe") return error_response(404, "etud non inscrit au formsemestre du groupe")
groups = ( groups = (
GroupDescr.query.filter_by(partition_id=group.partition.id) GroupDescr.query.filter_by(partition_id=group.partition.id)
.join(group_membership) .join(group_membership)
@ -180,13 +181,13 @@ def group_create(partition_id: int):
""" """
partition: Partition = Partition.query.get_or_404(partition_id) partition: Partition = Partition.query.get_or_404(partition_id)
if not partition.groups_editable: if not partition.groups_editable:
abort(404, "partition non editable") return error_response(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name") group_name = data.get("group_name")
if group_name is None: if group_name is None:
abort(404, "missing group name or invalid data format") return error_response(404, "missing group name or invalid data format")
if not GroupDescr.check_name(partition, group_name): if not GroupDescr.check_name(partition, group_name):
abort(404, "invalid group_name") return error_response(404, "invalid group_name")
group_name = group_name.strip() group_name = group_name.strip()
group = GroupDescr(group_name=group_name, partition_id=partition_id) group = GroupDescr(group_name=group_name, partition_id=partition_id)
@ -204,7 +205,7 @@ def group_delete(group_id: int):
"""Suppression d'un groupe""" """Suppression d'un groupe"""
group = GroupDescr.query.get_or_404(group_id) group = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable: if not group.partition.groups_editable:
abort(404, "partition non editable") return error_response(404, "partition non editable")
formsemestre_id = group.partition.formsemestre_id formsemestre_id = group.partition.formsemestre_id
log(f"deleting {group}") log(f"deleting {group}")
db.session.delete(group) db.session.delete(group)
@ -220,12 +221,12 @@ def group_edit(group_id: int):
"""Edit a group""" """Edit a group"""
group: GroupDescr = GroupDescr.query.get_or_404(group_id) group: GroupDescr = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable: if not group.partition.groups_editable:
abort(404, "partition non editable") return error_response(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name") group_name = data.get("group_name")
if group_name is not None: if group_name is not None:
if not GroupDescr.check_name(group.partition, group_name, existing=True): if not GroupDescr.check_name(group.partition, group_name, existing=True):
abort(404, "invalid group_name") return error_response(404, "invalid group_name")
group.group_name = group_name.strip() group.group_name = group_name.strip()
db.session.add(group) db.session.add(group)
db.session.commit() db.session.commit()
@ -253,12 +254,12 @@ def partition_create(formsemestre_id: int):
data = request.get_json(force=True) # may raise 400 Bad Request data = request.get_json(force=True) # may raise 400 Bad Request
partition_name = data.get("partition_name") partition_name = data.get("partition_name")
if partition_name is None: if partition_name is None:
abort(404, "missing partition_name or invalid data format") return error_response(404, "missing partition_name or invalid data format")
if not Partition.check_name(formsemestre, partition_name): if not Partition.check_name(formsemestre, partition_name):
abort(404, "invalid partition_name") return error_response(404, "invalid partition_name")
numero = data.get("numero", 0) numero = data.get("numero", 0)
if not isinstance(numero, int): if not isinstance(numero, int):
abort(404, "invalid type for numero") return error_response(404, "invalid type for numero")
args = { args = {
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"partition_name": partition_name.strip(), "partition_name": partition_name.strip(),
@ -269,7 +270,7 @@ def partition_create(formsemestre_id: int):
boolean_field, False if boolean_field != "groups_editable" else True boolean_field, False if boolean_field != "groups_editable" else True
) )
if not isinstance(value, bool): if not isinstance(value, bool):
abort(404, f"invalid type for {boolean_field}") return error_response(404, f"invalid type for {boolean_field}")
args[boolean_field] = value args[boolean_field] = value
partition = Partition(**args) partition = Partition(**args)
@ -281,6 +282,52 @@ def partition_create(formsemestre_id: int):
return jsonify(partition.to_dict(with_groups=True)) return jsonify(partition.to_dict(with_groups=True))
@bp.route("/formsemestre/<int:formsemestre_id>/partitions/order", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def formsemestre_order_partitions(formsemestre_id: int):
"""Modifie l'ordre des partitions du formsemestre
JSON args: [partition_id1, partition_id2, ...]
"""
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
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
):
return error_response(
404,
message="paramètre liste des partitions invalide",
)
for p_id, numero in zip(partition_ids, range(len(partition_ids))):
p = Partition.query.get_or_404(p_id)
p.numero = numero
db.session.add(p)
db.session.commit()
return jsonify(formsemestre.to_dict())
@bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def partition_order_groups(partition_id: int):
"""Modifie l'ordre des groupes de la partition
JSON args: [group_id1, group_id2, ...]
"""
partition = Partition.query.get_or_404(partition_id)
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
):
return error_response(
404,
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()
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"]) @bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups) @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def partition_edit(partition_id: int): def partition_edit(partition_id: int):
@ -304,14 +351,14 @@ def partition_edit(partition_id: int):
if not Partition.check_name( if not Partition.check_name(
partition.formsemestre, partition_name, existing=True partition.formsemestre, partition_name, existing=True
): ):
abort(404, "invalid partition_name") return error_response(404, "invalid partition_name")
partition.partition_name = partition_name.strip() partition.partition_name = partition_name.strip()
modified = True modified = True
numero = data.get("numero") numero = data.get("numero")
if numero is not None and numero != partition.numero: if numero is not None and numero != partition.numero:
if not isinstance(numero, int): if not isinstance(numero, int):
abort(404, "invalid type for numero") return error_response(404, "invalid type for numero")
partition.numero = numero partition.numero = numero
modified = True modified = True
@ -319,7 +366,7 @@ def partition_edit(partition_id: int):
value = data.get(boolean_field) value = data.get(boolean_field)
if value is not None and value != getattr(partition, boolean_field): if value is not None and value != getattr(partition, boolean_field):
if not isinstance(value, bool): if not isinstance(value, bool):
abort(404, f"invalid type for {boolean_field}") return error_response(404, f"invalid type for {boolean_field}")
setattr(partition, boolean_field, value) setattr(partition, boolean_field, value)
modified = True modified = True
@ -345,7 +392,7 @@ def partition_delete(partition_id: int):
""" """
partition = Partition.query.get_or_404(partition_id) partition = Partition.query.get_or_404(partition_id)
if not partition.partition_name: if not partition.partition_name:
abort(404, "ne peut pas supprimer la partition par défaut") return error_response(404, "ne peut pas supprimer la partition par défaut")
is_parcours = partition.is_parcours() is_parcours = partition.is_parcours()
formsemestre: FormSemestre = partition.formsemestre formsemestre: FormSemestre = partition.formsemestre
log(f"deleting partition {partition}") log(f"deleting partition {partition}")

View File

@ -90,7 +90,7 @@ class Partition(db.Model):
d.pop("formsemestre", None) d.pop("formsemestre", None)
if with_groups: if with_groups:
groups = sorted(self.groups, key=lambda g: g.group_name) groups = sorted(self.groups, key=lambda g: (g.numero or 0, g.group_name))
# un dict et non plus une liste, pour JSON # un dict et non plus une liste, pour JSON
d["groups"] = { d["groups"] = {
group.id: group.to_dict(with_partition=False) for group in groups group.id: group.to_dict(with_partition=False) for group in groups
@ -109,6 +109,8 @@ class GroupDescr(db.Model):
partition_id = db.Column(db.Integer, db.ForeignKey("partition.id")) partition_id = db.Column(db.Integer, db.ForeignKey("partition.id"))
# "A", "C2", ... (NULL for 'all'): # "A", "C2", ... (NULL for 'all'):
group_name = db.Column(db.String(GROUPNAME_STR_LEN)) group_name = db.Column(db.String(GROUPNAME_STR_LEN))
# Numero = ordre de presentation)
numero = db.Column(db.Integer)
etuds = db.relationship( etuds = db.relationship(
"Identite", "Identite",
@ -131,9 +133,12 @@ class GroupDescr(db.Model):
"id": self.id, "id": self.id,
"partition_id": self.partition_id, "partition_id": self.partition_id,
"name": self.group_name, "name": self.group_name,
"numero": self.numero,
} }
if with_partition: if with_partition:
d["partition"] = self.partition.to_dict(with_groups=False) d["partition"] = sorted(
self.partition, key=lambda p: p.numero or 0
).to_dict(with_groups=False)
return d return d
@classmethod @classmethod

View File

@ -87,7 +87,7 @@ partitionEditor = ndb.EditableTable(
) )
groupEditor = ndb.EditableTable( groupEditor = ndb.EditableTable(
"group_descr", "group_id", ("group_id", "partition_id", "group_name") "group_descr", "group_id", ("group_id", "partition_id", "group_name", "numero")
) )
group_list = groupEditor.list group_list = groupEditor.list

View File

@ -181,6 +181,20 @@ POST_JSON(
POST_JSON(f"/partition/{2379}/delete") POST_JSON(f"/partition/{2379}/delete")
#
POST_JSON(
"/partition/2264/groups/order",
data=[5563, 5562, 5561, 5560, 5558, 5557, 5316, 5315],
)
POST_JSON(
"/formsemestre/1063/partitions/order",
data=[2264, 2263, 2265, 2266, 2267, 2372, 2378],
)
GET(f"/partition/2264")
# Recherche de formsemestres # Recherche de formsemestres
sems = GET(f"/formsemestres/query?etape_apo=V1RT&annee_scolaire=2021") sems = GET(f"/formsemestres/query?etape_apo=V1RT&annee_scolaire=2021")