forked from ScoDoc/ScoDoc
WIP: API edition partitions
This commit is contained in:
parent
98cb7bae37
commit
b53958c777
@ -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 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
|
||||||
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc import sco_utils as scu
|
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)
|
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
|
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:
|
||||||
@ -156,6 +159,7 @@ def group_create(partition_id: int):
|
|||||||
group = GroupDescr(group_name=group_name, partition_id=partition_id)
|
group = GroupDescr(group_name=group_name, partition_id=partition_id)
|
||||||
db.session.add(group)
|
db.session.add(group)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
||||||
return jsonify(group.to_dict(with_partition=True))
|
return jsonify(group.to_dict(with_partition=True))
|
||||||
|
|
||||||
|
|
||||||
@ -163,19 +167,25 @@ def group_create(partition_id: int):
|
|||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIEditGroups)
|
@token_permission_required(Permission.APIEditGroups)
|
||||||
def group_delete(group_id: int):
|
def group_delete(group_id: int):
|
||||||
"""Delete group"""
|
"""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:
|
||||||
|
abort(404, "partition non editable")
|
||||||
|
formsemestre_id = group.partition.formsemestre_id
|
||||||
db.session.delete(group)
|
db.session.delete(group)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre_id)
|
||||||
return jsonify({"OK": 1})
|
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_auth.login_required
|
||||||
@token_permission_required(Permission.APIEditGroups)
|
@token_permission_required(Permission.APIEditGroups)
|
||||||
def group_edit(group_id: int):
|
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:
|
||||||
|
abort(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:
|
||||||
@ -184,4 +194,124 @@ def group_edit(group_id: int):
|
|||||||
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()
|
||||||
|
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
|
||||||
return jsonify(group.to_dict(with_partition=True))
|
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})
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- coding: UTF-8 -*
|
||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
"""ScoDoc models: formsemestre
|
"""ScoDoc models: formsemestre
|
||||||
"""
|
"""
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- 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 import db
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import GROUPNAME_STR_LEN
|
from app.models import GROUPNAME_STR_LEN
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
class Partition(db.Model):
|
class Partition(db.Model):
|
||||||
@ -41,6 +46,7 @@ class Partition(db.Model):
|
|||||||
"GroupDescr",
|
"GroupDescr",
|
||||||
backref=db.backref("partition", lazy=True),
|
backref=db.backref("partition", lazy=True),
|
||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@ -56,6 +62,27 @@ class Partition(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
|
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:
|
def to_dict(self, with_groups=False) -> dict:
|
||||||
"""as a dict, with or without groups"""
|
"""as a dict, with or without groups"""
|
||||||
d = dict(self.__dict__)
|
d = dict(self.__dict__)
|
||||||
|
Loading…
Reference in New Issue
Block a user