diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index 3aaef1c78..6c2479021 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -565,11 +565,14 @@ def save_groups_auto_assignment(formsemestre_id: int): @permission_required(Permission.ScoView) @as_json def formsemestre_edt(formsemestre_id: int): - """l'emploi du temps du semestre + """l'emploi du temps du semestre. Si ok, une liste d'évènements. Sinon, une chaine indiquant un message d'erreur. + + group_ids permet de filtrer sur les groupes ScoDoc. """ query = FormSemestre.query.filter_by(id=formsemestre_id) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) formsemestre: FormSemestre = query.first_or_404(formsemestre_id) - return sco_edt_cal.formsemestre_edt_dict(formsemestre) + group_ids = request.args.getlist("group_ids", int) + return sco_edt_cal.formsemestre_edt_dict(formsemestre, group_ids=group_ids) diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 9c2104c3c..a3a9ada56 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -111,11 +111,17 @@ _COLOR_PALETTE = [ _EVENT_DEFAULT_COLOR = "rgb(214, 233, 248)" -def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]: +def formsemestre_edt_dict( + formsemestre: FormSemestre, group_ids: list[int] = None +) -> list[dict]: """EDT complet du semestre, comme une liste de dict serialisable en json. - Fonction appellée par l'API /formsemestre//edt + Fonction appelée par l'API /formsemestre//edt + group_ids indiquer les groupes ScoDoc à afficher (les autres sont filtrés). + Les évènements pour lesquels le groupe ScoDoc n'est pas reconnu sont + toujours présents. TODO: spécifier intervalle de dates start et end """ + group_ids_set = set(group_ids) if group_ids else set() try: events_scodoc = _load_and_convert_ics(formsemestre) except ScoValueError as exc: @@ -138,9 +144,12 @@ def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]: {scu.EMO_WARNING} non reconnu """ ) + if group and group_ids_set and group.id not in group_ids_set: + continue # ignore cet évènement modimpl: ModuleImpl | bool = event["modimpl"] if modimpl is False: - mod_disp = f"""
+ mod_disp = f"""
{scu.EMO_WARNING} non configuré
""" else: diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index b0967835d..a37e7934c 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -290,8 +290,9 @@ def get_group_members(group_id, etat=None): return r -def get_group_infos(group_id, etat=None): # was _getlisteetud - """legacy code: used by group_list and trombino""" +def get_group_infos(group_id, etat: str | None = None): # was _getlisteetud + """legacy code: used by group_list and trombino. + etat: état de l'inscription.""" from app.scodoc import sco_formsemestre cnx = ndb.GetDBConnexion() diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py index d1c35ca7f..d02e7cc74 100644 --- a/app/scodoc/sco_groups_view.py +++ b/app/scodoc/sco_groups_view.py @@ -153,63 +153,79 @@ def groups_view( return "\n".join(H) -def form_groups_choice(groups_infos, with_selectall_butt=False, submit_on_change=False): +def form_groups_choice( + groups_infos, + with_selectall_butt=False, + with_deselect_butt=False, + submit_on_change=False, + default_deselect_others=True, +): """form pour selection groupes group_ids est la liste des groupes actuellement sélectionnés et doit comporter au moins un élément, sauf si formsemestre_id est spécifié. (utilisé pour retrouver le semestre et proposer la liste des autres groupes) - Si submit_on_change, ajoute une classe "submit_on_change" qui est utilisee en JS + Si submit_on_change, soumet (recharge la page) à chaque modif. + Si default_deselect_others, désélectionne le groupe "Tous" quand on sélectionne un autre groupe. + + Ces deux options ajoutent des classes utilisées en JS pour la gestion du formulaire. """ default_group_id = sco_groups.get_default_group(groups_infos.formsemestre_id) H = [ - """
- - + f""" + + + Groupes: + { + menu_groups_choice( + groups_infos, + submit_on_change=submit_on_change, + default_deselect_others=default_deselect_others, + ) + } """ - % (groups_infos.formsemestre_id, default_group_id) ] - - H.append(menu_groups_choice(groups_infos, submit_on_change=submit_on_change)) - if with_selectall_butt: H.append( - """""" + """""" + ) + if with_deselect_butt: + H.append( + """""" ) H.append("
") return "\n".join(H) -def menu_groups_choice(groups_infos, submit_on_change=False): +def menu_groups_choice( + groups_infos, submit_on_change=False, default_deselect_others=True +): """menu pour selection groupes group_ids est la liste des groupes actuellement sélectionnés et doit comporter au moins un élément, sauf si formsemestre_id est spécifié. (utilisé pour retrouver le semestre et proposer la liste des autres groupes) """ default_group_id = sco_groups.get_default_group(groups_infos.formsemestre_id) - - if submit_on_change: - klass = "submit_on_change" - else: - klass = "" - H = [ - """ + + """ + ] for partition in groups_infos.partitions: H.append('' % partition["partition_name"]) @@ -289,14 +305,19 @@ class DisplayedGroupsInfos: .formsemestre_id : semestre "principal" (en fait celui du 1er groupe de la liste) .members .groups_titles + + etat: filtrage selon l'état de l'inscription + select_all_when_unspecified : sélectionne le groupe "tous" si aucun groupe n'est indiqué. + empty_list_select_all: si vrai (défaut) on sélectionne le groupe tous si aucun groupe indiqué. """ def __init__( self, group_ids=(), # groupes specifies dans l'URL, ou un seul int - formsemestre_id=None, - etat=None, + formsemestre_id: int | None = None, + etat: str | None = None, select_all_when_unspecified=False, + empty_list_select_all=True, moduleimpl_id=None, # used to find formsemestre when unspecified ): if isinstance(group_ids, int): @@ -317,21 +338,26 @@ class DisplayedGroupsInfos: if not group_ids: # appel sans groupe (eg page accueil) if not formsemestre_id: - raise Exception("missing parameter formsemestre_id or group_ids") - if select_all_when_unspecified: - group_ids = [ - sco_groups.get_default_group(formsemestre_id, fix_if_missing=True) - ] - else: - # selectionne le premier groupe trouvé, s'il y en a un - partition = sco_groups.get_partitions_list( - formsemestre_id, with_default=True - )[0] - groups = sco_groups.get_partition_groups(partition) - if groups: - group_ids = [groups[0]["group_id"]] + raise ValueError("missing parameter formsemestre_id or group_ids") + if empty_list_select_all: + if select_all_when_unspecified: + group_ids = [ + sco_groups.get_default_group( + formsemestre_id, fix_if_missing=True + ) + ] else: - group_ids = [sco_groups.get_default_group(formsemestre_id)] + # selectionne le premier groupe trouvé, s'il y en a un + partition = sco_groups.get_partitions_list( + formsemestre_id, with_default=True + )[0] + groups = sco_groups.get_partition_groups(partition) + if groups: + group_ids = [groups[0]["group_id"]] + else: + group_ids = [sco_groups.get_default_group(formsemestre_id)] + else: + group_ids = [] gq = [] for group_id in group_ids: @@ -375,7 +401,8 @@ class DisplayedGroupsInfos: if not self.formsemestre: # aucun groupe selectionne self.formsemestre = sco_formsemestre.get_formsemestre(formsemestre_id) - + if formsemestre_id not in self.sems: + self.sems[formsemestre_id] = self.formsemestre self.sortuniq() if len(self.sems) > 1: diff --git a/app/static/js/groups_view.js b/app/static/js/groups_view.js index 8daabe574..28bbeeba5 100644 --- a/app/static/js/groups_view.js +++ b/app/static/js/groups_view.js @@ -5,46 +5,54 @@ $().ready(function () { for (var i = 0; i < spans.length; i++) { var sp = spans[i]; var etudid = sp.id; - $(sp).load(SCO_URL + '/etud_photo_html?etudid=' + etudid); + $(sp).load(SCO_URL + "/etud_photo_html?etudid=" + etudid); } }); - // L'URL pour recharger l'état courant de la page (groupes et tab selectionnes) // (ne fonctionne que pour les requetes GET: manipule la query string) function groups_view_url() { var url = $.url(); - delete url.param()['group_ids']; // retire anciens groupes de l'URL - delete url.param()['curtab']; // retire ancien tab actif + delete url.param()["group_ids"]; // retire anciens groupes de l'URL + delete url.param()["curtab"]; // retire ancien tab actif if (CURRENT_TAB_HASH) { - url.param()['curtab'] = CURRENT_TAB_HASH; + url.param()["curtab"] = CURRENT_TAB_HASH; } - delete url.param()['formsemestre_id']; - url.param()['formsemestre_id'] = $("#group_selector")[0].formsemestre_id.value; + delete url.param()["formsemestre_id"]; + url.param()["formsemestre_id"] = + $("#group_selector")[0].formsemestre_id.value; var selected_groups = $("#group_selector select").val(); - url.param()['group_ids'] = selected_groups; // remplace par groupes selectionnes + url.param()["group_ids"] = selected_groups; // remplace par groupes selectionnes return url; } -// Selectionne tous les etudiants et recharge la page: -function select_tous() { +// Sélectionne le groupe "tous" et recharge la page: +function select_groupe_tous() { var url = groups_view_url(); var default_group_id = $("#group_selector")[0].default_group_id.value; - delete url.param()['group_ids']; - url.param()['group_ids'] = [default_group_id]; + delete url.param()["group_ids"]; + url.param()["group_ids"] = [default_group_id]; - var query_string = $.param(url.param(), traditional = true); - window.location = url.attr('base') + url.attr('path') + '?' + query_string; + var query_string = $.param(url.param(), (traditional = true)); + window.location = url.attr("base") + url.attr("path") + "?" + query_string; +} + +// Recharge la page sans arguments group_ids +function remove_group_filter() { + var url = groups_view_url(); + delete url.param()["group_ids"]; + var query_string = $.param(url.param(), (traditional = true)); + window.location = url.attr("base") + url.attr("path") + "?" + query_string; } // L'URL pour l'état courant de la page: function get_current_url() { var url = groups_view_url(); - var query_string = $.param(url.param(), traditional = true); - return url.attr('base') + url.attr('path') + '?' + query_string; + var query_string = $.param(url.param(), (traditional = true)); + return url.attr("base") + url.attr("path") + "?" + query_string; } // Recharge la page en changeant les groupes selectionnés et en conservant le tab actif: @@ -53,13 +61,15 @@ function submit_group_selector() { } function show_current_tab() { - $('.nav-tabs [href="#' + CURRENT_TAB_HASH + '"]').tab('show'); + if (document.getElementsByClassName("nav-tabs").length < 0) { + $('.nav-tabs [href="#' + CURRENT_TAB_HASH + '"]').tab("show"); + } } -var CURRENT_TAB_HASH = $.url().param()['curtab']; +var CURRENT_TAB_HASH = $.url().param()["curtab"]; $().ready(function () { - $('.nav-tabs a').on('shown.bs.tab', function (e) { + $(".nav-tabs a").on("shown.bs.tab", function (e) { CURRENT_TAB_HASH = e.target.hash.slice(1); // sans le # }); @@ -69,7 +79,13 @@ $().ready(function () { function change_list_options() { var url = groups_view_url(); var selected_options = $("#group_list_options").val(); - var options = ["with_paiement", "with_archives", "with_annotations", "with_codes", "with_bourse"]; + var options = [ + "with_paiement", + "with_archives", + "with_annotations", + "with_codes", + "with_bourse", + ]; for (var i = 0; i < options.length; i++) { var option = options[i]; delete url.param()[option]; @@ -77,8 +93,8 @@ function change_list_options() { url.param()[option] = 1; } } - var query_string = $.param(url.param(), traditional = true); - window.location = url.attr('base') + url.attr('path') + '?' + query_string; + var query_string = $.param(url.param(), (traditional = true)); + window.location = url.attr("base") + url.attr("path") + "?" + query_string; } // Menu choix groupe: @@ -95,64 +111,87 @@ function toggle_visible_etuds() { var input_eval = $("#formnotes_evaluation_id"); if (input_eval.length > 0) { var evaluation_id = input_eval[0].value; - $("#menu_saisie_tableur a").attr("href", "saisie_notes_tableur?evaluation_id=" + evaluation_id + qargs); + $("#menu_saisie_tableur a").attr( + "href", + "saisie_notes_tableur?evaluation_id=" + evaluation_id + qargs + ); // lien feuille excel: - $("#lnk_feuille_saisie").attr("href", "feuille_saisie_notes?evaluation_id=" + evaluation_id + qargs); + $("#lnk_feuille_saisie").attr( + "href", + "feuille_saisie_notes?evaluation_id=" + evaluation_id + qargs + ); } // Update champs form group_ids_str let group_ids_str = Array.from( document.querySelectorAll("#group_ids_sel option:checked") - ).map( - function (elem) { return elem.value; } - ).join(); - document.querySelectorAll("input.group_ids_str").forEach(elem => elem.value = group_ids_str); + ) + .map(function (elem) { + return elem.value; + }) + .join(); + document + .querySelectorAll("input.group_ids_str") + .forEach((elem) => (elem.value = group_ids_str)); } $().ready(function () { - $('#group_ids_sel').multiselect( - { - includeSelectAllOption: false, - nonSelectedText: 'choisir...', - // buttonContainer: '
', - onChange: function (element, checked) { - if (checked == true) { - var default_group_id = $(".default_group")[0].value; + $("#group_ids_sel").multiselect({ + includeSelectAllOption: false, + nonSelectedText: "choisir...", + // buttonContainer: '
', + onChange: function (element, checked) { + // Gestion du groupe "tous" + if ( + checked == true && + $("#group_ids_sel").hasClass("default_deselect_others") + ) { + var default_group_id = $(".default_group")[0].value; - if (element.hasClass("default_group")) { - // click sur groupe "tous" - // deselectionne les autres - $("#group_ids_sel option:selected").each(function (index, opt) { - if (opt.value != default_group_id) { - $("#group_ids_sel").multiselect('deselect', opt.value); - } - }); - - } else { - // click sur un autre item - // si le groupe "tous" est selectionne et que l'on coche un autre, le deselectionner - var default_is_selected = false; - $("#group_ids_sel option:selected").each(function (index, opt) { - if (opt.value == default_group_id) { - default_is_selected = true; - return false; - } - }); - if (default_is_selected) { - $("#group_ids_sel").multiselect('deselect', default_group_id); + if (element.hasClass("default_group")) { + // click sur groupe "tous" + // deselectionne les autres + $("#group_ids_sel option:selected").each(function ( + index, + opt + ) { + if (opt.value != default_group_id) { + $("#group_ids_sel").multiselect( + "deselect", + opt.value + ); } + }); + } else { + // click sur un autre item + // si le groupe "tous" est selectionne et que l'on coche un autre, le deselectionner + var default_is_selected = false; + $("#group_ids_sel option:selected").each(function ( + index, + opt + ) { + if (opt.value == default_group_id) { + default_is_selected = true; + return false; + } + }); + if (default_is_selected) { + $("#group_ids_sel").multiselect( + "deselect", + default_group_id + ); } } - - toggle_visible_etuds(); - // referme le menu apres chaque choix: - $("#group_selector .btn-group").removeClass('open'); - - if ($("#group_ids_sel").hasClass("submit_on_change")) { - submit_group_selector(); - } } - } - ); + + toggle_visible_etuds(); + // referme le menu apres chaque choix: + $("#group_selector .btn-group").removeClass("open"); + + if ($("#group_ids_sel").hasClass("submit_on_change")) { + submit_group_selector(); + } + }, + }); // initial setup toggle_visible_etuds(); @@ -160,27 +199,27 @@ $().ready(function () { // Trombinoscope $().ready(function () { - var elems = $(".trombi-photo"); for (var i = 0; i < elems.length; i++) { - $(elems[i]).qtip( - { - content: { - ajax: { - url: SCO_URL + "/etud_info_html?with_photo=0&etudid=" + get_etudid_from_elem(elems[i]) - }, - text: "Loading..." + $(elems[i]).qtip({ + content: { + ajax: { + url: + SCO_URL + + "/etud_info_html?with_photo=0&etudid=" + + get_etudid_from_elem(elems[i]), }, - position: { - at: "right", - my: "left top" - }, - style: { - classes: 'qtip-etud' - }, - // utile pour debugguer le css: - // hide: { event: 'unfocus' } - } - ); + text: "Loading...", + }, + position: { + at: "right", + my: "left top", + }, + style: { + classes: "qtip-etud", + }, + // utile pour debugguer le css: + // hide: { event: 'unfocus' } + }); } }); diff --git a/app/templates/formsemestre/edt.j2 b/app/templates/formsemestre/edt.j2 index 0c4edce75..a4c91c38b 100644 --- a/app/templates/formsemestre/edt.j2 +++ b/app/templates/formsemestre/edt.j2 @@ -3,8 +3,9 @@ {% block styles %} {{super()}} - - + + + {% endblock %} @@ -13,6 +14,8 @@

Expérimental: emploi du temps

+ {{ form_groups_choice|safe }} +
{% endblock app_content %} {% block scripts %} {{ super() }} + + +