ScoDocMM/sco_liste_notes.py

895 lines
30 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Liste des notes d'une évaluation
"""
import urllib
from types import StringType
import sco_utils as scu
import notesdb as ndb
from notes_log import log
from TrivialFormulator import TrivialFormulator, TF
import sco_formsemestre
import sco_moduleimpl
import sco_groups
import sco_evaluations
import htmlutils
import sco_excel
from gen_tables import GenTable
from htmlutils import histogram_notes
import VERSION
def do_evaluation_listenotes(context, REQUEST):
"""
Affichage des notes d'une évaluation
args: evaluation_id
"""
mode = None
if REQUEST.form.has_key("evaluation_id"):
evaluation_id = REQUEST.form["evaluation_id"]
mode = "eval"
evals = context.do_evaluation_list({"evaluation_id": evaluation_id})
if REQUEST.form.has_key("moduleimpl_id"):
moduleimpl_id = REQUEST.form["moduleimpl_id"]
mode = "module"
evals = context.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if not mode:
raise ValueError("missing argument: evaluation or module")
if not evals:
return "<p>Aucune évaluation !</p>"
format = REQUEST.form.get("format", "html")
E = evals[0] # il y a au moins une evaluation
# description de l'evaluation
if mode == "eval":
H = [
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
)
]
else:
H = []
# groupes
groups = sco_groups.do_evaluation_listegroupes(
context, E["evaluation_id"], include_default=True
)
grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons
grnams = [g["group_id"] for g in groups] # noms des checkbox
if len(evals) > 1:
descr = [
("moduleimpl_id", {"default": E["moduleimpl_id"], "input_type": "hidden"})
]
else:
descr = [
("evaluation_id", {"default": E["evaluation_id"], "input_type": "hidden"})
]
if len(grnams) > 1:
descr += [
(
"s",
{
"input_type": "separator",
"title": "<b>Choix du ou des groupes d'étudiants:</b>",
},
),
(
"group_ids",
{
"input_type": "checkbox",
"title": "",
"allowed_values": grnams,
"labels": grlabs,
"attributes": ('onclick="document.tf.submit();"',),
},
),
]
else:
if grnams:
def_nam = grnams[0]
else:
def_nam = ""
descr += [
(
"group_ids",
{"input_type": "hidden", "type": "list", "default": [def_nam]},
)
]
descr += [
(
"anonymous_listing",
{
"input_type": "checkbox",
"title": "",
"allowed_values": ("yes",),
"labels": ('listing "anonyme"',),
"attributes": ('onclick="document.tf.submit();"',),
"template": '<tr><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s &nbsp;&nbsp;',
},
),
(
"note_sur_20",
{
"input_type": "checkbox",
"title": "",
"allowed_values": ("yes",),
"labels": ("notes sur 20",),
"attributes": ('onclick="document.tf.submit();"',),
"template": "%(elem)s &nbsp;&nbsp;",
},
),
(
"hide_groups",
{
"input_type": "checkbox",
"title": "",
"allowed_values": ("yes",),
"labels": ("masquer les groupes",),
"attributes": ('onclick="document.tf.submit();"',),
"template": "%(elem)s &nbsp;&nbsp;",
},
),
(
"with_emails",
{
"input_type": "checkbox",
"title": "",
"allowed_values": ("yes",),
"labels": ("montrer les e-mails",),
"attributes": ('onclick="document.tf.submit();"',),
"template": "%(elem)s</td></tr>",
},
),
]
tf = TrivialFormulator(
REQUEST.URL0,
REQUEST.form,
descr,
cancelbutton=None,
submitbutton=None,
bottom_buttons=False,
method="GET",
cssclass="noprint",
name="tf",
is_submitted=True, # toujours "soumis" (démarre avec liste complète)
)
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1]
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
"%s/Notes/moduleimpl_status?moduleimpl_id=%s"
% (context.ScoURL(), E["moduleimpl_id"])
)
else:
anonymous_listing = tf[2]["anonymous_listing"]
note_sur_20 = tf[2]["note_sur_20"]
hide_groups = tf[2]["hide_groups"]
with_emails = tf[2]["with_emails"]
return _make_table_notes(
context,
REQUEST,
tf[1],
evals,
format=format,
note_sur_20=note_sur_20,
anonymous_listing=anonymous_listing,
group_ids=tf[2]["group_ids"],
hide_groups=hide_groups,
with_emails=with_emails,
)
def _make_table_notes(
context,
REQUEST,
html_form,
evals,
format="",
note_sur_20=False,
anonymous_listing=False,
hide_groups=False,
with_emails=False,
group_ids=[],
):
"""Generate table for evaluations marks"""
if not evals:
return "<p>Aucune évaluation !</p>"
E = evals[0]
moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
# (debug) check that all evals are in same module:
for e in evals:
if e["moduleimpl_id"] != moduleimpl_id:
raise ValueError("invalid evaluations list")
if format == "xls":
keep_numeric = True # pas de conversion des notes en strings
else:
keep_numeric = False
# Si pas de groupe, affiche tout
if not group_ids:
group_ids = [sco_groups.get_default_group(context, M["formsemestre_id"])]
groups = sco_groups.listgroups(context, group_ids)
gr_title = sco_groups.listgroups_abbrev(groups)
gr_title_filename = sco_groups.listgroups_filename(groups)
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, E["evaluation_id"], groups, include_dems=True
)
if anonymous_listing:
columns_ids = ["code"] # cols in table
else:
if format == "xls" or format == "xml":
columns_ids = ["nom", "prenom"]
else:
columns_ids = ["nomprenom"]
if not hide_groups:
columns_ids.append("group")
titles = {
"code": "Code",
"group": "Groupe",
"nom": "Nom",
"prenom": "Prénom",
"nomprenom": "Nom",
"expl_key": "Rem.",
"email": "e-mail",
"emailperso": "e-mail perso",
}
rows = []
class keymgr(dict): # comment : key (pour regrouper les comments a la fin)
def __init__(self):
self.lastkey = 1
def nextkey(self):
r = self.lastkey
self.lastkey += 1
# self.lastkey = chr(ord(self.lastkey)+1)
return str(r)
K = keymgr()
for etudid in etudids:
css_row_class = None
# infos identite etudiant
etud = context.getEtudInfo(etudid=etudid, filled=1)[0]
# infos inscription
inscr = context.do_formsemestre_inscription_list(
{"etudid": etudid, "formsemestre_id": M["formsemestre_id"]}
)[0]
if inscr["etat"] == "I": # si inscrit, indique groupe
groups = sco_groups.get_etud_groups(context, etudid, sem)
grc = sco_groups.listgroups_abbrev(groups)
else:
if inscr["etat"] == "D":
grc = "DEM" # attention: ce code est re-ecrit plus bas, ne pas le changer (?)
css_row_class = "etuddem"
else:
grc = inscr["etat"]
code = "" # code pour listings anonyme, à la place du nom
if context.get_preference("anonymous_lst_code") == "INE":
code = etud["code_ine"]
elif context.get_preference("anonymous_lst_code") == "NIP":
code = etud["code_nip"]
if not code: # laisser le code vide n'aurait aucun sens, prenons l'etudid
code = etudid
rows.append(
{
"code": code,
"_code_td_attrs": 'style="padding-left: 1em; padding-right: 2em;"',
"etudid": etudid,
"nom": scu.strupper(etud["nom"]),
"_nomprenom_target": "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
% (M["formsemestre_id"], etudid),
"_nomprenom_td_attrs": 'id="%s" class="etudinfo"' % (etud["etudid"]),
"prenom": scu.strcapitalize(scu.strlower(etud["prenom"])),
"nomprenom": etud["nomprenom"],
"group": grc,
"email": etud["email"],
"emailperso": etud["emailperso"],
"_css_row_class": css_row_class or "",
}
)
# Lignes en tête:
coefs = {
"nom": "",
"prenom": "",
"nomprenom": "",
"group": "",
"code": "",
"_css_row_class": "sorttop fontitalic",
"_table_part": "head",
}
note_max = {
"nom": "",
"prenom": "",
"nomprenom": "",
"group": "",
"code": "",
"_css_row_class": "sorttop fontitalic",
"_table_part": "head",
}
moys = {
"_css_row_class": "moyenne sortbottom",
"_table_part": "foot",
#'_nomprenom_td_attrs' : 'colspan="2" ',
"nomprenom": "Moyenne (sans les absents) :",
"comment": "",
}
# Ajoute les notes de chaque évaluation:
for e in evals:
e["eval_state"] = sco_evaluations.do_evaluation_etat(
context, e["evaluation_id"]
)
notes, nb_abs, nb_att = _add_eval_columns(
context,
e,
rows,
titles,
coefs,
note_max,
moys,
K,
note_sur_20,
keep_numeric,
)
columns_ids.append(e["evaluation_id"])
#
if anonymous_listing:
rows.sort(key=lambda x: x["code"])
else:
rows.sort(key=lambda x: (x["nom"], x["prenom"])) # sort by nom, prenom
# Si module, ajoute moyenne du module:
if len(evals) > 1:
_add_moymod_column(
context,
sem["formsemestre_id"],
e,
rows,
titles,
coefs,
note_max,
moys,
note_sur_20,
keep_numeric,
)
columns_ids.append("moymod")
# Ajoute colonnes emails tout à droite:
if with_emails:
columns_ids += ["email", "emailperso"]
# Ajoute lignes en tête et moyennes
if len(evals) > 0:
rows = [coefs, note_max] + rows
rows.append(moys)
# ajout liens HTMl vers affichage une evaluation:
if format == "html" and len(evals) > 1:
rlinks = {"_table_part": "head"}
for e in evals:
rlinks[e["evaluation_id"]] = "afficher"
rlinks[
"_" + e["evaluation_id"] + "_help"
] = "afficher seulement les notes de cette évaluation"
rlinks["_" + e["evaluation_id"] + "_target"] = (
"evaluation_listenotes?evaluation_id=" + e["evaluation_id"]
)
rlinks["_" + e["evaluation_id"] + "_td_attrs"] = ' class="tdlink" '
rows.append(rlinks)
if len(evals) == 1: # colonne "Rem." seulement si une eval
if format == "html": # pas d'indication d'origine en pdf (pour affichage)
columns_ids.append("expl_key")
elif format == "xls" or format == "xml":
columns_ids.append("comment")
# titres divers:
gl = "".join(["&group_ids%3Alist=" + g for g in group_ids])
if note_sur_20:
gl = "&note_sur_20%3Alist=yes" + gl
if anonymous_listing:
gl = "&anonymous_listing%3Alist=yes" + gl
if hide_groups:
gl = "&hide_groups%3Alist=yes" + gl
if with_emails:
gl = "&with_emails%3Alist=yes" + gl
if len(evals) == 1:
evalname = "%s-%s" % (Mod["code"], ndb.DateDMYtoISO(E["jour"]))
hh = "%s, %s (%d étudiants)" % (E["description"], gr_title, len(etudids))
filename = scu.make_filename("notes_%s_%s" % (evalname, gr_title_filename))
caption = hh
pdf_title = "%(description)s (%(jour)s)" % e
html_title = ""
base_url = "evaluation_listenotes?evaluation_id=%s" % E["evaluation_id"] + gl
html_next_section = (
'<div class="notes_evaluation_stats">%d absents, %d en attente.</div>'
% (nb_abs, nb_att)
)
else:
filename = scu.make_filename("notes_%s_%s" % (Mod["code"], gr_title_filename))
title = "Notes du module %(code)s %(titre)s" % Mod
title += " semestre %(titremois)s" % sem
if gr_title and gr_title != "tous":
title += " %s" % gr_title
caption = title
html_next_section = ""
if format == "pdf":
caption = "" # same as pdf_title
pdf_title = title
html_title = (
"""<h2 class="formsemestre">Notes du module <a href="moduleimpl_status?moduleimpl_id=%s">%s %s</a></h2>"""
% (moduleimpl_id, Mod["code"], Mod["titre"])
)
base_url = "evaluation_listenotes?moduleimpl_id=%s" % moduleimpl_id + gl
# display
tab = GenTable(
titles=titles,
columns_ids=columns_ids,
rows=rows,
html_sortable=True,
base_url=base_url,
filename=filename,
origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
caption=caption,
html_next_section=html_next_section,
page_title="Notes de " + sem["titremois"],
html_title=html_title,
pdf_title=pdf_title,
html_class="table_leftalign notes_evaluation",
preferences=context.get_preferences(M["formsemestre_id"]),
# html_generate_cells=False # la derniere ligne (moyennes) est incomplete
)
t = tab.make_page(context, format=format, with_html_headers=False, REQUEST=REQUEST)
if format != "html":
return t
if len(evals) > 1:
all_complete = True
for e in evals:
if not e["eval_state"]["evalcomplete"]:
all_complete = False
if all_complete:
eval_info = '<span class="eval_info eval_complete">Evaluations prises en compte dans les moyennes</span>'
else:
eval_info = '<span class="eval_info help">Les évaluations en vert et orange sont prises en compte dans les moyennes. Celles en rouge n\'ont pas toutes leurs notes.</span>'
return html_form + eval_info + t + "<p></p>"
else:
# Une seule evaluation: ajoute histogramme
histo = histogram_notes(notes)
# 2 colonnes: histo, comments
C = [
"<table><tr><td><div><h4>Répartition des notes:</h4>"
+ histo
+ "</div></td>\n",
'<td style="padding-left: 50px; vertical-align: top;"><p>',
]
commentkeys = K.items() # [ (comment, key), ... ]
commentkeys.sort(lambda x, y: cmp(int(x[1]), int(y[1])))
for (comment, key) in commentkeys:
C.append(
'<span class="colcomment">(%s)</span> <em>%s</em><br/>' % (key, comment)
)
if commentkeys:
C.append(
'<span><a class=stdlink" href="evaluation_list_operations?evaluation_id=%s">Gérer les opérations</a></span><br/>'
% E["evaluation_id"]
)
eval_info = "xxx"
if E["eval_state"]["evalcomplete"]:
eval_info = '<span class="eval_info eval_complete">Evaluation prise en compte dans les moyennes</span>'
elif E["eval_state"]["evalattente"]:
eval_info = '<span class="eval_info eval_attente">Il y a des notes en attente (les autres sont prises en compte)</span>'
else:
eval_info = '<span class="eval_info eval_incomplete">Notes incomplètes, évaluation non prise en compte dans les moyennes</span>'
return (
sco_evaluations.evaluation_describe(
context, evaluation_id=E["evaluation_id"], REQUEST=REQUEST
)
+ eval_info
+ html_form
+ t
+ "\n".join(C)
)
def _add_eval_columns(
context, e, rows, titles, coefs, note_max, moys, K, note_sur_20, keep_numeric
):
"""Add eval e"""
nb_notes = 0
nb_abs = 0
nb_att = 0
sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
evaluation_id = e["evaluation_id"]
NotesDB = context._notes_getall(evaluation_id)
for row in rows:
etudid = row["etudid"]
if NotesDB.has_key(etudid):
val = NotesDB[etudid]["value"]
if val is None:
nb_abs += 1
if val == scu.NOTES_ATTENTE:
nb_att += 1
# calcul moyenne SANS LES ABSENTS
if val != None and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_ATTENTE:
if e["note_max"] > 0:
valsur20 = val * 20.0 / e["note_max"] # remet sur 20
else:
valsur20 = 0
notes.append(valsur20) # toujours sur 20 pour l'histogramme
if note_sur_20:
val = valsur20 # affichage notes / 20 demandé
nb_notes = nb_notes + 1
sum_notes += val
val_fmt = scu.fmt_note(val, keep_numeric=keep_numeric)
comment = NotesDB[etudid]["comment"]
if comment is None:
comment = ""
explanation = "%s (%s) %s" % (
NotesDB[etudid]["date"].strftime("%d/%m/%y %Hh%M"),
NotesDB[etudid]["uid"],
comment,
)
else:
explanation = ""
val_fmt = ""
val = None
if val is None:
row["_" + evaluation_id + "_td_attrs"] = 'class="etudabs" '
if not row.get("_css_row_class", ""):
row["_css_row_class"] = "etudabs"
# regroupe les commentaires
if explanation:
if K.has_key(explanation):
expl_key = "(%s)" % K[explanation]
else:
K[explanation] = K.nextkey()
expl_key = "(%s)" % K[explanation]
else:
expl_key = ""
row.update(
{
evaluation_id: val_fmt,
"_" + evaluation_id + "_help": explanation,
# si plusieurs evals seront ecrasés et non affichés:
"comment": explanation,
"expl_key": expl_key,
"_expl_key_help": explanation,
}
)
coefs[evaluation_id] = "coef. %s" % e["coefficient"]
if note_sur_20:
nmax = 20.0
else:
nmax = e["note_max"]
if keep_numeric:
note_max[evaluation_id] = nmax
else:
note_max[evaluation_id] = "/ %s" % nmax
if nb_notes > 0:
moys[evaluation_id] = "%.3g" % (sum_notes / nb_notes)
moys["_" + evaluation_id + "_help"] = "moyenne sur %d notes (%s le %s)" % (
nb_notes,
e["description"],
e["jour"],
)
else:
moys[evaluation_id] = ""
titles[evaluation_id] = "%(description)s (%(jour)s)" % e
if e["eval_state"]["evalcomplete"]:
titles["_" + evaluation_id + "_td_attrs"] = 'class="eval_complete"'
elif e["eval_state"]["evalattente"]:
titles["_" + evaluation_id + "_td_attrs"] = 'class="eval_attente"'
else:
titles["_" + evaluation_id + "_td_attrs"] = 'class="eval_incomplete"'
return notes, nb_abs, nb_att # pour histogramme
def _add_moymod_column(
context,
formsemestre_id,
e,
rows,
titles,
coefs,
note_max,
moys,
note_sur_20,
keep_numeric,
):
"""Ajoute la colonne moymod à rows"""
col_id = "moymod"
nt = context._getNotesCache().get_NotesTable(
context, formsemestre_id
) # > get_etud_mod_moy
nb_notes = 0
sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
for row in rows:
etudid = row["etudid"]
val = nt.get_etud_mod_moy(
e["moduleimpl_id"], etudid
) # note sur 20, ou 'NA','NI'
row[col_id] = scu.fmt_note(val, keep_numeric=keep_numeric)
row["_" + col_id + "_td_attrs"] = ' class="moyenne" '
if type(val) != StringType:
notes.append(val)
nb_notes = nb_notes + 1
sum_notes += val
coefs[col_id] = ""
if keep_numeric:
note_max[col_id] = 20.0
else:
note_max[col_id] = "/ 20"
titles[col_id] = "Moyenne module"
if nb_notes > 0:
moys[col_id] = "%.3g" % (sum_notes / nb_notes)
moys["_" + col_id + "_help"] = "moyenne des moyennes"
else:
moys[col_id] = ""
# ---------------------------------------------------------------------------------
# matin et/ou après-midi ?
def _eval_demijournee(E):
"1 si matin, 0 si apres midi, 2 si toute la journee"
am, pm = False, False
if E["heure_debut"] < "13:00":
am = True
if E["heure_fin"] > "13:00":
pm = True
if am and pm:
demijournee = 2
elif am:
demijournee = 1
else:
demijournee = 0
pm = True
return am, pm, demijournee
def evaluation_check_absences(context, evaluation_id):
"""Vérifie les absences au moment de cette évaluation.
Cas incohérents que l'on peut rencontrer pour chaque étudiant:
note et absent
ABS et pas noté absent
ABS et absent justifié
EXC et pas noté absent
EXC et pas justifie
Ramene 3 listes d'etudid
"""
E = context.do_evaluation_list({"evaluation_id": evaluation_id})[0]
if not E["jour"]:
return [], [], [], [], [] # evaluation sans date
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True
)
am, pm, demijournee = _eval_demijournee(E)
# Liste les absences à ce moment:
A = context.Absences.ListeAbsJour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
As = set([x["etudid"] for x in A]) # ensemble des etudiants absents
NJ = context.Absences.ListeAbsNonJustJour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
NJs = set([x["etudid"] for x in NJ]) # ensemble des etudiants absents non justifies
Just = context.Absences.ListeAbsJour(
ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm, is_abs=None, is_just=True
)
Justs = set([x["etudid"] for x in Just]) # ensemble des etudiants avec justif
# Les notes:
NotesDB = context._notes_getall(evaluation_id)
ValButAbs = [] # une note mais noté absent
AbsNonSignalee = [] # note ABS mais pas noté absent
ExcNonSignalee = [] # note EXC mais pas noté absent
ExcNonJust = [] # note EXC mais absent non justifie
AbsButExc = [] # note ABS mais justifié
for etudid in etudids:
if NotesDB.has_key(etudid):
val = NotesDB[etudid]["value"]
if (
val != None and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_ATTENTE
) and etudid in As:
# note valide et absent
ValButAbs.append(etudid)
if val is None and not etudid in As:
# absent mais pas signale comme tel
AbsNonSignalee.append(etudid)
if val == scu.NOTES_NEUTRALISE and not etudid in As:
# Neutralisé mais pas signale absent
ExcNonSignalee.append(etudid)
if val == scu.NOTES_NEUTRALISE and etudid in NJs:
# EXC mais pas justifié
ExcNonJust.append(etudid)
if val is None and etudid in Justs:
# ABS mais justificatif
AbsButExc.append(etudid)
return ValButAbs, AbsNonSignalee, ExcNonSignalee, ExcNonJust, AbsButExc
def evaluation_check_absences_html(
context, evaluation_id, with_header=True, show_ok=True, REQUEST=None
):
"""Affiche etat verification absences d'une evaluation"""
E = context.do_evaluation_list({"evaluation_id": evaluation_id})[0]
am, pm, demijournee = _eval_demijournee(E)
(
ValButAbs,
AbsNonSignalee,
ExcNonSignalee,
ExcNonJust,
AbsButExc,
) = evaluation_check_absences(context, evaluation_id)
if with_header:
H = [
context.html_sem_header(REQUEST, "Vérification absences à l'évaluation"),
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
),
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
]
else:
# pas de header, mais un titre
H = [
"""<h2 class="eval_check_absences">%s du %s """
% (E["description"], E["jour"])
]
if (
not ValButAbs
and not AbsNonSignalee
and not ExcNonSignalee
and not ExcNonJust
):
H.append(': <span class="eval_check_absences_ok">ok</span>')
H.append("</h2>")
def etudlist(etudids, linkabs=False):
H.append("<ul>")
if not etudids and show_ok:
H.append("<li>aucun</li>")
for etudid in etudids:
etud = context.getEtudInfo(etudid=etudid, filled=True)[0]
H.append(
'<li><a class="discretelink" href="ficheEtud?etudid=%(etudid)s">%(nomprenom)s</a>'
% etud
)
if linkabs:
H.append(
'<a class="stdlink" href="Absences/doSignaleAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s&moduleimpl_id=%s">signaler cette absence</a>'
% (
etud["etudid"],
urllib.quote(E["jour"]),
urllib.quote(E["jour"]),
demijournee,
E["moduleimpl_id"],
)
)
H.append("</li>")
H.append("</ul>")
if ValButAbs or show_ok:
H.append(
"<h3>Etudiants ayant une note alors qu'ils sont signalés absents:</h3>"
)
etudlist(ValButAbs)
if AbsNonSignalee or show_ok:
H.append(
"""<h3>Etudiants avec note "ABS" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
)
etudlist(AbsNonSignalee, linkabs=True)
if ExcNonSignalee or show_ok:
H.append(
"""<h3>Etudiants avec note "EXC" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
)
etudlist(ExcNonSignalee)
if ExcNonJust or show_ok:
H.append(
"""<h3>Etudiants avec note "EXC" alors qu'ils sont absents <em>non justifiés</em>:</h3>"""
)
etudlist(ExcNonJust)
if AbsButExc or show_ok:
H.append(
"""<h3>Etudiants avec note "ABS" alors qu'ils ont une <em>justification</em>:</h3>"""
)
etudlist(AbsButExc)
if with_header:
H.append(context.sco_footer(REQUEST))
return "\n".join(H)
def formsemestre_check_absences_html(context, formsemestre_id, REQUEST=None):
"""Affiche etat verification absences pour toutes les evaluations du semestre !"""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
H = [
context.html_sem_header(
REQUEST, "Vérification absences aux évaluations de ce semestre", sem
),
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.
Sont listés tous les modules avec des évaluations.<br/>Aucune action n'est effectuée:
il vous appartient de corriger les erreurs détectées si vous le jugez nécessaire.
</p>""",
]
# Modules, dans l'ordre
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
context, formsemestre_id=formsemestre_id
)
for M in Mlist:
evals = context.do_evaluation_list({"moduleimpl_id": M["moduleimpl_id"]})
if evals:
H.append(
'<div class="module_check_absences"><h2><a href="moduleimpl_status?moduleimpl_id=%s">%s: %s</a></h2>'
% (M["moduleimpl_id"], M["module"]["code"], M["module"]["abbrev"])
)
for E in evals:
H.append(
evaluation_check_absences_html(
context,
E["evaluation_id"],
with_header=False,
show_ok=False,
REQUEST=REQUEST,
)
)
if evals:
H.append("</div>")
H.append(context.sco_footer(REQUEST))
return "\n".join(H)