forked from ScoDoc/ScoDoc
Export excel table recap avec colonnes choisies en HTML
This commit is contained in:
parent
cf78081234
commit
d3672423db
@ -63,6 +63,7 @@ def formsemestre_recapcomplet(
|
||||
xml_with_decisions=False,
|
||||
force_publishing=True,
|
||||
selected_etudid=None,
|
||||
visible_col_ids=None,
|
||||
):
|
||||
"""Page récapitulant les notes d'un semestre.
|
||||
Grand tableau récapitulatif avec toutes les notes de modules
|
||||
@ -86,7 +87,7 @@ def formsemestre_recapcomplet(
|
||||
if not isinstance(formsemestre_id, int):
|
||||
abort(404)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
|
||||
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xlsvisible", "xml"}
|
||||
supported_formats = file_formats | {"html", "evals"}
|
||||
if tabformat not in supported_formats:
|
||||
raise ScoValueError(f"Format non supporté: {tabformat}")
|
||||
@ -94,6 +95,7 @@ def formsemestre_recapcomplet(
|
||||
mode_jury = int(mode_jury)
|
||||
xml_with_decisions = int(xml_with_decisions)
|
||||
force_publishing = int(force_publishing)
|
||||
visible_col_ids = visible_col_ids.split(",") if visible_col_ids else None
|
||||
filename = scu.sanitize_filename(
|
||||
f"""{'jury' if mode_jury else 'recap'
|
||||
}{'-evals' if tabformat == 'xlsall' else ''
|
||||
@ -107,6 +109,7 @@ def formsemestre_recapcomplet(
|
||||
filename=filename,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
visible_col_ids=visible_col_ids,
|
||||
)
|
||||
|
||||
table_html, _, freq_codes_annuels = _formsemestre_recapcomplet_to_html(
|
||||
@ -124,8 +127,9 @@ def formsemestre_recapcomplet(
|
||||
]
|
||||
if len(formsemestre.inscriptions) > 0:
|
||||
H.append(
|
||||
f"""<form name="f" method="get" action="{request.base_url}">
|
||||
f"""<form id="export_menu" name="f" method="get" action="{request.base_url}">
|
||||
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"></input>
|
||||
<input type="hidden" id="visible_col_ids" name="visible_col_ids" value=""></input>
|
||||
"""
|
||||
)
|
||||
if mode_jury:
|
||||
@ -133,13 +137,14 @@ def formsemestre_recapcomplet(
|
||||
f'<input type="hidden" name="mode_jury" value="{mode_jury}"></input>'
|
||||
)
|
||||
H.append(
|
||||
'<select name="tabformat" onchange="document.f.submit()" class="noprint">'
|
||||
'<select name="tabformat" id="tabformat" onchange="submit_from_export_menu();" class="noprint">'
|
||||
)
|
||||
for fmt, label in (
|
||||
("html", "Tableau"),
|
||||
("evals", "Avec toutes les évaluations"),
|
||||
("xlsx", "Excel (non formaté)"),
|
||||
("xlsall", "Excel avec évaluations"),
|
||||
("xlsvisible", "Excel avec colonnes telles affichées"),
|
||||
("json", "Bulletins JSON"),
|
||||
):
|
||||
if fmt == tabformat:
|
||||
@ -314,16 +319,19 @@ def _formsemestre_recapcomplet_to_file(
|
||||
xml_nodate=False, # format XML sans dates (sert pour debug cache: comparaison de XML)
|
||||
xml_with_decisions=False,
|
||||
force_publishing=True,
|
||||
visible_col_ids=None,
|
||||
):
|
||||
"""Calcule et renvoie le tableau récapitulatif."""
|
||||
if tabformat.startswith("xls"):
|
||||
include_evaluations = tabformat == "xlsall"
|
||||
visible_col_ids = visible_col_ids if tabformat == "xlsvisible" else None
|
||||
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
data, filename = gen_formsemestre_recapcomplet_excel(
|
||||
res,
|
||||
mode_jury=mode_jury,
|
||||
include_evaluations=include_evaluations,
|
||||
filename=filename,
|
||||
visible_col_ids=visible_col_ids,
|
||||
)
|
||||
mime, suffix = scu.get_mime_suffix("xlsx")
|
||||
return scu.send_file(data, filename=filename, mime=mime, suffix=suffix)
|
||||
@ -537,11 +545,13 @@ def gen_formsemestre_recapcomplet_excel(
|
||||
mode_jury: bool = False,
|
||||
include_evaluations=False,
|
||||
filename: str = "",
|
||||
visible_col_ids: list[str] | None = None,
|
||||
) -> tuple:
|
||||
"""Génère le tableau recap ou jury en excel (xlsx).
|
||||
Utilisé pour menu (export excel), archives et autres besoins particuliers (API).
|
||||
Attention: le tableau exporté depuis la page html est celui généré en js par DataTables,
|
||||
et non celui-ci.
|
||||
Si visible_col_ids est non None, ne génère que les colonnes indiquées (+ les codes étudiants, toujours présents)
|
||||
"""
|
||||
# En excel, ajoute les adresses mail, si on a le droit de les voir.
|
||||
table = _gen_formsemestre_recapcomplet_table(
|
||||
@ -552,5 +562,9 @@ def gen_formsemestre_recapcomplet_excel(
|
||||
convert_values=False,
|
||||
filename=filename,
|
||||
)
|
||||
|
||||
return table.excel(), filename
|
||||
if visible_col_ids is not None:
|
||||
# Ajoute colonnes qui doivent toujours être présentes en excel:
|
||||
for cod in ("code_nip", "etudid"):
|
||||
if cod not in visible_col_ids:
|
||||
visible_col_ids = [cod] + visible_col_ids
|
||||
return table.excel(col_ids=visible_col_ids), filename
|
||||
|
@ -326,3 +326,31 @@ $(function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// liste des id de colonnes visibles, dans leur ordre d'affichage
|
||||
// (chaine avec ids séparés par des virgules)
|
||||
function get_visible_column_ids() {
|
||||
const table = $("table.table_recap").DataTable();
|
||||
const visibles = table.columns().visible();
|
||||
let col_ids = "";
|
||||
for (i=0; i < visibles.length; i++) {
|
||||
if (visibles[i]) {
|
||||
let th = table.column(i).header();
|
||||
if (col_ids.length) {
|
||||
col_ids += ",";
|
||||
}
|
||||
col_ids += th.dataset.col_id;
|
||||
}
|
||||
}
|
||||
return col_ids;
|
||||
}
|
||||
|
||||
function submit_from_export_menu() {
|
||||
let form = document.querySelector("#export_menu");
|
||||
let tabformat = document.getElementById("tabformat").value;
|
||||
if (tabformat == "xlsvisible") {
|
||||
let cols_input = document.getElementById("visible_col_ids");
|
||||
cols_input.value = get_visible_column_ids();
|
||||
}
|
||||
form.submit();
|
||||
}
|
@ -90,8 +90,8 @@ class Table(Element):
|
||||
"ordered list of column groups names"
|
||||
self.group_titles = {}
|
||||
"title (in header top row) for the group"
|
||||
self.head = []
|
||||
self.foot = []
|
||||
self.head: list["Row"] = []
|
||||
self.foot: list["Row"] = []
|
||||
self.column_group = {}
|
||||
"the group of the column: { col_id : group }"
|
||||
self.column_classes: defaultdict[str, set[str]] = defaultdict(set)
|
||||
@ -281,6 +281,7 @@ class Table(Element):
|
||||
col_id,
|
||||
None,
|
||||
title,
|
||||
attrs={"data-col_id": col_id},
|
||||
classes=classes,
|
||||
group=self.column_group.get(col_id),
|
||||
raw_content=raw_title or title,
|
||||
@ -297,8 +298,10 @@ class Table(Element):
|
||||
foot_cell = self.foot_title_row.cells[col_id] if self.foot_title_row else None
|
||||
return head_cell, foot_cell
|
||||
|
||||
def excel(self, wb: Workbook = None):
|
||||
"""Simple Excel representation of the table."""
|
||||
def excel(self, wb: Workbook = None, col_ids=None):
|
||||
"""Simple Excel representation of the table.
|
||||
Si col_ids(liste d'ids) est spécifié, ne génère que ces colonnes, dans l'ordre.
|
||||
"""
|
||||
self._prepare()
|
||||
if wb is None:
|
||||
sheet = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
|
||||
@ -309,13 +312,13 @@ class Table(Element):
|
||||
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))
|
||||
sheet.append_row(row.to_excel(sheet, style=style_bold, col_ids=col_ids))
|
||||
|
||||
for row in self.rows:
|
||||
sheet.append_row(row.to_excel(sheet, style=style_base))
|
||||
sheet.append_row(row.to_excel(sheet, style=style_base, col_ids=col_ids))
|
||||
|
||||
for row in self.foot:
|
||||
sheet.append_row(row.to_excel(sheet, style=style_base))
|
||||
sheet.append_row(row.to_excel(sheet, style=style_base, col_ids=col_ids))
|
||||
|
||||
if self.caption:
|
||||
sheet.append_blank_row() # empty line
|
||||
@ -325,9 +328,10 @@ class Table(Element):
|
||||
sheet.append_single_cell_row(self.origin, style_base)
|
||||
|
||||
# Largeurs des colonnes
|
||||
actual_col_ids = col_ids if col_ids else self.column_ids
|
||||
for col_id, width in self.xls_columns_width.items():
|
||||
try:
|
||||
idx = self.column_ids.index(col_id)
|
||||
idx = actual_col_ids.index(col_id)
|
||||
col = get_column_letter(idx + 1)
|
||||
sheet.set_column_dimension_width(col, width)
|
||||
except ValueError:
|
||||
@ -365,7 +369,7 @@ class Row(Element):
|
||||
title: str,
|
||||
content: str,
|
||||
group: str = None,
|
||||
attrs: list[str] = None,
|
||||
attrs: dict[str, str] = None,
|
||||
classes: list[str] = None,
|
||||
data: dict[str, str] = None,
|
||||
elt: str = None,
|
||||
@ -466,9 +470,15 @@ class Row(Element):
|
||||
for col_id in self.table.raw_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)
|
||||
def to_excel(self, sheet, style=None, col_ids=None) -> list:
|
||||
"""Build excel row for given sheet.
|
||||
If col_ids is given, generate only this columns
|
||||
"""
|
||||
if col_ids is None:
|
||||
return sheet.make_row(self.to_dict().values(), style=style)
|
||||
# Version avec seulement colonnes spécifiées:
|
||||
d = self.to_dict()
|
||||
return sheet.make_row([d[k] for k in col_ids if k in d], style=style)
|
||||
|
||||
|
||||
class BottomRow(Row):
|
||||
|
Loading…
x
Reference in New Issue
Block a user