diff --git a/app/but/apc_edit_ue.py b/app/but/apc_edit_ue.py index 1e439c71..55687f0f 100644 --- a/app/but/apc_edit_ue.py +++ b/app/but/apc_edit_ue.py @@ -17,7 +17,13 @@ def form_ue_choix_niveau(ue: UniteEns) -> str: """Form. HTML pour associer une UE à un niveau de compétence""" ref_comp = ue.formation.referentiel_competence if ref_comp is None: - return """
pas de référentiel de compétence
""" + return f"""
+
Pas de référentiel de compétence associé à cette formation !
+
associer un référentiel de compétence +
+
""" annee = (ue.semestre_idx + 1) // 2 # 1, 2, 3 niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(annee) @@ -39,7 +45,7 @@ def form_ue_choix_niveau(ue: UniteEns) -> str: options.append("""""") options_str = "\n".join(options) return f""" -
+
Niveau de compétence associé:
""".format( - module_id, ",".join(sco_tag_module.module_tag_list(module_id)) - ) + html_foot_markup=f"""
+ """ if not create else "", initvalues=module_dict if module else {}, @@ -793,11 +805,14 @@ def module_edit( # do_module_edit(tf[2]) # Modifie les parcours - if "parcours" in tf[2]: - module.parcours = [ - ApcParcours.query.get(int(parcour_id_str)) - for parcour_id_str in tf[2]["parcours"] - ] + if ("parcours" in tf[2]) and formation.referentiel_competence: + if "-1" in tf[2]["parcours"]: # "tous" + module.parcours = formation.referentiel_competence.parcours.all() + else: + module.parcours = [ + ApcParcours.query.get(int(parcour_id_str)) + for parcour_id_str in tf[2]["parcours"] + ] # Modifie les AC if "app_critiques" in tf[2]: module.app_critiques = [ @@ -811,7 +826,7 @@ def module_edit( "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id, - semestre_idx=tf[2]["semestre_id"], + semestre_idx=tf[2]["semestre_id"] if is_apc else 1, ) ) diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 3ccba772..1acf5b95 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -39,23 +39,21 @@ from app.models import Module, ModuleImpl, Evaluation, EvaluationUEPoids, UniteE from app.models import ScolarNews from app.models.formations import Formation from app.models.formsemestre import FormSemestre +from app.models.but_refcomp import ApcParcours import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app.scodoc import sco_cache from app.scodoc import sco_groups from app import log -from app.scodoc.TrivialFormulator import TrivialFormulator, TF +from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.sco_vdi import ApoEtapeVDI from app.scodoc import html_sco_header from app.scodoc import sco_codes_parcours from app.scodoc import sco_compute_moy -from app.scodoc import sco_edit_matiere from app.scodoc import sco_edit_module -from app.scodoc import sco_edit_ue from app.scodoc import sco_etud -from app.scodoc import sco_evaluations from app.scodoc import sco_evaluation_db from app.scodoc import sco_formations from app.scodoc import sco_formsemestre @@ -119,12 +117,12 @@ def formsemestre_editwithmodules(formsemestre_id): vals = scu.get_request_args() if not vals.get("tf_submitted", False): H.append( - """

