forked from ScoDoc/ScoDoc
Merge pull request 'scodoc9_placement_PR' (#126) from jmplace/ScoDoc-Lille:scodoc9_placement_PR into master
Reviewed-on: https://scodoc.org/git/viennet/ScoDoc/pulls/126
This commit is contained in:
commit
11ba73d264
1
.gitignore
vendored
1
.gitignore
vendored
@ -170,3 +170,4 @@ Thumbs.db
|
|||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
|
||||||
|
copy
|
||||||
|
@ -482,9 +482,9 @@ class GenTable(object):
|
|||||||
ses.append_blank_row() # empty line
|
ses.append_blank_row() # empty line
|
||||||
ses.append_single_cell_row(self.origin, style_base)
|
ses.append_single_cell_row(self.origin, style_base)
|
||||||
if wb is None:
|
if wb is None:
|
||||||
return ses.generate_standalone()
|
return ses.generate()
|
||||||
else:
|
else:
|
||||||
ses.generate_embeded()
|
ses.generate()
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
"raw text representation of the table"
|
"raw text representation of the table"
|
||||||
|
@ -269,9 +269,14 @@ def etudarchive_generate_excel_sample(group_id=None, REQUEST=None):
|
|||||||
],
|
],
|
||||||
extra_cols=["fichier_a_charger"],
|
extra_cols=["fichier_a_charger"],
|
||||||
)
|
)
|
||||||
return sco_excel.send_excel_file(
|
return scu.send_file(
|
||||||
REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX
|
data,
|
||||||
|
"ImportFichiersEtudiants",
|
||||||
|
scu.XLSX_SUFFIX,
|
||||||
|
scu.XLSX_MIMETYPE,
|
||||||
|
attached=True,
|
||||||
)
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
def etudarchive_import_files_form(group_id, REQUEST=None):
|
def etudarchive_import_files_form(group_id, REQUEST=None):
|
||||||
|
@ -35,11 +35,11 @@ from enum import Enum
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
import openpyxl.utils.datetime
|
import openpyxl.utils.datetime
|
||||||
|
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||||
|
from openpyxl.comments import Comment
|
||||||
from openpyxl import Workbook, load_workbook
|
from openpyxl import Workbook, load_workbook
|
||||||
from openpyxl.cell import WriteOnlyCell
|
from openpyxl.cell import WriteOnlyCell
|
||||||
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
||||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
|
||||||
from openpyxl.comments import Comment
|
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import notesdb
|
from app.scodoc import notesdb
|
||||||
@ -59,24 +59,9 @@ class COLORS(Enum):
|
|||||||
LIGHT_YELLOW = "FFFFFF99"
|
LIGHT_YELLOW = "FFFFFF99"
|
||||||
|
|
||||||
|
|
||||||
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(" ", "_")
|
|
||||||
)
|
|
||||||
request.RESPONSE.setHeader("content-type", mime)
|
|
||||||
request.RESPONSE.setHeader(
|
|
||||||
"content-disposition", 'attachment; filename="%s"' % filename
|
|
||||||
)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
|
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
|
||||||
# font, border, number_format, fill, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
# font, border, number_format, fill,...
|
||||||
|
# (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||||
|
|
||||||
|
|
||||||
def xldate_as_datetime(xldate, datemode=0):
|
def xldate_as_datetime(xldate, datemode=0):
|
||||||
@ -86,6 +71,12 @@ def xldate_as_datetime(xldate, datemode=0):
|
|||||||
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
class ScoExcelBook:
|
||||||
"""Permet la génération d'un classeur xlsx composé de plusieurs feuilles.
|
"""Permet la génération d'un classeur xlsx composé de plusieurs feuilles.
|
||||||
usage:
|
usage:
|
||||||
@ -98,13 +89,16 @@ class ScoExcelBook:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sheets = [] # list of sheets
|
self.sheets = [] # list of sheets
|
||||||
|
self.wb = Workbook(write_only=True)
|
||||||
|
|
||||||
def create_sheet(self, sheet_name="feuille", default_style=None):
|
def create_sheet(self, sheet_name="feuille", default_style=None):
|
||||||
"""Crée une nouvelle feuille dans ce classeur
|
"""Crée une nouvelle feuille dans ce classeur
|
||||||
sheet_name -- le nom de la feuille
|
sheet_name -- le nom de la feuille
|
||||||
default_style -- le style par défaut
|
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)
|
self.sheets.append(sheet)
|
||||||
return sheet
|
return sheet
|
||||||
|
|
||||||
@ -112,12 +106,12 @@ class ScoExcelBook:
|
|||||||
"""génération d'un stream binaire représentant la totalité du classeur.
|
"""génération d'un stream binaire représentant la totalité du classeur.
|
||||||
retourne le flux
|
retourne le flux
|
||||||
"""
|
"""
|
||||||
wb = Workbook(write_only=True)
|
|
||||||
for sheet in self.sheets:
|
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)
|
# construction d'un flux
|
||||||
|
# (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||||
with NamedTemporaryFile() as tmp:
|
with NamedTemporaryFile() as tmp:
|
||||||
wb.save(tmp.name)
|
self.wb.save(tmp.name)
|
||||||
tmp.seek(0)
|
tmp.seek(0)
|
||||||
return tmp.read()
|
return tmp.read()
|
||||||
|
|
||||||
@ -125,6 +119,7 @@ class ScoExcelBook:
|
|||||||
def excel_make_style(
|
def excel_make_style(
|
||||||
bold=False,
|
bold=False,
|
||||||
italic=False,
|
italic=False,
|
||||||
|
outline=False,
|
||||||
color: COLORS = COLORS.BLACK,
|
color: COLORS = COLORS.BLACK,
|
||||||
bgcolor: COLORS = None,
|
bgcolor: COLORS = None,
|
||||||
halign=None,
|
halign=None,
|
||||||
@ -145,7 +140,14 @@ def excel_make_style(
|
|||||||
size -- taille de police
|
size -- taille de police
|
||||||
"""
|
"""
|
||||||
style = {}
|
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
|
style["font"] = font
|
||||||
if bgcolor:
|
if bgcolor:
|
||||||
style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value)
|
style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value)
|
||||||
@ -182,41 +184,93 @@ class ScoExcelSheet:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sheet_name="feuille", default_style=None, wb=None):
|
def __init__(self, sheet_name="feuille", default_style=None, wb=None):
|
||||||
"""Création de la feuille.
|
"""Création de la feuille. sheet_name
|
||||||
sheet_name -- le nom de la feuille
|
-- le nom de la feuille default_style
|
||||||
default_style -- le style par défaut des cellules
|
-- le style par défaut des cellules ws
|
||||||
wb -- le WorkBook dans laquelle se trouve la feuille. Si wb est None (cas d'un classeur mono-feuille),
|
-- None si la feuille est autonome (dans ce cas ell crée son propre wb), sinon c'est la worksheet
|
||||||
un workbook est crée et associé à cette feuille.
|
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.
|
# 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' ?)
|
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||||
self.sheet_name = sheet_name[
|
self.sheet_name = adjust_sheetname(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 }
|
|
||||||
if default_style is None:
|
if default_style is None:
|
||||||
default_style = excel_make_style()
|
default_style = excel_make_style()
|
||||||
self.default_style = default_style
|
self.default_style = default_style
|
||||||
self.wb = wb or Workbook(write_only=True) # Création de workbook si nécessaire
|
if wb is None:
|
||||||
self.ws = self.wb.create_sheet(title=self.sheet_name)
|
self.wb = Workbook()
|
||||||
|
self.ws = self.wb.active
|
||||||
|
self.ws.title = self.sheet_name
|
||||||
|
else:
|
||||||
|
self.wb = None
|
||||||
|
self.ws = wb
|
||||||
|
# internal data
|
||||||
|
self.rows = [] # list of list of cells
|
||||||
self.column_dimensions = {}
|
self.column_dimensions = {}
|
||||||
|
self.row_dimensions = {}
|
||||||
|
|
||||||
def set_column_dimension_width(self, cle, value):
|
def excel_make_composite_style(
|
||||||
"""Détermine la largeur d'une colonne.
|
self,
|
||||||
cle -- identifie la colonne ("A"n "B", ...)
|
alignment=None,
|
||||||
value -- la dimension (unité : 7 pixels comme affiché dans Excel)
|
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
|
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):
|
def set_row_dimension_height(self, cle=None, value=21):
|
||||||
"""Masque ou affiche une colonne.
|
"""Détermine la hauteur d'une ligne. cle -- identifie la ligne (1, 2, ...) si None,
|
||||||
cle -- identifie la colonne ("A"n "B", ...)
|
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)
|
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):
|
def make_cell(self, value: any = None, style=None, comment=None):
|
||||||
"""Construit une cellule.
|
"""Construit une cellule.
|
||||||
@ -232,8 +286,12 @@ class ScoExcelSheet:
|
|||||||
style = self.default_style
|
style = self.default_style
|
||||||
if "font" in style:
|
if "font" in style:
|
||||||
cell.font = style["font"]
|
cell.font = style["font"]
|
||||||
|
if "alignment" in style:
|
||||||
|
cell.alignment = style["alignment"]
|
||||||
if "border" in style:
|
if "border" in style:
|
||||||
cell.border = style["border"]
|
cell.border = style["border"]
|
||||||
|
if "fill" in style:
|
||||||
|
cell.fill = style["fill"]
|
||||||
if "number_format" in style:
|
if "number_format" in style:
|
||||||
cell.number_format = style["number_format"]
|
cell.number_format = style["number_format"]
|
||||||
if "fill" in style:
|
if "fill" in style:
|
||||||
@ -272,73 +330,31 @@ class ScoExcelSheet:
|
|||||||
"""ajoute une ligne déjà construite à la feuille."""
|
"""ajoute une ligne déjà construite à la feuille."""
|
||||||
self.rows.append(row)
|
self.rows.append(row)
|
||||||
|
|
||||||
# def set_style(self, style=None, li=None, co=None):
|
def prepare(self):
|
||||||
# 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):
|
|
||||||
"""génére un flux décrivant la feuille.
|
"""génére un flux décrivant la feuille.
|
||||||
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
|
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
|
||||||
ou pour la génération d'un classeur multi-feuilles
|
ou pour la génération d'un classeur multi-feuilles
|
||||||
"""
|
"""
|
||||||
for col in self.column_dimensions.keys():
|
for row in self.column_dimensions.keys():
|
||||||
self.ws.column_dimensions[col] = self.column_dimensions[col]
|
self.ws.column_dimensions[row] = self.column_dimensions[row]
|
||||||
|
for row in self.row_dimensions.keys():
|
||||||
|
self.ws.row_dimensions[row] = self.row_dimensions[row]
|
||||||
for row in self.rows:
|
for row in self.rows:
|
||||||
self.ws.append(row)
|
self.ws.append(row)
|
||||||
|
|
||||||
def generate_standalone(self):
|
def generate(self):
|
||||||
"""génération d'un classeur mono-feuille"""
|
"""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)
|
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||||
|
self.prepare()
|
||||||
with NamedTemporaryFile() as tmp:
|
with NamedTemporaryFile() as tmp:
|
||||||
self.wb.save(tmp.name)
|
self.wb.save(tmp.name)
|
||||||
tmp.seek(0)
|
tmp.seek(0)
|
||||||
return tmp.read()
|
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(
|
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
|
||||||
@ -377,7 +393,7 @@ def excel_simple_table(
|
|||||||
cell_style = text_style
|
cell_style = text_style
|
||||||
cells.append(ws.make_cell(it, cell_style))
|
cells.append(ws.make_cell(it, cell_style))
|
||||||
ws.append_row(cells)
|
ws.append_row(cells)
|
||||||
return ws.generate_standalone()
|
return ws.generate()
|
||||||
|
|
||||||
|
|
||||||
def excel_feuille_saisie(e, titreannee, description, lines):
|
def excel_feuille_saisie(e, titreannee, description, lines):
|
||||||
@ -538,7 +554,7 @@ def excel_feuille_saisie(e, titreannee, description, lines):
|
|||||||
ws.make_cell("cellule vide -> note non modifiée", style_expl),
|
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):
|
def excel_bytes_to_list(bytes_content):
|
||||||
@ -758,4 +774,4 @@ def excel_feuille_listeappel(
|
|||||||
cell_2 = ws.make_cell(("Liste éditée le " + dt), style1i)
|
cell_2 = ws.make_cell(("Liste éditée le " + dt), style1i)
|
||||||
ws.append_row([None, cell_2])
|
ws.append_row([None, cell_2])
|
||||||
|
|
||||||
return ws.generate_standalone()
|
return ws.generate()
|
||||||
|
@ -722,7 +722,10 @@ def groups_table(
|
|||||||
server_name=request.url_root,
|
server_name=request.url_root,
|
||||||
)
|
)
|
||||||
filename = "liste_%s" % groups_infos.groups_filename + ".xlsx"
|
filename = "liste_%s" % groups_infos.groups_filename + ".xlsx"
|
||||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
return scu.send_file(
|
||||||
|
xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||||
elif format == "allxls":
|
elif format == "allxls":
|
||||||
# feuille Excel avec toutes les infos etudiants
|
# feuille Excel avec toutes les infos etudiants
|
||||||
if not groups_infos.members:
|
if not groups_infos.members:
|
||||||
@ -791,7 +794,10 @@ def groups_table(
|
|||||||
title = "etudiants_%s" % groups_infos.groups_filename
|
title = "etudiants_%s" % groups_infos.groups_filename
|
||||||
xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
|
xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
|
||||||
filename = title + scu.XLSX_SUFFIX
|
filename = title + scu.XLSX_SUFFIX
|
||||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
return scu.send_file(
|
||||||
|
xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||||
else:
|
else:
|
||||||
raise ValueError("unsupported format")
|
raise ValueError("unsupported format")
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -324,5 +324,12 @@ def feuille_preparation_jury(formsemestre_id, REQUEST):
|
|||||||
REQUEST.AUTHENTICATED_USER,
|
REQUEST.AUTHENTICATED_USER,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
xls = ws.generate_standalone()
|
xls = ws.generate()
|
||||||
return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")
|
return scu.send_file(
|
||||||
|
xls,
|
||||||
|
f"PrepaJury{sn}{scu.XLSX_SUFFIX}",
|
||||||
|
scu.XLSX_SUFFIX,
|
||||||
|
scu.XLSX_MIMETYPE,
|
||||||
|
attached=True,
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")
|
||||||
|
@ -829,7 +829,10 @@ def feuille_saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
|||||||
|
|
||||||
filename = "notes_%s_%s.xlsx" % (evalname, gr_title_filename)
|
filename = "notes_%s_%s.xlsx" % (evalname, gr_title_filename)
|
||||||
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
|
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
|
||||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
return scu.send_file(
|
||||||
|
xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||||
|
|
||||||
|
|
||||||
def has_existing_decision(M, E, etudid):
|
def has_existing_decision(M, E, etudid):
|
||||||
|
@ -486,7 +486,10 @@ def photos_generate_excel_sample(group_ids=[], REQUEST=None):
|
|||||||
],
|
],
|
||||||
extra_cols=["fichier_photo"],
|
extra_cols=["fichier_photo"],
|
||||||
)
|
)
|
||||||
return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
return scu.send_file(
|
||||||
|
data, "ImportPhotos", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
def photos_import_files_form(group_ids=[], REQUEST=None):
|
def photos_import_files_form(group_ids=[], REQUEST=None):
|
||||||
|
@ -1092,13 +1092,11 @@ h2.formsemestre, .gtrcontent h2 {
|
|||||||
#formnotes td.tf-fieldlabel {
|
#formnotes td.tf-fieldlabel {
|
||||||
border-bottom: 1px dotted #fdcaca;
|
border-bottom: 1px dotted #fdcaca;
|
||||||
}
|
}
|
||||||
|
.wtf-field li {
|
||||||
/* Formulaires ScoDoc 9 */
|
display: inline;
|
||||||
form.sco-form {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
}
|
||||||
div.sco-submit {
|
.wtf-field .errors {
|
||||||
margin-top: 2em;
|
color: red ; font-weight: bold;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
.formsemestre_menubar {
|
.formsemestre_menubar {
|
||||||
|
77
app/templates/scodoc/forms/placement.html
Normal file
77
app/templates/scodoc/forms/placement.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{% import 'bootstrap/wtf.html' as wtf %}
|
||||||
|
|
||||||
|
{% macro render_field(field) %}
|
||||||
|
<tr>
|
||||||
|
<td class="wtf-field">{{ field.label }}</td>
|
||||||
|
<td class="wtf-field">{{ field()|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 }}
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
<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) }}
|
||||||
|
<!-- Tentative de recréer le choix des groupes sous forme de cases à cocher // demande à créer des champs wtf dynamiquement
|
||||||
|
{% for partition in form.groups_tree %}
|
||||||
|
<tr>
|
||||||
|
{% if partition == 'Tous' %}
|
||||||
|
<td rowspan="{{ form.nb_groups }}">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>
|
||||||
|
<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>il est possible de choisir un ou plusieurs groupes (shift/ctrl click) ou de choisir 'tous'.</li>
|
||||||
|
<li>Choisir le format du fichier résultat :
|
||||||
|
<ul>
|
||||||
|
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;</li>
|
||||||
|
<li>le format xls produit un classeur avec deux onglets:
|
||||||
|
<ul>
|
||||||
|
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
|
||||||
|
peut servir de feuille d'émargement;</li>
|
||||||
|
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
|
||||||
|
</ul></li>
|
||||||
|
</ul> </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -1645,8 +1645,8 @@ sco_publish(
|
|||||||
"/placement_eval_selectetuds",
|
"/placement_eval_selectetuds",
|
||||||
sco_placement.placement_eval_selectetuds,
|
sco_placement.placement_eval_selectetuds,
|
||||||
Permission.ScoEnsView,
|
Permission.ScoEnsView,
|
||||||
|
methods=["GET", "POST"],
|
||||||
)
|
)
|
||||||
sco_publish("/do_placement", sco_placement.do_placement, Permission.ScoEnsView)
|
|
||||||
|
|
||||||
# --- Saisie des notes
|
# --- Saisie des notes
|
||||||
sco_publish(
|
sco_publish(
|
||||||
|
@ -1937,7 +1937,10 @@ def import_generate_excel_sample(REQUEST, with_codesemestre="1"):
|
|||||||
data = sco_import_etuds.sco_import_generate_excel_sample(
|
data = sco_import_etuds.sco_import_generate_excel_sample(
|
||||||
format, with_codesemestre, exclude_cols=["photo_filename"]
|
format, with_codesemestre, exclude_cols=["photo_filename"]
|
||||||
)
|
)
|
||||||
return sco_excel.send_excel_file(REQUEST, data, "ImportEtudiants" + scu.XLSX_SUFFIX)
|
return scu.send_file(
|
||||||
|
data, "ImportEtudiants", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, data, "ImportEtudiants" + scu.XLSX_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
# --- Données admission
|
# --- Données admission
|
||||||
@ -1955,9 +1958,10 @@ def import_generate_admission_sample(REQUEST, formsemestre_id):
|
|||||||
exclude_cols=["nationalite", "foto", "photo_filename"],
|
exclude_cols=["nationalite", "foto", "photo_filename"],
|
||||||
group_ids=[group["group_id"]],
|
group_ids=[group["group_id"]],
|
||||||
)
|
)
|
||||||
return sco_excel.send_excel_file(
|
return scu.send_file(
|
||||||
REQUEST, data, "AdmissionEtudiants" + scu.XLSX_SUFFIX
|
data, "AdmissionEtudiants", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
)
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, data, "AdmissionEtudiants" + scu.XLSX_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
# --- Données admission depuis fichier excel (version nov 2016)
|
# --- Données admission depuis fichier excel (version nov 2016)
|
||||||
|
@ -65,8 +65,6 @@ from app import log
|
|||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
from app.scodoc.sco_permissions_check import can_handle_passwd
|
from app.scodoc.sco_permissions_check import can_handle_passwd
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||||
from app.scodoc.sco_excel import send_excel_file
|
|
||||||
from app.scodoc.sco_import_users import generate_excel_sample
|
|
||||||
from app.views import users_bp as bp
|
from app.views import users_bp as bp
|
||||||
|
|
||||||
|
|
||||||
@ -490,9 +488,10 @@ def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1):
|
|||||||
def import_users_generate_excel_sample(REQUEST):
|
def import_users_generate_excel_sample(REQUEST):
|
||||||
"une feuille excel pour importation utilisateurs"
|
"une feuille excel pour importation utilisateurs"
|
||||||
data = sco_import_users.generate_excel_sample()
|
data = sco_import_users.generate_excel_sample()
|
||||||
return sco_excel.send_excel_file(
|
return scu.send_file(
|
||||||
REQUEST, data, "ImportUtilisateurs" + scu.XLSX_SUFFIX
|
data, "ImportUtilisateurs", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||||
)
|
)
|
||||||
|
# return sco_excel.send_excel_file(REQUEST, data, "ImportUtilisateurs" + scu.XLSX_SUFFIX)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/import_users_form", methods=["GET", "POST"])
|
@bp.route("/import_users_form", methods=["GET", "POST"])
|
||||||
|
Loading…
Reference in New Issue
Block a user