From c8872bd2204bbc9eaf2e332c328361d5da205ef3 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sun, 12 Sep 2021 09:31:07 +0200 Subject: [PATCH] excel file returned --- app/scodoc/sco_excel.py | 2 +- app/scodoc/sco_placement.py | 979 ++++++++++++++++-------------------- 2 files changed, 433 insertions(+), 548 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index aae6a01d6a..337d33f08a 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -70,7 +70,7 @@ def send_from_flask(data, filename, mime=scu.XLSX_MIMETYPE): response = make_response(data) response.headers['Content-Type'] = mime response.headers['Content-Disposition'] = 'attachment; filename="%s"' % filename - + return response def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE): """publication fichier. diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index 665bf64f41..e39d5f7479 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -76,6 +76,9 @@ from app.scodoc.TrivialFormulator import TrivialFormulator _ = lambda x: x # sans babel _l = _ +COORD = "Coordonnées" +SEQ = "Continue" + class PlacementForm(FlaskForm): TOUS = "Tous" @@ -87,9 +90,7 @@ class PlacementForm(FlaskForm): wtforms.validators.DataRequired("indiquez le format du fichier attendu"), ], ) - surveillants = StringField( - "Surveillants", validators=[wtforms.validators.DataRequired("Test")] - ) + surveillants = StringField("Surveillants", validators=[]) batiment = StringField("Batiment") salle = StringField("Salle") nb_rangs = SelectField( @@ -97,7 +98,7 @@ class PlacementForm(FlaskForm): ) etiquetage = RadioField( "Numérotation", - choices=["Continue", "Coordonnées"], + choices=[SEQ, COORD], validators=[ wtforms.validators.DataRequired("indiquez le style de numérotation"), ], @@ -177,7 +178,17 @@ def placement_eval_selectetuds(evaluation_id): ) form.set_evaluation_infos(evaluation_id) if form.validate_on_submit(): - return _exec_placement(form) # calcul et generation du fichier + runner = PlacementRunner(form) + if not runner.check_placement(): + return ( + """

Génération du placement impossible pour %s

+

