Compare commits

...

2 Commits

Author SHA1 Message Date
5a96e42b6d notes alpha 2024-07-01 07:30:45 +02:00
db39c89257 merging cells 2024-06-30 11:22:07 +02:00
4 changed files with 215 additions and 36 deletions

View File

@ -410,7 +410,7 @@ class ScoExcelSheet:
] ]
else: else:
return [ return [
self.make_cell(value, style, comment) self.make_cell(value, styles, comment)
for value, comment in zip(values, comments) for value, comment in zip(values, comments)
] ]
@ -443,7 +443,7 @@ class ScoExcelSheet:
for row in self.rows: for row in self.rows:
self.ws.append(row) self.ws.append(row)
def generate(self, column_widths=None): def generate(self, column_widths=None, merged=[]):
"""génération d'un classeur mono-feuille""" """génération d'un classeur mono-feuille"""
# this method makes sense for standalone worksheet (else call workbook.generate()) # this method makes sense for standalone worksheet (else call workbook.generate())
if self.wb is None: # embeded sheet if self.wb is None: # embeded sheet
@ -452,12 +452,16 @@ class ScoExcelSheet:
# construction d'un flux # construction d'un flux
# https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream # https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream
self.prepare() self.prepare()
for tuple in merged:
top, left, bottom, right = tuple
self.ws.merge_cells(
start_row=top, end_row=bottom, start_column=left, end_column=right
)
# largeur des colonnes # largeur des colonnes
if column_widths: if column_widths:
for k, v in column_widths.items(): for k, v in column_widths.items():
self.set_column_dimension_width(k, v) self.set_column_dimension_width(k, v)
if self.auto_filter is not None: if self.auto_filter is not None:
self.ws.auto_filter.ref = self.auto_filter self.ws.auto_filter.ref = self.auto_filter
with NamedTemporaryFile() as tmp: with NamedTemporaryFile() as tmp:

View File

@ -77,7 +77,6 @@ def make_alignment(halign=None, valign=None, orientation=None, style=None):
if orientation: if orientation:
alignment.rota = orientation alignment.rota = orientation
if alignment and style: if alignment and style:
breakpoint()
style["alignment"] = alignment style["alignment"] = alignment
return alignment return alignment

View File

