forked from ScoDoc/ScoDoc
212 lines
7.1 KiB
Python
212 lines
7.1 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
#
|
|
# Gestion scolarite IUT
|
|
#
|
|
# Copyright (c) 1999 - 2023 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@gmail.com
|
|
#
|
|
##############################################################################
|
|
|
|
"""Gestion évaluations (ScoDoc7, code en voie de modernisation)
|
|
"""
|
|
|
|
import flask
|
|
from flask import url_for, g
|
|
from flask_login import current_user
|
|
import sqlalchemy as sa
|
|
|
|
from app import db, log
|
|
|
|
from app.models import Evaluation
|
|
from app.models.evaluations import check_convert_evaluation_args
|
|
import app.scodoc.sco_utils as scu
|
|
import app.scodoc.notesdb as ndb
|
|
from app.scodoc.sco_exceptions import AccessDenied
|
|
|
|
from app.scodoc import sco_cache
|
|
from app.scodoc import sco_moduleimpl
|
|
|
|
|
|
_evaluationEditor = ndb.EditableTable(
|
|
"notes_evaluation",
|
|
"evaluation_id",
|
|
(
|
|
"evaluation_id",
|
|
"moduleimpl_id",
|
|
"date_debut",
|
|
"date_fin",
|
|
"description",
|
|
"note_max",
|
|
"coefficient",
|
|
"visibulletin",
|
|
"publish_incomplete",
|
|
"evaluation_type",
|
|
"numero",
|
|
),
|
|
sortkey="numero, date_debut desc", # plus recente d'abord
|
|
output_formators={
|
|
"numero": ndb.int_null_is_zero,
|
|
},
|
|
input_formators={
|
|
"visibulletin": bool,
|
|
"publish_incomplete": bool,
|
|
"evaluation_type": int,
|
|
},
|
|
)
|
|
|
|
|
|
def get_evaluations_dict(args: dict) -> list[dict]:
|
|
"""Liste evaluations, triées numero (or most recent date first).
|
|
Fonction de transition pour ancien code ScoDoc7.
|
|
|
|
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'
|
|
"""
|
|
# calcule duree (chaine de car.) de chaque evaluation et ajoute jour_iso, matin, apresmidi
|
|
return [
|
|
e.to_dict()
|
|
for e in Evaluation.query.filter_by(**args).order_by(
|
|
sa.desc(Evaluation.numero), sa.desc(Evaluation.date_debut)
|
|
)
|
|
]
|
|
|
|
|
|
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 += get_evaluations_dict(args={"moduleimpl_id": modimpl["moduleimpl_id"]})
|
|
return evals
|
|
|
|
|
|
def do_evaluation_edit(args):
|
|
"edit an evaluation"
|
|
evaluation_id = args["evaluation_id"]
|
|
evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
|
|
if evaluation is None:
|
|
raise ValueError("evaluation inexistante !")
|
|
|
|
if not evaluation.moduleimpl.can_edit_evaluation(current_user):
|
|
raise AccessDenied(
|
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
|
)
|
|
args["moduleimpl_id"] = evaluation.moduleimpl.id
|
|
check_convert_evaluation_args(evaluation.moduleimpl, args)
|
|
|
|
cnx = ndb.GetDBConnexion()
|
|
_evaluationEditor.edit(cnx, args)
|
|
# inval cache pour ce semestre
|
|
sco_cache.invalidate_formsemestre(
|
|
formsemestre_id=evaluation.moduleimpl.formsemestre_id
|
|
)
|
|
|
|
|
|
# ancien _notes_getall
|
|
def do_evaluation_get_all_notes(
|
|
evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
|
|
):
|
|
"""Toutes les notes pour une évaluation: { etudid : { 'value' : value, 'date' : date ... }}
|
|
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
|
|
"""
|
|
# 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)
|
|
if do_cache:
|
|
r = sco_cache.EvaluationCache.get(evaluation_id)
|
|
if r is not None:
|
|
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
|
|
|
|
|
|
def moduleimpl_evaluation_move(evaluation_id: int, after=0, redirect=1):
|
|
"""Move before/after previous one (decrement/increment numero)
|
|
(published)
|
|
"""
|
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
|
redirect = int(redirect)
|
|
# access: can change eval ?
|
|
if not evaluation.moduleimpl.can_edit_evaluation(current_user):
|
|
raise AccessDenied(
|
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
|
)
|
|
Evaluation.moduleimpl_evaluation_renumber(
|
|
evaluation.moduleimpl, only_if_unumbered=True
|
|
)
|
|
e = get_evaluations_dict(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 = get_evaluations_dict({"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"]:
|
|
log("Warning: moduleimpl_evaluation_move: forcing renumber")
|
|
Evaluation.moduleimpl_evaluation_renumber(
|
|
evaluation.moduleimpl, only_if_unumbered=False
|
|
)
|
|
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"],
|
|
)
|
|
)
|