Skip to content
Snippets Groups Projects
Commit a9696fe5 authored by Emmanuel Viennet's avatar Emmanuel Viennet
Browse files

Tableau recap: export xls. (et abandon de l'export CSV).

parent e4ab0d58
No related branches found
No related tags found
No related merge requests found
...@@ -334,9 +334,7 @@ def do_formsemestre_archive( ...@@ -334,9 +334,7 @@ def do_formsemestre_archive(
etudids = [m["etudid"] for m in groups_infos.members] etudids = [m["etudid"] for m in groups_infos.members]
# Tableau recap notes en XLS (pour tous les etudiants, n'utilise pas les groupes) # Tableau recap notes en XLS (pour tous les etudiants, n'utilise pas les groupes)
data, _ = gen_formsemestre_recapcomplet_excel( data, _ = gen_formsemestre_recapcomplet_excel(res, include_evaluations=True)
formsemestre, res, include_evaluations=True, format="xls"
)
if data: if data:
PVArchive.store(archive_id, "Tableau_moyennes" + scu.XLSX_SUFFIX, data) PVArchive.store(archive_id, "Tableau_moyennes" + scu.XLSX_SUFFIX, data)
# Tableau recap notes en HTML (pour tous les etudiants, n'utilise pas les groupes) # Tableau recap notes en HTML (pour tous les etudiants, n'utilise pas les groupes)
......
...@@ -194,12 +194,12 @@ class ScoExcelSheet: ...@@ -194,12 +194,12 @@ class ScoExcelSheet:
* pour finir appel de la méthode de génération * pour finir appel de la méthode de génération
""" """
def __init__(self, sheet_name="feuille", default_style=None, wb=None): def __init__(self, sheet_name="feuille", default_style=None, wb: Workbook = None):
"""Création de la feuille. sheet_name """Création de la feuille. sheet_name
-- le nom de la feuille default_style -- le nom de la feuille default_style
-- le style par défaut des cellules ws -- 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 -- None si la feuille est autonome (dans ce cas elle 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. créée par le workbook propriétaire un workbook est créé 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' ?)
......
...@@ -225,16 +225,14 @@ def _do_formsemestre_recapcomplet( ...@@ -225,16 +225,14 @@ def _do_formsemestre_recapcomplet(
selected_etudid=selected_etudid, selected_etudid=selected_etudid,
) )
return data return data
elif format.startswith("xls") or format == "csv": elif format.startswith("xls"):
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
include_evaluations = format in {"xlsall", "csv "} include_evaluations = format in {"xlsall", "csv "} # csv not supported anymore
if format != "csv": if format != "csv":
format = "xlsx" format = "xlsx"
data, filename = gen_formsemestre_recapcomplet_excel( data, filename = gen_formsemestre_recapcomplet_excel(
formsemestre,
res, res,
include_evaluations=include_evaluations, include_evaluations=include_evaluations,
format=format,
filename=filename, filename=filename,
) )
return scu.send_file(data, filename=filename, mime=scu.get_mime_suffix(format)) return scu.send_file(data, filename=filename, mime=scu.get_mime_suffix(format))
...@@ -446,33 +444,22 @@ def _gen_formsemestre_recapcomplet_html( ...@@ -446,33 +444,22 @@ def _gen_formsemestre_recapcomplet_html(
def gen_formsemestre_recapcomplet_excel( def gen_formsemestre_recapcomplet_excel(
formsemestre: FormSemestre,
res: NotesTableCompat, res: NotesTableCompat,
include_evaluations=False, include_evaluations=False,
filename: str = "", filename: str = "",
format="xls",
) -> tuple: ) -> tuple:
"""Génère le tableau recap en excel (xlsx) ou CSV. """Génère le tableau recap en excel (xlsx).
Utilisé pour archives et autres besoins particuliers (API). Utilisé pour archives et autres besoins particuliers (API).
Attention: le tableau exporté depuis la page html est celui généré en js par DataTables, Attention: le tableau exporté depuis la page html est celui généré en js par DataTables,
et non celui-ci. et non celui-ci.
""" """
suffix = scu.CSV_SUFFIX if format == "csv" else scu.XLSX_SUFFIX filename += scu.XLSX_SUFFIX
filename += suffix
# XXX TODO A ADAPTER XXX !!! !!!
table = TableRecap( table = TableRecap(
res, res,
convert_values=False, convert_values=False,
include_evaluations=include_evaluations, include_evaluations=include_evaluations,
preferences=sco_preferences.SemPreferences(formsemestre_id=formsemestre.id),
)
# tab = GenTable(
# columns_ids=column_ids,
# titles=titles,
# rows=rows + footer_rows,
# preferences=sco_preferences.SemPreferences(formsemestre_id=formsemestre.id), # preferences=sco_preferences.SemPreferences(formsemestre_id=formsemestre.id),
# ) )
return table.gen(format=format), filename return table.excel(), filename
...@@ -8,8 +8,15 @@ ...@@ -8,8 +8,15 @@
""" """
from collections import defaultdict from collections import defaultdict
from openpyxl import Workbook
from openpyxl.utils import get_column_letter
from app.scodoc import sco_excel
class Element: class Element:
"Element de base pour les tables"
def __init__( def __init__(
self, self,
elt: str, elt: str,
...@@ -49,18 +56,6 @@ class Element: ...@@ -49,18 +56,6 @@ class Element:
class Table(Element): class Table(Element):
"""Construction d'une table de résultats """Construction d'une table de résultats
table = Table()
row = table.new_row(id="xxx", category="yyy")
row.new_cell( col_id, title, content [,classes] [, idx], [group], [keys:dict={}] )
rows = table.get_rows([category="yyy"])
table.sort_rows(key [, reverse])
table.set_titles(titles)
table.update_titles(titles)
table.set_column_groups(groups: list[str])
table.insert_group(group:str, [after=str], [before=str])
Ordre des colonnes: groupées par groupes, et dans chaque groupe par ordre d'insertion Ordre des colonnes: groupées par groupes, et dans chaque groupe par ordre d'insertion
On fixe l'ordre des groupes par ordre d'insertion On fixe l'ordre des groupes par ordre d'insertion
ou par insert_group ou par set_column_groups. ou par insert_group ou par set_column_groups.
...@@ -74,6 +69,12 @@ class Table(Element): ...@@ -74,6 +69,12 @@ class Table(Element):
attrs: dict[str, str] = None, attrs: dict[str, str] = None,
data: dict = None, data: dict = None,
row_class=None, row_class=None,
xls_sheet_name="feuille",
xls_before_table=[], # liste de cellules a placer avant la table
xls_style_base=None, # style excel pour les cellules
xls_columns_width=None, # { col_id : largeur en "pixels excel" }
caption="",
origin="",
): ):
super().__init__("table", classes=classes, attrs=attrs, data=data) super().__init__("table", classes=classes, attrs=attrs, data=data)
self.row_class = row_class or Row self.row_class = row_class or Row
...@@ -103,6 +104,14 @@ class Table(Element): ...@@ -103,6 +104,14 @@ class Table(Element):
self, "title_foot", cell_elt="th", classes=["titles"] self, "title_foot", cell_elt="th", classes=["titles"]
) )
self.empty_cell = Cell.empty() self.empty_cell = Cell.empty()
# Excel (xls) spécifique:
self.xls_before_table = xls_before_table
self.xls_columns_width = xls_columns_width or {}
self.xls_sheet_name = xls_sheet_name
self.xls_style_base = xls_style_base
#
self.caption = caption
self.origin = origin
def _prepare(self): def _prepare(self):
"""Prepare the table before generation: """Prepare the table before generation:
...@@ -130,7 +139,7 @@ class Table(Element): ...@@ -130,7 +139,7 @@ class Table(Element):
self.selected_row_id = row_id self.selected_row_id = row_id
def to_list(self) -> list[dict]: def to_list(self) -> list[dict]:
"""as a list, each row is a dict""" """as a list, each row is a dict (sans les lignes d'en-tête ni de pied de table)"""
self._prepare() self._prepare()
return [row.to_dict() for row in self.rows] return [row.to_dict() for row in self.rows]
...@@ -265,6 +274,45 @@ class Table(Element): ...@@ -265,6 +274,45 @@ class Table(Element):
return self.head_title_row.cells.get(col_id), self.foot_title_row.cells[col_id] return self.head_title_row.cells.get(col_id), self.foot_title_row.cells[col_id]
def excel(self, wb: Workbook = None):
"""Simple Excel representation of the table."""
self._prepare()
if wb is None:
sheet = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
else:
sheet = wb.create_sheet(sheet_name=self.xls_sheet_name)
sheet.rows += self.xls_before_table
style_bold = sco_excel.excel_make_style(bold=True)
style_base = self.xls_style_base or sco_excel.excel_make_style()
for row in self.head:
sheet.append_row(row.to_excel(sheet, style=style_bold))
for row in self.rows:
sheet.append_row(row.to_excel(sheet, style=style_base))
for row in self.foot:
sheet.append_row(row.to_excel(sheet, style=style_base))
if self.caption:
sheet.append_blank_row() # empty line
sheet.append_single_cell_row(self.caption, style_base)
if self.origin:
sheet.append_blank_row() # empty line
sheet.append_single_cell_row(self.origin, style_base)
# Largeurs des colonnes
for col_id, width in self.xls_columns_width.items():
try:
idx = self.column_ids.index(col_id)
col = get_column_letter(idx + 1)
sheet.set_column_dimension_width(col, width)
except ValueError:
pass
if wb is None:
return sheet.generate()
class Row(Element): class Row(Element):
"""A row.""" """A row."""
...@@ -371,6 +419,10 @@ class Row(Element): ...@@ -371,6 +419,10 @@ class Row(Element):
for col_id in self.table.column_ids for col_id in self.table.column_ids
} }
def to_excel(self, sheet, style=None) -> list:
"build excel row for given sheet"
return sheet.make_row(self.to_dict().values(), style=style)
class BottomRow(Row): class BottomRow(Row):
"""Une ligne spéciale pour le pied de table """Une ligne spéciale pour le pied de table
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment