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"] resp = u["prenomnom"]
nomcomplet = u["nomcomplet"] nomcomplet = u["nomcomplet"]
can_edit = sco_permissions_check.can_edit_notes( can_edit = sco_permissions_check.can_edit_notes(
current_user, moduleimpl_id, allow_ens=False , moduleimpl_id, allow_ens=False
) )
link = ( link = (

View File

@ -111,7 +111,7 @@ class PlacementForm(FlaskForm):
) )
submit = SubmitField("OK") 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}) eval_data = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not eval_data: if not eval_data:
raise ScoValueError("invalid evaluation_id") raise ScoValueError("invalid evaluation_id")
@ -146,7 +146,7 @@ def placement_eval_selectetuds(evaluation_id, REQUEST=None):
request.form, request.form,
data={"evaluation_id": int(evaluation_id), "groups": PlacementForm.TOUS}, 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(): if form.validate_on_submit():
exec_placement(form) exec_placement(form)
return flask.redirect(titi()) 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(sco_evaluations.evaluation_describe(evaluation_id=evaluation_id))
H.append("<h3>Placement et émargement des étudiants</h3>") H.append("<h3>Placement et émargement des étudiants</h3>")
H.append(render_template("forms/placement.html", form=form)) 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() F = html_sco_header.sco_footer()
return "\n".join(H) + "<p>" + F return "\n".join(H) + "<p>" + F
@ -323,17 +302,17 @@ def do_placement_selectetuds():
for x in group_ids for x in group_ids
] ]
query = ( query = (
"evaluation_id=%s&placement_method=%s&teachers=%s&building=%s&room=%s&columns=%s&numbering=%s&" "evaluation_id=%s&placement_method=%s&teachers=%s&building=%s&room=%s&columns=%s&numbering=%s&"
% ( % (
evaluation_id, evaluation_id,
placement_method, placement_method,
teachers, teachers,
building, building,
room, room,
columns, columns,
numbering, numbering,
) )
+ "&".join(gs) + "&".join(gs)
) )
return flask.redirect(scu.NotesURL() + "/do_placement?" + query) return flask.redirect(scu.NotesURL() + "/do_placement?" + query)
else: else:
@ -342,45 +321,78 @@ def do_placement_selectetuds():
) )
def do_placement(REQUEST): def exec_placement(form):
"""
Choisi le placement
"""
authuser = REQUEST.AUTHENTICATED_USER
authusername = str(authuser)
try: try:
evaluation_id = int(REQUEST.form["evaluation_id"]) evaluation_id = int(form["evaluation_id"].data)
except: except:
raise ScoValueError( raise ScoValueError(
"Formulaire incomplet ! Vous avez sans doute attendu trop longtemps, veuillez vous reconnecter. Si le problème persiste, contacter l'administrateur. Merci." "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 # Check access
# (admin, respformation, and responsable_id) # (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 ( return (
"<h2>Génération du placement impossible pour %s</h2>" % authusername "<h2>Génération du placement impossible pour %s</h2>" % authusername
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous + """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
avez l'autorisation d'effectuer cette opération)</p> avez l'autorisation d'effectuer cette opération)</p>
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p> <p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
""" """
% E["moduleimpl_id"] % 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() cnx = ndb.GetDBConnexion()
# Infos transmises # Infos transmises
placement_method = REQUEST.form["placement_method"] evaluation_id = form["evaluation_id"].data
teachers = REQUEST.form["teachers"] etiquetage = form["etiquetage"].data
building = REQUEST.form["building"] teachers = form["surveillants"].data
room = REQUEST.form["room"] building = form["batiment"].data
columns = REQUEST.form["columns"] room = form["salle"].data
numbering = REQUEST.form["numbering"] nb_rangs = form["nb_rangs"].data
group_ids = form["groups"].data
# Construit liste des etudiants # Construit liste des etudiants
group_ids = REQUEST.form.get("group_ids", [])
groups = sco_groups.listgroups(group_ids) groups = sco_groups.listgroups(group_ids)
gr_title_filename = sco_groups.listgroups_filename(groups) gr_title_filename = sco_groups.listgroups_filename(groups)
# gr_title = sco_groups.listgroups_abbrev(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 if None in [g["group_name"] for g in groups]: # tous les etudiants
getallstudents = True getallstudents = True
gr_title_filename = "tous" gr_title_filename = "tous"
@ -389,47 +401,70 @@ def do_placement(REQUEST):
etudids = sco_groups.do_evaluation_listeetuds_groups( etudids = sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, groups, getallstudents=getallstudents, include_dems=True 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) listetud = [] # liste de couples (nom,prenom)
for etudid in etudids: for etudid in etudids:
# infos identite etudiant (xxx sous-optimal: 1/select par etudiant) # infos identite etudiant (xxx sous-optimal: 1/select par etudiant)
ident = sco_etud.etudident_list(cnx, {"etudid": etudid})[0] ident = sco_etud.etudident_list(cnx, {"etudid": etudid})[0]
# infos inscription # infos inscription
inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
{"etudid": etudid, "formsemestre_id": M["formsemestre_id"]} {"etudid": etudid, "formsemestre_id": moduleimpl_data["formsemestre_id"]}
)[0] )[0]
if inscr["etat"] != "D": if inscr["etat"] != "D":
nom = ident["nom"].upper() nom = ident["nom"].upper()
prenom = ident["prenom"].lower().capitalize() prenom = ident["prenom"].lower().capitalize()
listetud.append((nom, prenom)) listetud.append((nom, prenom))
random.shuffle(listetud) 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}" filename = f"placement_{evalname}_{gr_title_filename}{scu.XLSX_SUFFIX}"
xls = _excel_feuille_placement( 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) return sco_excel.send_excel_file(REQUEST, xls, filename)
else: else:
@ -495,8 +530,8 @@ def do_placement(REQUEST):
rows=rows, rows=rows,
filename=filename, filename=filename,
origin="Généré par %s le " % sco_version.SCONAME origin="Généré par %s le " % sco_version.SCONAME
+ scu.timedate_human_repr() + scu.timedate_human_repr()
+ "", + "",
pdf_title=pdf_title, pdf_title=pdf_title,
# pdf_shorttitle = '', # pdf_shorttitle = '',
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]), preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
@ -669,16 +704,16 @@ def _titres(ws, description, evaluation, building, room, styles):
def _feuille0( def _feuille0(
ws0, ws0,
description, description,
evaluation, evaluation,
styles, styles,
numbering, numbering,
listetud, listetud,
nbcolumns, nbcolumns,
building, building,
room, room,
space, space,
): ):
_titres(ws0, description, evaluation, building, room, styles) _titres(ws0, description, evaluation, building, room, styles)
# entetes colonnes - feuille0 # entetes colonnes - feuille0
@ -766,16 +801,16 @@ def _next_page(ws):
def _feuille1( def _feuille1(
ws, ws,
description, description,
evaluation, evaluation,
styles, styles,
numbering, numbering,
maxlines, maxlines,
nbcolumns, nbcolumns,
building, building,
room, room,
listetud, listetud,
): ):
# etudiants - feuille1 # etudiants - feuille1
# structuration: # structuration:
@ -832,15 +867,15 @@ def _feuille1(
def _excel_feuille_placement( def _excel_feuille_placement(
evaluation, evaluation,
description, description,
listetud, listetud,
columns, columns,
space, space,
maxlines, maxlines,
building, building,
room, room,
numbering, numbering,
): ):
"""Genere feuille excel pour placement des etudiants. """Genere feuille excel pour placement des etudiants.
E: evaluation (dict) E: evaluation (dict)
@ -862,7 +897,7 @@ def _excel_feuille_placement(
ws0.set_column_dimension_width("A", 750 * column_width_ratio) ws0.set_column_dimension_width("A", 750 * column_width_ratio)
for col in range(nbcolumns): for col in range(nbcolumns):
ws0.set_column_dimension_width( ws0.set_column_dimension_width(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col + 1 : col + 2], width "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col + 1: col + 2], width
) )
SheetName1 = "Positions" SheetName1 = "Positions"

View File

@ -3,7 +3,7 @@
{% macro render_field(field) %} {% macro render_field(field) %}
<tr> <tr>
<td class="wtf-field">{{ field.label }}</td> <td class="wtf-field">{{ field.label }}</td>
<td class="wtf-field">{{ field(**kwargs)|safe }} <td class="wtf-field">{{ field()|safe }}
{% if field.errors %} {% if field.errors %}
<ul class=errors> <ul class=errors>
{% for error in field.errors %} {% for error in field.errors %}
@ -18,6 +18,7 @@
<div class="saisienote_etape1 form_placement"> <div class="saisienote_etape1 form_placement">
<form method=post> <form method=post>
{{ form.evaluation_id }} {{ form.evaluation_id }}
{{ form.csrf_token }}
<table class="tf"> <table class="tf">
<tbody> <tbody>
{{ render_field(form.surveillants) }} {{ render_field(form.surveillants) }}
@ -51,6 +52,25 @@
<input id="gr_cancel" type=submit value="Annuler"> <input id="gr_cancel" type=submit value="Annuler">
</script> </script>
</form> </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> </div>

View File

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