From 04a8177d12694db423ef4da32d89aa4d4c03d614 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Mon, 26 Aug 2024 14:46:28 +0200
Subject: [PATCH] =?UTF-8?q?Editeur=20de=20partitions:=20liens=20vers=20out?=
=?UTF-8?q?il=20de=20r=C3=A9partition?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/scodoc/sco_groups.py | 108 ++++++++----------
app/static/css/partition_editor.css | 20 +++-
.../formsemestre/groups_auto_repartition.j2 | 30 +++++
app/templates/scolar/partition_editor.j2 | 37 +++++-
.../scolar/students_groups_auto_assignment.j2 | 13 +++
app/views/scolar.py | 13 +--
sco_version.py | 2 +-
7 files changed, 150 insertions(+), 73 deletions(-)
create mode 100644 app/templates/formsemestre/groups_auto_repartition.j2
diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py
index c585223d1..ae9d7e1ca 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 00bbb2483..90e895bea 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 000000000..34a799b92
--- /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 70ad81bb8..e334cffdc 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 84e5914f7..a8630aaac 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 424500d24..28ead66f6 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 67bc67d63..5f1901f33 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"
--
GitLab