(vérifiez que le semestre n'est pas verrouillé et que vous + avez l'autorisation d'effectuer cette opération)

+

Continuer

+ """ + % runner.__dict__ + ) + return runner._exec_placement() # 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)) @@ -187,551 +198,425 @@ def placement_eval_selectetuds(evaluation_id): return "\n".join(H) + "

" + 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 """

Génération du placement impossible pour %s

-

(vérifiez que le semestre n'est pas verrouillé et que vous -avez l'autorisation d'effectuer cette opération)

-

Continuer

-""" % ( - 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"], - } +class PlacementRunner: + def __init__(self, form): + """Calcul et génération du fichier sur la base des données du formulaire""" + self.evaluation_id = form["evaluation_id"].data + self.etiquetage = form["etiquetage"].data + self.surveillants = form["surveillants"].data + self.batiment = form["batiment"].data + self.salle = form["salle"].data + self.nb_rangs = form["nb_rangs"].data + self.file_format = form["file_format"].data + self.groups_ids = form["groups"].data + self.eval_data = sco_evaluations.do_evaluation_list( + {"evaluation_id": self.evaluation_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() - else: - distributeur = _Distributeur2D(d["nb_rangs"]) - for etud in d["listetud"]: - plan.append((etud, distributeur.suivant())) - return plan - - -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"], - d["listetud"], - d["nb_rangs"], - d["batiment"], - d["salle"], - d["etiquetage"], - ) - return sco_excel.send_from_flask(xls, filename) - - -def _production_pdf(d): - pdf_title = d["desceval"] - pdf_title += ( - "Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" % d["eval_data"] - ) - - filename = "placement_%(evalname)s_%(gr_title_filename)s.pdf" % d - titles = { - "nom": "Nom", - "prenom": "Prenom", - "colonne": "Colonne", - "ligne": "Ligne", - "place": "Place", - } - nbcolumns = int(columns) - if numbering == "coordinate": - columns_ids = ["nom", "prenom", "colonne", "ligne"] - else: - columns_ids = ["nom", "prenom", "place"] - - # etudiants - line = 1 - col = 1 - orderetud = [] - for etudid in listetud: - if numbering == "coordinate": - orderetud.append((etudid[0], etudid[1], col, line)) + self.cnx = ndb.GetDBConnexion() + self.groups = sco_groups.listgroups(self.groups_ids) + self.gr_title_filename = sco_groups.listgroups_filename(self.groups) + # gr_title = sco_groups.listgroups_abbrev(d['groups']) + self.current_user = current_user + self.moduleimpl_id = self.eval_data["moduleimpl_id"] + self.moduleimpl_data = sco_moduleimpl.do_moduleimpl_list( + moduleimpl_id=self.moduleimpl_id + )[0] + self.Mod = sco_edit_module.do_module_list( + args={"module_id": self.moduleimpl_data["module_id"]} + )[0] + self.sem = sco_formsemestre.get_formsemestre( + self.moduleimpl_data["formsemestre_id"] + ) + self.evalname = "%s-%s" % ( + self.Mod["code"], + ndb.DateDMYtoISO(self.eval_data["jour"]), + ) + if self.eval_data["description"]: + self.evaltitre = self.eval_data["description"] else: - orderetud.append((etudid[0], etudid[1], col + (line - 1) * nbcolumns)) + self.evaltitre = "évaluation du %s" % self.eval_data["jour"] + self.desceval = [ + ["%s" % self.sem["titreannee"]], + ["Module : %s - %s" % (self.Mod["code"], self.Mod["abbrev"])], + ["Surveillants : %s" % self.surveillants], + ["Batiment : %(batiment)s - Salle : %(salle)s" % self.__dict__], + [ + "Controle : %s (coef. %g)" + % (self.evaltitre, self.eval_data["coefficient"]) + ], + ] # une liste de liste de chaines: description de l'evaluation - if col == nbcolumns: - col = 0 - line += 1 - col += 1 - - rows = [] - orderetud.sort() - for etudid in orderetud: - if numbering == "coordinate": - rows.append( - { - "nom": etudid[0], - "prenom": etudid[1], - "colonne": etudid[2], - "ligne": etudid[3], - } - ) - else: - rows.append({"nom": etudid[0], "prenom": etudid[1], "place": etudid[2]}) - - tab = GenTable( - titles=titles, - columns_ids=columns_ids, - rows=rows, - filename=filename, - origin="Généré par %s le " % sco_version.SCONAME - + scu.timedate_human_repr() - + "", - pdf_title=pdf_title, - # pdf_shorttitle = '', - preferences=sco_preferences.SemPreferences(M["formsemestre_id"]), - # html_generate_cells=False # la derniere ligne (moyennes) est incomplete - ) - t = tab.make_page(format="pdf", with_html_headers=False, REQUEST=REQUEST) - return t - - -def _one_header(ws, numbering, styles): - cells = [] - if numbering == "coordinate": - cells.append(ws.make_cell("Nom", styles["2bi"])) - cells.append(ws.make_cell("Prénom", styles["2bi"])) - cells.append(ws.make_cell("Colonne", styles["2bi"])) - cells.append(ws.make_cell("Ligne", styles["2bi"])) - else: - cells.append(ws.make_cell("Nom", styles["2bi"])) - cells.append(ws.make_cell("Prénom", styles["2bi"])) - cells.append(ws.make_cell("Place", styles["2bi"])) - return cells - - -def _headers(ws, numbering, styles, nb_listes): - cells = [] - for _ in range(nb_listes): - cells += _one_header(ws, numbering, styles) - cells.append(ws.make_cell("")) - ws.append_row(cells) - - -def _make_styles(ws0, ws1): - # polices - font0 = Font(name="Calibri", bold=True, size=12) - font1b = copy(font0) - font1b.size = 9 - font1i = Font(name="Arial", italic=True, size=10) - font1o = Font(name="Arial", outline=True, size=10) - font2bi = Font(name="Arial", bold=True, italic=True, size=8) - font2 = Font(name="Arial", size=10) - - # bordures - side_double = Side(border_style="double", color=COLORS.BLACK.value) - side_thin = Side(border_style="thin", color=COLORS.BLACK.value) - - # bordures - border1t = Border(left=side_double, top=side_double, right=side_double) - border1bb = Border(left=side_double, bottom=side_double, right=side_double) - border1bm = Border(left=side_double, right=side_double) - border1m = Border(left=side_double, bottom=side_thin, right=side_double) - border2m = Border(top=side_thin, bottom=side_thin) - border2r = Border(top=side_thin, bottom=side_thin, right=side_thin) - border2l = Border(left=side_thin, top=side_thin, bottom=side_thin) - border2b = Border(left=side_thin, top=side_thin, bottom=side_thin, right=side_thin) - - # alignements - align_center_center = Alignment(horizontal="center", vertical="center") - align_right_bottom = Alignment(horizontal="right", vertical="bottom") - align_left_center = Alignment(horizontal="left", vertical="center") - align_right_center = Alignment(horizontal="right", vertical="center") - - # patterns - pattern = PatternFill( - fill_type="solid", fgColor=sco_excel.COLORS.LIGHT_YELLOW.value - ) - - # styles - styles = { - "titres": sco_excel.excel_make_style(font_name="Arial", bold=True, size=12), - "1t": ws0.excel_make_composite_style( - font=font0, alignment=align_center_center, border=border1t - ), - "1m": ws0.excel_make_composite_style( - font=font1b, alignment=align_center_center, border=border1m - ), - "1bm": ws0.excel_make_composite_style( - font=font1b, alignment=align_center_center, border=border1bm - ), - "1bb": ws0.excel_make_composite_style( - font=font1o, alignment=align_right_bottom, border=border1bb - ), - "2b": ws1.excel_make_composite_style( - font=font1i, alignment=align_center_center, border=border2b - ), - "2bi": ws1.excel_make_composite_style( - font=font2bi, alignment=align_center_center, border=border2b, fill=pattern - ), - "2l": ws1.excel_make_composite_style( - font=font2, alignment=align_left_center, border=border2l - ), - "2m1": ws1.excel_make_composite_style( - font=font2, alignment=align_left_center, border=border2m - ), - "2m2": ws1.excel_make_composite_style( - font=font2, alignment=align_right_center, border=border2m - ), - "2r": ws1.excel_make_composite_style( - font=font2, alignment=align_right_center, border=border2r - ), - } - return styles - - -def _init_lines(maxlines): - return [ - [] for _ in range(maxlines) - ] # lines[no_ligne] -> liste des cellules de la ligne (no_lignes de 1..maxlines - - -def _write_lines(ws, lines): - for line in lines: - ws.append_row(line) - - -def _titres(ws, description, evaluation, building, room, styles): - dt = time.strftime("%d/%m/%Y a %Hh%M") - ws.append_single_cell_row( - "Feuille placement etudiants éditée le %s" % dt, styles["titres"] - ) - for line, desceval in enumerate(description): - if line in [1, 4, 7]: - ws.append_blank_row() - ws.append_single_cell_row(desceval[0], styles["titres"]) - ws.append_single_cell_row( - "Date : %s - Horaire : %s à %s" - % (evaluation["jour"], evaluation["heure_debut"], evaluation["heure_fin"]), - styles["titres"], - ) - ws.append_single_cell_row( - "Date : %s - Horaire : %s à %s" - % (evaluation["jour"], evaluation["heure_debut"], evaluation["heure_fin"]), - styles["titres"], - ) - - -def _feuille0( - ws0, - description, - evaluation, - styles, - numbering, - listetud, - nbcolumns, - building, - room, - space, -): - _titres(ws0, description, evaluation, building, room, styles) - # entetes colonnes - feuille0 - cells = [ws0.make_cell()] - for col in range(nbcolumns): - cells.append(ws0.make_cell("colonne %s" % (col + 1), styles["2b"])) - ws0.append_row(cells) - # etudiants - line = 1 - col = 1 - linetud = [] - orderetud = [] - placementetud = [] - for etudid in listetud: - linetud.append(etudid) - if numbering == "coordinate": - orderetud.append((etudid[0], etudid[1], col, line)) - else: - orderetud.append((etudid[0], etudid[1], col + (line - 1) * nbcolumns)) - - if col == nbcolumns: - placementetud.append(linetud) - linetud = [] - col = 0 - line += 1 - col += 1 - if len(linetud) > 0: - placementetud.append(linetud) - - # etudiants - feuille0 - place = 1 - for rang, linetud in enumerate(placementetud, start=1): - # Chaque rang est affiché sur 3 lignes xlsx (notées A, B, C) - # ligne A: le nom, ligne B: le prénom, ligne C: un espace ou la place - cells_a = [ws0.make_cell(rang, styles["2b"])] - cells_b = [ws0.make_cell("", styles["2b"])] - cells_c = [ws0.make_cell("", styles["2b"])] - row = 14 # premieère ligne de signature - for etudid in linetud: - cells_a.append(ws0.make_cell(etudid[0], styles["1t"])) - cells_b.append(ws0.make_cell(etudid[1], styles["1m"])) - if numbering == "coordinate": - cell_c = ws0.make_cell("", styles["1bb"]) - else: - cell_c = ws0.make_cell("place %s" % place, styles["1bb"]) - cells_c.append(cell_c) - ws0.set_row_dimension_height(row, space / 25) - row += 3 - place = place + 1 - if col == nbcolumns: - ws0.append_row(cells_a) - ws0.append_row(cells_b) - ws0.append_row(cells_c) - cells_a = [ws0.make_cell(rang, styles["2b"])] - cells_b = [ws0.make_cell("", styles["2b"])] - cells_c = [ws0.make_cell("", styles["2b"])] - # publication du rang final incomplet - ws0.append_row(cells_a) - ws0.append_row(cells_b) - ws0.append_row(cells_c) - ws0.set_row_dimension_height(row, space / 25) - - -def _compute_ordretud(listetud, nbcolumns, numbering): - orderetud = [] - line = 1 - col = 1 - for etudid in listetud: - if numbering == "coordinate": - orderetud.append((etudid[0], etudid[1], col, line)) - else: - orderetud.append( - (etudid[0], etudid[1], "%s" % (col + (line - 1) * nbcolumns)) - ) - col += 1 - if col > nbcolumns: - col = 1 - line += 1 - orderetud.sort() - return orderetud - - -def _next_page(ws): - pass - - -def _feuille1( - ws, - description, - evaluation, - styles, - numbering, - maxlines, - nbcolumns, - building, - room, - listetud, -): - # etudiants - feuille1 - # structuration: - # 1 page = maxlistes listes - # 1 liste = 3 ou 4 colonnes(excel) (selon numbering) et (maximum maxlines) lignes - maxlistes = 2 # nombre de listes par page - # computes excel columns widths - if numbering == "coordinate": - gabarit = [16, 18, 6, 6, 2] - else: - gabarit = [16, 18, 12, 2] - widths = [] - for _ in range(maxlistes): - widths += gabarit - ws.set_column_dimension_width(value=widths) - nb_etu_restant = len(listetud) - _titres(ws, description, evaluation, building, room, styles) - nb_listes = min( - maxlistes, nb_etu_restant // maxlines + 1 - ) # nombre de colonnes dans la page - _headers(ws, numbering, styles, nb_listes) - # construction liste alphabétique - # Affichage - lines = _init_lines(maxlines) - orderetud = _compute_ordretud(listetud, nbcolumns, numbering) - line = 0 - col = 0 - for etudid in orderetud: - # check for skip of list or page - if col > 0: # add a empty cell between lists - lines[line].append(ws.make_cell()) - lines[line].append(ws.make_cell(etudid[0], styles["2l"])) - lines[line].append(ws.make_cell(etudid[1], styles["2m1"])) - if numbering == "coordinate": - lines[line].append(ws.make_cell(etudid[2], styles["2m2"])) - lines[line].append(ws.make_cell(etudid[3], styles["2r"])) - else: - lines[line].append(ws.make_cell(etudid[2], styles["2r"])) - line = line + 1 - if line >= maxlines: # fin de liste - col = col + 1 - line = 0 - if col >= maxlistes: # fin de page - _write_lines(ws, lines) - lines = _init_lines(maxlines) - col = 0 - ws.append_blank_row() - nb_etu_restant -= maxlistes * maxlines - nb_listes = min( - maxlistes, nb_etu_restant // maxlines + 1 - ) # nombre de colonnes dans la page - _headers(ws, numbering, styles, nb_listes) - _write_lines(ws, lines) - - -def _excel_feuille_placement( - evaluation, - description, - listetud, - columns, - building, - room, - numbering, -): - """Genere feuille excel pour placement des etudiants. - E: evaluation (dict) - lines: liste de tuples - (etudid, nom, prenom, etat, groupe, val, explanation) - """ - sem_preferences = sco_preferences.SemPreferences() - space = sem_preferences.get("feuille_placement_emargement") - maxlines = sem_preferences.get("feuille_placement_positions") - nbcolumns = int(columns) - column_width_ratio = 1 / 250 # changement d unités entre pyExcelerator et openpyxl - - wb = ScoExcelBook() - - SheetName0 = "Emargement" - ws0 = wb.create_sheet(SheetName0) - # ajuste largeurs colonnes (unite inconnue, empirique) - width = 4500 * column_width_ratio - if nbcolumns > 5: - width = 22500 * column_width_ratio // nbcolumns - - 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 + def check_placement(self): + # Check access (admin, respformation, and responsable_id) + return sco_permissions_check.can_edit_notes( + self.current_user, self.moduleimpl_id ) - SheetName1 = "Positions" - ws1 = wb.create_sheet(SheetName1) + def _exec_placement(self): + self._repartition() + if self.file_format == "xls": + return self._production_xls() + else: + return self._production_pdf() - styles = _make_styles(ws0, ws1) - _feuille0( - ws0, - description, - evaluation, - styles, - numbering, - listetud, - nbcolumns, - building, - room, - space, - ) - _feuille1( - ws1, - description, - evaluation, - styles, - numbering, - maxlines, - nbcolumns, - building, - room, - listetud, - ) - return wb.generate() + def _repartition(self): + """ + Calcule le placement. retourne une liste de couples ((nom, prenom), position) + """ + # Construit liste des etudiants et les réparti + self.groups = sco_groups.listgroups(self.groups_ids) + self.listetud = self._build_listetud() + self.plan = self._affectation_places() + + def _build_listetud(self): + if None in [g["group_name"] for g in self.groups]: # tous les etudiants + getallstudents = True + gr_title_filename = "tous" + else: + getallstudents = False + etudids = sco_groups.do_evaluation_listeetuds_groups( + self.evaluation_id, + self.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(self.cnx, {"etudid": etudid})[0] + # infos inscription + inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( + { + "etudid": etudid, + "formsemestre_id": self.moduleimpl_data["formsemestre_id"], + } + )[0] + if inscr["etat"] != "D": + nom = ident["nom"].upper() + prenom = ident["prenom"].lower().capitalize() + etudid = ident["etudid"] + listetud.append((nom, prenom, etudid)) + random.shuffle(listetud) + return listetud + + def _affectation_places(self): + plan = [] + if self.etiquetage == SEQ: + distributeur = _DistributeurContinu() + else: + distributeur = _Distributeur2D(self.nb_rangs) + for etud in self.listetud: + plan.append((etud, distributeur.suivant())) + return plan + + def _production_xls(self): + filename = scu.make_filename( + "placement_%s_%s%s" + % (self.evalname, self.gr_title_filename, scu.XLSX_SUFFIX) + ) + xls = self._excel_feuille_placement() + return sco_excel.send_from_flask(xls, filename) + + def _production_pdf(self): + pdf_title = self.desceval + pdf_title += ( + "Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" + % self.eval_data + ) + + filename = "placement_%(evalname)s_%(gr_title_filename)s.pdf" % self + titles = { + "nom": "Nom", + "prenom": "Prenom", + "colonne": "Colonne", + "ligne": "Ligne", + "place": "Place", + } + nb_rangs = int(self.nb_rangs) + if self.etiquetage == COORD: + columns_ids = ["nom", "prenom", "colonne", "ligne"] + else: + columns_ids = ["nom", "prenom", "place"] + + rows = [] + for etud in sorted(plan, key=lambda etud: etud[0][0]): # sort by name + if self.etiquetage == COORD: + rows.append( + { + "nom": etud[0][0], + "prenom": etud[0][1], + "colonne": etud[1][0], + "ligne": etud[1][1], + } + ) + else: + rows.append({"nom": etud[0][0], "prenom": etud[0][1], "place": etud[1]}) + + tab = GenTable( + titles=titles, + columns_ids=columns_ids, + rows=rows, + filename=filename, + origin="Généré par %s le " % sco_version.SCONAME + + scu.timedate_human_repr() + + "", + pdf_title=pdf_title, + # pdf_shorttitle = '', + preferences=sco_preferences.SemPreferences(M["formsemestre_id"]), + # html_generate_cells=False # la derniere ligne (moyennes) est incomplete + ) + t = tab.make_page(format="pdf", with_html_headers=False, REQUEST=REQUEST) + return t + + def _one_header(self, ws): + cells = [ + ws.make_cell("Nom", self.styles["2bi"]), + ws.make_cell("Prénom", self.styles["2bi"]), + ] + if self.etiquetage == COORD: + cells.append(ws.make_cell("Colonne", self.styles["2bi"])) + cells.append(ws.make_cell("Ligne", self.styles["2bi"])) + else: + cells.append(ws.make_cell("Place", self.styles["2bi"])) + return cells + + def _headers(self, ws, nb_listes): + cells = [] + for _ in range(nb_listes): + cells += self._one_header(ws) + cells.append(ws.make_cell("")) + ws.append_row(cells) + + def _make_styles(self, ws0, ws1): + # polices + font0 = Font(name="Calibri", bold=True, size=12) + font1b = copy(font0) + font1b.size = 9 + font1i = Font(name="Arial", italic=True, size=10) + font1o = Font(name="Arial", outline=True, size=10) + font2bi = Font(name="Arial", bold=True, italic=True, size=8) + font2 = Font(name="Arial", size=10) + + # bordures + side_double = Side(border_style="double", color=COLORS.BLACK.value) + side_thin = Side(border_style="thin", color=COLORS.BLACK.value) + + # bordures + border1t = Border(left=side_double, top=side_double, right=side_double) + border1bb = Border(left=side_double, bottom=side_double, right=side_double) + border1bm = Border(left=side_double, right=side_double) + border1m = Border(left=side_double, bottom=side_thin, right=side_double) + border2m = Border(top=side_thin, bottom=side_thin) + border2r = Border(top=side_thin, bottom=side_thin, right=side_thin) + border2l = Border(left=side_thin, top=side_thin, bottom=side_thin) + border2b = Border( + left=side_thin, top=side_thin, bottom=side_thin, right=side_thin + ) + + # alignements + align_center_center = Alignment(horizontal="center", vertical="center") + align_right_bottom = Alignment(horizontal="right", vertical="bottom") + align_left_center = Alignment(horizontal="left", vertical="center") + align_right_center = Alignment(horizontal="right", vertical="center") + + # patterns + pattern = PatternFill( + fill_type="solid", fgColor=sco_excel.COLORS.LIGHT_YELLOW.value + ) + + # styles + self.styles = { + "titres": sco_excel.excel_make_style(font_name="Arial", bold=True, size=12), + "1t": ws0.excel_make_composite_style( + font=font0, alignment=align_center_center, border=border1t + ), + "1m": ws0.excel_make_composite_style( + font=font1b, alignment=align_center_center, border=border1m + ), + "1bm": ws0.excel_make_composite_style( + font=font1b, alignment=align_center_center, border=border1bm + ), + "1bb": ws0.excel_make_composite_style( + font=font1o, alignment=align_right_bottom, border=border1bb + ), + "2b": ws1.excel_make_composite_style( + font=font1i, alignment=align_center_center, border=border2b + ), + "2bi": ws1.excel_make_composite_style( + font=font2bi, + alignment=align_center_center, + border=border2b, + fill=pattern, + ), + "2l": ws1.excel_make_composite_style( + font=font2, alignment=align_left_center, border=border2l + ), + "2m1": ws1.excel_make_composite_style( + font=font2, alignment=align_left_center, border=border2m + ), + "2m2": ws1.excel_make_composite_style( + font=font2, alignment=align_right_center, border=border2m + ), + "2r": ws1.excel_make_composite_style( + font=font2, alignment=align_right_center, border=border2r + ), + } + + def _init_lines(self, maxlines): + return [ + [] for _ in range(maxlines) + ] # lines[no_ligne] -> liste des cellules de la ligne (no_lignes de 1..maxlines + + def _write_lines(self, ws, lines): + for line in lines: + ws.append_row(line) + + def _titres(self, ws): + dt = time.strftime("%d/%m/%Y a %Hh%M") + ws.append_single_cell_row( + "Feuille placement etudiants éditée le %s" % dt, self.styles["titres"] + ) + for line, desceval in enumerate(self.desceval): + if line in [1, 4, 7]: + ws.append_blank_row() + ws.append_single_cell_row(desceval[0], self.styles["titres"]) + ws.append_single_cell_row( + "Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" + % self.eval_data, + self.styles["titres"], + ) + + def _feuille0(self, ws0, space): + self._titres(ws0) + # entetes colonnes - feuille0 + cells = [ws0.make_cell()] + for col in range(self.nb_rangs): + cells.append(ws0.make_cell("colonne %s" % (col + 1), self.styles["2b"])) + ws0.append_row(cells) + + # etudiants - feuille0 + place = 1 + for rang, linetud in enumerate(self.plan, start=1): + # Chaque rang est affiché sur 3 lignes xlsx (notées A, B, C) + # ligne A: le nom, ligne B: le prénom, ligne C: un espace ou la place + cells_a = [ws0.make_cell(rang, self.styles["2b"])] + cells_b = [ws0.make_cell("", self.styles["2b"])] + cells_c = [ws0.make_cell("", self.styles["2b"])] + row = 14 # premieère ligne de signature + for etudid in linetud: + cells_a.append(ws0.make_cell(etudid[0], self.styles["1t"])) + cells_b.append(ws0.make_cell(etudid[1], self.styles["1m"])) + if self.etiquetage == COORD: + cell_c = ws0.make_cell("", self.styles["1bb"]) + else: + cell_c = ws0.make_cell("place %s" % place, self.styles["1bb"]) + cells_c.append(cell_c) + ws0.set_row_dimension_height(row, space / 25) + row += 3 + place = place + 1 + if col == self.nb_rangs: + ws0.append_row(cells_a) + ws0.append_row(cells_b) + ws0.append_row(cells_c) + cells_a = [ws0.make_cell(rang, self.styles["2b"])] + cells_b = [ws0.make_cell("", self.styles["2b"])] + cells_c = [ws0.make_cell("", self.styles["2b"])] + # publication du rang final incomplet + ws0.append_row(cells_a) + ws0.append_row(cells_b) + ws0.append_row(cells_c) + ws0.set_row_dimension_height(row, space / 25) + + def _next_page(ws): + pass + + def _feuille1(self, ws, maxlines): + # etudiants - feuille1 + # structuration: + # 1 page = maxlistes listes + # 1 liste = 3 ou 4 colonnes(excel) (selon numbering) et (maximum maxlines) lignes + maxlistes = 2 # nombre de listes par page + # computes excel columns widths + if self.etiquetage == COORD: + gabarit = [16, 18, 6, 6, 2] + else: + gabarit = [16, 18, 12, 2] + widths = [] + for _ in range(maxlistes): + widths += gabarit + ws.set_column_dimension_width(value=widths) + nb_etu_restant = len(self.listetud) + self._titres(ws) + nb_listes = min( + maxlistes, nb_etu_restant // maxlines + 1 + ) # nombre de colonnes dans la page + self._headers(ws, nb_listes) + # construction liste alphabétique + # Affichage + lines = self._init_lines(maxlines) + line = 0 + col = 0 + for etud in sorted(self.plan, key=lambda etud: etud[0][0]): + # check for skip of list or page + if col > 0: # add a empty cell between lists + lines[line].append(ws.make_cell()) + lines[line].append(ws.make_cell(etud[0][0], self.styles["2l"])) + lines[line].append(ws.make_cell(etud[0][1], self.styles["2m1"])) + if self.etiquetage == COORD: + lines[line].append(ws.make_cell(etud[1][0], self.styles["2m2"])) + lines[line].append(ws.make_cell(etud[1][1], self.styles["2r"])) + else: + lines[line].append(ws.make_cell(etud[1], self.styles["2r"])) + line = line + 1 + if line >= maxlines: # fin de liste + col = col + 1 + line = 0 + if col >= maxlistes: # fin de page + self._write_lines(ws, lines) + lines = self._init_lines(maxlines) + col = 0 + ws.append_blank_row() + nb_etu_restant -= maxlistes * maxlines + nb_listes = min( + maxlistes, nb_etu_restant // maxlines + 1 + ) # nombre de colonnes dans la page + self._headers(ws, nb_listes) + self._write_lines(ws, lines) + + def _excel_feuille_placement(self): + """Genere feuille excel pour placement des etudiants. + E: evaluation (dict) + lines: liste de tuples + (etudid, nom, prenom, etat, groupe, val, explanation) + """ + breakpoint() + sem_preferences = sco_preferences.SemPreferences() + space = sem_preferences.get("feuille_placement_emargement") + maxlines = sem_preferences.get("feuille_placement_positions") + nb_rangs = int(self.nb_rangs) + column_width_ratio = ( + 1 / 250 + ) # changement d unités entre pyExcelerator et openpyxl + + wb = ScoExcelBook() + + SheetName0 = "Emargement" + ws0 = wb.create_sheet(SheetName0) + # ajuste largeurs colonnes (unite inconnue, empirique) + width = 4500 * column_width_ratio + if nb_rangs > 5: + width = 22500 * column_width_ratio // nb_rangs + + ws0.set_column_dimension_width("A", 750 * column_width_ratio) + for col in range(nb_rangs): + ws0.set_column_dimension_width( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col + 1 : col + 2], width + ) + + SheetName1 = "Positions" + ws1 = wb.create_sheet(SheetName1) + + self._make_styles(ws0, ws1) + self._feuille0(ws0, space) + self._feuille1(ws1, maxlines) + return wb.generate()