ScoDoc/app/scodoc/sco_evaluation_edit.py

372 lines
13 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
#
##############################################################################
"""Formulaire ajout/édition d'une évaluation
"""
import time
import flask
from flask import url_for, render_template
from flask import g
from flask_login import current_user
from flask import request
from app import db
from app import log
from app import models
from app.models.evaluations import Evaluation
from app.models.formsemestre import FormSemestre
from app.models.moduleimpls import ModuleImpl
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc import html_sco_header
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_permissions_check
from app.scodoc import sco_preferences
def evaluation_create_form(
moduleimpl_id=None,
evaluation_id=None,
edit=False,
page_title="Évaluation",
):
"Formulaire création/édition d'une évaluation (pas de ses notes)"
if evaluation_id is not None:
evaluation: Evaluation = models.Evaluation.query.get(evaluation_id)
if evaluation is None:
raise ScoValueError("Cette évaluation n'existe pas ou plus !")
moduleimpl_id = evaluation.moduleimpl_id
#
modimpl: ModuleImpl = (
ModuleImpl.query.filter_by(id=moduleimpl_id)
.join(FormSemestre)
.filter_by(dept_id=g.scodoc_dept_id)
.first_or_404()
)
modimpl_o = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[
0
]
mod = modimpl_o["module"]
formsemestre_id = modimpl_o["formsemestre_id"]
formsemestre = modimpl.formsemestre
sem_ues = formsemestre.query_ues(with_sport=False).all()
is_malus = mod["module_type"] == ModuleType.MALUS
is_apc = mod["module_type"] in (ModuleType.RESSOURCE, ModuleType.SAE)
preferences = sco_preferences.SemPreferences(formsemestre.id)
can_edit_poids = not preferences["but_disable_edit_poids_evaluations"]
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
#
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
return f"""
{html_sco_header.sco_header()}
<h2>Opération non autorisée</h2>
<p>Modification évaluation impossible pour {current_user.get_nomplogin()}</p>
<p><a href="{url_for('notes.moduleimpl_status',
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
}" class="stdlink">Revenir</a>
</p>
{html_sco_header.sco_footer()}
"""
if not edit:
# création nouvel
if moduleimpl_id is None:
raise ValueError("missing moduleimpl_id parameter")
initvalues = {
"note_max": 20,
"jour": time.strftime("%d/%m/%Y", time.localtime()),
"publish_incomplete": is_malus,
}
submitlabel = "Créer cette évaluation"
action = "Création d'une évaluation"
link = ""
else:
# édition données existantes
# setup form init values
if evaluation_id is None:
raise ValueError("missing evaluation_id parameter")
initvalues = evaluation.to_dict()
moduleimpl_id = initvalues["moduleimpl_id"]
submitlabel = "Modifier les données"
action = "Modification d'une évaluation"
link = ""
# Note maximale actuelle dans cette éval ?
etat = sco_evaluations.do_evaluation_etat(evaluation_id)
if etat["maxi_num"] is not None:
min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"])
else:
min_note_max = scu.NOTES_PRECISION
#
if min_note_max > scu.NOTES_PRECISION:
min_note_max_str = scu.fmt_note(min_note_max)
else:
min_note_max_str = "0"
#
H = [
f"""<h3>{action} en
{scu.MODULE_TYPE_NAMES[mod["module_type"]]} <a class="stdlink" href="{
url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
}">{mod["code"] or "module sans code"} {mod["titre"]}</a> {link}</h3>
"""
]
heures = [f"{h:02d}h{m:02d}" for h in range(8, 19) for m in (0, 30)]
#
initvalues["visibulletin"] = initvalues.get("visibulletin", True)
if initvalues["visibulletin"]:
initvalues["visibulletinlist"] = ["X"]
else:
initvalues["visibulletinlist"] = []
initvalues["coefficient"] = initvalues.get("coefficient", 1.0)
vals = scu.get_request_args()
if vals.get("tf_submitted", False) and "visibulletinlist" not in vals:
vals["visibulletinlist"] = []
#
ue_coef_dict = modimpl.module.get_ue_coef_dict()
if is_apc: # BUT: poids vers les UE
if edit:
if evaluation.set_default_poids():
db.session.commit()
ue_poids_dict = evaluation.get_ue_poids_dict()
for ue in sem_ues:
initvalues[f"poids_{ue.id}"] = ue_poids_dict[ue.id]
else:
for ue in sem_ues:
coef_ue = ue_coef_dict.get(ue.id, 0.0) or 0.0
if coef_ue > 0:
poids = 1.0 # par défaut au départ
else:
poids = 0.0
initvalues[f"poids_{ue.id}"] = poids
#
form = [
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
("moduleimpl_id", {"default": moduleimpl_id, "input_type": "hidden"}),
(
"jour",
{
"input_type": "datedmy",
"title": "Date",
"size": 12,
"explanation": "date de l'examen, devoir ou contrôle",
},
),
(
"heure_debut",
{
"title": "Heure de début",
"explanation": "heure du début de l'épreuve",
"input_type": "menu",
"allowed_values": heures,
"labels": heures,
},
),
(
"heure_fin",
{
"title": "Heure de fin",
"explanation": "heure de fin de l'épreuve",
"input_type": "menu",
"allowed_values": heures,
"labels": heures,
},
),
]
if is_malus: # pas de coefficient
form.append(("coefficient", {"input_type": "hidden", "default": "1."}))
elif not is_apc: # modules standard hors BUT
form.append(
(
"coefficient",
{
"size": 6,
"type": "float", # peut être négatif (!)
"explanation": "coef. dans le module (choisi librement par l'enseignant, non utilisé pour rattrapage et 2ème session)",
"allow_null": False,
},
)
)
form += [
(
"note_max",
{
"size": 4,
"type": "float",
"title": "Notes de 0 à",
"explanation": f"barème (note max actuelle: {min_note_max_str})",
"allow_null": False,
"max_value": scu.NOTES_MAX,
"min_value": min_note_max,
},
),
(
"description",
{
"size": 36,
"type": "text",
"explanation": """type d'évaluation, apparait sur le bulletins longs. Exemples: "contrôle court", "examen de TP", "examen final".""",
},
),
(
"visibulletinlist",
{
"input_type": "checkbox",
"allowed_values": ["X"],
"labels": [""],
"title": "Visible sur bulletins",
"explanation": "(pour les bulletins en version intermédiaire)",
},
),
(
"publish_incomplete",
{
"input_type": "boolcheckbox",
"title": "Prise en compte immédiate",
"explanation": "notes utilisées même si incomplètes",
},
),
(
"evaluation_type",
{
"input_type": "menu",
"title": "Modalité",
"allowed_values": (
scu.EVALUATION_NORMALE,
scu.EVALUATION_RATTRAPAGE,
scu.EVALUATION_SESSION2,
),
"type": "int",
"labels": (
"Normale",
"Rattrapage (remplace si meilleure note)",
"Deuxième session (remplace toujours)",
),
},
),
]
if is_apc: # ressources et SAÉs
form += [
(
"coefficient",
{
"size": 6,
"type": "float",
"explanation": "importance de l'évaluation (multiplie les poids ci-dessous)",
"allow_null": False,
},
),
]
# Liste des UE utilisées dans des modules de ce semestre:
if sem_ues and not can_edit_poids:
form.append(
(
"sep_poids",
{
"input_type": "separator",
"title": """
<div class="warning-light">les poids ne sont pas modifiables (voir réglage paramétrage)
</div>""",
},
)
)
for ue in sem_ues:
coef_ue = ue_coef_dict.get(ue.id, 0.0)
form.append(
(
f"poids_{ue.id}",
{
"title": f"Poids {ue.acronyme}",
"size": 2,
"type": "float",
"explanation": f"""
<span class="eval_coef_ue" title="coef. du module dans cette UE">({
"coef. mod.:" +str(coef_ue) if coef_ue
else "ce module n'a pas de coef. dans cette UE"
})</span>
<span class="eval_coef_ue_titre">{ue.titre}</span>
""",
"allow_null": False,
# ok si poids nul ou coef vers l'UE nul:
"validator": lambda val, field: (not val)
or ue_coef_dict.get(int(field[len("poids_") :]), 0.0) != 0,
"enabled": (coef_ue != 0 or initvalues[f"poids_{ue.id}"] != 0.0)
and can_edit_poids,
},
),
)
tf = TrivialFormulator(
request.base_url,
vals,
form,
cancelbutton="Annuler",
submitlabel=submitlabel,
initvalues=initvalues,
readonly=False,
)
dest_url = url_for(
"notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id
)
if tf[0] == 0:
head = html_sco_header.sco_header(page_title=page_title)
return (
head
+ "\n".join(H)
+ "\n"
+ tf[1]
+ render_template("scodoc/help/evaluations.j2", is_apc=is_apc)
+ html_sco_header.sco_footer()
)
elif tf[0] == -1:
return flask.redirect(dest_url)
else:
# form submission
if tf[2]["visibulletinlist"]:
tf[2]["visibulletin"] = True
else:
tf[2]["visibulletin"] = False
if edit:
sco_evaluation_db.do_evaluation_edit(tf[2])
else:
# création d'une evaluation (via fonction ScoDoc7)
evaluation_id = sco_evaluation_db.do_evaluation_create(**tf[2])
if is_apc:
# Set poids
evaluation = models.Evaluation.query.get(evaluation_id)
for ue in sem_ues:
evaluation.set_ue_poids(ue, tf[2][f"poids_{ue.id}"])
db.session.add(evaluation)
db.session.commit()
return flask.redirect(dest_url)