Modernisation suppressions UE et formations

This commit is contained in:
Emmanuel Viennet 2022-07-13 18:52:07 +02:00
parent 9480e15b83
commit 48e31b5f39
7 changed files with 88 additions and 64 deletions

View File

@ -105,9 +105,11 @@ class Formation(db.Model):
return len(self.formsemestres.filter_by(etat=False).all()) > 0
def invalidate_module_coefs(self, semestre_idx: int = None):
"""Invalide les coefficients de modules cachés.
Si semestre_idx est None, invalide tous les semestres,
"""Invalide le cache des coefficients de modules.
Si semestre_idx est None, invalide les coefs de tous les semestres,
sinon invalide le semestre indiqué et le cache de la formation.
Dans tous les cas, invalide tous les formsemestres.
"""
if semestre_idx is None:
keys = {f"{self.id}.{m.semestre_id}" for m in self.modules}

View File

@ -83,10 +83,9 @@ class UniteEns(db.Model):
return sco_edit_ue.ue_is_locked(self.id)
def can_be_deleted(self) -> bool:
"""True si l'UE n'est pas utilisée dans des formsemestre
et n'a pas de module rattachés
"""True si l'UE n'a pas de moduleimpl rattachés
(pas un seul module de cette UE n'a de modimpl)
"""
# "pas un seul module de cette UE n'a de modimpl...""
return (self.modules.count() == 0) or not any(
m.modimpls.all() for m in self.modules
)

View File

