# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """Ajout/Modification/Supression matieres (portage from DTML) """ import flask from flask import g, url_for, request from app import db, log from app.models import Formation, Matiere, UniteEns, 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, ScoNonEmptyFormationObject, ) from app.scodoc import html_sco_header _matiereEditor = ndb.EditableTable( "notes_matieres", "matiere_id", ("matiere_id", "ue_id", "numero", "titre"), sortkey="numero", output_formators={"numero": ndb.int_null_is_zero}, ) def matiere_list(*args, **kw): "list matieres" cnx = ndb.GetDBConnexion() return _matiereEditor.list(cnx, *args, **kw) def do_matiere_edit(*args, **kw): "edit a matiere" from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_formation cnx = ndb.GetDBConnexion() # check mat = matiere_list({"matiere_id": args[0]["matiere_id"]})[0] if matiere_is_locked(mat["matiere_id"]): raise ScoLockedFormError() # edit _matiereEditor.edit(cnx, *args, **kw) formation_id = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"] db.session.get(Formation, formation_id).invalidate_cached_sems() def do_matiere_create(args): "create a matiere" from app.scodoc import sco_edit_ue from app.models import ScolarNews cnx = ndb.GetDBConnexion() # check ue = sco_edit_ue.ue_list({"ue_id": args["ue_id"]})[0] # create matiere r = _matiereEditor.create(cnx, args) # news formation = db.session.get(Formation, ue["formation_id"]) ScolarNews.add( typ=ScolarNews.NEWS_FORM, obj=ue["formation_id"], text=f"Modification de la formation {formation.acronyme}", ) formation.invalidate_cached_sems() return r def matiere_create(ue_id=None): """Creation d'une matiere""" ue: UniteEns = UniteEns.query.get_or_404(ue_id) default_numero = max([mat.numero for mat in ue.matieres] or [9]) + 1 H = [ html_sco_header.sco_header(page_title="Création d'une matière"), f"""<h2>Création d'une matière dans l'UE {ue.titre or ''} ({ue.acronyme})</h2> <p class="help">Les matières sont des groupes de modules dans une UE d'une formation donnée. Les matières servent surtout pour la présentation (bulletins, etc) mais <em>n'ont pas de rôle dans le calcul des notes.</em> </p> <p class="help">Si votre formation n'utilise pas la notion de "matières", créez une matière par UE, et donnez lui le même nom que l'UE (en effet, tout module doit appartenir à une matière). </p> <p class="help">Comme les UE, les matières n'ont pas de coefficient associé. </p>""", ] tf = TrivialFormulator( request.base_url, scu.get_request_args(), ( ("ue_id", {"input_type": "hidden", "default": ue_id}), ( "titre", { "size": 30, "explanation": "nom de la matière.", }, ), ( "numero", { "size": 2, "explanation": "numéro (1,2,3,4...) pour affichage", "type": "int", "default": default_numero, "allow_null": False, }, ), ), submitlabel="Créer cette matière", ) dest_url = url_for( "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue.formation_id ) if tf[0] == 0: return "\n".join(H) + tf[1] + html_sco_header.sco_footer() elif tf[0] == -1: return flask.redirect(dest_url) else: # check unicity mats = matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]}) if mats: return ( "\n".join(H) + tf_error_message("Titre de matière déjà existant dans cette UE") + tf[1] + html_sco_header.sco_footer() ) _ = do_matiere_create(tf[2]) 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_edit_ue from app.scodoc import sco_edit_module cnx = ndb.GetDBConnexion() # check 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] 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": matiere.id}) for mod in mods: sco_edit_module.do_module_delete(mod["module_id"]) _matiereEditor.delete(cnx, oid) # news formation = db.session.get(Formation, ue["formation_id"]) ScolarNews.add( typ=ScolarNews.NEWS_FORM, obj=ue["formation_id"], text=f"Modification de la formation {formation.acronyme}", ) formation.invalidate_cached_sems() def matiere_delete(matiere_id=None): """Delete matière""" from app.scodoc import sco_edit_ue 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"), "<h2>Suppression de la matière %(titre)s" % mat, " dans l'UE (%(acronyme)s))</h2>" % UE, ] dest_url = url_for( "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=str(UE["formation_id"]), ) tf = TrivialFormulator( request.base_url, scu.get_request_args(), (("matiere_id", {"input_type": "hidden"}),), initvalues=mat, submitlabel="Confirmer la suppression", cancelbutton="Annuler", ) if tf[0] == 0: return "\n".join(H) + tf[1] + html_sco_header.sco_footer() elif tf[0] == -1: return flask.redirect(dest_url) else: do_matiere_delete(matiere_id) return flask.redirect(dest_url) def matiere_edit(matiere_id=None): """Edit matiere""" from app.scodoc import sco_edit_ue F = matiere_list(args={"matiere_id": matiere_id}) if not F: raise ScoValueError("Matière inexistante !") F = F[0] ues = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]}) if not ues: raise ScoValueError("UE inexistante !") ue = ues[0] formation: Formation = Formation.query.get_or_404(ue["formation_id"]) ues = sco_edit_ue.ue_list(args={"formation_id": ue["formation_id"]}) ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues] ue_ids = [u["ue_id"] for u in ues] H = [ html_sco_header.sco_header(page_title="Modification d'une matière"), """<h2>Modification de la matière %(titre)s""" % F, f"""(formation ({formation.acronyme}, version {formation.version})</h2>""", ] help = """<p class="help">Les matières sont des groupes de modules dans une UE d'une formation donnée. Les matières servent surtout pour la présentation (bulletins, etc) mais <em>n'ont pas de rôle dans le calcul des notes.</em> </p> <p class="help">Si votre formation n'utilise pas la notion de "matières", créez une matière par UE, et donnez lui le même nom que l'UE (en effet, tout module doit appartenir à une matière). </p> <p class="help">Comme les UE, les matières n'ont pas de coefficient associé. </p>""" tf = TrivialFormulator( request.base_url, scu.get_request_args(), ( ("matiere_id", {"input_type": "hidden"}), ( "ue_id", { "input_type": "menu", "allowed_values": ue_ids, "labels": ue_names, "title": "UE", }, ), ("titre", {"size": 30, "explanation": "nom de cette matière"}), ( "numero", { "size": 2, "explanation": "numéro (1,2,3,4...) pour affichage", "type": "int", }, ), ), initvalues=F, submitlabel="Modifier les valeurs", ) dest_url = url_for( "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=str(ue["formation_id"]), ) if tf[0] == 0: return "\n".join(H) + tf[1] + help + html_sco_header.sco_footer() elif tf[0] == -1: return flask.redirect(dest_url) else: # check unicity mats = matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]}) if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id): return ( "\n".join(H) + tf_error_message("Titre de matière déjà existant dans cette UE") + tf[1] + html_sco_header.sco_footer() ) # changement d'UE ? if tf[2]["ue_id"] != F["ue_id"]: log("attaching mat %s to new UE %s" % (matiere_id, tf[2]["ue_id"])) ndb.SimpleQuery( "UPDATE notes_modules SET ue_id = %(ue_id)s WHERE matiere_id=%(matiere_id)s", {"ue_id": tf[2]["ue_id"], "matiere_id": matiere_id}, ) do_matiere_edit(tf[2]) return flask.redirect(dest_url) def matiere_is_locked(matiere_id): """True if matiere should not be modified (contains modules used in a locked formsemestre) """ r = ndb.SimpleDictFetch( """SELECT ma.id FROM notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi WHERE ma.id = mod.matiere_id AND mi.module_id = mod.id AND mi.formsemestre_id = sem.id AND ma.id = %(matiere_id)s AND sem.etat = false """, {"matiere_id": matiere_id}, ) return len(r) > 0