placement fait

This commit is contained in:
Jean-Marie Place 2021-09-11 18:33:55 +02:00
parent 37484b7fc9
commit 050e54de3e
4 changed files with 167 additions and 113 deletions

View File

@ -1046,7 +1046,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
resp = u["prenomnom"]
nomcomplet = u["nomcomplet"]
can_edit = sco_permissions_check.can_edit_notes(
current_user, moduleimpl_id, allow_ens=False
, moduleimpl_id, allow_ens=False
)
link = (

View File

@ -111,7 +111,7 @@ class PlacementForm(FlaskForm):
)
submit = SubmitField("OK")
def _set_evaluation_infos(self, evaluation_id):
def set_evaluation_infos(self, evaluation_id):
eval_data = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not eval_data:
raise ScoValueError("invalid evaluation_id")
@ -146,7 +146,7 @@ def placement_eval_selectetuds(evaluation_id, REQUEST=None):
request.form,
data={"evaluation_id": int(evaluation_id), "groups": PlacementForm.TOUS},
)
form._set_evaluation_infos(evaluation_id)
form.set_evaluation_infos(evaluation_id)
if form.validate_on_submit():
exec_placement(form)
return flask.redirect(titi())
@ -154,27 +154,6 @@ def placement_eval_selectetuds(evaluation_id, REQUEST=None):
H.append(sco_evaluations.evaluation_describe(evaluation_id=evaluation_id))
H.append("<h3>Placement et émargement des étudiants</h3>")
H.append(render_template("forms/placement.html", form=form))
H.append(
"""<h3>Explications</h3>
<ul>
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer le nombre de colonnes;</li>
<li>deux types de placements sont possibles :
<ul>
<li>continue suppose que les tables ont toutes un numéro unique;</li>
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
</ul></li>
<li>Choisir le format du fichier résultat :
<ul>
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;</li>
<li>le format xls produit un classeur avec deux onglets:
<ul>
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
peut servir de feuille d'émargement;</li>
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
</ul></li>
</ul> </li>
</ul> """
)
F = html_sco_header.sco_footer()
return "\n".join(H) + "<p>" + F
@ -342,22 +321,19 @@ def do_placement_selectetuds():
)
def do_placement(REQUEST):
"""
Choisi le placement
"""
authuser = REQUEST.AUTHENTICATED_USER
authusername = str(authuser)
def exec_placement(form):
try:
evaluation_id = int(REQUEST.form["evaluation_id"])
evaluation_id = int(form["evaluation_id"].data)
except:
raise ScoValueError(
"Formulaire incomplet ! Vous avez sans doute attendu trop longtemps, veuillez vous reconnecter. Si le problème persiste, contacter l'administrateur. Merci."
)
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
eval_data = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
# Check access
# (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(
current_user, eval_data["moduleimpl_id"]
):
return (
"<h2>Génération du placement impossible pour %s</h2>" % authusername
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
@ -366,21 +342,57 @@ def do_placement(REQUEST):
"""
% E["moduleimpl_id"]
)
plan = repartition(form, eval_data)
breakpoint()
sem_preferences = sco_preferences.SemPreferences()
space = sem_preferences.get("feuille_placement_emargement")
maxlines = sem_preferences.get("feuille_placement_positions")
def repartition(form, eval_data):
"""
Calcule le placement. retourne une liste de couples ((nom, prenom), position)
"""
cnx = ndb.GetDBConnexion()
# Infos transmises
placement_method = REQUEST.form["placement_method"]
teachers = REQUEST.form["teachers"]
building = REQUEST.form["building"]
room = REQUEST.form["room"]
columns = REQUEST.form["columns"]
numbering = REQUEST.form["numbering"]
evaluation_id = form["evaluation_id"].data
etiquetage = form["etiquetage"].data
teachers = form["surveillants"].data
building = form["batiment"].data
room = form["salle"].data
nb_rangs = form["nb_rangs"].data
group_ids = form["groups"].data
# Construit liste des etudiants
group_ids = REQUEST.form.get("group_ids", [])
groups = sco_groups.listgroups(group_ids)
gr_title_filename = sco_groups.listgroups_filename(groups)
# gr_title = sco_groups.listgroups_abbrev(groups)
moduleimpl_data = sco_moduleimpl.do_moduleimpl_list(
moduleimpl_id=eval_data["moduleimpl_id"]
)[0]
Mod = sco_edit_module.do_module_list(
args={"module_id": moduleimpl_data["module_id"]}
)[0]
sem = sco_formsemestre.get_formsemestre(moduleimpl_data["formsemestre_id"])
evalname = "%s-%s" % (Mod["code"], ndb.DateDMYtoISO(eval_data["jour"]))
if eval_data["description"]:
evaltitre = eval_data["description"]
else:
evaltitre = "évaluation du %s" % eval_data["jour"]
desceval = [
["%s" % sem["titreannee"]],
["Module : %s - %s" % (Mod["code"], Mod["abbrev"])],
["Surveillants : %s" % teachers],
["Batiment : %s - Salle : %s" % (building, room)],
["Controle : %s (coef. %g)" % (evaltitre, eval_data["coefficient"])]
] # une liste de liste de chaines: description de l'evaluation
listetud = build_listetud(cnx, groups, evaluation_id, moduleimpl_data)
return affectation_places(listetud, etiquetage, nb_rangs)
def build_listetud(cnx, groups, evaluation_id, moduleimpl_data):
if None in [g["group_name"] for g in groups]: # tous les etudiants
getallstudents = True
gr_title_filename = "tous"
@ -389,47 +401,70 @@ def do_placement(REQUEST):
etudids = sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, groups, getallstudents=getallstudents, include_dems=True
)
if not etudids:
return "<p>Aucun groupe sélectionné !</p>"
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
evalname = "%s-%s" % (Mod["code"], ndb.DateDMYtoISO(E["jour"]))
if E["description"]:
evaltitre = E["description"]
else:
evaltitre = "évaluation du %s" % E["jour"]
desceval = [] # une liste de liste de chaines: description de l'evaluation
desceval.append(["%s" % sem["titreannee"]])
desceval.append(["Module : %s - %s" % (Mod["code"], Mod["abbrev"])])
desceval.append(["Surveillants : %s" % teachers])
desceval.append(["Batiment : %s - Salle : %s" % (building, room)])
desceval.append(["Controle : %s (coef. %g)" % (evaltitre, E["coefficient"])])
listetud = [] # liste de couples (nom,prenom)
for etudid in etudids:
# infos identite etudiant (xxx sous-optimal: 1/select par etudiant)
ident = sco_etud.etudident_list(cnx, {"etudid": etudid})[0]
# infos inscription
inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
{"etudid": etudid, "formsemestre_id": M["formsemestre_id"]}
{"etudid": etudid, "formsemestre_id": moduleimpl_data["formsemestre_id"]}
)[0]
if inscr["etat"] != "D":
nom = ident["nom"].upper()
prenom = ident["prenom"].lower().capitalize()
listetud.append((nom, prenom))
random.shuffle(listetud)
return listetud
sem_preferences = sco_preferences.SemPreferences()
space = sem_preferences.get("feuille_placement_emargement")
maxlines = sem_preferences.get("feuille_placement_positions")
if placement_method == "xls":
class DistributeurContinu:
"""Distribue les places selon un ordre numérique."""
def __init(self):
self.position = 1
def suivant(self):
retour = self.position
self.position += 1
return retour
class Distributeur2D:
"""Distribue les places selon des coordonnées sur nb_rangs."""
def __init__(self, nb_rangs):
self.nb_rangs = nb_rangs
self.rang = 1
self.index = 1
def suivant(self):
retour = (self.index, self.rang)
self.rang += 1
if self.rang > self.nb_rangs:
self.rang = 1
self.index += 1
return retour
def affectation_places(listetud, etiquetage, nb_rangs=1):
affectation = []
if etiquetage == "continu":
distributeur = DistributeurContinu()
else:
distributeur = Distributeur2D(nb_rangs)
for etud in listetud:
affectation.append((etud, distributeur.suivant()))
return affectation
def production_xls(file_format, eval_dat, plan):
def production(file_format, eval_dat, plan):
if file_format == "xls":
filename = f"placement_{evalname}_{gr_title_filename}{scu.XLSX_SUFFIX}"
xls = _excel_feuille_placement(
E, desceval, listetud, columns, space, maxlines, building, room, numbering
eval_data, desceval, listetud, columns, space, maxlines, building, room, numbering
)
return sco_excel.send_excel_file(REQUEST, xls, filename)
else:

View File

@ -3,7 +3,7 @@
{% macro render_field(field) %}
<tr>
<td class="wtf-field">{{ field.label }}</td>
<td class="wtf-field">{{ field(**kwargs)|safe }}
<td class="wtf-field">{{ field()|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
@ -18,6 +18,7 @@
<div class="saisienote_etape1 form_placement">
<form method=post>
{{ form.evaluation_id }}
{{ form.csrf_token }}
<table class="tf">
<tbody>
{{ render_field(form.surveillants) }}
@ -51,6 +52,25 @@
<input id="gr_cancel" type=submit value="Annuler">
</script>
</form>
<h3>Explications</h3>
<ul>
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer le nombre de colonnes;</li>
<li>deux types de placements sont possibles :
<ul>
<li>continue suppose que les tables ont toutes un numéro unique;</li>
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
</ul></li>
<li>Choisir le format du fichier résultat :
<ul>
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;</li>
<li>le format xls produit un classeur avec deux onglets:
<ul>
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
peut servir de feuille d'émargement;</li>
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
</ul></li>
</ul> </li>
</ul>
</div>

View File

@ -1643,7 +1643,6 @@ sco_publish(
Permission.ScoEnsView,
methods=["GET", "POST"],
)
sco_publish("/do_placement", sco_placement.do_placement, Permission.ScoEnsView)
# --- Saisie des notes
sco_publish(