2020-09-26 16:19:37 +02:00
|
|
|
# -*- mode: python -*-
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# Gestion scolarite IUT
|
|
|
|
#
|
2021-01-01 17:51:08 +01:00
|
|
|
# Copyright (c) 1999 - 2021 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
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2021-06-19 23:21:37 +02:00
|
|
|
"""Ajout/Modification/Suppression modules
|
2020-09-26 16:19:37 +02:00
|
|
|
(portage from DTML)
|
|
|
|
"""
|
2021-07-31 18:01:10 +02:00
|
|
|
import flask
|
2021-09-18 10:10:02 +02:00
|
|
|
from flask import url_for, g, request
|
2021-09-18 13:42:19 +02:00
|
|
|
from flask_login import current_user
|
2021-07-31 18:01:10 +02:00
|
|
|
|
2021-06-19 23:21:37 +02:00
|
|
|
import app.scodoc.notesdb as ndb
|
|
|
|
import app.scodoc.sco_utils as scu
|
2021-08-29 19:57:32 +02:00
|
|
|
from app import log
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
|
|
|
from app.scodoc.sco_permissions import Permission
|
|
|
|
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError
|
|
|
|
from app.scodoc import html_sco_header
|
|
|
|
from app.scodoc import sco_codes_parcours
|
|
|
|
from app.scodoc import sco_edit_matiere
|
|
|
|
from app.scodoc import sco_moduleimpl
|
|
|
|
from app.scodoc import sco_news
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
_MODULE_HELP = """<p class="help">
|
|
|
|
Les modules sont décrits dans le programme pédagogique. Un module est pour ce
|
|
|
|
logiciel l'unité pédagogique élémentaire. On va lui associer une note
|
|
|
|
à travers des <em>évaluations</em>. <br/>
|
|
|
|
Cette note (moyenne de module) sera utilisée pour calculer la moyenne
|
|
|
|
générale (et la moyenne de l'UE à laquelle appartient le module). Pour
|
|
|
|
cela, on utilisera le <em>coefficient</em> associé au module.
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p class="help">Un module possède un enseignant responsable
|
|
|
|
(typiquement celui qui dispense le cours magistral). On peut associer
|
|
|
|
au module une liste d'enseignants (typiquement les chargés de TD).
|
|
|
|
Tous ces enseignants, plus le responsable du semestre, pourront
|
|
|
|
saisir et modifier les notes de ce module.
|
|
|
|
</p> """
|
|
|
|
|
2021-06-16 18:18:32 +02:00
|
|
|
_moduleEditor = ndb.EditableTable(
|
|
|
|
"notes_modules",
|
|
|
|
"module_id",
|
|
|
|
(
|
|
|
|
"module_id",
|
|
|
|
"titre",
|
|
|
|
"code",
|
|
|
|
"abbrev",
|
|
|
|
"heures_cours",
|
|
|
|
"heures_td",
|
|
|
|
"heures_tp",
|
|
|
|
"coefficient",
|
|
|
|
"ue_id",
|
|
|
|
"matiere_id",
|
|
|
|
"formation_id",
|
|
|
|
"semestre_id",
|
|
|
|
"numero",
|
|
|
|
"code_apogee",
|
|
|
|
"module_type"
|
|
|
|
#'ects'
|
|
|
|
),
|
|
|
|
sortkey="numero, code, titre",
|
|
|
|
output_formators={
|
|
|
|
"heures_cours": ndb.float_null_is_zero,
|
|
|
|
"heures_td": ndb.float_null_is_zero,
|
|
|
|
"heures_tp": ndb.float_null_is_zero,
|
|
|
|
"numero": ndb.int_null_is_zero,
|
|
|
|
"coefficient": ndb.float_null_is_zero,
|
|
|
|
"module_type": ndb.int_null_is_zero
|
|
|
|
#'ects' : ndb.float_null_is_null
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-10-16 19:20:36 +02:00
|
|
|
def module_list(*args, **kw):
|
2021-06-16 18:18:32 +02:00
|
|
|
"list modules"
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
return _moduleEditor.list(cnx, *args, **kw)
|
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def do_module_create(args) -> int:
|
2021-06-16 18:18:32 +02:00
|
|
|
"create a module"
|
|
|
|
# create
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_formations
|
|
|
|
|
2021-06-16 18:18:32 +02:00
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
r = _moduleEditor.create(cnx, args)
|
|
|
|
|
|
|
|
# news
|
2021-08-19 10:28:35 +02:00
|
|
|
F = sco_formations.formation_list(args={"formation_id": args["formation_id"]})[0]
|
2021-06-16 18:18:32 +02:00
|
|
|
sco_news.add(
|
2021-06-19 23:21:37 +02:00
|
|
|
typ=sco_news.NEWS_FORM,
|
2021-06-16 18:18:32 +02:00
|
|
|
object=args["formation_id"],
|
|
|
|
text="Modification de la formation %(acronyme)s" % F,
|
2021-08-01 10:16:16 +02:00
|
|
|
max_frequency=3,
|
2021-06-16 18:18:32 +02:00
|
|
|
)
|
|
|
|
return r
|
|
|
|
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
def module_create(matiere_id=None):
|
2021-01-01 18:40:47 +01:00
|
|
|
"""Creation d'un module"""
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_formations
|
|
|
|
from app.scodoc import sco_edit_ue
|
|
|
|
|
2021-08-09 10:09:04 +02:00
|
|
|
if matiere_id is None:
|
2020-09-26 16:19:37 +02:00
|
|
|
raise ScoValueError("invalid matiere !")
|
2021-10-17 23:19:26 +02:00
|
|
|
M = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_id})[0]
|
|
|
|
UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
|
2021-08-19 10:28:35 +02:00
|
|
|
Fo = sco_formations.formation_list(args={"formation_id": UE["formation_id"]})[0]
|
2020-09-26 16:19:37 +02:00
|
|
|
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
2021-07-09 17:47:06 +02:00
|
|
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
2020-09-26 16:19:37 +02:00
|
|
|
H = [
|
2021-07-29 16:31:15 +02:00
|
|
|
html_sco_header.sco_header(page_title="Création d'un module"),
|
2020-09-26 16:19:37 +02:00
|
|
|
"""<h2>Création d'un module dans la matière %(titre)s""" % M,
|
|
|
|
""" (UE %(acronyme)s)</h2>""" % UE,
|
|
|
|
_MODULE_HELP,
|
|
|
|
]
|
|
|
|
# cherche le numero adequat (pour placer le module en fin de liste)
|
2021-10-16 19:20:36 +02:00
|
|
|
Mods = module_list(args={"matiere_id": matiere_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
if Mods:
|
|
|
|
default_num = max([m["numero"] for m in Mods]) + 10
|
|
|
|
else:
|
|
|
|
default_num = 10
|
|
|
|
tf = TrivialFormulator(
|
2021-09-18 10:10:02 +02:00
|
|
|
request.base_url,
|
2021-09-27 16:42:14 +02:00
|
|
|
scu.get_request_args(),
|
2020-09-26 16:19:37 +02:00
|
|
|
(
|
|
|
|
(
|
|
|
|
"code",
|
|
|
|
{
|
|
|
|
"size": 10,
|
|
|
|
"explanation": "code du module (doit être unique dans la formation)",
|
|
|
|
"allow_null": False,
|
|
|
|
"validator": lambda val, field, formation_id=Fo[
|
|
|
|
"formation_id"
|
2021-08-20 01:09:55 +02:00
|
|
|
]: check_module_code_unicity(val, field, formation_id),
|
2020-09-26 16:19:37 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
("titre", {"size": 30, "explanation": "nom du module"}),
|
|
|
|
("abbrev", {"size": 20, "explanation": "nom abrégé (pour bulletins)"}),
|
|
|
|
(
|
|
|
|
"module_type",
|
|
|
|
{
|
|
|
|
"input_type": "menu",
|
|
|
|
"title": "Type",
|
|
|
|
"explanation": "",
|
|
|
|
"labels": ("Standard", "Malus"),
|
2021-02-04 20:02:44 +01:00
|
|
|
"allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)),
|
2020-09-26 16:19:37 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_cours",
|
|
|
|
{"size": 4, "type": "float", "explanation": "nombre d'heures de cours"},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_td",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "nombre d'heures de Travaux Dirigés",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_tp",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "nombre d'heures de Travaux Pratiques",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"coefficient",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "coefficient dans la formation (PPN)",
|
|
|
|
"allow_null": False,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS (inutilisés: les crédits sont associés aux UE)' }),
|
|
|
|
("formation_id", {"default": UE["formation_id"], "input_type": "hidden"}),
|
|
|
|
("ue_id", {"default": M["ue_id"], "input_type": "hidden"}),
|
|
|
|
("matiere_id", {"default": M["matiere_id"], "input_type": "hidden"}),
|
|
|
|
(
|
|
|
|
"semestre_id",
|
|
|
|
{
|
|
|
|
"input_type": "menu",
|
|
|
|
"type": "int",
|
2021-08-21 00:24:51 +02:00
|
|
|
"title": parcours.SESSION_NAME.capitalize(),
|
2020-09-26 16:19:37 +02:00
|
|
|
"explanation": "%s de début du module dans la formation standard"
|
|
|
|
% parcours.SESSION_NAME,
|
|
|
|
"labels": [str(x) for x in semestres_indices],
|
|
|
|
"allowed_values": semestres_indices,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"code_apogee",
|
|
|
|
{
|
|
|
|
"title": "Code Apogée",
|
2021-06-24 23:09:06 +02:00
|
|
|
"size": 25,
|
|
|
|
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
2020-09-26 16:19:37 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"numero",
|
|
|
|
{
|
|
|
|
"size": 2,
|
|
|
|
"explanation": "numéro (1,2,3,4...) pour ordre d'affichage",
|
|
|
|
"type": "int",
|
|
|
|
"default": default_num,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
submitlabel="Créer ce module",
|
|
|
|
)
|
|
|
|
if tf[0] == 0:
|
2021-07-29 10:19:00 +02:00
|
|
|
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
|
2020-09-26 16:19:37 +02:00
|
|
|
else:
|
2021-08-20 01:09:55 +02:00
|
|
|
do_module_create(tf[2])
|
2021-07-31 18:01:10 +02:00
|
|
|
return flask.redirect(
|
|
|
|
url_for(
|
2021-10-17 23:19:26 +02:00
|
|
|
"notes.ue_table",
|
2021-07-31 18:01:10 +02:00
|
|
|
scodoc_dept=g.scodoc_dept,
|
|
|
|
formation_id=UE["formation_id"],
|
|
|
|
)
|
2020-09-26 16:19:37 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def do_module_delete(oid):
|
2021-06-16 18:18:32 +02:00
|
|
|
"delete module"
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_formations
|
|
|
|
|
2021-10-16 19:20:36 +02:00
|
|
|
mod = module_list({"module_id": oid})[0]
|
2021-08-20 01:09:55 +02:00
|
|
|
if module_is_locked(mod["module_id"]):
|
2021-06-16 18:18:32 +02:00
|
|
|
raise ScoLockedFormError()
|
|
|
|
|
|
|
|
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
2021-10-15 14:00:51 +02:00
|
|
|
mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
|
2021-06-16 18:18:32 +02:00
|
|
|
if mods:
|
2021-09-21 14:01:46 +02:00
|
|
|
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
|
|
|
<p class="help">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.
|
|
|
|
</p>
|
2021-10-17 23:19:26 +02:00
|
|
|
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
2021-09-21 14:01:46 +02:00
|
|
|
formation_id=mod["formation_id"])}">reprendre</a>
|
|
|
|
"""
|
2021-06-16 18:18:32 +02:00
|
|
|
raise ScoGenError(err_page)
|
|
|
|
# delete
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
_moduleEditor.delete(cnx, oid)
|
|
|
|
|
|
|
|
# news
|
2021-08-19 10:28:35 +02:00
|
|
|
F = sco_formations.formation_list(args={"formation_id": mod["formation_id"]})[0]
|
2021-06-16 18:18:32 +02:00
|
|
|
sco_news.add(
|
2021-06-19 23:21:37 +02:00
|
|
|
typ=sco_news.NEWS_FORM,
|
2021-06-16 18:18:32 +02:00
|
|
|
object=mod["formation_id"],
|
|
|
|
text="Modification de la formation %(acronyme)s" % F,
|
2021-08-01 10:16:16 +02:00
|
|
|
max_frequency=3,
|
2021-06-16 18:18:32 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
def module_delete(module_id=None):
|
2020-09-26 16:19:37 +02:00
|
|
|
"""Delete a module"""
|
|
|
|
if not module_id:
|
|
|
|
raise ScoValueError("invalid module !")
|
2021-10-16 19:20:36 +02:00
|
|
|
Mods = module_list(args={"module_id": module_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
if not Mods:
|
|
|
|
raise ScoValueError("Module inexistant !")
|
|
|
|
Mod = Mods[0]
|
|
|
|
H = [
|
2021-07-29 16:31:15 +02:00
|
|
|
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
2020-09-26 16:19:37 +02:00
|
|
|
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % Mod,
|
|
|
|
]
|
|
|
|
|
2021-08-09 10:09:04 +02:00
|
|
|
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
2020-09-26 16:19:37 +02:00
|
|
|
tf = TrivialFormulator(
|
2021-09-18 10:10:02 +02:00
|
|
|
request.base_url,
|
2021-09-27 16:42:14 +02:00
|
|
|
scu.get_request_args(),
|
2020-09-26 16:19:37 +02:00
|
|
|
(("module_id", {"input_type": "hidden"}),),
|
|
|
|
initvalues=Mod,
|
|
|
|
submitlabel="Confirmer la suppression",
|
|
|
|
cancelbutton="Annuler",
|
|
|
|
)
|
|
|
|
if tf[0] == 0:
|
2021-07-29 10:19:00 +02:00
|
|
|
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
|
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:
|
2021-08-20 01:09:55 +02:00
|
|
|
do_module_delete(module_id)
|
2021-07-31 18:01:10 +02:00
|
|
|
return flask.redirect(dest_url)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def do_module_edit(val):
|
2021-06-16 18:18:32 +02:00
|
|
|
"edit a module"
|
2021-07-19 19:53:01 +02:00
|
|
|
from app.scodoc import sco_edit_formation
|
|
|
|
|
2021-06-16 18:18:32 +02:00
|
|
|
# check
|
2021-10-16 19:20:36 +02:00
|
|
|
mod = module_list({"module_id": val["module_id"]})[0]
|
2021-08-20 01:09:55 +02:00
|
|
|
if module_is_locked(mod["module_id"]):
|
2021-06-16 18:18:32 +02:00
|
|
|
# formation verrouillée: empeche de modifier certains champs:
|
|
|
|
protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id")
|
|
|
|
for f in protected_fields:
|
|
|
|
if f in val:
|
|
|
|
del val[f]
|
|
|
|
# edit
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
_moduleEditor.edit(cnx, val)
|
2021-07-19 19:53:01 +02:00
|
|
|
sco_edit_formation.invalidate_sems_in_formation(mod["formation_id"])
|
2021-06-16 18:18:32 +02:00
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def check_module_code_unicity(code, field, formation_id, module_id=None):
|
2020-09-26 16:19:37 +02:00
|
|
|
"true si code module unique dans la formation"
|
2021-10-16 19:20:36 +02:00
|
|
|
Mods = module_list(args={"code": code, "formation_id": formation_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
if module_id: # edition: supprime le module en cours
|
|
|
|
Mods = [m for m in Mods if m["module_id"] != module_id]
|
|
|
|
|
|
|
|
return len(Mods) == 0
|
|
|
|
|
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
def module_edit(module_id=None):
|
2020-09-26 16:19:37 +02:00
|
|
|
"""Edit a module"""
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_formations
|
|
|
|
from app.scodoc import sco_tag_module
|
|
|
|
|
2020-09-26 16:19:37 +02:00
|
|
|
if not module_id:
|
|
|
|
raise ScoValueError("invalid module !")
|
2021-10-16 19:20:36 +02:00
|
|
|
Mod = module_list(args={"module_id": module_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
if not Mod:
|
|
|
|
raise ScoValueError("invalid module !")
|
|
|
|
Mod = Mod[0]
|
2021-08-20 01:09:55 +02:00
|
|
|
unlocked = not module_is_locked(module_id)
|
2021-08-19 10:28:35 +02:00
|
|
|
Fo = sco_formations.formation_list(args={"formation_id": Mod["formation_id"]})[0]
|
2020-09-26 16:19:37 +02:00
|
|
|
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
2021-02-03 22:00:41 +01:00
|
|
|
M = ndb.SimpleDictFetch(
|
2021-08-08 17:38:46 +02:00
|
|
|
"""SELECT ue.acronyme, mat.*, mat.id AS matiere_id
|
|
|
|
FROM notes_matieres mat, notes_ue ue
|
|
|
|
WHERE mat.ue_id = ue.id
|
2021-08-21 00:49:36 +02:00
|
|
|
AND ue.formation_id = %(formation_id)s
|
2021-08-08 17:38:46 +02:00
|
|
|
ORDER BY ue.numero, mat.numero
|
|
|
|
""",
|
2020-09-26 16:19:37 +02:00
|
|
|
{"formation_id": Mod["formation_id"]},
|
|
|
|
)
|
|
|
|
Mnames = ["%s / %s" % (x["acronyme"], x["titre"]) for x in M]
|
|
|
|
Mids = ["%s!%s" % (x["ue_id"], x["matiere_id"]) for x in M]
|
|
|
|
Mod["ue_matiere_id"] = "%s!%s" % (Mod["ue_id"], Mod["matiere_id"])
|
|
|
|
|
2021-07-09 17:47:06 +02:00
|
|
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-08-09 10:09:04 +02:00
|
|
|
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
H = [
|
2021-06-13 18:29:53 +02:00
|
|
|
html_sco_header.sco_header(
|
2020-09-26 16:19:37 +02:00
|
|
|
page_title="Modification du module %(titre)s" % Mod,
|
|
|
|
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
|
|
|
|
javascripts=[
|
|
|
|
"libjs/jQuery-tagEditor/jquery.tag-editor.min.js",
|
|
|
|
"libjs/jQuery-tagEditor/jquery.caret.min.js",
|
|
|
|
"js/module_tag_editor.js",
|
|
|
|
],
|
|
|
|
),
|
|
|
|
"""<h2>Modification du module %(titre)s""" % Mod,
|
|
|
|
""" (formation %(acronyme)s, version %(version)s)</h2>""" % Fo,
|
|
|
|
_MODULE_HELP,
|
|
|
|
]
|
|
|
|
if not unlocked:
|
|
|
|
H.append(
|
|
|
|
"""<div class="ue_warning"><span>Formation verrouillée, seuls certains éléments peuvent être modifiés</span></div>"""
|
|
|
|
)
|
|
|
|
|
|
|
|
tf = TrivialFormulator(
|
2021-09-18 10:10:02 +02:00
|
|
|
request.base_url,
|
2021-09-27 16:42:14 +02:00
|
|
|
scu.get_request_args(),
|
2020-09-26 16:19:37 +02:00
|
|
|
(
|
|
|
|
(
|
|
|
|
"code",
|
|
|
|
{
|
|
|
|
"size": 10,
|
|
|
|
"explanation": "code du module (doit être unique dans la formation)",
|
|
|
|
"allow_null": False,
|
|
|
|
"validator": lambda val, field, formation_id=Mod[
|
|
|
|
"formation_id"
|
|
|
|
]: check_module_code_unicity(
|
2021-08-20 01:09:55 +02:00
|
|
|
val, field, formation_id, module_id=module_id
|
2020-09-26 16:19:37 +02:00
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
("titre", {"size": 30, "explanation": "nom du module"}),
|
|
|
|
("abbrev", {"size": 20, "explanation": "nom abrégé (pour bulletins)"}),
|
|
|
|
(
|
|
|
|
"module_type",
|
|
|
|
{
|
|
|
|
"input_type": "menu",
|
|
|
|
"title": "Type",
|
|
|
|
"explanation": "",
|
|
|
|
"labels": ("Standard", "Malus"),
|
2021-02-04 20:02:44 +01:00
|
|
|
"allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)),
|
2020-09-26 16:19:37 +02:00
|
|
|
"enabled": unlocked,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_cours",
|
|
|
|
{"size": 4, "type": "float", "explanation": "nombre d'heures de cours"},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_td",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "nombre d'heures de Travaux Dirigés",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"heures_tp",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "nombre d'heures de Travaux Pratiques",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"coefficient",
|
|
|
|
{
|
|
|
|
"size": 4,
|
|
|
|
"type": "float",
|
|
|
|
"explanation": "coefficient dans la formation (PPN)",
|
|
|
|
"allow_null": False,
|
|
|
|
"enabled": unlocked,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS', 'enabled' : unlocked }),
|
|
|
|
("formation_id", {"input_type": "hidden"}),
|
|
|
|
("ue_id", {"input_type": "hidden"}),
|
|
|
|
("module_id", {"input_type": "hidden"}),
|
|
|
|
(
|
|
|
|
"ue_matiere_id",
|
|
|
|
{
|
|
|
|
"input_type": "menu",
|
|
|
|
"title": "Matière",
|
|
|
|
"explanation": "un module appartient à une seule matière.",
|
|
|
|
"labels": Mnames,
|
|
|
|
"allowed_values": Mids,
|
|
|
|
"enabled": unlocked,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"semestre_id",
|
|
|
|
{
|
|
|
|
"input_type": "menu",
|
|
|
|
"type": "int",
|
|
|
|
"title": parcours.SESSION_NAME.capitalize(),
|
|
|
|
"explanation": "%s de début du module dans la formation standard"
|
|
|
|
% parcours.SESSION_NAME,
|
|
|
|
"labels": [str(x) for x in semestres_indices],
|
|
|
|
"allowed_values": semestres_indices,
|
|
|
|
"enabled": unlocked,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"code_apogee",
|
|
|
|
{
|
|
|
|
"title": "Code Apogée",
|
2021-06-24 23:09:06 +02:00
|
|
|
"size": 25,
|
|
|
|
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
2020-09-26 16:19:37 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"numero",
|
|
|
|
{
|
|
|
|
"size": 2,
|
|
|
|
"explanation": "numéro (1,2,3,4...) pour ordre d'affichage",
|
|
|
|
"type": "int",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
html_foot_markup="""<div style="width: 90%;"><span class="sco_tag_edit"><textarea data-module_id="{}" class="module_tag_editor">{}</textarea></span></div>""".format(
|
2021-08-20 01:09:55 +02:00
|
|
|
module_id, ",".join(sco_tag_module.module_tag_list(module_id))
|
2020-09-26 16:19:37 +02:00
|
|
|
),
|
|
|
|
initvalues=Mod,
|
|
|
|
submitlabel="Modifier ce module",
|
|
|
|
)
|
|
|
|
|
|
|
|
if tf[0] == 0:
|
2021-07-29 10:19:00 +02:00
|
|
|
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
|
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:
|
|
|
|
# l'UE peut changer
|
|
|
|
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
|
|
|
# Check unicité code module dans la formation
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
do_module_edit(tf[2])
|
2021-07-31 18:01:10 +02:00
|
|
|
return flask.redirect(dest_url)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
# Edition en ligne du code Apogee
|
2021-09-27 10:20:10 +02:00
|
|
|
def edit_module_set_code_apogee(id=None, value=None):
|
2021-06-24 23:09:06 +02:00
|
|
|
"Set UE code apogee"
|
2020-09-26 16:19:37 +02:00
|
|
|
module_id = id
|
|
|
|
value = value.strip("-_ \t")
|
|
|
|
log("edit_module_set_code_apogee: module_id=%s code_apogee=%s" % (module_id, value))
|
|
|
|
|
2021-10-16 19:20:36 +02:00
|
|
|
modules = module_list(args={"module_id": module_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
if not modules:
|
2021-06-24 23:09:06 +02:00
|
|
|
return "module invalide" # should not occur
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
do_module_edit({"module_id": module_id, "code_apogee": value})
|
2020-09-26 16:19:37 +02:00
|
|
|
if not value:
|
2021-02-04 20:02:44 +01:00
|
|
|
value = scu.APO_MISSING_CODE_STR
|
2020-09-26 16:19:37 +02:00
|
|
|
return value
|
|
|
|
|
|
|
|
|
2021-10-16 19:20:36 +02:00
|
|
|
def module_table(formation_id):
|
2020-09-26 16:19:37 +02:00
|
|
|
"""Liste des modules de la formation
|
|
|
|
(XXX inutile ou a revoir)
|
|
|
|
"""
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_formations
|
|
|
|
|
2020-09-26 16:19:37 +02:00
|
|
|
if not formation_id:
|
|
|
|
raise ScoValueError("invalid formation !")
|
2021-08-19 10:28:35 +02:00
|
|
|
F = sco_formations.formation_list(args={"formation_id": formation_id})[0]
|
2020-09-26 16:19:37 +02:00
|
|
|
H = [
|
2021-07-29 16:31:15 +02:00
|
|
|
html_sco_header.sco_header(page_title="Liste des modules de %(titre)s" % F),
|
2020-09-26 16:19:37 +02:00
|
|
|
"""<h2>Listes des modules dans la formation %(titre)s (%(acronyme)s)</h2>"""
|
|
|
|
% F,
|
|
|
|
'<ul class="notes_module_list">',
|
|
|
|
]
|
2021-09-18 13:42:19 +02:00
|
|
|
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-10-16 19:20:36 +02:00
|
|
|
for Mod in module_list(args={"formation_id": formation_id}):
|
2020-09-26 16:19:37 +02:00
|
|
|
H.append('<li class="notes_module_list">%s' % Mod)
|
|
|
|
if editable:
|
|
|
|
H.append('<a href="module_edit?module_id=%(module_id)s">modifier</a>' % Mod)
|
|
|
|
H.append(
|
|
|
|
'<a href="module_delete?module_id=%(module_id)s">supprimer</a>' % Mod
|
|
|
|
)
|
|
|
|
H.append("</li>")
|
|
|
|
H.append("</ul>")
|
2021-07-29 10:19:00 +02:00
|
|
|
H.append(html_sco_header.sco_footer())
|
2020-09-26 16:19:37 +02:00
|
|
|
return "\n".join(H)
|
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def module_is_locked(module_id):
|
2021-06-13 18:29:53 +02:00
|
|
|
"""True if module should not be modified
|
|
|
|
(used in a locked formsemestre)
|
|
|
|
"""
|
|
|
|
r = ndb.SimpleDictFetch(
|
2021-08-08 17:38:46 +02:00
|
|
|
"""SELECT mi.id
|
|
|
|
FROM notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
|
|
|
|
WHERE mi.module_id = mod.id
|
|
|
|
AND mi.formsemestre_id = sem.id
|
|
|
|
AND mi.module_id = %(module_id)s
|
|
|
|
AND sem.etat = false
|
2021-06-13 18:29:53 +02:00
|
|
|
""",
|
|
|
|
{"module_id": module_id},
|
|
|
|
)
|
|
|
|
return len(r) > 0
|
|
|
|
|
|
|
|
|
2021-08-20 01:09:55 +02:00
|
|
|
def module_count_moduleimpls(module_id):
|
2021-06-13 18:29:53 +02:00
|
|
|
"Number of moduleimpls using this module"
|
2021-10-15 14:00:51 +02:00
|
|
|
mods = sco_moduleimpl.moduleimpl_list(module_id=module_id)
|
2021-06-13 18:29:53 +02:00
|
|
|
return len(mods)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
2021-01-01 18:40:47 +01:00
|
|
|
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_edit_ue
|
|
|
|
|
2021-10-21 06:32:03 +02:00
|
|
|
ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-10-21 06:32:03 +02:00
|
|
|
for ue in ues:
|
2020-09-26 16:19:37 +02:00
|
|
|
# Un seul module de malus par UE:
|
|
|
|
nb_mod_malus = len(
|
|
|
|
[
|
|
|
|
mod
|
2021-10-16 19:20:36 +02:00
|
|
|
for mod in module_list(args={"ue_id": ue["ue_id"]})
|
2021-02-04 20:02:44 +01:00
|
|
|
if mod["module_type"] == scu.MODULE_MALUS
|
2020-09-26 16:19:37 +02:00
|
|
|
]
|
|
|
|
)
|
|
|
|
if nb_mod_malus == 0:
|
2021-09-27 10:20:10 +02:00
|
|
|
ue_add_malus_module(ue["ue_id"], titre=titre)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
if redirect:
|
2021-10-21 06:32:03 +02:00
|
|
|
return flask.redirect(
|
|
|
|
url_for(
|
|
|
|
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
|
|
|
|
)
|
|
|
|
)
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
|
2021-09-27 10:20:10 +02:00
|
|
|
def ue_add_malus_module(ue_id, titre=None, code=None):
|
2021-01-01 18:40:47 +01:00
|
|
|
"""Add a malus module in this ue"""
|
2021-06-19 23:21:37 +02:00
|
|
|
from app.scodoc import sco_edit_ue
|
|
|
|
|
2021-10-17 23:19:26 +02:00
|
|
|
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
|
2020-09-26 16:19:37 +02:00
|
|
|
|
|
|
|
if titre is None:
|
|
|
|
titre = ""
|
|
|
|
if code is None:
|
|
|
|
code = "MALUS%d" % ue["numero"]
|
|
|
|
|
|
|
|
# Tout module doit avoir un semestre_id (indice 1, 2, ...)
|
2021-08-20 01:09:55 +02:00
|
|
|
semestre_ids = sco_edit_ue.ue_list_semestre_ids(ue)
|
2020-09-26 16:19:37 +02:00
|
|
|
if semestre_ids:
|
|
|
|
semestre_id = semestre_ids[0]
|
|
|
|
else:
|
|
|
|
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
|
|
|
# le semestre ? ou affecter le malus au semestre 1 ???
|
|
|
|
raise ScoValueError(
|
|
|
|
"Impossible d'ajouter un malus s'il n'y a pas d'autres modules"
|
|
|
|
)
|
|
|
|
|
|
|
|
# Matiere pour placer le module malus
|
2021-10-17 23:19:26 +02:00
|
|
|
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue_id})
|
2020-09-26 16:19:37 +02:00
|
|
|
numero = max([mat["numero"] for mat in Matlist]) + 10
|
2021-06-16 18:18:32 +02:00
|
|
|
matiere_id = sco_edit_matiere.do_matiere_create(
|
2021-08-20 01:09:55 +02:00
|
|
|
{"ue_id": ue_id, "titre": "Malus", "numero": numero}
|
2020-09-26 16:19:37 +02:00
|
|
|
)
|
|
|
|
|
2021-06-16 18:18:32 +02:00
|
|
|
module_id = do_module_create(
|
2020-09-26 16:19:37 +02:00
|
|
|
{
|
|
|
|
"titre": titre,
|
|
|
|
"code": code,
|
|
|
|
"coefficient": 0.0, # unused
|
|
|
|
"ue_id": ue_id,
|
|
|
|
"matiere_id": matiere_id,
|
|
|
|
"formation_id": ue["formation_id"],
|
|
|
|
"semestre_id": semestre_id,
|
2021-02-04 20:02:44 +01:00
|
|
|
"module_type": scu.MODULE_MALUS,
|
2020-09-26 16:19:37 +02:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
return module_id
|