Tableau recap: export xls. (et abandon de l'export CSV).
This commit is contained in:
parent
e4ab0d5885
commit
a9696fe598
@ -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:
|
|||||||
* 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(
|
|||||||
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(
|
|||||||
|
|
||||||
|
|
||||||
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),
|
# preferences=sco_preferences.SemPreferences(formsemestre_id=formsemestre.id),
|
||||||
)
|
)
|
||||||
|
|
||||||
# tab = GenTable(
|
return table.excel(), filename
|
||||||
# columns_ids=column_ids,
|
|
||||||
# titles=titles,
|
|
||||||
# rows=rows + footer_rows,
|
|
||||||
# preferences=sco_preferences.SemPreferences(formsemestre_id=formsemestre.id),
|
|
||||||
# )
|
|
||||||
|
|
||||||
return table.gen(format=format), filename
|
|
||||||
|
@ -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:
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
|
|
||||||
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):
|
|||||||
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
|
||||||
|
Loading…
Reference in New Issue
Block a user