# -*- 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 de bord module """ import math import time from flask import g, url_for from flask_login import current_user from app import db from app.auth.models import User from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre, ModuleImpl from app.models.evaluations import Evaluation from app.models.ues import UniteEns import app.scodoc.sco_utils as scu from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_exceptions import ScoInvalidIdType from app.scodoc.sco_cursus_dut import formsemestre_has_decisions from app.scodoc.sco_permissions import Permission from app.scodoc import html_sco_header from app.scodoc import htmlutils from app.scodoc import sco_abs from app.scodoc import sco_compute_moy from app.scodoc import sco_edit_module from app.scodoc import sco_evaluations from app.scodoc import sco_evaluation_db 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_moduleimpl from app.scodoc import sco_permissions_check from app.scodoc import sco_users # ported from old DTML code in oct 2009 # menu evaluation dans moduleimpl def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): "Menu avec actions sur une evaluation" E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0] modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] group_id = sco_groups.get_default_group(modimpl["formsemestre_id"]) if ( sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"], allow_ens=False ) and nbnotes != 0 ): sup_label = "Supprimer évaluation impossible (il y a des notes)" else: sup_label = "Supprimer évaluation" menuEval = [ { "title": "Saisir notes", "endpoint": "notes.saisie_notes", "args": { "evaluation_id": evaluation_id, }, "enabled": sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"] ), }, { "title": "Modifier évaluation", "endpoint": "notes.evaluation_edit", "args": { "evaluation_id": evaluation_id, }, "enabled": sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"], allow_ens=False ), }, { "title": sup_label, "endpoint": "notes.evaluation_delete", "args": { "evaluation_id": evaluation_id, }, "enabled": nbnotes == 0 and sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"], allow_ens=False ), }, { "title": "Supprimer toutes les notes", "endpoint": "notes.evaluation_suppress_alln", "args": { "evaluation_id": evaluation_id, }, "enabled": sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"], allow_ens=False ), }, { "title": "Afficher les notes", "endpoint": "notes.evaluation_listenotes", "args": { "evaluation_id": evaluation_id, }, "enabled": nbnotes > 0, }, { "title": "Placement étudiants", "endpoint": "notes.placement_eval_selectetuds", "args": { "evaluation_id": evaluation_id, }, "enabled": sco_permissions_check.can_edit_notes( current_user, E["moduleimpl_id"] ), }, { "title": "Absences ce jour", "endpoint": "absences.EtatAbsencesDate", "args": { "date": E["jour"], "group_ids": group_id, }, "enabled": E["jour"], }, { "title": "Vérifier notes vs absents", "endpoint": "notes.evaluation_check_absences_html", "args": { "evaluation_id": evaluation_id, }, "enabled": nbnotes > 0 and E["jour"], }, ] return htmlutils.make_menu("actions", menuEval, alone=True) def _ue_coefs_html(coefs_lst) -> str: """ """ max_coef = max([x[1] for x in coefs_lst]) if coefs_lst else 1.0 H = """
Coefficients vers les UE
""" if coefs_lst: H += ( f"""
""" + "\n".join( [ f"""
{coef}
{ue.acronyme}
""" for ue, coef in coefs_lst ] ) + "
" ) else: H += """
non définis
""" H += "
" return H def moduleimpl_status(moduleimpl_id=None, partition_id=None): """Tableau de bord module (liste des evaluations etc)""" if not isinstance(moduleimpl_id, int): raise ScoInvalidIdType("moduleimpl_id must be an integer !") modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) M = modimpl.to_dict() formsemestre_id = modimpl.formsemestre_id formsemestre: FormSemestre = modimpl.formsemestre Mod = sco_edit_module.module_list(args={"module_id": modimpl.module_id})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list( moduleimpl_id=M["moduleimpl_id"] ) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) mod_evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) mod_evals.sort( key=lambda x: (x["numero"], x["jour"], x["heure_debut"]), reverse=True ) # la plus RECENTE en tête # sem_locked = not sem["etat"] can_edit_evals = ( sco_permissions_check.can_edit_notes( current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] ) and not sem_locked ) can_edit_notes = ( sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) and not sem_locked ) arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() # module_resp = User.query.get(M["responsable_id"]) mod_type_name = scu.MODULE_TYPE_NAMES[Mod["module_type"]] H = [ html_sco_header.sco_header( page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}" ), f"""

