1
0
forked from ScoDoc/ScoDoc

WIP: API edition partitions

This commit is contained in:
Emmanuel Viennet 2022-07-20 15:07:31 +02:00
parent 98cb7bae37
commit b53958c777
3 changed files with 166 additions and 4 deletions

View File

@ -15,6 +15,7 @@ from app.api.auth import token_auth, token_permission_required
from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import GroupDescr, Partition
from app.models.groups import group_membership
from app.scodoc import sco_cache
from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@ -145,6 +146,8 @@ def group_create(partition_id: int):
}
"""
partition: Partition = Partition.query.get_or_404(partition_id)
if not partition.groups_editable:
abort(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name")
if group_name is None:
@ -156,6 +159,7 @@ def group_create(partition_id: int):
group = GroupDescr(group_name=group_name, partition_id=partition_id)
db.session.add(group)
db.session.commit()
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
@ -163,19 +167,25 @@ def group_create(partition_id: int):
@token_auth.login_required
@token_permission_required(Permission.APIEditGroups)
def group_delete(group_id: int):
"""Delete group"""
"""Suppression d'un groupe"""
group = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable:
abort(404, "partition non editable")
formsemestre_id = group.partition.formsemestre_id
db.session.delete(group)
db.session.commit()
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify({"OK": 1})
@bp.route("/group/<init:group_id>/edit", methods=["POST"])
@bp.route("/group/<int:group_id>/edit", methods=["POST"])
@token_auth.login_required
@token_permission_required(Permission.APIEditGroups)
def group_edit(group_id: int):
"""Edit a group"""
group: GroupDescr = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable:
abort(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name")
if group_name is not None:
@ -184,4 +194,124 @@ def group_edit(group_id: int):
group.group_name = group_name.strip()
db.session.add(group)
db.session.commit()
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
@token_auth.login_required
@token_permission_required(Permission.APIEditGroups)
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
}
"""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
data = request.get_json(force=True) # may raise 400 Bad Request
partition_name = data.get("partition_name")
if partition_name is None:
abort(404, "missing partition_name or invalid data format")
if not Partition.check_name(formsemestre, partition_name):
abort(404, "invalid partition_name")
numero = data.get("numero", 0)
if not isinstance(numero, int):
abort(404, "invalid type for numero")
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
)
if not isinstance(boolean_field, bool):
abort(404, f"invalid type for {boolean_field}")
args[boolean_field] = value
partition = Partition(**args)
db.session.add(partition)
db.session.commit()
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@token_auth.login_required
@token_permission_required(Permission.APIEditGroups)
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
}
"""
partition = Partition.query.get_or_404(partition_id)
data = request.get_json(force=True) # may raise 400 Bad Request
modified = False
partition_name = data.get("partition_name")
if partition_name is not None and partition_name != partition.partition_name:
if not Partition.check_name(
partition.formsemestre, partition_name, existing=True
):
abort(404, "invalid partition_name")
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):
abort(404, "invalid type for numero")
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):
if not isinstance(boolean_field, bool):
abort(404, f"invalid type for {boolean_field}")
setattr(partition, boolean_field, value)
modified = True
if modified:
db.session.add(partition)
db.session.commit()
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
@token_auth.login_required
@token_permission_required(Permission.APIEditGroups)
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.
"""
partition = Partition.query.get_or_404(partition_id)
if not partition.partition_name:
abort(404, "ne peut pas supprimer la partition par défaut")
is_parcours = partition.is_parcours()
formsemestre: FormSemestre = partition.formsemestre
db.session.delete(partition)
sco_cache.invalidate_formsemestre(formsemestre.id)
if is_parcours:
formsemestre.update_inscriptions_parcours_from_groups()
return jsonify({"OK": 1})

View File

@ -1,4 +1,9 @@
# -*- coding: UTF-8 -*
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc models: formsemestre
"""

View File

@ -1,12 +1,17 @@
# -*- coding: UTF-8 -*
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""Groups & partitions
"""ScoDoc models: Groups & partitions
"""
from typing import Any
from app import db
from app.models import SHORT_STR_LEN
from app.models import GROUPNAME_STR_LEN
from app.scodoc import sco_utils as scu
class Partition(db.Model):
@ -41,6 +46,7 @@ class Partition(db.Model):
"GroupDescr",
backref=db.backref("partition", lazy=True),
lazy="dynamic",
cascade="all, delete-orphan",
)
def __init__(self, **kwargs):
@ -56,6 +62,27 @@ class Partition(db.Model):
def __repr__(self):
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
@classmethod
def check_name(
cls, formsemestre: "FormSemestre", partition_name: str, existing=False
) -> bool:
"""check if a partition named 'partition_name' can be created in the given formsemestre.
If existing is True, allow a partition_name already existing in the formsemestre.
"""
if not isinstance(partition_name, str):
return False
if not len(partition_name.strip()) > 0:
return False
if (not existing) and (
partition_name in [p.partition_name for p in formsemestre.partitions]
):
return False
return True
def is_parcours(self) -> bool:
"Vrai s'il s'agit de la partitoon de parcours"
return self.partition_name == scu.PARTITION_PARCOURS
def to_dict(self, with_groups=False) -> dict:
"""as a dict, with or without groups"""
d = dict(self.__dict__)