Contrib JM Place i/o Excel

This commit is contained in:
Emmanuel Viennet 2021-08-02 09:52:07 +03:00
commit 0cb9d18344
4 changed files with 1715 additions and 1740 deletions

View File

@ -40,7 +40,10 @@ from app.scodoc.scolog import logdb
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_preferences
import six
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment, Protection
from openpyxl.cell import WriteOnlyCell
from tempfile import NamedTemporaryFile
# colors, voir exemple format.py
COLOR_CODES = {
@ -54,7 +57,7 @@ COLOR_CODES = {
}
def sendExcelFile(REQUEST, data, filename):
def send_excel_file(REQUEST, data, filename):
"""publication fichier.
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
"""
@ -63,7 +66,7 @@ def sendExcelFile(REQUEST, data, filename):
.replace("&", "")
.replace(" ", "_")
)
REQUEST.RESPONSE.setHeader("content-type", scu.XLS_MIMETYPE)
REQUEST.RESPONSE.setHeader("content-type", scu.XLSX_MIMETYPE)
REQUEST.RESPONSE.setHeader(
"content-disposition", 'attachment; filename="%s"' % filename
)
@ -220,7 +223,7 @@ class ScoExcelSheet(object):
sauvegarde = True
else:
sauvegarde = False
ws0 = wb.add_sheet(self.sheet_name.decode(scu.SCO_ENCODING))
ws0 = wb.add_sheet(self.sheet_name)
li = 0
for l in self.cells:
co = 0
@ -476,145 +479,127 @@ def Excel_to_list(data, convert_to_string=str): # we may need 'encoding' argume
#
return diag, M
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
# font, border, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
#
def Excel_feuille_listeappel(
context,
sem,
groupname,
lines,
partitions=[], # partitions a montrer (colonnes)
with_codes=False, # indique codes etuds
with_paiement=False, # indique si etudiant a paye inscription
server_name=None,
):
"generation feuille appel"
def __make_cell(ws, value: any = u"", style=None):
"""Contruit/retourne une cellule en spécifiant contenu et style.
ws -- La feuille sera intégrée la cellule
value -- le contenu de la cellule (texte)
style -- le style de la cellule
"""
cell = WriteOnlyCell(ws, value)
if "font" in style:
cell.font = style["font"]
if "border" in style:
cell.border = style["border"]
return cell
def excel_feuille_listeappel(sem, groupname, lines, partitions=None, with_codes=False, with_paiement=False,
server_name=None):
"""generation feuille appel"""
if partitions is None:
partitions = []
formsemestre_id = sem["formsemestre_id"]
SheetName = "Liste " + groupname
wb = Workbook()
ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
sheet_name = "Liste " + groupname
wb = Workbook(write_only=True)
ws = wb.create_sheet(title=sheet_name)
ws.column_dimensions["A"].width = 3
ws.column_dimensions["B"].width = 35
ws.column_dimensions["C"].width = 12
font1 = Font()
font1.name = "Arial"
font1.height = 10 * 0x14
font1 = Font(name="Arial", size=11)
font1i = Font(name="Arial", size=10, italic=True)
font1b = Font(name="Arial", size=11, bold=True)
font1i = Font()
font1i.name = "Arial"
font1i.height = 10 * 0x14
font1i.italic = True
side_thin = Side(border_style="thin", color="FF000000")
style1i = XFStyle()
style1i.font = font1i
border_tbl = Border(top=side_thin, bottom=side_thin, left=side_thin)
border_tblr = Border(
top=side_thin, bottom=side_thin, left=side_thin, right=side_thin
)
style1b = XFStyle()
style1b.font = font1
borders = Borders()
borders.left = 1
borders.top = 1
borders.bottom = 1
style1b.borders = borders
style1i = {
"font": font1i,
}
style2 = XFStyle()
font2 = Font()
font2.name = "Arial"
font2.height = 14 * 0x14
style2.font = font2
style1b = {
"font": font1,
"border": border_tbl,
}
style2b = XFStyle()
style2b.font = font1i
borders = Borders()
borders.left = 1
borders.top = 1
borders.bottom = 1
borders.right = 1
style2b.borders = borders
style2 = {
"font": Font(name="Arial", size=14),
}
style2tb = XFStyle()
borders = Borders()
borders.top = 1
borders.bottom = 1
style2tb.borders = borders
style2tb.font = Font()
style2tb.font.height = 16 * 0x14 # -> ligne hautes
style2b = {
"font": font1i,
"border": border_tblr,
}
style2t3 = XFStyle()
borders = Borders()
borders.top = 1
borders.bottom = 1
borders.left = 1
style2t3.borders = borders
style2t3 = {
"border": border_tblr,
}
style2t3bold = XFStyle()
borders = Borders()
borders.top = 1
borders.bottom = 1
borders.left = 1
style2t3bold.borders = borders
fontb = Font()
fontb.bold = True
style2t3bold.font = fontb
style2t3bold = {
"font": font1b,
"border": border_tblr,
}
style3 = XFStyle()
font3 = Font()
font3.name = "Arial"
font3.bold = True
font3.height = 14 * 0x14
style3.font = font3
style3 = {
"font": Font(name="Arial", bold=True, size=14),
}
NbWeeks = 4 # nombre de colonnes pour remplir absences
nb_weeks = 4 # nombre de colonnes pour remplir absences
# ligne 1
li = 0
ws0.write(
li,
1,
(
"%s %s (%s - %s)"
% (
sco_preferences.get_preference("DeptName", formsemestre_id),
notesdb.unquote(sem["titre_num"]),
sem["date_debut"],
sem["date_fin"],
)
).decode(scu.SCO_ENCODING),
style2,
title = "%s %s (%s - %s)" % (
sco_preferences.get_preference("DeptName", formsemestre_id),
notesdb.unquote(sem["titre_num"]),
sem["date_debut"],
sem["date_fin"],
)
# ligne 2
li += 1
ws0.write(li, 1, u"Discipline :", style2)
# ligne 3
li += 1
ws0.write(li, 1, u"Enseignant :", style2)
ws0.write(li, 5, ("Groupe %s" % groupname).decode(scu.SCO_ENCODING), style3)
# Avertissement pour ne pas confondre avec listes notes
ws0.write(
li + 1, 2, u"Ne pas utiliser cette feuille pour saisir les notes !", style1i
)
#
li += 2
li += 1
ws0.write(li, 1, u"Nom", style3)
co = 2
for partition in partitions:
if partition["partition_name"]:
ws0.write(
li, co, partition["partition_name"].decode(scu.SCO_ENCODING), style3
)
co += 1
if with_codes:
coc = co
ws0.write(li, coc, u"etudid", style3)
ws0.write(li, coc + 1, u"code_nip", style3)
ws0.write(li, coc + 2, u"code_ine", style3)
co += 3
for i in range(NbWeeks):
ws0.write(li, co + i, "", style2b)
cell_2 = __make_cell(ws, title, style2)
ws.append([None, cell_2])
# ligne 2
cell_2 = __make_cell(ws, u"Discipline :", style2)
ws.append([None, cell_2])
# ligne 3
cell_2 = __make_cell(ws, u"Enseignant :", style2)
cell_6 = __make_cell(ws, ("Groupe %s" % groupname), style3)
ws.append([None, cell_2, None, None, None, None, cell_6])
# ligne 4: Avertissement pour ne pas confondre avec listes notes
cell_2 = __make_cell(ws, u"Ne pas utiliser cette feuille pour saisir les notes !", style1i)
ws.append([None, None, cell_2])
ws.append([None])
ws.append([None])
# ligne 7: Entête (contruction dans une liste cells)
cells = [None] # passe la première colonne
cell_2 = __make_cell(ws, u"Nom", style3)
cells.append(cell_2)
for partition in partitions:
cells.append(__make_cell(ws, partition["partition_name"], style3))
if with_codes:
cells.append(__make_cell(ws, u"etudid", style3))
cells.append(__make_cell(ws, u"code_nip", style3))
cells.append(__make_cell(ws, u"code_ine", style3))
for i in range(nb_weeks):
cells.append(__make_cell(ws, "", style2b))
ws.append(cells)
n = 0
# pour chaque étudiant
for t in lines:
n += 1
li += 1
ws0.write(li, 0, n, style1b)
nomprenom = (
t["civilite_str"]
+ " "
@ -631,45 +616,39 @@ def Excel_feuille_listeappel(
elif not paie:
nomprenom += " (non paiement)"
style_nom = style2t3bold
ws0.write(li, 1, nomprenom.decode(scu.SCO_ENCODING), style_nom)
co = 2
cell_1 = __make_cell(ws, n, style1b)
cell_2 = __make_cell(ws, nomprenom, style_nom)
cells = [cell_1, cell_2]
for partition in partitions:
if partition["partition_name"]:
ws0.write(
li,
co,
t.get(partition["partition_id"], "").decode(scu.SCO_ENCODING),
style2t3,
cells.append(
__make_cell(ws, t.get(partition["partition_id"], u""), style2t3)
)
co += 1
if with_codes:
ws0.write(li, coc, t["etudid"].decode(scu.SCO_ENCODING), style2t3)
if t["code_nip"]:
code_nip = t["code_nip"].decode(scu.SCO_ENCODING)
else:
code_nip = u""
ws0.write(li, coc + 1, code_nip, style2t3)
if t["code_ine"]:
code_ine = t["code_ine"].decode(scu.SCO_ENCODING)
else:
code_ine = u""
ws0.write(li, coc + 2, code_ine, style2t3)
if t["etath"]:
etath = t["etath"].decode(scu.SCO_ENCODING)
else:
etath = u""
ws0.write(li, co, etath, style2b) # etat
for i in range(1, NbWeeks):
ws0.write(li, co + i, u"", style2b) # cellules vides
ws0.row(li).height = 850 # sans effet ?
#
li += 2
cells.append(__make_cell(ws, t["etudid"], style2t3))
code_nip = t.get("code_nip", u"")
cells.append(__make_cell(ws, code_nip, style2t3))
code_ine = t.get("code_ine", u"")
cells.append(__make_cell(ws, code_ine, style2t3))
cells.append(__make_cell(ws, t.get("etath", ""), style2b))
for i in range(1, nb_weeks):
cells.append(__make_cell(ws, style=style2t3))
# ws0.row(li).height = 850 # sans effet ?
# (openpyxl: en mode optimisé, les hauteurs de lignes doivent être spécifiées avant toutes les cellules)
ws.append(cells)
ws.append([None])
# bas de page (date, serveur)
dt = time.strftime("%d/%m/%Y à %Hh%M")
if server_name:
dt += " sur " + server_name
ws0.write(li, 1, ("Liste éditée le " + dt).decode(scu.SCO_ENCODING), style1i)
#
ws0.col(0).width = 850
ws0.col(1).width = 9000
cell_2 = __make_cell(ws, ("Liste éditée le " + dt), style1i)
ws.append([None, cell_2])
return wb.savetostr()
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
with NamedTemporaryFile() as tmp:
wb.save(tmp.name)
tmp.seek(0)
return tmp.read()

View File

@ -724,18 +724,12 @@ def groups_table(
return tab.make_page(context, format=format, REQUEST=REQUEST)
elif format == "xlsappel":
xls = sco_excel.Excel_feuille_listeappel(
context,
groups_infos.formsemestre, # le 1er semestre, serait à modifier si plusieurs
groups_infos.groups_titles,
groups_infos.members,
partitions=groups_infos.partitions,
with_codes=with_codes,
with_paiement=with_paiement,
server_name=REQUEST.BASE0,
)
filename = "liste_%s" % groups_infos.groups_filename + ".xls"
return sco_excel.sendExcelFile(REQUEST, xls, filename)
xls = sco_excel.excel_feuille_listeappel(groups_infos.formsemestre, groups_infos.groups_titles,
groups_infos.members, partitions=groups_infos.partitions,
with_codes=with_codes, with_paiement=with_paiement,
server_name=REQUEST.BASE0)
filename = "liste_%s" % groups_infos.groups_filename + ".xlsx"
return sco_excel.send_excel_file(REQUEST, xls, filename)
elif format == "allxls":
# feuille Excel avec toutes les infos etudiants
if not groups_infos.members:
@ -806,7 +800,7 @@ def groups_table(
title = "etudiants_%s" % groups_infos.groups_filename
xls = sco_excel.Excel_SimpleTable(titles=titles, lines=L, SheetName=title)
filename = title + ".xls"
return sco_excel.sendExcelFile(REQUEST, xls, filename)
return sco_excel.send_excel_file(REQUEST, xls, filename)
else:
raise ValueError("unsupported format")

View File

@ -301,6 +301,7 @@ CSV_FIELDSEP = ";"
CSV_LINESEP = "\n"
CSV_MIMETYPE = "text/comma-separated-values"
XLS_MIMETYPE = "application/vnd.ms-excel"
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
PDF_MIMETYPE = "application/pdf"
XML_MIMETYPE = "text/xml"
JSON_MIMETYPE = "application/json"

View File

@ -1,3 +1,4 @@
openpyxl
alembic==1.6.5
attrs==21.2.0
Babel==2.9.1