Calcul des etuds d'un modimpl avec notes en ATT. Affichage sur tableau bord. Fix tri liste etuds (#595).
This commit is contained in:
parent
58184664df
commit
1309e77bfa
@ -85,6 +85,8 @@ class ModuleImplResults:
|
||||
"{ evaluation.id : bool } indique si à prendre en compte ou non."
|
||||
self.evaluations_etat = {}
|
||||
"{ evaluation_id: EvaluationEtat }"
|
||||
self.etudids_attente = set()
|
||||
"etudids avec au moins une note ATT dans ce module"
|
||||
self.en_attente = False
|
||||
"Vrai si au moins une évaluation a une note en attente"
|
||||
#
|
||||
@ -145,7 +147,6 @@ class ModuleImplResults:
|
||||
evals_notes = pd.DataFrame(index=self.etudids, dtype=float)
|
||||
self.evaluations_completes = []
|
||||
self.evaluations_completes_dict = {}
|
||||
self.en_attente = False
|
||||
for evaluation in moduleimpl.evaluations:
|
||||
eval_df = self._load_evaluation_notes(evaluation)
|
||||
# is_complete ssi tous les inscrits (non dem) au semestre ont une note
|
||||
@ -172,15 +173,20 @@ class ModuleImplResults:
|
||||
eval_df, how="left", left_index=True, right_index=True
|
||||
)
|
||||
# Notes en attente: (ne prend en compte que les inscrits, non démissionnaires)
|
||||
nb_att = sum(
|
||||
evals_notes[str(evaluation.id)][list(inscrits_module)]
|
||||
== scu.NOTES_ATTENTE
|
||||
eval_notes_inscr = evals_notes[str(evaluation.id)][list(inscrits_module)]
|
||||
eval_etudids_attente = set(
|
||||
eval_notes_inscr.iloc[
|
||||
(eval_notes_inscr == scu.NOTES_ATTENTE).to_numpy()
|
||||
].index
|
||||
)
|
||||
self.etudids_attente |= eval_etudids_attente
|
||||
self.evaluations_etat[evaluation.id] = EvaluationEtat(
|
||||
evaluation_id=evaluation.id, nb_attente=nb_att, is_complete=is_complete
|
||||
evaluation_id=evaluation.id,
|
||||
nb_attente=len(eval_etudids_attente),
|
||||
is_complete=is_complete,
|
||||
)
|
||||
if nb_att > 0:
|
||||
self.en_attente = True
|
||||
# au moins une note en ATT dans ce modimpl:
|
||||
self.en_attente = bool(self.etudids_attente)
|
||||
|
||||
# Force columns names to integers (evaluation ids)
|
||||
evals_notes.columns = pd.Index([int(x) for x in evals_notes.columns], dtype=int)
|
||||
|
@ -5,10 +5,12 @@ import pandas as pd
|
||||
import flask_sqlalchemy
|
||||
|
||||
from app import db
|
||||
from app.auth.models import User
|
||||
from app.comp import df_cache
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.modules import Module
|
||||
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
@ -99,6 +101,27 @@ class ModuleImpl(db.Model):
|
||||
d.pop("module", None)
|
||||
return d
|
||||
|
||||
def can_change_ens_by(self, user: User, raise_exc=False) -> bool:
|
||||
"""Check if user can modify module resp.
|
||||
If raise_exc, raises exception (AccessDenied or ScoLockedSemError) if not.
|
||||
= Admin, et dir des etud. (si option l'y autorise)
|
||||
"""
|
||||
if not self.formsemestre.etat:
|
||||
if raise_exc:
|
||||
raise ScoLockedSemError("Modification impossible: semestre verrouille")
|
||||
return False
|
||||
# -- check access
|
||||
# admin ou resp. semestre avec flag resp_can_change_resp
|
||||
if user.has_permission(Permission.ScoImplement):
|
||||
return True
|
||||
if (
|
||||
user.id in [resp.id for resp in self.formsemestre.responsables]
|
||||
) and self.formsemestre.resp_can_change_ens:
|
||||
return True
|
||||
if raise_exc:
|
||||
raise AccessDenied(f"Modification impossible pour {user}")
|
||||
return False
|
||||
|
||||
|
||||
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
||||
notes_modules_enseignants = db.Table(
|
||||
|
@ -122,6 +122,14 @@ class ScoLockedFormError(ScoValueError):
|
||||
super().__init__(msg=msg, dest_url=dest_url)
|
||||
|
||||
|
||||
class ScoLockedSemError(ScoValueError):
|
||||
"Modification d'un formsemestre verrouillé"
|
||||
|
||||
def __init__(self, msg="", dest_url=None):
|
||||
msg = "Ce semestre est verrouillé ! " + str(msg)
|
||||
super().__init__(msg=msg, dest_url=dest_url)
|
||||
|
||||
|
||||
class ScoNonEmptyFormationObject(ScoValueError):
|
||||
"""On ne peut pas supprimer un module/matiere ou UE si des formsemestre s'y réfèrent"""
|
||||
|
||||
|
@ -377,7 +377,7 @@ def can_change_module_resp(moduleimpl_id):
|
||||
if not current_user.has_permission(Permission.ScoImplement) and (
|
||||
(current_user.id not in sem["responsables"]) or (not sem["resp_can_change_ens"])
|
||||
):
|
||||
raise AccessDenied("Modification impossible pour %s" % current_user)
|
||||
raise AccessDenied(f"Modification impossible pour {current_user}")
|
||||
return M, sem
|
||||
|
||||
|
||||
|
@ -38,6 +38,7 @@ from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import FormSemestre, Identite, ScolarFormSemestreValidation, UniteEns
|
||||
|
||||
from app import log
|
||||
from app.tables import list_etuds
|
||||
from app.scodoc.scolog import logdb
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import htmlutils
|
||||
@ -520,14 +521,15 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
||||
H.append(f"""<th title="{ue.titre or ''}">{ue.acronyme}</th>""")
|
||||
H.append("""</tr>""")
|
||||
|
||||
for etudid, ues_etud in table_inscr.items():
|
||||
etud: Identite = Identite.query.get(etudid)
|
||||
etuds = list_etuds.etuds_sorted_from_ids(table_inscr.keys())
|
||||
for etud in etuds:
|
||||
ues_etud = table_inscr[etud.id]
|
||||
H.append(
|
||||
f"""<tr><td><a class="discretelink etudinfo" id={etudid}
|
||||
f"""<tr><td><a class="discretelink etudinfo" id={etud.id}
|
||||
href="{url_for(
|
||||
"scolar.ficheEtud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etudid,
|
||||
etudid=etud.id,
|
||||
)}"
|
||||
>{etud.nomprenom}</a></td>"""
|
||||
)
|
||||
@ -539,7 +541,7 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
||||
else:
|
||||
# Validations d'UE déjà enregistrées dans d'autres semestres
|
||||
validations_ue = (
|
||||
ScolarFormSemestreValidation.query.filter_by(etudid=etudid)
|
||||
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
||||
.filter(
|
||||
ScolarFormSemestreValidation.formsemestre_id
|
||||
!= res.formsemestre.id,
|
||||
@ -556,7 +558,8 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
||||
)
|
||||
validation = validations_ue[-1] if validations_ue else None
|
||||
expl_validation = (
|
||||
f"""Validée ({validation.code}) le {validation.event_date.strftime("%d/%m/%Y")}"""
|
||||
f"""Validée ({validation.code}) le {
|
||||
validation.event_date.strftime("%d/%m/%Y")}"""
|
||||
if validation
|
||||
else ""
|
||||
)
|
||||
@ -567,13 +570,13 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
||||
title="{etud.nomprenom} {'inscrit' if est_inscr else 'non inscrit'} à l'UE {ue.acronyme}. {expl_validation}",
|
||||
onchange="change_ue_inscr(this);"
|
||||
data-url_inscr={
|
||||
url_for("notes.etud_inscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etudid,
|
||||
url_for("notes.etud_inscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id,
|
||||
formsemestre_id=res.formsemestre.id, ue_id=ue.id)
|
||||
}
|
||||
data-url_desinscr={
|
||||
url_for("notes.etud_desinscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etudid,
|
||||
url_for("notes.etud_desinscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id,
|
||||
formsemestre_id=res.formsemestre.id, ue_id=ue.id)
|
||||
}
|
||||
/>
|
||||
|
@ -36,6 +36,7 @@ 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_common import ResultatsSemestre
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import FormSemestre, ModuleImpl
|
||||
from app.models.evaluations import Evaluation
|
||||
@ -59,9 +60,7 @@ 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
|
||||
from app.tables import list_etuds
|
||||
|
||||
# menu evaluation dans moduleimpl
|
||||
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0) -> str:
|
||||
@ -196,23 +195,20 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
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()
|
||||
mi_dict = 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]
|
||||
mod_dict = 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]
|
||||
formation_dict = sco_formations.formation_list(
|
||||
args={"formation_id": sem["formation_id"]}
|
||||
)[0]
|
||||
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=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
|
||||
# Evaluations, la plus RECENTE en tête
|
||||
evaluations = modimpl.evaluations.order_by(
|
||||
Evaluation.numero.desc(),
|
||||
Evaluation.jour.desc(),
|
||||
@ -240,18 +236,23 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
)
|
||||
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"]]
|
||||
module_resp = User.query.get(modimpl.responsable_id)
|
||||
mod_type_name = scu.MODULE_TYPE_NAMES[mod_dict["module_type"]]
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}"
|
||||
page_title=f"{mod_type_name} {mod_dict['code']} {mod_dict['titre']}",
|
||||
javascripts=["js/etud_info.js"],
|
||||
init_qtip=True,
|
||||
),
|
||||
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 ""}
|
||||
<tt>{mod_dict['code']}</tt> {mod_dict['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()}">
|
||||
scu.ModuleType(mod_dict['module_type']).name.lower()}">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="fichetitre2">Responsable: </td><td class="redboldtext">
|
||||
@ -259,18 +260,14 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
<span class="blacktt">({module_resp.user_name})</span>
|
||||
""",
|
||||
]
|
||||
try:
|
||||
sco_moduleimpl.can_change_module_resp(moduleimpl_id)
|
||||
if modimpl.can_change_ens_by(current_user):
|
||||
H.append(
|
||||
"""<a class="stdlink" href="edit_moduleimpl_resp?moduleimpl_id=%s">modifier</a>"""
|
||||
% moduleimpl_id
|
||||
f"""<a class="stdlink" href="{url_for("notes.edit_moduleimpl_resp",
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
|
||||
}" >modifier</a>"""
|
||||
)
|
||||
except:
|
||||
pass
|
||||
H.append("""</td><td>""")
|
||||
H.append(
|
||||
", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]])
|
||||
)
|
||||
H.append(", ".join([u.get_nomprenom() for u in modimpl.enseignants]))
|
||||
H.append("""</td><td>""")
|
||||
try:
|
||||
sco_moduleimpl.can_change_ens(moduleimpl_id)
|
||||
@ -302,7 +299,8 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
H.append("""</td><td></td></tr>""")
|
||||
# 3ieme ligne: Formation
|
||||
H.append(
|
||||
"""<tr><td class="fichetitre2">Formation: </td><td>%(titre)s</td></tr>""" % F
|
||||
"""<tr><td class="fichetitre2">Formation: </td><td>%(titre)s</td></tr>"""
|
||||
% formation_dict
|
||||
)
|
||||
# Ligne: Inscrits
|
||||
H.append(
|
||||
@ -312,15 +310,18 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
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"]
|
||||
% mi_dict["moduleimpl_id"]
|
||||
)
|
||||
H.append("</td></tr>")
|
||||
# Ligne: règle de calcul
|
||||
has_expression = sco_compute_moy.moduleimpl_has_expression(M)
|
||||
has_expression = sco_compute_moy.moduleimpl_has_expression(mi_dict)
|
||||
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"]
|
||||
f"""<tr>
|
||||
<td class="fichetitre2" colspan="4">Règle de calcul:
|
||||
<span class="formula" title="mode de calcul de la moyenne du module"
|
||||
>moyenne=<tt>{mi_dict["computation_expr"]}</tt>
|
||||
</span>"""
|
||||
)
|
||||
H.append("""<span class="warning">inutilisée dans cette version de ScoDoc""")
|
||||
if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False):
|
||||
@ -380,20 +381,24 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
#
|
||||
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>"""
|
||||
"""<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>
|
||||
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="%s"/>"""
|
||||
% (nb_evaluations, moduleimpl_id)
|
||||
<input type="hidden" name="moduleimpl_id" value="{moduleimpl_id}"/>"""
|
||||
)
|
||||
#
|
||||
# Liste les noms de partitions
|
||||
partitions = sco_groups.get_partitions_list(sem["formsemestre_id"])
|
||||
H.append(
|
||||
"""Afficher les groupes
|
||||
"""Afficher les groupes
|
||||
de <select name="partition_id" onchange="document.f.submit();">"""
|
||||
)
|
||||
been_selected = False
|
||||
@ -409,8 +414,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
if name is None:
|
||||
name = "Tous"
|
||||
H.append(
|
||||
"""<option value="%s" %s>%s</option>"""
|
||||
% (partition["partition_id"], selected, name)
|
||||
f"""<option value="{partition['partition_id']}" {selected}>{name}</option>"""
|
||||
)
|
||||
H.append(
|
||||
"""</select>
|
||||
@ -420,20 +424,21 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
</form>
|
||||
</p>
|
||||
"""
|
||||
% M
|
||||
% mi_dict
|
||||
)
|
||||
|
||||
# -------- 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=M['moduleimpl_id'])
|
||||
url_for("notes.evaluation_create", scodoc_dept=g.scodoc_dept, moduleimpl_id=mi_dict['moduleimpl_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=M['moduleimpl_id'],
|
||||
url_for("notes.moduleimpl_evaluation_renumber",
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mi_dict['moduleimpl_id'],
|
||||
redirect=1)
|
||||
}">Trier par date</a>
|
||||
"""
|
||||
@ -477,31 +482,35 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<p>Rappel : seules les notes des évaluations complètement saisies
|
||||
(affichées en vert) apparaissent dans les bulletins.
|
||||
</p>
|
||||
"""
|
||||
@ -844,3 +853,22 @@ def _evaluation_poids_html(evaluation: Evaluation, max_poids: float = 0.0) -> st
|
||||
+ "</div>"
|
||||
)
|
||||
return H
|
||||
|
||||
|
||||
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 ""
|
||||
|
@ -641,4 +641,9 @@ table.dataTable.order-column.stripe.hover tbody tr.even:hover td.sorting_1 {
|
||||
table.dataTable.gt_table {
|
||||
width: auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
/* Tables non centrées */
|
||||
table.dataTable.gt_table.gt_left {
|
||||
margin-left: 16px;
|
||||
}
|
117
app/tables/list_etuds.py
Normal file
117
app/tables/list_etuds.py
Normal file
@ -0,0 +1,117 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""Liste simple d'étudiants
|
||||
"""
|
||||
|
||||
from flask import g, url_for
|
||||
from app.models import Identite
|
||||
from app.tables import table_builder as tb
|
||||
|
||||
|
||||
class TableEtud(tb.Table):
|
||||
"""Table listant des étudiants
|
||||
Peut-être sous-classée pour ajouter des colonnes.
|
||||
L'id de la ligne est etuid, et le row stocke etud.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
etuds: list[Identite] = None,
|
||||
classes: list[str] = None,
|
||||
row_class=None,
|
||||
with_foot_titles=False,
|
||||
**kwargs,
|
||||
):
|
||||
self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows
|
||||
classes = classes or ["gt_table", "gt_left"]
|
||||
super().__init__(
|
||||
row_class=row_class or RowEtud,
|
||||
classes=classes,
|
||||
with_foot_titles=with_foot_titles,
|
||||
**kwargs,
|
||||
)
|
||||
self.add_etuds(etuds)
|
||||
|
||||
def add_etuds(self, etuds: list[Identite]):
|
||||
"Ajoute des étudiants à la table"
|
||||
for etud in etuds:
|
||||
row = self.row_class(self, etud)
|
||||
row.add_etud_cols()
|
||||
self.add_row(row)
|
||||
|
||||
|
||||
class RowEtud(tb.Row):
|
||||
"Ligne de la table d'étudiants"
|
||||
# pour le moment très simple, extensible (codes, liens bulletins, ...)
|
||||
def __init__(self, table: TableEtud, etud: Identite, *args, **kwargs):
|
||||
super().__init__(table, etud.id, *args, **kwargs)
|
||||
self.etud = etud
|
||||
|
||||
def add_etud_cols(self):
|
||||
"""Ajoute colonnes étudiant: codes, noms"""
|
||||
etud = self.etud
|
||||
self.table.group_titles.update(
|
||||
{
|
||||
"etud_codes": "Codes",
|
||||
"identite_detail": "",
|
||||
"identite_court": "",
|
||||
}
|
||||
)
|
||||
# --- Codes (seront cachés, mais exportés en excel)
|
||||
# self.add_cell("etudid", "etudid", etud.id, "etud_codes")
|
||||
# self.add_cell(
|
||||
# "code_nip",
|
||||
# "code_nip",
|
||||
# etud.code_nip or "",
|
||||
# "etud_codes",
|
||||
# )
|
||||
|
||||
# --- Identité étudiant
|
||||
# url_bulletin = url_for(
|
||||
# "notes.formsemestre_bulletinetud",
|
||||
# scodoc_dept=g.scodoc_dept,
|
||||
# formsemestre_id=res.formsemestre.id,
|
||||
# etudid=etud.id,
|
||||
# )
|
||||
url_bulletin = None # pour extension future
|
||||
self.add_cell("civilite_str", "Civ.", etud.civilite_str, "identite_detail")
|
||||
self.add_cell(
|
||||
"nom_disp",
|
||||
"Nom",
|
||||
etud.nom_disp(),
|
||||
"identite_detail",
|
||||
data={"order": etud.sort_key},
|
||||
target=url_bulletin,
|
||||
target_attrs={"class": "etudinfo discretelink", "id": str(etud.id)},
|
||||
)
|
||||
self.add_cell("prenom", "Prénom", etud.prenom, "identite_detail")
|
||||
# self.add_cell(
|
||||
# "nom_short",
|
||||
# "Nom",
|
||||
# etud.nom_short,
|
||||
# "identite_court",
|
||||
# data={
|
||||
# "order": etud.sort_key,
|
||||
# "etudid": etud.id,
|
||||
# "nomprenom": etud.nomprenom,
|
||||
# },
|
||||
# target=url_bulletin,
|
||||
# target_attrs={"class": "etudinfo", "id": str(etud.id)},
|
||||
# )
|
||||
|
||||
|
||||
def etuds_sorted_from_ids(etudids) -> list[Identite]:
|
||||
"Liste triée d'etuds à partir d'une collections d'etudids"
|
||||
etuds = [Identite.query.get_or_404(etudid) for etudid in etudids]
|
||||
return sorted(etuds, key=lambda etud: etud.sort_key)
|
||||
|
||||
|
||||
def html_table_etuds(etudids) -> str:
|
||||
"""Table HTML simple des étudiants indiqués"""
|
||||
etuds = etuds_sorted_from_ids(etudids)
|
||||
table = TableEtud(etuds)
|
||||
return table.html()
|
@ -38,8 +38,6 @@ class TableRecap(tb.Table):
|
||||
moy_sae_<modimpl_id>_<ue_id>, ... les moyennes de SAE dans l'UE
|
||||
|
||||
On ajoute aussi des classes:
|
||||
- pour les lignes:
|
||||
selected_row pour l'étudiant sélectionné
|
||||
- les colonnes:
|
||||
- la moyenne générale a la classe col_moy_gen
|
||||
- les colonnes SAE ont la classe col_sae
|
||||
|
@ -68,6 +68,7 @@ class Table(Element):
|
||||
classes: list[str] = None,
|
||||
attrs: dict[str, str] = None,
|
||||
data: dict = None,
|
||||
with_foot_titles=True,
|
||||
row_class=None,
|
||||
xls_sheet_name="feuille",
|
||||
xls_before_table=[], # liste de cellules a placer avant la table
|
||||
@ -100,8 +101,10 @@ class Table(Element):
|
||||
self.head_title_row: "Row" = Row(
|
||||
self, "title_head", cell_elt="th", classes=["titles"]
|
||||
)
|
||||
self.foot_title_row: "Row" = Row(
|
||||
self, "title_foot", cell_elt="th", classes=["titles"]
|
||||
self.foot_title_row: "Row" = (
|
||||
Row(self, "title_foot", cell_elt="th", classes=["titles"])
|
||||
if with_foot_titles
|
||||
else None
|
||||
)
|
||||
self.empty_cell = Cell.empty()
|
||||
# Excel (xls) spécifique:
|
||||
@ -119,8 +122,10 @@ class Table(Element):
|
||||
"""
|
||||
self.sort_columns()
|
||||
# Titres
|
||||
self.add_head_row(self.head_title_row)
|
||||
self.add_foot_row(self.foot_title_row)
|
||||
if self.head_title_row:
|
||||
self.add_head_row(self.head_title_row)
|
||||
if self.foot_title_row:
|
||||
self.add_foot_row(self.foot_title_row)
|
||||
|
||||
def get_row_by_id(self, row_id) -> "Row":
|
||||
"return the row, or None"
|
||||
@ -261,18 +266,23 @@ class Table(Element):
|
||||
title = title or ""
|
||||
if col_id not in self.titles:
|
||||
self.titles[col_id] = title
|
||||
self.head_title_row.cells[col_id] = self.head_title_row.add_cell(
|
||||
col_id,
|
||||
None,
|
||||
title,
|
||||
classes=classes,
|
||||
group=self.column_group.get(col_id),
|
||||
)
|
||||
self.foot_title_row.cells[col_id] = self.foot_title_row.add_cell(
|
||||
col_id, None, title, classes=classes
|
||||
)
|
||||
|
||||
return self.head_title_row.cells.get(col_id), self.foot_title_row.cells[col_id]
|
||||
if self.head_title_row:
|
||||
self.head_title_row.cells[col_id] = self.head_title_row.add_cell(
|
||||
col_id,
|
||||
None,
|
||||
title,
|
||||
classes=classes,
|
||||
group=self.column_group.get(col_id),
|
||||
)
|
||||
if self.foot_title_row:
|
||||
self.foot_title_row.cells[col_id] = self.foot_title_row.add_cell(
|
||||
col_id, None, title, classes=classes
|
||||
)
|
||||
head_cell = (
|
||||
self.head_title_row.cells.get(col_id) if self.head_title_row else None
|
||||
)
|
||||
foot_cell = self.foot_title_row.cells[col_id] if self.foot_title_row else None
|
||||
return head_cell, foot_cell
|
||||
|
||||
def excel(self, wb: Workbook = None):
|
||||
"""Simple Excel representation of the table."""
|
||||
|
@ -1043,15 +1043,18 @@ def edit_enseignants_form(moduleimpl_id):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def edit_moduleimpl_resp(moduleimpl_id):
|
||||
def edit_moduleimpl_resp(moduleimpl_id: int):
|
||||
"""Changement d'un enseignant responsable de module
|
||||
Accessible par Admin et dir des etud si flag resp_can_change_ens
|
||||
"""
|
||||
M, sem = sco_moduleimpl.can_change_module_resp(moduleimpl_id)
|
||||
modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
||||
modimpl.can_change_ens_by(current_user, raise_exc=True) # access control
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
'Modification du responsable du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
|
||||
% (moduleimpl_id, M["module"]["titre"]),
|
||||
f"""Modification du responsable du <a href="{
|
||||
url_for("notes.moduleimpl_status",
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
|
||||
}">module {modimpl.module.titre or ""}</a>""",
|
||||
javascripts=["libjs/AutoSuggest.js"],
|
||||
cssstyles=["css/autosuggest_inquisitor.css"],
|
||||
bodyOnLoad="init_tf_form('')",
|
||||
@ -1065,9 +1068,9 @@ def edit_moduleimpl_resp(moduleimpl_id):
|
||||
uid2display[u["id"]] = u["nomplogin"]
|
||||
allowed_user_names = list(uid2display.values())
|
||||
|
||||
initvalues = M
|
||||
initvalues = modimpl.to_dict(with_module=False)
|
||||
initvalues["responsable_id"] = uid2display.get(
|
||||
M["responsable_id"], M["responsable_id"]
|
||||
modimpl.responsable_id, modimpl.responsable_id
|
||||
)
|
||||
form = [
|
||||
("moduleimpl_id", {"input_type": "hidden"}),
|
||||
@ -1112,9 +1115,8 @@ def edit_moduleimpl_resp(moduleimpl_id):
|
||||
)
|
||||
else:
|
||||
responsable_id = User.get_user_id_from_nomplogin(tf[2]["responsable_id"])
|
||||
if (
|
||||
not responsable_id
|
||||
): # presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps)
|
||||
if not responsable_id:
|
||||
# presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps)
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.moduleimpl_status",
|
||||
@ -1123,16 +1125,15 @@ def edit_moduleimpl_resp(moduleimpl_id):
|
||||
)
|
||||
)
|
||||
|
||||
sco_moduleimpl.do_moduleimpl_edit(
|
||||
{"moduleimpl_id": moduleimpl_id, "responsable_id": responsable_id},
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
modimpl.responsable_id = responsable_id
|
||||
db.session.add(modimpl)
|
||||
db.session.commit()
|
||||
flash("Responsable modifié")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.moduleimpl_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
head_message="responsable modifié",
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.4.39"
|
||||
SCOVERSION = "9.4.40"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user