forked from ScoDoc/ScoDoc
989 lines
36 KiB
Python
989 lines
36 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
#
|
|
# Gestion scolarite IUT
|
|
#
|
|
# Copyright (c) 1999 - 2024 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 datetime
|
|
|
|
from flask import g, render_template, url_for
|
|
from flask_login import current_user
|
|
|
|
from app import db, log
|
|
from app.auth.models import User
|
|
from app.comp import res_sem
|
|
from app.comp.res_common import ResultatsSemestre
|
|
from app.comp.res_compat import NotesTableCompat
|
|
from app.models import Evaluation, FormSemestre, Module, ModuleImpl, UniteEns
|
|
import app.scodoc.sco_utils as scu
|
|
from app.scodoc import sco_assiduites as scass
|
|
from app.scodoc.codes_cursus import UE_SPORT
|
|
from app.scodoc.sco_cursus_dut import formsemestre_has_decisions
|
|
from app.scodoc.sco_exceptions import ScoValueError
|
|
from app.scodoc.sco_permissions import Permission
|
|
|
|
from app.scodoc import htmlutils
|
|
from app.scodoc import sco_evaluations
|
|
from app.scodoc import sco_groups
|
|
from app.scodoc import sco_moduleimpl
|
|
from app.tables import list_etuds
|
|
|
|
|
|
# menu evaluation dans moduleimpl
|
|
def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
|
|
"Menu avec actions sur une evaluation"
|
|
modimpl: ModuleImpl = evaluation.moduleimpl
|
|
group_id = sco_groups.get_default_group(modimpl.formsemestre_id)
|
|
evaluation_id = evaluation.id
|
|
can_edit_notes = modimpl.can_edit_notes(current_user, allow_ens=False)
|
|
can_edit_notes_ens = modimpl.can_edit_notes(current_user)
|
|
|
|
if can_edit_notes and nbnotes != 0:
|
|
sup_label = "Suppression évaluation impossible (il y a des notes)"
|
|
else:
|
|
sup_label = "Supprimer évaluation"
|
|
|
|
formsemestre: FormSemestre = FormSemestre.get_formsemestre(modimpl.formsemestre_id)
|
|
disable_abs: str | bool = scass.has_assiduites_disable_pref(formsemestre)
|
|
|
|
menu_eval = [
|
|
{
|
|
"title": "Saisir les notes",
|
|
"endpoint": "notes.form_saisie_notes",
|
|
"args": {
|
|
"evaluation_id": evaluation_id,
|
|
},
|
|
"enabled": can_edit_notes_ens,
|
|
},
|
|
{
|
|
"title": "Saisir par fichier tableur",
|
|
"id": "menu_saisie_tableur",
|
|
"endpoint": "notes.saisie_notes_tableur",
|
|
"args": {
|
|
"evaluation_id": evaluation.id,
|
|
},
|
|
},
|
|
{
|
|
"title": "Modifier évaluation",
|
|
"endpoint": "notes.evaluation_edit",
|
|
"args": {
|
|
"evaluation_id": evaluation_id,
|
|
},
|
|
"enabled": can_edit_notes,
|
|
},
|
|
{
|
|
"title": sup_label,
|
|
"endpoint": "notes.evaluation_delete",
|
|
"args": {
|
|
"evaluation_id": evaluation_id,
|
|
},
|
|
"enabled": nbnotes == 0 and can_edit_notes,
|
|
},
|
|
{
|
|
"title": "Supprimer toutes les notes",
|
|
"endpoint": "notes.evaluation_suppress_alln",
|
|
"args": {
|
|
"evaluation_id": evaluation_id,
|
|
},
|
|
"enabled": can_edit_notes,
|
|
},
|
|
{
|
|
"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": can_edit_notes_ens,
|
|
},
|
|
{
|
|
"title": "Absences ce jour",
|
|
"endpoint": "assiduites.etat_abs_date",
|
|
"args": {
|
|
"group_ids": group_id,
|
|
"evaluation_id": evaluation.id,
|
|
"date_debut": (
|
|
evaluation.date_debut.isoformat() if evaluation.date_debut else ""
|
|
),
|
|
"date_fin": (
|
|
evaluation.date_fin.isoformat() if evaluation.date_fin else ""
|
|
),
|
|
},
|
|
"enabled": evaluation.date_debut is not None
|
|
and evaluation.date_fin is not None
|
|
and not disable_abs,
|
|
},
|
|
{
|
|
"title": "Vérifier notes vs absents",
|
|
"endpoint": "notes.evaluation_check_absences_html",
|
|
"args": {
|
|
"evaluation_id": evaluation_id,
|
|
},
|
|
"enabled": nbnotes > 0
|
|
and evaluation.date_debut is not None
|
|
and not disable_abs,
|
|
},
|
|
]
|
|
|
|
return htmlutils.make_menu("actions", menu_eval, alone=True)
|
|
|
|
|
|
def _ue_coefs_html(modimpl: ModuleImpl) -> str:
|
|
""" """
|
|
coefs_lst = modimpl.module.ue_coefs_list()
|
|
max_coef = max(x[1] for x in coefs_lst) if coefs_lst else 1.0
|
|
H = f"""
|
|
<div id="modimpl_coefs">
|
|
<div>Coefficients vers les UEs
|
|
<span><a class="stdlink" href="{
|
|
url_for(
|
|
"notes.edit_modules_ue_coefs",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formation_id=modimpl.module.formation.id,
|
|
semestre_idx=modimpl.formsemestre.semestre_id,
|
|
)
|
|
}">détail</a>
|
|
</span>
|
|
</div>
|
|
"""
|
|
|
|
if coefs_lst:
|
|
H += _html_hinton_map(
|
|
colors=(uc[0].color for uc in coefs_lst),
|
|
max_val=max_coef,
|
|
size=36,
|
|
title=modimpl.module.get_ue_coefs_descr(),
|
|
values=(uc[1] for uc in coefs_lst),
|
|
)
|
|
# (
|
|
# 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
|
|
# if coef > 0
|
|
# ]
|
|
# )
|
|
# + "</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)"""
|
|
modimpl: ModuleImpl = ModuleImpl.get_modimpl(moduleimpl_id)
|
|
g.current_moduleimpl_id = modimpl.id
|
|
module: Module = modimpl.module
|
|
formsemestre_id = modimpl.formsemestre_id
|
|
formsemestre: FormSemestre = modimpl.formsemestre
|
|
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
|
moduleimpl_id=moduleimpl_id
|
|
)
|
|
|
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
|
# Evaluations, par numéros ou la plus RECENTE en tête
|
|
evaluations = modimpl.evaluations.order_by(
|
|
Evaluation.numero.desc(),
|
|
Evaluation.date_debut.desc(),
|
|
).all()
|
|
nb_evaluations = len(evaluations)
|
|
# Le poids max pour chaque catégorie d'évaluation
|
|
max_poids_by_type: dict[int, float] = {}
|
|
for eval_type in (
|
|
Evaluation.EVALUATION_NORMALE,
|
|
Evaluation.EVALUATION_RATTRAPAGE,
|
|
Evaluation.EVALUATION_SESSION2,
|
|
Evaluation.EVALUATION_BONUS,
|
|
):
|
|
max_poids_by_type[eval_type] = max(
|
|
[
|
|
max([p.poids for p in e.ue_poids] or [0]) * (e.coefficient or 0.0)
|
|
for e in evaluations
|
|
if e.evaluation_type == eval_type
|
|
]
|
|
or [0.0]
|
|
)
|
|
#
|
|
sem_locked = not formsemestre.etat
|
|
can_edit_evals = (
|
|
modimpl.can_edit_notes(current_user, allow_ens=formsemestre.ens_can_edit_eval)
|
|
and not sem_locked
|
|
)
|
|
can_edit_notes = modimpl.can_edit_notes(current_user) and not sem_locked
|
|
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
|
|
#
|
|
module_resp = db.session.get(User, modimpl.responsable_id)
|
|
if module_resp is None:
|
|
# l'utilisateur a été supprimé par erreur
|
|
# ré-affecte le module au 1er resp. du semestre
|
|
if modimpl.formsemestre.responsables:
|
|
log(
|
|
f"""Modimpl resp. {modimpl} suppressed: affecting to {
|
|
modimpl.formsemestre.responsables[0]}"""
|
|
)
|
|
modimpl.responsable_id = modimpl.formsemestre.responsables[0].id
|
|
db.session.add(modimpl)
|
|
db.session.commit()
|
|
else:
|
|
raise ScoValueError(
|
|
"Module et semestre sans responsables: contacter l'assistance"
|
|
)
|
|
|
|
mod_type_name = scu.MODULE_TYPE_NAMES[module.module_type]
|
|
H = [
|
|
f"""<h2 class="formsemestre">{mod_type_name}
|
|
<tt>{module.code}</tt> {module.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(module.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>
|
|
""",
|
|
]
|
|
if modimpl.can_change_responsable(current_user):
|
|
H.append(
|
|
f"""<a class="stdlink" href="{url_for("notes.edit_moduleimpl_resp",
|
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
|
|
}" >modifier</a>"""
|
|
)
|
|
H.append("""</td><td>""")
|
|
H.append(", ".join([u.get_nomprenom() for u in modimpl.enseignants]))
|
|
H.append("""</td><td>""")
|
|
if modimpl.can_change_ens(raise_exc=False):
|
|
H.append(
|
|
f"""<a class="stdlink" href="{
|
|
url_for("notes.edit_enseignants_form",
|
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id
|
|
)
|
|
}">modifier les enseignants</a>"""
|
|
)
|
|
|
|
H.append("""</td></tr>""")
|
|
|
|
# 2ieme ligne: Semestre, Coef
|
|
H.append("""<tr><td class="fichetitre2">""")
|
|
if formsemestre.semestre_id >= 0:
|
|
H.append(f"""Semestre: </td><td>{formsemestre.semestre_id}""")
|
|
else:
|
|
H.append("""</td><td>""")
|
|
if sem_locked:
|
|
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))
|
|
else:
|
|
H.append(
|
|
f"""Coef. dans le semestre: {
|
|
"non défini" if modimpl.module.coefficient is None else modimpl.module.coefficient
|
|
}"""
|
|
)
|
|
H.append("""</td><td></td></tr>""")
|
|
# 3ieme ligne: Formation
|
|
H.append(
|
|
f"""<tr>
|
|
<td class="fichetitre2">Formation: </td><td>{formsemestre.formation.titre}</td>
|
|
</tr>
|
|
"""
|
|
)
|
|
# Ligne: Inscrits
|
|
H.append(
|
|
f"""<tr><td class="fichetitre2">Inscrits: </td><td> {len(mod_inscrits)} étudiants"""
|
|
)
|
|
if modimpl.can_change_inscriptions(raise_exc=False):
|
|
H.append(
|
|
f"""<a class="stdlink" style="margin-left:2em;"
|
|
href="{
|
|
url_for("notes.moduleimpl_inscriptions_edit",
|
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id
|
|
)}">modifier</a>"""
|
|
)
|
|
H.append(
|
|
"""</td></tr>
|
|
<tr><td colspan="4"></td></tr>
|
|
"""
|
|
)
|
|
|
|
disable_abs: str | bool = scass.has_assiduites_disable_pref(formsemestre)
|
|
|
|
if not disable_abs:
|
|
H.append(
|
|
f"""<tr><td colspan="4"><span class="moduleimpl_abs_link"><a class="stdlink"
|
|
href="{
|
|
url_for("notes.view_module_abs", scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
|
|
}">Absences dans ce module</a></span>"""
|
|
)
|
|
# 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.AbsChange)
|
|
and formsemestre.est_courant()
|
|
and not disable_abs
|
|
):
|
|
group_id = sco_groups.get_default_group(formsemestre_id)
|
|
H.append(
|
|
f"""
|
|
<span class="moduleimpl_abs_link"><a class="stdlink" href="{
|
|
url_for("assiduites.signal_assiduites_group", scodoc_dept=g.scodoc_dept)
|
|
}?group_ids={group_id}&day={
|
|
datetime.date.today().isoformat()
|
|
}&formsemestre_id={formsemestre.id}
|
|
&moduleimpl_id={moduleimpl_id}
|
|
"
|
|
>Saisie Absences</a></span>
|
|
"""
|
|
)
|
|
current_week: str = datetime.datetime.now().strftime("%G-W%V")
|
|
H.append(
|
|
f"""
|
|
<span class="moduleimpl_abs_link"><a class="stdlink" href="{
|
|
url_for("assiduites.signal_assiduites_hebdo",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=formsemestre.id,
|
|
group_ids=group_id,
|
|
week=current_week,
|
|
moduleimpl_id=moduleimpl_id
|
|
)
|
|
}
|
|
"
|
|
>Saisie Absences (Hebdo)</a></span>
|
|
"""
|
|
)
|
|
H.append("</td></tr></table>")
|
|
#
|
|
if not modimpl.check_apc_conformity(nt):
|
|
H.append(
|
|
"""<div class="warning conformite">Les poids des évaluations de ce
|
|
module ne permettent pas d'évaluer toutes les UEs (compétences)
|
|
prévues par les coefficients du programme.
|
|
<b>Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE.</b>
|
|
Vérifiez les poids des évaluations.
|
|
</div>"""
|
|
)
|
|
if not modimpl.check_apc_conformity(
|
|
nt, evaluation_type=Evaluation.EVALUATION_SESSION2
|
|
):
|
|
H.append(
|
|
"""<div class="warning conformite">
|
|
Il y a des évaluations de <b>deuxième session</b>
|
|
mais leurs poids ne permettent pas d'évaluer toutes les UEs (compétences)
|
|
prévues par les coefficients du programme.
|
|
La deuxième session ne sera donc <b>pas prise en compte</b>.
|
|
Vérifiez les poids de ces évaluations.
|
|
</div>"""
|
|
)
|
|
if not modimpl.check_apc_conformity(
|
|
nt, evaluation_type=Evaluation.EVALUATION_RATTRAPAGE
|
|
):
|
|
H.append(
|
|
"""<div class="warning conformite">
|
|
Il y a des évaluations de <b>rattrapage</b>
|
|
mais leurs poids n'évaluent pas toutes les UEs (compétences)
|
|
prévues par les coefficients du programme.
|
|
Vérifiez les poids de ces évaluations.
|
|
</div>"""
|
|
)
|
|
|
|
if formsemestre_has_decisions(formsemestre_id):
|
|
H.append(
|
|
"""<div class="formsemestre-warning-box">
|
|
<div class="warning">Décisions de jury saisies: seul le ou la responsable du
|
|
semestre peut saisir des notes (elle devra modifier les décisions de jury).
|
|
</div>
|
|
</div>"""
|
|
)
|
|
#
|
|
H.append(
|
|
f"""<p><form name="f">
|
|
<span style="font-size:120%%; font-weight: bold;">{nb_evaluations} évaluations :</span>
|
|
<span style="padding-left: 30px;">
|
|
<input type="hidden" name="moduleimpl_id" value="{moduleimpl_id}"/>"""
|
|
)
|
|
#
|
|
# Liste les noms de partitions
|
|
partitions = sco_groups.get_partitions_list(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(
|
|
f"""<option value="{partition['partition_id']}" {selected}>{name}</option>"""
|
|
)
|
|
H.append(
|
|
f"""</select>
|
|
|
|
<a class="stdlink" href="{
|
|
url_for("notes.evaluation_listenotes", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
|
}">Voir toutes les notes</a>
|
|
</span>
|
|
</form>
|
|
</p>
|
|
"""
|
|
)
|
|
|
|
# -------- Tableau des evaluations
|
|
top_table_links = ""
|
|
if can_edit_evals:
|
|
top_table_links = f"""<a class="stdlink" href="{
|
|
url_for("notes.evaluation_create", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
|
}">Créer nouvelle évaluation</a>
|
|
"""
|
|
if nb_evaluations > 0:
|
|
top_table_links += f"""
|
|
<a class="stdlink" style="margin-left:2em;" href="{
|
|
url_for("notes.moduleimpl_evaluation_renumber",
|
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
|
}" title="Ordonner les évaluations par date">Trier par date</a>
|
|
"""
|
|
bot_table_links = (
|
|
top_table_links
|
|
+ f"""
|
|
<a class="stdlink" style="margin-left:2em;" href="{
|
|
url_for("notes.moduleimpl_import_notes",
|
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
|
}" title="Charger toutles les notes via tableur">Importer les notes</a>
|
|
"""
|
|
)
|
|
else:
|
|
bot_table_links = top_table_links
|
|
if nb_evaluations > 0:
|
|
H.append(
|
|
'<div class="moduleimpl_evaluations_top_links">'
|
|
+ top_table_links
|
|
+ "</div>"
|
|
)
|
|
H.append("""<table class="moduleimpl_evaluations">""")
|
|
eval_index = nb_evaluations - 1
|
|
first_eval = True
|
|
for evaluation in evaluations:
|
|
H.append(
|
|
_ligne_evaluation(
|
|
modimpl,
|
|
evaluation,
|
|
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=nb_evaluations,
|
|
is_apc=nt.is_apc,
|
|
max_poids=max_poids_by_type.get(evaluation.evaluation_type, 10000.0),
|
|
)
|
|
)
|
|
eval_index -= 1
|
|
first_eval = False
|
|
#
|
|
H.append("""<tr><td colspan="8">""")
|
|
if sem_locked:
|
|
H.append(f"""{scu.icontag("lock32_img")} semestre verrouillé""")
|
|
elif can_edit_evals:
|
|
H.append(
|
|
f"""<div class="moduleimpl_evaluations_table_bot">{bot_table_links}</div>"""
|
|
)
|
|
|
|
H.append(
|
|
f"""</td></tr>
|
|
</table>
|
|
|
|
<div class="list_etuds_attente">
|
|
{_html_modimpl_etuds_attente(nt, modimpl)}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- LEGENDE -->
|
|
<hr>
|
|
<h4>Légende</h4>
|
|
<ul>
|
|
<li>{scu.icontag("edit_img")} : modifie description de l'évaluation
|
|
(date, heure, coefficient, ...)
|
|
</li>
|
|
<li>{scu.icontag("notes_img")} : saisie des notes</li>
|
|
<li>{scu.icontag("delete_img")} : indique qu'il n'y a aucune note
|
|
entrée (cliquer pour supprimer cette évaluation)
|
|
</li>
|
|
<li>{scu.icontag("status_orange_img")} : indique qu'il manque
|
|
quelques notes dans cette évaluation
|
|
</li>
|
|
<li>{scu.icontag("status_green_img")} : toutes les notes sont
|
|
entrées (cliquer pour les afficher)
|
|
</li>
|
|
<li>{scu.icontag("status_visible_img")} : indique que cette évaluation
|
|
sera mentionnée dans les bulletins au format "intermédiaire"
|
|
</li>
|
|
</ul>
|
|
|
|
<p>Rappel : seules les notes des évaluations complètement saisies
|
|
(affichées en vert) apparaissent dans les bulletins.
|
|
</p>
|
|
"""
|
|
)
|
|
return render_template(
|
|
"sco_page.j2",
|
|
content="".join(H),
|
|
title=f"{mod_type_name} {module.code} {module.titre}",
|
|
)
|
|
|
|
|
|
def _ligne_evaluation(
|
|
modimpl: ModuleImpl,
|
|
evaluation: Evaluation,
|
|
first_eval: bool = True,
|
|
partition_id: int = 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: bool = False,
|
|
max_poids: float = 0.0,
|
|
) -> str:
|
|
"""Ligne <tr> décrivant une évaluation dans le tableau de bord moduleimpl."""
|
|
H = []
|
|
# evaluation: Evaluation = db.session.get(Evaluation, eval_dict["evaluation_id"])
|
|
etat = sco_evaluations.do_evaluation_etat(
|
|
evaluation.id,
|
|
partition_id=partition_id,
|
|
select_first_partition=True,
|
|
)
|
|
if evaluation.evaluation_type == Evaluation.EVALUATION_RATTRAPAGE:
|
|
tr_class = "mievr mievr_rattr"
|
|
elif evaluation.evaluation_type == Evaluation.EVALUATION_SESSION2:
|
|
tr_class = "mievr mievr_session2"
|
|
elif evaluation.evaluation_type == Evaluation.EVALUATION_BONUS:
|
|
tr_class = "mievr mievr_bonus"
|
|
else:
|
|
tr_class = "mievr"
|
|
|
|
if not evaluation.visibulletin:
|
|
tr_class += " non_visible_inter"
|
|
tr_class_1 = "mievr"
|
|
if evaluation.is_blocked():
|
|
tr_class += " evaluation_blocked"
|
|
tr_class_1 += " evaluation_blocked"
|
|
|
|
if not first_eval:
|
|
H.append("""<tr><td colspan="8"> </td></tr>""")
|
|
tr_class_1 += " mievr_spaced"
|
|
H.append(
|
|
f"""<tr class="{tr_class_1} mievr_tit"><td class="mievr_tit" colspan="8">"""
|
|
)
|
|
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:
|
|
if modimpl.module.module_type == scu.ModuleType.MALUS:
|
|
H.append("""<span class="eval_warning_coef">malus</span>""")
|
|
else:
|
|
H.append("""<span class="eval_warning_coef">coef. nul !</span>""")
|
|
elif is_apc:
|
|
# visualisation des poids (Hinton map)
|
|
H.append(_evaluation_poids_html(evaluation, max_poids))
|
|
H.append("""<div class="evaluation_titre">""")
|
|
if evaluation.date_debut:
|
|
H.append(evaluation.descr_date())
|
|
else:
|
|
H.append(
|
|
f"""<a href="{url_for("notes.evaluation_edit",
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}" class="mievr_evalnodate">Évaluation sans date</a>"""
|
|
)
|
|
H.append(f" <em>{evaluation.description}</em>")
|
|
if evaluation.evaluation_type == Evaluation.EVALUATION_RATTRAPAGE:
|
|
H.append(
|
|
"""<span class="mievr_rattr" title="remplace si meilleure note">rattrapage</span>"""
|
|
)
|
|
elif evaluation.evaluation_type == Evaluation.EVALUATION_SESSION2:
|
|
H.append(
|
|
"""<span class="mievr_session2" title="remplace autres notes">session 2</span>"""
|
|
)
|
|
elif evaluation.evaluation_type == Evaluation.EVALUATION_BONUS:
|
|
H.append(
|
|
"""<span class="mievr_bonus" title="s'ajoute aux moyennes de ce module">bonus</span>"""
|
|
)
|
|
#
|
|
if etat["last_modif"]:
|
|
H.append(
|
|
f"""<span class="mievr_lastmodif">(dernière modif le {
|
|
etat["last_modif"].strftime(scu.DATEATIME_FMT)})</span>"""
|
|
)
|
|
#
|
|
H.append(
|
|
f"""
|
|
</td>
|
|
<td class="evaluation_order">
|
|
<span class="evalindex" title="Indice dans les vecteurs (formules)">{
|
|
eval_index:2}</span>
|
|
<span class="eval_arrows_chld">
|
|
"""
|
|
)
|
|
# Fleches:
|
|
if eval_index != (nb_evals - 1) and can_edit_evals:
|
|
H.append(
|
|
f"""<a href="{url_for("notes.moduleimpl_evaluation_move",
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id, after=0)
|
|
}" class="aud">{arrow_up}</a>"""
|
|
)
|
|
else:
|
|
H.append(arrow_none)
|
|
if (eval_index > 0) and can_edit_evals:
|
|
H.append(
|
|
f"""<a href="{url_for("notes.moduleimpl_evaluation_move",
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id, after=1)
|
|
}" class="aud">{arrow_down}</a>"""
|
|
)
|
|
else:
|
|
H.append(arrow_none)
|
|
|
|
if evaluation.is_blocked():
|
|
etat_txt = f"""évaluation bloquée {
|
|
"jusqu'au " + evaluation.blocked_until.strftime(scu.DATE_FMT)
|
|
if evaluation.blocked_until < Evaluation.BLOCKED_FOREVER
|
|
else "" }
|
|
"""
|
|
etat_descr = """prise en compte bloquée"""
|
|
elif etat["evalcomplete"]:
|
|
etat_txt = f"""Moyenne (prise en compte{
|
|
""
|
|
if evaluation.visibulletin
|
|
else ", cachée en intermédiaire"})
|
|
"""
|
|
etat_descr = f"""notes utilisées dans les moyennes{
|
|
", évaluation cachée sur les bulletins en version intermédiaire et sur la passerelle"
|
|
}"""
|
|
elif etat["evalattente"] and not evaluation.publish_incomplete:
|
|
etat_txt = "Moyenne (prise en compte, mais <b>notes en attente</b>)"
|
|
etat_descr = "il y a des notes en attente"
|
|
elif evaluation.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 = "Moyenne (<b>non</b> prise en compte)"
|
|
etat_descr = "il manque des notes"
|
|
else:
|
|
etat_txt = ""
|
|
if etat_txt:
|
|
if can_edit_evals:
|
|
etat_txt = f"""<a href="{ url_for("notes.evaluation_edit",
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}" title="{etat_descr}">{etat_txt}</a>"""
|
|
|
|
H.append(
|
|
f"""</span></span></td>
|
|
</tr>
|
|
<tr class="{tr_class} mievr_in">
|
|
<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 moduleimpl_evaluation_moy"
|
|
colspan="2"><span>{etat_txt}</span></th>
|
|
</tr>
|
|
<tr class="{tr_class} mievr_in">
|
|
<td class="mievr">"""
|
|
)
|
|
if can_edit_evals:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.evaluation_edit',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">{scu.icontag("edit_img", alt="modifier", title="Modifier informations")}</a>"""
|
|
)
|
|
if can_edit_notes:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.form_saisie_notes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>"""
|
|
)
|
|
if etat["nb_notes"] == 0:
|
|
if can_edit_evals:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.evaluation_delete',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">"""
|
|
)
|
|
H.append(scu.icontag("delete_img", alt="supprimer", title="Supprimer"))
|
|
if can_edit_evals:
|
|
H.append("""</a>""")
|
|
elif etat["evalcomplete"]:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.evaluation_listenotes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">{scu.icontag("status_green_img", title="ok")}</a>"""
|
|
)
|
|
else:
|
|
if etat["evalattente"]:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.evaluation_listenotes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">{scu.icontag(
|
|
"status_greenorange_img",
|
|
file_format="gif",
|
|
title="notes en attente",
|
|
)}</a>"""
|
|
)
|
|
else:
|
|
H.append(
|
|
f"""<a class="smallbutton" href="{url_for('notes.evaluation_listenotes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">{scu.icontag("status_orange_img", title="il manque des notes")}</a>"""
|
|
)
|
|
#
|
|
if evaluation.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 can_edit_notes:
|
|
H.append(
|
|
moduleimpl_evaluation_menu(
|
|
evaluation,
|
|
nbnotes=etat["nb_notes"],
|
|
)
|
|
)
|
|
#
|
|
H.append(
|
|
f"""</td>
|
|
<td class="mievr_dur">{evaluation.descr_duree()}</td>
|
|
<td class="rightcell mievr_coef">{evaluation.coefficient:g}</td>
|
|
"""
|
|
)
|
|
H.append(
|
|
f"""
|
|
<td class="rightcell mievr_nbnotes">{etat["nb_notes"]} / {etat["nb_inscrits"]}</td>
|
|
<td class="rightcell mievr_coef">{etat["nb_abs"]}</td>
|
|
<td class="rightcell mievr_coef">{etat["nb_neutre"]}</td>
|
|
<td class="rightcell" colspan="2">"""
|
|
% etat
|
|
)
|
|
if etat["nb_notes"]:
|
|
H.append(
|
|
f"""<b>{etat["moy"]} / 20</b>
|
|
(<a class="stdlink" href="{
|
|
url_for('notes.evaluation_listenotes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">afficher</a>)"""
|
|
)
|
|
else:
|
|
H.append(
|
|
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
|
}">saisir notes</a>
|
|
"""
|
|
)
|
|
H.append("""</td></tr>""")
|
|
#
|
|
if etat["nb_notes"] == 0:
|
|
H.append(f"""<tr class="{tr_class}"><td></td>""")
|
|
# if modimpl.module.is_apc():
|
|
# H.append(
|
|
# f"""<td colspan="8" class="eval_poids">{
|
|
# evaluation.get_ue_poids_str()}</td>"""
|
|
# )
|
|
# else:
|
|
# H.append('<td colspan="8"></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(f"""<tr class="{tr_class}"><td> </td>""")
|
|
# if first_group and modimpl.module.is_apc():
|
|
# H.append(
|
|
# f"""<td class="eval_poids" colspan="4">{
|
|
# evaluation.get_ue_poids_str()}</td>"""
|
|
# )
|
|
# else:
|
|
H.append("""<td colspan="4"></td>""")
|
|
# first_group = False
|
|
if gr_moyenne["group_name"] is None:
|
|
name = "Tous" # tous
|
|
else:
|
|
name = f"""Groupe {gr_moyenne["group_name"]}"""
|
|
H.append(
|
|
f"""<td colspan="2" class="mievr_grtit">{name} </td>
|
|
<td colspan="2">"""
|
|
)
|
|
if gr_moyenne["gr_nb_notes"] > 0:
|
|
H.append(
|
|
f"""{gr_moyenne["gr_moy"]} (<a class="stdlink" href="{
|
|
url_for('notes.evaluation_listenotes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
|
|
tf_submitted=1, **{'group_ids:list': gr_moyenne["group_id"]})
|
|
}">{gr_moyenne["gr_nb_notes"]} notes</a>"""
|
|
)
|
|
if gr_moyenne["gr_nb_att"] > 0:
|
|
H.append(
|
|
f""", <span class="redboldtext">{
|
|
gr_moyenne["gr_nb_att"]} en attente</span>"""
|
|
)
|
|
H.append(""")""")
|
|
if gr_moyenne["group_id"] in etat["gr_incomplets"]:
|
|
H.append("""[<font color="red">""")
|
|
if can_edit_notes:
|
|
H.append(
|
|
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
|
|
**{'group_ids:list': gr_moyenne["group_id"]})
|
|
}">incomplet : terminer saisie</a></font>]"""
|
|
)
|
|
else:
|
|
H.append("""incomplet</font>]""")
|
|
else:
|
|
H.append("""<span class="redboldtext"> """)
|
|
if can_edit_notes:
|
|
H.append(
|
|
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
|
|
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
|
|
**{'group_ids': gr_moyenne["group_id"]})
|
|
}">"""
|
|
)
|
|
H.append("pas de notes")
|
|
if can_edit_notes:
|
|
H.append("""</a>""")
|
|
H.append("</span>")
|
|
H.append("""</td></tr>""")
|
|
return "\n".join(H)
|
|
|
|
|
|
def _evaluation_poids_html(evaluation: Evaluation, max_poids: float = 0.0) -> str:
|
|
"""graphe html (Hinton map) montrant les poids x coef de l'évaluation"""
|
|
ue_poids = evaluation.get_ue_poids_dict(sort=True) # { ue_id : poids }
|
|
if not ue_poids:
|
|
return ""
|
|
values = [poids * (evaluation.coefficient) for poids in ue_poids.values()]
|
|
colors = [db.session.get(UniteEns, ue_id).color for ue_id in ue_poids]
|
|
return _html_hinton_map(
|
|
classes=("evaluation_poids",),
|
|
colors=colors,
|
|
max_val=max_poids,
|
|
title=f"Poids de l'évaluation vers les UEs: {evaluation.get_ue_poids_str()}",
|
|
values=values,
|
|
)
|
|
|
|
|
|
def _html_hinton_map(
|
|
classes=(),
|
|
colors=(),
|
|
max_val: float | None = None,
|
|
size=12,
|
|
title: str = "",
|
|
values=(),
|
|
) -> str:
|
|
"""Représente une liste de nombres sous forme de carrés"""
|
|
if max_val is None:
|
|
max_val = max(values)
|
|
if max_val < scu.NOTES_PRECISION:
|
|
return ""
|
|
return (
|
|
f"""<div class="hinton_map {" ".join(classes)}"
|
|
style="--size:{size}px;"
|
|
title="{title}"
|
|
data-tooltip>"""
|
|
+ "\n".join(
|
|
[
|
|
f"""<div>
|
|
<div style="--boxsize:{size*math.sqrt(value/max_val)}px;
|
|
{'background-color: ' + color + ';' if color else ''}
|
|
"></div>
|
|
</div>"""
|
|
for value, color in zip(values, colors)
|
|
]
|
|
)
|
|
+ "</div>"
|
|
)
|
|
|
|
|
|
def _html_modimpl_etuds_attente(res: ResultatsSemestre, modimpl: ModuleImpl) -> str:
|
|
"""Affiche la liste des étudiants ayant au moins une note en attente dans ce modimpl"""
|
|
m_res = res.modimpls_results.get(modimpl.id)
|
|
if m_res:
|
|
if not m_res.etudids_attente:
|
|
return "<div><em>Aucun étudiant n'a de notes en attente.</em></div>"
|
|
elif len(m_res.etudids_attente) < 10:
|
|
return f"""
|
|
<h4>Étudiants avec une note en attente :</h4>
|
|
{list_etuds.html_table_etuds(m_res.etudids_attente)}
|
|
"""
|
|
else:
|
|
return f"""<div class="warning"><em>{
|
|
len(m_res.etudids_attente)
|
|
} étudiants ont des notes en attente.</em></div>"""
|
|
|
|
return ""
|