# -*- 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 time from flask import g, url_for from flask_login import current_user from app.auth.models import User from app.comp import res_sem from app.comp.res_common import NotesTableCompat from app.models import FormSemestre from app.models import ModuleImpl from app.models.evaluations import Evaluation import app.scodoc.sco_utils as scu from app.scodoc.sco_exceptions import ScoInvalidIdType from app.scodoc.sco_parcours_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_cache 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 = """ <div id="modimpl_coefs"> <div>Coefficients vers les UE</div> """ if coefs_lst: H += ( f""" <div class="coefs_histo" style="--max:{max_coef}"> """ + "\n".join( [ f"""<div style="--coef:{coef}; {'background-color: ' + ue.color + ';' if ue.color else ''} "><div>{coef}</div>{ue.acronyme}</div>""" for ue, coef in coefs_lst ] ) + "</div>" ) else: H += """<div class="missing_value">non définis</div>""" H += "</div>" 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.query.get_or_404(moduleimpl_id) M = modimpl.to_dict() formsemestre_id = M["formsemestre_id"] Mod = sco_edit_module.module_list(args={"module_id": M["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(modimpl.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 # caneditevals = sco_permissions_check.can_edit_notes( current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] ) caneditnotes = sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) 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"""<h2 class="formsemestre">{mod_type_name} <tt>{Mod['code']}</tt> {Mod['titre']} {"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""} </h2> <div class="moduleimpl_tableaubord moduleimpl_type_{ scu.ModuleType(Mod['module_type']).name.lower()}"> <table> <tr> <td class="fichetitre2">Responsable: </td><td class="redboldtext"> {module_resp.get_nomcomplet()} <span class="blacktt">({module_resp.user_name})</span> """, ] try: sco_moduleimpl.can_change_module_resp(moduleimpl_id) H.append( """<a class="stdlink" href="edit_moduleimpl_resp?moduleimpl_id=%s">modifier</a>""" % moduleimpl_id ) except: pass H.append("""</td><td>""") H.append( ", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]) ) H.append("""</td><td>""") try: sco_moduleimpl.can_change_ens(moduleimpl_id) H.append( """<a class="stdlink" href="edit_enseignants_form?moduleimpl_id=%s">modifier les enseignants</a>""" % moduleimpl_id ) except: pass H.append("""</td></tr>""") # 2ieme ligne: Semestre, Coef H.append("""<tr><td class="fichetitre2">""") if sem["semestre_id"] >= 0: H.append("""Semestre: </td><td>%s""" % sem["semestre_id"]) else: H.append("""</td><td>""") if not sem["etat"]: H.append(scu.icontag("lock32_img", title="verrouillé")) H.append("""</td><td class="fichetitre2">""") if modimpl.module.is_apc(): H.append(_ue_coefs_html(modimpl.module.ue_coefs_list())) else: H.append(f"Coef. dans le semestre: {modimpl.module.coefficient}") H.append("""</td><td></td></tr>""") # 3ieme ligne: Formation H.append( """<tr><td class="fichetitre2">Formation: </td><td>%(titre)s</td></tr>""" % F ) # Ligne: Inscrits H.append( """<tr><td class="fichetitre2">Inscrits: </td><td> %d étudiants""" % len(mod_inscrits) ) if current_user.has_permission(Permission.ScoEtudInscrit): H.append( """<a class="stdlink" style="margin-left:2em;" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">modifier</a>""" % M["moduleimpl_id"] ) H.append("</td></tr>") # Ligne: règle de calcul has_expression = sco_compute_moy.moduleimpl_has_expression(M) if has_expression: H.append( '<tr><td class="fichetitre2" colspan="4">Règle de calcul: <span class="formula" title="mode de calcul de la moyenne du module">moyenne=<tt>%s</tt></span>' % M["computation_expr"] ) H.append('<span class="warning">inutilisée dans cette version de ScoDoc</span>') H.append("</td></tr>") else: H.append( '<tr><td colspan="4">' # <em title="mode de calcul de la moyenne du module">règle de calcul standard</em>' ) # if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): # H.append( # f' (<a class="stdlink" href="edit_moduleimpl_expr?moduleimpl_id={moduleimpl_id}">changer</a>)' # ) H.append("</td></tr>") H.append( '<tr><td colspan="4"><span class="moduleimpl_abs_link"><a class="stdlink" href="view_module_abs?moduleimpl_id=%s">Absences dans ce module</a></span>' % 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 sco_formsemestre.sem_est_courant(sem): datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday() group_id = sco_groups.get_default_group(formsemestre_id) H.append( f""" <span class="moduleimpl_abs_link"><a class="stdlink" href="{url_for("absences.SignaleAbsenceGrHebdo", scodoc_dept=g.scodoc_dept,formsemestre_id=formsemestre_id, moduleimpl_id=moduleimpl_id, datelundi=datelundi, group_ids=group_id)}"> Saisie Absences hebdo.</a></span> """ ) H.append("</td></tr></table>") # if not modimpl.check_apc_conformity(): H.append( """<div class="warning conformite">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. </div>""" ) # 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( """<ul class="tf-msg"><li class="tf-msg warning">Décisions de jury saisies: seul le responsable du semestre peut saisir des notes (il devra modifier les décisions de jury).</li></ul>""" ) # H.append( """<p><form name="f"><span style="font-size:120%%; font-weight: bold;">%d évaluations :</span> <span style="padding-left: 30px;"> <input type="hidden" name="moduleimpl_id" value="%s"/>""" % (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 <select name="partition_id" onchange="document.f.submit();">""" ) been_selected = False for partition in partitions: if not partition_id and not been_selected: selected = "selected" been_selected = True if partition["partition_id"] == partition_id: selected = "selected" else: selected = "" name = partition["partition_name"] if name is None: name = "Tous" H.append( """<option value="%s" %s>%s</option>""" % (partition["partition_id"], selected, name) ) H.append( """</select> <a class="stdlink" href="evaluation_listenotes?moduleimpl_id=%(moduleimpl_id)s">Voir toutes les notes</a> </span> </form> </p> """ % M ) # -------- Tableau des evaluations top_table_links = "" if sem["etat"]: # non verrouillé top_table_links = ( """<a class="stdlink" href="evaluation_create?moduleimpl_id=%(moduleimpl_id)s">Créer nouvelle évaluation</a> <a class="stdlink" style="margin-left:2em;" href="module_evaluation_renumber?moduleimpl_id=%(moduleimpl_id)s&redirect=1">Trier par date</a> """ % M ) if mod_evals: H.append( '<div class="moduleimpl_evaluations_top_links">' + top_table_links + "</div>" ) H.append("""<table class="moduleimpl_evaluations">""") eval_index = len(mod_evals) - 1 first_eval = True for eval in mod_evals: evaluation: Evaluation = Evaluation.query.get( eval["evaluation_id"] ) # TODO unifier etat = sco_evaluations.do_evaluation_etat( eval["evaluation_id"], partition_id=partition_id, select_first_partition=True, ) if eval["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><td colspan="8"> </td></tr>""") tr_class_1 += " mievr_spaced" H.append("""<tr class="%s"><td class="mievr_tit" colspan="8">""" % tr_class_1) if eval["jour"]: H.append("""Le %(jour)s%(descrheure)s""" % eval) else: H.append( """<a href="evaluation_edit?evaluation_id=%(evaluation_id)s" class="mievr_evalnodate">Evaluation sans date</a>""" % eval ) H.append(" <em>%(description)s</em>" % eval) if eval["evaluation_type"] == scu.EVALUATION_RATTRAPAGE: H.append( """<span class="mievr_rattr" title="remplace si meilleure note">rattrapage</span>""" ) elif eval["evaluation_type"] == scu.EVALUATION_SESSION2: H.append( """<span class="mievr_rattr" title="remplace autres notes">session 2</span>""" ) if etat["last_modif"]: H.append( """<span class="mievr_lastmodif">(dernière modif le %s)</span>""" % etat["last_modif"].strftime("%d/%m/%Y à %Hh%M") ) H.append('<span class="evalindex_cont">') if has_expression or True: H.append( """<span class="evalindex" title="Indice dans les vecteurs (formules)">%2d</span>""" % eval_index ) # Fleches: H.append('<span class="eval_arrows_chld">') if eval_index != (len(mod_evals) - 1) and caneditevals: H.append( '<a href="module_evaluation_move?evaluation_id=%s&after=0" class="aud">%s</a>' % (eval["evaluation_id"], arrow_up) ) else: H.append(arrow_none) if (eval_index > 0) and caneditevals: H.append( '<a href="module_evaluation_move?evaluation_id=%s&after=1" class="aud">%s</a>' % (eval["evaluation_id"], arrow_down) ) else: H.append(arrow_none) H.append("</span></span>") eval_index -= 1 H.append("""</td></tr>""") H.append( """<tr class="%s"><th class="moduleimpl_evaluations" colspan="2"> </th><th class="moduleimpl_evaluations">Durée</th><th class="moduleimpl_evaluations">Coef.</th><th class="moduleimpl_evaluations">Notes</th><th class="moduleimpl_evaluations">Abs</th><th class="moduleimpl_evaluations">N</th><th class="moduleimpl_evaluations">Moyenne """ % tr_class ) if etat["evalcomplete"]: etat_txt = """(prise en compte)""" etat_descr = "notes utilisées dans les moyennes" elif eval["publish_incomplete"]: etat_txt = """(prise en compte <b>immédiate</b>)""" etat_descr = ( "il manque des notes, mais la prise en compte immédiate a été demandée" ) elif etat["nb_notes"] != 0: etat_txt = "(<b>non</b> prise en compte)" etat_descr = "il manque des notes" else: etat_txt = "" if caneditevals and etat_txt: etat_txt = ( '<a href="evaluation_edit?evaluation_id=%s" title="%s">%s</a>' % (eval["evaluation_id"], etat_descr, etat_txt) ) H.append(etat_txt) H.append("""</th></tr>""") H.append("""<tr class="%s"><td class="mievr">""" % tr_class) if caneditevals: H.append( """<a class="smallbutton" href="evaluation_edit?evaluation_id=%s">%s</a>""" % ( eval["evaluation_id"], scu.icontag( "edit_img", alt="modifier", title="Modifier informations" ), ) ) if caneditnotes: H.append( """<a class="smallbutton" href="saisie_notes?evaluation_id=%s">%s</a>""" % ( eval["evaluation_id"], scu.icontag( "notes_img", alt="saisie notes", title="Saisie des notes" ), ) ) if etat["nb_notes"] == 0: if caneditevals: H.append( """<a class="smallbutton" href="evaluation_delete?evaluation_id=%(evaluation_id)s">""" % eval ) H.append(scu.icontag("delete_img", alt="supprimer", title="Supprimer")) if caneditevals: H.append("""</a>""") elif etat["evalcomplete"]: H.append( """<a class="smallbutton" href="evaluation_listenotes?evaluation_id=%s">%s</a>""" % (eval["evaluation_id"], scu.icontag("status_green_img", title="ok")) ) else: if etat["evalattente"]: H.append( """<a class="smallbutton" href="evaluation_listenotes?evaluation_id=%s">%s</a>""" % ( eval["evaluation_id"], scu.icontag( "status_greenorange_img", file_format="gif", title="notes en attente", ), ) ) else: H.append( """<a class="smallbutton" href="evaluation_listenotes?evaluation_id=%s">%s</a>""" % ( eval["evaluation_id"], scu.icontag("status_orange_img", title="il manque des notes"), ) ) # if eval["visibulletin"]: H.append( scu.icontag( "status_visible_img", title="visible dans bulletins intermédiaires" ) ) else: H.append(" ") H.append('</td><td class="mievr_menu">') if caneditnotes: H.append( moduleimpl_evaluation_menu( eval["evaluation_id"], nbnotes=etat["nb_notes"], ) ) H.append("</td>") # H.append( """ <td class="mievr_dur">%s</td><td class="rightcell mievr_coef">%s</td>""" % (eval["duree"], "%g" % eval["coefficient"]) ) H.append( """<td class="rightcell mievr_nbnotes">%(nb_notes)s / %(nb_inscrits)s</td> <td class="rightcell mievr_coef">%(nb_abs)s</td> <td class="rightcell mievr_coef">%(nb_neutre)s</td> <td class="rightcell">""" % etat ) if etat["moy"]: H.append("<b>%s / %g</b>" % (etat["moy"], eval["note_max"])) H.append( """ (<a href="evaluation_listenotes?evaluation_id=%s">afficher</a>)""" % (eval["evaluation_id"],) ) else: H.append( """<a class="redlink" href="saisie_notes?evaluation_id=%s">saisir notes</a>""" % (eval["evaluation_id"]) ) H.append("""</td></tr>""") # if etat["nb_notes"] == 0: H.append("""<tr class="%s"><td></td>""" % tr_class) if modimpl.module.is_apc(): H.append( f"""<td colspan="7" class="eval_poids">{ evaluation.get_ue_poids_str()}</td>""" ) else: H.append('<td colspan="7"></td>') H.append("""</tr>""") else: # il y a deja des notes saisies gr_moyennes = etat["gr_moyennes"] first_group = True for gr_moyenne in gr_moyennes: H.append("""<tr class="%s">""" % tr_class) H.append("""<td> </td>""") if first_group and modimpl.module.is_apc(): H.append( f"""<td class="eval_poids" colspan="3">{ evaluation.get_ue_poids_str()}</td>""" ) else: H.append("""<td colspan="3"></td>""") first_group = False if gr_moyenne["group_name"] is None: name = "Tous" # tous else: name = "Groupe %s" % gr_moyenne["group_name"] H.append( """<td colspan="3" class="mievr_grtit">%s </td><td>""" % name ) if gr_moyenne["gr_nb_notes"] > 0: H.append("%(gr_moy)s" % gr_moyenne) H.append( """ (<a href="evaluation_listenotes?tf_submitted=1&evaluation_id=%s&group_ids%%3Alist=%s">%s notes</a>""" % ( eval["evaluation_id"], gr_moyenne["group_id"], gr_moyenne["gr_nb_notes"], ) ) if gr_moyenne["gr_nb_att"] > 0: H.append( """, <span class="redboldtext">%s en attente</span>""" % gr_moyenne["gr_nb_att"] ) H.append(""")""") if gr_moyenne["group_id"] in etat["gr_incomplets"]: H.append("""[<font color="red">""") if caneditnotes: H.append( """<a class="redlink" href="saisie_notes?evaluation_id=%s&group_ids:list=%s">incomplet</a></font>]""" % (eval["evaluation_id"], gr_moyenne["group_id"]) ) else: H.append("""incomplet</font>]""") else: H.append("""<span class="redboldtext"> """) if caneditnotes: H.append( """<a class="redlink" href="saisie_notes?evaluation_id=%s&group_ids:list=%s">""" % (eval["evaluation_id"], gr_moyenne["group_id"]) ) H.append("pas de notes") if caneditnotes: H.append("""</a>""") H.append("</span>") H.append("""</td></tr>""") first_eval = False # if caneditevals or not sem["etat"]: H.append("""<tr><td colspan="8">""") if not sem["etat"]: H.append("""%s semestre verrouillé""" % scu.icontag("lock32_img")) else: H.append(top_table_links) H.append( """</td></tr> </table> </div> <!-- LEGENDE --> <hr> <h4>Légende</h4> <ul> <li>%s : modifie description de l'évaluation (date, heure, coefficient, ...)</li> <li>%s : saisie des notes</li> <li>%s : indique qu'il n'y a aucune note entrée (cliquer pour supprimer cette évaluation)</li> <li>%s : indique qu'il manque quelques notes dans cette évaluation</li> <li>%s : toutes les notes sont entrées (cliquer pour les afficher)</li> <li>%s : indique que cette évaluation sera mentionnée dans les bulletins au format "intermédiaire" </ul> <p>Rappel : seules les notes des évaluations complètement saisies (affichées en vert) apparaissent dans les bulletins. </p> """ % ( scu.icontag("edit_img"), scu.icontag("notes_img"), scu.icontag("delete_img"), scu.icontag("status_orange_img"), scu.icontag("status_green_img"), scu.icontag("status_visible_img"), ) ) H.append(html_sco_header.sco_footer()) return "".join(H)