# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2021 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/Suppression UE """ import notesdb as ndb import sco_utils as scu from notes_log import log from TrivialFormulator import TrivialFormulator, TF from gen_tables import GenTable import sco_groups import sco_formsemestre import sco_formsemestre_validation import sco_codes_parcours import sco_tag_module from sco_permissions import ScoChangeFormation, ScoEditFormationTags, ScoImplement from sco_exceptions import ScoValueError, ScoLockedFormError _ueEditor = ndb.EditableTable( "notes_ue", "ue_id", ( "ue_id", "formation_id", "acronyme", "numero", "titre", "type", "ue_code", "ects", "is_external", "code_apogee", "coefficient", ), sortkey="numero", input_formators={"type": ndb.int_null_is_zero}, output_formators={ "numero": ndb.int_null_is_zero, "ects": ndb.float_null_is_null, "coefficient": ndb.float_null_is_zero, }, ) def do_ue_list(context, *args, **kw): "list UEs" cnx = ndb.GetDBConnexion() return _ueEditor.list(cnx, *args, **kw) def do_ue_create(context, args, REQUEST): "create an ue" cnx = ndb.GetDBConnexion() # check duplicates ues = do_ue_list( context, {"formation_id": args["formation_id"], "acronyme": args["acronyme"]} ) if ues: raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) # create r = _ueEditor.create(cnx, args) # news F = sco_formations.formation_list( context, args={"formation_id": args["formation_id"]} )[0] sco_news.add( context, REQUEST, typ=NEWS_FORM, object=args["formation_id"], text="Modification de la formation %(acronyme)s" % F, ) return r def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=False): "delete UE and attached matieres (but not modules (it should ?))" cnx = ndb.GetDBConnexion() log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations)) # check ue = do_ue_list(context, {"ue_id": ue_id}) if not ue: raise ScoValueError("UE inexistante !") ue = ue[0] if sco_edit_ue.ue_is_locked(context, ue["ue_id"]): raise ScoLockedFormError() # Il y a-t-il des etudiants ayant validé cette UE ? # si oui, propose de supprimer les validations validations = sco_parcours_dut.scolar_formsemestre_validation_list( cnx, args={"ue_id": ue_id} ) if validations and not delete_validations and not force: return scu.confirm_dialog( context, "
%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"]), dest_url="", REQUEST=REQUEST, target_variable="delete_validations", cancel_url="ue_list?formation_id=%s" % ue["formation_id"], parameters={"ue_id": ue_id, "dialog_confirmed": 1}, ) if delete_validations: log("deleting all validations of UE %s" % ue_id) ndb.SimpleQuery( context, "DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}, ) # delete all matiere in this UE mats = sco_edit_matiere.do_matiere_list(context, {"ue_id": ue_id}) for mat in mats: sco_edit_matiere.do_matiere_delete(context, mat["matiere_id"], REQUEST) # delete uecoef and events ndb.SimpleQuery( context, "DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}, ) ndb.SimpleQuery( context, "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 ?): sco_core.inval_cache(context) # news F = sco_formations.formation_list( context, args={"formation_id": ue["formation_id"]} )[0] sco_news.add( context, REQUEST, typ=NEWS_FORM, object=ue["formation_id"], text="Modification de la formation %(acronyme)s" % F, ) # if not force: return REQUEST.RESPONSE.redirect( scu.NotesURL() + "/ue_list?formation_id=" + str(ue["formation_id"]) ) else: return None def ue_create(context, formation_id=None, REQUEST=None): """Creation d'une UE""" return ue_edit(context, create=True, formation_id=formation_id, REQUEST=REQUEST) def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None): """Modification ou creation d'une UE""" create = int(create) if not create: U = do_ue_list(context, args={"ue_id": ue_id}) if not U: raise ScoValueError("UE inexistante !") U = U[0] formation_id = U["formation_id"] title = "Modification de l'UE %(titre)s" % U initvalues = U submitlabel = "Modifier les valeurs" else: title = "Création d'une UE" initvalues = {} submitlabel = "Créer cette UE" Fol = sco_formations.formation_list(context, args={"formation_id": formation_id}) if not Fol: raise ScoValueError( "Formation %s inexistante ! (si vous avez suivi un lien valide, merci de signaler le problème)" % formation_id ) Fo = Fol[0] parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"]) H = [ html_sco_header.sco_header( context, REQUEST, page_title=title, javascripts=["js/edit_ue.js"] ), "Les UE sont des groupes de modules dans une formation donnée, utilisés pour l'évaluation (on calcule des moyennes par UE et applique des seuils ("barres")).
Note: L'UE n'a pas de coefficient associé. Seuls les modules ont des coefficients.
""", ] ue_types = parcours.ALLOWED_UE_TYPES ue_types.sort() ue_types_names = [sco_codes_parcours.UE_TYPE_NAME[k] for k in ue_types] ue_types = [str(x) for x in ue_types] fw = [ ("ue_id", {"input_type": "hidden"}), ("create", {"input_type": "hidden", "default": create}), ("formation_id", {"input_type": "hidden", "default": formation_id}), ("titre", {"size": 30, "explanation": "nom de l'UE"}), ("acronyme", {"size": 8, "explanation": "abbréviation", "allow_null": False}), ( "numero", { "size": 2, "explanation": "numéro (1,2,3,4) de l'UE pour l'ordre d'affichage", "type": "int", }, ), ( "type", { "explanation": "type d'UE", "input_type": "menu", "allowed_values": ue_types, "labels": ue_types_names, }, ), ( "ects", { "size": 4, "type": "float", "title": "ECTS", "explanation": "nombre de crédits ECTS", }, ), ( "coefficient", { "size": 4, "type": "float", "title": "Coefficient", "explanation": """les coefficients d'UE ne sont utilisés que lorsque l'option Utiliser les coefficients d'UE pour calculer la moyenne générale est activée. Par défaut, le coefficient d'une UE est simplement la somme des coefficients des modules dans lesquels l'étudiant a des notes. """, }, ), ( "ue_code", { "size": 12, "title": "Code UE", "explanation": "code interne (optionnel). Toutes les UE partageant le même code (et le même code de formation) sont compatibles (compensation de semestres, capitalisation d'UE). Voir liste ci-dessous.", }, ), ( "code_apogee", { "title": "Code Apogée", "size": 15, "explanation": "code élément pédagogique Apogée (optionnel)", }, ), ] if parcours.UE_IS_MODULE: # demande le semestre pour creer le module immediatement: semestres_indices = range(1, parcours.NB_SEM + 1) fw.append( ( "semestre_id", { "input_type": "menu", "type": "int", "title": scu.strcapitalize(parcours.SESSION_NAME), "explanation": "%s de début du module dans la formation" % parcours.SESSION_NAME, "labels": [str(x) for x in semestres_indices], "allowed_values": semestres_indices, }, ) ) if create and not parcours.UE_IS_MODULE: fw.append( ( "create_matiere", { "input_type": "boolcheckbox", "default": False, "title": "Créer matière identique", "explanation": "créer immédiatement une matière dans cette UE (utile si on n'utilise pas de matières)", }, ) ) tf = TrivialFormulator( REQUEST.URL0, REQUEST.form, fw, initvalues=initvalues, submitlabel=submitlabel ) if tf[0] == 0: X = """ """ return "\n".join(H) + tf[1] + X + html_sco_header.sco_footer(context, REQUEST) else: if create: if not tf[2]["ue_code"]: del tf[2]["ue_code"] if not tf[2]["numero"]: if not "semestre_id" in tf[2]: tf[2]["semestre_id"] = 0 # numero regroupant par semestre ou année: tf[2]["numero"] = next_ue_numero( context, formation_id, int(tf[2]["semestre_id"] or 0) ) ue_id = do_ue_create(context, tf[2], REQUEST) if parcours.UE_IS_MODULE or tf[2]["create_matiere"]: matiere_id = sco_edit_matiere.do_matiere_create( context, {"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1}, REQUEST, ) if parcours.UE_IS_MODULE: # dans ce mode, crée un (unique) module dans l'UE: _ = sco_edit_module.do_module_create( context, { "titre": tf[2]["titre"], "code": tf[2]["acronyme"], "coefficient": 1.0, # tous les modules auront coef 1, et on utilisera les ECTS "ue_id": ue_id, "matiere_id": matiere_id, "formation_id": formation_id, "semestre_id": tf[2]["semestre_id"], }, REQUEST, ) else: do_ue_edit(context, tf[2]) return REQUEST.RESPONSE.redirect( scu.NotesURL() + "/ue_list?formation_id=" + formation_id ) def _add_ue_semestre_id(context, ue_list): """ajoute semestre_id dans les ue, en regardant le premier module de chacune. Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000), qui les place à la fin de la liste. """ for ue in ue_list: Modlist = sco_edit_module.do_module_list(context, args={"ue_id": ue["ue_id"]}) if Modlist: ue["semestre_id"] = Modlist[0]["semestre_id"] else: ue["semestre_id"] = 1000000 def next_ue_numero(context, formation_id, semestre_id=None): """Numero d'une nouvelle UE dans cette formation. Si le semestre est specifie, cherche les UE ayant des modules de ce semestre """ ue_list = do_ue_list(context, args={"formation_id": formation_id}) if not ue_list: return 0 if semestre_id is None: return ue_list[-1]["numero"] + 1000 else: # Avec semestre: (prend le semestre du 1er module de l'UE) _add_ue_semestre_id(context, ue_list) ue_list_semestre = [ue for ue in ue_list if ue["semestre_id"] == semestre_id] if ue_list_semestre: return ue_list_semestre[-1]["numero"] + 10 else: return ue_list[-1]["numero"] + 1000 def ue_delete( context, ue_id=None, delete_validations=False, dialog_confirmed=False, REQUEST=None ): """Delete an UE""" ue = do_ue_list(context, args={"ue_id": ue_id}) if not ue: raise ScoValueError("UE inexistante !") ue = ue[0] if not dialog_confirmed: return scu.confirm_dialog( context, "Cette formation est verrouillée car %d semestres verrouillés s'y réferent. Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module), vous devez:
' + msg + "
") if has_duplicate_ue_codes: H.append( """