Fix #578 API : Gestion semestre verrouillé. + tests unitaires API OK.

This commit is contained in:
Emmanuel Viennet 2023-01-24 08:12:24 -03:00
parent 80b4465815
commit 1971e5c3de
7 changed files with 106 additions and 21 deletions

View File

@ -171,6 +171,8 @@ def set_etud_group(etudid: int, group_id: int):
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
)
group = query.first_or_404()
if not group.partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
return json_error(404, "etud non inscrit au formsemestre du groupe")
@ -197,6 +199,8 @@ def group_remove_etud(group_id: int, etudid: int):
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
)
group = query.first_or_404()
if not group.partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if etud in group.etuds:
group.etuds.remove(etud)
db.session.commit()
@ -222,6 +226,8 @@ def partition_remove_etud(partition_id: int, etudid: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition = query.first_or_404()
if not partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
groups = (
GroupDescr.query.filter_by(partition_id=partition_id)
.join(group_membership)
@ -252,8 +258,10 @@ def group_create(partition_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition: Partition = query.first_or_404()
if not partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if not partition.groups_editable:
return json_error(404, "partition non editable")
return json_error(403, "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:
@ -284,8 +292,10 @@ def group_delete(group_id: int):
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
)
group: GroupDescr = query.first_or_404()
if not group.partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if not group.partition.groups_editable:
return json_error(404, "partition non editable")
return json_error(403, "partition non editable")
formsemestre_id = group.partition.formsemestre_id
log(f"deleting {group}")
db.session.delete(group)
@ -308,8 +318,10 @@ def group_edit(group_id: int):
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
)
group: GroupDescr = query.first_or_404()
if not group.partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if not group.partition.groups_editable:
return json_error(404, "partition non editable")
return json_error(403, "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:
@ -348,6 +360,8 @@ def partition_create(formsemestre_id: int):
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.etat:
return json_error(403, "formsemestre verrouillé")
data = request.get_json(force=True) # may raise 400 Bad Request
partition_name = data.get("partition_name")
if partition_name is None:
@ -396,6 +410,8 @@ def formsemestre_order_partitions(formsemestre_id: int):
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.etat:
return json_error(403, "formsemestre verrouillé")
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
@ -433,6 +449,8 @@ def partition_order_groups(partition_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition: Partition = query.first_or_404()
if not partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
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
@ -474,6 +492,8 @@ def partition_edit(partition_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition: Partition = query.first_or_404()
if not partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
data = request.get_json(force=True) # may raise 400 Bad Request
modified = False
partition_name = data.get("partition_name")
@ -532,6 +552,8 @@ def partition_delete(partition_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition: Partition = query.first_or_404()
if not partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé")
if not partition.partition_name:
return json_error(404, "ne peut pas supprimer la partition par défaut")
is_parcours = partition.is_parcours()

View File

@ -63,51 +63,51 @@ class FormSemestre(db.Model):
"False si verrouillé"
modalite = db.Column(
db.String(SHORT_STR_LEN), db.ForeignKey("notes_form_modalites.modalite")
) # "FI", "FAP", "FC", ...
# gestion compensation sem DUT:
)
"Modalité de formation: 'FI', 'FAP', 'FC', ..."
gestion_compensation = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
# ne publie pas le bulletin XML ou JSON:
"gestion compensation sem DUT (inutilisé en APC)"
bul_hide_xml = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
# Bloque le calcul des moyennes (générale et d'UE)
"ne publie pas le bulletin XML ou JSON"
block_moyennes = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
# Bloque le calcul de la moyenne générale (utile pour BUT)
"Bloque le calcul des moyennes (générale et d'UE)"
block_moyenne_generale = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
"Si vrai, la moyenne générale indicative BUT n'est pas calculée"
# semestres decales (pour gestion jurys):
gestion_semestrielle = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
# couleur fond bulletins HTML:
"Semestres décalés (pour gestion jurys DUT, pas implémenté ou utile en BUT)"
bul_bgcolor = db.Column(
db.String(SHORT_STR_LEN),
default="white",
server_default="white",
nullable=False,
)
# autorise resp. a modifier semestre:
"couleur fond bulletins HTML"
resp_can_edit = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
)
# autorise resp. a modifier slt les enseignants:
"autorise resp. à modifier le formsemestre"
resp_can_change_ens = db.Column(
db.Boolean(), nullable=False, default=True, server_default="true"
)
# autorise les ens a creer des evals:
"autorise resp. a modifier slt les enseignants"
ens_can_edit_eval = db.Column(
db.Boolean(), nullable=False, default=False, server_default="False"
)
# code element semestre Apogee, eg 'VRTW1' ou 'V2INCS4,V2INLS4,...'
"autorise les enseignants à créer des évals dans leurs modimpls"
elt_sem_apo = db.Column(db.Text()) # peut être fort long !
# code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...'
"code element semestre Apogee, eg 'VRTW1' ou 'V2INCS4,V2INLS4,...'"
elt_annee_apo = db.Column(db.Text())
"code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...'"
# Relations:
etapes = db.relationship(

View File

@ -4,4 +4,5 @@ markers =
but_gb
lemans
lyon
test_test

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.4.30"
SCOVERSION = "9.4.31"
SCONAME = "ScoDoc"

View File

@ -25,7 +25,7 @@ from app import models
from app.auth.models import User, Role, UserRole
from app.entreprises.models import entreprises_reset_database
from app.models import departements
from app.models import Departement, departements
from app.models import Formation, UniteEns, Matiere, Module
from app.models import FormSemestre, FormSemestreInscription
from app.models import GroupDescr
@ -73,6 +73,7 @@ def make_shell_context():
"ctx": app.test_request_context(),
"current_app": flask.current_app,
"current_user": current_user,
"Departement": Departement,
"db": db,
"Evaluation": Evaluation,
"flask": flask,

View File

@ -34,7 +34,11 @@ def test_list_users(api_admin_headers):
# Tous les utilisateurs, vus par SuperAdmin:
users = GET("/users/query", headers=admin_h)
assert len(users) > 2
# Les utilisateurs du dept. TAPI
users_TAPI = GET("/users/query?departement=TAPI", headers=admin_h)
nb_TAPI = len(users_TAPI)
assert nb_TAPI > 1
# Les utilisateurs de chaque département (+ ceux sans département)
all_users = []
for acronym in [dept["acronym"] for dept in depts] + [""]:
@ -59,9 +63,8 @@ def test_list_users(api_admin_headers):
for i, u in enumerate(u for u in u_users if u["dept"] != "TAPI"):
headers = get_auth_headers(u["user_name"], "test")
users_by_u = GET("/users/query", headers=headers)
assert len(users_by_u) == 4 + i
# explication: tous ont le droit de voir les 3 users de TAPI
# (test, other et u_TAPI)
assert len(users_by_u) == nb_TAPI + 1 + i
# explication: tous ont le droit de voir les users de TAPI
# plus l'utilisateur de chaque département jusqu'au leur
# (u_AA voit AA, u_BB voit AA et BB, etc)
@ -90,6 +93,10 @@ def test_edit_users(api_admin_headers):
)
assert user["dept"] == "TAPI"
assert user["active"] is False
user = GET(f"/user/{user['id']}", headers=admin_h)
assert user["nom"] == "Toto"
assert user["dept"] == "TAPI"
assert user["active"] is False
def test_roles(api_admin_headers):
@ -229,3 +236,10 @@ def test_modif_users_depts(api_admin_headers):
ok = True
assert ok
# Nettoyage:
# on ne peut pas supprimer l'utilisateur lambda, mais on
# le rend inactif et on le retire de son département
u = POST_JSON(
f"/user/{u_lambda['id']}/edit",
{"active": False, "dept": None},
headers=admin_h,
)

47
tests/api/test_test.py Normal file
View File

@ -0,0 +1,47 @@
# -*- coding: UTF-8 -*
"""Unit tests for... tests
Ensure test DB is in the expected initial state.
Usage: pytest tests/unit/test_test.py
"""
import pytest
from tests.api.setup_test_api import (
api_headers,
GET,
)
@pytest.mark.test_test
def test_test_db(api_headers):
"""Check that we indeed have: 2 users, 1 dept, 3 formsemestres.
Juste après init, les ensembles seront ceux donnés ci-dessous.
Les autres tests peuvent ajouter des éléments, c'edt pourquoi on utilise issubset().
"""
headers = api_headers
assert {
"admin_api",
"admin",
"lecteur_api",
"other",
"test",
"u_AA",
"u_BB",
"u_CC",
"u_DD",
"u_TAPI",
}.issubset({u["user_name"] for u in GET("/users/query", headers=headers)})
assert {
"AA",
"BB",
"CC",
"DD",
"TAPI",
}.issubset({d["acronym"] for d in GET("/departements", headers=headers)})
assert 1 in (
formsemestre["semestre_id"]
for formsemestre in GET("/formsemestres/query", headers=headers)
)