From e5cdb2ef6983383a1afcc949aa67b2125739c8c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 11 Apr 2023 23:56:50 +0200 Subject: [PATCH] Associations UE / Parcours: UI --- app/but/apc_edit_ue.py | 29 +++- app/but/cursus_but.py | 10 +- app/models/formsemestre.py | 1 + app/models/ues.py | 2 +- app/scodoc/sco_edit_ue.py | 6 +- app/static/css/parcour_formation.css | 12 +- app/static/css/refcomp_parcours_niveaux.css | 8 +- app/static/css/scodoc.css | 9 +- app/static/js/edit_ue.js | 138 +++----------------- app/templates/but/parcour_formation.j2 | 26 ++-- tests/api/test_api_formsemestre.py | 2 +- 11 files changed, 84 insertions(+), 159 deletions(-) diff --git a/app/but/apc_edit_ue.py b/app/but/apc_edit_ue.py index 079c2820..da0b3113 100644 --- a/app/but/apc_edit_ue.py +++ b/app/but/apc_edit_ue.py @@ -13,15 +13,15 @@ from app.models import ApcReferentielCompetences, UniteEns from app.scodoc import codes_cursus -def form_ue_choix_niveau(ue: UniteEns) -> str: - """Form. HTML pour associer une UE à un niveau de compétence. +def form_ue_choix_parcours(ue: UniteEns) -> str: + """Form. HTML pour associer une UE à ses parcours. Le menu select lui même est vide et rempli en JS par appel à get_ue_niveaux_options_html """ if ue.type != codes_cursus.UE_STANDARD: return "" ref_comp = ue.formation.referentiel_competence if ref_comp is None: - return f"""
+ return f"""
Pas de référentiel de compétence associé à cette formation !
+

Parcours du BUT

+ """ + ] + # Choix des parcours + ue_pids = [p.id for p in ue.parcours] + H.append("""
""") + for parcour in ref_comp.parcours: + H.append( + f"""""" + ) + H.append("""
""") + # + H.append( + f"""
  • str: diff --git a/app/but/cursus_but.py b/app/but/cursus_but.py index 50734f60..4654a9cb 100644 --- a/app/but/cursus_but.py +++ b/app/but/cursus_but.py @@ -371,7 +371,7 @@ def formsemestre_warning_apc_setup( return "" if formsemestre.formation.referentiel_competence is None: return f""" @@ -404,12 +404,12 @@ def formsemestre_warning_apc_setup( return f"""
    Problème dans la configuration de la formation:
      -
    • { '
    • '.join(H) } +
    • { '
    • '.join(H) }

    Vérifiez les parcours cochés pour ce semestre, - et les associations entre UE et niveaux dans la formation. + et les associations entre UE et niveaux dans la formation.

    """ diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index ef2858ba..d256cf7a 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -151,6 +151,7 @@ class FormSemestre(db.Model): secondary=parcours_formsemestre, lazy="subquery", backref=db.backref("formsemestres", lazy=True), + order_by=(ApcParcours.numero, ApcParcours.code), ) def __init__(self, **kwargs): diff --git a/app/models/ues.py b/app/models/ues.py index 3212e32a..a4385880 100644 --- a/app/models/ues.py +++ b/app/models/ues.py @@ -317,7 +317,7 @@ class UniteEns(db.Model): if niveau.competence.referentiel.id != self.formation.referentiel_competence.id: return False, "Le niveau n'appartient pas au référentiel de la formation" if niveau.id == self.niveau_competence_id: - return True # nothing to do + return True, "" # nothing to do if (niveau is not None) and (self.niveau_competence_id is not None): ok, error_message = self.check_niveau_unique_dans_parcours( niveau, self.parcours diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 8467df1c..cd286f89 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -476,9 +476,9 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No cancelbutton="Revenir à la formation", ) if tf[0] == 0: - niveau_competence_div = "" + ue_parcours_div = "" if ue and is_apc: - niveau_competence_div = apc_edit_ue.form_ue_choix_niveau(ue) + ue_parcours_div = apc_edit_ue.form_ue_choix_parcours(ue) if ue and ue.modules.count() and ue.semestre_idx is not None: modules_div = f"""
    {ue.modules.count()} modules sont rattachés @@ -508,7 +508,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No "\n".join(H) + tf[1] + clone_form - + niveau_competence_div + + ue_parcours_div + modules_div + bonus_div + ue_div diff --git a/app/static/css/parcour_formation.css b/app/static/css/parcour_formation.css index efd5adf3..a3f4d53a 100644 --- a/app/static/css/parcour_formation.css +++ b/app/static/css/parcour_formation.css @@ -19,12 +19,18 @@ div.les_parcours>div.focus { opacity: 1; } -div.les_parcours>div>a:hover { +div.les_parcours>div.link { + background-color: var(--sco-color-background); + color: navy; +} + + +div.les_parcours>div.parc>a:hover { color: #ccc; } -div.les_parcours>div>a, -div.les_parcours>div>a:visited { +div.les_parcours>div.parc>a, +div.les_parcours>div.parc>a:visited { color: white; } diff --git a/app/static/css/refcomp_parcours_niveaux.css b/app/static/css/refcomp_parcours_niveaux.css index bc825421..550dc85f 100644 --- a/app/static/css/refcomp_parcours_niveaux.css +++ b/app/static/css/refcomp_parcours_niveaux.css @@ -22,8 +22,8 @@ div.table_niveaux_parcours { margin-left: 12px; margin-top: 12px; - background: rgb(14, 5, 73); - color: #eee; + background: rgb(210, 210, 210); + color: #111; border-radius: 8px; width: fit-content; padding: 8px; @@ -47,7 +47,7 @@ table.table_niveaux_parcours th { } table.table_niveaux_parcours tr.parcours_but { - color: white; + color: #111; } table.table_niveaux_parcours tr.parcours_but td { @@ -70,7 +70,7 @@ table.table_niveaux_parcours tr td:not(:first-child) { table.table_niveaux_parcours tr.annee_but td:first-child { width: 92px; font-weight: bold; - color: #ddd; + color: #111; } table.table_niveaux_parcours tr.annee_but td.empty { diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index fa8ce74d..c8f8843b 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -5,6 +5,7 @@ --sco-content-min-width: 600px; --sco-content-max-width: 1024px; --sco-color-explication: rgb(10, 58, 140); + --sco-color-background: rgb(242, 242, 238); } html, @@ -12,7 +13,7 @@ body { margin: 0; padding: 0; width: 100%; - background-color: rgb(242, 242, 238); + background-color: var(--sco-color-background); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12pt; } @@ -937,7 +938,7 @@ span.linktitresem a:visited { a.stdlink, a.stdlink:visited { - color: blue; + color: #0e0e9d; text-decoration: underline; } @@ -2539,6 +2540,10 @@ div.ue_advanced { margin-right: 15px; } +div.ue_advanced h3 { + margin-top: 2px; +} + div#ue_list_modules { background-color: rgb(251, 225, 165); border: 1px solid blue; diff --git a/app/static/js/edit_ue.js b/app/static/js/edit_ue.js index 4b64c9a4..b1d8bddb 100644 --- a/app/static/js/edit_ue.js +++ b/app/static/js/edit_ue.js @@ -11,7 +11,6 @@ $().ready(function () { }); update_bonus_description(); } - update_menus_niveau_competence(); }); function update_bonus_description() { @@ -37,131 +36,26 @@ function update_ue_list() { }); } -function set_ue_parcour(elem) { - let ue_id = elem.dataset.ue_id; - let parcour_id = elem.value; - let set_ue_parcour_url = elem.dataset.setter; - $.post(set_ue_parcour_url, - { - ue_id: ue_id, - parcour_id: parcour_id, - }, - function (result) { - sco_message("UE associée au parcours"); - update_menus_niveau_competence(); - } - ); -} +function set_ue_parcour(checkbox) { + let url = checkbox.dataset.setter; + const checkboxes = document.querySelectorAll('#choix_parcours input[type="checkbox"]:checked'); + const parcours_ids = []; + checkboxes.forEach(function (checkbox) { + parcours_ids.push(checkbox.value); + }); -function set_ue_niveau_competence(elem) { - let ue_id = elem.dataset.ue_id; - let niveau_id = elem.value; - let set_ue_niveau_competence_url = elem.dataset.setter; - $.post(set_ue_niveau_competence_url, - { - ue_id: ue_id, - niveau_id: niveau_id, - }, - function (result) { - sco_message("niveau de compétence enregistré"); - - update_menus_niveau_competence(); - } - ); -} - -// Met à jour les niveaux utilisés (disabled) ou non affectés -// dans les menus d'association UE <-> niveau -function update_menus_niveau_competence() { - // let selected_niveaux = []; - // document.querySelectorAll("form.form_ue_choix_niveau select").forEach( - // elem => { selected_niveaux.push(elem.value); } - // ); - - // document.querySelectorAll("form.form_ue_choix_niveau select").forEach( - // elem => { - // for (let i = 0; i < elem.options.length; i++) { - // elem.options[i].disabled = (i != elem.options.selectedIndex) - // && (selected_niveaux.indexOf(elem.options[i].value) != -1) - // && (elem.options[i].value != ""); - // } - // } - // ); - - // nouveau: - document.querySelectorAll("select.niveau_select").forEach( - elem => { - let ue_id = elem.dataset.ue_id; - $.get("get_ue_niveaux_options_html", - { - ue_id: ue_id, - }, - function (result) { - elem.innerHTML = result; - } - ); - } - ); -} - -// ---- Nouveau formulaire choix parcours et niveau ----- -//document.querySelectorAll("select.select_ue_parcours").forEach( -// elem => { elem.addEventListener('change', change_ue_parcours); } -//); -$().ready(function () { - $('select.select_ue_parcours').multiselect( - { - includeSelectAllOption: false, - nonSelectedText: 'choisir...', - // buttonContainer: '
    ', - onChange: function (element, checked) { - var parent = element.parent(); - var selectedOptions = parent.getValue().split(","); - let set_ue_parcours = element.context.dataset.set_ue_parcours; - - fetch(set_ue_parcours, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(selectedOptions) - }) - .then(response => response.json()) - .then(data => { - if (!data.status) { - sco_message(data.message); - // get the option element corresponding to the selected value - var option = parent.find('option[value="' + element.val() + '"]'); - // uncheck the option - option.prop('selected', false); - // refresh the multiselect to reflect the change - parent.multiselect('refresh'); - } - }) - .catch(error => console.error('Error: ' + error)); - - // // referme le menu apres chaque choix: - // $("#group_selector .btn-group").removeClass('open'); - - // if ($("#group_ids_sel").hasClass("submit_on_change")) { - // submit_group_selector(); - // } - } - } - ); -}); - -function change_ue_parcours(event) { - const multiselect = event.target; - const selectedOptions = Array.from(this.selectedOptions).map(option => option.value); - fetch('/set_option/', { // XXX TODO + fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(selectedOptions) + body: JSON.stringify(parcours_ids) }) .then(response => response.json()) - .then(data => console.log('Success!')) - .catch(error => console.error('Error: ' + error)); -}; + .then(data => { + if (data.status) { + sco_message(data.message); + } + }); +} + diff --git a/app/templates/but/parcour_formation.j2 b/app/templates/but/parcour_formation.j2 index b19cd4a0..97293121 100644 --- a/app/templates/but/parcour_formation.j2 +++ b/app/templates/but/parcour_formation.j2 @@ -49,13 +49,24 @@ {# Liens vers les différents parcours #}
    {% for parc in formation.referentiel_competence.parcours %} - {# Description d'un parcours #} @@ -107,19 +118,6 @@ Choisissez un parcours...
    {% endif %} -{# Liens bas de page #} - {% if parcour %}
    diff --git a/tests/api/test_api_formsemestre.py b/tests/api/test_api_formsemestre.py index 10ed4090..3525f570 100644 --- a/tests/api/test_api_formsemestre.py +++ b/tests/api/test_api_formsemestre.py @@ -724,4 +724,4 @@ def _compare_formsemestre_resultat(res: list[dict], ref: list[dict]): for res_d, ref_d in zip(res, ref): assert sorted(res_d.keys()) == sorted(ref_d.keys()) for k in res_d: - assert res_d[k] == ref_d[k] + assert res_d[k] == ref_d[k], f"values for key {k} differ."