# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2023 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 # ############################################################################## """Liste des notes d'une évaluation """ from collections import defaultdict import numpy as np import flask from flask import url_for, g, request from app.comp import res_sem from app.comp import moy_mod from app.comp.moy_mod import ModuleImplResults from app.comp.res_but import ResultatsSemestreBUT from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre, Module from app.models.etudiants import Identite from app.models.evaluations import Evaluation from app.models.moduleimpls import ModuleImpl from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.sco_etud import etud_sort_key from app.scodoc import sco_evaluations from app.scodoc import sco_evaluation_db from app.scodoc import sco_groups from app.scodoc import sco_preferences from app.scodoc import sco_users import app.scodoc.sco_utils as scu from app.scodoc.gen_tables import GenTable from app.scodoc.htmlutils import histogram_notes import sco_version def do_evaluation_listenotes( evaluation_id=None, moduleimpl_id=None, fmt="html" ) -> tuple[str | flask.Response, str]: """ Affichage des notes d'une évaluation (si evaluation_id) ou de toutes les évaluations d'un module (si moduleimpl_id) """ mode = None evaluations: list[Evaluation] = [] if moduleimpl_id is not None: mode = "module" modimpl = ModuleImpl.query.get_or_404(moduleimpl_id) evaluations = modimpl.evaluations.all() elif evaluation_id is not None: mode = "eval" evaluations = Evaluation.query.filter_by(id=evaluation_id).all() else: raise ValueError("missing argument: evaluation or module") if not evaluations: return "
Aucune évaluation !
", "ScoDoc" evaluation = evaluations[0] modimpl = evaluation.moduleimpl # il y a au moins une evaluation # description de l'evaluation if evaluation_id is not None: H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)] page_title = f"Notes {evaluation.description or modimpl.module.code}" else: H = [] page_title = f"Notes {modimpl.module.code}" # groupes groups = sco_groups.do_evaluation_listegroupes(evaluation.id, include_default=True) grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons grnams = [str(g["group_id"]) for g in groups] # noms des checkbox if len(evaluations) > 1: descr = [ ( "moduleimpl_id", {"default": modimpl.id, "input_type": "hidden"}, ) ] else: descr = [ ( "evaluation_id", {"default": evaluation.id, "input_type": "hidden"}, ) ] if len(grnams) > 1: descr += [ ( "s", { "input_type": "separator", "title": "Choix du ou des groupes d'étudiants:", }, ), ( "group_ids", { "input_type": "checkbox", "title": "", "allowed_values": grnams, "labels": grlabs, "attributes": ('onclick="document.tf.submit();"',), }, ), ] else: if grnams: def_nam = grnams[0] else: def_nam = "" descr += [ ( "group_ids", {"input_type": "hidden", "type": "list", "default": [def_nam]}, ) ] descr += [ ( "anonymous_listing", { "input_type": "checkbox", "title": "", "allowed_values": ("yes",), "labels": ('listing "anonyme"',), "attributes": ('onclick="document.tf.submit();"',), "template": """Aucune évaluation !
" evaluation = evaluations[0] modimpl = evaluation.moduleimpl module: Module = modimpl.module formsemestre: FormSemestre = modimpl.formsemestre is_apc = module.formation.is_apc() if is_apc: res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre) is_conforme = modimpl.check_apc_conformity(res) evals_poids, ues = moy_mod.load_evaluations_poids(modimpl.id) if not ues: is_apc = False else: evals_poids, ues = None, None is_conforme = True # (debug) check that all evals are in same module: for e in evaluations: if e.moduleimpl_id != modimpl.id: raise ValueError("invalid evaluations list") if fmt == "xls": keep_numeric = True # pas de conversion des notes en strings else: keep_numeric = False # Si pas de groupe, affiche tout if not group_ids: group_ids = [sco_groups.get_default_group(formsemestre.id)] groups = sco_groups.listgroups(group_ids) gr_title = sco_groups.listgroups_abbrev(groups) gr_title_filename = sco_groups.listgroups_filename(groups) if anonymous_listing: columns_ids = ["code"] # cols in table else: if fmt == "xls" or fmt == "xml": columns_ids = ["nom", "prenom"] else: columns_ids = ["nomprenom"] if not hide_groups: columns_ids.append("group") titles = { "code": "Code", "group": "Groupe", "nom": "Nom", "prenom": "Prénom", "nomprenom": "Nom", "expl_key": "Rem.", "email": "e-mail", "emailperso": "e-mail perso", "signatures": "Signatures", } rows = [] class KeyManager(dict): "comment : key (pour regrouper les comments a la fin)" def __init__(self): self.lastkey = 1 def nextkey(self) -> str: "get new key (int)" r = self.lastkey self.lastkey += 1 # self.lastkey = chr(ord(self.lastkey)+1) return str(r) key_mgr = KeyManager() # code pour listings anonyme, à la place du nom if sco_preferences.get_preference("anonymous_lst_code") == "INE": anonymous_lst_key = "code_ine" elif sco_preferences.get_preference("anonymous_lst_code") == "NIP": anonymous_lst_key = "code_nip" else: anonymous_lst_key = "etudid" etudid_etats = sco_groups.do_evaluation_listeetuds_groups( evaluation.id, groups, include_demdef=True ) for etudid, etat in etudid_etats: css_row_class = None # infos identite etudiant etud: Identite = Identite.query.filter_by( id=etudid, dept_id=g.scodoc_dept_id ).first() if etud is None: continue if etat == scu.INSCRIT: # si inscrit, indique groupe groups = sco_groups.get_etud_groups(etudid, formsemestre.id) grc = sco_groups.listgroups_abbrev(groups) else: if etat == scu.DEMISSION: grc = "DEM" # attention: ce code est re-ecrit plus bas, ne pas le changer (?) css_row_class = "etuddem" else: grc = etat code = getattr(etud, anonymous_lst_key) if not code: # laisser le code vide n'aurait aucun sens, prenons l'etudid code = etudid rows.append( { "code": str(code), # INE, NIP ou etudid "_code_td_attrs": 'style="padding-left: 1em; padding-right: 2em;"', "etudid": etudid, "nom": etud.nom.upper(), "_nomprenom_target": url_for( "notes.formsemestre_bulletinetud", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, etudid=etudid, ), "_nomprenom_td_attrs": f"""id="{etudid}" class="etudinfo" data-sort="{ etud.sort_key}" """, "prenom": etud.prenom.lower().capitalize(), "nom_usuel": etud.nom_usuel, "nomprenom": etud.nomprenom, "group": grc, "_group_td_attrs": 'class="group"', "email": etud.get_first_email(), "emailperso": etud.get_first_email("emailperso"), "_css_row_class": css_row_class or "", } ) # Lignes en tête: row_coefs = { "nom": "", "prenom": "", "nomprenom": "", "group": "", "code": "", "_css_row_class": "sorttop fontitalic", "_table_part": "head", } row_poids = { "nom": "", "prenom": "", "nomprenom": "", "group": "", "code": "", "_css_row_class": "sorttop poids", "_table_part": "head", } row_note_max = { "nom": "", "prenom": "", "nomprenom": "", "group": "", "code": "", "_css_row_class": "sorttop fontitalic", "_table_part": "head", } row_moys = { "_css_row_class": "moyenne sortbottom", "_table_part": "foot", #'_nomprenom_td_attrs' : 'colspan="2" ', "nomprenom": "Moyenne :", "comment": "", } # Ajoute les notes de chaque évaluation: evals_state: dict[int, dict] = {} for e in evaluations: evals_state[e.id] = sco_evaluations.do_evaluation_etat(e.id) notes, nb_abs, nb_att = _add_eval_columns( e, evals_state[e.id], evals_poids, ues, rows, titles, row_coefs, row_poids, row_note_max, row_moys, is_apc, key_mgr, note_sur_20, keep_numeric, fmt=fmt, ) columns_ids.append(e.id) # if anonymous_listing: rows.sort(key=lambda x: x["code"] or "") else: # sort by nom, prenom, sans accents rows.sort(key=etud_sort_key) # Si module, ajoute la (les) "moyenne(s) du module: if mode == "module": if len(evaluations) > 1: # Moyenne de l'étudiant dans le module # Affichée même en APC à titre indicatif _add_moymod_column( formsemestre.id, modimpl.id, rows, columns_ids, titles, row_coefs, row_poids, row_note_max, row_moys, is_apc, keep_numeric, ) if is_apc: # Ajoute une colonne par UE _add_apc_columns( modimpl, evals_poids, ues, rows, columns_ids, titles, is_conforme, row_coefs, row_poids, row_note_max, row_moys, keep_numeric, ) # Ajoute colonnes emails tout à droite: if with_emails: columns_ids += ["email", "emailperso"] # Ajoute lignes en tête et moyennes if len(evaluations) > 0 and fmt != "bordereau": rows_head = [row_coefs] if is_apc: rows_head.append(row_poids) rows_head.append(row_note_max) rows = rows_head + rows rows.append(row_moys) # ajout liens HTMl vers affichage une evaluation: if fmt == "html" and len(evaluations) > 1: rlinks = {"_table_part": "head"} for e in evaluations: rlinks[e.id] = "afficher" rlinks[ "_" + str(e.id) + "_help" ] = "afficher seulement les notes de cette évaluation" rlinks["_" + str(e.id) + "_target"] = url_for( "notes.evaluation_listenotes", scodoc_dept=g.scodoc_dept, evaluation_id=e.id, ) rlinks["_" + str(e.id) + "_td_attrs"] = ' class="tdlink" ' rows.append(rlinks) if len(evaluations) == 1: # colonne "Rem." seulement si une eval if fmt == "html": # pas d'indication d'origine en pdf (pour affichage) columns_ids.append("expl_key") elif fmt == "xls" or fmt == "xml": columns_ids.append("comment") elif fmt == "bordereau": columns_ids.append("signatures") # titres divers: gl = "".join(["&group_ids%3Alist=" + str(g) for g in group_ids]) if note_sur_20: gl = "¬e_sur_20%3Alist=yes" + gl if anonymous_listing: gl = "&anonymous_listing%3Alist=yes" + gl if hide_groups: gl = "&hide_groups%3Alist=yes" + gl if with_emails: gl = "&with_emails%3Alist=yes" + gl if len(evaluations) == 1: evalname = f"""{module.code}-{ evaluation.date_debut.replace(tzinfo=None).isoformat() if evaluation.date_debut else ""}""" hh = "%s, %s (%d étudiants)" % ( evaluation.description, gr_title, len(etudid_etats), ) filename = scu.make_filename(f"notes_{evalname}_{gr_title_filename}") if fmt == "bordereau": hh = f""" {len(etudid_etats)} étudiants { nb_abs} absent{'s' if nb_abs > 1 else ''}, {nb_att} en attente.""" # Attention: ReportLab supporte seulement '
Répartition des notes:{histo} |
"""
]
commentkeys = list(key_mgr.items()) # [ (comment, key), ... ]
commentkeys.sort(key=lambda x: int(x[1]))
for comment, key in commentkeys:
C.append(f"""({key}) {comment}
|