Nettoyage code table recap.

This commit is contained in:
Emmanuel Viennet 2022-04-06 18:51:01 +02:00
parent 3ab0e89c2f
commit c488ad3a62
8 changed files with 192 additions and 800 deletions

View File

@ -542,17 +542,18 @@ class ResultatsSemestre(ResultatsCache):
# Le bonus sport appliqué sur cette UE
if (self.bonus_ues is not None) and (ue.id in self.bonus_ues):
val = self.bonus_ues[ue.id][etud.id] or ""
val_fmt = fmt_note(val)
val_fmt = val_fmt_html = fmt_note(val)
if val:
val_fmt = f'<span class="green-arrow-up"></span><span class="sp2l">{val_fmt}</span>'
val_fmt_html = f'<span class="green-arrow-up"></span><span class="sp2l">{val_fmt}</span>'
idx = add_cell(
row,
f"bonus_ue_{ue.id}",
f"Bonus {ue.acronyme}",
val_fmt,
val_fmt_html,
"col_ue_bonus",
idx,
)
row[f"_bonus_ue_{ue.id}_xls"] = val_fmt
# Les moyennes des modules (ou ressources et SAÉs) dans cette UE
idx_malus = idx # place pour colonne malus à gauche des modules
idx += 1
@ -581,20 +582,21 @@ class ResultatsSemestre(ResultatsCache):
col_id = (
f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}"
)
val_fmt = fmt_note(val)
val_fmt = val_fmt_html = fmt_note(val)
if modimpl.module.module_type == scu.ModuleType.MALUS:
val_fmt = (
val_fmt_html = (
(scu.EMO_RED_TRIANGLE_DOWN + val_fmt) if val else ""
)
idx = add_cell(
row,
col_id,
modimpl.module.code,
val_fmt,
val_fmt_html,
# class col_res mod_ue_123
f"col_{modimpl.module.type_abbrv()} mod_ue_{ue.id}",
idx,
)
row[f"_{col_id}_xls"] = val_fmt
if modimpl.module.module_type == scu.ModuleType.MALUS:
titles[f"_{col_id}_col_order"] = idx_malus
titles_bot[f"_{col_id}_target"] = url_for(
@ -611,17 +613,20 @@ class ResultatsSemestre(ResultatsCache):
f"_{col_id}_target_attrs"
] = f""" title="{modimpl.module.titre} ({nom_resp})" """
modimpl_ids.add(modimpl.id)
ue_valid_txt = f"{nb_ues_validables}/{len(ues_sans_bonus)}"
ue_valid_txt = (
ue_valid_txt_html
) = f"{nb_ues_validables}/{len(ues_sans_bonus)}"
if nb_ues_warning:
ue_valid_txt += " " + scu.EMO_WARNING
ue_valid_txt_html += " " + scu.EMO_WARNING
add_cell(
row,
"ues_validables",
"UEs",
ue_valid_txt,
ue_valid_txt_html,
"col_ue col_ues_validables",
29, # juste avant moy. gen.
)
row["_ues_validables_xls"] = ue_valid_txt
if nb_ues_warning:
row["_ues_validables_class"] += " moy_ue_warning"
elif nb_ues_validables < len(ues_sans_bonus):

View File

@ -209,7 +209,8 @@ class GenTable(object):
omit_hidden_lines=False,
pdf_mode=False, # apply special pdf reportlab processing
pdf_style_list=[], # modified: list of platypus table style commands
):
xls_mode=False, # get xls content if available
) -> list:
"table data as a list of lists (rows)"
T = []
line_num = 0 # line number in input data
@ -237,9 +238,14 @@ class GenTable(object):
# if colspan_count > 0:
# continue # skip cells after a span
if pdf_mode:
content = row.get(f"_{cid}_pdf", "") or row.get(cid, "") or ""
content = row.get(f"_{cid}_pdf", False) or row.get(cid, "")
elif xls_mode:
content = row.get(f"_{cid}_xls", False) or row.get(cid, "")
else:
content = row.get(cid, "") or "" # nota: None converted to ''
content = row.get(cid, "")
# Convert None to empty string ""
content = "" if content is None else content
colspan = row.get("_%s_colspan" % cid, 0)
if colspan > 1:
pdf_style_list.append(
@ -299,7 +305,7 @@ class GenTable(object):
return self.xml()
elif format == "json":
return self.json()
raise ValueError("GenTable: invalid format: %s" % format)
raise ValueError(f"GenTable: invalid format: {format}")
def _gen_html_row(self, row, line_num=0, elem="td", css_classes=""):
"row is a dict, returns a string <tr...>...</tr>"
@ -479,23 +485,23 @@ class GenTable(object):
def excel(self, wb=None):
"""Simple Excel representation of the table"""
if wb is None:
ses = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
sheet = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
else:
ses = wb.create_sheet(sheet_name=self.xls_sheet_name)
ses.rows += self.xls_before_table
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 = sco_excel.excel_make_style()
ses.append_row(ses.make_row(self.get_titles_list(), style_bold))
for line in self.get_data_list():
ses.append_row(ses.make_row(line, style_base))
sheet.append_row(sheet.make_row(self.get_titles_list(), style_bold))
for line in self.get_data_list(xls_mode=True):
sheet.append_row(sheet.make_row(line, style_base))
if self.caption:
ses.append_blank_row() # empty line
ses.append_single_cell_row(self.caption, style_base)
sheet.append_blank_row() # empty line
sheet.append_single_cell_row(self.caption, style_base)
if self.origin:
ses.append_blank_row() # empty line
ses.append_single_cell_row(self.origin, style_base)
sheet.append_blank_row() # empty line
sheet.append_single_cell_row(self.origin, style_base)
if wb is None:
return ses.generate()
return sheet.generate()
def text(self):
"raw text representation of the table"

View File

@ -47,14 +47,15 @@
qui est une description (humaine, format libre) de l'archive.
"""
import chardet
import datetime
import glob
import json
import mimetypes
import os
import re
import shutil
import time
import chardet
import flask
from flask import g, request
@ -63,7 +64,9 @@ from flask_login import current_user
import app.scodoc.sco_utils as scu
from config import Config
from app import log
from app.models import Departement
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import Departement, FormSemestre
from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc.sco_exceptions import (
AccessDenied,
@ -95,8 +98,8 @@ class BaseArchiver(object):
self.root = os.path.join(*dirs)
log("initialized archiver, path=" + self.root)
path = dirs[0]
for dir in dirs[1:]:
path = os.path.join(path, dir)
for directory in dirs[1:]:
path = os.path.join(path, directory)
try:
scu.GSL.acquire()
if not os.path.isdir(path):
@ -117,11 +120,11 @@ class BaseArchiver(object):
try:
scu.GSL.acquire()
if not os.path.isdir(dept_dir):
log("creating directory %s" % dept_dir)
log(f"creating directory {dept_dir}")
os.mkdir(dept_dir)
obj_dir = os.path.join(dept_dir, str(oid))
if not os.path.isdir(obj_dir):
log("creating directory %s" % obj_dir)
log(f"creating directory {obj_dir}")
os.mkdir(obj_dir)
finally:
scu.GSL.release()
@ -163,8 +166,9 @@ class BaseArchiver(object):
def get_archive_date(self, archive_id):
"""Returns date (as a DateTime object) of an archive"""
dt = [int(x) for x in os.path.split(archive_id)[1].split("-")]
return datetime.datetime(*dt)
return datetime.datetime(
*[int(x) for x in os.path.split(archive_id)[1].split("-")]
)
def list_archive(self, archive_id: str) -> str:
"""Return list of filenames (without path) in archive"""
@ -195,8 +199,7 @@ class BaseArchiver(object):
archive_id = os.path.join(self.get_obj_dir(oid), archive_name)
if not os.path.isdir(archive_id):
log(
"invalid archive name: %s, oid=%s, archive_id=%s"
% (archive_name, oid, archive_id)
f"invalid archive name: {archive_name}, oid={oid}, archive_id={archive_id}"
)
raise ValueError("invalid archive name")
return archive_id
@ -223,7 +226,7 @@ class BaseArchiver(object):
+ os.path.sep
+ "-".join(["%02d" % x for x in time.localtime()[:6]])
)
log("creating archive: %s" % archive_id)
log(f"creating archive: {archive_id}")
try:
scu.GSL.acquire()
os.mkdir(archive_id) # if exists, raises an OSError
@ -302,9 +305,14 @@ def do_formsemestre_archive(
Store:
- tableau recap (xls), pv jury (xls et pdf), bulletins (xml et pdf), lettres individuelles (pdf)
"""
from app.scodoc.sco_recapcomplet import make_formsemestre_recapcomplet
from app.scodoc.sco_recapcomplet import (
gen_formsemestre_recapcomplet_excel,
gen_formsemestre_recapcomplet_html,
gen_formsemestre_recapcomplet_json,
)
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
sem_archive_id = formsemestre_id
archive_id = PVArchive.create_obj_archive(sem_archive_id, description)
date = PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
@ -319,37 +327,38 @@ def do_formsemestre_archive(
etudids = [m["etudid"] for m in groups_infos.members]
# Tableau recap notes en XLS (pour tous les etudiants, n'utilise pas les groupes)
data, _, _ = make_formsemestre_recapcomplet(formsemestre_id, format="xls")
data, _ = gen_formsemestre_recapcomplet_excel(
formsemestre, res, include_evaluations=True, format="xls"
)
if 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)
data, _, _ = make_formsemestre_recapcomplet(
formsemestre_id, format="html", disable_etudlink=True
table_html = gen_formsemestre_recapcomplet_html(
formsemestre, res, include_evaluations=True
)
if data:
if table_html:
data = "\n".join(
[
html_sco_header.sco_header(
page_title="Moyennes archivées le %s" % date,
head_message="Moyennes archivées le %s" % date,
page_title=f"Moyennes archivées le {date}",
head_message=f"Moyennes archivées le {date}",
no_side_bar=True,
),
'<h2 class="fontorange">Valeurs archivées le %s</h2>' % date,
f'<h2 class="fontorange">Valeurs archivées le {date}</h2>',
'<style type="text/css">table.notes_recapcomplet tr { color: rgb(185,70,0); }</style>',
data,
table_html,
html_sco_header.sco_footer(),
]
)
data = data.encode(scu.SCO_ENCODING)
PVArchive.store(archive_id, "Tableau_moyennes.html", data)
# Bulletins en XML (pour tous les etudiants, n'utilise pas les groupes)
data, _, _ = make_formsemestre_recapcomplet(
formsemestre_id, format="xml", xml_with_decisions=True
)
# Bulletins en JSON
data = gen_formsemestre_recapcomplet_json(formsemestre_id, xml_with_decisions=True)
data_js = json.dumps(data, indent=1, cls=scu.ScoDocJSONEncoder)
data_js = data_js.encode(scu.SCO_ENCODING)
if data:
data = data.encode(scu.SCO_ENCODING)
PVArchive.store(archive_id, "Bulletins.xml", data)
PVArchive.store(archive_id, "Bulletins.json", data_js)
# Decisions de jury, en XLS
data = sco_pvjury.formsemestre_pvjury(formsemestre_id, format="xls", publish=False)
if data:

View File

@ -185,13 +185,13 @@ def excel_make_style(
class ScoExcelSheet:
"""Représente une feuille qui peut être indépendante ou intégrée dans un SCoExcelBook.
"""Représente une feuille qui peut être indépendante ou intégrée dans un ScoExcelBook.
En application des directives de la bibliothèque sur l'écriture optimisée, l'ordre des opérations
est imposé:
* instructions globales (largeur/maquage des colonnes et ligne, ...)
* construction et ajout des cellules et ligne selon le sens de lecture (occidental)
ligne de haut en bas et cellules de gauche à droite (i.e. A1, A2, .. B1, B2, ..)
* pour finit 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):
@ -260,7 +260,7 @@ class ScoExcelSheet:
for i, val in enumerate(value):
self.ws.column_dimensions[self.i2col(i)].width = val
# No keys: value is a list of widths
elif type(cle) == str: # accepts set_column_with("D", ...)
elif isinstance(cle, str): # accepts set_column_with("D", ...)
self.ws.column_dimensions[cle].width = value
else:
self.ws.column_dimensions[self.i2col(cle)].width = value
@ -337,7 +337,8 @@ class ScoExcelSheet:
return cell
def make_row(self, values: list, style=None, comments=None):
def make_row(self, values: list, style=None, comments=None) -> list:
"build a row"
# TODO make possible differents styles in a row
if comments is None:
comments = [None] * len(values)

View File

@ -184,9 +184,6 @@ def formsemestre_validation_etud_form(
"notes.formsemestre_recapcomplet",
scodoc_dept=g.scodoc_dept,
modejury=1,
hidemodules=1,
hidebac=1,
pref_override=0,
formsemestre_id=formsemestre_id,
sortcol=sortcol
or None, # pour refaire tri sorttable du tableau de notes
@ -437,14 +434,6 @@ def _redirect_valid_choice(formsemestre_id, etudid, Se, choice, desturl, sortcol
# sinon renvoie au listing general,
# if choice.new_code_prev:
# flask.redirect( 'formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1&desturl=%s' % (formsemestre_id, etudid, desturl) )
# else:
# if not desturl:
# desturl = 'formsemestre_recapcomplet?modejury=1&hidemodules=1&formsemestre_id=' + str(formsemestre_id)
# flask.redirect(desturl)
def _dispcode(c):
if not c:
return ""

View File

@ -382,18 +382,6 @@ class BasePreferences(object):
"only_global": False,
},
),
(
"recap_hidebac",
{
"initvalue": 0,
"title": "Cacher la colonne Bac",
"explanation": "sur la table récapitulative",
"input_type": "boolcheckbox",
"category": "misc",
"labels": ["non", "oui"],
"only_global": False,
},
),
# ------------------ Absences
(
"email_chefdpt",

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.1.91"
SCOVERSION = "9.1.92"
SCONAME = "ScoDoc"