1
0
forked from ScoDoc/ScoDoc

before refactoring

This commit is contained in:
Jean-Marie Place 2021-09-12 07:04:05 +02:00
parent ed07e42222
commit 7f63ab222b
2 changed files with 197 additions and 373 deletions

View File

@ -35,6 +35,7 @@ from enum import Enum
from tempfile import NamedTemporaryFile
import openpyxl.utils.datetime
from flask import make_response
from openpyxl import Workbook, load_workbook
from openpyxl.cell import WriteOnlyCell
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
@ -64,14 +65,21 @@ class COLORS(Enum):
LIGHT_YELLOW = "FFFFFF99"
def send_from_flask(data, filename, mime=scu.XLSX_MIMETYPE):
scu.make_filename(filename)
response = make_response(data)
response.headers['Content-Type'] = mime
response.headers['Content-Disposition'] = 'attachment; filename="%s"' % filename
def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE):
"""publication fichier.
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
"""
filename = (
scu.unescape_html(scu.suppress_accents(filename))
.replace("&", "")
.replace(" ", "_")
.replace("&", "")
.replace(" ", "_")
)
request.RESPONSE.setHeader("content-type", mime)
request.RESPONSE.setHeader(
@ -136,16 +144,16 @@ class ScoExcelBook:
def excel_make_style(
bold=False,
italic=False,
outline=False,
color: COLORS = COLORS.BLACK,
bgcolor: COLORS = None,
halign=None,
valign=None,
number_format=None,
font_name="Arial",
size=10,
bold=False,
italic=False,
outline=False,
color: COLORS = COLORS.BLACK,
bgcolor: COLORS = None,
halign=None,
valign=None,
number_format=None,
font_name="Arial",
size=10,
):
"""Contruit un style.
Les couleurs peuvent être spécfiées soit par une valeur de COLORS,
@ -228,12 +236,12 @@ class ScoExcelSheet:
self.row_dimensions = {}
def excel_make_composite_style(
self,
alignment=None,
border=None,
fill=None,
number_format=None,
font=None,
self,
alignment=None,
border=None,
fill=None,
number_format=None,
font=None,
):
style = {}
if font is not None:
@ -374,7 +382,7 @@ class ScoExcelSheet:
def excel_simple_table(
titles=None, lines=None, sheet_name=b"feuille", titles_styles=None, comments=None
titles=None, lines=None, sheet_name=b"feuille", titles_styles=None, comments=None
):
"""Export simple type 'CSV': 1ere ligne en gras, le reste tel quel"""
ws = ScoExcelSheet(sheet_name)
@ -643,13 +651,13 @@ def _excel_to_list(filelike): # we may need 'encoding' argument ?
def excel_feuille_listeappel(
sem,
groupname,
lines,
partitions=None,
with_codes=False,
with_paiement=False,
server_name=None,
sem,
groupname,
lines,
partitions=None,
with_codes=False,
with_paiement=False,
server_name=None,
):
"""generation feuille appel"""
if partitions is None:
@ -751,7 +759,7 @@ def excel_feuille_listeappel(
for t in lines:
n += 1
nomprenom = (
t["civilite_str"] + " " + t["nom"] + " " + t["prenom"].lower().capitalize()
t["civilite_str"] + " " + t["nom"] + " " + t["prenom"].lower().capitalize()
)
style_nom = style2t3
if with_paiement:

View File

@ -35,9 +35,8 @@ import random
import time
from copy import copy
import flask
import wtforms.validators
from flask import request, render_template
from flask import request, render_template, url_for
from flask_login import current_user
from werkzeug import Response
from flask_wtf import FlaskForm
@ -141,273 +140,7 @@ class PlacementForm(FlaskForm):
self.groups.choices = choices
def placement_eval_selectetuds(evaluation_id):
"""Creation de l'écran de placement"""
form = PlacementForm(
request.form,
data={"evaluation_id": int(evaluation_id), "groups": PlacementForm.TOUS},
)
form.set_evaluation_infos(evaluation_id)
if form.validate_on_submit():
exec_placement(form) # calcul et generation du fichier
return flask.redirect(titi())
H = [html_sco_header.sco_header(init_jquery_ui=True)]
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))
F = html_sco_header.sco_footer()
return "\n".join(H) + "<p>" + F
# def do_placement_selectetuds():
# """
# Choisi les étudiants et les infos sur la salle pour leur placement.
# """
# # M = sco_moduleimpl.do_moduleimpl_list( moduleimpl_id=E["moduleimpl_id"])[0]
# # description de l'evaluation
# H = [
# sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
# "<h3>Placement et émargement des étudiants</h3>",
# ]
# #
# descr = [
# ("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
# (
# "placement_method",
# {
# "input_type": "radio",
# "default": "xls",
# "allow_null": False,
# "allowed_values": ["pdf", "xls"],
# "labels": ["fichier pdf", "fichier xls"],
# "title": "Format de fichier :",
# },
# ),
# ("teachers", {"size": 25, "title": "Surveillants :"}),
# ("building", {"size": 25, "title": "Batiment :"}),
# ("room", {"size": 10, "title": "Salle :"}),
# (
# "columns",
# {
# "input_type": "radio",
# "default": "5",
# "allow_null": False,
# "allowed_values": ["3", "4", "5", "6", "7", "8"],
# "labels": [
# "3 colonnes",
# "4 colonnes",
# "5 colonnes",
# "6 colonnes",
# "7 colonnes",
# "8 colonnes",
# ],
# "title": "Nombre de colonnes :",
# },
# ),
# (
# "numbering",
# {
# "input_type": "radio",
# "default": "coordinate",
# "allow_null": False,
# "allowed_values": ["continuous", "coordinate"],
# "labels": ["continue", "coordonnées"],
# "title": "Numérotation :",
# },
# ),
# ]
# if no_groups:
# submitbuttonattributes = []
# descr += [
# (
# "group_ids",
# {
# "default": [
# g["group_id"] # pylint: disable=invalid-sequence-index
# for g in groups
# ],
# "input_type": "hidden",
# "type": "list",
# },
# )
# ]
# else:
# descr += [
# (
# "group_ids",
# {
# "input_type": "checkbox",
# "title": "Choix groupe(s) d'étudiants :",
# "allowed_values": grnams,
# "labels": grlabs,
# "attributes": ['onchange="gr_change(this);"'],
# },
# )
# ]
#
# if not ("group_ids" in REQUEST.form and REQUEST.form["group_ids"]):
# submitbuttonattributes = ['disabled="1"']
# else:
# submitbuttonattributes = [] # groupe(s) preselectionnés
# H.append(
# # JS pour desactiver le bouton OK si aucun groupe selectionné
# """<script type="text/javascript">
# function gr_change(e) {
# var boxes = document.getElementsByName("group_ids:list");
# var nbchecked = 0;
# for (var i=0; i < boxes.length; i++) {
# if (boxes[i].checked)
# nbchecked++;
# }
# if (nbchecked > 0) {
# document.getElementsByName('gr_submit')[0].disabled=false;
# } else {
# document.getElementsByName('gr_submit')[0].disabled=true;
# }
# }
# </script>
# """
# )
#
# tf = TrivialFormulator(
# REQUEST.URL0,
# REQUEST.form,
# descr,
# cancelbutton="Annuler",
# submitbuttonattributes=submitbuttonattributes,
# submitlabel="OK",
# formid="gr",
# )
# if tf[0] == 0:
# # H.append( """<div class="saisienote_etape1">
# # <span class="titredivplacementetudiants">Choix du groupe et de la localisation</span>
# # """)
# H.append("""<div class="saisienote_etape1">""")
# return "\n".join(H) + "\n" + tf[1] + "\n</div>"
# elif tf[0] == -1:
# return flask.redirect(
# "%s/Notes/moduleimpl_status?moduleimpl_id=%s"
# % (scu.ScoURL(), E["moduleimpl_id"])
# )
# else:
# placement_method = tf[2]["placement_method"]
# teachers = tf[2]["teachers"]
# building = tf[2]["building"]
# room = tf[2]["room"]
# group_ids = tf[2]["group_ids"]
# columns = tf[2]["columns"]
# numbering = tf[2]["numbering"]
# if columns in ("3", "4", "5", "6", "7", "8"):
# gs = [
# ("group_ids%3Alist=" + six.moves.urllib.parse.quote_plus(x))
# for x in group_ids
# ]
# query = (
# "evaluation_id=%s&placement_method=%s&teachers=%s&building=%s&room=%s&columns=%s&numbering=%s&"
# % (
# evaluation_id,
# placement_method,
# teachers,
# building,
# room,
# columns,
# numbering,
# )
# + "&".join(gs)
# )
# return flask.redirect(scu.NotesURL() + "/do_placement?" + query)
# else:
# raise ValueError(
# "invalid placement_method (%s)" % tf[2]["placement_method"]
# )
def exec_placement(form):
"""Calcul et génération du fichier sur la base des données du formulaire"""
d = {
"evaluation_id": form["evaluation_id"].data,
"etiquetage": form["etiquetage"].data,
"surveillants": form["surveillants"].data,
"batiment": form["batiment"].data,
"salle": form["salle"].data,
"nb_rangs": form["nb_rangs"].data,
"groups": form["groups"].data,
}
breakpoint()
d["eval_data"] = sco_evaluations.do_evaluation_list(d)[0]
# Check access (admin, respformation, and responsable_id)
d["current_user"] = current_user
d["moduleimpl_id"] = d["eval_data"]["moduleimpl_id"]
if not sco_permissions_check.can_edit_notes(d["current_user"], d["moduleimpl_id"]):
return (
"""<h2>Génération du placement impossible pour %(current_user)s</h2>
<p>(vérifiez que le semestre n'est pas verrouillé et que vous
avez l'autorisation d'effectuer cette opération)</p>
<p><a href="moduleimpl_status?moduleimpl_id=%(module_id)s">Continuer</a></p>
"""
% d
)
d["cnx"] = ndb.GetDBConnexion()
d["plan"] = repartition(d)
d["gr_title_filename"] = sco_groups.listgroups_filename(d['groups'])
# gr_title = sco_groups.listgroups_abbrev(d['groups'])
d["moduleimpl_data"] = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=d["moduleimpl_id"])
d["Mod"] = sco_edit_module.do_module_list(args={"module_id": d["moduleimpl_id"]})[0]
d["sem"] = sco_formsemestre.get_formsemestre(d["moduleimpl_data"]["formsemestre_id"])
d["evalname"] = "%s-%s" % (d["Mod"]["code"], ndb.DateDMYtoISO(eval_data["jour"]))
if d["eval_data"]["description"]:
d["evaltitre"] = d["eval_data"]["description"]
else:
d["evaltitre"] = "évaluation du %s" % eval_data["jour"]
d["desceval"] = [
["%s" % d["sem"]["titreannee"]],
["Module : %s - %s" % (d["Mod"]["code"], d["Mod"]["abbrev"])],
["Surveillants : %(surveillant)s" % d],
["Batiment : %(batiment)s - Salle : %(salle)s" % d],
["Controle : %s (coef. %g)" % (d["evaltitre"], d["eval_data"]["coefficient"])],
] # une liste de liste de chaines: description de l'evaluation
if form["file_format"].data == "xls":
production_xls(d)
else:
production_pdf(d)
def repartition(d):
"""
Calcule le placement. retourne une liste de couples ((nom, prenom), position)
"""
# Construit liste des etudiants
groups = sco_groups.listgroups(d["groups"])
d["listetud"] = build_listetud(d)
return affectation_places(d)
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"
else:
getallstudents = False
etudids = sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, groups, getallstudents=getallstudents, include_dems=True
)
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": 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
class DistributeurContinu:
class _DistributeurContinu:
"""Distribue les places selon un ordre numérique."""
def __init(self):
@ -419,7 +152,7 @@ class DistributeurContinu:
return retour
class Distributeur2D:
class _Distributeur2D:
"""Distribue les places selon des coordonnées sur nb_rangs."""
def __init__(self, nb_rangs):
@ -436,19 +169,140 @@ class Distributeur2D:
return retour
def affectation_places(d):
def placement_eval_selectetuds(evaluation_id):
"""Creation de l'écran de placement"""
form = PlacementForm(
request.form,
data={"evaluation_id": int(evaluation_id), "groups": PlacementForm.TOUS},
)
form.set_evaluation_infos(evaluation_id)
if form.validate_on_submit():
return _exec_placement(form) # calcul et generation du fichier
# return flask.redirect(url_for("scodoc.index"))
H = [html_sco_header.sco_header(init_jquery_ui=True)]
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))
F = html_sco_header.sco_footer()
return "\n".join(H) + "<p>" + F
def _exec_placement(form):
"""Calcul et génération du fichier sur la base des données du formulaire"""
d = {
"evaluation_id": form["evaluation_id"].data,
"etiquetage": form["etiquetage"].data,
"surveillants": form["surveillants"].data,
"batiment": form["batiment"].data,
"salle": form["salle"].data,
"nb_rangs": form["nb_rangs"].data,
"groups_ids": form["groups"].data,
}
d["eval_data"] = sco_evaluations.do_evaluation_list(
{"evaluation_id": d["evaluation_id"]}
)[0]
# Check access (admin, respformation, and responsable_id)
d["current_user"] = current_user
d["moduleimpl_id"] = d["eval_data"]["moduleimpl_id"]
if not sco_permissions_check.can_edit_notes(d["current_user"], d["moduleimpl_id"]):
return """<h2>Génération du placement impossible pour %s</h2>
<p>(vérifiez que le semestre n'est pas verrouillé et que vous
avez l'autorisation d'effectuer cette opération)</p>
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
""" % (
d["current_user"].user_name,
d["module_id"],
)
d["cnx"] = ndb.GetDBConnexion()
d["groups"] = sco_groups.listgroups(d["groups_ids"])
d["gr_title_filename"] = sco_groups.listgroups_filename(d["groups"])
# gr_title = sco_groups.listgroups_abbrev(d['groups'])
d["moduleimpl_data"] = sco_moduleimpl.do_moduleimpl_list(
moduleimpl_id=d["moduleimpl_id"]
)[0]
d["Mod"] = sco_edit_module.do_module_list(
args={"module_id": d["moduleimpl_data"]["module_id"]}
)[0]
d["sem"] = sco_formsemestre.get_formsemestre(
d["moduleimpl_data"]["formsemestre_id"]
)
d["evalname"] = "%s-%s" % (
d["Mod"]["code"],
ndb.DateDMYtoISO(d["eval_data"]["jour"]),
)
if d["eval_data"]["description"]:
d["evaltitre"] = d["eval_data"]["description"]
else:
d["evaltitre"] = "évaluation du %s" % d["eval_data"]["jour"]
d["desceval"] = [
["%s" % d["sem"]["titreannee"]],
["Module : %s - %s" % (d["Mod"]["code"], d["Mod"]["abbrev"])],
["Surveillants : %(surveillants)s" % d],
["Batiment : %(batiment)s - Salle : %(salle)s" % d],
["Controle : %s (coef. %g)" % (d["evaltitre"], d["eval_data"]["coefficient"])],
] # une liste de liste de chaines: description de l'evaluation
d["plan"] = _repartition(d)
if form["file_format"].data == "xls":
return _production_xls(d)
else:
return _production_pdf(d)
def _repartition(d):
"""
Calcule le placement. retourne une liste de couples ((nom, prenom), position)
"""
# Construit liste des etudiants
d["groups"] = sco_groups.listgroups(d["groups_ids"])
d["listetud"] = _build_listetud(d)
return _affectation_places(d)
def _build_listetud(d):
if None in [g["group_name"] for g in d["groups"]]: # tous les etudiants
getallstudents = True
gr_title_filename = "tous"
else:
getallstudents = False
etudids = sco_groups.do_evaluation_listeetuds_groups(
d["evaluation_id"],
d["groups"],
getallstudents=getallstudents,
include_dems=True,
)
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(d["cnx"], {"etudid": etudid})[0]
# infos inscription
inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
{
"etudid": etudid,
"formsemestre_id": d["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
def _affectation_places(d):
plan = []
if d["etiquetage"] == "continu":
distributeur = DistributeurContinu()
distributeur = _DistributeurContinu()
else:
distributeur = Distributeur2D(d["nb_rangs"])
distributeur = _Distributeur2D(d["nb_rangs"])
for etud in d["listetud"]:
plan.append((etud, distributeur.suivant()))
return plan
def production_xls(d):
filename = f"placement_{evalname}_{gr_title_filename}{scu.XLSX_SUFFIX}"
def _production_xls(d):
breakpoint()
filename = scu.make_filename("placement_%(evalname)s_%(gr_title_filename)s{scu.XLSX_SUFFIX}" % d)
xls = _excel_feuille_placement(
d["eval_data"],
d["desceval"],
@ -458,13 +312,13 @@ def production_xls(d):
d["salle"],
d["etiquetage"],
)
return sco_excel.send_excel_file(REQUEST, xls, filename)
return sco_excel.send_from_flask(xls, filename)
def production_pdf(d):
def _production_pdf(d):
pdf_title = d["desceval"]
pdf_title += (
"Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" % d["eval_data"]
"Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" % d["eval_data"]
)
filename = "placement_%(evalname)s_%(gr_title_filename)s.pdf" % d
@ -517,8 +371,8 @@ def production_pdf(d):
rows=rows,
filename=filename,
origin="Généré par %s le " % sco_version.SCONAME
+ scu.timedate_human_repr()
+ "",
+ scu.timedate_human_repr()
+ "",
pdf_title=pdf_title,
# pdf_shorttitle = '',
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
@ -528,42 +382,6 @@ def production_pdf(d):
return t
def placement_eval_selectetuds_old(evaluation_id, REQUEST=None):
"""Dialogue placement etudiants: choix methode et localisation"""
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")
theeval = evals[0]
if theeval["description"]:
page_title = 'Placement "%s"' % theeval["description"]
else:
page_title = "Placement des étudiants"
H = [html_sco_header.sco_header(page_title=page_title)]
formid = "placementfile"
if not REQUEST.form.get("%s-submitted" % formid, False):
# not submitted, choix groupe
r = do_placement_selectetuds()
if r:
if isinstance(r, str):
H.append(r)
elif isinstance(r, Response):
H.append(r.get_data().decode("utf-8"))
H.append(
"""<h3>Explications</h3> <ul> <li>Choisir le format du fichier résultat :</li> <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</li> <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> </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 :</li> <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> </ul> """
)
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def _one_header(ws, numbering, styles):
cells = []
if numbering == "coordinate":
@ -691,16 +509,16 @@ def _titres(ws, description, evaluation, building, room, styles):
def _feuille0(
ws0,
description,
evaluation,
styles,
numbering,
listetud,
nbcolumns,
building,
room,
space,
ws0,
description,
evaluation,
styles,
numbering,
listetud,
nbcolumns,
building,
room,
space,
):
_titres(ws0, description, evaluation, building, room, styles)
# entetes colonnes - feuille0
@ -788,16 +606,16 @@ def _next_page(ws):
def _feuille1(
ws,
description,
evaluation,
styles,
numbering,
maxlines,
nbcolumns,
building,
room,
listetud,
ws,
description,
evaluation,
styles,
numbering,
maxlines,
nbcolumns,
building,
room,
listetud,
):
# etudiants - feuille1
# structuration:
@ -854,15 +672,13 @@ def _feuille1(
def _excel_feuille_placement(
evaluation,
description,
listetud,
columns,
space,
maxlines,
building,
room,
numbering,
evaluation,
description,
listetud,
columns,
building,
room,
numbering,
):
"""Genere feuille excel pour placement des etudiants.
E: evaluation (dict)
@ -887,7 +703,7 @@ def _excel_feuille_placement(
ws0.set_column_dimension_width("A", 750 * column_width_ratio)
for col in range(nbcolumns):
ws0.set_column_dimension_width(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col + 1 : col + 2], width
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col + 1: col + 2], width
)
SheetName1 = "Positions"