Seuls les modules cochés font partie de ce semestre. + """

Seuls les modules cochés font partie de ce semestre. Pour les retirer, les décocher et appuyer sur le bouton "modifier".

-

Attention : s'il y a déjà des évaluations dans un module, +

Attention : s'il y a déjà des évaluations dans un module, il ne peut pas être supprimé !

-

Les modules ont toujours un responsable. +

Les modules ont toujours un responsable. Par défaut, c'est le directeur des études.

Un semestre ne peut comporter qu'une seule UE "bonus sport/culture"

@@ -153,7 +151,7 @@ def do_formsemestre_createwithmodules(edit=False): formsemestre = FormSemestre.query.get_or_404(formsemestre_id) if not current_user.has_permission(Permission.ScoImplement): if not edit: - # il faut ScoImplement pour creer un semestre + # il faut ScoImplement pour créer un semestre raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") else: if not sem["resp_can_edit"] or current_user.id not in sem["responsables"]: @@ -175,6 +173,7 @@ def do_formsemestre_createwithmodules(edit=False): formation = Formation.query.get(formation_id) if formation is None: raise ScoValueError("Formation inexistante !") + is_apc = formation.is_apc() if not edit: initvalues = {"titre": _default_sem_title(formation)} semestre_id = int(vals["semestre_id"]) @@ -210,12 +209,12 @@ def do_formsemestre_createwithmodules(edit=False): if NB_SEM == 1: semestre_id_list = [-1] else: - if edit and formation.is_apc(): + if edit and is_apc: # en APC, ne permet pas de changer de semestre semestre_id_list = [formsemestre.semestre_id] else: semestre_id_list = list(range(1, NB_SEM + 1)) - if not formation.is_apc(): + if not is_apc: # propose "pas de semestre" seulement en classique semestre_id_list.insert(0, -1) @@ -226,7 +225,7 @@ def do_formsemestre_createwithmodules(edit=False): else: semestre_id_labels.append(f"S{sid}") # Liste des modules dans cette formation - if formation.is_apc(): + if is_apc: modules = formation.modules.order_by(Module.module_type, Module.numero) else: modules = ( @@ -318,10 +317,10 @@ def do_formsemestre_createwithmodules(edit=False): { "size": 40, "title": "Nom de ce semestre", - "explanation": """n'indiquez pas les dates, ni le semestre, ni la modalité dans + "explanation": f"""n'indiquez pas les dates, ni le semestre, ni la modalité dans le titre: ils seront automatiquement ajoutés """ - % _default_sem_title(formation), + value="remettre titre par défaut" onClick="document.tf.titre.value='{ + _default_sem_title(formation)}';"/>""", }, ), ( @@ -343,11 +342,9 @@ def do_formsemestre_createwithmodules(edit=False): "allowed_values": semestre_id_list, "labels": semestre_id_labels, "explanation": "en BUT, on ne peut pas modifier le semestre après création" - if formation.is_apc() - else "", - "attributes": ['onchange="change_semestre_id();"'] - if formation.is_apc() + if is_apc else "", + "attributes": ['onchange="change_semestre_id();"'] if is_apc else "", }, ), ) @@ -386,7 +383,7 @@ def do_formsemestre_createwithmodules(edit=False): mf = mf_manual for n in range(1, scu.EDIT_NB_ETAPES + 1): - mf["title"] = "Etape Apogée (%d)" % n + mf["title"] = f"Etape Apogée ({n})" modform.append(("etape_apo" + str(n), mf.copy())) modform.append( ( @@ -443,15 +440,19 @@ def do_formsemestre_createwithmodules(edit=False): ) ) if edit: - formtit = ( - """ -

Modifier les coefficients des UE capitalisées

-

Sélectionner les modules, leurs responsables et les étudiants à inscrire:

+ formtit = f""" +

Modifier les coefficients des UE capitalisées

+

Sélectionner les modules, leurs responsables et les étudiants + à inscrire:

""" - % formsemestre_id - ) else: - formtit = """

Sélectionner les modules et leurs responsables

Si vous avez des parcours (options), ne sélectionnez que les modules du tronc commun.

""" + formtit = """

Sélectionner les modules et leurs responsables

+

Si vous avez des parcours (options), dans un premier + ne sélectionnez que les modules du tronc commun, puis après inscriptions, + revenez ajouter les modules de parcours en sélectionnant les groupes d'étudiants + à y inscrire. +

""" modform += [ ( @@ -531,12 +532,52 @@ def do_formsemestre_createwithmodules(edit=False): "explanation": "empêcher le calcul des moyennes d'UE et générale.", }, ), + ] + # Choix des parcours + if is_apc: + ref_comp = formation.referentiel_competence + if ref_comp: + modform += [ + ( + "parcours", + { + "input_type": "checkbox", + "vertical": True, + "dom_id": "tf_module_parcours", + "labels": [parcour.libelle for parcour in ref_comp.parcours], + "allowed_values": [ + str(parcour.id) for parcour in ref_comp.parcours + ], + "explanation": "Parcours proposés dans ce semestre.", + }, + ) + ] + if edit: + sem["parcours"] = [str(parcour.id) for parcour in formsemestre.parcours] + else: + modform += [ + ( + "parcours", + { + "input_type": "separator", + "title": f"""{scu.EMO_WARNING } + Pas de parcours: + vérifier la formation + """, + }, + ) + ] + + # Choix des modules + modform += [ ( "sep", { "input_type": "separator", "title": "", - "template": "%s" % formtit, + "template": f"
{formtit}", }, ), ] @@ -544,8 +585,8 @@ def do_formsemestre_createwithmodules(edit=False): nbmod = 0 for semestre_id in semestre_ids: - if formation.is_apc(): - # pour restreindre l'édition aux module du semestre sélectionné + if is_apc: + # pour restreindre l'édition aux modules du semestre sélectionné tr_class = f'class="sem{semestre_id}"' else: tr_class = "" @@ -560,7 +601,7 @@ def do_formsemestre_createwithmodules(edit=False): "sep", { "input_type": "separator", - "title": "Semestre %s" % semestre_id, + "title": f"Semestre {semestre_id}", "template": templ_sep, }, ) @@ -568,13 +609,13 @@ def do_formsemestre_createwithmodules(edit=False): for mod in mods: if mod["semestre_id"] == semestre_id and ( (not edit) # creation => tous modules - or (not formation.is_apc()) # pas BUT, on peut mixer les semestres + or (not is_apc) # pas BUT, on peut mixer les semestres or (semestre_id == formsemestre.semestre_id) # module du semestre or (mod["module_id"] in module_ids_set) # module déjà présent ): nbmod += 1 if edit: - select_name = "%s!group_id" % mod["module_id"] + select_name = f"{mod['module_id']}!group_id" def opt_selected(gid): if gid == vals.get(select_name): @@ -603,13 +644,16 @@ def do_formsemestre_createwithmodules(edit=False): group["group_name"], ) fcg += "" - itemtemplate = ( - f"""" - ) + itemtemplate = f""" + + + + """ else: - itemtemplate = f"""""" + itemtemplate = f""" + + + """ modform.append( ( "MI" + str(mod["module_id"]), @@ -742,7 +786,8 @@ def do_formsemestre_createwithmodules(edit=False): for module_id in tf[2]["tf-checked"]: mod_resp_id = User.get_user_id_from_nomplogin(tf[2][module_id]) if mod_resp_id is None: - # Si un module n'a pas de responsable (ou inconnu), l'affecte au 1er directeur des etudes: + # Si un module n'a pas de responsable (ou inconnu), + # l'affecte au 1er directeur des etudes: mod_resp_id = tf[2]["responsable_id"] tf[2][module_id] = mod_resp_id @@ -763,7 +808,7 @@ def do_formsemestre_createwithmodules(edit=False): module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]] _formsemestre_check_ue_bonus_unicity(module_ids_checked) if not edit: - if formation.is_apc(): + if is_apc: _formsemestre_check_module_list( module_ids_checked, tf[2]["semestre_id"] ) @@ -777,14 +822,6 @@ def do_formsemestre_createwithmodules(edit=False): "responsable_id": tf[2][f"MI{module_id}"], } _ = sco_moduleimpl.do_moduleimpl_create(modargs) - flash("Nouveau semestre créé") - return flask.redirect( - url_for( - "notes.formsemestre_status", - scodoc_dept=g.scodoc_dept, - formsemestre_id=formsemestre_id, - ) - ) else: # Modification du semestre: # on doit creer les modules nouvellement selectionnés @@ -794,7 +831,7 @@ def do_formsemestre_createwithmodules(edit=False): module_ids_tocreate = [ x for x in module_ids_checked if not x in module_ids_existing ] - if formation.is_apc(): + if is_apc: _formsemestre_check_module_list( module_ids_tocreate, tf[2]["semestre_id"] ) @@ -868,27 +905,46 @@ def do_formsemestre_createwithmodules(edit=False): modargs, formsemestre_id=formsemestre_id ) mod = sco_edit_module.module_list({"module_id": module_id})[0] - - if msg: - msg_html = ( - '
Attention !
  • ' - + "
  • ".join(msg) - + "
" - ) - if ok: - msg_html += "

Modification effectuée

" - else: - msg_html += "

Modification effectuée (mais modules cités non supprimés)

" - msg_html += ( - 'retour au tableau de bord' - % formsemestre_id - ) - return msg_html + # --- Assocation des parcours + formsemestre = FormSemestre.query.get(formsemestre_id) + if "parcours" in tf[2]: + formsemestre.parcours = [ + ApcParcours.query.get(int(parcour_id_str)) + for parcour_id_str in tf[2]["parcours"] + ] + db.session.add(formsemestre) + db.session.commit() + # --- Fin + if edit: + if msg: + msg_html = ( + '
Attention !
  • ' + + "
  • ".join(msg) + + "
" + ) + if ok: + msg_html += "

Modification effectuée

" else: - return flask.redirect( - "formsemestre_status?formsemestre_id=%s&head_message=Semestre modifié" - % formsemestre_id - ) + msg_html += "

Modification effectuée (mais modules cités non supprimés)

" + msg_html += ( + 'retour au tableau de bord' + % formsemestre_id + ) + return msg_html + else: + return flask.redirect( + "formsemestre_status?formsemestre_id=%s&head_message=Semestre modifié" + % formsemestre_id + ) + else: + flash("Nouveau semestre créé") + return flask.redirect( + url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre_id, + ) + ) def _formsemestre_check_module_list(module_ids, semestre_idx): diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 299318ed..fbf16c3d 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -929,10 +929,18 @@ def formsemestre_status_head(formsemestre_id=None, page_title=None): })""" ) H.append("") + if sem.parcours: + H.append( + f""" + + + + """ + ) evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id) H.append( - '
%(label)s%(elem)s""" - + fcg - + "
%(label)s%(elem)s{fcg}
%(label)s%(elem)s
%(label)s%(elem)s
Parcours: {', '.join(parcours.code for parcours in sem.parcours)}
Evaluations: %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides' + '
Évaluations: %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides' % evals ) if evals["last_modif"]: diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index c9933ffb..1696009a 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -2206,7 +2206,7 @@ ul.notes_module_list { list-style-type: none; } -div#ue_choix_niveau { +div.ue_choix_niveau { background-color: rgb(191, 242, 255); border: 1px solid blue; border-radius: 10px; diff --git a/app/static/js/module_edit.js b/app/static/js/module_edit.js new file mode 100644 index 00000000..9882fdff --- /dev/null +++ b/app/static/js/module_edit.js @@ -0,0 +1,8 @@ +/* Page édition module */ + + +$(document).ready(function () { + +}); + + diff --git a/app/templates/but/refcomp_assoc.html b/app/templates/but/refcomp_assoc.html index 9f8335be..9ba07dee 100644 --- a/app/templates/but/refcomp_assoc.html +++ b/app/templates/but/refcomp_assoc.html @@ -6,11 +6,25 @@

Associer un référentiel de compétences

Association d'un référentiel de compétence à la formation - {{formation.titre}} ({{formation.acronyme}}) + {{formation.titre}} ({{formation.acronyme}})
-
-
- {{ wtf.quick_form(form) }} +
+ + Référentiel actuellement associé: + {% if formation.referentiel_competence is not none %} + {{ formation.referentiel_competence.specialite_long }} + supprimer + {% else %} + aucun + {% endif %} +
+
+ {{ wtf.quick_form(form) }} +
diff --git a/app/views/refcomp.py b/app/views/refcomp.py index 27e5c83e..fb9e2eb1 100644 --- a/app/views/refcomp.py +++ b/app/views/refcomp.py @@ -160,6 +160,20 @@ def refcomp_assoc_formation(formation_id: int): ) +@bp.route("/referentiel/comp/desassoc_formation/", methods=["GET"]) +@scodoc +@permission_required(Permission.ScoChangeFormation) +def refcomp_desassoc_formation(formation_id: int): + """Désassocie la formation de son ref. de compétence""" + formation = Formation.query.get_or_404(formation_id) + formation.referentiel_competence = None + db.session.add(formation) + db.session.commit() + return redirect( + url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id) + ) + + @bp.route( "/referentiel/comp/load", defaults={"formation_id": None}, methods=["GET", "POST"] ) diff --git a/sco_version.py b/sco_version.py index 3222274f..242844ed 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.2.22" +SCOVERSION = "9.3a" SCONAME = "ScoDoc"