2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2023-01-02 13:16:27 +01:00
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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
#
##############################################################################
""" Saisie des notes
Formulaire revu en juillet 2016
"""
2021-01-17 22:31:28 +01:00
import time
import psycopg2
2020-09-26 16:19:37 +02:00
2021-08-01 10:16:16 +02:00
import flask
2021-09-18 10:10:02 +02:00
from flask import g , url_for , request
2021-07-31 18:01:10 +02:00
from flask_login import current_user
2022-12-05 21:59:10 +01:00
from app . auth . models import User
2022-02-13 23:53:11 +01:00
from app . comp import res_sem
2022-03-27 22:25:00 +02:00
from app . comp . res_compat import NotesTableCompat
2022-09-02 15:43:55 +02:00
from app . models import Evaluation , FormSemestre
2022-11-22 13:13:16 +01:00
from app . models import ModuleImpl , ScolarNews
2022-10-05 23:48:54 +02:00
from app . models . etudiants import Identite
2021-06-19 23:21:37 +02:00
import app . scodoc . sco_utils as scu
2021-11-12 22:17:46 +01:00
from app . scodoc . sco_utils import ModuleType
2021-06-19 23:21:37 +02:00
import app . scodoc . notesdb as ndb
2021-08-29 19:57:32 +02:00
from app import log
2021-06-19 23:21:37 +02:00
from app . scodoc . sco_exceptions import (
2021-01-17 22:31:28 +01:00
AccessDenied ,
2021-06-19 23:21:37 +02:00
InvalidNoteValue ,
2021-01-17 22:31:28 +01:00
NoteProcessError ,
2021-10-13 15:56:24 +02:00
ScoGenError ,
2022-04-12 17:12:51 +02:00
ScoInvalidParamError ,
2021-01-17 22:31:28 +01:00
ScoValueError ,
)
2021-06-19 23:21:37 +02:00
from app . scodoc . TrivialFormulator import TrivialFormulator , TF
2021-08-11 13:01:37 +02:00
from app . scodoc import html_sco_header , sco_users
2021-06-19 23:21:37 +02:00
from app . scodoc import htmlutils
2021-06-21 10:17:16 +02:00
from app . scodoc import sco_abs
2021-07-19 19:53:01 +02:00
from app . scodoc import sco_cache
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_edit_module
from app . scodoc import sco_evaluations
2021-11-12 22:17:46 +01:00
from app . scodoc import sco_evaluation_db
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_excel
from app . scodoc import sco_formsemestre
from app . scodoc import sco_formsemestre_inscriptions
from app . scodoc import sco_groups
from app . scodoc import sco_groups_view
from app . scodoc import sco_moduleimpl
from app . scodoc import sco_permissions_check
from app . scodoc import sco_undo_notes
from app . scodoc import sco_etud
2021-01-17 22:31:28 +01:00
2020-09-26 16:19:37 +02:00
def convert_note_from_string (
note ,
note_max ,
2021-01-17 22:31:28 +01:00
note_min = scu . NOTES_MIN ,
2022-12-18 03:14:13 +01:00
etudid : int = None ,
absents : list [ int ] = None ,
tosuppress : list [ int ] = None ,
invalids : list [ int ] = None ,
2020-09-26 16:19:37 +02:00
) :
""" converti une valeur (chaine saisie) vers une note numérique (float)
Les listes absents , tosuppress et invalids sont modifiées
"""
invalid = False
note_value = None
note = note . replace ( " , " , " . " )
if note [ : 3 ] == " ABS " :
note_value = None
absents . append ( etudid )
elif note [ : 3 ] == " NEU " or note [ : 3 ] == " EXC " :
2021-01-17 22:31:28 +01:00
note_value = scu . NOTES_NEUTRALISE
2020-09-26 16:19:37 +02:00
elif note [ : 3 ] == " ATT " :
2021-01-17 22:31:28 +01:00
note_value = scu . NOTES_ATTENTE
2020-09-26 16:19:37 +02:00
elif note [ : 3 ] == " SUP " :
2021-01-17 22:31:28 +01:00
note_value = scu . NOTES_SUPPRESS
2020-09-26 16:19:37 +02:00
tosuppress . append ( etudid )
else :
try :
note_value = float ( note )
if ( note_value < note_min ) or ( note_value > note_max ) :
raise ValueError
2022-09-02 15:43:55 +02:00
except ValueError :
2020-09-26 16:19:37 +02:00
invalids . append ( etudid )
invalid = True
return note_value , invalid
def _displayNote ( val ) :
""" Convert note from DB to viewable string.
Utilisé seulement pour I / O vers formulaires ( sans perte de precision )
( Utiliser fmt_note pour les affichages )
"""
if val is None :
val = " ABS "
2021-01-17 22:31:28 +01:00
elif val == scu . NOTES_NEUTRALISE :
2020-09-26 16:19:37 +02:00
val = " EXC " # excuse, note neutralise
2021-01-17 22:31:28 +01:00
elif val == scu . NOTES_ATTENTE :
2020-09-26 16:19:37 +02:00
val = " ATT " # attente, note neutralise
2021-01-17 22:31:28 +01:00
elif val == scu . NOTES_SUPPRESS :
2020-09-26 16:19:37 +02:00
val = " SUPR "
else :
val = " %g " % val
return val
def _check_notes ( notes , evaluation , mod ) :
""" notes is a list of tuples (etudid, value)
mod is the module ( used to ckeck type , for malus )
returns list of valid notes ( etudid , float value )
and 4 lists of etudid : invalids , withoutnotes , absents , tosuppress , existingjury
"""
note_max = evaluation [ " note_max " ]
2021-11-12 22:17:46 +01:00
if mod [ " module_type " ] in (
scu . ModuleType . STANDARD ,
scu . ModuleType . RESSOURCE ,
scu . ModuleType . SAE ,
) :
2021-01-17 22:31:28 +01:00
note_min = scu . NOTES_MIN
2021-11-12 22:17:46 +01:00
elif mod [ " module_type " ] == ModuleType . MALUS :
2020-09-26 16:19:37 +02:00
note_min = - 20.0
else :
raise ValueError ( " Invalid module type " ) # bug
L = [ ] # liste (etudid, note) des notes ok (ou absent)
invalids = [ ] # etudid avec notes invalides
withoutnotes = [ ] # etudid sans notes (champs vides)
absents = [ ] # etudid absents
tosuppress = [ ] # etudids avec ancienne note à supprimer
for ( etudid , note ) in notes :
note = str ( note ) . strip ( ) . upper ( )
2022-01-21 22:03:16 +01:00
try :
etudid = int ( etudid ) #
except ValueError as exc :
2022-09-02 15:43:55 +02:00
raise ScoValueError ( f " Code étudiant ( { etudid } ) invalide " ) from exc
2020-09-26 16:19:37 +02:00
if note [ : 3 ] == " DEM " :
continue # skip !
if note :
value , invalid = convert_note_from_string (
note ,
note_max ,
note_min = note_min ,
etudid = etudid ,
absents = absents ,
tosuppress = tosuppress ,
invalids = invalids ,
)
if not invalid :
L . append ( ( etudid , value ) )
else :
withoutnotes . append ( etudid )
return L , invalids , withoutnotes , absents , tosuppress
2021-09-27 10:20:10 +02:00
def do_evaluation_upload_xls ( ) :
2020-09-26 16:19:37 +02:00
"""
Soumission d ' un fichier XLS (evaluation_id, notefile)
"""
2021-09-18 13:42:19 +02:00
authuser = current_user
2021-09-28 09:14:04 +02:00
vals = scu . get_request_args ( )
2021-09-27 10:20:10 +02:00
evaluation_id = int ( vals [ " evaluation_id " ] )
comment = vals [ " comment " ]
2021-11-12 22:17:46 +01:00
E = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } ) [ 0 ]
2021-10-15 14:00:51 +02:00
M = sco_moduleimpl . moduleimpl_withmodule_list ( moduleimpl_id = E [ " moduleimpl_id " ] ) [ 0 ]
2020-09-26 16:19:37 +02:00
# Check access
# (admin, respformation, and responsable_id)
2021-07-31 18:01:10 +02:00
if not sco_permissions_check . can_edit_notes ( authuser , E [ " moduleimpl_id " ] ) :
2020-09-26 16:19:37 +02:00
raise AccessDenied ( " Modification des notes impossible pour %s " % authuser )
#
2021-09-27 10:20:10 +02:00
diag , lines = sco_excel . excel_file_to_list ( vals [ " notefile " ] )
2020-09-26 16:19:37 +02:00
try :
if not lines :
raise InvalidNoteValue ( )
# -- search eval code
n = len ( lines )
i = 0
while i < n :
if not lines [ i ] :
diag . append ( " Erreur: format invalide (ligne vide ?) " )
raise InvalidNoteValue ( )
f0 = lines [ i ] [ 0 ] . strip ( )
if f0 and f0 [ 0 ] == " ! " :
break
i = i + 1
if i == n :
diag . append ( " Erreur: format invalide ! (pas de ligne evaluation_id) " )
raise InvalidNoteValue ( )
2021-11-10 09:37:45 +01:00
eval_id_str = lines [ i ] [ 0 ] . strip ( ) [ 1 : ]
try :
eval_id = int ( eval_id_str )
except ValueError :
eval_id = None
2020-09-26 16:19:37 +02:00
if eval_id != evaluation_id :
diag . append (
2022-11-22 13:13:16 +01:00
f """ Erreur: fichier invalide: le code d ' évaluation de correspond pas ! ( ' {
eval_id_str } ' != ' { evaluation_id } ' ) " " "
2020-09-26 16:19:37 +02:00
)
raise InvalidNoteValue ( )
# --- get notes -> list (etudid, value)
# ignore toutes les lignes ne commençant pas par !
notes = [ ]
ni = i + 1
try :
for line in lines [ i + 1 : ] :
if line :
cell0 = line [ 0 ] . strip ( )
if cell0 and cell0 [ 0 ] == " ! " :
etudid = cell0 [ 1 : ]
if len ( line ) > 4 :
val = line [ 4 ] . strip ( )
else :
val = " " # ligne courte: cellule vide
if etudid :
notes . append ( ( etudid , val ) )
ni + = 1
except :
diag . append (
2022-11-22 13:13:16 +01:00
f """ Erreur: Ligne invalide ! (erreur ligne { ni } )<br> { lines [ ni ] } """
2020-09-26 16:19:37 +02:00
)
raise InvalidNoteValue ( )
# -- check values
2021-02-03 22:00:41 +01:00
L , invalids , withoutnotes , absents , _ = _check_notes ( notes , E , M [ " module " ] )
2020-09-26 16:19:37 +02:00
if len ( invalids ) :
diag . append (
2022-11-22 13:13:16 +01:00
f " Erreur: la feuille contient { len ( invalids ) } notes invalides</p> "
2020-09-26 16:19:37 +02:00
)
if len ( invalids ) < 25 :
etudsnames = [
2021-06-19 23:21:37 +02:00
sco_etud . get_etud_info ( etudid = etudid , filled = True ) [ 0 ] [ " nomprenom " ]
2020-09-26 16:19:37 +02:00
for etudid in invalids
]
diag . append ( " Notes invalides pour: " + " , " . join ( etudsnames ) )
raise InvalidNoteValue ( )
else :
2021-11-22 00:31:53 +01:00
nb_changed , nb_suppress , existing_decisions = notes_add (
2021-08-21 00:24:51 +02:00
authuser , evaluation_id , L , comment
2020-09-26 16:19:37 +02:00
)
# news
2021-11-12 22:17:46 +01:00
E = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } ) [
0
]
2021-10-15 14:00:51 +02:00
M = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = E [ " moduleimpl_id " ] ) [ 0 ]
2021-10-16 19:20:36 +02:00
mod = sco_edit_module . module_list ( args = { " module_id " : M [ " module_id " ] } ) [ 0 ]
2020-09-26 16:19:37 +02:00
mod [ " moduleimpl_id " ] = M [ " moduleimpl_id " ]
2021-08-21 12:23:00 +02:00
mod [ " url " ] = url_for (
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
moduleimpl_id = mod [ " moduleimpl_id " ] ,
2022-03-16 19:08:17 +01:00
_external = True ,
2021-08-21 12:23:00 +02:00
)
2022-04-12 17:12:51 +02:00
ScolarNews . add (
typ = ScolarNews . NEWS_NOTE ,
obj = M [ " moduleimpl_id " ] ,
2020-09-26 16:19:37 +02:00
text = ' Chargement notes dans <a href= " %(url)s " > %(titre)s </a> ' % mod ,
url = mod [ " url " ] ,
2022-04-13 00:32:31 +02:00
max_frequency = 30 * 60 , # 30 minutes
2020-09-26 16:19:37 +02:00
)
msg = (
" <p> %d notes changées ( %d sans notes, %d absents, %d note supprimées)</p> "
% ( nb_changed , len ( withoutnotes ) , len ( absents ) , nb_suppress )
)
if existing_decisions :
msg + = """ <p class= " warning " >Important: il y avait déjà des décisions de jury enregistrées, qui sont potentiellement à revoir suite à cette modification !</p> """
# msg += '<p>' + str(notes) # debug
return 1 , msg
except InvalidNoteValue :
if diag :
msg = (
' <ul class= " tf-msg " ><li class= " tf_msg " > '
+ ' </li><li class= " tf_msg " > ' . join ( diag )
+ " </li></ul> "
)
else :
msg = ' <ul class= " tf-msg " ><li class= " tf_msg " >Une erreur est survenue</li></ul> '
return 0 , msg + " <p>(pas de notes modifiées)</p> "
2022-10-05 23:48:54 +02:00
def do_evaluation_set_etud_note ( evaluation : Evaluation , etud : Identite , value ) - > bool :
""" Enregistre la note d ' un seul étudiant
value : valeur externe ( float ou str )
"""
if not sco_permissions_check . can_edit_notes ( current_user , evaluation . moduleimpl . id ) :
raise AccessDenied ( f " Modification des notes impossible pour { current_user } " )
# Convert and check value
L , invalids , _ , _ , _ = _check_notes (
[ ( etud . id , value ) ] , evaluation . to_dict ( ) , evaluation . moduleimpl . module . to_dict ( )
)
if len ( invalids ) == 0 :
nb_changed , _ , _ = notes_add (
current_user , evaluation . id , L , " Initialisation notes "
)
if nb_changed == 1 :
return True
return False # error
2022-12-25 03:27:28 +01:00
def do_evaluation_set_missing (
evaluation_id , value , dialog_confirmed = False , group_ids_str : str = " "
) :
2021-01-01 18:40:47 +01:00
""" Initialisation des notes manquantes """
2022-09-02 15:43:55 +02:00
evaluation = Evaluation . query . get_or_404 ( evaluation_id )
modimpl = evaluation . moduleimpl
2020-09-26 16:19:37 +02:00
# Check access
# (admin, respformation, and responsable_id)
2022-09-02 15:43:55 +02:00
if not sco_permissions_check . can_edit_notes ( current_user , modimpl . id ) :
raise AccessDenied ( f " Modification des notes impossible pour { current_user } " )
2020-09-26 16:19:37 +02:00
#
2022-01-17 00:18:08 +01:00
notes_db = sco_evaluation_db . do_evaluation_get_all_notes ( evaluation_id )
2022-12-25 03:27:28 +01:00
if not group_ids_str :
groups = None
else :
group_ids = [ int ( x ) for x in str ( group_ids_str ) . split ( " , " ) ]
groups = sco_groups . listgroups ( group_ids )
2021-11-20 17:21:51 +01:00
etudid_etats = sco_groups . do_evaluation_listeetuds_groups (
2022-12-25 03:27:28 +01:00
evaluation_id ,
getallstudents = groups is None ,
groups = groups ,
include_demdef = False ,
2020-09-26 16:19:37 +02:00
)
2022-12-25 03:27:28 +01:00
2020-09-26 16:19:37 +02:00
notes = [ ]
2021-11-20 17:21:51 +01:00
for etudid , _ in etudid_etats : # pour tous les inscrits
2022-01-17 00:18:08 +01:00
if etudid not in notes_db : # pas de note
2020-09-26 16:19:37 +02:00
notes . append ( ( etudid , value ) )
2022-10-05 23:48:54 +02:00
# Convert and check values
2022-09-02 15:43:55 +02:00
L , invalids , _ , _ , _ = _check_notes (
notes , evaluation . to_dict ( ) , modimpl . module . to_dict ( )
)
dest_url = url_for (
" notes.saisie_notes " , scodoc_dept = g . scodoc_dept , evaluation_id = evaluation_id
)
2020-09-26 16:19:37 +02:00
diag = " "
2022-09-02 15:43:55 +02:00
if len ( invalids ) > 0 :
diag = f " Valeur { value } invalide ou hors barème "
2020-09-26 16:19:37 +02:00
if diag :
2022-09-02 15:43:55 +02:00
return f """
{ html_sco_header . sco_header ( ) }
< h2 > { diag } < / h2 >
< p > < a href = " { dest_url } " >
Recommencer < / a >
< / p >
{ html_sco_header . sco_footer ( ) }
"""
2020-09-26 16:19:37 +02:00
# Confirm action
if not dialog_confirmed :
2022-12-25 03:27:28 +01:00
plural = len ( L ) > 1
2021-06-02 22:40:34 +02:00
return scu . confirm_dialog (
2022-09-02 15:43:55 +02:00
f """ <h2>Mettre toutes les notes manquantes de l ' évaluation
à la valeur { value } ? < / h2 >
2020-09-26 16:19:37 +02:00
< p > Seuls les étudiants pour lesquels aucune note ( ni valeur , ni ABS , ni EXC )
n ' a été rentrée seront affectés.</p>
2022-12-25 03:27:28 +01:00
< p > < b > { len ( L ) } étudiant { " s " if plural else " " } concerné { " s " if plural else " " }
par ce changement de note . < / b >
< / p >
2022-09-02 15:43:55 +02:00
""" ,
2020-09-26 16:19:37 +02:00
dest_url = " " ,
2022-09-02 15:43:55 +02:00
cancel_url = dest_url ,
2022-12-25 03:27:28 +01:00
parameters = {
" evaluation_id " : evaluation_id ,
" value " : value ,
" group_ids_str " : group_ids_str ,
} ,
2020-09-26 16:19:37 +02:00
)
# ok
comment = " Initialisation notes manquantes "
2021-11-22 00:31:53 +01:00
nb_changed , _ , _ = notes_add ( current_user , evaluation_id , L , comment )
2020-09-26 16:19:37 +02:00
# news
2022-09-03 16:17:56 +02:00
url = url_for (
2021-08-21 12:23:00 +02:00
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
2022-09-03 16:17:56 +02:00
moduleimpl_id = evaluation . moduleimpl_id ,
2021-08-21 12:23:00 +02:00
)
2022-04-12 17:12:51 +02:00
ScolarNews . add (
typ = ScolarNews . NEWS_NOTE ,
2022-09-03 16:17:56 +02:00
obj = evaluation . moduleimpl_id ,
text = f """ Initialisation notes dans <a href= " { url } " > { modimpl . module . titre or " " } </a> """ ,
url = url ,
2022-04-13 00:32:31 +02:00
max_frequency = 30 * 60 ,
2020-09-26 16:19:37 +02:00
)
2022-09-02 15:43:55 +02:00
return f """
{ html_sco_header . sco_header ( ) }
2021-08-21 12:23:00 +02:00
< h2 > { nb_changed } notes changées < / h2 >
< ul >
2022-09-02 15:43:55 +02:00
< li > < a class = " stdlink " href = " {dest_url} " >
2021-08-21 12:23:00 +02:00
Revenir au formulaire de saisie des notes < / a >
< / li >
< li > < a class = " stdlink " href = " {
url_for (
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
2022-09-02 15:43:55 +02:00
moduleimpl_id = evaluation . moduleimpl_id ,
2021-08-21 12:23:00 +02:00
) } " >Tableau de bord du module</a>
< / li >
< / ul >
2022-09-02 15:43:55 +02:00
{ html_sco_header . sco_footer ( ) }
2021-08-21 12:23:00 +02:00
"""
2020-09-26 16:19:37 +02:00
2021-07-31 18:01:10 +02:00
def evaluation_suppress_alln ( evaluation_id , dialog_confirmed = False ) :
2020-09-26 16:19:37 +02:00
" suppress all notes in this eval "
2021-11-12 22:17:46 +01:00
E = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } ) [ 0 ]
2020-09-26 16:19:37 +02:00
2021-06-19 23:21:37 +02:00
if sco_permissions_check . can_edit_notes (
2021-07-31 18:01:10 +02:00
current_user , E [ " moduleimpl_id " ] , allow_ens = False
2021-06-19 23:21:37 +02:00
) :
2020-09-26 16:19:37 +02:00
# On a le droit de modifier toutes les notes
# recupere les etuds ayant une note
2022-01-17 00:18:08 +01:00
notes_db = sco_evaluation_db . do_evaluation_get_all_notes ( evaluation_id )
2021-06-19 23:21:37 +02:00
elif sco_permissions_check . can_edit_notes (
2021-07-31 18:01:10 +02:00
current_user , E [ " moduleimpl_id " ] , allow_ens = True
2021-06-19 23:21:37 +02:00
) :
2020-09-26 16:19:37 +02:00
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
2022-01-17 00:18:08 +01:00
notes_db = sco_evaluation_db . do_evaluation_get_all_notes (
2021-10-20 19:11:26 +02:00
evaluation_id , by_uid = current_user . id
2021-06-19 23:21:37 +02:00
)
2020-09-26 16:19:37 +02:00
else :
2022-11-22 13:13:16 +01:00
raise AccessDenied ( f " Modification des notes impossible pour { current_user } " )
2020-09-26 16:19:37 +02:00
2022-01-17 00:18:08 +01:00
notes = [ ( etudid , scu . NOTES_SUPPRESS ) for etudid in notes_db . keys ( ) ]
2020-09-26 16:19:37 +02:00
2022-11-22 13:13:16 +01:00
status_url = url_for (
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
moduleimpl_id = E [ " moduleimpl_id " ] ,
)
2020-09-26 16:19:37 +02:00
if not dialog_confirmed :
2021-11-22 00:31:53 +01:00
nb_changed , nb_suppress , existing_decisions = notes_add (
2021-12-17 22:53:34 +01:00
current_user , evaluation_id , notes , do_it = False , check_inscription = False
2020-09-26 16:19:37 +02:00
)
2022-11-22 13:13:16 +01:00
msg = f """ <p>Confirmer la suppression des { nb_suppress } notes ?
< em > ( peut affecter plusieurs groupes ) < / em >
< / p >
"""
2020-09-26 16:19:37 +02:00
if existing_decisions :
2022-11-22 13:13:16 +01:00
msg + = """ <p class= " warning " >Important: il y a déjà des décisions de
jury enregistrées , qui seront potentiellement à revoir suite à
cette modification ! < / p > """
2021-06-02 22:40:34 +02:00
return scu . confirm_dialog (
2020-09-26 16:19:37 +02:00
msg ,
dest_url = " " ,
OK = " Supprimer les notes " ,
2022-11-22 13:13:16 +01:00
cancel_url = status_url ,
2020-09-26 16:19:37 +02:00
parameters = { " evaluation_id " : evaluation_id } ,
)
# modif
2021-11-22 00:31:53 +01:00
nb_changed , nb_suppress , existing_decisions = notes_add (
2021-12-17 22:53:34 +01:00
current_user ,
evaluation_id ,
notes ,
comment = " effacer tout " ,
check_inscription = False ,
2020-09-26 16:19:37 +02:00
)
assert nb_changed == nb_suppress
2022-11-22 13:13:16 +01:00
H = [ f """ <p> { nb_suppress } notes supprimées</p> """ ]
2020-09-26 16:19:37 +02:00
if existing_decisions :
H . append (
2022-11-22 13:13:16 +01:00
""" <p class= " warning " >Important: il y avait déjà des décisions
de jury enregistrées , qui sont potentiellement à revoir suite
à cette modification !
< / p > """
2020-09-26 16:19:37 +02:00
)
H + = [
2022-11-22 13:13:16 +01:00
f """ <p><a class= " stdlink " href= " { status_url } " >continuer</a>
"""
2020-09-26 16:19:37 +02:00
]
# news
2022-11-22 13:13:16 +01:00
modimpl = ModuleImpl . query . get ( E [ " moduleimpl_id " ] )
2022-04-12 17:12:51 +02:00
ScolarNews . add (
typ = ScolarNews . NEWS_NOTE ,
2022-11-22 13:13:16 +01:00
obj = modimpl . id ,
text = f """ Suppression des notes d ' une évaluation dans
< a class = " stdlink " href = " {status_url} "
> { modimpl . module . titre or ' module sans titre ' } < / a >
""" ,
url = status_url ,
2020-09-26 16:19:37 +02:00
)
2021-07-29 16:31:15 +02:00
return html_sco_header . sco_header ( ) + " \n " . join ( H ) + html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
2021-12-17 22:53:34 +01:00
def notes_add (
2022-12-05 21:59:10 +01:00
user : User ,
2021-12-17 22:53:34 +01:00
evaluation_id : int ,
notes : list ,
comment = None ,
do_it = True ,
check_inscription = True ,
) - > tuple :
2020-09-26 16:19:37 +02:00
"""
Insert or update notes
notes is a list of tuples ( etudid , value )
If do_it is False , simulate the process and returns the number of values that
WOULD be changed or suppressed .
Nota :
- si la note existe deja avec valeur distincte , ajoute une entree au log ( notes_notes_log )
2021-11-22 00:31:53 +01:00
Return tuple ( nb_changed , nb_suppress , existing_decisions )
2020-09-26 16:19:37 +02:00
"""
2021-07-12 22:38:30 +02:00
now = psycopg2 . Timestamp (
* time . localtime ( ) [ : 6 ]
) # datetime.datetime.now().isoformat()
2020-09-26 16:19:37 +02:00
# Verifie inscription et valeur note
2021-11-20 17:21:51 +01:00
inscrits = {
x [ 0 ]
for x in sco_groups . do_evaluation_listeetuds_groups (
2022-01-16 23:47:52 +01:00
evaluation_id , getallstudents = True , include_demdef = True
2020-09-26 16:19:37 +02:00
)
2021-11-20 17:21:51 +01:00
}
2020-09-26 16:19:37 +02:00
for ( etudid , value ) in notes :
2021-12-17 22:53:34 +01:00
if check_inscription and ( etudid not in inscrits ) :
2022-01-17 22:32:44 +01:00
raise NoteProcessError ( f " etudiant { etudid } non inscrit dans ce module " )
if ( value is not None ) and not isinstance ( value , float ) :
2020-09-26 16:19:37 +02:00
raise NoteProcessError (
2022-01-17 22:32:44 +01:00
f " etudiant { etudid } : valeur de note invalide ( { value } ) "
2020-09-26 16:19:37 +02:00
)
# Recherche notes existantes
2022-01-17 00:18:08 +01:00
notes_db = sco_evaluation_db . do_evaluation_get_all_notes ( evaluation_id )
2020-09-26 16:19:37 +02:00
# Met a jour la base
2021-11-12 22:17:46 +01:00
cnx = ndb . GetDBConnexion ( )
2021-06-19 23:21:37 +02:00
cursor = cnx . cursor ( cursor_factory = ndb . ScoDocCursor )
2020-09-26 16:19:37 +02:00
nb_changed = 0
nb_suppress = 0
2021-11-12 22:17:46 +01:00
E = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } ) [ 0 ]
2021-10-15 14:00:51 +02:00
M = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = E [ " moduleimpl_id " ] ) [ 0 ]
2020-09-26 16:19:37 +02:00
existing_decisions = (
[ ]
) # etudids pour lesquels il y a une decision de jury et que la note change
try :
for ( etudid , value ) in notes :
changed = False
2022-01-17 00:18:08 +01:00
if etudid not in notes_db :
2020-09-26 16:19:37 +02:00
# nouvelle note
2021-01-17 22:31:28 +01:00
if value != scu . NOTES_SUPPRESS :
2020-09-26 16:19:37 +02:00
if do_it :
aa = {
" etudid " : etudid ,
" evaluation_id " : evaluation_id ,
" value " : value ,
" comment " : comment ,
2021-08-10 12:57:38 +02:00
" uid " : user . id ,
2020-09-26 16:19:37 +02:00
" date " : now ,
}
2021-06-19 23:21:37 +02:00
ndb . quote_dict ( aa )
2020-09-26 16:19:37 +02:00
cursor . execute (
2021-09-15 15:19:08 +02:00
""" INSERT INTO notes_notes
( etudid , evaluation_id , value , comment , date , uid )
VALUES ( % ( etudid ) s , % ( evaluation_id ) s , % ( value ) s , % ( comment ) s , % ( date ) s , % ( uid ) s )
""" ,
2020-09-26 16:19:37 +02:00
aa ,
)
changed = True
else :
# il y a deja une note
2022-01-17 00:18:08 +01:00
oldval = notes_db [ etudid ] [ " value " ]
2020-09-26 16:19:37 +02:00
if type ( value ) != type ( oldval ) :
changed = True
2022-11-22 13:13:16 +01:00
elif type ( value ) == float and (
2021-01-17 22:31:28 +01:00
abs ( value - oldval ) > scu . NOTES_PRECISION
2020-09-26 16:19:37 +02:00
) :
changed = True
elif value != oldval :
changed = True
if changed :
# recopie l'ancienne note dans notes_notes_log, puis update
if do_it :
cursor . execute (
2021-09-15 15:19:08 +02:00
""" INSERT INTO notes_notes_log
2022-11-22 13:13:16 +01:00
( etudid , evaluation_id , value , comment , date , uid )
2021-08-11 13:01:37 +02:00
SELECT etudid , evaluation_id , value , comment , date , uid
2021-09-15 15:19:08 +02:00
FROM notes_notes
2022-11-22 13:13:16 +01:00
WHERE etudid = % ( etudid ) s
2021-08-11 13:01:37 +02:00
and evaluation_id = % ( evaluation_id ) s
""" ,
2020-09-26 16:19:37 +02:00
{ " etudid " : etudid , " evaluation_id " : evaluation_id } ,
)
aa = {
" etudid " : etudid ,
" evaluation_id " : evaluation_id ,
" value " : value ,
" date " : now ,
" comment " : comment ,
2021-08-10 12:57:38 +02:00
" uid " : user . id ,
2020-09-26 16:19:37 +02:00
}
2021-06-19 23:21:37 +02:00
ndb . quote_dict ( aa )
2021-01-17 22:31:28 +01:00
if value != scu . NOTES_SUPPRESS :
2020-09-26 16:19:37 +02:00
if do_it :
cursor . execute (
2021-09-15 15:19:08 +02:00
""" UPDATE notes_notes
SET value = % ( value ) s , comment = % ( comment ) s , date = % ( date ) s , uid = % ( uid ) s
2022-11-22 13:13:16 +01:00
WHERE etudid = % ( etudid ) s
2021-08-11 13:01:37 +02:00
and evaluation_id = % ( evaluation_id ) s
""" ,
2020-09-26 16:19:37 +02:00
aa ,
)
else : # suppression ancienne note
if do_it :
log (
2021-11-22 00:31:53 +01:00
" notes_add, suppress, evaluation_id= %s , etudid= %s , oldval= %s "
2020-09-26 16:19:37 +02:00
% ( evaluation_id , etudid , oldval )
)
cursor . execute (
2021-09-15 15:19:08 +02:00
""" DELETE FROM notes_notes
2021-08-11 13:01:37 +02:00
WHERE etudid = % ( etudid ) s
AND evaluation_id = % ( evaluation_id ) s
""" ,
2020-09-26 16:19:37 +02:00
aa ,
)
# garde trace de la suppression dans l'historique:
2021-01-17 22:31:28 +01:00
aa [ " value " ] = scu . NOTES_SUPPRESS
2020-09-26 16:19:37 +02:00
cursor . execute (
2022-11-22 13:13:16 +01:00
""" INSERT INTO notes_notes_log (etudid,evaluation_id,value,comment,date,uid)
2021-08-11 13:01:37 +02:00
VALUES ( % ( etudid ) s , % ( evaluation_id ) s , % ( value ) s , % ( comment ) s , % ( date ) s , % ( uid ) s )
""" ,
2020-09-26 16:19:37 +02:00
aa ,
)
nb_suppress + = 1
if changed :
nb_changed + = 1
2021-08-21 00:24:51 +02:00
if has_existing_decision ( M , E , etudid ) :
2020-09-26 16:19:37 +02:00
existing_decisions . append ( etudid )
2022-01-15 21:36:06 +01:00
except Exception as exc :
2021-11-22 00:31:53 +01:00
log ( " *** exception in notes_add " )
2020-09-26 16:19:37 +02:00
if do_it :
2021-10-13 15:56:24 +02:00
cnx . rollback ( ) # abort
2020-09-26 16:19:37 +02:00
# inval cache
2021-07-19 19:53:01 +02:00
sco_cache . invalidate_formsemestre (
formsemestre_id = M [ " formsemestre_id " ]
2020-09-26 16:19:37 +02:00
) # > modif notes (exception)
2021-10-13 15:56:24 +02:00
sco_cache . EvaluationCache . delete ( evaluation_id )
2022-01-15 21:36:06 +01:00
raise ScoGenError ( " Erreur enregistrement note: merci de ré-essayer " ) from exc
2020-09-26 16:19:37 +02:00
if do_it :
cnx . commit ( )
2021-07-19 19:53:01 +02:00
sco_cache . invalidate_formsemestre (
formsemestre_id = M [ " formsemestre_id " ]
2021-06-13 23:37:14 +02:00
) # > modif notes
2021-07-19 19:53:01 +02:00
sco_cache . EvaluationCache . delete ( evaluation_id )
2020-09-26 16:19:37 +02:00
return nb_changed , nb_suppress , existing_decisions
2021-10-13 15:56:24 +02:00
def saisie_notes_tableur ( evaluation_id , group_ids = ( ) ) :
2021-01-01 18:40:47 +01:00
""" Saisie des notes via un fichier Excel """
2022-11-22 15:25:33 +01:00
evaluation = Evaluation . query . get_or_404 ( evaluation_id )
moduleimpl_id = evaluation . moduleimpl . id
formsemestre_id = evaluation . moduleimpl . formsemestre_id
if not sco_permissions_check . can_edit_notes ( current_user , moduleimpl_id ) :
2020-09-26 16:19:37 +02:00
return (
2021-07-29 16:31:15 +02:00
html_sco_header . sco_header ( )
2022-11-22 15:25:33 +01:00
+ f """
< h2 > Modification des notes impossible pour { current_user . user_name } < / h2 >
< p > ( vérifiez que le semestre n ' est pas verrouillé et que vous
avez l ' autorisation d ' effectuer cette opération )
< / p >
< p > < a class = " stdlink " href = " {
url_for ( " notes.moduleimpl_status " , scodoc_dept = g . scodoc_dept ,
moduleimpl_id = moduleimpl_id )
} " >Continuer</a></p>
2020-09-26 16:19:37 +02:00
"""
2021-07-29 10:19:00 +02:00
+ html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
)
2022-11-22 15:25:33 +01:00
page_title = " Saisie des notes " + (
f """ de { evaluation . description } """ if evaluation . description else " "
)
2020-09-26 16:19:37 +02:00
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view . DisplayedGroupsInfos (
group_ids = group_ids ,
formsemestre_id = formsemestre_id ,
select_all_when_unspecified = True ,
etat = None ,
)
H = [
2021-06-13 23:37:14 +02:00
html_sco_header . sco_header (
2020-09-26 16:19:37 +02:00
page_title = page_title ,
javascripts = sco_groups_view . JAVASCRIPTS ,
cssstyles = sco_groups_view . CSSSTYLES ,
init_qtip = True ,
) ,
2021-09-05 12:30:11 +02:00
sco_evaluations . evaluation_describe ( evaluation_id = evaluation_id ) ,
2020-09-26 16:19:37 +02:00
""" <span class= " eval_title " >Saisie des notes par fichier</span> """ ,
]
# Menu choix groupe:
H . append ( """ <div id= " group-tabs " ><table><tr><td> """ )
2021-08-21 00:24:51 +02:00
H . append ( sco_groups_view . form_groups_choice ( groups_infos ) )
2020-09-26 16:19:37 +02:00
H . append ( " </td></tr></table></div> " )
H . append (
2022-11-22 15:25:33 +01:00
f """ <div class= " saisienote_etape1 " >
2020-09-26 16:19:37 +02:00
< span class = " titredivsaisienote " > Etape 1 : < / span >
< ul >
2022-11-22 15:25:33 +01:00
< li > < a class = " stdlink " href = " feuille_saisie_notes?evaluation_id= {evaluation_id} & {
groups_infos . groups_query_args } "
id = " lnk_feuille_saisie " > obtenir le fichier tableur à remplir < / a >
< / li >
< li > ou < a class = " stdlink " href = " { url_for( " notes . saisie_notes " ,
scodoc_dept = g . scodoc_dept , evaluation_id = evaluation_id )
} " >aller au formulaire de saisie</a></li>
2020-09-26 16:19:37 +02:00
< / ul >
< / div >
2022-11-22 15:25:33 +01:00
< form >
< input type = " hidden " name = " evaluation_id " id = " formnotes_evaluation_id "
value = " {evaluation_id} " / >
< / form >
2020-09-26 16:19:37 +02:00
"""
)
H . append (
""" <div class= " saisienote_etape2 " >
2021-08-11 11:40:28 +02:00
< span class = " titredivsaisienote " > Etape 2 : chargement d ' un fichier de notes</span> " " " # '
2020-09-26 16:19:37 +02:00
)
nf = TrivialFormulator (
2021-09-18 10:10:02 +02:00
request . base_url ,
2021-09-27 16:42:14 +02:00
scu . get_request_args ( ) ,
2020-09-26 16:19:37 +02:00
(
( " evaluation_id " , { " default " : evaluation_id , " input_type " : " hidden " } ) ,
(
" notefile " ,
{ " input_type " : " file " , " title " : " Fichier de note (.xls) " , " size " : 44 } ,
) ,
(
" comment " ,
{
" size " : 44 ,
" title " : " Commentaire " ,
" explanation " : " (la colonne remarque du fichier excel est ignorée) " ,
} ,
) ,
) ,
formid = " notesfile " ,
submitlabel = " Télécharger " ,
)
if nf [ 0 ] == 0 :
H . append (
""" <p>Le fichier doit être un fichier tableur obtenu via
l ' étape 1 ci-dessus, puis complété et enregistré au format Excel.
< / p > """
)
H . append ( nf [ 1 ] )
elif nf [ 0 ] == - 1 :
H . append ( " <p>Annulation</p> " )
elif nf [ 0 ] == 1 :
2021-09-27 10:20:10 +02:00
updiag = do_evaluation_upload_xls ( )
2020-09-26 16:19:37 +02:00
if updiag [ 0 ] :
H . append ( updiag [ 1 ] )
H . append (
2022-11-22 15:25:33 +01:00
f """ <p>Notes chargées.
< a class = " stdlink " href = " {
url_for ( " notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept , moduleimpl_id = moduleimpl_id )
} " >
2020-09-26 16:19:37 +02:00
Revenir au tableau de bord du module < / a >
& nbsp ; & nbsp ; & nbsp ;
2022-11-22 15:25:33 +01:00
< a class = " stdlink " href = " { url_for( " notes . saisie_notes " ,
scodoc_dept = g . scodoc_dept , evaluation_id = evaluation . id )
} " >Charger d ' autres notes dans cette évaluation</a>
2020-09-26 16:19:37 +02:00
< / p > """
)
else :
H . append (
2022-11-22 15:25:33 +01:00
f """
< p class = " redboldtext " > Notes non chargées ! < / p >
{ updiag [ 1 ] }
< p > < a class = " stdlink " href = " { url_for( " notes . saisie_notes_tableur " ,
scodoc_dept = g . scodoc_dept , evaluation_id = evaluation . id )
} " >
Reprendre < / a >
< / p >
"""
2020-09-26 16:19:37 +02:00
)
2022-11-22 15:25:33 +01:00
2020-09-26 16:19:37 +02:00
#
H . append ( """ </div><h3>Autres opérations</h3><ul> """ )
2021-06-19 23:21:37 +02:00
if sco_permissions_check . can_edit_notes (
2022-11-22 15:25:33 +01:00
current_user , moduleimpl_id , allow_ens = False
2020-09-26 16:19:37 +02:00
) :
H . append (
2022-11-22 15:25:33 +01:00
f """
2020-09-26 16:19:37 +02:00
< li >
2022-12-25 03:27:28 +01:00
< form action = " do_evaluation_set_missing " method = " POST " >
2020-09-26 16:19:37 +02:00
Mettre toutes les notes manquantes à < input type = " text " size = " 5 " name = " value " / >
2022-11-22 15:25:33 +01:00
< input type = " submit " value = " OK " / >
< input type = " hidden " name = " evaluation_id " value = " {evaluation_id} " / >
2020-09-26 16:19:37 +02:00
< em > ABS indique " absent " ( zéro ) , EXC " excusé " ( neutralisées ) , ATT " attente " < / em >
< / form >
2022-11-22 15:25:33 +01:00
< / li >
< li > < a class = " stdlink " href = " { url_for( " notes . evaluation_suppress_alln " ,
scodoc_dept = g . scodoc_dept , evaluation_id = evaluation_id )
} " >Effacer toutes les notes de cette évaluation</a>
( ceci permet ensuite de supprimer l ' évaluation si besoin)
< / li >
< li > < a class = " stdlink " href = " { url_for( " notes . moduleimpl_status " ,
scodoc_dept = g . scodoc_dept , moduleimpl_id = moduleimpl_id )
} " >Revenir au module</a>
< / li >
< li > < a class = " stdlink " href = " { url_for( " notes . saisie_notes " ,
scodoc_dept = g . scodoc_dept , evaluation_id = evaluation . id )
} " >Revenir au formulaire de saisie</a>
< / li >
< / ul >
2020-09-26 16:19:37 +02:00
2022-11-22 15:25:33 +01:00
< h3 > Explications < / h3 >
2020-09-26 16:19:37 +02:00
< ol >
2022-11-22 15:25:33 +01:00
< li > Etape 1 :
2020-09-26 16:19:37 +02:00
< ol > < li > choisir le ou les groupes d ' étudiants;</li>
< li > télécharger le fichier Excel à remplir . < / li >
< / ol >
< / li >
2022-11-22 15:25:33 +01:00
< li > Etape 2 ( cadre vert ) : Indiquer le fichier Excel
< em > téléchargé à l ' étape 1</em> et dans lequel on a saisi des notes.
Remarques :
< ul >
< li > le fichier Excel peut être incomplet : on peut ne saisir que quelques notes
et répéter l ' opération (en téléchargeant un nouveau fichier) plus tard;
< / li >
< li > seules les valeurs des notes modifiées sont prises en compte ;
< / li >
< li > seules les notes sont extraites du fichier Excel ;
< / li >
< li > on peut optionnellement ajouter un commentaire ( type " copies corrigées
par Dupont " , ou " Modif . suite à contestation " ) dans la case " Commentaire " .
< / li >
< li > le fichier Excel < em > doit impérativement être celui chargé à l ' étape 1
pour cette évaluation < / em > . Il n ' est pas possible d ' utiliser une liste d ' appel
ou autre document Excel téléchargé d ' une autre page.
< / li >
< / ul >
2020-09-26 16:19:37 +02:00
< / li >
< / ol >
"""
2022-11-22 15:25:33 +01:00
)
2021-07-29 10:19:00 +02:00
H . append ( html_sco_header . sco_footer ( ) )
2020-09-26 16:19:37 +02:00
return " \n " . join ( H )
2021-09-27 10:20:10 +02:00
def feuille_saisie_notes ( evaluation_id , group_ids = [ ] ) :
2021-01-01 18:40:47 +01:00
""" Document Excel pour saisie notes dans l ' évaluation et les groupes indiqués """
2021-11-12 22:17:46 +01:00
evals = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } )
2020-09-26 16:19:37 +02:00
if not evals :
raise ScoValueError ( " invalid evaluation_id " )
2022-05-06 01:15:37 +02:00
eval_dict = evals [ 0 ]
M = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = eval_dict [ " moduleimpl_id " ] ) [ 0 ]
2020-09-26 16:19:37 +02:00
formsemestre_id = M [ " formsemestre_id " ]
2021-10-16 19:20:36 +02:00
Mod = sco_edit_module . module_list ( args = { " module_id " : M [ " module_id " ] } ) [ 0 ]
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( M [ " formsemestre_id " ] )
2021-08-11 13:01:37 +02:00
mod_responsable = sco_users . user_info ( M [ " responsable_id " ] )
2022-05-06 01:15:37 +02:00
if eval_dict [ " jour " ] :
indication_date = ndb . DateDMYtoISO ( eval_dict [ " jour " ] )
2020-09-26 16:19:37 +02:00
else :
2022-05-06 01:15:37 +02:00
indication_date = scu . sanitize_filename ( eval_dict [ " description " ] ) [ : 12 ]
eval_name = " %s - %s " % ( Mod [ " code " ] , indication_date )
2020-09-26 16:19:37 +02:00
2022-05-06 01:15:37 +02:00
if eval_dict [ " description " ] :
evaltitre = " %s du %s " % ( eval_dict [ " description " ] , eval_dict [ " jour " ] )
2020-09-26 16:19:37 +02:00
else :
2022-05-06 01:15:37 +02:00
evaltitre = " évaluation du %s " % eval_dict [ " jour " ]
2020-09-26 16:19:37 +02:00
description = " %s en %s ( %s ) resp. %s " % (
evaltitre ,
2022-02-14 10:05:55 +01:00
Mod [ " abbrev " ] or " " ,
Mod [ " code " ] or " " ,
2021-08-11 13:01:37 +02:00
mod_responsable [ " prenomnom " ] ,
2020-09-26 16:19:37 +02:00
)
groups_infos = sco_groups_view . DisplayedGroupsInfos (
group_ids = group_ids ,
formsemestre_id = formsemestre_id ,
select_all_when_unspecified = True ,
etat = None ,
)
2021-08-19 10:28:35 +02:00
groups = sco_groups . listgroups ( groups_infos . group_ids )
2020-09-26 16:19:37 +02:00
gr_title_filename = sco_groups . listgroups_filename ( groups )
2021-02-03 22:00:41 +01:00
# gr_title = sco_groups.listgroups_abbrev(groups)
2020-09-26 16:19:37 +02:00
if None in [ g [ " group_name " ] for g in groups ] : # tous les etudiants
getallstudents = True
2021-02-03 22:00:41 +01:00
# gr_title = "tous"
2020-09-26 16:19:37 +02:00
gr_title_filename = " tous "
else :
getallstudents = False
2021-11-20 17:21:51 +01:00
etudids = [
x [ 0 ]
for x in sco_groups . do_evaluation_listeetuds_groups (
2022-01-16 23:47:52 +01:00
evaluation_id , groups , getallstudents = getallstudents , include_demdef = True
2021-11-20 17:21:51 +01:00
)
]
2020-09-26 16:19:37 +02:00
# une liste de liste de chaines: lignes de la feuille de calcul
L = [ ]
2022-05-06 01:15:37 +02:00
etuds = _get_sorted_etuds ( eval_dict , etudids , formsemestre_id )
2020-09-26 16:19:37 +02:00
for e in etuds :
etudid = e [ " etudid " ]
2022-03-27 10:05:12 +02:00
groups = sco_groups . get_etud_groups ( etudid , formsemestre_id )
2020-09-26 16:19:37 +02:00
grc = sco_groups . listgroups_abbrev ( groups )
L . append (
[
" %s " % etudid ,
2021-08-21 00:24:51 +02:00
e [ " nom " ] . upper ( ) ,
e [ " prenom " ] . lower ( ) . capitalize ( ) ,
2020-09-26 16:19:37 +02:00
e [ " inscr " ] [ " etat " ] ,
grc ,
e [ " val " ] ,
e [ " explanation " ] ,
]
)
2022-05-06 01:15:37 +02:00
filename = " notes_ %s _ %s " % ( eval_name , gr_title_filename )
xls = sco_excel . excel_feuille_saisie (
eval_dict , sem [ " titreannee " ] , description , lines = L
)
2021-09-21 22:19:08 +02:00
return scu . send_file ( xls , filename , scu . XLSX_SUFFIX , mime = scu . XLSX_MIMETYPE )
2021-09-27 10:20:10 +02:00
# return sco_excel.send_excel_file(xls, filename)
2020-09-26 16:19:37 +02:00
2021-08-21 00:24:51 +02:00
def has_existing_decision ( M , E , etudid ) :
2020-09-26 16:19:37 +02:00
""" Verifie s ' il y a une validation pour cet etudiant dans ce semestre ou UE
Si oui , return True
"""
formsemestre_id = M [ " formsemestre_id " ]
2022-02-13 23:53:11 +01:00
formsemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
nt : NotesTableCompat = res_sem . load_formsemestre_results ( formsemestre )
2020-09-26 16:19:37 +02:00
if nt . get_etud_decision_sem ( etudid ) :
return True
dec_ues = nt . get_etud_decision_ues ( etudid )
if dec_ues :
2021-10-16 19:20:36 +02:00
mod = sco_edit_module . module_list ( { " module_id " : M [ " module_id " ] } ) [ 0 ]
2020-09-26 16:19:37 +02:00
ue_id = mod [ " ue_id " ]
if ue_id in dec_ues :
return True # decision pour l'UE a laquelle appartient cette evaluation
return False # pas de decision de jury affectee par cette note
# -----------------------------
# Nouveau formulaire saisie notes (2016)
2021-09-27 10:20:10 +02:00
def saisie_notes ( evaluation_id , group_ids = [ ] ) :
2021-01-01 18:40:47 +01:00
""" Formulaire saisie notes d ' une évaluation pour un groupe """
2022-04-12 17:12:51 +02:00
if not isinstance ( evaluation_id , int ) :
raise ScoInvalidParamError ( )
2021-09-15 23:02:11 +02:00
group_ids = [ int ( group_id ) for group_id in group_ids ]
2021-11-12 22:17:46 +01:00
evals = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } )
2020-09-26 16:19:37 +02:00
if not evals :
2022-04-12 17:12:51 +02:00
raise ScoValueError ( " évaluation inexistante " )
2020-09-26 16:19:37 +02:00
E = evals [ 0 ]
2021-10-15 14:00:51 +02:00
M = sco_moduleimpl . moduleimpl_withmodule_list ( moduleimpl_id = E [ " moduleimpl_id " ] ) [ 0 ]
2020-09-26 16:19:37 +02:00
formsemestre_id = M [ " formsemestre_id " ]
# Check access
# (admin, respformation, and responsable_id)
2021-09-16 14:58:56 +02:00
if not sco_permissions_check . can_edit_notes ( current_user , E [ " moduleimpl_id " ] ) :
2020-09-26 16:19:37 +02:00
return (
2021-07-29 16:31:15 +02:00
html_sco_header . sco_header ( )
2021-09-16 14:58:56 +02:00
+ " <h2>Modification des notes impossible pour %s </h2> "
% current_user . user_name
2020-09-26 16:19:37 +02:00
+ """ <p>(vérifiez que le semestre n ' est pas verrouillé et que vous
avez l ' autorisation d ' effectuer cette opération ) < / p >
< p > < a href = " moduleimpl_status?moduleimpl_id= %s " > Continuer < / a > < / p >
"""
% E [ " moduleimpl_id " ]
2021-07-29 10:19:00 +02:00
+ html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
)
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view . DisplayedGroupsInfos (
group_ids = group_ids ,
formsemestre_id = formsemestre_id ,
select_all_when_unspecified = True ,
etat = None ,
)
if E [ " description " ] :
page_title = ' Saisie " %s " ' % E [ " description " ]
else :
page_title = " Saisie des notes "
# HTML page:
H = [
2021-06-13 23:37:14 +02:00
html_sco_header . sco_header (
2020-09-26 16:19:37 +02:00
page_title = page_title ,
javascripts = sco_groups_view . JAVASCRIPTS + [ " js/saisie_notes.js " ] ,
cssstyles = sco_groups_view . CSSSTYLES ,
init_qtip = True ,
) ,
2022-06-02 11:37:13 +02:00
sco_evaluations . evaluation_describe (
evaluation_id = evaluation_id , link_saisie = False
) ,
2020-09-26 16:19:37 +02:00
' <div id= " saisie_notes " ><span class= " eval_title " >Saisie des notes</span> ' ,
]
H . append ( """ <div id= " group-tabs " ><table><tr><td> """ )
2021-08-21 00:24:51 +02:00
H . append ( sco_groups_view . form_groups_choice ( groups_infos ) )
2020-09-26 16:19:37 +02:00
H . append ( ' </td><td style= " padding-left: 35px; " > ' )
H . append (
2021-01-17 22:31:28 +01:00
htmlutils . make_menu (
2020-09-26 16:19:37 +02:00
" Autres opérations " ,
[
{
" title " : " Saisie par fichier tableur " ,
" id " : " menu_saisie_tableur " ,
2021-06-14 18:08:52 +02:00
" endpoint " : " notes.saisie_notes_tableur " ,
" args " : {
" evaluation_id " : E [ " evaluation_id " ] ,
" group_ids " : groups_infos . group_ids ,
} ,
2020-09-26 16:19:37 +02:00
} ,
{
" title " : " Voir toutes les notes du module " ,
2021-06-14 18:08:52 +02:00
" endpoint " : " notes.evaluation_listenotes " ,
" args " : { " moduleimpl_id " : E [ " moduleimpl_id " ] } ,
2020-09-26 16:19:37 +02:00
} ,
{
" title " : " Effacer toutes les notes de cette évaluation " ,
2021-06-14 18:08:52 +02:00
" endpoint " : " notes.evaluation_suppress_alln " ,
" args " : { " evaluation_id " : E [ " evaluation_id " ] } ,
2020-09-26 16:19:37 +02:00
} ,
] ,
alone = True ,
)
)
H . append ( """ </td></tr></table></div> """ )
# Le formulaire de saisie des notes:
2021-08-21 12:23:00 +02:00
destination = url_for (
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
moduleimpl_id = E [ " moduleimpl_id " ] ,
2021-02-07 15:31:35 +01:00
)
2021-08-21 12:23:00 +02:00
2022-12-25 03:27:28 +01:00
form = _form_saisie_notes ( E , M , groups_infos , destination = destination )
2020-09-26 16:19:37 +02:00
if form is None :
2021-08-21 12:23:00 +02:00
log ( f " redirecting to { destination } " )
2021-07-31 18:01:10 +02:00
return flask . redirect ( destination )
2020-09-26 16:19:37 +02:00
H . append ( form )
#
H . append ( " </div> " ) # /saisie_notes
H . append (
""" <div class= " sco_help " >
< p > Les modifications sont enregistrées au fur et à mesure . < / p >
< h4 > Codes spéciaux : < / h4 >
< ul >
< li > ABS : absent ( compte comme un zéro ) < / li >
< li > EXC : excusé ( note neutralisée ) < / li >
< li > SUPR : pour supprimer une note existante < / li >
< li > ATT : note en attente ( permet de publier une évaluation avec des notes manquantes ) < / li >
< / ul >
< / div > """
)
2021-07-29 10:19:00 +02:00
H . append ( html_sco_header . sco_footer ( ) )
2020-09-26 16:19:37 +02:00
return " \n " . join ( H )
2022-05-06 01:15:37 +02:00
def _get_sorted_etuds ( eval_dict : dict , etudids : list , formsemestre_id : int ) :
2021-11-12 22:17:46 +01:00
notes_db = sco_evaluation_db . do_evaluation_get_all_notes (
2022-05-06 01:15:37 +02:00
eval_dict [ " evaluation_id " ]
2021-06-19 23:21:37 +02:00
) # Notes existantes
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2020-09-26 16:19:37 +02:00
etuds = [ ]
for etudid in etudids :
# infos identite etudiant
2021-06-19 23:21:37 +02:00
e = sco_etud . etudident_list ( cnx , { " etudid " : etudid } ) [ 0 ]
sco_etud . format_etud_ident ( e )
2020-09-26 16:19:37 +02:00
etuds . append ( e )
# infos inscription dans ce semestre
2021-06-17 00:08:37 +02:00
e [ " inscr " ] = sco_formsemestre_inscriptions . do_formsemestre_inscription_list (
2021-08-19 10:28:35 +02:00
{ " etudid " : etudid , " formsemestre_id " : formsemestre_id }
2020-09-26 16:19:37 +02:00
) [ 0 ]
# Groupes auxquels appartient cet étudiant:
2022-03-27 10:05:12 +02:00
e [ " groups " ] = sco_groups . get_etud_groups ( etudid , formsemestre_id )
2020-09-26 16:19:37 +02:00
# Information sur absence (tenant compte de la demi-journée)
2022-05-06 01:15:37 +02:00
jour_iso = ndb . DateDMYtoISO ( eval_dict [ " jour " ] )
2020-09-26 16:19:37 +02:00
warn_abs_lst = [ ]
2022-05-06 01:15:37 +02:00
if eval_dict [ " matin " ] :
2021-07-19 19:53:01 +02:00
nbabs = sco_abs . count_abs ( etudid , jour_iso , jour_iso , matin = 1 )
nbabsjust = sco_abs . count_abs_just ( etudid , jour_iso , jour_iso , matin = 1 )
2020-09-26 16:19:37 +02:00
if nbabs :
if nbabsjust :
warn_abs_lst . append ( " absent justifié le matin ! " )
else :
warn_abs_lst . append ( " absent le matin ! " )
2022-05-06 01:15:37 +02:00
if eval_dict [ " apresmidi " ] :
2021-07-19 19:53:01 +02:00
nbabs = sco_abs . count_abs ( etudid , jour_iso , jour_iso , matin = 0 )
nbabsjust = sco_abs . count_abs_just ( etudid , jour_iso , jour_iso , matin = 0 )
2020-09-26 16:19:37 +02:00
if nbabs :
if nbabsjust :
warn_abs_lst . append ( " absent justifié l ' après-midi ! " )
else :
warn_abs_lst . append ( " absent l ' après-midi ! " )
e [ " absinfo " ] = ' <span class= " sn_abs " > ' + " " . join ( warn_abs_lst ) + " </span> "
# Note actuelle de l'étudiant:
2021-11-12 22:17:46 +01:00
if etudid in notes_db :
e [ " val " ] = _displayNote ( notes_db [ etudid ] [ " value " ] )
comment = notes_db [ etudid ] [ " comment " ]
2020-09-26 16:19:37 +02:00
if comment is None :
comment = " "
e [ " explanation " ] = " %s ( %s ) %s " % (
2021-11-12 22:17:46 +01:00
notes_db [ etudid ] [ " date " ] . strftime ( " %d / % m/ % y % Hh % M " ) ,
notes_db [ etudid ] [ " uid " ] ,
2020-09-26 16:19:37 +02:00
comment ,
)
else :
e [ " val " ] = " "
e [ " explanation " ] = " "
# Démission ?
if e [ " inscr " ] [ " etat " ] == " D " :
# if not e['val']:
e [ " val " ] = " DEM "
e [ " explanation " ] = " Démission "
etuds . sort ( key = lambda x : ( x [ " nom " ] , x [ " prenom " ] ) )
return etuds
2022-12-25 03:27:28 +01:00
def _form_saisie_notes ( E , M , groups_infos , destination = " " ) :
2020-09-26 16:19:37 +02:00
""" Formulaire HTML saisie des notes dans l ' évaluation E du moduleimpl M
pour les groupes indiqués .
On charge tous les étudiants , ne seront montrés que ceux
des groupes sélectionnés grace a un filtre en javascript .
"""
evaluation_id = E [ " evaluation_id " ]
formsemestre_id = M [ " formsemestre_id " ]
2021-11-20 17:21:51 +01:00
etudids = [
x [ 0 ]
for x in sco_groups . do_evaluation_listeetuds_groups (
2022-01-16 23:47:52 +01:00
evaluation_id , getallstudents = True , include_demdef = True
2021-11-20 17:21:51 +01:00
)
]
2020-09-26 16:19:37 +02:00
if not etudids :
return ' <div class= " ue_warning " ><span>Aucun étudiant sélectionné !</span></div> '
# Decisions de jury existantes ?
2021-08-21 00:24:51 +02:00
decisions_jury = { etudid : has_existing_decision ( M , E , etudid ) for etudid in etudids }
2021-08-21 12:23:00 +02:00
# Nb de decisions de jury (pour les inscrits à l'évaluation):
nb_decisions = sum ( decisions_jury . values ( ) )
2020-09-26 16:19:37 +02:00
2021-08-21 00:24:51 +02:00
etuds = _get_sorted_etuds ( E , etudids , formsemestre_id )
2020-09-26 16:19:37 +02:00
# Build form:
descr = [
( " evaluation_id " , { " default " : evaluation_id , " input_type " : " hidden " } ) ,
( " formsemestre_id " , { " default " : formsemestre_id , " input_type " : " hidden " } ) ,
2022-12-25 03:27:28 +01:00
(
" group_ids " ,
{ " default " : groups_infos . group_ids , " input_type " : " hidden " , " type " : " list " } ,
) ,
2020-09-26 16:19:37 +02:00
# ('note_method', { 'default' : note_method, 'input_type' : 'hidden'}),
( " comment " , { " size " : 44 , " title " : " Commentaire " , " return_focus_next " : True } ) ,
( " changed " , { " default " : " 0 " , " input_type " : " hidden " } ) , # changed in JS
]
2021-11-12 22:17:46 +01:00
if M [ " module " ] [ " module_type " ] in (
ModuleType . STANDARD ,
ModuleType . RESSOURCE ,
ModuleType . SAE ,
) :
2020-09-26 16:19:37 +02:00
descr . append (
(
" s3 " ,
{
" input_type " : " text " , # affiche le barème
" title " : " Notes " ,
" cssclass " : " formnote_bareme " ,
" readonly " : True ,
" default " : " / %g " % E [ " note_max " ] ,
} ,
)
)
2021-11-12 22:17:46 +01:00
elif M [ " module " ] [ " module_type " ] == ModuleType . MALUS :
2020-09-26 16:19:37 +02:00
descr . append (
(
" s3 " ,
{
" input_type " : " text " , # affiche le barème
" title " : " " ,
" cssclass " : " formnote_bareme " ,
" readonly " : True ,
" default " : " Points de malus (soustraits à la moyenne de l ' UE, entre -20 et 20) " ,
} ,
)
)
else :
raise ValueError ( " invalid module type ( %s ) " % M [ " module " ] [ " module_type " ] ) # bug
initvalues = { }
for e in etuds :
etudid = e [ " etudid " ]
disabled = e [ " val " ] == " DEM "
etud_classes = [ ]
if disabled :
classdem = " etud_dem "
etud_classes . append ( " etud_dem " )
disabled_attr = ' disabled= " %d " ' % disabled
else :
classdem = " "
disabled_attr = " "
# attribue a chaque element une classe css par groupe:
for group_info in e [ " groups " ] :
2021-08-11 13:01:37 +02:00
etud_classes . append ( " group- " + str ( group_info [ " group_id " ] ) )
2020-09-26 16:19:37 +02:00
2021-02-13 22:06:03 +01:00
label = (
' <span class= " %s " > ' % classdem
+ e [ " civilite_str " ]
+ " "
2021-06-19 23:21:37 +02:00
+ sco_etud . format_nomprenom ( e , reverse = True )
2021-02-13 22:06:03 +01:00
+ " </span> "
)
2020-09-26 16:19:37 +02:00
# Historique des saisies de notes:
if not disabled :
explanation = (
' <span id= " hist_ %s " > ' % etudid
2021-08-21 00:24:51 +02:00
+ get_note_history_menu ( evaluation_id , etudid )
2020-09-26 16:19:37 +02:00
+ " </span> "
)
else :
explanation = " "
explanation = e [ " absinfo " ] + explanation
# Lien modif decision de jury:
explanation + = ' <span id= " jurylink_ %s " class= " jurylink " ></span> ' % etudid
# Valeur actuelle du champ:
2021-08-10 17:12:10 +02:00
initvalues [ " note_ " + str ( etudid ) ] = e [ " val " ]
2020-09-26 16:19:37 +02:00
label_link = ' <a class= " etudinfo " id= " %s " > %s </a> ' % ( etudid , label )
# Element de formulaire:
descr . append (
(
2021-08-10 17:12:10 +02:00
" note_ " + str ( etudid ) ,
2020-09-26 16:19:37 +02:00
{
" size " : 5 ,
" title " : label_link ,
" explanation " : explanation ,
" return_focus_next " : True ,
" attributes " : [
' class= " note %s " ' % classdem ,
disabled_attr ,
2021-09-15 22:13:04 +02:00
' data-last-saved-value= " %s " ' % e [ " val " ] ,
' data-orig-value= " %s " ' % e [ " val " ] ,
' data-etudid= " %s " ' % etudid ,
2020-09-26 16:19:37 +02:00
] ,
" template " : """ <tr %(item_dom_attr)s class= " etud_elem """
+ " " . join ( etud_classes )
2021-08-11 13:01:37 +02:00
+ """ " ><td class= " tf-fieldlabel " > %(label)s </td>
< td class = " tf-field " > % ( elem ) s < / td > < / tr >
""" ,
2020-09-26 16:19:37 +02:00
} ,
)
)
#
H = [ ]
if nb_decisions > 0 :
H . append (
""" <div class= " saisie_warn " >
< ul class = " tf-msg " >
2022-12-25 03:27:28 +01:00
< li class = " tf-msg " > Attention : il y a déjà des < b > décisions de jury < / b > enregistrées pour
{ nb_decisions } étudiants . Après changement des notes , vérifiez la situation ! < / li >
2020-09-26 16:19:37 +02:00
< / ul >
< / div > """
)
tf = TF (
destination ,
2021-09-27 16:42:14 +02:00
scu . get_request_args ( ) ,
2020-09-26 16:19:37 +02:00
descr ,
initvalues = initvalues ,
2021-08-21 13:40:47 +02:00
submitbutton = False ,
2020-09-26 16:19:37 +02:00
formid = " formnotes " ,
2021-07-12 22:38:30 +02:00
method = " GET " ,
2020-09-26 16:19:37 +02:00
)
H . append ( tf . getform ( ) ) # check and init
2021-08-21 13:40:47 +02:00
H . append (
f """ <a href= " { url_for ( " notes.moduleimpl_status " , scodoc_dept = g . scodoc_dept ,
moduleimpl_id = M [ " moduleimpl_id " ] )
} " class= " btn btn - primary " >Terminer</a>
"""
)
2020-09-26 16:19:37 +02:00
if tf . canceled ( ) :
return None
elif ( not tf . submitted ( ) ) or not tf . result :
2021-08-11 13:01:37 +02:00
# ajout formulaire saisie notes manquantes
2020-09-26 16:19:37 +02:00
H . append (
2022-12-25 03:27:28 +01:00
f """
2020-09-26 16:19:37 +02:00
< div >
2022-12-25 03:27:28 +01:00
< form id = " do_evaluation_set_missing " action = " do_evaluation_set_missing " method = " POST " >
Mettre les notes manquantes à
< input type = " text " size = " 5 " name = " value " / >
< input type = " submit " value = " OK " / >
< input type = " hidden " name = " evaluation_id " value = " {evaluation_id} " / >
< input class = " group_ids_str " type = " hidden " name = " group_ids_str " value = " {
" , " . join ( [ str ( x ) for x in groups_infos . group_ids ] )
} " />
< em > ABS indique " absent " ( zéro ) , EXC " excusé " ( neutralisées ) , ATT " attente " < / em >
2020-09-26 16:19:37 +02:00
< / form >
< / div >
"""
)
# affiche formulaire
return " \n " . join ( H )
else :
# form submission
# rien à faire
return None
2021-09-27 10:20:10 +02:00
def save_note ( etudid = None , evaluation_id = None , value = None , comment = " " ) :
2021-01-01 18:40:47 +01:00
""" Enregistre une note (ajax) """
2021-09-18 13:42:19 +02:00
authuser = current_user
2020-09-26 16:19:37 +02:00
log (
" save_note: evaluation_id= %s etudid= %s uid= %s value= %s "
% ( evaluation_id , etudid , authuser , value )
)
2021-11-12 22:17:46 +01:00
E = sco_evaluation_db . do_evaluation_list ( { " evaluation_id " : evaluation_id } ) [ 0 ]
2021-10-15 14:00:51 +02:00
M = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = E [ " moduleimpl_id " ] ) [ 0 ]
2021-10-16 19:20:36 +02:00
Mod = sco_edit_module . module_list ( args = { " module_id " : M [ " module_id " ] } ) [ 0 ]
2021-08-21 12:23:00 +02:00
Mod [ " url " ] = url_for (
" notes.moduleimpl_status " ,
scodoc_dept = g . scodoc_dept ,
moduleimpl_id = M [ " moduleimpl_id " ] ,
2022-03-16 19:08:17 +01:00
_external = True ,
2021-08-21 12:23:00 +02:00
)
2020-09-26 16:19:37 +02:00
result = { " nbchanged " : 0 } # JSON
# Check access: admin, respformation, or responsable_id
2021-07-31 18:01:10 +02:00
if not sco_permissions_check . can_edit_notes ( authuser , E [ " moduleimpl_id " ] ) :
2020-09-26 16:19:37 +02:00
result [ " status " ] = " unauthorized "
else :
2021-02-03 22:00:41 +01:00
L , _ , _ , _ , _ = _check_notes ( [ ( etudid , value ) ] , E , Mod )
2020-09-26 16:19:37 +02:00
if L :
2021-11-22 00:31:53 +01:00
nbchanged , _ , existing_decisions = notes_add (
2021-08-21 00:24:51 +02:00
authuser , evaluation_id , L , comment = comment , do_it = True
2020-09-26 16:19:37 +02:00
)
2022-04-12 17:12:51 +02:00
ScolarNews . add (
typ = ScolarNews . NEWS_NOTE ,
obj = M [ " moduleimpl_id " ] ,
2020-09-26 16:19:37 +02:00
text = ' Chargement notes dans <a href= " %(url)s " > %(titre)s </a> ' % Mod ,
url = Mod [ " url " ] ,
max_frequency = 30 * 60 , # 30 minutes
)
result [ " nbchanged " ] = nbchanged
result [ " existing_decisions " ] = existing_decisions
if nbchanged > 0 :
2021-08-21 00:24:51 +02:00
result [ " history_menu " ] = get_note_history_menu ( evaluation_id , etudid )
2020-09-26 16:19:37 +02:00
else :
result [ " history_menu " ] = " " # no update needed
result [ " status " ] = " ok "
2021-09-21 13:36:56 +02:00
return scu . sendJSON ( result )
2020-09-26 16:19:37 +02:00
2021-08-21 00:24:51 +02:00
def get_note_history_menu ( evaluation_id , etudid ) :
2020-09-26 16:19:37 +02:00
""" Menu HTML historique de la note """
2021-08-21 00:24:51 +02:00
history = sco_undo_notes . get_note_history ( evaluation_id , etudid )
2020-09-26 16:19:37 +02:00
if not history :
return " "
H = [ ]
if len ( history ) > 1 :
H . append (
' <select data-etudid= " %s " class= " note_history " onchange= " change_history(this); " > '
% etudid
)
envir = " select "
item = " option "
else :
# pas de menu
H . append ( ' <span class= " history " > ' )
envir = " span "
item = " span "
first = True
for i in history :
jt = i [ " date " ] . strftime ( " le %d / % m/ % Y à % H: % M " ) + " ( %s ) " % i [ " user_name " ]
dispnote = _displayNote ( i [ " value " ] )
if first :
nv = " " # ne repete pas la valeur de la note courante
else :
# ancienne valeur
2021-09-15 22:13:04 +02:00
nv = " : %s " % dispnote
2020-09-26 16:19:37 +02:00
first = False
if i [ " comment " ] :
comment = ' <span class= " histcomment " > %s </span> ' % i [ " comment " ]
else :
comment = " "
H . append (
' < %s data-note= " %s " > %s %s %s </ %s > ' % ( item , dispnote , jt , nv , comment , item )
)
H . append ( " </ %s > " % envir )
return " \n " . join ( H )