@ -474,8 +474,6 @@ imports = {}
def compute_sems(etud, formation_id, formation_titre, descripteurs, Se): def compute_sems(etud, formation_id, formation_titre, descripteurs, Se):
inscriptions = {} inscriptions = {}
rsems = Se.sems[:] # copy rsems = Se.sems[:] # copy
# breakpoint()
# rsems.reverse()
if formation_id not in imports: if formation_id not in imports:
imports[formation_id] = {} imports[formation_id] = {}
for ( for (

View File

@ -39,7 +39,7 @@ from flask import request
from flask_login import current_user from flask_login import current_user
from app import db from app import db
from app.but import bulletin_but, cursus_but from app.but import bulletin_but, cursus_but, jury_but
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import ( from app.models import (
@ -58,10 +58,12 @@ from app.scodoc import sco_cursus
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import sco_version import sco_version
from app.scodoc.sco_excel import excel_make_style, COLORS from app.scodoc.sco_excel import excel_make_style, COLORS, ScoExcelSheet
from app.scodoc.sco_excel_add import make_pattern from app.scodoc.sco_excel_add import make_pattern
from app.scodoc.sco_portal_apogee import get_etud_apogee, query_apogee_portal
from app.scodoc.sco_prepajury_formats import Formatter from app.scodoc.sco_prepajury_formats import Formatter
from app.tables.jury_recap import TableJury
from app.tables.recap import TableRecap
COLORS = { COLORS = {
"ETUDIANT": ["FFFF99"], "ETUDIANT": ["FFFF99"],
@ -384,6 +386,13 @@ class Export:
self.formsemestre_id: int = formsemestre_id self.formsemestre_id: int = formsemestre_id
self.formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id) self.formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
self.nt: NotesTableCompat = res_sem.load_formsemestre_results(self.formsemestre) self.nt: NotesTableCompat = res_sem.load_formsemestre_results(self.formsemestre)
self.recap = TableJury(
self.nt,
convert_values=False,
include_evaluations=False,
mode_jury=True,
read_only=True,
)
etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id) etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id)
main_partition_id = sco_groups.formsemestre_get_main_partition(formsemestre_id) main_partition_id = sco_groups.formsemestre_get_main_partition(formsemestre_id)
self.etuds = self.nt.get_inscrits(order_by="moy") self.etuds = self.nt.get_inscrits(order_by="moy")
@ -413,6 +422,14 @@ class Export:
self.data_sems[sem_id]["formsemestre"] = formsemestre self.data_sems[sem_id]["formsemestre"] = formsemestre
nt = res_sem.load_formsemestre_results(formsemestre) nt = res_sem.load_formsemestre_results(formsemestre)
self.data_sems[sem_id]["nt"] = nt self.data_sems[sem_id]["nt"] = nt
# self.data_sems[sem_id]["recap"] = TableJury(
# nt,
# convert_values=False,
# include_evaluations=False,
# mode_jury=True,
# read_only=True,
# )
current_app.logger.info(f"Table semestre {sem_id} chargée.")
def load_formsemestre(self, etud, session): def load_formsemestre(self, etud, session):
for sem in session.sems: for sem in session.sems:
@ -444,22 +461,55 @@ class Export:
# sexe=quote_xml_attr(etud.civilite_str), # compat # sexe=quote_xml_attr(etud.civilite_str), # compat
bulletins_sem = bulletin_but.BulletinBUT(self.formsemestre) bulletins_sem = bulletin_but.BulletinBUT(self.formsemestre)
self.data_etud[etud.id]["bulletin_but"] = bulletins_sem.bulletin_etud(etud) self.data_etud[etud.id]["bulletin_but"] = bulletins_sem.bulletin_etud(etud)
self.data_etud[etud.id]["cursus"] = cursus_but.EtudCursusBUT( # self.data_etud[etud.id]["cursus"] = (
etud, self.formsemestre.formation # self.recap.row_by_id[etud.id].cells["code_cursus"].raw_content
) # )
# self.data_etud[etud.id]["bulletin_sem"] = bulletins_sem.bulletin_etud(etud) self.data_etud[etud.id]["bulletin_sem"] = bulletins_sem.bulletin_etud(etud)
breakpoint() # self.data_etud[etud.id]["nbabs"] = (
# self.recap.row_by_id[etud.id].cells["nbabs"].raw_content
# )
# self.data_etud[etud.id]["nbabsjust"] = (
# self.recap.row_by_id[etud.id].cells["nbabsjust"].raw_content
# )
# self.data_etud[etud.id]["parcours"] = self.recap.row_by_id[etud.id].cells[].raw_content
def load_data(self): def load_data(self):
self.load_parcours() self.load_parcours()
breakpoint()
for etud in self.etuds: for etud in self.etuds:
self.data_etud[etud.id] = {} self.data_etud[etud.id] = {}
self.data_etud[etud.id]["etud"] = etud self.data_etud[etud.id]["etud"] = etud
self.data_etud[etud.id]["sems"] = {} self.data_etud[etud.id]["sems"] = {}
self.load_etu(etud) self.load_etu(etud)
session = sco_cursus.get_situation_etud_cursus(etud, self.formsemestre_id) session = sco_cursus.get_situation_etud_cursus(etud, self.formsemestre_id)
breakpoint()
self.data_etud[etud.id]["cursus"] = session.parcours self.data_etud[etud.id]["cursus"] = session.parcours
nbabsinj, nbabsjust, nbabs = self.formsemestre.get_abs_count(etud.id)
self.data_etud[etud.id]["abs_tot"] = nbabs
self.data_etud[etud.id]["abs_just"] = nbabsjust
self.data_etud[etud.id]["abs_non_just"] = nbabsinj
cursus = " ".join(
[
f"S{ins.formsemestre.semestre_id}"
for ins in reversed(etud.inscriptions())
if ins.formsemestre.formation.formation_code
== self.formsemestre.formation.formation_code
]
)
self.data_etud[etud.id]["code_cursus"] = cursus
self.data_etud[etud.id]["deca"] = [
jury_but.DecisionsProposeesAnnee(etud, ins.formsemestre)
for ins in reversed(etud.inscriptions())
]
self.data_etud[etud.id]["history"] = {
1: None,
2: None,
3: None,
4: None,
5: None,
6: None,
}
for deca in self.data_etud[etud.id]["deca"]:
self.data_etud[etud.id]["history"][deca.formsemestre.semestre_id] = deca
self.load_formsemestre(etud, session) self.load_formsemestre(etud, session)
def build_formator(self): def build_formator(self):
@ -504,21 +554,15 @@ class Export:
styles[row_no].append(excel_make_style(bgcolor=color[level2])) styles[row_no].append(excel_make_style(bgcolor=color[level2]))
compte += nbelt compte += nbelt
# current_app.logger.info(f" {row_no}, {left}, {nbelt} ") # current_app.logger.info(f" {row_no}, {left}, {nbelt} ")
# sheet.ws.merge_cells(
# start_row=row_no + 1,
# end_row=row_no + 1,
# start_column=left + 1,
# end_column=left + nbelt + 1,
# )
return compte return compte
else: else:
return 1 return 1
def header_append(self, raws, items, row_no): def header_append(self, rows, items, row_no):
if row_no <= 4: if row_no <= 4:
if items is None: if items is None:
raws[row_no].append("") rows[row_no].append("")
self.header_append(raws, None, row_no + 1) self.header_append(rows, None, row_no + 1)
return 1 return 1
else: else:
compte = 0 compte = 0
@ -526,35 +570,164 @@ class Export:
if item != "COLOR": if item != "COLOR":
detail = items[item] detail = items[item]
if item[0] != "+": if item[0] != "+":
raws[row_no].append(item) rows[row_no].append(item)
else: else:
raws[row_no].append(item[1:]) rows[row_no].append(item[1:])
nbelt = self.header_append(raws, detail, row_no + 1) nbelt = self.header_append(rows, detail, row_no + 1)
for _ in range(1, nbelt): for _ in range(1, nbelt):
raws[row_no].append("") rows[row_no].append("")
compte += nbelt compte += nbelt
return compte return compte
else: else:
return 1 return 1
def write_header(self, sheet): def write_header(self, sheet):
raws = [[], [], [], [], []] rows = [[], [], [], [], []]
styles = [[], [], [], [], []] styles = [[], [], [], [], []]
col_no = 0 col_no = 0
raw_no = 0 raw_no = 0
self.header_format(styles, CATEGORIES, 0, 0, ["FFFF00"], 0) self.header_format(styles, CATEGORIES, 0, 0, ["FFFF00"], 0)
self.header_append(raws, CATEGORIES, 0) self.header_append(rows, CATEGORIES, 0)
for i in range(0, 4): for i in range(0, 4):
sheet.append_row( sheet.append_row(
sheet.make_row( sheet.make_row(
raws[i], rows[i],
styles[i], styles[i],
# style=excel_make_style( # style=excel_make_style(
# font_name="Calibri", size=12, bold=False, bgcolor="D0FFFF" # font_name="Calibri", size=12, bold=False, bgcolor="D0FFFF"
# ), # ),
) )
) )
sheet.ws.merge_cells(start_row=9, end_row=8, start_column=1, end_column=10)
def merge_header(self, frames, element, top, left):
if element is None or top >= 8:
frames.append((top, left, 8, left))
return left + 1
else:
tuples = []
current = left
for item in element:
detail = element[item]
next = self.merge_header(
frames,
detail,
top + 1,
current,
)
current = next
frames.append((top, left, top, next - 1))
return next
def translate_ue(self, code):
return {
"ADM": "VAL",
"AJ": "AJ",
"ADJ": "ADJI",
"PASD": "PASD",
"CMP": "COMP",
}.get(code, f"<{code}>")
def translate_rcue(self, code):
return {
"ADM": "VAL",
"AJ": "AJ",
"ADJ": "ADJI",
"PASD": "PASD",
"CMP": "COMP",
}.get(code, f"<{code}>")
def translate_ann(self, code):
return {
"ADM": "VAL",
"AJ": "AJ",
"ADJ": "ADJI",
"PASD": "PASD",
"ADSUP": "VALR",
}.get(code, f"<{code}>")
def detail_competence(self, etud, semestre, comp_no):
history = self.data_etud[etud.id]["history"][semestre]
if semestre % 2 == 0:
result = ["", "", "", ""]
else:
result = ["", ""]
if history is not None and comp_no < len(history.niveaux_competences):
niveau = history.niveaux_competences[comp_no]
rcue = history.rcue_by_niveau[niveau.id]
if semestre % 2 == 1:
ue1 = rcue.ue_1
val_ue1 = (
None if ue1 is None else history.decisions_ues[ue1.id].validation
)
dec_ue1 = "" if val_ue1 is None else self.translate_ue(val_ue1.code)
result = [rcue.moy_ue_1, dec_ue1]
else:
ue2 = rcue.ue_2
val_ue2 = (
None if ue2 is None else history.decisions_ues[ue2.id].validation
)
dec_ue2 = "" if val_ue2 is None else self.translate_ue(val_ue2.code)
val_niv = history.decisions_rcue_by_niveau[niveau.id].validation
dec_niv = "" if val_niv is None else self.translate_rcue(val_niv.code)
result = [rcue.moy_ue_2, dec_ue2, rcue.moy_rcue, dec_niv]
return result
def detail_annuel(self, etud, but):
result = ["", "", ""] if but == 1 else ["", "", "", ""]
history = self.data_etud[etud.id]["history"].get(but * 2, None)
if history is not None:
nb_rcue_valides = history.nb_rcue_valides
moy_gen = self.calcul_moy_ann(etud, but)
if history.validation:
dec_ann = self.translate_ann(history.validation.code)
else:
dec_ann = ""
result = [nb_rcue_valides, moy_gen, dec_ann]
if but != 1:
if dec_ann in {"ADM"}:
result.append("ADM")
else:
result.append("AJ")
return result
def calcul_moy_ann(self, etud, but):
pass
history = self.data_etud[etud.id]["history"].get(but * 2, None)
nb_rcues = 0
tot_rcues = 0.0
for rcue in history.rcue_by_niveau.values():
if rcue.moy_rcue is not None:
tot_rcues += rcue.moy_rcue
nb_rcues += 1
if nb_rcues > 0:
return tot_rcues / nb_rcues
else:
return ""
def display(self, sheet: ScoExcelSheet):
for etud in self.etuds:
values = [
etud.id,
etud.code_nip,
etud.civilite,
etud.nom,
etud.prenom,
""
if self.data_etud[etud.id]["deca"][-1].parcour is None
else self.data_etud[etud.id]["deca"][-1].parcour.code,
self.data_etud[etud.id]["code_cursus"],
self.data_etud[etud.id]["abs_tot"],
self.data_etud[etud.id]["abs_non_just"],
]
for but in range(1, 4):
sImpair = 2 * but - 1
sPair = 2 * but
for comp in range(0, 6):
values += self.detail_competence(etud, sImpair, comp)
values += self.detail_competence(etud, sPair, comp)
values += self.detail_annuel(etud, but)
row = sheet.make_row(values)
sheet.append_row(row)
def feuille_preparation_lille(formsemestre_id): def feuille_preparation_lille(formsemestre_id):
@ -582,6 +755,9 @@ def feuille_preparation_lille(formsemestre_id):
# "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold # "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold
# ) # )
sheet.append_blank_row() sheet.append_blank_row()
sheet.append_blank_row()
sheet.append_blank_row()
sheet.append_blank_row()
export.write_header(sheet) export.write_header(sheet)
# Ligne de titre # Ligne de titre
@ -698,8 +874,8 @@ def feuille_preparation_lille(formsemestre_id):
# ) # )
# UE : Correspondances acronyme et titre complet # UE : Correspondances acronyme et titre complet
sheet.append_blank_row() export.display(sheet)
# export.merge_header(sheet, CATEGORIES["ETUDIANT"]["Absences"]["Tot."], 6, 8, 5)
# sheet.append_single_cell_row("Titre des UE") # sheet.append_single_cell_row("Titre des UE")
# if prev_moy: # if prev_moy:
# for ue in ntp.get_ues_stat_dict(filter_sport=True): # for ue in ntp.get_ues_stat_dict(filter_sport=True):
@ -717,7 +893,9 @@ def feuille_preparation_lille(formsemestre_id):
# current_user, # current_user,
# ) # )
# ) # )
xls = sheet.generate() merged = []
export.merge_header(merged, CATEGORIES, 4, 1)
xls = sheet.generate(merged=merged)
flash("Feuille préparation jury générée") flash("Feuille préparation jury générée")
return scu.send_file( return scu.send_file(
xls, xls,