2021-11-12 22:17:46 +01: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.
|
2021-11-12 22:17:46 +01: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@gmail.com
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2023-01-27 14:20:52 +01:00
|
|
|
"""Gestion évaluations (ScoDoc7, code en voie de modernisation)
|
2021-11-12 22:17:46 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
import pprint
|
|
|
|
|
|
|
|
import flask
|
|
|
|
from flask import url_for, g
|
|
|
|
from flask_login import current_user
|
|
|
|
|
2023-01-27 14:20:52 +01:00
|
|
|
from app import db, log
|
2022-02-10 21:55:06 +01:00
|
|
|
|
2023-01-27 14:20:52 +01:00
|
|
|
from app.models import Evaluation, ModuleImpl, ScolarNews
|
2023-08-22 17:02:00 +02:00
|
|
|
from app.models.evaluations import evaluation_enrich_dict, check_convert_evaluation_args
|
2021-11-12 22:17:46 +01:00
|
|
|
import app.scodoc.sco_utils as scu
|
|
|
|
import app.scodoc.notesdb as ndb
|
|
|
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
|
|
|
|
|
|
|
from app.scodoc import sco_cache
|
|
|
|
from app.scodoc import sco_moduleimpl
|
|
|
|
from app.scodoc import sco_permissions_check
|
|
|
|
|
|
|
|
|
|
|
|
_evaluationEditor = ndb.EditableTable(
|
|
|
|
"notes_evaluation",
|
|
|
|
"evaluation_id",
|
|
|
|
(
|
|
|
|
"evaluation_id",
|
|
|
|
"moduleimpl_id",
|
|
|
|
"jour",
|
|
|
|
"heure_debut",
|
|
|
|
"heure_fin",
|
|
|
|
"description",
|
|
|
|
"note_max",
|
|
|
|
"coefficient",
|
|
|
|
"visibulletin",
|
|
|
|
"publish_incomplete",
|
|
|
|
"evaluation_type",
|
|
|
|
"numero",
|
|
|
|
),
|
|
|
|
sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord
|
|
|
|
output_formators={
|
|
|
|
"jour": ndb.DateISOtoDMY,
|
|
|
|
"numero": ndb.int_null_is_zero,
|
|
|
|
},
|
|
|
|
input_formators={
|
|
|
|
"jour": ndb.DateDMYtoISO,
|
|
|
|
"heure_debut": ndb.TimetoISO8601, # converti par evaluation_enrich_dict
|
|
|
|
"heure_fin": ndb.TimetoISO8601, # converti par evaluation_enrich_dict
|
|
|
|
"visibulletin": bool,
|
|
|
|
"publish_incomplete": bool,
|
|
|
|
"evaluation_type": int,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def do_evaluation_list(args, sortkey=None):
|
|
|
|
"""List evaluations, sorted by numero (or most recent date first).
|
|
|
|
|
|
|
|
Ajoute les champs:
|
|
|
|
'duree' : '2h30'
|
|
|
|
'matin' : 1 (commence avant 12:00) ou 0
|
|
|
|
'apresmidi' : 1 (termine après 12:00) ou 0
|
|
|
|
'descrheure' : ' de 15h00 à 16h30'
|
|
|
|
"""
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
evals = _evaluationEditor.list(cnx, args, sortkey=sortkey)
|
2022-11-01 11:19:28 +01:00
|
|
|
# calcule duree (chaine de car.) de chaque evaluation et ajoute jour_iso, matin, apresmidi
|
2021-11-12 22:17:46 +01:00
|
|
|
for e in evals:
|
|
|
|
evaluation_enrich_dict(e)
|
|
|
|
|
|
|
|
return evals
|
|
|
|
|
|
|
|
|
|
|
|
def do_evaluation_list_in_formsemestre(formsemestre_id):
|
|
|
|
"list evaluations in this formsemestre"
|
|
|
|
mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
|
|
|
|
evals = []
|
|
|
|
for modimpl in mods:
|
|
|
|
evals += do_evaluation_list(args={"moduleimpl_id": modimpl["moduleimpl_id"]})
|
|
|
|
return evals
|
|
|
|
|
|
|
|
|
|
|
|
def do_evaluation_edit(args):
|
|
|
|
"edit an evaluation"
|
|
|
|
evaluation_id = args["evaluation_id"]
|
2023-08-22 17:02:00 +02:00
|
|
|
evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
|
|
|
|
if evaluation is None:
|
2021-11-12 22:17:46 +01:00
|
|
|
raise ValueError("evaluation inexistante !")
|
2023-08-22 17:02:00 +02:00
|
|
|
|
|
|
|
if not evaluation.moduleimpl.can_edit_evaluation(current_user):
|
2021-11-12 22:17:46 +01:00
|
|
|
raise AccessDenied(
|
2023-08-22 17:02:00 +02:00
|
|
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
2021-11-12 22:17:46 +01:00
|
|
|
)
|
2023-08-22 17:02:00 +02:00
|
|
|
args["moduleimpl_id"] = evaluation.moduleimpl.id
|
|
|
|
check_convert_evaluation_args(evaluation.moduleimpl, args)
|
2021-11-12 22:17:46 +01:00
|
|
|
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
_evaluationEditor.edit(cnx, args)
|
|
|
|
# inval cache pour ce semestre
|
2023-08-22 17:02:00 +02:00
|
|
|
sco_cache.invalidate_formsemestre(
|
|
|
|
formsemestre_id=evaluation.moduleimpl.formsemestre_id
|
|
|
|
)
|
2021-11-12 22:17:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
def do_evaluation_delete(evaluation_id):
|
|
|
|
"delete evaluation"
|
2023-01-27 14:20:52 +01:00
|
|
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
|
|
|
modimpl: ModuleImpl = evaluation.moduleimpl
|
2023-08-22 17:02:00 +02:00
|
|
|
if not modimpl.can_edit_evaluation(current_user):
|
2021-11-12 22:17:46 +01:00
|
|
|
raise AccessDenied(
|
2023-01-27 14:20:52 +01:00
|
|
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
2021-11-12 22:17:46 +01:00
|
|
|
)
|
2022-01-17 00:18:08 +01:00
|
|
|
notes_db = do_evaluation_get_all_notes(evaluation_id) # { etudid : value }
|
|
|
|
notes = [x["value"] for x in notes_db.values()]
|
2021-11-12 22:17:46 +01:00
|
|
|
if notes:
|
|
|
|
raise ScoValueError(
|
|
|
|
"Impossible de supprimer cette évaluation: il reste des notes"
|
|
|
|
)
|
|
|
|
|
2023-01-27 14:20:52 +01:00
|
|
|
db.session.delete(evaluation)
|
|
|
|
db.session.commit()
|
2021-11-12 22:17:46 +01:00
|
|
|
|
|
|
|
# inval cache pour ce semestre
|
2023-01-27 14:20:52 +01:00
|
|
|
sco_cache.invalidate_formsemestre(formsemestre_id=modimpl.formsemestre_id)
|
2021-11-12 22:17:46 +01:00
|
|
|
# news
|
2023-01-27 14:20:52 +01:00
|
|
|
url = url_for(
|
|
|
|
"notes.moduleimpl_status",
|
|
|
|
scodoc_dept=g.scodoc_dept,
|
|
|
|
moduleimpl_id=modimpl.id,
|
2021-11-12 22:17:46 +01:00
|
|
|
)
|
2022-04-12 17:12:51 +02:00
|
|
|
ScolarNews.add(
|
|
|
|
typ=ScolarNews.NEWS_NOTE,
|
2023-01-27 14:20:52 +01:00
|
|
|
obj=modimpl.id,
|
|
|
|
text=f"""Suppression d'une évaluation dans <a href="{
|
|
|
|
url
|
|
|
|
}">{modimpl.module.titre}</a>""",
|
|
|
|
url=url,
|
2021-11-12 22:17:46 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ancien _notes_getall
|
|
|
|
def do_evaluation_get_all_notes(
|
|
|
|
evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
|
|
|
|
):
|
2023-06-03 22:43:04 +02:00
|
|
|
"""Toutes les notes pour une évaluation: { etudid : { 'value' : value, 'date' : date ... }}
|
2021-11-12 22:17:46 +01:00
|
|
|
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
|
|
|
|
"""
|
2023-05-29 16:04:41 +02:00
|
|
|
# pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
|
|
|
|
do_cache = filter_suppressed and table == "notes_notes" and (by_uid is None)
|
2021-11-12 22:17:46 +01:00
|
|
|
if do_cache:
|
|
|
|
r = sco_cache.EvaluationCache.get(evaluation_id)
|
2023-01-27 14:20:52 +01:00
|
|
|
if r is not None:
|
2021-11-12 22:17:46 +01:00
|
|
|
return r
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
|
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
|
|
|
cond = " where evaluation_id=%(evaluation_id)s"
|
|
|
|
if by_uid:
|
|
|
|
cond += " and uid=%(by_uid)s"
|
|
|
|
|
|
|
|
cursor.execute(
|
|
|
|
"select * from " + table + cond,
|
|
|
|
{"evaluation_id": evaluation_id, "by_uid": by_uid},
|
|
|
|
)
|
|
|
|
res = cursor.dictfetchall()
|
|
|
|
d = {}
|
|
|
|
if filter_suppressed:
|
|
|
|
for x in res:
|
|
|
|
if x["value"] != scu.NOTES_SUPPRESS:
|
|
|
|
d[x["etudid"]] = x
|
|
|
|
else:
|
|
|
|
for x in res:
|
|
|
|
d[x["etudid"]] = x
|
|
|
|
if do_cache:
|
|
|
|
status = sco_cache.EvaluationCache.set(evaluation_id, d)
|
|
|
|
if not status:
|
|
|
|
log(f"Warning: EvaluationCache.set: {evaluation_id}\t{status}")
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
2023-01-27 14:20:52 +01:00
|
|
|
def moduleimpl_evaluation_move(evaluation_id: int, after=0, redirect=1):
|
2021-11-12 22:17:46 +01:00
|
|
|
"""Move before/after previous one (decrement/increment numero)
|
|
|
|
(published)
|
|
|
|
"""
|
2023-01-27 14:20:52 +01:00
|
|
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
|
|
|
moduleimpl_id = evaluation.moduleimpl_id
|
2021-11-12 22:17:46 +01:00
|
|
|
redirect = int(redirect)
|
|
|
|
# access: can change eval ?
|
2023-08-22 17:02:00 +02:00
|
|
|
if not evaluation.moduleimpl.can_edit_evaluation(current_user):
|
2021-11-12 22:17:46 +01:00
|
|
|
raise AccessDenied(
|
2023-01-27 14:20:52 +01:00
|
|
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
2021-11-12 22:17:46 +01:00
|
|
|
)
|
2023-08-22 17:02:00 +02:00
|
|
|
Evaluation.moduleimpl_evaluation_renumber(
|
|
|
|
evaluation.moduleimpl, only_if_unumbered=True
|
|
|
|
)
|
2021-11-12 22:17:46 +01:00
|
|
|
e = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
|
|
|
|
|
|
|
|
after = int(after) # 0: deplace avant, 1 deplace apres
|
|
|
|
if after not in (0, 1):
|
|
|
|
raise ValueError('invalid value for "after"')
|
|
|
|
mod_evals = do_evaluation_list({"moduleimpl_id": e["moduleimpl_id"]})
|
|
|
|
if len(mod_evals) > 1:
|
|
|
|
idx = [p["evaluation_id"] for p in mod_evals].index(evaluation_id)
|
|
|
|
neigh = None # object to swap with
|
|
|
|
if after == 0 and idx > 0:
|
|
|
|
neigh = mod_evals[idx - 1]
|
|
|
|
elif after == 1 and idx < len(mod_evals) - 1:
|
|
|
|
neigh = mod_evals[idx + 1]
|
|
|
|
if neigh: #
|
|
|
|
if neigh["numero"] == e["numero"]:
|
2023-01-27 14:20:52 +01:00
|
|
|
log("Warning: moduleimpl_evaluation_move: forcing renumber")
|
2023-08-22 17:02:00 +02:00
|
|
|
Evaluation.moduleimpl_evaluation_renumber(
|
|
|
|
evaluation.moduleimpl, only_if_unumbered=False
|
2023-01-27 14:20:52 +01:00
|
|
|
)
|
2021-11-12 22:17:46 +01:00
|
|
|
else:
|
|
|
|
# swap numero with neighbor
|
|
|
|
e["numero"], neigh["numero"] = neigh["numero"], e["numero"]
|
|
|
|
do_evaluation_edit(e)
|
|
|
|
do_evaluation_edit(neigh)
|
|
|
|
# redirect to moduleimpl page:
|
|
|
|
if redirect:
|
|
|
|
return flask.redirect(
|
|
|
|
url_for(
|
|
|
|
"notes.moduleimpl_status",
|
|
|
|
scodoc_dept=g.scodoc_dept,
|
|
|
|
moduleimpl_id=e["moduleimpl_id"],
|
|
|
|
)
|
|
|
|
)
|