diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index c585223d1ed526a1e3f343530a805d1febdd1cc6..ae9d7e1ca3c334d1fad87911902c393bfa1b6d3d 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -1332,17 +1332,6 @@ def groups_auto_repartition(partition: Partition): ), ] - H = [ - f"""<h2>Répartition des groupes de {partition.partition_name}</h2> - <p>Semestre {formsemestre.titre_annee()}</p> - <p class="help">Les groupes existants seront <b>effacés</b> et remplacés par - ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau - des groupes (en utilisant la dernière moyenne générale disponible pour - chaque étudiant) et de maximiser la mixité de chaque groupe. - </p> - """, - ] - tf = TrivialFormulator( request.base_url, scu.get_request_args(), @@ -1354,58 +1343,59 @@ def groups_auto_repartition(partition: Partition): ) if tf[0] == 0: return render_template( - "sco_page.j2", - title="Répartition des groupes", - content="\n".join(H) + "\n" + tf[1], + "formsemestre/groups_auto_repartition.j2", + title="Répartition dans des groupes", + form_html=tf[1], + partition=partition, sco=ScoData(formsemestre=formsemestre), ) - elif tf[0] == -1: + if tf[0] == -1: return flask.redirect(dest_url) - else: - # form submission - log(f"groups_auto_repartition({partition})") - group_names = tf[2]["groupNames"] - group_names = sorted({x.strip() for x in group_names.split(",")}) - # Détruit les groupes existant de cette partition - for group in partition.groups: - db.session.delete(group) - db.session.commit() - # Crée les nouveaux groupes - groups = [] - for group_name in group_names: - if group_name.strip(): - groups.append(partition.create_group(group_name)) - # - nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - identdict = nt.identdict - # build: { civilite : liste etudids trie par niveau croissant } - civilites = {x["civilite"] for x in identdict.values()} - listes = {} + + # form submission + log(f"groups_auto_repartition({partition})") + group_names = tf[2]["groupNames"] + group_names = sorted({x.strip() for x in group_names.split(",")}) + # Détruit les groupes existant de cette partition + for group in partition.groups: + db.session.delete(group) + db.session.commit() + # Crée les nouveaux groupes + groups = [] + for group_name in group_names: + if group_name.strip(): + groups.append(partition.create_group(group_name)) + # + nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + identdict = nt.identdict + # build: { civilite : liste etudids trie par niveau croissant } + civilites = {x["civilite"] for x in identdict.values()} + listes = {} + for civilite in civilites: + listes[civilite] = [ + (_get_prev_moy(x["etudid"], formsemestre.id), x["etudid"]) + for x in identdict.values() + if x["civilite"] == civilite + ] + listes[civilite].sort() + log("listes[%s] = %s" % (civilite, listes[civilite])) + # affect aux groupes: + n = len(identdict) + igroup = 0 + nbgroups = len(groups) + while n > 0: + log(f"n={n}") for civilite in civilites: - listes[civilite] = [ - (_get_prev_moy(x["etudid"], formsemestre.id), x["etudid"]) - for x in identdict.values() - if x["civilite"] == civilite - ] - listes[civilite].sort() - log("listes[%s] = %s" % (civilite, listes[civilite])) - # affect aux groupes: - n = len(identdict) - igroup = 0 - nbgroups = len(groups) - while n > 0: - log(f"n={n}") - for civilite in civilites: - log(f"civilite={civilite}") - if len(listes[civilite]): - n -= 1 - etudid = listes[civilite].pop()[1] - group = groups[igroup] - igroup = (igroup + 1) % nbgroups - log(f"in {etudid} in group {group.id}") - change_etud_group_in_partition(etudid, group) - log(f"{etudid} in group {group.id}") - return flask.redirect(dest_url) + log(f"civilite={civilite}") + if len(listes[civilite]): + n -= 1 + etudid = listes[civilite].pop()[1] + group = groups[igroup] + igroup = (igroup + 1) % nbgroups + log(f"in {etudid} in group {group.id}") + change_etud_group_in_partition(etudid, group) + log(f"{etudid} in group {group.id}") + return flask.redirect(dest_url) def _get_prev_moy(etudid: int, formsemestre_id: int) -> float | str: diff --git a/app/static/css/partition_editor.css b/app/static/css/partition_editor.css index 00bbb2483473f0ef460af5230a659d5c82d3d4ae..90e895bea985d1770f23931c01b45084e9f37cfb 100644 --- a/app/static/css/partition_editor.css +++ b/app/static/css/partition_editor.css @@ -309,7 +309,7 @@ body.editionActivated .filtres>div>div>div>div { display: none; } -#zonePartitions span.editing a { +#zonePartitions span.editing a { text-decoration: none; } @@ -400,7 +400,7 @@ body.editionActivated .filtres .nonEditable .move { /*****************************/ /* Zone Etudiants */ /*****************************/ -#zoneChoix summary{ +#zoneChoix summary { margin: 0 0 16px; cursor: pointer; } @@ -450,6 +450,7 @@ body.editionActivated .filtres .nonEditable .move { margin-bottom: 4px; width: fit-content; } + #zoneChoix .autoAffectation .progress { position: absolute; top: 100%; @@ -524,7 +525,8 @@ body.editionActivated .filtres .nonEditable .move { } #zoneChoix .etudiants .partition>div, -#zoneChoix .etudiants .partition span { +#zoneChoix .etudiants .partition span, +div.partition-name { display: block; padding: 4px 8px; border: 1px solid #aaa; @@ -541,12 +543,22 @@ body.editionActivated .filtres .nonEditable .move { color: #fff; } -#zoneChoix .etudiants .partition>:nth-child(1) { +#zoneChoix .etudiants .partition>:nth-child(1), +div.partition-name { background: #09c; border-color: #09c; color: #fff; } +div.partition-name { + display: inline-block; + width: fit-content; +} + +div.partition-name a { + color: #fff !important; +} + section:not(#zonePartitions) .hide { display: none !important; } diff --git a/app/templates/formsemestre/groups_auto_repartition.j2 b/app/templates/formsemestre/groups_auto_repartition.j2 new file mode 100644 index 0000000000000000000000000000000000000000..34a799b9291c58e5e5717126e999bc4c6beb4769 --- /dev/null +++ b/app/templates/formsemestre/groups_auto_repartition.j2 @@ -0,0 +1,30 @@ +{# Formulaire répartition auto dans les groupes #} + +{% extends "sco_page.j2" %} + +{% block app_content %} + +<h2>Répartition des groupes de {{partition.partition_name}}</h2> + +<div class="scobox"> +<div class="scobox-title">Semestre {{sco.formsemestre.titre_annee()}}</div> + +<div class="help space-after-24 space-before-24"> +💡Les groupes existants dans cette partition seront <b>effacés</b> et remplacés +par ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau des +groupes (en utilisant la dernière moyenne générale disponible pour chaque +étudiant) et de maximiser la mixité de chaque groupe. +</div> + +{{form_html|safe}} + +</div> + +<div> +<a class="stdlink" href="{{ + url_for('scolar.partition_editor', + scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id) +}}">Retour à l'éditeur de partitions</a> +</div> + +{% endblock %} diff --git a/app/templates/scolar/partition_editor.j2 b/app/templates/scolar/partition_editor.j2 index 70ad81bb83a5ed2196b9e5cafa1055f90404f9c3..e334cffdc22ec7c9516cce3f76d619b40d28ce3a 100644 --- a/app/templates/scolar/partition_editor.j2 +++ b/app/templates/scolar/partition_editor.j2 @@ -52,7 +52,23 @@ <details> <summary>Outils d'affectation</summary> <div class="autoAffectation"> - <a href="students_groups_auto_assignment?formsemestre_id={{formsemestre.id}}"><svg + <svg + xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" + stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> + <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3" /> + </svg> Répartir les étudiants dans les groupes + <div id="partitionsARepartir"> + {# peuplé en js par listeGroupesRepartition() + <div class="partition-name"><a href="">TD</a></div> + <div class="partition-name"><a href="">groupes de projets</a></div> + #} + </div> + </div> + <div class="autoAffectation"> + <a href="{{ + url_for('scolar.students_groups_auto_assignment', + scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id + )}}"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3" /> @@ -134,6 +150,7 @@ processDatas(partitions, etudiants); processEvents(); listeGroupesAutoaffectation(); + listeGroupesRepartition(); document.querySelector("body").classList.add("loaded"); document.querySelector('.wait').style.display = "none"; @@ -354,6 +371,19 @@ } + function listeGroupesRepartition() { // peuple la liste des partitions pour répartition auto + let output = ''; + document.querySelectorAll('#zonePartitions .filtres>[data-idpartition]').forEach(partition => { + if (!partition.classList.contains('nonEditable')) { + output += ` + <div class="partition-name"> + <a href="groups_auto_repartition/${partition.dataset.idpartition}">${partition.children[0].children[1].innerText} + </a></div>`; + } + }); + document.querySelector("#partitionsARepartir").innerHTML = output; + } + /******************************/ /* Gestionnaire d'événements */ /******************************/ @@ -677,6 +707,7 @@ </div>`; listeGroupesAutoaffectation(); + listeGroupesRepartition(); }) .catch(error => { document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (1).</h2>"; @@ -730,6 +761,7 @@ // divGroupe.querySelector(".modif").click(); listeGroupesAutoaffectation(); + listeGroupesRepartition(); }) .catch(error => { document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (4).</h2>"; @@ -811,6 +843,7 @@ document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (2).</h2>"; } listeGroupesAutoaffectation(); + listeGroupesRepartition(); }) .catch(error => { document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (3).</h2>"; @@ -924,6 +957,7 @@ document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>"; } listeGroupesAutoaffectation(); + listeGroupesRepartition(); }) } @@ -1038,6 +1072,7 @@ document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>"; } listeGroupesAutoaffectation(); + listeGroupesRepartition(); }) .catch(error => { document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (7).</h2>"; diff --git a/app/templates/scolar/students_groups_auto_assignment.j2 b/app/templates/scolar/students_groups_auto_assignment.j2 index 84e5914f72fa1abad3f1c167a276977fca3a48a1..a8630aaac599ccb6e6ae7b7e2af988dca7a07ef7 100644 --- a/app/templates/scolar/students_groups_auto_assignment.j2 +++ b/app/templates/scolar/students_groups_auto_assignment.j2 @@ -1,3 +1,7 @@ +{%- extends 'sco_page.j2' -%} + +{% block styles %} +{{super()}} <style> .wait { position: fixed; @@ -212,7 +216,10 @@ z-index: 1; } </style> +{% endblock %} + +{% block app_content %} <main class="moitemoite"> <div class="wait"></div> <section> @@ -249,6 +256,11 @@ </main> +{% endblock %} + +{% block scripts %} +{{ super() }} + <script src="{{scu.STATIC_DIR}}/libjs/xlsx-populate-1.21.0.min.js"></script> <script> /************************/ @@ -858,3 +870,4 @@ } </script> +{% endblock %} \ No newline at end of file diff --git a/app/views/scolar.py b/app/views/scolar.py index 424500d24131f1fad6d34f680e56e5368763fa52..28ead66f6205555536bb065e2c1b8e327c6639bc 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1003,15 +1003,12 @@ def partition_editor(formsemestre_id: int, edit_partition=False): @scodoc7func def students_groups_auto_assignment(formsemestre_id: int): """Répartition auto des groupes""" - formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) - H = [ - render_template( - "scolar/students_groups_auto_assignment.j2", - formsemestre=formsemestre, - ), - ] + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) return render_template( - "sco_page.j2", title="Répartition des groupes", content="\n".join(H) + "scolar/students_groups_auto_assignment.j2", + formsemestre=formsemestre, + title="Répartition des groupes", + sco=ScoData(formsemestre=formsemestre), ) diff --git a/sco_version.py b/sco_version.py index 67bc67d63ff850669a90ace21af2b702a83a5812..5f1901f3340a4ae79476e2cdd23cc63982ca391f 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.7.13" +SCOVERSION = "9.7.14" SCONAME = "ScoDoc"