@ -228,7 +228,7 @@ class TableRecapWithEvalsCache(ScoDocCache):
def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=False)
formsemestre_id=None, pdfonly=False
):
"""expire cache pour un semestre (ou tous si formsemestre_id non spécifié).
"""expire cache pour un semestre (ou tous ceux du département si formsemestre_id non spécifié).
Si pdfonly, n'expire que les bulletins pdf cachés.
"""
from app.models.formsemestre import FormSemestre

View File

@ -42,7 +42,7 @@ from app.models import ScolarNews
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError
from app.scodoc.sco_exceptions import ScoValueError, ScoNonEmptyFormationObject
from app.scodoc import html_sco_header
from app.scodoc import sco_codes_parcours
@ -100,26 +100,38 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
return "\n".join(H)
def do_formation_delete(oid):
def do_formation_delete(formation_id):
"""delete a formation (and all its UE, matieres, modules)
XXX delete all ues, will break if there are validations ! USE WITH CARE !
Warning: delete all ues, will ask if there are validations !
"""
F = sco_formations.formation_list(args={"formation_id": oid})[0]
if sco_formations.formation_has_locked_sems(oid):
raise ScoLockedFormError()
cnx = ndb.GetDBConnexion()
# delete all UE in this formation
ues = sco_edit_ue.ue_list({"formation_id": oid})
for ue in ues:
sco_edit_ue.do_ue_delete(ue["ue_id"], force=True)
formation: Formation = Formation.query.get(formation_id)
if formation is None:
return
acronyme = formation.acronyme
if formation.formsemestres.count():
raise ScoNonEmptyFormationObject(
type_objet="formation",
msg=formation.titre,
dest_url=url_for(
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id
),
)
sco_formations._formationEditor.delete(cnx, oid)
# Suppression des modules
for module in formation.modules:
db.session.delete(module)
db.session.flush()
# Suppression des UEs
for ue in formation.ues:
sco_edit_ue.do_ue_delete(ue, force=True)
db.session.delete(formation)
# news
ScolarNews.add(
typ=ScolarNews.NEWS_FORM,
obj=oid,
text=f"Suppression de la formation {F['acronyme']}",
obj=formation_id,
text=f"Suppression de la formation {acronyme}",
)

View File

@ -37,7 +37,14 @@ from app import db
from app import log
from app.but import apc_edit_ue
from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
from app.models import Formation, UniteEns, ModuleImpl, Module
from app.models import (
Formation,
FormSemestreUEComputationExpr,
FormSemestreUECoef,
Matiere,
UniteEns,
)
from app.models import ApcValidationRCUE, ScolarFormSemestreValidation, ScolarEvent
from app.models import ScolarNews
from app.models.formations import Matiere
import app.scodoc.notesdb as ndb
@ -138,12 +145,11 @@ def do_ue_create(args):
return ue_id
def do_ue_delete(ue_id, delete_validations=False, force=False):
"delete UE and attached matieres (but not modules)"
from app.scodoc import sco_cursus_dut
ue = UniteEns.query.get_or_404(ue_id)
formation = ue.formation
def do_ue_delete(ue: UniteEns, delete_validations=False, force=False):
"""delete UE and attached matieres (but not modules).
Si force, pas de confirmation dialog et pas de redirect
"""
formation: Formation = ue.formation
semestre_idx = ue.semestre_idx
if not ue.can_be_deleted():
raise ScoNonEmptyFormationObject(
@ -157,20 +163,22 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
),
)
cnx = ndb.GetDBConnexion()
log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue.id, delete_validations))
# check
# if ue_is_locked(ue.id):
# raise ScoLockedFormError()
log(f"do_ue_delete: ue_id={ue.id}, delete_validations={delete_validations}")
# Il y a-t-il des etudiants ayant validé cette UE ?
# si oui, propose de supprimer les validations
validations = sco_cursus_dut.scolar_formsemestre_validation_list(
cnx, args={"ue_id": ue.id}
)
if validations and not delete_validations and not force:
validations_ue = ScolarFormSemestreValidation.query.filter_by(ue_id=ue.id).all()
validations_rcue = ApcValidationRCUE.query.filter(
(ApcValidationRCUE.ue1_id == ue.id) | (ApcValidationRCUE.ue2_id == ue.id)
).all()
if (
(len(validations_ue) > 0 or len(validations_rcue) > 0)
and not delete_validations
and not force
):
return scu.confirm_dialog(
"<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>"
% (len(validations), ue.acronyme, ue.titre),
f"""<p>Des étudiants ont une décision de jury sur l'UE {ue.acronyme} ({ue.titre})</p>
<p>Si vous supprimez cette UE, ces décisions vont être supprimées !</p>""",
dest_url="",
target_variable="delete_validations",
cancel_url=url_for(
@ -183,31 +191,34 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
)
if delete_validations:
log(f"deleting all validations of UE {ue.id}")
ndb.SimpleQuery(
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
{"ue_id": ue.id},
)
for v in validations_ue:
db.session.delete(v)
for v in validations_rcue:
db.session.delete(v)
# delete old formulas
ndb.SimpleQuery(
"DELETE FROM notes_formsemestre_ue_computation_expr WHERE ue_id=%(ue_id)s",
{"ue_id": ue.id},
)
# delete all matiere in this UE
mats = sco_edit_matiere.matiere_list({"ue_id": ue.id})
for mat in mats:
sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
# delete uecoef and events
ndb.SimpleQuery(
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
{"ue_id": ue.id},
)
ndb.SimpleQuery("DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue.id})
cnx = ndb.GetDBConnexion()
_ueEditor.delete(cnx, ue.id)
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement
# utilisé: acceptable de tout invalider):
formulas = FormSemestreUEComputationExpr.query.filter_by(ue_id=ue.id).all()
for formula in formulas:
db.session.delete(formula)
# delete all matieres in this UE
for mat in Matiere.query.filter_by(ue_id=ue.id):
db.session.delete(mat)
# delete uecoefs
for uecoef in FormSemestreUECoef.query.filter_by(ue_id=ue.id):
db.session.delete(uecoef)
# delete events
for event in ScolarEvent.query.filter_by(ue_id=ue.id):
db.session.delete(event)
db.session.flush()
db.session.delete(ue)
db.session.commit()
# cas compliqué, mais rarement utilisé: acceptable de tout invalider
formation.invalidate_module_coefs()
# -> invalide aussi .invalidate_formsemestre()
# -> invalide aussi les formsemestres
# news
ScolarNews.add(
typ=ScolarNews.NEWS_FORM,
@ -601,7 +612,7 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
),
)
return do_ue_delete(ue.id, delete_validations=delete_validations)
return do_ue_delete(ue, delete_validations=delete_validations)
def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list

View File

@ -116,7 +116,7 @@ class ScoNonEmptyFormationObject(ScoValueError):
"""On ne peut pas supprimer un module/matiere ou UE si des formsemestre s'y réfèrent"""
def __init__(self, type_objet="objet'", msg="", dest_url=None):
msg = f"""<h3>{type_objet} "{msg}" utilisé dans des semestres: suppression impossible.</h3>
msg = f"""<h3>{type_objet} "{msg}" utilisé(e) dans des semestres: suppression impossible.</h3>
<p class="help">Il faut d'abord supprimer le semestre (ou en retirer ce {type_objet}).
Mais il est peut-être préférable de laisser ce programme intact et d'en créer une
nouvelle version pour la modifier sans affecter les semestres déjà en place.

View File

@ -327,7 +327,7 @@ def test_formations(test_client):
# --- Suppression d'une formation
sco_edit_formation.do_formation_delete(oid=formation_id2)
sco_edit_formation.do_formation_delete(formation_id=formation_id2)
lif3 = notes.formation_list(format="json").get_data(as_text=True)
assert isinstance(lif3, str)
load_lif3 = json.loads(lif3)