{mod_type_name} {Mod['code']} {Mod['titre']} {"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""}

""") # 2ieme ligne: Semestre, Coef H.append("""""") # 3ieme ligne: Formation H.append( """""" % F ) # Ligne: Inscrits H.append( """") # Ligne: règle de calcul has_expression = sco_compute_moy.moduleimpl_has_expression(M) if has_expression: H.append( '") else: H.append( '") H.append( '
Responsable: {module_resp.get_nomcomplet()} ({module_resp.user_name}) """, ] try: sco_moduleimpl.can_change_module_resp(moduleimpl_id) H.append( """modifier""" % moduleimpl_id ) except: pass H.append("""""") H.append( ", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]) ) H.append("""""") try: sco_moduleimpl.can_change_ens(moduleimpl_id) H.append( """modifier les enseignants""" % moduleimpl_id ) except: pass H.append("""
""") if sem["semestre_id"] >= 0: H.append("""Semestre: %s""" % sem["semestre_id"]) else: H.append("""""") if sem_locked: H.append(scu.icontag("lock32_img", title="verrouillé")) H.append("""""") if modimpl.module.is_apc(): H.append(_ue_coefs_html(modimpl.module.ue_coefs_list())) else: H.append( f"""Coef. dans le semestre: { "non défini" if modimpl.module.coefficient is None else modimpl.module.coefficient }""" ) H.append("""
Formation: %(titre)s
Inscrits: %d étudiants""" % len(mod_inscrits) ) if current_user.has_permission(Permission.ScoEtudInscrit): H.append( """modifier""" % M["moduleimpl_id"] ) H.append("
Règle de calcul: moyenne=%s' % M["computation_expr"] ) H.append("""inutilisée dans cette version de ScoDoc""") if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): H.append( f""" supprimer""" ) H.append("""""") H.append("
' # règle de calcul standard' ) # if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): # H.append( # f' (changer)' # ) H.append("
Absences dans ce module' % moduleimpl_id ) # Adapté à partir d'une suggestion de DS (Le Havre) # Liens saisies absences seulement si permission et date courante dans le semestre if ( current_user.has_permission(Permission.ScoAbsChange) and formsemestre.est_courant() ): datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday() group_id = sco_groups.get_default_group(formsemestre_id) H.append( f""" Saisie Absences hebdo. """ ) H.append("
") # if not modimpl.check_apc_conformity(): H.append( """
Les poids des évaluations de ce module ne sont pas encore conformes au PN. Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE.
""" ) # if has_expression and nt.expr_diagnostics: H.append(sco_formsemestre_status.html_expr_diagnostic(nt.expr_diagnostics)) # if formsemestre_has_decisions(formsemestre_id): H.append( """""" ) # H.append( """

%d évaluations : """ % (len(mod_evals), moduleimpl_id) ) # # Liste les noms de partitions partitions = sco_groups.get_partitions_list(sem["formsemestre_id"]) H.append( """Afficher les groupes de       Voir toutes les notes

""" % M ) # -------- Tableau des evaluations top_table_links = "" if can_edit_evals: top_table_links = f"""Créer nouvelle évaluation """ if mod_evals: top_table_links += f""" Trier par date """ if mod_evals: H.append( '" ) H.append("""""") eval_index = len(mod_evals) - 1 first_eval = True for eval_dict in mod_evals: H.append( _ligne_evaluation( modimpl, eval_dict, first_eval=first_eval, partition_id=partition_id, arrow_down=arrow_down, arrow_none=arrow_none, arrow_up=arrow_up, can_edit_evals=can_edit_evals, can_edit_notes=can_edit_notes, eval_index=eval_index, nb_evals=len(mod_evals), is_apc=nt.is_apc, ) ) eval_index -= 1 first_eval = False # H.append("""
""") if sem_locked: H.append(f"""{scu.icontag("lock32_img")} semestre verrouillé""") elif can_edit_evals: H.append(top_table_links) H.append( f"""

Légende

Rappel : seules les notes des évaluations complètement saisies (affichées en vert) apparaissent dans les bulletins.

""" ) H.append(html_sco_header.sco_footer()) return "".join(H) def _ligne_evaluation( modimpl: ModuleImpl, eval_dict: dict, first_eval: bool = True, partition_id=None, arrow_down=None, arrow_none=None, arrow_up=None, can_edit_evals: bool = False, can_edit_notes: bool = False, eval_index: int = 0, nb_evals: int = 0, is_apc=False, ) -> str: """Ligne décrivant une évaluation dans le tableau de bord moduleimpl.""" H = [] # TODO unifier pour ne plus utiliser eval_dict evaluation: Evaluation = Evaluation.query.get(eval_dict["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat( evaluation.id, partition_id=partition_id, select_first_partition=True, ) if evaluation.evaluation_type in ( scu.EVALUATION_RATTRAPAGE, scu.EVALUATION_SESSION2, ): tr_class = "mievr mievr_rattr" else: tr_class = "mievr" tr_class_1 = "mievr" if not first_eval: H.append(""" """) tr_class_1 += " mievr_spaced" H.append(f"""""") coef = evaluation.coefficient if is_apc: if not evaluation.get_ue_poids_dict(): # Au cas où les poids par défaut n'existent pas encore: if evaluation.set_default_poids(): db.session.commit() coef *= sum(evaluation.get_ue_poids_dict().values()) if modimpl.module.ue.type != UE_SPORT: # Avertissement si coefs x poids nuls if coef < scu.NOTES_PRECISION: H.append("""coef. nul !""") elif is_apc: # visualisation des poids H.append(_evaluation_poids_html(evaluation)) if evaluation.jour: H.append( f"""Le {evaluation.jour.strftime("%d/%m/%Y")} {evaluation.descr_heure()}""" ) else: H.append( f"""Évaluation sans date""" ) H.append(f"    {evaluation.description or ''}") if evaluation.evaluation_type == scu.EVALUATION_RATTRAPAGE: H.append( """rattrapage""" ) elif evaluation.evaluation_type == scu.EVALUATION_SESSION2: H.append( """session 2""" ) # if etat["last_modif"]: H.append( f"""(dernière modif le { etat["last_modif"].strftime("%d/%m/%Y à %Hh%M")})""" ) # H.append( f""" { eval_index:2} """ ) # Fleches: if eval_index != (nb_evals - 1) and can_edit_evals: H.append( f"""{arrow_up}""" ) else: H.append(arrow_none) if (eval_index > 0) and can_edit_evals: H.append( f"""{arrow_down}""" ) else: H.append(arrow_none) H.append( f"""   Durée Coef. Notes Abs N Moyenne """ ) if etat["evalcomplete"]: etat_txt = """(prise en compte)""" etat_descr = "notes utilisées dans les moyennes" elif eval_dict["publish_incomplete"]: etat_txt = """(prise en compte immédiate)""" etat_descr = ( "il manque des notes, mais la prise en compte immédiate a été demandée" ) elif etat["nb_notes"] != 0: etat_txt = "(non prise en compte)" etat_descr = "il manque des notes" else: etat_txt = "" if can_edit_evals and etat_txt: etat_txt = f"""{etat_txt}""" H.append( f"""{etat_txt} """ ) if can_edit_evals: H.append( f"""{scu.icontag("edit_img", alt="modifier", title="Modifier informations")}""" ) if can_edit_notes: H.append( f"""{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}""" ) if etat["nb_notes"] == 0: if can_edit_evals: H.append( f"""""" ) H.append(scu.icontag("delete_img", alt="supprimer", title="Supprimer")) if can_edit_evals: H.append("""""") elif etat["evalcomplete"]: H.append( f"""{scu.icontag("status_green_img", title="ok")}""" ) else: if etat["evalattente"]: H.append( f"""{scu.icontag( "status_greenorange_img", file_format="gif", title="notes en attente", )}""" ) else: H.append( f"""{scu.icontag("status_orange_img", title="il manque des notes")}""" ) # if eval_dict["visibulletin"]: H.append( scu.icontag( "status_visible_img", title="visible dans bulletins intermédiaires" ) ) else: H.append(" ") H.append('') if can_edit_notes: H.append( moduleimpl_evaluation_menu( evaluation.id, nbnotes=etat["nb_notes"], ) ) # H.append( f""" {eval_dict["duree"]} {eval_dict["coefficient"]:g} """ ) H.append( f""" {etat["nb_notes"]} / {etat["nb_inscrits"]} {etat["nb_abs"]} {etat["nb_neutre"]} """ % etat ) if etat["moy"]: H.append( f"""{etat["moy"]} / {eval_dict["note_max"]:g}   (afficher)""" ) else: H.append( f"""saisir notes """ ) H.append("""""") # if etat["nb_notes"] == 0: H.append(f"""""") if modimpl.module.is_apc(): H.append( f"""{ evaluation.get_ue_poids_str()}""" ) else: H.append('') H.append("""""") else: # il y a deja des notes saisies gr_moyennes = etat["gr_moyennes"] first_group = True for gr_moyenne in gr_moyennes: H.append(f""" """) if first_group and modimpl.module.is_apc(): H.append( f"""{ evaluation.get_ue_poids_str()}""" ) else: H.append("""""") first_group = False if gr_moyenne["group_name"] is None: name = "Tous" # tous else: name = f"""Groupe {gr_moyenne["group_name"]}""" H.append(f"""{name}  """) if gr_moyenne["gr_nb_notes"] > 0: H.append( f"""{gr_moyenne["gr_moy"]}  ({gr_moyenne["gr_nb_notes"]} notes""" ) if gr_moyenne["gr_nb_att"] > 0: H.append( f""", { gr_moyenne["gr_nb_att"]} en attente""" ) H.append(""")""") if gr_moyenne["group_id"] in etat["gr_incomplets"]: H.append("""[""") if can_edit_notes: H.append( f"""incomplet]""" ) else: H.append("""incomplet]""") else: H.append("""  """) if can_edit_notes: H.append( f"""""" ) H.append("pas de notes") if can_edit_notes: H.append("""""") H.append("") H.append("""""") return "\n".join(H) def _evaluation_poids_html(evaluation: Evaluation) -> str: """graphe html montrant les poids de l'évaluation""" ue_poids = evaluation.get_ue_poids_dict(sort=True) # { ue_id : poids } if not ue_poids: return max_poids = max(ue_poids.values()) if max_poids < scu.NOTES_PRECISION: return H = ( """
""" + "\n".join( [ f"""
""" for ue, poids in ( (UniteEns.query.get(ue_id), poids) for ue_id, poids in ue_poids.items() ) ] ) + "
" ) return H