ScoDoc/app/formations/edit_matiere.py

379 lines
12 KiB
Python
Raw Normal View History

2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2023-12-31 23:04:06 +01:00
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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)
"""
2021-08-01 10:16:16 +02:00
import flask
from flask import g, render_template, request, url_for
from app import db, log
from app.models import Formation, Matiere, UniteEns, ScolarNews
2021-08-01 10:16:16 +02:00
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
2022-01-04 20:03:38 +01:00
from app.scodoc.sco_exceptions import (
ScoValueError,
ScoLockedFormError,
ScoNonEmptyFormationObject,
)
2020-09-26 16:19:37 +02:00
2021-06-16 18:18:32 +02:00
_matiereEditor = ndb.EditableTable(
"notes_matieres",
"matiere_id",
("matiere_id", "ue_id", "numero", "titre"),
sortkey="numero",
output_formators={"numero": ndb.int_null_is_zero},
)
2021-10-17 23:19:26 +02:00
def matiere_list(*args, **kw):
2021-06-16 18:18:32 +02:00
"list matieres"
cnx = ndb.GetDBConnexion()
return _matiereEditor.list(cnx, *args, **kw)
2021-08-20 01:09:55 +02:00
def do_matiere_edit(*args, **kw):
2021-06-16 18:18:32 +02:00
"edit a matiere"
from app.formations import edit_ue
2021-07-19 19:53:01 +02:00
2021-06-16 18:18:32 +02:00
cnx = ndb.GetDBConnexion()
# check
2021-10-17 23:19:26 +02:00
mat = matiere_list({"matiere_id": args[0]["matiere_id"]})[0]
2021-08-20 01:09:55 +02:00
if matiere_is_locked(mat["matiere_id"]):
2021-06-16 18:18:32 +02:00
raise ScoLockedFormError()
# edit
_matiereEditor.edit(cnx, *args, **kw)
formation_id = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"]
db.session.get(Formation, formation_id).invalidate_cached_sems()
2021-06-16 18:18:32 +02:00
2021-08-20 01:09:55 +02:00
def do_matiere_create(args):
2021-06-16 18:18:32 +02:00
"create a matiere"
from app.formations import edit_ue
2021-06-16 18:18:32 +02:00
cnx = ndb.GetDBConnexion()
# check
ue = edit_ue.ue_list({"ue_id": args["ue_id"]})[0]
2021-06-16 18:18:32 +02:00
# create matiere
r = _matiereEditor.create(cnx, args)
# news
formation = db.session.get(Formation, ue["formation_id"])
2022-04-12 17:12:51 +02:00
ScolarNews.add(
typ=ScolarNews.NEWS_FORM,
obj=ue["formation_id"],
2022-03-15 10:57:52 +01:00
text=f"Modification de la formation {formation.acronyme}",
2021-06-16 18:18:32 +02:00
)
formation.invalidate_cached_sems()
2021-06-16 18:18:32 +02:00
return r
2020-09-26 16:19:37 +02:00
def matiere_create(ue_id=None):
2021-01-01 18:40:47 +01:00
"""Creation d'une matiere"""
2023-07-10 14:32:16 +02:00
ue: UniteEns = UniteEns.query.get_or_404(ue_id)
default_numero = max([mat.numero for mat in ue.matieres] or [9]) + 1
2020-09-26 16:19:37 +02:00
H = [
f"""<h2>Création d'une matière dans l'UE {ue.titre or ''} ({ue.acronyme})</h2>
2023-07-10 14:32:16 +02:00
<p class="help">Les matières sont des groupes de modules dans une UE
2020-09-26 16:19:37 +02:00
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>
2023-12-31 23:04:06 +01:00
</p>
2020-09-26 16:19:37 +02:00
<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(),
2020-09-26 16:19:37 +02:00
(
("ue_id", {"input_type": "hidden", "default": ue_id}),
2023-07-10 14:32:16 +02:00
(
"titre",
{
"size": 30,
"explanation": "nom de la matière.",
},
),
2020-09-26 16:19:37 +02:00
(
"numero",
{
"size": 2,
"explanation": "numéro (1,2,3,4...) pour affichage",
"type": "int",
2023-07-10 14:32:16 +02:00
"default": default_numero,
"allow_null": False,
2020-09-26 16:19:37 +02:00
},
),
),
submitlabel="Créer cette matière",
)
2021-08-09 10:09:04 +02:00
dest_url = url_for(
2023-07-10 14:32:16 +02:00
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue.formation_id
2021-08-09 10:09:04 +02:00
)
2020-09-26 16:19:37 +02:00
if tf[0] == 0:
return render_template(
"sco_page.j2", title="Création d'une matière", content="\n".join(H) + tf[1]
)
if tf[0] == -1:
2021-07-31 18:01:10 +02:00
return flask.redirect(dest_url)
# check unicity
mats = matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]})
if mats:
return render_template(
"sco_page.j2",
title="Création d'une matière",
content=(
2020-09-26 16:19:37 +02:00
"\n".join(H)
+ tf_error_message("Titre de matière déjà existant dans cette UE")
+ tf[1]
),
)
_ = do_matiere_create(tf[2])
return flask.redirect(dest_url)
2020-09-26 16:19:37 +02:00
2022-01-04 20:03:38 +01:00
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
2021-08-20 01:09:55 +02:00
def do_matiere_delete(oid):
2021-06-16 18:18:32 +02:00
"delete matiere and attached modules"
from app.formations import edit_module, edit_ue
2021-06-16 18:18:32 +02:00
cnx = ndb.GetDBConnexion()
# check
2022-01-04 20:03:38 +01:00
matiere = Matiere.query.get_or_404(oid)
mat = matiere_list({"matiere_id": oid})[0] # compat sco7
ue = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
2022-01-04 20:03:38 +01:00
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(f"do_matiere_delete: matiere_id={matiere.id}")
2021-06-16 18:18:32 +02:00
# delete all modules in this matiere
mods = edit_module.module_list({"matiere_id": matiere.id})
2021-06-16 18:18:32 +02:00
for mod in mods:
edit_module.do_module_delete(mod["module_id"])
2021-06-16 18:18:32 +02:00
_matiereEditor.delete(cnx, oid)
# news
formation = db.session.get(Formation, ue["formation_id"])
2022-04-12 17:12:51 +02:00
ScolarNews.add(
typ=ScolarNews.NEWS_FORM,
obj=ue["formation_id"],
2022-03-15 10:57:52 +01:00
text=f"Modification de la formation {formation.acronyme}",
2021-06-16 18:18:32 +02:00
)
formation.invalidate_cached_sems()
2021-06-16 18:18:32 +02:00
def matiere_delete(matiere_id=None):
"""Delete matière"""
from app.formations import edit_ue
2022-01-04 20:03:38 +01:00
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_dict = edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0]
2020-09-26 16:19:37 +02:00
H = [
2022-01-04 20:03:38 +01:00
"<h2>Suppression de la matière %(titre)s" % mat,
" dans l'UE (%(acronyme)s))</h2>" % ue_dict,
2020-09-26 16:19:37 +02:00
]
dest_url = url_for(
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
formation_id=str(ue_dict["formation_id"]),
)
2020-09-26 16:19:37 +02:00
tf = TrivialFormulator(
request.base_url,
scu.get_request_args(),
2020-09-26 16:19:37 +02:00
(("matiere_id", {"input_type": "hidden"}),),
2022-01-04 20:03:38 +01:00
initvalues=mat,
2020-09-26 16:19:37 +02:00
submitlabel="Confirmer la suppression",
cancelbutton="Annuler",
)
if tf[0] == 0:
return render_template(
"sco_page.j2",
title="Suppression d'une matière",
content="\n".join(H) + tf[1],
)
if tf[0] == -1:
2021-07-31 18:01:10 +02:00
return flask.redirect(dest_url)
2020-09-26 16:19:37 +02:00
do_matiere_delete(matiere_id)
return flask.redirect(dest_url)
2020-09-26 16:19:37 +02:00
def matiere_edit(matiere_id=None):
2020-09-26 16:19:37 +02:00
"""Edit matiere"""
from app.formations import edit_ue
2021-10-17 23:19:26 +02:00
F = matiere_list(args={"matiere_id": matiere_id})
2020-09-26 16:19:37 +02:00
if not F:
raise ScoValueError("Matière inexistante !")
F = F[0]
ues = edit_ue.ue_list(args={"ue_id": F["ue_id"]})
if not ues:
2020-09-26 16:19:37 +02:00
raise ScoValueError("UE inexistante !")
ue = ues[0]
formation: Formation = Formation.query.get_or_404(ue["formation_id"])
ues = edit_ue.ue_list(args={"formation_id": ue["formation_id"]})
2020-09-26 16:19:37 +02:00
ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
ue_ids = [u["ue_id"] for u in ues]
H = [
"""<h2>Modification de la matière %(titre)s""" % F,
f"""(formation ({formation.acronyme}, version {formation.version})</h2>""",
2020-09-26 16:19:37 +02:00
]
help_msg = """<p class="help">Les matières sont des groupes de modules dans une UE
2020-09-26 16:19:37 +02:00
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>
2023-12-31 23:04:06 +01:00
</p>
2020-09-26 16:19:37 +02:00
<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(),
2020-09-26 16:19:37 +02:00
(
("matiere_id", {"input_type": "hidden"}),
(
"ue_id",
{
"input_type": "menu",
"allowed_values": ue_ids,
"labels": ue_names,
"title": "UE",
},
2020-09-26 16:19:37 +02:00
),
("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"]),
)
2020-09-26 16:19:37 +02:00
if tf[0] == 0:
return render_template(
"sco_page.j2",
title="Modification d'une matière",
content="\n".join(H) + tf[1] + help_msg,
)
2020-09-26 16:19:37 +02:00
elif tf[0] == -1:
2021-07-31 18:01:10 +02:00
return flask.redirect(dest_url)
2020-09-26 16:19:37 +02:00
else:
# check unicity
2021-10-17 23:19:26 +02:00
mats = matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]})
2020-09-26 16:19:37 +02:00
if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id):
return render_template(
"sco_page.j2",
title="Modification d'une matière",
content=(
"\n".join(H)
+ tf_error_message("Titre de matière déjà existant dans cette UE")
+ tf[1]
),
2020-09-26 16:19:37 +02:00
)
# 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"]))
2021-02-03 22:00:41 +01:00
ndb.SimpleQuery(
2020-09-26 16:19:37 +02:00
"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},
)
2021-08-20 01:09:55 +02:00
do_matiere_edit(tf[2])
2020-09-26 16:19:37 +02:00
2021-07-31 18:01:10 +02:00
return flask.redirect(dest_url)
2021-06-13 19:12:20 +02:00
2021-08-20 01:09:55 +02:00
def matiere_is_locked(matiere_id):
2021-06-13 19:12:20 +02:00
"""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
2021-08-09 10:09:04 +02:00
AND ma.id = %(matiere_id)s
AND sem.etat = false
2021-06-13 19:12:20 +02:00
""",
{"matiere_id": matiere_id},
)
return len(r) > 0