diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index adeeebba6a..aae6a01d6a 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -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: diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index 24795ab18e..665bf64f41 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -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("
" + 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), -# "
(vérifiez que le semestre n'est pas verrouillé et que vous -avez l'autorisation d'effectuer cette opération)
- -""" - % 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("" + 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 """
(vérifiez que le semestre n'est pas verrouillé et que vous +avez l'autorisation d'effectuer cette opération)
+ +""" % ( + 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( - """