forked from ScoDoc/ScoDoc
fin fusion
This commit is contained in:
parent
dccae56fe7
commit
770ccb4d6e
@ -40,10 +40,15 @@ from openpyxl.cell import WriteOnlyCell
|
||||
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||
from openpyxl.comments import Comment
|
||||
from openpyxl import Workbook, load_workbook
|
||||
from openpyxl.cell import WriteOnlyCell
|
||||
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc import sco_preferences
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
|
||||
@ -79,52 +84,14 @@ def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE):
|
||||
# font, border, number_format, fill, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||
|
||||
|
||||
# (stolen from xlrd)
|
||||
# Convert an Excel number (presumed to represent a date, a datetime or a time) into
|
||||
# a Python datetime.datetime
|
||||
# @param xldate The Excel number
|
||||
# @param datemode 0: 1900-based, 1: 1904-based.
|
||||
# @return a datetime.datetime object, to the nearest_second.
|
||||
# <br>Special case: if 0.0 <= xldate < 1.0, it is assumed to represent a time;
|
||||
# a datetime.time object will be returned.
|
||||
# <br>Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number"
|
||||
# is zero.
|
||||
#
|
||||
# _XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01
|
||||
#
|
||||
|
||||
|
||||
def xldate_as_datetime(xldate, datemode=0):
|
||||
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
||||
# if datemode not in (0, 1):
|
||||
# raise ValueError("invalid mode %s" % datemode)
|
||||
# if xldate == 0.00:
|
||||
# return datetime.time(0, 0, 0)
|
||||
# if xldate < 0.00:
|
||||
# raise ValueError("invalid date code %s" % xldate)
|
||||
# xldays = int(xldate)
|
||||
# frac = xldate - xldays
|
||||
# seconds = int(round(frac * 86400.0))
|
||||
# assert 0 <= seconds <= 86400
|
||||
# if seconds == 86400:
|
||||
# seconds = 0
|
||||
# xldays += 1
|
||||
# if xldays >= _XLDAYS_TOO_LARGE[datemode]:
|
||||
# raise ValueError("date too large %s" % xldate)
|
||||
#
|
||||
# if xldays == 0:
|
||||
# # second = seconds % 60; minutes = seconds // 60
|
||||
# minutes, second = divmod(seconds, 60)
|
||||
# # minute = minutes % 60; hour = minutes // 60
|
||||
# hour, minute = divmod(minutes, 60)
|
||||
# return datetime.time(hour, minute, second)
|
||||
#
|
||||
# if xldays < 61 and datemode == 0:
|
||||
# raise ValueError("ambiguous date %s" % xldate)
|
||||
#
|
||||
# return datetime.datetime.fromordinal(
|
||||
# xldays + 693594 + 1462 * datemode
|
||||
# ) + datetime.timedelta(seconds=seconds)
|
||||
|
||||
|
||||
def adjust_sheetname(sheet_name):
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
return sheet_name[:31]
|
||||
|
||||
|
||||
class ScoExcelBook:
|
||||
@ -139,13 +106,16 @@ class ScoExcelBook:
|
||||
|
||||
def __init__(self):
|
||||
self.sheets = [] # list of sheets
|
||||
self.wb = Workbook(write_only=True)
|
||||
|
||||
def create_sheet(self, sheet_name="feuille", default_style=None):
|
||||
"""Crée une nouvelle feuille dans ce classeur
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut
|
||||
"""
|
||||
sheet = ScoExcelSheet(sheet_name, default_style)
|
||||
sheet_name = adjust_sheetname(sheet_name)
|
||||
ws = self.wb.create_sheet(sheet_name)
|
||||
sheet = ScoExcelSheet(sheet_name, default_style, ws)
|
||||
self.sheets.append(sheet)
|
||||
return sheet
|
||||
|
||||
@ -153,12 +123,11 @@ class ScoExcelBook:
|
||||
"""génération d'un stream binaire représentant la totalité du classeur.
|
||||
retourne le flux
|
||||
"""
|
||||
wb = Workbook(write_only=True)
|
||||
for sheet in self.sheets:
|
||||
sheet.generate(self)
|
||||
sheet.prepare()
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
with NamedTemporaryFile() as tmp:
|
||||
wb.save(tmp.name)
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
@ -166,6 +135,7 @@ class ScoExcelBook:
|
||||
def excel_make_style(
|
||||
bold=False,
|
||||
italic=False,
|
||||
outline=False,
|
||||
color: COLORS = COLORS.BLACK,
|
||||
bgcolor: COLORS = None,
|
||||
halign=None,
|
||||
@ -186,7 +156,14 @@ def excel_make_style(
|
||||
size -- taille de police
|
||||
"""
|
||||
style = {}
|
||||
font = Font(name=font_name, bold=bold, italic=italic, color=color.value, size=size)
|
||||
font = Font(
|
||||
name=font_name,
|
||||
bold=bold,
|
||||
italic=italic,
|
||||
outline=outline,
|
||||
color=color.value,
|
||||
size=size,
|
||||
)
|
||||
style["font"] = font
|
||||
if bgcolor:
|
||||
style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value)
|
||||
@ -222,42 +199,94 @@ class ScoExcelSheet:
|
||||
* pour finit appel de la méthode de génération
|
||||
"""
|
||||
|
||||
def __init__(self, sheet_name="feuille", default_style=None, wb=None):
|
||||
"""Création de la feuille.
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut des cellules
|
||||
wb -- le WorkBook dans laquelle se trouve la feuille. Si wb est None (cas d'un classeur mono-feuille),
|
||||
un workbook est crée et associé à cette feuille.
|
||||
def __init__(self, sheet_name="feuille", default_style=None, ws=None):
|
||||
"""Création de la feuille. sheet_name
|
||||
-- le nom de la feuille default_style
|
||||
-- le style par défaut des cellules ws
|
||||
-- None si la feuille est autonome (dans ce cas ell crée son propre wb), sinon c'est la worksheet
|
||||
créée par le workbook propriétaire un workbook est crée et associé à cette feuille.
|
||||
"""
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
self.sheet_name = sheet_name[
|
||||
:31
|
||||
] # if len(sheet_name) > 31: sheet_name = 'Feuille' ?
|
||||
self.rows = [] # list of list of cells
|
||||
# self.cells_styles_lico = {} # { (li,co) : style }
|
||||
# self.cells_styles_li = {} # { li : style }
|
||||
# self.cells_styles_co = {} # { co : style }
|
||||
self.sheet_name = adjust_sheetname(sheet_name)
|
||||
if default_style is None:
|
||||
default_style = excel_make_style()
|
||||
self.default_style = default_style
|
||||
self.wb = wb or Workbook(write_only=True) # Création de workbook si nécessaire
|
||||
self.ws = self.wb.create_sheet(title=self.sheet_name)
|
||||
if ws is None:
|
||||
self.wb = Workbook()
|
||||
self.ws = self.wb.active
|
||||
self.ws.title = self.sheet_name
|
||||
else:
|
||||
self.wb = None
|
||||
self.ws = ws
|
||||
# internal data
|
||||
self.rows = [] # list of list of cells
|
||||
self.column_dimensions = {}
|
||||
self.row_dimensions = {}
|
||||
|
||||
def set_column_dimension_width(self, cle, value):
|
||||
"""Détermine la largeur d'une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
value -- la dimension (unité : 7 pixels comme affiché dans Excel)
|
||||
def excel_make_composite_style(
|
||||
self,
|
||||
alignment=None,
|
||||
border=None,
|
||||
fill=None,
|
||||
number_format=None,
|
||||
font=None,
|
||||
):
|
||||
style = {}
|
||||
if font is not None:
|
||||
style["font"] = font
|
||||
if alignment is not None:
|
||||
style["alignment"] = alignment
|
||||
if border is not None:
|
||||
style["border"] = border
|
||||
if fill is not None:
|
||||
style["fill"] = fill
|
||||
if number_format is None:
|
||||
style["number_format"] = FORMAT_GENERAL
|
||||
else:
|
||||
style["number_format"] = number_format
|
||||
return style
|
||||
|
||||
@staticmethod
|
||||
def i2col(idx):
|
||||
if idx < 26: # one letter key
|
||||
return chr(idx + 65)
|
||||
else: # two letters AA..ZZ
|
||||
first = (idx // 26) + 66
|
||||
second = (idx % 26) + 65
|
||||
return "" + chr(first) + chr(second)
|
||||
|
||||
def set_column_dimension_width(self, cle=None, value=21):
|
||||
"""Détermine la largeur d'une colonne. cle -- identifie la colonne ("A" "B", ... ou 0, 1, 2, ...) si None,
|
||||
value donne la liste des largeurs de colonnes depuis A, B, C, ... value -- la dimension (unité : 7 pixels
|
||||
comme affiché dans Excel)
|
||||
"""
|
||||
if cle is None:
|
||||
for i, val in enumerate(value):
|
||||
self.ws.column_dimensions[self.i2col(i)].width = val
|
||||
# No keys: value is a list of widths
|
||||
elif type(cle) == str: # accepts set_column_with("D", ...)
|
||||
self.ws.column_dimensions[cle].width = value
|
||||
else:
|
||||
self.ws.column_dimensions[self.i2col(cle)].width = value
|
||||
|
||||
def set_column_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
def set_row_dimension_height(self, cle=None, value=21):
|
||||
"""Détermine la hauteur d'une ligne. cle -- identifie la ligne (1, 2, ...) si None,
|
||||
value donne la liste des hauteurs de colonnes depuis 1, 2, 3, ... value -- la dimension
|
||||
"""
|
||||
if cle is None:
|
||||
for i, val in enumerate(value, start=1):
|
||||
self.ws.row_dimensions[i].height = val
|
||||
# No keys: value is a list of widths
|
||||
else:
|
||||
self.ws.row_dimensions[cle].height = value
|
||||
|
||||
def set_row_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une ligne.
|
||||
cle -- identifie la colonne (1...)
|
||||
value -- boolean (vrai = colonne cachée)
|
||||
"""
|
||||
self.ws.column_dimensions[cle].hidden = value
|
||||
self.ws.row_dimensions[cle].hidden = value
|
||||
|
||||
def make_cell(self, value: any = None, style=None, comment=None):
|
||||
"""Construit une cellule.
|
||||
@ -271,8 +300,12 @@ class ScoExcelSheet:
|
||||
style = self.default_style
|
||||
if "font" in style:
|
||||
cell.font = style["font"]
|
||||
if "alignment" in style:
|
||||
cell.alignment = style["alignment"]
|
||||
if "border" in style:
|
||||
cell.border = style["border"]
|
||||
if "fill" in style:
|
||||
cell.fill = style["fill"]
|
||||
if "number_format" in style:
|
||||
cell.number_format = style["number_format"]
|
||||
if "fill" in style:
|
||||
@ -311,73 +344,31 @@ class ScoExcelSheet:
|
||||
"""ajoute une ligne déjà construite à la feuille."""
|
||||
self.rows.append(row)
|
||||
|
||||
# def set_style(self, style=None, li=None, co=None):
|
||||
# if li is not None and co is not None:
|
||||
# self.cells_styles_lico[(li, co)] = style
|
||||
# elif li is None:
|
||||
# self.cells_styles_li[li] = style
|
||||
# elif co is None:
|
||||
# self.cells_styles_co[co] = style
|
||||
#
|
||||
# def get_cell_style(self, li, co):
|
||||
# """Get style for specified cell"""
|
||||
# return (
|
||||
# self.cells_styles_lico.get((li, co), None)
|
||||
# or self.cells_styles_li.get(li, None)
|
||||
# or self.cells_styles_co.get(co, None)
|
||||
# or self.default_style
|
||||
# )
|
||||
|
||||
def _generate_ws(self):
|
||||
def prepare(self):
|
||||
"""génére un flux décrivant la feuille.
|
||||
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
|
||||
ou pour la génération d'un classeur multi-feuilles
|
||||
"""
|
||||
for col in self.column_dimensions.keys():
|
||||
self.ws.column_dimensions[col] = self.column_dimensions[col]
|
||||
for col in self.row_dimensions.keys():
|
||||
self.ws.row_dimensions[col] = self.row_dimensions[col]
|
||||
for row in self.rows:
|
||||
self.ws.append(row)
|
||||
|
||||
def generate_standalone(self):
|
||||
def generate(self):
|
||||
"""génération d'un classeur mono-feuille"""
|
||||
self._generate_ws()
|
||||
# this method makes sense only if it is a standalone worksheet (else call workbook.generate()
|
||||
if self.wb is None: # embeded sheet
|
||||
raise ScoValueError("can't generate a single sheet from a ScoWorkbook")
|
||||
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
self.prepare()
|
||||
with NamedTemporaryFile() as tmp:
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
def generate_embeded(self):
|
||||
"""generation d'une feuille include dans un classeur multi-feuilles"""
|
||||
self._generate_ws()
|
||||
|
||||
def gen_workbook(self, wb=None):
|
||||
"""TODO: à remplacer"""
|
||||
"""Generates and returns a workbook from stored data.
|
||||
If wb, add a sheet (tab) to the existing workbook (in this case, returns None).
|
||||
"""
|
||||
if wb is None:
|
||||
wb = Workbook() # Création du fichier
|
||||
sauvegarde = True
|
||||
else:
|
||||
sauvegarde = False
|
||||
ws0 = wb.add_sheet(self.sheet_name)
|
||||
li = 0
|
||||
for row in self.rows:
|
||||
co = 0
|
||||
for c in row:
|
||||
# safety net: allow only str, int and float
|
||||
# #py3 #sco8 A revoir lors de la ré-écriture de ce module
|
||||
# XXX if type(c) not in (IntType, FloatType):
|
||||
# c = str(c).decode(scu.SCO_ENCODING)
|
||||
ws0.write(li, co, c, self.get_cell_style(li, co))
|
||||
co += 1
|
||||
li += 1
|
||||
if sauvegarde:
|
||||
return wb.savetostr()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def excel_simple_table(
|
||||
titles=None, lines=None, sheet_name=b"feuille", titles_styles=None, comments=None
|
||||
@ -416,7 +407,7 @@ def excel_simple_table(
|
||||
cell_style = text_style
|
||||
cells.append(ws.make_cell(it, cell_style))
|
||||
ws.append_row(cells)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_feuille_saisie(e, titreannee, description, lines):
|
||||
@ -577,7 +568,7 @@ def excel_feuille_saisie(e, titreannee, description, lines):
|
||||
ws.make_cell("cellule vide -> note non modifiée", style_expl),
|
||||
]
|
||||
)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_bytes_to_list(bytes_content):
|
||||
@ -797,4 +788,4 @@ def excel_feuille_listeappel(
|
||||
cell_2 = ws.make_cell(("Liste éditée le " + dt), style1i)
|
||||
ws.append_row([None, cell_2])
|
||||
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
@ -33,13 +33,30 @@ Contribution M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import random
|
||||
import time
|
||||
from copy import copy
|
||||
|
||||
import flask
|
||||
import wtforms.validators
|
||||
from flask import request, render_template
|
||||
from flask_login import current_user
|
||||
from werkzeug import Response
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
StringField,
|
||||
PasswordField,
|
||||
BooleanField,
|
||||
SubmitField,
|
||||
SelectField,
|
||||
RadioField,
|
||||
HiddenField,
|
||||
SelectMultipleField,
|
||||
validators,
|
||||
)
|
||||
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
||||
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_evaluations
|
||||
@ -57,23 +74,116 @@ from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc.sco_excel import * # XXX à vérifier
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
|
||||
_ = lambda x: x # sans babel
|
||||
_l = _
|
||||
|
||||
def do_placement_selectetuds(REQUEST):
|
||||
|
||||
class PlacementForm(FlaskForm):
|
||||
TOUS = "Tous"
|
||||
evaluation_id = HiddenField("evaluation_id")
|
||||
file_format = RadioField(
|
||||
"Format de fichier",
|
||||
choices=["pdf", "xls"],
|
||||
validators=[
|
||||
wtforms.validators.DataRequired("indiquez le format du fichier attendu"),
|
||||
],
|
||||
)
|
||||
surveillants = StringField(
|
||||
"Surveillants", validators=[wtforms.validators.DataRequired("Test")]
|
||||
)
|
||||
batiment = StringField("Batiment")
|
||||
salle = StringField("Salle")
|
||||
nb_rangs = SelectField(
|
||||
"nb_rangs", coerce=int, choices=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
|
||||
)
|
||||
etiquetage = RadioField(
|
||||
"Numérotation",
|
||||
choices=["Continue", "Coordonnées"],
|
||||
validators=[
|
||||
wtforms.validators.DataRequired("indiquez le style de numérotation"),
|
||||
],
|
||||
)
|
||||
groups = SelectMultipleField(
|
||||
"Groupe(s)",
|
||||
validators=[
|
||||
wtforms.validators.DataRequired("indiquez au moins un groupe"),
|
||||
],
|
||||
)
|
||||
submit = SubmitField("OK")
|
||||
|
||||
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")
|
||||
eval_data = eval_data[0]
|
||||
# groupes
|
||||
groups = sco_groups.do_evaluation_listegroupes(
|
||||
evaluation_id, include_default=True
|
||||
)
|
||||
self.groups_tree = {}
|
||||
self.has_groups = False
|
||||
for group in groups:
|
||||
partition = group["partition_name"] or self.TOUS # TODO check required
|
||||
group_id = group["group_id"]
|
||||
group_name = group["group_name"] or self.TOUS
|
||||
if partition not in self.groups_tree:
|
||||
self.groups_tree[partition] = {}
|
||||
self.groups_tree[partition][group_name] = group_id
|
||||
if partition != self.TOUS:
|
||||
self.has_groups = True
|
||||
self.groups_tree_length = len(self.groups_tree)
|
||||
if self.has_groups:
|
||||
choices = []
|
||||
for partition in self.groups_tree:
|
||||
for groupe in self.groups_tree[partition]:
|
||||
id = str(self.groups_tree[partition][groupe])
|
||||
choices.append((id, "%s (%s)" % (str(groupe), partition)))
|
||||
self.groups.choices = choices
|
||||
|
||||
|
||||
def placement_eval_selectetuds(evaluation_id, REQUEST=None):
|
||||
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)
|
||||
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))
|
||||
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
|
||||
|
||||
|
||||
def do_placement_selectetuds():
|
||||
"""
|
||||
Choisi les étudiants et les infos sur la salle pour leur placement.
|
||||
"""
|
||||
evaluation_id = int(REQUEST.form["evaluation_id"])
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not E:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = E[0]
|
||||
# M = sco_moduleimpl.do_moduleimpl_list( moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
# groupes
|
||||
groups = sco_groups.do_evaluation_listegroupes(evaluation_id, include_default=True)
|
||||
grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons
|
||||
grnams = [g["group_id"] for g in groups] # noms des checkbox
|
||||
no_groups = (len(groups) == 1) and groups[0]["group_name"] is None
|
||||
|
||||
# description de l'evaluation
|
||||
H = [
|
||||
sco_evaluations.evaluation_describe(
|
||||
@ -247,7 +357,6 @@ def do_placement(REQUEST):
|
||||
"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]
|
||||
|
||||
# Check access
|
||||
# (admin, respformation, and responsable_id)
|
||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
||||
@ -321,7 +430,7 @@ def do_placement(REQUEST):
|
||||
|
||||
if placement_method == "xls":
|
||||
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
|
||||
)
|
||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||
@ -399,7 +508,7 @@ def do_placement(REQUEST):
|
||||
return t
|
||||
|
||||
|
||||
def placement_eval_selectetuds(evaluation_id, REQUEST=None):
|
||||
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:
|
||||
@ -415,298 +524,170 @@ def placement_eval_selectetuds(evaluation_id, REQUEST=None):
|
||||
formid = "placementfile"
|
||||
if not REQUEST.form.get("%s-submitted" % formid, False):
|
||||
# not submitted, choix groupe
|
||||
r = do_placement_selectetuds(REQUEST)
|
||||
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>
|
||||
"""
|
||||
"""<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 Excel_feuille_placement(
|
||||
E, description, listetud, columns, space, maxlines, building, room, numbering
|
||||
):
|
||||
"""Genere feuille excel pour placement des etudiants.
|
||||
E: evaluation (dict)
|
||||
lines: liste de tuples
|
||||
(etudid, nom, prenom, etat, groupe, val, explanation)
|
||||
"""
|
||||
nbcolumns = int(columns)
|
||||
|
||||
wb = Workbook()
|
||||
|
||||
SheetName0 = "Emargement"
|
||||
ws0 = wb.add_sheet(SheetName0.decode(scu.SCO_ENCODING))
|
||||
# ajuste largeurs colonnes (unite inconnue, empirique)
|
||||
width = 4500
|
||||
if nbcolumns > 5:
|
||||
width = 22500 // nbcolumns
|
||||
|
||||
for col in range(nbcolumns):
|
||||
ws0.col(col + 1).width = width
|
||||
ws0.col(0).width = 750
|
||||
|
||||
SheetName1 = "Positions"
|
||||
ws1 = wb.add_sheet(SheetName1.decode(scu.SCO_ENCODING))
|
||||
def _one_header(ws, numbering, styles):
|
||||
cells = []
|
||||
if numbering == "coordinate":
|
||||
ws1.col(0).width = 4000
|
||||
ws1.col(1).width = 4500
|
||||
ws1.col(2).width = 1500
|
||||
ws1.col(3).width = 1500
|
||||
|
||||
ws1.col(4).width = 500
|
||||
|
||||
ws1.col(5).width = 4000
|
||||
ws1.col(6).width = 4500
|
||||
ws1.col(7).width = 1500
|
||||
ws1.col(8).width = 1500
|
||||
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:
|
||||
ws1.col(0).width = 4000
|
||||
ws1.col(1).width = 4500
|
||||
ws1.col(2).width = 3000
|
||||
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
|
||||
|
||||
ws1.col(3).width = 500
|
||||
|
||||
ws1.col(4).width = 4000
|
||||
ws1.col(5).width = 4500
|
||||
ws1.col(6).width = 3000
|
||||
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
|
||||
font0 = Font()
|
||||
font0.name = "Arial"
|
||||
font0.bold = True
|
||||
font0.height = 12 * 0x14
|
||||
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
|
||||
|
||||
font1b = Font()
|
||||
font1b.name = "Arial"
|
||||
font1b.bold = True
|
||||
font1b.height = 9 * 0x14
|
||||
|
||||
font1i = Font()
|
||||
font1i.name = "Arial"
|
||||
font1i.height = 10 * 0x14
|
||||
font1i.italic = True
|
||||
def _init_lines(maxlines):
|
||||
return [
|
||||
[] for _ in range(maxlines)
|
||||
] # lines[no_ligne] -> liste des cellules de la ligne (no_lignes de 1..maxlines
|
||||
|
||||
font1o = Font()
|
||||
font1o.name = "Arial"
|
||||
font1o.height = 10 * 0x14
|
||||
font1o.outline = True
|
||||
|
||||
font2bi = Font()
|
||||
font2bi.name = "Arial"
|
||||
font2bi.height = 8 * 0x14
|
||||
font2bi.bold = True
|
||||
font2bi.italic = True
|
||||
def _write_lines(ws, lines):
|
||||
for line in lines:
|
||||
ws.append_row(line)
|
||||
|
||||
font2 = Font()
|
||||
font2.name = "Arial"
|
||||
font2.height = 10 * 0x14
|
||||
|
||||
style_titres = XFStyle()
|
||||
style_titres.font = font0
|
||||
|
||||
style1t = XFStyle()
|
||||
style1t.font = font1b
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_CENTER
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style1t.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.DOUBLE
|
||||
borders.top = Borders.DOUBLE
|
||||
borders.bottom = Borders.NO_LINE
|
||||
borders.right = Borders.DOUBLE
|
||||
style1t.borders = borders
|
||||
|
||||
style1m = XFStyle()
|
||||
style1m.font = font1b
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_CENTER
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style1m.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.DOUBLE
|
||||
borders.top = Borders.NO_LINE
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.DOUBLE
|
||||
style1m.borders = borders
|
||||
|
||||
style1bm = XFStyle()
|
||||
borders = Borders()
|
||||
borders.left = Borders.DOUBLE
|
||||
borders.top = Borders.NO_LINE
|
||||
borders.bottom = Borders.NO_LINE
|
||||
borders.right = Borders.DOUBLE
|
||||
style1bm.borders = borders
|
||||
|
||||
style1bb = XFStyle()
|
||||
style1bb.font = font1o
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_RIGHT
|
||||
alignment.vert = Alignment.VERT_BOTTOM
|
||||
style1bb.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.DOUBLE
|
||||
borders.top = Borders.NO_LINE
|
||||
borders.bottom = Borders.DOUBLE
|
||||
borders.right = Borders.DOUBLE
|
||||
style1bb.borders = borders
|
||||
|
||||
style2b = XFStyle()
|
||||
style2b.font = font1i
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_CENTER
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2b.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.THIN
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.THIN
|
||||
style2b.borders = borders
|
||||
|
||||
style2bi = XFStyle()
|
||||
style2bi.font = font2bi
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_CENTER
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2bi.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.THIN
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.THIN
|
||||
style2bi.borders = borders
|
||||
pattern = Pattern()
|
||||
pattern.pattern = Pattern.SOLID_PATTERN
|
||||
pattern._pattern_back_colour = "gray"
|
||||
style2bi.pattern = pattern
|
||||
|
||||
style2l = XFStyle()
|
||||
style2l.font = font2
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_LEFT
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2l.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.THIN
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.NO_LINE
|
||||
style2l.borders = borders
|
||||
|
||||
style2m1 = XFStyle()
|
||||
style2m1.font = font2
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_LEFT
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2m1.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.NO_LINE
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.NO_LINE
|
||||
style2m1.borders = borders
|
||||
|
||||
style2m2 = XFStyle()
|
||||
style2l.font = font2
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_RIGHT
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2m2.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.NO_LINE
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.NO_LINE
|
||||
style2m2.borders = borders
|
||||
|
||||
style2r = XFStyle()
|
||||
style2l.font = font2
|
||||
alignment = Alignment()
|
||||
alignment.horz = Alignment.HORZ_RIGHT
|
||||
alignment.vert = Alignment.VERT_CENTER
|
||||
style2r.alignment = alignment
|
||||
borders = Borders()
|
||||
borders.left = Borders.NO_LINE
|
||||
borders.top = Borders.THIN
|
||||
borders.bottom = Borders.THIN
|
||||
borders.right = Borders.THIN
|
||||
style2r.borders = borders
|
||||
|
||||
# ligne de titres
|
||||
li = 0
|
||||
line = 0
|
||||
def _titres(ws, description, evaluation, building, room, styles):
|
||||
dt = time.strftime("%d/%m/%Y a %Hh%M")
|
||||
ws0.write(li, 0, "Feuille placement etudiants éditée le %s" % dt, style_titres)
|
||||
ws1.write(li, 0, "Feuille placement etudiants éditée le %s" % dt, style_titres)
|
||||
for desceval in description:
|
||||
if line % 2 == 0:
|
||||
li += 2
|
||||
else:
|
||||
li += 1
|
||||
line += 1
|
||||
ws0.write(li, 0, desceval[0].decode(scu.SCO_ENCODING), style_titres)
|
||||
ws1.write(li, 0, desceval[0].decode(scu.SCO_ENCODING), style_titres)
|
||||
li += 1
|
||||
ws0.write(
|
||||
li,
|
||||
0,
|
||||
"Date : %s - Horaire : %s à %s" % (E["jour"], E["heure_debut"], E["heure_fin"]),
|
||||
style_titres,
|
||||
ws.append_single_cell_row(
|
||||
"Feuille placement etudiants éditée le %s" % dt, styles["titres"]
|
||||
)
|
||||
ws1.write(
|
||||
li,
|
||||
0,
|
||||
"Date : %s - Horaire : %s à %s" % (E["jour"], E["heure_debut"], E["heure_fin"]),
|
||||
style_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"],
|
||||
)
|
||||
li += 1
|
||||
|
||||
|
||||
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):
|
||||
ws0.write(li, col + 1, "colonne %s" % (col + 1), style2b)
|
||||
# entetes colonnes - feuille1
|
||||
if numbering == "coordinate":
|
||||
ws1.write(li, 0, "Nom", style2bi)
|
||||
ws1.write(li, 1, "Prénom", style2bi)
|
||||
ws1.write(li, 2, "Colonne", style2bi)
|
||||
ws1.write(li, 3, "Ligne", style2bi)
|
||||
|
||||
ws1.write(li, 5, "Nom", style2bi)
|
||||
ws1.write(li, 6, "Prénom", style2bi)
|
||||
ws1.write(li, 7, "Colonne", style2bi)
|
||||
ws1.write(li, 8, "Ligne", style2bi)
|
||||
else:
|
||||
ws1.write(li, 0, "Nom", style2bi)
|
||||
ws1.write(li, 1, "Prénom", style2bi)
|
||||
ws1.write(li, 2, "Place", style2bi)
|
||||
|
||||
ws1.write(li, 4, "Nom", style2bi)
|
||||
ws1.write(li, 5, "Prénom", style2bi)
|
||||
ws1.write(li, 6, "Place", style2bi)
|
||||
|
||||
cells.append(ws0.make_cell("colonne %s" % (col + 1), styles["2b"]))
|
||||
ws0.append_row(cells)
|
||||
# etudiants
|
||||
line = 1
|
||||
col = 1
|
||||
@ -730,78 +711,188 @@ def Excel_feuille_placement(
|
||||
placementetud.append(linetud)
|
||||
|
||||
# etudiants - feuille0
|
||||
line = 0
|
||||
li0 = li
|
||||
for linetud in placementetud:
|
||||
li0 += 1
|
||||
line += 1
|
||||
ws0.write(li0, 0, line, style2b)
|
||||
col = 1
|
||||
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:
|
||||
ws0.write(li0, col, (etudid[0]).decode(scu.SCO_ENCODING), style1t)
|
||||
ws0.write(li0 + 1, col, (etudid[1]).decode(scu.SCO_ENCODING), style1m)
|
||||
ws0.row(li0 + 2).height = space
|
||||
cells_a.append(ws0.make_cell(etudid[0], styles["1t"]))
|
||||
cells_b.append(ws0.make_cell(etudid[1], styles["1m"]))
|
||||
if numbering == "coordinate":
|
||||
ws0.write(li0 + 2, col, " ", style1bb)
|
||||
cell_c = ws0.make_cell("", styles["1bb"])
|
||||
else:
|
||||
ws0.write(
|
||||
li0 + 2, col, "place %s" % (col + (line - 1) * nbcolumns), style1bb
|
||||
)
|
||||
# ws0.write(li+3,col, ' ', style1bm )
|
||||
# ws0.write(li+4,col, ' ', style1bb )
|
||||
|
||||
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:
|
||||
col = 0
|
||||
li0 += 2
|
||||
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
|
||||
|
||||
# etudiants - feuille1
|
||||
if numbering == "coordinate":
|
||||
coloffset = 5
|
||||
else:
|
||||
coloffset = 4
|
||||
line = 0
|
||||
li1 = li
|
||||
nbcol = 0
|
||||
col = 0
|
||||
orderetud.sort()
|
||||
for etudid in orderetud:
|
||||
li1 += 1
|
||||
if col > nbcolumns:
|
||||
col = 1
|
||||
line += 1
|
||||
ws1.write(li1, col, (etudid[0]).decode(scu.SCO_ENCODING), style2l)
|
||||
ws1.write(li1, col + 1, (etudid[1]).decode(scu.SCO_ENCODING), style2m1)
|
||||
if numbering == "coordinate":
|
||||
ws1.write(li1, col + 2, etudid[2], style2m2)
|
||||
ws1.write(li1, col + 3, etudid[3], style2r)
|
||||
else:
|
||||
ws1.write(li1, col + 2, etudid[2], style2r)
|
||||
orderetud.sort()
|
||||
return orderetud
|
||||
|
||||
if line == maxlines:
|
||||
|
||||
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
|
||||
li1 = li
|
||||
nbcol = nbcol + 1
|
||||
col = col + coloffset
|
||||
if nbcol == 2:
|
||||
li = li + maxlines + 2
|
||||
li1 = li
|
||||
nbcol = 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":
|
||||
ws1.write(li, 0, "Nom", style2bi)
|
||||
ws1.write(li, 1, "Prénom", style2bi)
|
||||
ws1.write(li, 2, "Colonne", style2bi)
|
||||
ws1.write(li, 3, "Ligne", style2bi)
|
||||
|
||||
ws1.write(li, 5, "Nom", style2bi)
|
||||
ws1.write(li, 6, "Prénom", style2bi)
|
||||
ws1.write(li, 7, "Colonne", style2bi)
|
||||
ws1.write(li, 8, "Ligne", style2bi)
|
||||
lines[line].append(ws.make_cell(etudid[2], styles["2m2"]))
|
||||
lines[line].append(ws.make_cell(etudid[3], styles["2r"]))
|
||||
else:
|
||||
ws1.write(li, 0, "Nom", style2bi)
|
||||
ws1.write(li, 1, "Prénom", style2bi)
|
||||
ws1.write(li, 2, "Place", style2bi)
|
||||
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)
|
||||
|
||||
ws1.write(li, 4, "Nom", style2bi)
|
||||
ws1.write(li, 5, "Prénom", style2bi)
|
||||
ws1.write(li, 6, "Place", style2bi)
|
||||
return wb.savetostr()
|
||||
|
||||
def _excel_feuille_placement(
|
||||
evaluation,
|
||||
description,
|
||||
listetud,
|
||||
columns,
|
||||
space,
|
||||
maxlines,
|
||||
building,
|
||||
room,
|
||||
numbering,
|
||||
):
|
||||
"""Genere feuille excel pour placement des etudiants.
|
||||
E: evaluation (dict)
|
||||
lines: liste de tuples
|
||||
(etudid, nom, prenom, etat, groupe, val, explanation)
|
||||
"""
|
||||
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
|
||||
)
|
||||
|
||||
SheetName1 = "Positions"
|
||||
ws1 = wb.create_sheet(SheetName1)
|
||||
|
||||
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()
|
||||
|
56
app/templates/forms/placement.html
Normal file
56
app/templates/forms/placement.html
Normal file
@ -0,0 +1,56 @@
|
||||
{% import 'bootstrap/wtf.html' as wtf %}
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<tr>
|
||||
<td class="wtf-field">{{ field.label }}</td>
|
||||
<td class="wtf-field">{{ field(**kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="saisienote_etape1 form_placement">
|
||||
<form method=post>
|
||||
{{ form.evaluation_id }}
|
||||
<table class="tf">
|
||||
<tbody>
|
||||
{{ render_field(form.surveillants) }}
|
||||
{{ render_field(form.batiment) }}
|
||||
{{ render_field(form.salle) }}
|
||||
{{ render_field(form.nb_rangs) }}
|
||||
{{ render_field(form.etiquetage) }}
|
||||
{% if form.has_groups %}
|
||||
{{ render_field(form.groups) }}
|
||||
<!--
|
||||
{% for partition in form.groups_tree %}
|
||||
<tr>
|
||||
{% if partition == 'Tous' %}
|
||||
<td rowspan="{{ form.groups_tree_length }}">Groupes</td>
|
||||
{% endif %}
|
||||
<td>{{ partition }}</td>
|
||||
<td>
|
||||
{% for groupe in form.groups_tree[partition] %}
|
||||
{{ groupe }}{{ form[form.groups_tree[partition][groupe]] }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
-->
|
||||
{% endif %}
|
||||
{{ render_field(form.file_format) }}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<input id="gr_submit" type=submit value="Ok">
|
||||
<input id="gr_cancel" type=submit value="Annuler">
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -1611,6 +1611,7 @@ sco_publish(
|
||||
"/placement_eval_selectetuds",
|
||||
sco_placement.placement_eval_selectetuds,
|
||||
Permission.ScoEnsView,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish("/do_placement", sco_placement.do_placement, Permission.ScoEnsView)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user