diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 08f1af9f5..3283dc184 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -161,7 +161,10 @@ def excel_make_style( ) style["font"] = font if bgcolor: - style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value) + if isinstance(bgcolor, str): + style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor) + else: + style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value) if halign or valign: al = Alignment() if halign: @@ -348,15 +351,21 @@ class ScoExcelSheet: return cell - def make_row(self, values: list, style=None, comments=None) -> list: + def make_row(self, values: list, styles=None, comments=None) -> list: "build a row" # TODO make possible differents styles in a row if comments is None: comments = [None] * len(values) - return [ - self.make_cell(value, style, comment) - for value, comment in zip(values, comments) - ] + if isinstance(styles, list): + return [ + self.make_cell(value, style, comment) + for value, style, comment in zip(values, styles, comments) + ] + else: + return [ + self.make_cell(value, style, comment) + for value, comment in zip(values, comments) + ] def append_single_cell_row(self, value: any, style=None): """construit une ligne composée d'une seule cellule et l'ajoute à la feuille. diff --git a/app/scodoc/sco_excel_add.py b/app/scodoc/sco_excel_add.py new file mode 100644 index 000000000..28ebc4b54 --- /dev/null +++ b/app/scodoc/sco_excel_add.py @@ -0,0 +1,221 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2019 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + + +""" Excel file handling +""" + +# from sco_utils import * +# from sco_excel import * +import time, datetime + +from openpyxl.styles import Font, Alignment, PatternFill, Side, Border + +from app.scodoc.sco_excel import COLORS, ScoExcelSheet + + +def make_font( + bold=False, + italic=False, + font_name=None, + font_size=None, + color=COLORS.BLACK.value, + style=None, + uline=False, +): + font = None + if bold or italic or font_name or font_size: + font = Font() + if bold: + font.bold = bold + if uline: + font.underline = Font.UNDERLINE_SINGLE + if italic: + font.italic = italic + font.name = font_name if font_name else "Arial" + if font_size: + font.height = 20 * font_size + if color: + font.color = color + if font and style: + style["font"] = font + return font + + +def make_alignment(halign=None, valign=None, orientation=None, style=None): + alignment = None + if halign or valign or orientation: + alignment = Alignment() + if halign: + alignment.horz = halign + if valign: + alignment.vert = valign + if orientation: + alignment.rota = orientation + if alignment and style: + breakpoint() + style["alignment"] = alignment + return alignment + + +def make_numfmt(fmt, style=None): + if fmt and style: + style["num_format_str"] = fmt + + +def make_pattern(bgcolor, style=None): + pattern = PatternFill(fill_type="solid", fgColor=bgcolor) + if pattern and style: + style["fill"] = pattern + return pattern + + +Sides = { + "none": Side(border_style=None), + "thin": Side(border_style="thin"), + "medium": Side(border_style="medium"), + "thick": Side(border_style="thick"), + "filet": Side(border_style="hair"), +} + + +def make_borders(left=None, top=None, bottom=None, right=None, style=None): + border = None + if left or right or top or bottom: + border = Border() + if left: + border.left = Sides[left] + if right: + border.right = Sides[right] + if top: + border.top = Sides[top] + if bottom: + border.bottom = Sides[bottom] + if border and style: + style["border"] = border + return border + + +# +# +# def Excel_MakeStyle( +# bold=False, +# italic=False, +# color="black", +# bgcolor=None, +# halign=None, +# valign=None, +# orientation=None, +# font_name=None, +# font_size=None, +# ): +# style = XFStyle() +# make_font(bold, italic, font_name, font_size, color, style=style) +# make_alignment(halign, valign, orientation, style=style) +# make_pattern(bgcolor, style=style) +# return style +# +# +# class ScoExcelSheetExt(ScoExcelSheet): +# def __init__(self, sheet_name="feuille", default_style=None): +# ScoExcelSheet.__init__(self, sheet_name, default_style) +# self.cols_width = {} +# self.row_height = {} +# self.merged = [] +# self.panes_frozen = False +# self.horz_split_pos = 0 +# self.vert_split_pos = 0 +# +# def set_panes(self, panes_frozen=True, horz_split_pos=0, vert_split_pos=0): +# self.panes_frozen = panes_frozen +# self.horz_split_pos = horz_split_pos +# self.vert_split_pos = vert_split_pos +# +# def set_cols_width(self, cols_width): +# self.cols_width = cols_width +# +# def set_row_height(self, row_height): +# self.row_height = row_height +# +# def add_merged(self, first_row, last_row, first_col, last_col, value, style): +# self.merged.append((first_row, last_row, first_col, last_col, value, style)) +# +# def gen_workbook(self, wb=None): +# """Generates and returns a workbook from stored data. +# If wb, add a sheet (tab) to the existing workbook (in this case, returns None). +# """ +# if wb == None: +# wb = Workbook() # Création du fichier +# sauvegarde = True +# else: +# sauvegarde = False +# ws0 = wb.add_sheet(self.sheet_name.decode(SCO_ENCODING)) +# li = 0 +# for l in self.cells: +# co = 0 +# for c in l: +# # safety net: allow only str, int and float +# if type(c) == LongType: +# c = int(c) # assume all ScoDoc longs fits in int ! +# elif type(c) not in (IntType, FloatType): +# c = str(c).decode(SCO_ENCODING) +# +# ws0.write(li, co, c, self.get_cell_style(li, co)) +# co += 1 +# li += 1 +# for first_row, last_row, first_col, last_col, value, style in self.merged: +# ws0.write_merge( +# first_row, +# last_row, +# first_col, +# last_col, +# str(value).decode(SCO_ENCODING), +# style, +# ) +# for col_no in range(len(self.cols_width)): +# width = self.cols_width[col_no] +# ws0.col(col_no).width = abs(width) * 110 / 3 +# if width < 0: +# ws0.col(col_no).hidden = True +# row_styles = {} +# for row_no in range(len(self.row_height)): +# height = self.row_height[row_no] +# if height not in row_styles: +# fnt = Font() +# fnt.height = height * 12 +# row_styles[height] = XFStyle() +# row_styles[height].font = fnt +# ws0.row(row_no).set_style(row_styles[height]) # Excel way +# ws0.row(row_no).height = height * 19 / 2 # Libre-Office compatibilité +# if self.panes_frozen: +# ws0.panes_frozen = self.panes_frozen +# ws0.horz_split_pos = self.vert_split_pos +# ws0.vert_split_pos = self.horz_split_pos +# if sauvegarde: +# return wb.savetostr() +# else: +# return None diff --git a/app/scodoc/sco_prepajury_formats.py b/app/scodoc/sco_prepajury_formats.py new file mode 100644 index 000000000..0b44f932b --- /dev/null +++ b/app/scodoc/sco_prepajury_formats.py @@ -0,0 +1,175 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2019 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + + +""" Excel file formatting +""" + +# from sco_excel_add import * +import copy +from enum import IntFlag, Enum + +from app.scodoc.sco_excel import excel_make_style +from app.scodoc.sco_excel_add import make_font, make_pattern, make_numfmt, make_borders + + +class CellBorder(IntFlag): + TOP = 1 << 0 + BOTTOM = 1 << 1 + LEFT = 1 << 2 + RIGHT = 1 << 3 + + +# Liste des modificateurs de format + + +class CellFormat(IntFlag): + # Mise en Forme usuelle + FMT_CAP_UE = 1 << 4 # UE capitalisée + FMT_MOY = 1 << 5 # Moyenne de semestre + FMT_UE = 1 << 6 # Acquisition d'UE sans validation semestre + # Mise en Forme sur le contenu + EMPHASE_PREC = 1 << 7 # Cause échec de semestre + EMPHASE_ATB = 1 << 8 # Cause échec de semestre + EMPHASE_UEACQ = 1 << 9 # Cause échec de semestre + EMPHASE_ATT = 1 << 10 # Cause échec de semestre + EMPHASE_ABS = 1 << 11 # Absences + EMPHASE_COMP = 1 << 12 # Cause échec de semestre + + # Mise en forme numérique + PRECISE = 1 << 20 # Précision de l'affichage + + GRAYED = 1 << 22 # Grisé horizontal + + +# Color palette at : https://groups.google.com/g/python-excel/c/cePI-ojUJfc/m/dH72A-rm6_gJ +class CellColor(Enum): + COLOR_PREC = "D2B48C" # TAN + COLOR_ATB = "93CCEA" # LIGHT CORNFLOWER BLUE + COLOR_ATT = "FE8DB1" # LIGHT ORANGE + COLOR_COMP = "90EE90" # LIGHT GREEN + COLOR_UEACQ = "90EE90" # LIGHT GREEN + COLOR_ABS = "44845E" # LIGHT YELLOW + + +def format_note(valeur, warning, seuil, mini=True): + if seuil is not None: + if isinstance(valeur, (int, float)): + if mini: + diff = valeur - seuil + else: + diff = seuil - valeur + if -0.20 < diff < 0: + warning += CellFormat.PRECISE + return warning + + +class Formatter: + def __init__(self): + self.style_table = {} + self.last_rank = None + self.left_borders = [] + self.right_borders = [] + self.default_top = "none" + self.default_bottom = "none" + self.default_left = "none" + self.default_right = "none" + self.default_format = None + + # def add_category(self, left, right): + # self.left_borders.append(left) + # self.right_borders.append(right) + + def set_borders_default(self, left="none", top="none", bottom="none", right="none"): + self.default_bottom = bottom + self.default_left = left + self.default_right = right + self.default_top = top + + def borders(self, li, co): + index = 0 + index |= CellBorder.TOP if li == 0 else 0 + index |= CellBorder.BOTTOM if self.last_rank and li == self.last_rank else 0 + index |= CellBorder.LEFT if co in self.left_borders else 0 + index |= CellBorder.RIGHT if co in self.right_borders else 0 + return index + + def get_style(self, index=0): + if index not in self.style_table: + border_enable = "filet" + if self.default_format: + style = copy.copy(self.default_format) + else: + style = excel_make_style() + make_font( + style=style, + font_name="Calibri", + font_size=9, + bold=(CellFormat.FMT_MOY & index), + uline=(CellFormat.FMT_CAP_UE & index), + ) + make_borders( + style=style, left="none", right="none", top="filet", bottom="filet" + ) + if index & CellFormat.EMPHASE_ATB: + make_pattern(style=style, bgcolor=CellColor.COLOR_ATB.value) + elif index & CellFormat.EMPHASE_ATT: + make_pattern(style=style, bgcolor=CellColor.COLOR_ATT.value) + elif index & CellFormat.EMPHASE_PREC: + make_pattern(style=style, bgcolor=CellColor.COLOR_PREC.value) + elif index & CellFormat.EMPHASE_COMP: + make_pattern(style=style, bgcolor=CellColor.COLOR_COMP.value) + elif index & CellFormat.EMPHASE_UEACQ: + make_pattern(style=style, bgcolor=CellColor.COLOR_UEACQ.value) + elif index & CellFormat.EMPHASE_ABS: + make_pattern(style=style, bgcolor=CellColor.COLOR_ABS.value) + # elif not index & GRAYED: + # make_pattern(style=style, bgcolor=0x43) + if index & (CellFormat.FMT_CAP_UE | CellFormat.FMT_UE | CellFormat.FMT_MOY): + if index & CellFormat.PRECISE: + make_numfmt("#0.00", style) + else: + make_numfmt("#0.0", style) + top = border_enable if index & CellBorder.TOP else self.default_top + bottom = border_enable if index & CellBorder.BOTTOM else self.default_bottom + left = border_enable if index & CellBorder.LEFT else self.default_left + right = border_enable if index & CellBorder.RIGHT else self.default_right + make_borders(style=style, top=top, left=left, right=right, bottom=bottom) + self.style_table[index] = style + return self.style_table[index] + + def width(self, item): + if item is None: + return 1 + else: + compte = 0 + for subitem in item.values(): + compte += self.width(subitem) + return compte + + def set_default_format(self, default_format): + self.default_format = default_format diff --git a/app/scodoc/sco_prepajury_iuta.py b/app/scodoc/sco_prepajury_iuta.py new file mode 100644 index 000000000..8cdbe37c2 --- /dev/null +++ b/app/scodoc/sco_prepajury_iuta.py @@ -0,0 +1,1150 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2019 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + +"""Feuille excel pour preparation des jurys +""" +from openpyxl.styles import Font, Alignment, Border, Side, PatternFill, Color + +import app.scodoc.sco_utils as scu +from app.comp import res_sem +from app.scodoc import sco_cache, sco_codes_parcours +from app.scodoc import sco_formsemestre +from app.scodoc import sco_groups +from app.scodoc import sco_preferences +from app.scodoc import sco_etud +from app.scodoc import sco_parcours_dut +from app.scodoc import sco_prepajury_formats +from app.scodoc.sco_excel import ScoExcelSheet, excel_make_style, COLORS +from app.scodoc.sco_portal_apogee import query_apogee_portal +from app.scodoc.sco_prepajury_formats import CellFormat, format_note, Formatter +from app.scodoc import sco_abs +from app.scodoc import sco_excel_add +from app import log +from app.models import FormSemestre, Identite + +filtre_decision = ["", "RAT"] +NBSEM = 4 + + +class SemDescriptor: + def __init__(self, nt): + self.acros = {} # ue_code_s : ( numéro, acronyme, titre) + self.codes = [] + self.s_idx = "" + self.s_sx = "" + self.nt = nt + sid = nt.sem["semestre_id"] + if sid > 0: + self.s_idx = sid + self.s_sx = SEMESTRES_SX[sid] + for ue in nt.ues: + if ue.type == 0: + ue_code = ue.ue_code # code indentifiant l'UE + self.codes.append(ue_code) + self.acros[ue_code] = (ue.numero, ue.acronyme, ue.titre) + + def sort(self): + # prev_ue_codes.sort(lambda x, y, prev_ue_acro=prev_ue_acro: cmp(prev_ue_acro[x], prev_ue_acro[y])) + self.codes.sort(key=lambda x: self.acros[x]) + + def widths(self): + widths = [] + widths += [32 for _ in self.codes] + widths += [-32 for _ in range(NBUE - len(self.codes))] + widths += [40, 40] # moyenne et décision + return widths + + def titles(self): + titles = [] + titles += [self.acros[x][1] for x in self.codes] + titles += [ + "" for _ in range(NBUE - len(self.codes)) + ] # Compléte pour avoir NBUE colonnes par semestre + titles += ["Moy %s" % self.s_sx] + titles += ["Décision %s" % self.s_sx] + return titles + + def add_bloc_ue(self, worksheet): + for code in self.codes: + worksheet.append( + [" %-8s %s" % (self.acros[code][1], self.acros[code][2])] + ) + + +# Constantes liées aux decisions sur un semestre +CLASS_ATB = ["ATB", "ATJ", "NAR"] +CLASS_ATT = ["ATT", "AJ"] +CLASS_ADM = ["ADM", "ADM+", "ADC", "ADJ"] + + +def classe(code): + if code in CLASS_ATB: + return CLASS_ATB + if code in CLASS_ATT: + return CLASS_ATT + if code in CLASS_ADM: + return CLASS_ADM + return None + + +class Inscription: + def __init__(self, etudid, sem): + self.etudid = etudid + self.sem = sem + self.s_idx = sem["semestre_id"] + self.sx = SEMESTRES_SX[self.s_idx] + formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) + self.nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + # > get_ues, get_etud_ue_status, get_etud_moy_gen, get_etud_decision_sem + decision = self.nt.get_etud_decision_sem(etudid) + self.code = "" + if decision: + self.code = decision["code"] + if decision["compense_formsemestre_id"]: + self.code += "+" # indique qu'il a servi a compenser + self.moy = self.nt.get_etud_moy_gen(etudid) + self.moy_ue = {} + self.capitalized = {} + for ue in self.nt.ues: + ue_status = self.nt.get_etud_ue_status(etudid, ue.id) + ue_code = ue.ue_code # code identifiant l'UE + self.moy_ue[ue_code] = ue_status["moy"] + self.capitalized[ue_code] = ue_status["was_capitalized"] + + # + # def __repr__(self): + # return ( + # "[%s,%s]:\n" % (self.etudid, self.sx) + # + "ues: %s\n" % self.moy_ue + # + " %s (%s)\n" % (self.moy, self.code) + # ) + # + def display(self, worksheet, descripteur): + for ue_code in descripteur.codes: + if ue_code in self.moy_ue: + worksheet.append(fmt(self.moy_ue[ue_code])) + else: + worksheet.append("-") + for _ in range(NBUE - len(descripteur.codes)): + worksheet.append("") + worksheet.append(fmt(self.moy)) + worksheet.append(self.code) + + # def gabarit(self, descripteur, record): + # gbs = [] + # for ue_code, gb in descripteur.codes, record.gabarit_note: + # if ue_code in self.moy_ue: + # if self.capitalized[ue_code]: + # gb += format_note(FMT_CAP_UE, self.moy_ue[ue_code], gb) + # else: + # gb += format_note(FMT_UE, self.moy_ue[ue_code], gb) + # else: + # gb = 0 + # gbs.append(gb) + # for _ in range(NBUE - len(descripteur.codes)): + # gbs.append(0) + # gbs.append(format_note(FMT_MOY, self.moy), record.gabarit_moy) + # gbs.append(0) + # return gbs + + def compensable(self): + for ue in self.nt.ues: + if ue.type == 0: + moy_ue = self.moy_ue[ue.ue_code] + # if ( + # moy_ue != "NA" + # ): # PATCH COVID-19: une UE neutralisée n'empéche pas la validation d'un semestre + if moy_ue is None or isinstance(moy_ue, str): + return False + if moy_ue < 7.999: + return False + return True + + +# # Calcule la classe du semstre actuel à partir des notes +# # def reglementaire(self, record): +# # for co, moy_ue in enumerate(self.moy_ue.values(), start=0): +# # if moy_ue != "NA": # PATCH COVID-19: une UE neutralisée n'empéche pas la validation d'un semestre +# # if type(moy_ue) != FloatType and type(moy_ue) != IntType: +# # record.gabarit_note[co] |= EMPHASE_SEM +# # return CLASS_ATB +# # if moy_ue < 7.999: +# # record.gabarit_note[co] |= EMPHASE_SEM + SEUIL_8 +# # return CLASS_ATB +# # if type(self.moy) != FloatType and type(self.moy) != IntType: +# # record.gabarit_note[5] |= EMPHASE_SEM + SEUIL_10 +# # return CLASS_ATB +# # if self.moy < 9.999: +# # record.gabarit_note[5] |= EMPHASE_SEM + SEUIL_10 +# # return CLASS_ATT +# # return CLASS_ADM +# +# # def ues_acquises(self, descripteur): +# # liste = [] +# # for ue_code in descripteur.codes: +# # if ( +# # type(self.moy_ue[ue_code]) == FloatType +# # and self.moy_ue[ue_code] >= 9.999 +# # ): +# # liste.append(descripteur.acros[ue_code][1]) +# # return ",".join(liste) if len(liste) > 0 else "" + + +def fmt(x): + """reduit les notes a deux chiffres""" + x = scu.fmt_note(x, keep_numeric=False) + try: + return float(x) + except ValueError: + return x + + +# Calcul la signature (checksum) d'une valeur (chaine de car) +# = ' ' + valeur binaire de la chaine modulo 95 (valeur calculée entre 32 et 127) +def mod95(valeur): + clef = 0 + for car in str(valeur): + clef = (clef * 256 + ord(car)) % 95 + return chr(clef + 32) + + +def entete_semestres(s, descripteurs): + titles = [] + if s in descripteurs: + titles += descripteurs[s].titles() + else: + titles += ["", "", "", "", "", ""] + return titles + + +# Constantes liées aux rangs des semestres +NBSEM = 4 # valeur par défaut +SEMESTRES_IDX = range(1, NBSEM + 1) # valeur par défaut +SEMESTRES_SX = dict((sx, "S%s" % sx) for sx in SEMESTRES_IDX) # valeur par défaut +NBUE = 4 # 4 UE par semestre +SEUIL_ABS = 8 +CATEGORIES = [ + "GEN", + "APOGEE", + "SCODOC", + "S1", + "S2", + "S3", + "S4", + "ABS", + "REGL", + "PREJURY", + "JURY", + "NOTE", + "CLEF", +] +CATEGORIE_DESC = { + "GEN": {"nom": "", "items": ["rang", "nip"], "colour": "CCFFFF"}, + "APOGEE": { + "nom": "Données Apogée", + "items": [ + "Nom", + "Prénom", + "Et. étranger", + "ville", + "pays", + "né le", + "Bac", + "Etape", + "Boursier", + "Impayé", + "Réorient.", + ], + "colour": "00CCFF", + }, + "SCODOC": { + "nom": "Données Scodoc", + "items": ["ID", "Civ.", "Nom", "Prénom", "Parcours", "Groupe"], + "colour": "00FFFF", + }, + "S1": {"nom": "S1", "items": ["-", "-", "-", "-", "-", "-"], "colour": "F0F0F0"}, + "S2": {"nom": "S2", "items": ["-", "-", "-", "-", "-", "-"], "colour": "FFCC99"}, + "S3": {"nom": "S3", "items": ["-", "-", "-", "-", "-", "-"], "colour": "FFCC99"}, + "S4": {"nom": "S4", "items": ["-", "-", "-", "-", "-", "-"], "colour": "FFCC99"}, + "ABS": {"nom": "Abs.", "items": ["Total", "Non just."], "colour": "F0F0F0"}, + "REGL": { + "nom": "Réglementaire", + "items": [ + "Moy. comp.", + "Semestre", + "Proposition Sx", + "Sem. comp.", + "Compensation", + "UE acquises", + ], + "colour": "CCFFCC", + }, + "PREJURY": { + "nom": "Commission", + "items": [ + "Préconisation SX", + "Autre sem. (Sc)", + "Préconisation Sc", + "UE acquises", + ], + "colour": "339966", + }, + "JURY": { + "nom": "Jury", + "items": ["Décision SX", "Autre sem. (Sc)", "Décision Sc", "UE acquises"], + "colour": "99CC00", + }, + "NOTE": {"nom": "Remarques", "items": [""], "colour": "F0F0F0"}, + "CLEF": {"nom": "Clef", "items": ["-"], "colour": "F0F0F0"}, +} + +def recursive_add(compats, form): + if form not in compats: + compats.append(form) + if form in COMPATIBLES: + for succ in COMPATIBLES[form]: + recursive_add(compats, succ) + + +def est_compatible(from_form, to_form): + if from_form == to_form: + return True + # Calcule la cloture transitive + compats = [] + recursive_add(compats, from_form) + return to_form in compats + + +def update_libelles(act_sem, descripteurs): + sx = SEMESTRES_SX[act_sem] + for s in SEMESTRES_IDX: + sn = SEMESTRES_SX[s] + CATEGORIE_DESC[sn]["items"] = entete_semestres(s, descripteurs) + CATEGORIE_DESC[sn]["colour"] = "FFFF99" if s == act_sem else "E0E0E0" + CATEGORIE_DESC["REGL"]["items"] = [ + "Moy. comp.", + "Proposition %s" % sx, + "Sem. comp.", + "Compensation", + "UE acquises", + ] + CATEGORIE_DESC["PREJURY"]["items"] = [ + "Préconisation %s" % sx, + "Autre sem. (Sc)", + "Préconisation Sc", + "UE acquises", + ] + CATEGORIE_DESC["JURY"]["items"] = [ + "Décision %s" % sx, + "Autre sem. (Sc)", + "Décision Sc", + "UE acquises", + ] + + +def get_item(info, field): + return info[field] if field in info else "-" + + +def skip_n_lines(worksheet, nblines): + for _ in range(nblines): + worksheet.append_blank_row() + + +def display_donnees_apogee(infos): + if len(infos) == 1: + info = infos[0] + return [ + get_item(info, item) + for item in [ + "nom", + "prenom", + "Etudiant étranger", + "lieu_naissance", + "country", + "naissance", + "bac", + "etape", + "bourse", + "paiementinscription", + "Abandon/Réorientation", + ] + ] + else: + return ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"] + + +def display_donnees_scodoc(etudid, etud, record): + return [ + etudid, + etud["civilite"], + sco_etud.format_nom(etud["nom"]), + sco_etud.format_prenom(etud["prenom"]), + record.parcours, + record.groupesTd, + ] + + +def generate_styles(): + return { + "bold": excel_make_style(bold=True), + "center": excel_make_style(halign="center"), + "boldcenter": excel_make_style(bold=True, halign="center"), + "moy": excel_make_style( + bold=True, halign="center", bgcolor=COLORS.LIGHT_YELLOW + ), + "note": excel_make_style(halign="right"), + "note_bold": excel_make_style(halign="right", bold=True), + } + + +# def bloc_append_footer(l, REQUEST): +# l.append( +# [ +# "Préparé par %s le %s sur %s pour %s" +# % ( +# VERSION.SCONAME, +# time.strftime("%d/%m/%Y à %H:%M"), +# REQUEST.BASE0, +# REQUEST.AUTHENTICATED_USER, +# ) +# ] +# ) +# +# +formatteur = Formatter() + + +def formattage_table(worksheet, act_sem, descripteurs, rangmax): + cols_width = [] + cols_width += [22, -79] # Etudiant + cols_width += [-121, -130, -56, -89, -56, -56, -56, -56, -56, -56, -56] + cols_width += [-49, -34, 95, 80, 116, 26] # scodoc + for s in SEMESTRES_IDX: + if s in descripteurs and s in [act_sem - 1, act_sem, act_sem + 1]: + cols_width += descripteurs[s].widths() + else: + cols_width += [-32, -32, -32, -32, -37, -40] + cols_width += [32, 32] # absences + cols_width += [32, 40, 32, 38, 62] # réglementaire + cols_width += [40, 32, 40, 62] # préjury + cols_width += [-40, -32, -40, -62] # jury (hidden) + cols_width += [200] # Remarques + cols_width += [0] # clef + for i, w in enumerate(cols_width): + if w > 0: + worksheet.set_column_dimension_width(i, w / 7) + else: + worksheet.set_column_dimension_width(i, -w / 7) + worksheet.set_column_dimension_hidden(i, True) + worksheet.set_row_dimension_height(1, 35) + worksheet.set_row_dimension_height(2, 28) + worksheet.set_row_dimension_height(3, 39) + worksheet.set_row_dimension_height(4, 110) + + +imports = {} + + +def compute_sems(etud, formation_id, formation_titre, descripteurs, Se): + inscriptions = {} + rsems = Se.sems[:] # copy + # breakpoint() + # rsems.reverse() + if formation_id not in imports: + imports[formation_id] = {} + for ( + sem + ) in rsems: # parcours les semestres de l'étudiant du plus ancien au plus récent + if not sem["formation_id"] == formation_id: + titre = sem["titreannee"] + import_id = sem["formation_id"] + ok = est_compatible(formation_id, import_id) + if import_id not in imports[formation_id]: + imports[formation_id][import_id] = (titre, ok) + if est_compatible(formation_id, sem["formation_id"]): + s_idx = sem["semestre_id"] + inscriptions[s_idx] = Inscription(etud.id, sem) + if s_idx not in descripteurs: + descripteurs[s_idx] = SemDescriptor(inscriptions[s_idx].nt) + for form in imports[formation_id]: + (titre, ok) = imports[formation_id][form] + if not ok: + log( + "@### Dépendance manquante: @ %10s %5s: %5s %5s (%40s)" + % ( + sco_preferences.get_preference("DeptName"), + formation_id, + form, + titre, + ok, + ) + ) + return inscriptions + + +def format_legende(worksheet, first_line, line_count, style_titre, style_item): + worksheet.set_style(style=style_titre, li=first_line, co=0) + for line in range(first_line + 1, first_line + line_count): + worksheet.set_style(style=style_item, li=line, co=0) + + +# def bloc_append_legendes(worksheet, line_no, descripteurs, REQUEST): +# style_titre = sco_excel_add.Excel_MakeStyle( +# font_name="Courier New", font_size=10, bold=True +# ) +# style_item = sco_excel_add.Excel_MakeStyle(font_name="Courier New", font_size=10) +# +# line_count = bloc_append_legende_ues(worksheet, descripteurs) +# format_legende(worksheet, line_no, line_count, style_titre, style_item) +# line_no += line_count +# +# line_count = bloc_append_legende_iuta(worksheet) +# format_legende(worksheet, line_no, line_count, style_titre, style_item) +# line_no += line_count +# +# line_count = bloc_append_legende_scodoc(worksheet) +# format_legende(worksheet, line_no, line_count, style_titre, style_item) +# line_no += line_count +# +# bloc_append_footer(worksheet, REQUEST) + + +def bloc_append_entete(worksheet, sem, descripteurs, act_sem): + worksheet.append_row( + worksheet.make_row( + ["Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"])], + style=excel_make_style(font_name="Arial", size=14, bold=True), + ) + ) + worksheet.append_blank_row() + for line in generate_header(worksheet): + worksheet.append_row(line) + # worksheet.ws.merge_cells(start_row=3, start_column=1, end_row=3, end_column=2) + + +# def format_categorie(worksheet, col_no, categorie): +# upper = excel_make_style( +# bold=True, +# halign="center", +# valign="center", +# font_name="calibri", +# size=11, +# bgcolor=categorie["colour"], +# ) +# sco_excel_add.make_borders( +# style=upper, left="thin", top="medium", bottom="thin", right="thin" +# ) +# lower = copy.copy(upper) +# make_font(style=lower, font_name="Calibri", font_size=10, bold=True) +# sco_excel_add.make_alignment( +# style=lower, halign="center", valign="center", orientation="clockwise" +# ) +# lower_left = copy.copy(lower) +# sco_excel_add.make_alignment( +# style=lower_left, halign="center", valign="center", orientation="clockwise" +# ) +# lower_right = copy.copy(lower) +# sco_excel_add.make_alignment( +# style=lower_right, halign="center", valign="center", orientation="clockwise" +# ) +# +# sco_excel_add.make_font(style=upper, font_size=14) +# sco_excel_add.make_borders( +# style=lower, left="thin", top="thin", bottom="thin", right="thin" +# ) +# sco_excel_add.make_borders( +# style=lower_left, left="thin", top="thin", bottom="thin", right="thin" +# ) +# sco_excel_add.make_borders( +# style=lower_right, left="thin", top="thin", bottom="thin", right="thin" +# ) +# left_col = col_no +# right_col = col_no + len(categorie["items"]) +# worksheet.add_merged(2, 2, left_col, right_col - 1, categorie["nom"], upper) +# worksheet.set_style(lower_left, li=3, co=left_col) +# for c in range(left_col + 1, right_col - 1): +# worksheet.set_style(lower, li=3, co=c) +# worksheet.set_style(lower_right, li=3, co=right_col - 1) +# return right_col + + +def generate_categorie(worksheet, categorie): + title = categorie["nom"] + items_list = categorie["items"] + nb_center_cell = len(items_list) - 2 + + # fond + pattern = PatternFill(fill_type="solid", fgColor=categorie["colour"]) + + # bordures + side_thin = Side(border_style="thin", color=COLORS.BLACK.value) + side_hair = Side(border_style="hair", color=COLORS.BLACK.value) + + font_upper = Font(name="Arial", size=14, bold=True) + font_lower = Font(name="Calibri", size=10, bold=True) + align_upper = Alignment(horizontal="center", vertical="center") + align_lower = Alignment(horizontal="center", vertical="center", textRotation=90) + upper_left = worksheet.excel_make_composite_style( + fill=pattern, + font=font_upper, + alignment=align_upper, + border=Border(top=side_thin, right=side_hair, left=side_thin, bottom=side_hair), + ) + upper = worksheet.excel_make_composite_style( + font=font_upper, + fill=pattern, + alignment=align_upper, + border=Border(top=side_thin, right=side_hair, left=side_hair, bottom=side_hair), + ) + upper_right = worksheet.excel_make_composite_style( + font=font_upper, + alignment=align_upper, + fill=pattern, + border=Border(top=side_thin, right=side_thin, left=side_hair, bottom=side_hair), + ) + lower_left = worksheet.excel_make_composite_style( + font=font_lower, + alignment=align_lower, + fill=pattern, + border=Border(top=side_hair, right=side_hair, left=side_thin, bottom=side_thin), + ) + lower = worksheet.excel_make_composite_style( + font=font_lower, + alignment=align_lower, + fill=pattern, + border=Border(top=side_hair, right=side_hair, left=side_hair, bottom=side_thin), + ) + lower_right = worksheet.excel_make_composite_style( + font=font_lower, + alignment=align_lower, + fill=pattern, + border=Border(top=side_hair, right=side_thin, left=side_hair, bottom=side_thin), + ) + + upper_values = [] + upper_values += [title] + ["" for _ in range(1, len(items_list))] + upper_styles = [upper_left] + [upper] * nb_center_cell + [upper_right] + upper_cells = worksheet.make_row(upper_values, upper_styles) + + lower_values = [] + lower_values += items_list + lower_styles = [lower_left] + [lower] * nb_center_cell + [lower_right] + lower_cells = worksheet.make_row(lower_values, lower_styles) + + return upper_cells, lower_cells + + +def merge_categories(worksheet): + row = 3 + left = 0 + for categorie in CATEGORIES: + right = left + len(CATEGORIE_DESC[categorie]["items"]) - 1 + worksheet.merge(start_row=row, end_row=row, start_column=left, end_column=right) + left = right + 1 + + +def generate_header(worksheet): + uppers = [] + lowers = [] + for categorie in CATEGORIES: + upper, lower = generate_categorie(worksheet, CATEGORIE_DESC[categorie]) + uppers += upper + lowers += lower + return [uppers, lowers] + + +def bloc_append_legende_scodoc(worksheet): + codes = sco_codes_parcours.CODES_EXPL.keys() + codes.sort() + worksheet.append_row(worksheet.make_row(["Explication des codes (scodoc)"])) + for code in codes: + worksheet.append_row( + worksheet.make_row( + [" %-8s %s" % (code, sco_codes_parcours.CODES_EXPL[code])] + ) + ) + worksheet.append( + [ + " %-8s %s" + % ("ADM+", "indique que le semestre a déjà servi à en compenser un autre") + ] + ) + # UE : Correspondances acronyme et titre complet + worksheet.append([""]) + return len(codes) + 2 + + +# def bloc_append_legende_iuta(worksheet): +# worksheet.append(["Explication des codes (Jury)"]) +# worksheet.append([" %-8s %s" % ("ADM", "Admis")]) +# worksheet.append([" %-8s %s" % ("ADJ", "Admis par décision de jury")]) +# worksheet.append( +# [ +# " %-8s %s" +# % ( +# "ADC", +# "Admis par compensation (indiquer le n° du semestre utilisé : ADC2", +# ) +# ] +# ) +# worksheet.append( +# [ +# " %-8s %s" +# % ( +# "AJ", +# "Ajourné (non validé), peut devenir ADJ ou ADC, préconisé si réorientation", +# ) +# ] +# ) +# worksheet.append( +# [" %-8s %s" % ("RAT", "Attente de notes (équivalent du Report")] +# ) +# worksheet.append([" %-8s %s" % ("NAR", "Non autorisé à redoubler (exclu)")]) +# worksheet.append( +# [ +# " %-8s %s" +# % ("AP", "Autorisé à poursuivre (préciser dans quel semestre: AP2)") +# ] +# ) +# worksheet.append([]) +# worksheet.append([" %s" % "ATT et ATB disparaissent (remplacés par AJ)"]) +# worksheet.append( +# [" %s" % "Défaillant ou abandon : mettre NAR (exclu) ou AJ selon le cas"] +# ) +# worksheet.append([""]) +# return 12 +# +# +# def bloc_append_legende_ues(worksheet, descripteurs): +# # UE : Correspondances acronyme et titre complet +# worksheet.append([""]) +# worksheet.append(["Titre des UE"]) +# line_count = 1 +# for d in descripteurs: +# descripteurs[d].add_bloc_ue(worksheet) +# line_count += len(descripteurs[d].codes) + 1 +# worksheet.append([""]) +# return line_count + + +def format_line(worksheet, rang, gabarit): + styles = [] + for co, gb in enumerate(gabarit, start=0): + gb += formatteur.borders(rang, co) + if ((rang + 2) / 3) % 2 == 0: + gb |= CellFormat.GRAYED + styles.append(formatteur.get_style(gb)) + return styles + + +# def pane(worksheet): +# worksheet.set_panes(panes_frozen=True, horz_split_pos=16, vert_split_pos=4) +# +# +lignes = {} # etudid : Ligne + + +class Ligne: + def __init__(self, etudid): + self.etudid = etudid + self.regl = None + self.regl2 = None + self.regl2_sem = None + self.ues_acq = None + self.moy_inter = None + self.historique = {} + self.parcours = None + self.groupesTD = None + self.nbabs = None + self.nbabsnonjust = None + self.gabarit_abs = [0, 0] + self.gabarit_note = [] + self.gabarit_moy = CellFormat.FMT_MOY + self.gabarit_comp = CellFormat.FMT_MOY + + def calcul_compensation(self, act_sem, compensateur, compensed): + if self.historique[compensateur].code == "ADM+": + return False + if not self.historique[compensateur].compensable(): + return False + if not self.historique[compensed].compensable(): + return False + try: + moyenne = ( + self.historique[compensateur].moy + self.historique[compensed].moy + ) / 2.0 + except TypeError: + return False + self.moy_inter = moyenne + if moyenne < 9.999: + self.gabarit_comp += format_note(moyenne, CellFormat.FMT_MOY, 10.00) + return False + self.gabarit_comp += format_note( + moyenne, CellFormat.FMT_MOY + CellFormat.EMPHASE_COMP, 10.00 + ) + return True + + def other_compense_act(self, other, act_sem): + self.regl = "ADC%s" % other + # self.regl2 = "ADM+" # "ADM+" + # self.regl2_sem = "S%s" % other + + def act_compense_oth(self, act_sem, other): + self.regl = "ADM" # "ADM+" + self.regl2 = "ADC%s" % act_sem + self.regl2_sem = "S%s" % other + + def test_ue_acq(self, act_inscription, descripteur, act_sem): + base = (act_sem - 1) * 6 + liste = [] + for ue_code in descripteur.codes: + moyenne = act_inscription.moy_ue[ue_code] + if isinstance(moyenne, float) and moyenne >= 9.999: + liste.append(descripteur.acros[ue_code][1]) + self.gabarit_note[base] |= format_note( + moyenne, CellFormat.EMPHASE_UEACQ, 10.00 + ) + else: + self.gabarit_note[base] |= format_note(moyenne, 0, 10.00) + base += 1 + self.ues_acq = ",".join(liste) if len(liste) > 0 else "" + + def init_gabarit(self, descripteurs): + for s in SEMESTRES_IDX: + if s in self.historique: + for ue_code in descripteurs[s].codes: + if ue_code in self.historique[s].moy_ue: + if self.historique[s].capitalized[ue_code]: + self.gabarit_note += [CellFormat.FMT_CAP_UE] + else: + self.gabarit_note += [CellFormat.FMT_UE] + else: + self.gabarit_note += [0] + for _ in range(NBUE - len(descripteurs[s].codes)): + self.gabarit_note += [0] + self.gabarit_note += [CellFormat.FMT_MOY, 0] + else: + self.gabarit_note += [0, 0, 0, 0, 0, 0] + + def test_atb(self, inscription, base, descripteur): + flag = False + offset = 0 + for ue in descripteur.codes: + moy_ue = inscription.moy_ue[ue] + if ( + moy_ue != "NA" + ): # PATCH COVID-19: une UE neutralisée n'empéche pas la validation d'un semestre + if not isinstance(moy_ue, (int, float)): + self.gabarit_note[base + offset] |= format_note( + moy_ue, CellFormat.EMPHASE_ATB, None + ) + flag = True + if moy_ue < 7.995: + self.gabarit_note[base + offset] |= format_note( + moy_ue, CellFormat.EMPHASE_ATB, 8.00 + ) + flag = True + offset += 1 + if not isinstance(inscription.moy, (float, int)): + self.gabarit_note[base + 4] |= format_note( + inscription.moy, CellFormat.EMPHASE_ATB, None + ) + flag = True + else: + self.gabarit_note[base + 4] |= format_note(inscription.moy, 0, 10.00) + return flag + + def test_att(self, moyenne, base): + if not isinstance(moyenne, (int, float)) or moyenne < 9.995: + self.gabarit_note[base + 4] |= format_note( + moyenne, CellFormat.EMPHASE_ATT, 10.00 + ) + return CLASS_ATT + return False + + def reglementaire(self, act_sem, descripteur): + inscription = self.historique[act_sem] + base = 6 * (act_sem - 1) + self.gabarit_note[base + 4] |= format_note( + inscription.moy, CellFormat.FMT_MOY, 10.0 + ) + if self.test_atb(inscription, base, descripteur): + return CLASS_ATB + if self.test_att(inscription.moy, base): + return CLASS_ATT + return CLASS_ADM + + +def feuille_preparation_jury_dut(formsemestre_id): + "Feuille excel pour preparation des jurys DUT" + + # Structure de données + # Globales + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) + nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + etuds: Identite = nt.get_inscrits(order_by="moy") # tri par moy gen + sem = sco_formsemestre.get_formsemestre(formsemestre_id) + + etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id) + main_partition_id = sco_groups.formsemestre_get_main_partition(formsemestre_id)[ + "partition_id" + ] + dept_name = scu.unescape_html( + sco_preferences.get_preference("DeptName", formsemestre_id) + ) + + # définition des semestres courant(act_sem), précédent (prev_sem) et suivant (next_sem) + act_sem = nt.sem["semestre_id"] + prev_sem = act_sem - 1 if act_sem > 1 else None + next_sem = act_sem + 1 if act_sem < NBSEM else None + + # Données par rang de semestre (S1, .. S4) + descripteurs = {} # sx : SemDescripteur + + # Par étudiant + autorisations = {} + assidu = {} + # Récupération des données + for etud in etuds: + # On récupère les infos de l'étudiant courant + # info = sco_etud.get_etud_info(etudid=etudid, filled=True) + # if not info: + # continue # should not occur... + # etud = info[0] + # On récupère les semestres de l'étudiant courant + etudid = etud.id + Se = sco_parcours_dut.SituationEtudParcours( + etud.to_dict_scodoc7(), formsemestre_id + ) + historique = compute_sems( + etud, sem["formation_id"], sem["titreannee"], descripteurs, Se + ) + + # Ne traite que les étudiants pour lesquels une décision doit être prise + if historique[act_sem].code in filtre_decision: + record = Ligne(etudid) + record.historique = historique + lignes[etudid] = record + record.init_gabarit(descripteurs) + act_inscription = record.historique[act_sem] + + # Calcul des classes des semestres impliqués + act_class = record.reglementaire( + act_sem, descripteurs[act_sem] + ) # act_inscription.reglementaire(record) + if prev_sem in record.historique: + prev_class = classe(record.historique[prev_sem].code) + else: + prev_class = None + if next_sem in record.historique: + next_class = classe(record.historique[next_sem].code) + else: + next_class = None + + # Algorithme de calcul de la décision 'réglementaire' + if sem["ins"]["etat"] == "D" or (historique[act_sem].code == "DEF"): + # Etudiant démissionnaire -> 'AJ' ou 'DEF' ? + record.regl = "AJ" + elif act_class == CLASS_ADM: + if ( + prev_class == CLASS_ATT + ): # compensation éventuelle du prev_sem par act_sem + if record.calcul_compensation(act_sem, act_sem, prev_sem): + record.act_compense_oth(act_sem, prev_sem) + else: + record.regl = "AJ" + # record.test_ue_acq(act_inscription, descripteurs[act_sem], act_sem) + record.gabarit_note[ + (act_sem - 1) * 6 - 1 + ] |= CellFormat.EMPHASE_PREC + elif prev_class == CLASS_ADM: + if ( + next_class == CLASS_ATT + ): # compensation éventuelle du next_sem par act_sem + if record.calcul_compensation(act_sem, act_sem, next_sem): + record.act_compense_oth(act_sem, next_sem) + else: + record.regl = "ADM" + else: + record.regl = "ADM" + elif prev_class == CLASS_ATB: + record.regl = "AJ" + else: + record.regl = "ADM" + elif act_class == CLASS_ATT: + if prev_class == CLASS_ATT: + record.regl = "ATT" + elif ( + prev_class == CLASS_ADM + ): # compensation éventuelle du act_sem par prev_sem + if record.calcul_compensation(act_sem, prev_sem, act_sem): + record.other_compense_act(prev_sem, act_sem) + elif ( + next_class == CLASS_ADM + ): # compensation éventuelle du act_sem par next_sem + if record.calcul_compensation(act_sem, next_sem, act_sem): + record.other_compense_act(next_sem, act_sem) + else: + record.regl = "ATT" + else: + record.regl = "ATT" + elif prev_class == CLASS_ATB: + record.regl = "ATT" + else: + record.regl = "ATT" + elif act_class == CLASS_ATB: + record.regl = "ATB" + else: # should not happen + pass + + if record.regl in {"AJ", "ATT", "ATB"}: + record.test_ue_acq(act_inscription, descripteurs[act_sem], act_sem) + decision = record.historique[act_sem].nt.get_etud_decision_sem(etudid) + if decision: + assidu[etudid] = {0: "Non", 1: "Oui"}.get(decision["assidu"], "") + aut_list = sco_parcours_dut.formsemestre_get_autorisation_inscription( + etudid, formsemestre_id + ) + autorisations[etudid] = ", ".join( + ["S%s" % x["semestre_id"] for x in aut_list] + ) + # parcours: + record.parcours = Se.get_parcours_descr() + # groupe principal (td) + record.groupestd = "" + for s in Se.etud["sems"]: + if s["formsemestre_id"] == formsemestre_id: + record.groupesTd = etud_groups.get(etudid, {}).get( + main_partition_id, "" + ) + # absences: + record.nbabs, record.nbabsjust = sco_abs.get_abs_count(etudid, sem) + record.nbabsnonjust = record.nbabs - record.nbabsjust + record.gabarit_abs = ( + [0, 0] + if record.nbabsnonjust <= SEUIL_ABS + else [0, CellFormat.EMPHASE_ABS] + ) + else: + log("Etudiant ignoré: %s [%s]" % (etudid, historique[act_sem].code)) + + update_libelles(act_sem, descripteurs) + formatteur.set_default_format(excel_make_style(font_name="Calibri", size=9)) + formatteur.set_borders_default( + left="none", right="none", top="filet", bottom="filet" + ) + formatteur.last_rank = len(lignes) + formatteur.set_categories(CATEGORIES, CATEGORIE_DESC) + + # Tri des codes des UE des semestres: + for descripteur in descripteurs.values(): + descripteur.sort() + + worksheet = ScoExcelSheet(sheet_name="Preparation_Jury %s" % SEMESTRES_SX[act_sem]) + bloc_append_entete(worksheet, sem, descripteurs, act_sem) + + # Mise en forme finale + rang = 1 # numero etudiant + for etud in etuds: + etudid = etud.id + if etudid in lignes: + record = lignes[etudid] + if record.historique[act_sem].code in filtre_decision: + # récupération des données + etud = nt.identdict[etudid] + nip = etud["code_nip"] + infos = query_apogee_portal(nip=etud["code_nip"]) + # calculs de validation + + # affichage des données de l'étudiant + line = [str(rang), etud["code_nip"]] + line += display_donnees_apogee(infos) + line += display_donnees_scodoc(etudid, etud, record) + + co = len(line) + gabarit = [0 for _ in range(len(line))] + + for s in SEMESTRES_IDX: + if s in record.historique: + inscription = record.historique[s] + inscription.display(line, descripteurs[s]) + co += 6 + else: + line += ["", "", "", "", "", ""] + gabarit += record.gabarit_note + + line.append(fmt(str(record.nbabs))) + line.append(fmt(str(record.nbabsnonjust))) + if record.nbabsnonjust < 8: + gabarit += [0, 0] + else: + gabarit += [0, CellFormat.EMPHASE_ABS] + + if record.moy_inter is not None: + line.append(fmt(record.moy_inter)) + gabarit.append(record.gabarit_comp) + else: + line.append("") + gabarit.append(0) + + # line.append('S%s' % act_sem) + # gabarit.append(0) + + for _ in range(2): # Reglementaire et préjury + line.append(record.regl if record.regl is not None else "") + line.append( + record.regl2_sem if record.regl2_sem is not None else "" + ) + line.append(record.regl2 if record.regl2 is not None else "") + line.append(record.ues_acq if record.ues_acq is not None else "") + gabarit += [0, 0, 0, 0] + + # Jury et remarques + line += ["", "", "", "", ""] + gabarit += [0, 0, 0, 0, 0] + # clef + clef = [mod95(line[i]) for i in range(1, 50)] + line.append("".join(clef)) + gabarit += [0] + styles = format_line(worksheet, rang, gabarit) + worksheet.append_row(worksheet.make_row(line, styles)) + rang += 1 + + skip_n_lines(worksheet, 8) + # bloc_append_legendes(worksheet, rang + 4, descripteurs) + formattage_table(worksheet, act_sem, descripteurs, rang) + # pane(worksheet) + + merge_categories(worksheet) + xls = worksheet.generate() + return scu.send_file( + xls, + "PrepaJury %s %s.xls" % (dept_name, SEMESTRES_SX[act_sem]), + scu.XLSX_SUFFIX, + scu.XLSX_MIMETYPE, + ) diff --git a/app/scodoc/sco_prepajury_lille.py b/app/scodoc/sco_prepajury_lille.py index 1c963bfa0..5893100c2 100644 --- a/app/scodoc/sco_prepajury_lille.py +++ b/app/scodoc/sco_prepajury_lille.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,18 +28,27 @@ """Feuille excel pour préparation des jurys classiques (non BUT) """ import collections +import logging import time +from enum import Enum from openpyxl.styles.numbers import FORMAT_NUMBER_00 -from flask import flash +from flask import flash, current_app from flask import request from flask_login import current_user +from app import db from app.comp import res_sem from app.comp.res_compat import NotesTableCompat -from app.models import FormSemestre, Identite, ScolarAutorisationInscription -from app.scodoc import sco_assiduites, codes_cursus +from app.models import ( + FormSemestre, + Identite, + ScolarAutorisationInscription, + ApcParcours, +) +from app.scodoc import sco_assiduites +from app.scodoc import codes_cursus from app.scodoc import sco_groups from app.scodoc import sco_etud from app.scodoc import sco_excel @@ -48,121 +57,505 @@ from app.scodoc import sco_cursus from app.scodoc import sco_preferences import app.scodoc.sco_utils as scu import sco_version +from app.scodoc.sco_excel import excel_make_style, COLORS +from app.scodoc.sco_excel_add import make_pattern +from app.scodoc.sco_prepajury_formats import Formatter + + +COLORS = { + "ETUDIANT": ["FFFF99"], + "BUT1": ["95B3D7", "B8CCE4", "DCE6F1"], + "BUT2": ["C4D79B", "D8E4BC", "EBF1DE"], + "BUT3": ["FABF8F", "FCD5B4", "FDE9D9"], + "RECAP": ["D9D9D9"], +} +CATEGORIES = { + "ETUDIANT": { + "Id": None, + "NIP": None, + "civ": None, + "Nom": None, + "Prénom": None, + "Régime": None, + "Cursus": None, + "Absences": { + "Tot.": None, + "Non Just": None, + }, + }, + "BUT1": { + "+": { + "+UE 1.1": { + "Note": None, + "Rés.": None, + }, + "+UE 2.1": { + "Note": None, + "Rés.": None, + }, + "BCC 1": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 1.2": { + "Note": None, + "Rés.": None, + }, + "+UE 2.2": { + "Note": None, + "Rés.": None, + }, + "BCC 2": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 1.3": { + "Note": None, + "Rés.": None, + }, + "+UE 2.3": { + "Note": None, + "Rés.": None, + }, + "BCC 3": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 1.4": { + "Note": None, + "Rés.": None, + }, + "+UE 2.4": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 1.5": { + "Note": None, + "Rés.": None, + }, + "+UE 2.5": { + "Note": None, + "Rés.": None, + }, + "BCC 5": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 1.6": { + "Note": None, + "Rés.": None, + }, + "+UE 2.6": { + "Note": None, + "Rés.": None, + }, + "BCC 6": { + "Note": None, + "Rés.": None, + }, + }, + "Nb RCUE": None, + "Moy.": None, + "Rés.": None, + }, + "BUT2": { + "+": { + "+UE 3.1": { + "Note": None, + "Rés.": None, + }, + "+UE 4.1": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 3.2": { + "Note": None, + "Rés.": None, + }, + "+UE 4.2": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 3.3": { + "Note": None, + "Rés.": None, + }, + "+UE 4.3": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 3.4": { + "Note": None, + "Rés.": None, + }, + "+UE 4.4": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 3.5": { + "Note": None, + "Rés.": None, + }, + "+UE 4.5": { + "Note": None, + "Rés.": None, + }, + "BCC 5": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 3.6": { + "Note": None, + "Rés.": None, + }, + "+UE 4.6": { + "Note": None, + "Rés.": None, + }, + "BCC 6": { + "Note": None, + "Rés.": None, + }, + }, + "Nb RCUE": None, + "Moy.": None, + "Rés.": None, + "DUT": None, + }, + "BUT3": { + "+": { + "+UE 5.1": { + "Note": None, + "Rés.": None, + }, + "+UE 6.1": { + "Note": None, + "Rés.": None, + }, + "BCC 1": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 5.2": { + "Note": None, + "Rés.": None, + }, + "+UE 6.2": { + "Note": None, + "Rés.": None, + }, + "BCC 2": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 5.3": { + "Note": None, + "Rés.": None, + }, + "+UE 6.3": { + "Note": None, + "Rés.": None, + }, + "BCC 3": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 5.4": { + "Note": None, + "Rés.": None, + }, + "+UE 6.4": { + "Note": None, + "Rés.": None, + }, + "BCC 4": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 5.5": { + "Note": None, + "Rés.": None, + }, + "+UE 6.5": { + "Note": None, + "Rés.": None, + }, + "BCC 5": { + "Note": None, + "Rés.": None, + }, + }, + "+": { + "+UE 5.6": { + "Note": None, + "Rés.": None, + }, + "+UE 6.6": { + "Note": None, + "Rés.": None, + }, + "BCC 6": { + "Note": None, + "Rés.": None, + }, + }, + "Nb RCUE": None, + "Moy.": None, + "Rés.": None, + "BUT": None, + }, + "RECAP": { + "Devenir": None, + "Etape": None, + "Remarque": None, + }, +} + + +class IndexApc: + def __init__(self): + self.niveau: int = None + self.rcue: int = None + self.semestre: int = None + + def __init__(self, niveau): + self.__init__() + self.niveau = niveau + + def __init__(self, niveau, ue): + self.__init__(niveau) + + def __init__(self, niveau, ue, semestre): + self.__init__(niveau, ue) + self.semestre = semestre + + +class Export: + def __init__(self, formsemestre_id): + self.formsemestre_id: int = formsemestre_id + self.formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id) + self.nt: NotesTableCompat = res_sem.load_formsemestre_results(self.formsemestre) + etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id) + main_partition_id = sco_groups.formsemestre_get_main_partition(formsemestre_id) + self.etuds = self.nt.get_inscrits(order_by="moy") + self.formation_code = self.formsemestre.formation.formation_code + self.dept_name = scu.unescape_html( + sco_preferences.get_preference("DeptName", formsemestre_id) + ) + self.parcours = {} + self.competences = {} + self.data_etud = {} + self.data_sems = {} + self.data_comp = {} + self.load_data() + self.formatteur = Formatter() + self.build_formator() + + def load_parcours(self): + for etud in self.etuds: + parcours_id = self.nt.etuds_parcour_id[etud.id] + if parcours_id is not None and parcours_id not in self.data_comp: + parcours = db.session.get(ApcParcours, parcours_id) + self.data_comp[parcours_id] = parcours + + def load_formsemestre_data(self, formsemestre: FormSemestre): + sem_id = formsemestre.id + self.data_sems[sem_id] = {} + self.data_sems[sem_id]["formsemestre"] = formsemestre + nt = res_sem.load_formsemestre_results(formsemestre) + self.data_sems[sem_id]["nt"] = nt + + def load_formsemestre(self, etud, session): + for sem in session.sems: + if sem["formation"].formation_code == self.formation_code: + sem_id = sem["id"] + formsemestre: FormSemestre = FormSemestre.get_formsemestre(sem_id) + if sem_id not in self.data_sems: + self.load_formsemestre_data(formsemestre) + if session.prev_formsemestre: + prev_id = session.prev_formsemestre.id + prev_session = sco_cursus.get_situation_etud_cursus( + etud, prev_id + ) + self.load_formsemestre(etud, prev_session) + semestre_id = formsemestre.semestre_id + parcours = self.data_sems[sem_id]["nt"].etuds_parcour_id[etud.id] + if parcours is None or parcours == self.nt.etuds_parcour_id[etud.id]: + self.data_etud[etud.id]["sems"][semestre_id] = formsemestre + else: + current_app.logger.info(f"{etud.id}: Changement de parcours: ") + + def load_etu(self, etud): + # etudid=str(etudid), + # code_nip=etud.code_nip or "", + # code_ine=etud.code_ine or "", + # nom=quote_xml_attr(etud.nom), + # prenom=quote_xml_attr(etud.prenom), + # civilite=quote_xml_attr(etud.civilite_str), + # sexe=quote_xml_attr(etud.civilite_str), # compat + pass + + def load_data(self): + self.load_parcours() + for etud in self.etuds: + self.data_etud[etud.id] = {} + self.data_etud[etud.id]["etud"] = etud + self.data_etud[etud.id]["sems"] = {} + session = sco_cursus.get_situation_etud_cursus(etud, self.formsemestre_id) + breakpoint() + self.data_etud[etud.id]["cursus"] = session.parcours + self.load_formsemestre(etud, session) + + def build_formator(self): + self.formatteur = Formatter() + self.formatteur.set_default_format( + excel_make_style(font_name="Calibri", size=9) + ) + self.formatteur.set_borders_default( + left="none", right="none", top="filet", bottom="filet" + ) + self.formatteur.last_rank = 10 # len(lignes) + # self.formatteur.set_categories(CATEGORIES) + + def header_format(self, styles, items, row_no, left, color, level): + if row_no <= 4: + if items is None: + # styles[row_no].append(color[level]) + styles[row_no].append(excel_make_style(bgcolor=color[level])) + self.header_format(styles, None, row_no + 1, left, color, level) + return 1 + else: + color = items.get("COLOR", color) + compte = 0 + for item in items: + if item in COLORS: + color = COLORS[item] + detail = items[item] + if item[0] == "+": + level2 = level + 1 + else: + level2 = level + styles[row_no].append(excel_make_style(bgcolor=color[level2])) + nbelt = self.header_format( + styles, + detail, + row_no + 1, + left + compte, + color, + level2, + ) + for _ in range(1, nbelt): + styles[row_no].append(excel_make_style(bgcolor=color[level2])) + compte += 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 + else: + return 1 + + def header_append(self, raws, items, row_no): + if row_no <= 4: + if items is None: + raws[row_no].append("") + self.header_append(raws, None, row_no + 1) + return 1 + else: + compte = 0 + for item in items: + if item != "COLOR": + detail = items[item] + if item[0] != "+": + raws[row_no].append(item) + else: + raws[row_no].append(item[1:]) + nbelt = self.header_append(raws, detail, row_no + 1) + for _ in range(1, nbelt): + raws[row_no].append("") + compte += nbelt + return compte + else: + return 1 + + def write_header(self, sheet): + raws = [[], [], [], [], []] + styles = [[], [], [], [], []] + col_no = 0 + raw_no = 0 + self.header_format(styles, CATEGORIES, 0, 0, ["FFFF00"], 0) + self.header_append(raws, CATEGORIES, 0) + for i in range(0, 4): + sheet.append_row( + sheet.make_row( + raws[i], + styles[i], + # style=excel_make_style( + # 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 feuille_preparation_lille(formsemestre_id): - """Feuille excel pour préparation des jurys classiques. - Non adaptée pour le BUT. - """ - formsemestre = FormSemestre.query.get_or_404(formsemestre_id) - nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - breakpoint() - etuds: list[Identite] = nt.get_inscrits(order_by="moy") # tri par moy gen - sem = sco_formsemestre.get_formsemestre(formsemestre_id) + """Feuille excel pour préparation des jurys classiques.""" + export = Export(formsemestre_id) - etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id) - main_partition_id = sco_groups.formsemestre_get_main_partition(formsemestre_id)[ - "partition_id" - ] - - prev_moy_ue = collections.defaultdict(dict) # ue_code_s : { etudid : moy ue } - prev_ue_acro = {} # ue_code_s : acronyme (à afficher) - prev_moy = {} # moyennes gen sem prec - moy_ue = collections.defaultdict(dict) # ue_acro : moyennes { etudid : moy ue } - ue_acro = {} # ue_code_s : acronyme (à afficher) - moy = {} # moyennes gen - moy_inter = {} # moyenne gen. sur les 2 derniers semestres - code = {} # decision existantes s'il y en a - autorisations = {} - prev_code = {} # decisions sem prec - assidu = {} - parcours = {} # etudid : parcours, sous la forme S1, S2, S2, S3 - groupestd = {} # etudid : nom groupe principal - nbabs = {} - nbabsjust = {} - breakpoint() - for etud in etuds: - Se = sco_cursus.get_situation_etud_cursus(etud, formsemestre_id) - if Se.prev_formsemestre: - ntp: NotesTableCompat = res_sem.load_formsemestre_results( - Se.prev_formsemestre - ) - for ue in ntp.get_ues_stat_dict(filter_sport=True): - ue_status = ntp.get_etud_ue_status(etud.id, ue["ue_id"]) - ue_code_s = ( - ue["ue_code"] + "_%s" % ntp.sem["semestre_id"] - ) # code indentifiant l'UE - prev_moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else "" - prev_ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"]) - prev_moy[etud.id] = ntp.get_etud_moy_gen(etud.id) - prev_decision = ntp.get_etud_decision_sem(etud.id) - if prev_decision: - prev_code[etud.id] = prev_decision["code"] - if prev_decision["compense_formsemestre_id"]: - prev_code[etud.id] += "+" # indique qu'il a servi a compenser - - moy[etud.id] = nt.get_etud_moy_gen(etud.id) - for ue in nt.get_ues_stat_dict(filter_sport=True): - ue_status = nt.get_etud_ue_status(etud.id, ue["ue_id"]) - ue_code_s = f'{ue["ue_code"]}_{nt.sem["semestre_id"]}' - moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else "" - ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"]) - - if Se.prev_formsemestre: - try: - moy_inter[etud.id] = (moy[etud.id] + prev_moy[etud.id]) / 2.0 - except (KeyError, TypeError): - pass - - decision = nt.get_etud_decision_sem(etud.id) - if decision: - code[etud.id] = decision["code"] - if decision["compense_formsemestre_id"]: - code[etud.id] += "+" # indique qu'il a servi a compenser - assidu[etud.id] = {False: "Non", True: "Oui"}.get(decision["assidu"], "") - - autorisations_etud = ScolarAutorisationInscription.query.filter_by( - etudid=etud.id, origin_formsemestre_id=formsemestre_id - ).all() - autorisations[etud.id] = ", ".join( - [f"S{x.semestre_id}" for x in autorisations_etud] - ) - # parcours: - parcours[etud.id] = "sco_prepajury_lille:132" # Se.get_parcours_descr() - # groupe principal (td) - groupestd[etud.id] = "" - for s in Se.etud.formsemestre_inscriptions: - if s.formsemestre_id == formsemestre_id: - groupestd[etud.id] = etud_groups.get(etud.id, {}).get( - main_partition_id, "" - ) - # absences: - _, e_nbabsjust, e_nbabs = sco_assiduites.get_assiduites_count(etud.id, sem) - nbabs[etud.id] = e_nbabs - nbabsjust[etud.id] = e_nbabs - e_nbabsjust - - # Codes des UE "semestre précédent": - ue_prev_codes = list(prev_moy_ue.keys()) - ue_prev_codes.sort( - key=lambda x, prev_ue_acro=prev_ue_acro: prev_ue_acro[ # pylint: disable=undefined-variable - x - ] - ) - # Codes des UE "semestre courant": - ue_codes = list(moy_ue.keys()) - ue_codes.sort( - key=lambda x, ue_acro=ue_acro: ue_acro[x] # pylint: disable=undefined-variable - ) - - sid = sem["semestre_id"] - sn = sp = "" - if sid >= 0: - sn = f"S{sid}" - if prev_moy: # si qq chose dans precedent - sp = f"S{sid - 1}" - - sheet = sco_excel.ScoExcelSheet(sheet_name=f"Prepa Jury {sn}") + sheet = sco_excel.ScoExcelSheet(sheet_name=f"Prepation Jury") # génération des styles + style_bold = sco_excel.excel_make_style(size=10, bold=True) style_center = sco_excel.excel_make_style(halign="center") style_boldcenter = sco_excel.excel_make_style(bold=True, halign="center") @@ -177,10 +570,11 @@ def feuille_preparation_lille(formsemestre_id): ) # Première ligne - sheet.append_single_cell_row( - "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold - ) + # sheet.append_single_cell_row( + # "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold + # ) sheet.append_blank_row() + export.write_header(sheet) # Ligne de titre titles = ["Rang"] @@ -188,154 +582,138 @@ def feuille_preparation_lille(formsemestre_id): titles.append("NIP") if sco_preferences.get_preference("prepa_jury_ine"): titles.append("INE") - titles += [ - "etudid", - "Civ.", - "Nom", - "Prénom", - "Naissance", - "Bac", - "Spe", - "Rg Adm", - "Parcours", - "Groupe", - ] - if prev_moy: # si qq chose dans precedent - titles += [prev_ue_acro[x][1] for x in ue_prev_codes] + [ - f"Moy {sp}", - f"Décision {sp}", - ] - titles += [ue_acro[x][1] for x in ue_codes] + [f"Moy {sn}"] - if moy_inter: - titles += [f"Moy {sp}-{sn}"] - titles += ["Abs", "Abs Injust."] - if code: - titles.append("Proposit. {sn}") - if autorisations: - titles.append("Autorisations") - # titles.append('Assidu') - sheet.append_row(sheet.make_row(titles, style_boldcenter)) - # if prev_moy: - # tit_prev_moy = "Moy " + sp - # # col_prev_moy = titles.index(tit_prev_moy) - # tit_moy = "Moy " + sn - # col_moy = titles.index(tit_moy) - # col_abs = titles.index("Abs") - - def fmt(x): - "reduit les notes a deux chiffres" - x = scu.fmt_note(x, keep_numeric=False) - try: - return float(x) - except: - return x - - i = 1 # numero etudiant - for etud in etuds: - cells = [] - cells.append(sheet.make_cell(str(i))) - if sco_preferences.get_preference("prepa_jury_nip"): - cells.append(sheet.make_cell(etud.code_nip)) - if sco_preferences.get_preference("prepa_jury_ine"): - cells.append(sheet.make_cell(etud.code_ine)) - admission = etud.admission - cells += sheet.make_row( - [ - etud.id, - etud.civilite_str, - sco_etud.format_nom(etud.nom), - sco_etud.format_prenom(etud.prenom), - etud.date_naissance, - admission.bac, - admission.specialite, - admission.classement, - parcours[etud.id], - groupestd[etud.id], - ] - ) - co = len(cells) - if prev_moy: - for ue_acro in ue_prev_codes: - cells.append( - sheet.make_cell( - fmt(prev_moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note - ) - ) - co += 1 - cells.append( - sheet.make_cell(fmt(prev_moy.get(etud.id, "")), style_bold) - ) # moy gen prev - cells.append( - sheet.make_cell(fmt(prev_code.get(etud.id, "")), style_moy) - ) # decision prev - co += 2 - - for ue_acro in ue_codes: - cells.append( - sheet.make_cell( - fmt(moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note - ) - ) - co += 1 - cells.append( - sheet.make_cell(fmt(moy.get(etud.id, "")), style_note_bold) - ) # moy gen - co += 1 - if moy_inter: - cells.append(sheet.make_cell(fmt(moy_inter.get(etud.id, "")), style_note)) - cells.append(sheet.make_cell(str(nbabs.get(etud.id, "")), style_center)) - cells.append(sheet.make_cell(str(nbabsjust.get(etud.id, "")), style_center)) - if code: - cells.append(sheet.make_cell(code.get(etud.id, ""), style_moy)) - cells.append(sheet.make_cell(autorisations.get(etud.id, ""), style_moy)) - # l.append(assidu.get(etud.id, '')) - sheet.append_row(cells) - i += 1 + # if prev_moy: # si qq chose dans precedent + # titles += [prev_ue_acro[x][1] for x in ue_prev_codes] + [ + # f"Moy {sp}", + # f"Décision {sp}", + # ] + # titles += [ue_acro[x][1] for x in ue_codes] + [f"Moy {sn}"] + # if moy_inter: + # titles += [f"Moy {sp}-{sn}"] + # titles += ["Abs", "Abs Injust."] + # if code: + # titles.append("Proposit. {sn}") + # if autorisations: + # titles.append("Autorisations") + # # titles.append('Assidu') + # sheet.append_row(sheet.make_row(titles, style_boldcenter)) + # def fmt(x): + # "reduit les notes a deux chiffres" + # x = scu.fmt_note(x, keep_numeric=False) + # try: + # return float(x) + # except: + # return x # - sheet.append_blank_row() - # Explications des codes - codes = list(codes_cursus.CODES_EXPL.keys()) - codes.sort() - sheet.append_single_cell_row("Explication des codes") - for code in codes: - sheet.append_row( - sheet.make_row(["", "", "", code, codes_cursus.CODES_EXPL[code]]) - ) - sheet.append_row( - sheet.make_row( - [ - "", - "", - "", - "ADM+", - "indique que le semestre a déjà servi à en compenser un autre", - ] - ) - ) + # i = 1 # numero etudiant + # for etud in etuds: + # cells = [] + # cells.append(sheet.make_cell(str(i))) + # if sco_preferences.get_preference("prepa_jury_nip"): + # cells.append(sheet.make_cell(etud.code_nip)) + # if sco_preferences.get_preference("prepa_jury_ine"): + # cells.append(sheet.make_cell(etud.code_ine)) + # cells += sheet.make_row( + # [ + # etud.id, + # etud.civilite_str, + # scu.format_nom(etud.nom), + # scu.format_prenom(etud.prenom), + # etud.date_naissance, + # etud.admission.bac if etud.admission else "", + # etud.admission.specialite if etud.admission else "", + # etud.admission.classement if etud.admission else "", + # parcours[etud.id], + # groupestd[etud.id], + # ] + # ) + # co = len(cells) + # if prev_moy: + # for ue_acro in ue_prev_codes: + # cells.append( + # sheet.make_cell( + # fmt(prev_moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note + # ) + # ) + # co += 1 + # cells.append( + # sheet.make_cell(fmt(prev_moy.get(etud.id, "")), style_bold) + # ) # moy gen prev + # cells.append( + # sheet.make_cell(fmt(prev_code.get(etud.id, "")), style_moy) + # ) # decision prev + # co += 2 + # + # for ue_acro in ue_codes: + # cells.append( + # sheet.make_cell( + # fmt(moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note + # ) + # ) + # co += 1 + # cells.append( + # sheet.make_cell(fmt(moy.get(etud.id, "")), style_note_bold) + # ) # moy gen + # co += 1 + # if moy_inter: + # cells.append(sheet.make_cell(fmt(moy_inter.get(etud.id, "")), style_note)) + # cells.append(sheet.make_cell(nbabs.get(etud.id, ""), style_center)) + # cells.append(sheet.make_cell(nbabsjust.get(etud.id, ""), style_center)) + # if code: + # cells.append(sheet.make_cell(code.get(etud.id, ""), style_moy)) + # cells.append(sheet.make_cell(autorisations.get(etud.id, ""), style_moy)) + # # l.append(assidu.get(etud.id, '')) + # sheet.append_row(cells) + # i += 1 + # # + # sheet.append_blank_row() + + # # Explications des codes + + # codes = list(codes_cursus.CODES_EXPL.keys()) + # codes.sort() + # sheet.append_single_cell_row("Explication des codes") + # for code in codes: + # sheet.append_row( + # sheet.make_row(["", "", "", code, codes_cursus.CODES_EXPL[code]]) + # ) + # sheet.append_row( + # sheet.make_row( + # [ + # "", + # "", + # "", + # "ADM+", + # "indique que le semestre a déjà servi à en compenser un autre", + # ] + # ) + # ) # UE : Correspondances acronyme et titre complet + sheet.append_blank_row() - sheet.append_single_cell_row("Titre des UE") - if prev_moy: - for ue in ntp.get_ues_stat_dict(filter_sport=True): - sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]])) - for ue in nt.get_ues_stat_dict(filter_sport=True): - sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]])) - # - sheet.append_blank_row() - sheet.append_single_cell_row( - "Préparé par %s le %s sur %s pour %s" - % ( - sco_version.SCONAME, - time.strftime("%d/%m/%Y"), - request.url_root, - current_user, - ) - ) + + # sheet.append_single_cell_row("Titre des UE") + # if prev_moy: + # for ue in ntp.get_ues_stat_dict(filter_sport=True): + # sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]])) + # for ue in nt.get_ues_stat_dict(filter_sport=True): + # sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]])) + # # + # sheet.append_blank_row() + # sheet.append_single_cell_row( + # "Préparé par %s le %s sur %s pour %s" + # % ( + # sco_version.SCONAME, + # time.strftime(scu.DATE_FMT), + # request.url_root, + # current_user, + # ) + # ) xls = sheet.generate() flash("Feuille préparation jury générée") return scu.send_file( xls, - f"PrepaJury{sn}", + f"PrepaJury", scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE, )