# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2022 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 # ############################################################################## """Tableau récapitulatif des notes d'un semestre """ import datetime import time from xml.etree import ElementTree from flask import g, request from flask import make_response, url_for from app import log from app.but import bulletin_but from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre from app.models.etudiants import Identite from app.models.evaluations import Evaluation from app.scodoc.gen_tables import GenTable import app.scodoc.sco_utils as scu from app.scodoc import html_sco_header from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_xml from app.scodoc import sco_bulletins, sco_excel from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours from app.scodoc import sco_evaluations from app.scodoc import sco_evaluation_db from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import sco_formations from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_status from app.scodoc import sco_groups from app.scodoc import sco_permissions_check from app.scodoc import sco_preferences from app.scodoc import sco_etud from app.scodoc import sco_users from app.scodoc import sco_xml from app.scodoc.sco_codes_parcours import DEF, UE_SPORT def formsemestre_recapcomplet( formsemestre_id=None, modejury=False, # affiche lien saisie decision jury tabformat="html", sortcol=None, xml_with_decisions=False, # XML avec decisions rank_partition_id=None, # si None, calcul rang global force_publishing=True, # publie les XML/JSON meme si bulletins non publiés ): """Page récapitulant les notes d'un semestre. Grand tableau récapitulatif avec toutes les notes de modules pour tous les étudiants, les moyennes par UE et générale, trié par moyenne générale décroissante. tabformat: html : page web evals : page web, avec toutes les évaluations dans le tableau xls, xlsx: export excel simple xlsall : export excel simple, avec toutes les évaluations dans le tableau csv : export CSV, avec toutes les évaluations xml, json : concaténation de tous les bulletins, au format demandé pdf : NON SUPPORTE (car tableau trop grand pour générer un pdf utilisable) modejury: cache modules, affiche lien saisie decision jury """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) modejury = int(modejury) xml_with_decisions = int(xml_with_decisions) force_publishing = int(force_publishing) is_file = tabformat in {"csv", "json", "xls", "xlsx", "xlsall", "xml"} H = [] if not is_file: H += [ html_sco_header.sco_header( page_title="Récapitulatif", no_side_bar=True, init_qtip=True, javascripts=["js/etud_info.js", "js/table_recap.js"], ), sco_formsemestre_status.formsemestre_status_head( formsemestre_id=formsemestre_id ), ] if len(formsemestre.inscriptions) > 0: H.append( f"""
") H.append( f"""""" ) if sco_permissions_check.can_validate_sem(formsemestre_id): H.append("") if modejury: H.append( f"""Calcul automatique des décisions du jury
""" ) else: H.append( f"""Saisie des décisions du jury""" ) H.append("") if sco_preferences.get_preference("use_ue_coefs", formsemestre_id): H.append( """utilise les coefficients d'UE pour calculer la moyenne générale.
""" ) H.append(html_sco_header.sco_footer()) # HTML or binary data ? if len(H) > 1: return "".join(H) elif len(H) == 1: return H[0] else: return H def do_formsemestre_recapcomplet( formsemestre_id=None, format="html", # html, xml, xls, xlsall, json hidemodules=False, # ne pas montrer les modules (ignoré en XML) hidebac=False, # pas de colonne Bac (ignoré en XML) xml_nodate=False, # format XML sans dates (sert pour debug cache: comparaison de XML) modejury=False, # saisie décisions jury sortcol=None, # indice colonne a trier dans table T xml_with_decisions=False, disable_etudlink=False, rank_partition_id=None, # si None, calcul rang global force_publishing=True, ): """Calcule et renvoie le tableau récapitulatif.""" formsemestre = FormSemestre.query.get_or_404(formsemestre_id) filename = scu.sanitize_filename( f"""recap-{formsemestre.titre_num()}-{time.strftime("%Y-%m-%d")}""" ) if (format == "html" or format == "evals") and not modejury: res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) data = gen_formsemestre_recapcomplet_html( formsemestre, res, include_evaluations=(format == "evals"), filename=filename, ) return data elif format.startswith("xls") or format == "csv": res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) include_evaluations = format in {"xlsall", "csv "} if format != "csv": format = "xlsx" data, filename = gen_formsemestre_recapcomplet_excel( formsemestre, res, include_evaluations=include_evaluations, format=format, filename=filename, ) return scu.send_file(data, filename=filename, mime=scu.get_mime_suffix(format)) elif format == "xml": data = gen_formsemestre_recapcomplet_xml( formsemestre_id, xml_nodate, xml_with_decisions=xml_with_decisions, force_publishing=force_publishing, ) return scu.send_file(data, filename=filename, suffix=scu.XML_SUFFIX) elif format == "json": data = gen_formsemestre_recapcomplet_json( formsemestre_id, xml_nodate=xml_nodate, xml_with_decisions=xml_with_decisions, force_publishing=force_publishing, ) return scu.sendJSON(data, filename=filename) raise ScoValueError(f"Format demandé invalide: {format}") def gen_formsemestre_recapcomplet_xml( formsemestre_id, xml_nodate, xml_with_decisions=False, force_publishing=True, ) -> str: "XML export: liste tous les bulletins XML." formsemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) T = nt.get_table_moyennes_triees() if not T: return "", "", "xml" if xml_nodate: docdate = "" else: docdate = datetime.datetime.now().isoformat() doc = ElementTree.Element( "recapsemestre", formsemestre_id=str(formsemestre_id), date=docdate ) evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id) doc.append( ElementTree.Element( "evals_info", nb_evals_completes=str(evals["nb_evals_completes"]), nb_evals_en_cours=str(evals["nb_evals_en_cours"]), nb_evals_vides=str(evals["nb_evals_vides"]), date_derniere_note=str(evals["last_modif"]), ) ) for t in T: etudid = t[-1] sco_bulletins_xml.make_xml_formsemestre_bulletinetud( formsemestre_id, etudid, doc=doc, force_publishing=force_publishing, xml_nodate=xml_nodate, xml_with_decisions=xml_with_decisions, ) return ElementTree.tostring(doc).decode(scu.SCO_ENCODING) def gen_formsemestre_recapcomplet_json( formsemestre_id, xml_nodate=False, xml_with_decisions=False, force_publishing=True, ) -> dict: """JSON export: liste tous les bulletins JSON :param xml_nodate(bool): indique la date courante (attribut docdate) :param force_publishing: donne les bulletins même si non "publiés sur portail" :returns: dict """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) is_apc = formsemestre.formation.is_apc() if xml_nodate: docdate = "" else: docdate = datetime.datetime.now().isoformat() evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id) js_data = { "docdate": docdate, "formsemestre_id": formsemestre_id, "evals_info": { "nb_evals_completes": evals["nb_evals_completes"], "nb_evals_en_cours": evals["nb_evals_en_cours"], "nb_evals_vides": evals["nb_evals_vides"], "date_derniere_note": evals["last_modif"], }, "bulletins": [], } bulletins = js_data["bulletins"] formsemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) T = nt.get_table_moyennes_triees() for t in T: etudid = t[-1] if is_apc: etud = Identite.query.get(etudid) r = bulletin_but.BulletinBUT(formsemestre) bul = r.bulletin_etud(etud, formsemestre) else: bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict( formsemestre_id, etudid, force_publishing=force_publishing, xml_with_decisions=xml_with_decisions, ) bulletins.append(bul) return js_data def formsemestres_bulletins(annee_scolaire): """Tous les bulletins des semestres publiés des semestres de l'année indiquée. :param annee_scolaire(int): année de début de l'année scolaire :returns: JSON """ js_list = [] sems = sco_formsemestre.list_formsemestre_by_etape(annee_scolaire=annee_scolaire) log("formsemestres_bulletins(%s): %d sems" % (annee_scolaire, len(sems))) for sem in sems: js_data = gen_formsemestre_recapcomplet_json( sem["formsemestre_id"], force_publishing=False ) js_list.append(js_data) return scu.sendJSON(js_list) def _gen_cell(key: str, row: dict, elt="td"): "html table cell" klass = row.get(f"_{key}_class") attrs = f'class="{klass}"' if klass else "" order = row.get(f"_{key}_order") if order: attrs += f' data-order="{order}"' content = row.get(key, "") target = row.get(f"_{key}_target") target_attrs = row.get(f"_{key}_target_attrs", "") if target or target_attrs: # avec lien href = f'href="{target}"' if target else "" content = f"{content}" return f"<{elt} {attrs}>{content}{elt}>" def _gen_row(keys: list[str], row, elt="td"): klass = row.get("_tr_class") tr_class = f'class="{klass}"' if klass else "" return f'