diff --git a/app/scodoc/sco_prepajury_but.py b/app/scodoc/sco_prepajury_but.py index 3b33c7e96..fad731055 100644 --- a/app/scodoc/sco_prepajury_but.py +++ b/app/scodoc/sco_prepajury_but.py @@ -32,13 +32,23 @@ import time from openpyxl.styles.numbers import FORMAT_NUMBER_00 -from flask import flash +from flask import flash, abort from flask import request from flask_login import current_user +from app.but.cursus_but import EtudCursusBUT from app.comp import res_sem +from app.comp.res_but import ResultatsSemestreBUT +from app.comp.res_common import ResultatsSemestre from app.comp.res_compat import NotesTableCompat -from app.models import FormSemestre, Identite, ScolarAutorisationInscription +from app.models import ( + FormSemestre, + Identite, + ScolarAutorisationInscription, + ApcParcours, + Formation, + ApcCompetence, +) from app.scodoc import sco_abs from app.scodoc import codes_cursus from app.scodoc import sco_groups @@ -50,293 +60,213 @@ from app.scodoc import sco_preferences import app.scodoc.sco_utils as scu import sco_version +# Structure de donnée: +# --- _Compilation +# +----- parcours +# +- parcour : ApcParcours +# +- etudiants : list[_Etudiant] + + +class _Bilan: + def __init__(self): + self.note = 0 + self.resultat = 0 + self.format = 0 + + +class _UE: + def __init__(self): + self.bilan = None + + +class _Niveau: + def __init__(self, etudiant, niveauDesc, semestres): + self.bilan = None + self.etudiant = etudiant + self.candidates = niveauDesc.get_ues(etudiant) + self.UEs: tuple[_UE, _UE] = niveauDesc.get_ues(etudiant) + + +class _Annee: + def __init__(self): + self.bilan: _Bilan = None + self.niveaux = {} + + +class _Competence: + def __init__(self, etudiant, competenceDesc, semestres): + self.descr = competenceDesc + self.etudiant = etudiant + self.niveaux = [] + for niveauDesc in competenceDesc.niveaux: + self.niveaux.append(_Niveau(etudiant, niveauDesc, semestres)) + + +class _Semestre: + def __init__(self, formsemestre_id, formsemestre=None): + if formsemestre is None: + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + self.formsemestre = formsemestre + self.resultatsSemestreBUT: ResultatsSemestreBUT = ( + res_sem.load_formsemestre_results(formsemestre) + ) + # self.etuds_ues = ( + # self.resultatsSemestreBUT.load_ues_inscr_parcours() + # ) + + +class _NiveauDesc: + def __init__(self, scodocNiveau): + self.fromScodoc = scodocNiveau + self.ues = {} + for ue in scodocNiveau.ues: + self.ues[(ue.formation_id, ue.semestre_idx)] = ue + + def get_ues(self, etudiant): + """get list of candidates UEs for Niveau""" + ues = [None, None] + for inscription in etudiant.cursus.inscriptions: + formation_id = inscription.formsemestre.formation_id + semestre_idx = inscription.formsemestre.semestre_id + if (formation_id, semestre_idx) in self.ues: + breakpoint() + # identifier les ues cocernées + ues[semestre_idx % 2] = inscription.formsemestre + return ues + + +class _CompetenceDesc: + def __init__(self, scodocCompetence): + self.fromScodoc: ApcCompetence = scodocCompetence + self.niveaux: list[_NiveauDesc] = [ + _NiveauDesc(scodocNiveau) for scodocNiveau in scodocCompetence.niveaux.all() + ] + + +class _ParcoursDesc: + def __init__(self, formation, scodocParcour: ApcParcours = None): + self.fromScodoc: ApcParcours = scodocParcour # None pour le tronc commun 'TC' + self.etudiants = [] + if scodocParcour is None: + self.competences = [ + _CompetenceDesc(scodocCompetence) + for scodocCompetence in formation.referentiel_competence.get_competences_tronc_commun() + ] + else: + query = formation.query_competences_parcour(scodocParcour) + if query is None: + self.competences = [] + else: + self.competences = [ + _CompetenceDesc(scodocCompetence) + for scodocCompetence in query.all() + ] + + def add_etudiant(self, etudiant): + if not etudiant in self.etudiants: + self.etudiants.append(etudiant) + + def getData(self): + data = [] + etudiant: _Etudiant + for etudiant in self.etudiants: + data.append(etudiant.getData()) + return data + + +class _Etudiant: + def __init__(self, ident, formation): + self.ident = ident + self.cursus: EtudCursusBUT = EtudCursusBUT(ident, formation) + self.semestres = {} + self.parcour = self.cursus.inscriptions[-1].parcour + self.history = [] + self.competence = [] + self.annees = [] + for inscription in self.cursus.inscriptions: + formsemestre = inscription.formsemestre + self.semestres[formsemestre.semestre_id] = inscription.formsemestre + Sx = f"S{formsemestre.semestre_id}" + etat = inscription.etat + if etat != "I": + Sx += "(Dem)" if etat == "D" else f"({etat})" + self.history.append(Sx) + + def fill_in(self, parcourDescr, semestres, cursus): + for competenceDescr in parcourDescr.competences: + self.competence.append(_Competence(self, competenceDescr, semestres)) + + def getData(self): + result = [ + self.ident.id, + self.ident.code_nip, + self.ident.civilite, + self.ident.nom, + self.ident.prenom, + self.ident.etat_civil_pv(with_paragraph=False), + self.parcour.code, + ", ".join(self.history), + ] + return result + + +class _Compilation: + def __init__(self, formsemestre: FormSemestre): + self.semestres = {} + self.parcours = {} + formsemestre_id = formsemestre.formsemestre_id + self.semestre: _Semestre = _Semestre(formsemestre_id, formsemestre) + self.formation: Formation = formsemestre.formation + self.add_semestre(formsemestre_id) + breakpoint() + for ident in self.semestre.resultatsSemestreBUT.get_inscrits(order_by="moy"): + etudiant: _Etudiant = _Etudiant(ident, self.formation) + for inscription in etudiant.cursus.inscriptions: + formsemestre = inscription.formsemestre + self.add_semestre(formsemestre.id) + scodocParcour = etudiant.parcour + if scodocParcour is None: + parcourCode = "TC" + else: + parcourCode = scodocParcour.code + if parcourCode in self.parcours: + parcoursDesc = self.parcours[parcourCode] + else: + parcoursDesc = _ParcoursDesc(self.formation, scodocParcour) + self.parcours[parcourCode] = parcoursDesc + parcoursDesc.add_etudiant(etudiant) + etudiant.fill_in(parcoursDesc, self.semestres, etudiant.cursus) + + def add_semestre(self, formsemestre_id): + if not formsemestre_id in self.semestres: + self.semestres[formsemestre_id] = _Semestre(formsemestre_id) + + def add_parcours(self, parcour: ApcParcours, etudiant: _Etudiant): + parcour_code = parcour.get("code", "TC") + if not parcour_code in self.parcours: + self.parcours[parcour_code] = _ParcoursDesc(self.formation, parcour) + self.parcours[parcour_code].add(etudiant) + + def computes_decision(self): + pass + + def make_excel(self, filename: str): + for parcours in self.parcours: + data = self.parcours[parcours].getData() + mime, suffix = scu.get_mime_suffix("xlsx") + return scu.send_file(data, filename=filename, mime=mime, suffix=suffix) + def feuille_preparation_jury_but(formsemestre_id): - """Feuille excel pour préparation des jurys adaptée pour le BUT.""" - breakpoint() + if not isinstance(formsemestre_id, int): + abort(404) formsemestre = FormSemestre.get_formsemestre(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" - ] - - 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 = {} - for etud in etuds: - Se = sco_cursus.get_situation_etud_cursus( - etud.to_dict_scodoc7(), formsemestre_id - ) - if Se.prev: - formsemestre_prev = FormSemestre.query.get_or_404( - Se.prev["formsemestre_id"] - ) - ntp: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre_prev) - 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: - 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] = Se.get_cursus_descr() - # groupe principal (td) - groupestd[etud.id] = "" - for s in Se.etud["sems"]: - if s["formsemestre_id"] == formsemestre_id: - groupestd[etud.id] = etud_groups.get(etud.id, {}).get( - main_partition_id, "" - ) - # absences: - e_nbabs, e_nbabsjust = sco_abs.get_abs_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}") - # 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") - style_moy = sco_excel.excel_make_style( - bold=True, halign="center", bgcolor=sco_excel.COLORS.LIGHT_YELLOW - ) - style_note = sco_excel.excel_make_style( - halign="right", number_format=FORMAT_NUMBER_00 - ) - style_note_bold = sco_excel.excel_make_style( - halign="right", bold=True, number_format="General" - ) - - # Première ligne - sheet.append_single_cell_row( - "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold - ) - sheet.append_blank_row() - - # Ligne de titre - titles = ["Rang"] - if sco_preferences.get_preference("prepa_jury_nip"): - 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.first() - 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(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, - ) - ) - xls = sheet.generate() - flash("Feuille préparation jury générée") - return scu.send_file( - xls, - f"PrepaJury{sn}", - scu.XLSX_SUFFIX, - mime=scu.XLSX_MIMETYPE, + # res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + breakpoint() + """Feuille excel pour préparation des jurys adaptée pour le BUT.""" + compilation = _Compilation(formsemestre) + compilation.computes_decision() + filename = scu.sanitize_filename( + f"""{'jury'}-{formsemestre.titre_num()}-{time.strftime("%Y-%m-%d")}""" ) + return compilation.make_excel(filename)