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,
|
xml_with_decisions=False,
|
||||||
force_publishing=True,
|
force_publishing=True,
|
||||||
selected_etudid=None,
|
selected_etudid=None,
|
||||||
|
visible_col_ids=None,
|
||||||
):
|
):
|
||||||
"""Page récapitulant les notes d'un semestre.
|
"""Page récapitulant les notes d'un semestre.
|
||||||
Grand tableau récapitulatif avec toutes les notes de modules
|
Grand tableau récapitulatif avec toutes les notes de modules
|
||||||
@ -86,7 +87,7 @@ def formsemestre_recapcomplet(
|
|||||||
if not isinstance(formsemestre_id, int):
|
if not isinstance(formsemestre_id, int):
|
||||||
abort(404)
|
abort(404)
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
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"}
|
supported_formats = file_formats | {"html", "evals"}
|
||||||
if tabformat not in supported_formats:
|
if tabformat not in supported_formats:
|
||||||
raise ScoValueError(f"Format non supporté: {tabformat}")
|
raise ScoValueError(f"Format non supporté: {tabformat}")
|
||||||
@ -94,6 +95,7 @@ def formsemestre_recapcomplet(
|
|||||||
mode_jury = int(mode_jury)
|
mode_jury = int(mode_jury)
|
||||||
xml_with_decisions = int(xml_with_decisions)
|
xml_with_decisions = int(xml_with_decisions)
|
||||||
force_publishing = int(force_publishing)
|
force_publishing = int(force_publishing)
|
||||||
|
visible_col_ids = visible_col_ids.split(",") if visible_col_ids else None
|
||||||
filename = scu.sanitize_filename(
|
filename = scu.sanitize_filename(
|
||||||
f"""{'jury' if mode_jury else 'recap'
|
f"""{'jury' if mode_jury else 'recap'
|
||||||
}{'-evals' if tabformat == 'xlsall' else ''
|
}{'-evals' if tabformat == 'xlsall' else ''
|
||||||
@ -107,6 +109,7 @@ def formsemestre_recapcomplet(
|
|||||||
filename=filename,
|
filename=filename,
|
||||||
xml_with_decisions=xml_with_decisions,
|
xml_with_decisions=xml_with_decisions,
|
||||||
force_publishing=force_publishing,
|
force_publishing=force_publishing,
|
||||||
|
visible_col_ids=visible_col_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
table_html, _, freq_codes_annuels = _formsemestre_recapcomplet_to_html(
|
table_html, _, freq_codes_annuels = _formsemestre_recapcomplet_to_html(
|
||||||
@ -124,8 +127,9 @@ def formsemestre_recapcomplet(
|
|||||||
]
|
]
|
||||||
if len(formsemestre.inscriptions) > 0:
|
if len(formsemestre.inscriptions) > 0:
|
||||||
H.append(
|
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" name="formsemestre_id" value="{formsemestre_id}"></input>
|
||||||
|
<input type="hidden" id="visible_col_ids" name="visible_col_ids" value=""></input>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
if mode_jury:
|
if mode_jury:
|
||||||
@ -133,13 +137,14 @@ def formsemestre_recapcomplet(
|
|||||||
f'<input type="hidden" name="mode_jury" value="{mode_jury}"></input>'
|
f'<input type="hidden" name="mode_jury" value="{mode_jury}"></input>'
|
||||||
)
|
)
|
||||||
H.append(
|
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 (
|
for fmt, label in (
|
||||||
("html", "Tableau"),
|
("html", "Tableau"),
|
||||||
("evals", "Avec toutes les évaluations"),
|
("evals", "Avec toutes les évaluations"),
|
||||||
("xlsx", "Excel (non formaté)"),
|
("xlsx", "Excel (non formaté)"),
|
||||||
("xlsall", "Excel avec évaluations"),
|
("xlsall", "Excel avec évaluations"),
|
||||||
|
("xlsvisible", "Excel avec colonnes telles affichées"),
|
||||||
("json", "Bulletins JSON"),
|
("json", "Bulletins JSON"),
|
||||||
):
|
):
|
||||||
if fmt == tabformat:
|
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_nodate=False, # format XML sans dates (sert pour debug cache: comparaison de XML)
|
||||||
xml_with_decisions=False,
|
xml_with_decisions=False,
|
||||||
force_publishing=True,
|
force_publishing=True,
|
||||||
|
visible_col_ids=None,
|
||||||
):
|
):
|
||||||
"""Calcule et renvoie le tableau récapitulatif."""
|
"""Calcule et renvoie le tableau récapitulatif."""
|
||||||
if tabformat.startswith("xls"):
|
if tabformat.startswith("xls"):
|
||||||
include_evaluations = tabformat == "xlsall"
|
include_evaluations = tabformat == "xlsall"
|
||||||
|
visible_col_ids = visible_col_ids if tabformat == "xlsvisible" else None
|
||||||
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
data, filename = gen_formsemestre_recapcomplet_excel(
|
data, filename = gen_formsemestre_recapcomplet_excel(
|
||||||
res,
|
res,
|
||||||
mode_jury=mode_jury,
|
mode_jury=mode_jury,
|
||||||
include_evaluations=include_evaluations,
|
include_evaluations=include_evaluations,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
|
visible_col_ids=visible_col_ids,
|
||||||
)
|
)
|
||||||
mime, suffix = scu.get_mime_suffix("xlsx")
|
mime, suffix = scu.get_mime_suffix("xlsx")
|
||||||
return scu.send_file(data, filename=filename, mime=mime, suffix=suffix)
|
return scu.send_file(data, filename=filename, mime=mime, suffix=suffix)
|
||||||
@ -537,11 +545,13 @@ def gen_formsemestre_recapcomplet_excel(
|
|||||||
mode_jury: bool = False,
|
mode_jury: bool = False,
|
||||||
include_evaluations=False,
|
include_evaluations=False,
|
||||||
filename: str = "",
|
filename: str = "",
|
||||||
|
visible_col_ids: list[str] | None = None,
|
||||||
) -> tuple:
|
) -> tuple:
|
||||||
"""Génère le tableau recap ou jury en excel (xlsx).
|
"""Génère le tableau recap ou jury en excel (xlsx).
|
||||||
Utilisé pour menu (export excel), archives et autres besoins particuliers (API).
|
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,
|
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.
|
||||||
|
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.
|
# En excel, ajoute les adresses mail, si on a le droit de les voir.
|
||||||
table = _gen_formsemestre_recapcomplet_table(
|
table = _gen_formsemestre_recapcomplet_table(
|
||||||
@ -552,5 +562,9 @@ def gen_formsemestre_recapcomplet_excel(
|
|||||||
convert_values=False,
|
convert_values=False,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
)
|
)
|
||||||
|
if visible_col_ids is not None:
|
||||||
return table.excel(), filename
|
# 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"
|
"ordered list of column groups names"
|
||||||
self.group_titles = {}
|
self.group_titles = {}
|
||||||
"title (in header top row) for the group"
|
"title (in header top row) for the group"
|
||||||
self.head = []
|
self.head: list["Row"] = []
|
||||||
self.foot = []
|
self.foot: list["Row"] = []
|
||||||
self.column_group = {}
|
self.column_group = {}
|
||||||
"the group of the column: { col_id : group }"
|
"the group of the column: { col_id : group }"
|
||||||
self.column_classes: defaultdict[str, set[str]] = defaultdict(set)
|
self.column_classes: defaultdict[str, set[str]] = defaultdict(set)
|
||||||
@ -281,6 +281,7 @@ class Table(Element):
|
|||||||
col_id,
|
col_id,
|
||||||
None,
|
None,
|
||||||
title,
|
title,
|
||||||
|
attrs={"data-col_id": col_id},
|
||||||
classes=classes,
|
classes=classes,
|
||||||
group=self.column_group.get(col_id),
|
group=self.column_group.get(col_id),
|
||||||
raw_content=raw_title or title,
|
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
|
foot_cell = self.foot_title_row.cells[col_id] if self.foot_title_row else None
|
||||||
return head_cell, foot_cell
|
return head_cell, foot_cell
|
||||||
|
|
||||||
def excel(self, wb: Workbook = None):
|
def excel(self, wb: Workbook = None, col_ids=None):
|
||||||
"""Simple Excel representation of the table."""
|
"""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()
|
self._prepare()
|
||||||
if wb is None:
|
if wb is None:
|
||||||
sheet = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
|
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()
|
style_base = self.xls_style_base or sco_excel.excel_make_style()
|
||||||
|
|
||||||
for row in self.head:
|
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:
|
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:
|
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:
|
if self.caption:
|
||||||
sheet.append_blank_row() # empty line
|
sheet.append_blank_row() # empty line
|
||||||
@ -325,9 +328,10 @@ class Table(Element):
|
|||||||
sheet.append_single_cell_row(self.origin, style_base)
|
sheet.append_single_cell_row(self.origin, style_base)
|
||||||
|
|
||||||
# Largeurs des colonnes
|
# 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():
|
for col_id, width in self.xls_columns_width.items():
|
||||||
try:
|
try:
|
||||||
idx = self.column_ids.index(col_id)
|
idx = actual_col_ids.index(col_id)
|
||||||
col = get_column_letter(idx + 1)
|
col = get_column_letter(idx + 1)
|
||||||
sheet.set_column_dimension_width(col, width)
|
sheet.set_column_dimension_width(col, width)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -365,7 +369,7 @@ class Row(Element):
|
|||||||
title: str,
|
title: str,
|
||||||
content: str,
|
content: str,
|
||||||
group: str = None,
|
group: str = None,
|
||||||
attrs: list[str] = None,
|
attrs: dict[str, str] = None,
|
||||||
classes: list[str] = None,
|
classes: list[str] = None,
|
||||||
data: dict[str, str] = None,
|
data: dict[str, str] = None,
|
||||||
elt: str = None,
|
elt: str = None,
|
||||||
@ -466,9 +470,15 @@ class Row(Element):
|
|||||||
for col_id in self.table.raw_column_ids
|
for col_id in self.table.raw_column_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_excel(self, sheet, style=None) -> list:
|
def to_excel(self, sheet, style=None, col_ids=None) -> list:
|
||||||
"build excel row for given sheet"
|
"""Build excel row for given sheet.
|
||||||
return sheet.make_row(self.to_dict().values(), style=style)
|
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):
|
class BottomRow(Row):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user