diff --git a/app/__init__.py b/app/__init__.py index 0943a91f6..04e28207c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -190,6 +190,7 @@ def create_app(config_class=DevConfig): app.register_error_handler(ScoGenError, handle_sco_value_error) app.register_error_handler(ScoValueError, handle_sco_value_error) + app.register_error_handler(AccessDenied, handle_access_denied) app.register_error_handler(500, internal_server_error) app.register_error_handler(503, postgresql_server_error) diff --git a/app/scodoc/sco_edit_apc.py b/app/scodoc/sco_edit_apc.py index 773cf81d1..05b72b011 100644 --- a/app/scodoc/sco_edit_apc.py +++ b/app/scodoc/sco_edit_apc.py @@ -78,7 +78,8 @@ def html_edit_formation_apc( alt="supprimer", ), "delete_disabled": scu.icontag( - "delete_small_dis_img", title="Suppression impossible (module utilisé)" + "delete_small_dis_img", + title="Suppression impossible (utilisé dans des semestres)", ), } diff --git a/app/scodoc/sco_edit_matiere.py b/app/scodoc/sco_edit_matiere.py index 62a7e1d20..13ddb2a99 100644 --- a/app/scodoc/sco_edit_matiere.py +++ b/app/scodoc/sco_edit_matiere.py @@ -30,13 +30,18 @@ """ import flask from flask import g, url_for, request +from app.models.formations import Matiere import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app import log from app.models import Formation from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message -from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError +from app.scodoc.sco_exceptions import ( + ScoValueError, + ScoLockedFormError, + ScoNonEmptyFormationObject, +) from app.scodoc import html_sco_header _matiereEditor = ndb.EditableTable( @@ -156,6 +161,16 @@ associé. return flask.redirect(dest_url) +def can_delete_matiere(matiere: Matiere) -> tuple[bool, str]: + "True si la matiere n'est pas utilisée dans des formsemestre" + locked = matiere_is_locked(matiere.id) + if locked: + return False + if any(m.modimpls.all() for m in matiere.modules): + return False + return True + + def do_matiere_delete(oid): "delete matiere and attached modules" from app.scodoc import sco_formations @@ -165,17 +180,16 @@ def do_matiere_delete(oid): cnx = ndb.GetDBConnexion() # check - mat = matiere_list({"matiere_id": oid})[0] + matiere = Matiere.query.get_or_404(oid) + mat = matiere_list({"matiere_id": oid})[0] # compat sco7 ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0] - locked = matiere_is_locked(mat["matiere_id"]) - if locked: - log("do_matiere_delete: mat=%s" % mat) - log("do_matiere_delete: ue=%s" % ue) - log("do_matiere_delete: locked sems: %s" % locked) - raise ScoLockedFormError() - log("do_matiere_delete: matiere_id=%s" % oid) + if not can_delete_matiere(matiere): + # il y a au moins un modimpl dans un module de cette matière + raise ScoNonEmptyFormationObject("Matière", matiere.titre) + + log("do_matiere_delete: matiere_id=%s" % matiere.id) # delete all modules in this matiere - mods = sco_edit_module.module_list({"matiere_id": oid}) + mods = sco_edit_module.module_list({"matiere_id": matiere.id}) for mod in mods: sco_edit_module.do_module_delete(mod["module_id"]) _matiereEditor.delete(cnx, oid) @@ -194,11 +208,25 @@ def matiere_delete(matiere_id=None): """Delete matière""" from app.scodoc import sco_edit_ue - M = matiere_list(args={"matiere_id": matiere_id})[0] - UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0] + matiere = Matiere.query.get_or_404(matiere_id) + if not can_delete_matiere(matiere): + # il y a au moins un modimpl dans un module de cette matière + raise ScoNonEmptyFormationObject( + "Matière", + matiere.titre, + dest_url=url_for( + "notes.ue_table", + formation_id=matiere.ue.formation_id, + semestre_idx=matiere.ue.semestre_idx, + scodoc_dept=g.scodoc_dept, + ), + ) + + mat = matiere_list(args={"matiere_id": matiere_id})[0] + UE = sco_edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0] H = [ html_sco_header.sco_header(page_title="Suppression d'une matière"), - "
Il faut d'abord supprimer le semestre. Mais il est peut être préférable de - laisser ce programme intact et d'en créer une nouvelle version pour la modifier. +
Il faut d'abord supprimer le semestre (ou en retirer ce module). 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.
reprendre @@ -365,12 +387,21 @@ def do_module_delete(oid): def module_delete(module_id=None): """Delete a module""" - if not module_id: - raise ScoValueError("invalid module !") - modules = module_list(args={"module_id": module_id}) - if not modules: - raise ScoValueError("Module inexistant !") - mod = modules[0] + module = Module.query.get_or_404(module_id) + mod = module_list(args={"module_id": module_id})[0] # sco7 + + if not can_delete_module(module): + raise ScoNonEmptyFormationObject( + "Module", + msg=module.titre, + dest_url=url_for( + "notes.ue_table", + scodoc_dept=g.scodoc_dept, + formation_id=module.formation_id, + semestre_idx=module.ue.semestre_idx, + ), + ) + H = [ html_sco_header.sco_header(page_title="Suppression d'un module"), """%d étudiants ont validé l'UE %s (%s)
Si vous supprimez cette UE, ces validations vont être supprimées !
" - % (len(validations), ue["acronyme"], ue["titre"]), + % (len(validations), ue.acronyme, ue.titre), dest_url="", target_variable="delete_validations", cancel_url=url_for( "notes.ue_table", scodoc_dept=g.scodoc_dept, - formation_id=str(ue["formation_id"]), + formation_id=ue.formation_id, + semestre_idx=ue.semestre_idx, ), - parameters={"ue_id": ue_id, "dialog_confirmed": 1}, + parameters={"ue_id": ue.id, "dialog_confirmed": 1}, ) if delete_validations: - log("deleting all validations of UE %s" % ue_id) + log("deleting all validations of UE %s" % ue.id) ndb.SimpleQuery( "DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s", - {"ue_id": ue_id}, + {"ue_id": ue.id}, ) # delete all matiere in this UE - mats = sco_edit_matiere.matiere_list({"ue_id": ue_id}) + 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}, + {"ue_id": ue.id}, ) - ndb.SimpleQuery("DELETE FROM scolar_events 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 ?): + _ueEditor.delete(cnx, ue.id) + # > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement + # utilisé: acceptable de tout invalider): sco_cache.invalidate_formsemestre() # news - F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0] + F = sco_formations.formation_list(args={"formation_id": ue.formation_id})[0] sco_news.add( typ=sco_news.NEWS_FORM, object=ue["formation_id"], @@ -198,10 +222,10 @@ def do_ue_delete(ue_id, delete_validations=False, force=False): "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue["formation_id"], + semestre_idx=ue.semestre_idx, ) ) - else: - return None + return None def ue_create(formation_id=None): @@ -211,8 +235,6 @@ def ue_create(formation_id=None): def ue_edit(ue_id=None, create=False, formation_id=None): """Modification ou création d'une UE""" - from app.scodoc import sco_formations - create = int(create) if not create: U = ue_list(args={"ue_id": ue_id}) @@ -444,24 +466,38 @@ def next_ue_numero(formation_id, semestre_id=None): def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False): """Delete an UE""" - ues = ue_list(args={"ue_id": ue_id}) - if not ues: - raise ScoValueError("UE inexistante !") - ue = ues[0] - - if not dialog_confirmed: - return scu.confirm_dialog( - "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. +
+ """ + super().__init__(msg=msg, dest_url=dest_url) class ScoGenError(ScoException): "exception avec affichage d'une page explicative ad-hoc" def __init__(self, msg=""): - ScoException.__init__(self, msg) + super().__init__(msg) class AccessDenied(ScoGenError): @@ -101,7 +116,7 @@ class APIInvalidParams(Exception): status_code = 400 def __init__(self, message, status_code=None, payload=None): - Exception.__init__(self) + super().__init__() self.message = message if status_code is not None: self.status_code = status_code diff --git a/app/templates/pn/form_ues.html b/app/templates/pn/form_ues.html index 69d698391..12e039733 100644 --- a/app/templates/pn/form_ues.html +++ b/app/templates/pn/form_ues.html @@ -23,14 +23,11 @@ {{icons.arrow_none|safe}} {% endif %} - {% if editable and not ue.modules.count() %} + {{icons.delete|safe}} - {% else %} - {{icons.delete_disabled|safe}} - {% endif %} - + }}">{% if editable and not ue.modules.count() %}{{icons.delete|safe}}{% else %}{{icons.delete_disabled|safe}}{% endif %} + {{ue.acronyme}} {{ue.titre}} diff --git a/app/templates/sco_value_error.html b/app/templates/sco_value_error.html index 9b7f3b547..3c4648425 100644 --- a/app/templates/sco_value_error.html +++ b/app/templates/sco_value_error.html @@ -7,10 +7,9 @@ {{ exc | safe }} -{% if g.scodoc_dept %} - retour page d'accueil - departement {{ g.scodoc_dept }} + continuer {% else %} retour page d'accueil {% endif %}