Merge pull request 'master' (#1) from ScoDoc/ScoDoc:master into master

Reviewed-on: lehmann/ScoDoc-Front#1
This commit is contained in:
Sébastien Lehmann 2021-12-19 18:36:06 +01:00
commit 19c736c894
20 changed files with 196 additions and 110 deletions

View File

@ -149,9 +149,9 @@ class ResultatsSemestreBUT:
"""dict synthèse résultats des modules indiqués, """dict synthèse résultats des modules indiqués,
avec évaluations de chacun.""" avec évaluations de chacun."""
d = {} d = {}
etud_idx = self.etud_index[etud.id] # etud_idx = self.etud_index[etud.id]
for mi in modimpls: for mi in modimpls:
mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id) # mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
# # moyennes indicatives (moyennes de moyennes d'UE) # # moyennes indicatives (moyennes de moyennes d'UE)
# try: # try:
# moyennes_etuds = np.nan_to_num( # moyennes_etuds = np.nan_to_num(

View File

@ -111,13 +111,14 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
N'utilise pas de cache ScoDoc. N'utilise pas de cache ScoDoc.
""" """
# L'index du dataframe est la liste des étudiants inscrits au semestre, sans les démissionnaires # L'index du dataframe est la liste des étudiants inscrits au semestre,
etudids = { # sans les démissionnaires
etudids = [
e.etudid e.etudid
for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.get_inscrits( for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.get_inscrits(
include_dem=False include_dem=False
) )
} ]
evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).all() evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).all()
# --- Calcul nombre d'inscrits pour détermnier si évaluation "complete": # --- Calcul nombre d'inscrits pour détermnier si évaluation "complete":
if evaluations: if evaluations:
@ -128,7 +129,8 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
nb_inscrits_module = len(inscrits_module) nb_inscrits_module = len(inscrits_module)
else: else:
nb_inscrits_module = 0 nb_inscrits_module = 0
evals_notes = pd.DataFrame(index=etudids, dtype=float) # empty df with all students # empty df with all students:
evals_notes = pd.DataFrame(index=etudids, dtype=float)
evaluations_completes = [] evaluations_completes = []
for evaluation in evaluations: for evaluation in evaluations:
eval_df = pd.read_sql_query( eval_df = pd.read_sql_query(

View File

@ -76,11 +76,11 @@ class Evaluation(db.Model):
db.session.add(copy) db.session.add(copy)
return copy return copy
def set_ue_poids(self, ue, poids: float): def set_ue_poids(self, ue, poids: float) -> None:
"""Set poids évaluation vers cette UE""" """Set poids évaluation vers cette UE"""
self.update_ue_poids_dict({ue.id: poids}) self.update_ue_poids_dict({ue.id: poids})
def set_ue_poids_dict(self, ue_poids_dict: dict): def set_ue_poids_dict(self, ue_poids_dict: dict) -> None:
"""set poids vers les UE (remplace existants) """set poids vers les UE (remplace existants)
ue_poids_dict = { ue_id : poids } ue_poids_dict = { ue_id : poids }
""" """
@ -91,16 +91,23 @@ class Evaluation(db.Model):
self.ue_poids = L self.ue_poids = L
self.moduleimpl.invalidate_evaluations_poids() # inval cache self.moduleimpl.invalidate_evaluations_poids() # inval cache
def update_ue_poids_dict(self, ue_poids_dict: dict): def update_ue_poids_dict(self, ue_poids_dict: dict) -> None:
"""update poids vers UE (ajoute aux existants)""" """update poids vers UE (ajoute aux existants)"""
current = self.get_ue_poids_dict() current = self.get_ue_poids_dict()
current.update(ue_poids_dict) current.update(ue_poids_dict)
self.set_ue_poids_dict(current) self.set_ue_poids_dict(current)
def get_ue_poids_dict(self): def get_ue_poids_dict(self) -> dict:
"""returns { ue_id : poids }""" """returns { ue_id : poids }"""
return {p.ue.id: p.poids for p in self.ue_poids} return {p.ue.id: p.poids for p in self.ue_poids}
def get_ue_poids_str(self) -> str:
"""string describing poids, for excel cells and pdfs
Note: si les poids ne sont pas initialisés (poids par défaut),
ils ne sont pas affichés.
"""
return ", ".join([f"{p.ue.acronyme}: {p.poids}" for p in self.ue_poids])
class EvaluationUEPoids(db.Model): class EvaluationUEPoids(db.Model):
"""Poids des évaluations (BUT) """Poids des évaluations (BUT)

View File

@ -33,6 +33,7 @@ import json
from app.but import bulletin_but from app.but import bulletin_but
from app.models.formsemestre import FormSemestre from app.models.formsemestre import FormSemestre
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
@ -86,6 +87,7 @@ def formsemestre_bulletinetud_published_dict(
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
etud = Identite.query.get(etudid)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
@ -139,6 +141,11 @@ def formsemestre_bulletinetud_published_dict(
if not published: if not published:
return d # stop ! return d # stop !
etat_inscription = etud.etat_inscription(formsemestre.id)
if etat_inscription != scu.INSCRIT:
d.update(dict_decision_jury(etudid, formsemestre_id, with_decisions=True))
return d
# Groupes: # Groupes:
partitions = sco_groups.get_partitions_list(formsemestre_id, with_default=False) partitions = sco_groups.get_partitions_list(formsemestre_id, with_default=False)
partitions_etud_groups = {} # { partition_id : { etudid : group } } partitions_etud_groups = {} # { partition_id : { etudid : group } }

View File

@ -104,7 +104,10 @@ def do_ue_create(args):
# check duplicates # check duplicates
ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]}) ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]})
if ues: if ues:
raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) raise ScoValueError(
f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé !
(chaque UE doit avoir un acronyme unique dans la formation)"""
)
# create # create
ue_id = _ueEditor.create(cnx, args) ue_id = _ueEditor.create(cnx, args)
@ -471,7 +474,7 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
raise ScoValueError("invalid formation_id") raise ScoValueError("invalid formation_id")
parcours = formation.get_parcours() parcours = formation.get_parcours()
is_apc = parcours.APC_SAE is_apc = parcours.APC_SAE
locked = sco_formations.formation_has_locked_sems(formation_id) locked = formation.has_locked_sems()
if semestre_idx == "all": if semestre_idx == "all":
semestre_idx = None semestre_idx = None
else: else:
@ -541,7 +544,7 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
if locked: if locked:
H.append( H.append(
f"""<p class="help">Cette formation est verrouillée car f"""<p class="help">Cette formation est verrouillée car
{len(locked)} semestres verrouillés s'y réferent. des semestres verrouillés s'y réferent.
Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module), Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module),
vous devez: vous devez:
</p> </p>
@ -1146,7 +1149,10 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False):
new_acro = args["acronyme"] new_acro = args["acronyme"]
ues = ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro}) ues = ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro})
if ues and ues[0]["ue_id"] != ue_id: if ues and ues[0]["ue_id"] != ue_id:
raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) raise ScoValueError(
f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé !
(chaque UE doit avoir un acronyme unique dans la formation)"""
)
# On ne peut pas supprimer le code UE: # On ne peut pas supprimer le code UE:
if "ue_code" in args and not args["ue_code"]: if "ue_code" in args and not args["ue_code"]:

View File

@ -109,30 +109,10 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
nb_inscrits = len( nb_inscrits = len(
sco_groups.do_evaluation_listeetuds_groups(evaluation_id, getallstudents=True) sco_groups.do_evaluation_listeetuds_groups(evaluation_id, getallstudents=True)
) )
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes( etuds_notes_dict = sco_evaluation_db.do_evaluation_get_all_notes(
evaluation_id evaluation_id
) # { etudid : value } ) # { etudid : note }
notes = [x["value"] for x in NotesDB.values()]
nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
nb_att = len([x for x in notes if x == scu.NOTES_ATTENTE])
moy_num, median_num, mini_num, maxi_num = notes_moyenne_median_mini_maxi(notes)
if moy_num is None:
median, moy = "", ""
median_num, moy_num = None, None
mini, maxi = "", ""
mini_num, maxi_num = None, None
else:
median = scu.fmt_note(median_num)
moy = scu.fmt_note(moy_num)
mini = scu.fmt_note(mini_num)
maxi = scu.fmt_note(maxi_num)
# cherche date derniere modif note
if len(NotesDB):
t = [x["date"] for x in NotesDB.values()]
last_modif = max(t)
else:
last_modif = None
# ---- Liste des groupes complets et incomplets # ---- Liste des groupes complets et incomplets
E = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})[0] E = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
@ -163,8 +143,32 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
# Nombre de notes valides d'étudiants inscrits au module # Nombre de notes valides d'étudiants inscrits au module
# (car il peut y avoir des notes d'étudiants désinscrits depuis l'évaluation) # (car il peut y avoir des notes d'étudiants désinscrits depuis l'évaluation)
nb_notes = len(insmodset.intersection(NotesDB)) etudids_avec_note = insmodset.intersection(etuds_notes_dict)
nb_notes_total = len(NotesDB) nb_notes = len(etudids_avec_note)
# toutes saisies, y compris chez des non-inscrits:
nb_notes_total = len(etuds_notes_dict)
notes = [etuds_notes_dict[etudid]["value"] for etudid in etudids_avec_note]
nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
nb_att = len([x for x in notes if x == scu.NOTES_ATTENTE])
moy_num, median_num, mini_num, maxi_num = notes_moyenne_median_mini_maxi(notes)
if moy_num is None:
median, moy = "", ""
median_num, moy_num = None, None
mini, maxi = "", ""
mini_num, maxi_num = None, None
else:
median = scu.fmt_note(median_num)
moy = scu.fmt_note(moy_num)
mini = scu.fmt_note(mini_num)
maxi = scu.fmt_note(maxi_num)
# cherche date derniere modif note
if len(etuds_notes_dict):
t = [x["date"] for x in etuds_notes_dict.values()]
last_modif = max(t)
else:
last_modif = None
# On considere une note "manquante" lorsqu'elle n'existe pas # On considere une note "manquante" lorsqu'elle n'existe pas
# ou qu'elle est en attente (ATT) # ou qu'elle est en attente (ATT)
@ -181,8 +185,8 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
groups[group["group_id"]] = group groups[group["group_id"]] = group
# #
isMissing = False isMissing = False
if i["etudid"] in NotesDB: if i["etudid"] in etuds_notes_dict:
val = NotesDB[i["etudid"]]["value"] val = etuds_notes_dict[i["etudid"]]["value"]
if val == scu.NOTES_ATTENTE: if val == scu.NOTES_ATTENTE:
isMissing = True isMissing = True
TotalNbAtt += 1 TotalNbAtt += 1

View File

@ -230,9 +230,10 @@ def formation_import_xml(doc: str, import_tags=True):
ue_id = sco_edit_ue.do_ue_create(ue_info[1]) ue_id = sco_edit_ue.do_ue_create(ue_info[1])
if xml_ue_id: if xml_ue_id:
ues_old2new[xml_ue_id] = ue_id ues_old2new[xml_ue_id] = ue_id
ue_reference = int(ue_info[1].get("reference")) # élément optionnel présent dans les exports BUT:
ue_reference = ue_info[1].get("reference")
if ue_reference: if ue_reference:
ue_reference_to_id[ue_reference] = ue_id ue_reference_to_id[int(ue_reference)] = ue_id
# -- create matieres # -- create matieres
for mat_info in ue_info[2]: for mat_info in ue_info[2]:
assert mat_info[0] == "matiere" assert mat_info[0] == "matiere"

View File

@ -66,7 +66,9 @@ def do_formsemestre_inscription_list(*args, **kw):
def do_formsemestre_inscription_listinscrits(formsemestre_id): def do_formsemestre_inscription_listinscrits(formsemestre_id):
"""Liste les inscrits (état I) à ce semestre et cache le résultat""" """Liste les inscrits (état I) à ce semestre et cache le résultat.
Result: [ { "etudid":, "formsemestre_id": , "etat": , "etape": }]
"""
r = sco_cache.SemInscriptionsCache.get(formsemestre_id) r = sco_cache.SemInscriptionsCache.get(formsemestre_id)
if r is None: if r is None:
# retreive list # retreive list

View File

@ -1210,6 +1210,7 @@ def formsemestre_tableau_modules(
) )
if mod.module_type in ( if mod.module_type in (
None, # ne devrait pas être nécessaire car la migration a remplacé les NULLs
ModuleType.STANDARD, ModuleType.STANDARD,
ModuleType.RESSOURCE, ModuleType.RESSOURCE,
ModuleType.SAE, ModuleType.SAE,
@ -1249,7 +1250,7 @@ def formsemestre_tableau_modules(
% (modimpl["moduleimpl_id"], nb_malus_notes) % (modimpl["moduleimpl_id"], nb_malus_notes)
) )
else: else:
raise ValueError("Invalid module_type") # a bug raise ValueError(f"Invalid module_type {mod.module_type}") # a bug
H.append("</td></tr>") H.append("</td></tr>")
return "\n".join(H) return "\n".join(H)

View File

@ -35,7 +35,6 @@ Optimisation possible:
""" """
import collections import collections
import operator import operator
import re
import time import time
from xml.etree import ElementTree from xml.etree import ElementTree
@ -45,6 +44,7 @@ import flask
from flask import g, request from flask import g, request
from flask import url_for, make_response from flask import url_for, make_response
from app import db
from app.models.groups import Partition from app.models.groups import Partition
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb

View File

@ -32,6 +32,7 @@ import flask
from flask import url_for, g, request from flask import url_for, g, request
from app import models from app import models
from app.models.evaluations import Evaluation
from app.models.moduleimpls import ModuleImpl from app.models.moduleimpls import ModuleImpl
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
@ -53,35 +54,34 @@ from app.scodoc.gen_tables import GenTable
from app.scodoc.htmlutils import histogram_notes from app.scodoc.htmlutils import histogram_notes
def do_evaluation_listenotes(): def do_evaluation_listenotes(
evaluation_id=None, moduleimpl_id=None, format="html"
) -> str:
""" """
Affichage des notes d'une évaluation Affichage des notes d'une évaluation (si evaluation_id)
ou de toutes les évaluations d'un module (si moduleimpl_id)
args: evaluation_id ou moduleimpl_id
(si moduleimpl_id, affiche toutes les évaluations du module)
""" """
mode = None mode = None
vals = scu.get_request_args() if moduleimpl_id:
if "evaluation_id" in vals:
evaluation_id = int(vals["evaluation_id"])
mode = "eval"
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
moduleimpl_id = int(vals["moduleimpl_id"])
mode = "module" mode = "module"
evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if not mode: elif evaluation_id:
mode = "eval"
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
else:
raise ValueError("missing argument: evaluation or module") raise ValueError("missing argument: evaluation or module")
if not evals: if not evals:
return "<p>Aucune évaluation !</p>" return "<p>Aucune évaluation !</p>"
format = vals.get("format", "html")
E = evals[0] # il y a au moins une evaluation E = evals[0] # il y a au moins une evaluation
modimpl = ModuleImpl.query.get(E["moduleimpl_id"])
# description de l'evaluation # description de l'evaluation
if mode == "eval": if mode == "eval":
H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)] H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)]
page_title = f"Notes {E['description'] or modimpl.module.code}"
else: else:
H = [] H = []
page_title = f"Notes {modimpl.module.code}"
# groupes # groupes
groups = sco_groups.do_evaluation_listegroupes( groups = sco_groups.do_evaluation_listegroupes(
E["evaluation_id"], include_default=True E["evaluation_id"], include_default=True
@ -187,7 +187,7 @@ def do_evaluation_listenotes():
is_submitted=True, # toujours "soumis" (démarre avec liste complète) is_submitted=True, # toujours "soumis" (démarre avec liste complète)
) )
if tf[0] == 0: if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] return "\n".join(H) + "\n" + tf[1], page_title
elif tf[0] == -1: elif tf[0] == -1:
return flask.redirect( return flask.redirect(
"%s/Notes/moduleimpl_status?moduleimpl_id=%s" "%s/Notes/moduleimpl_status?moduleimpl_id=%s"
@ -198,16 +198,19 @@ def do_evaluation_listenotes():
note_sur_20 = tf[2]["note_sur_20"] note_sur_20 = tf[2]["note_sur_20"]
hide_groups = tf[2]["hide_groups"] hide_groups = tf[2]["hide_groups"]
with_emails = tf[2]["with_emails"] with_emails = tf[2]["with_emails"]
return _make_table_notes( return (
tf[1], _make_table_notes(
evals, tf[1],
format=format, evals,
note_sur_20=note_sur_20, format=format,
anonymous_listing=anonymous_listing, note_sur_20=note_sur_20,
group_ids=tf[2]["group_ids"], anonymous_listing=anonymous_listing,
hide_groups=hide_groups, group_ids=tf[2]["group_ids"],
with_emails=with_emails, hide_groups=hide_groups,
mode=mode, with_emails=with_emails,
mode=mode,
),
page_title,
) )
@ -393,6 +396,7 @@ def _make_table_notes(
key_mgr, key_mgr,
note_sur_20, note_sur_20,
keep_numeric, keep_numeric,
format=format,
) )
columns_ids.append(e["evaluation_id"]) columns_ids.append(e["evaluation_id"])
# #
@ -406,7 +410,7 @@ def _make_table_notes(
# Si module, ajoute la (les) "moyenne(s) du module: # Si module, ajoute la (les) "moyenne(s) du module:
if mode == "module": if mode == "module":
if len(evals) > 1: if len(evals) > 1:
# Moyenne de l'étudant dans le module # Moyenne de l'étudiant dans le module
# Affichée même en APC à titre indicatif # Affichée même en APC à titre indicatif
_add_moymod_column( _add_moymod_column(
sem["formsemestre_id"], sem["formsemestre_id"],
@ -635,6 +639,7 @@ def _add_eval_columns(
K, K,
note_sur_20, note_sur_20,
keep_numeric, keep_numeric,
format="html",
): ):
"""Add eval e""" """Add eval e"""
nb_notes = 0 nb_notes = 0
@ -643,6 +648,7 @@ def _add_eval_columns(
sum_notes = 0 sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
evaluation_id = e["evaluation_id"] evaluation_id = e["evaluation_id"]
e_o = Evaluation.query.get(evaluation_id) # XXX en attendant ré-écriture
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
for row in rows: for row in rows:
etudid = row["etudid"] etudid = row["etudid"]
@ -704,9 +710,12 @@ def _add_eval_columns(
row_coefs[evaluation_id] = "coef. %s" % e["coefficient"] row_coefs[evaluation_id] = "coef. %s" % e["coefficient"]
if is_apc: if is_apc:
row_poids[evaluation_id] = _mini_table_eval_ue_poids( if format == "html":
evaluation_id, evals_poids, ues row_poids[evaluation_id] = _mini_table_eval_ue_poids(
) evaluation_id, evals_poids, ues
)
else:
row_poids[evaluation_id] = e_o.get_ue_poids_str()
if note_sur_20: if note_sur_20:
nmax = 20.0 nmax = 20.0
else: else:

View File

@ -31,9 +31,10 @@ import time
from flask import g, url_for from flask import g, url_for
from flask_login import current_user from flask_login import current_user
from app.auth.models import User
from app.auth.models import User
from app.models import ModuleImpl from app.models import ModuleImpl
from app.models.evaluations import Evaluation
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -391,8 +392,9 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
) )
H.append("""<table class="moduleimpl_evaluations">""") H.append("""<table class="moduleimpl_evaluations">""")
eval_index = len(mod_evals) - 1 eval_index = len(mod_evals) - 1
first = True first_eval = True
for eval in mod_evals: for eval in mod_evals:
evaluation = Evaluation.query.get(eval["evaluation_id"]) # TODO unifier
etat = sco_evaluations.do_evaluation_etat( etat = sco_evaluations.do_evaluation_etat(
eval["evaluation_id"], eval["evaluation_id"],
partition_id=partition_id, partition_id=partition_id,
@ -406,9 +408,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
else: else:
tr_class = "mievr" tr_class = "mievr"
tr_class_1 = "mievr" tr_class_1 = "mievr"
if first: if not first_eval:
first = False
else:
H.append("""<tr><td colspan="8">&nbsp;</td></tr>""") H.append("""<tr><td colspan="8">&nbsp;</td></tr>""")
tr_class_1 += " mievr_spaced" tr_class_1 += " mievr_spaced"
H.append("""<tr class="%s"><td class="mievr_tit" colspan="8">""" % tr_class_1) H.append("""<tr class="%s"><td class="mievr_tit" colspan="8">""" % tr_class_1)
@ -586,19 +586,35 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
H.append("""</td></tr>""") H.append("""</td></tr>""")
# #
if etat["nb_notes"] == 0: if etat["nb_notes"] == 0:
H.append("""<tr class="%s"><td colspan="8">&nbsp;""" % tr_class) H.append("""<tr class="%s"><td></td>""" % tr_class)
H.append("""</td></tr>""") if modimpl.module.is_apc():
H.append(
f"""<td colspan="7" class="eval_poids">{
evaluation.get_ue_poids_str()}</td>"""
)
else:
H.append('<td colspan="7"></td>')
H.append("""</tr>""")
else: # il y a deja des notes saisies else: # il y a deja des notes saisies
gr_moyennes = etat["gr_moyennes"] gr_moyennes = etat["gr_moyennes"]
first_group = True
for gr_moyenne in gr_moyennes: for gr_moyenne in gr_moyennes:
H.append("""<tr class="%s">""" % tr_class) H.append("""<tr class="%s">""" % tr_class)
H.append("""<td colspan="2">&nbsp;</td>""") H.append("""<td>&nbsp;</td>""")
if first_group and modimpl.module.is_apc():
H.append(
f"""<td class="eval_poids" colspan="3">{
evaluation.get_ue_poids_str()}</td>"""
)
else:
H.append("""<td colspan="3"></td>""")
first_group = False
if gr_moyenne["group_name"] is None: if gr_moyenne["group_name"] is None:
name = "Tous" # tous name = "Tous" # tous
else: else:
name = "Groupe %s" % gr_moyenne["group_name"] name = "Groupe %s" % gr_moyenne["group_name"]
H.append( H.append(
"""<td colspan="5" class="mievr_grtit">%s &nbsp;</td><td>""" % name """<td colspan="3" class="mievr_grtit">%s &nbsp;</td><td>""" % name
) )
if gr_moyenne["gr_nb_notes"] > 0: if gr_moyenne["gr_nb_notes"] > 0:
H.append("%(gr_moy)s" % gr_moyenne) H.append("%(gr_moy)s" % gr_moyenne)
@ -637,6 +653,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
H.append("""</a>""") H.append("""</a>""")
H.append("</span>") H.append("</span>")
H.append("""</td></tr>""") H.append("""</td></tr>""")
first_eval = False
# #
if caneditevals or not sem["etat"]: if caneditevals or not sem["etat"]:

View File

@ -37,7 +37,8 @@ from flask import make_response
from app import log from app import log
from app.but import bulletin_but from app.but import bulletin_but
from app.models import FormSemestre, UniteEns, Module, ModuleImpl from app.models import FormSemestre
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
@ -937,6 +938,9 @@ def _formsemestre_recapcomplet_json(
:param force_publishing: donne les bulletins même si non "publiés sur portail" :param force_publishing: donne les bulletins même si non "publiés sur portail"
:returns: dict, "", "json" :returns: dict, "", "json"
""" """
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
is_apc = formsemestre.formation.is_apc()
if xml_nodate: if xml_nodate:
docdate = "" docdate = ""
else: else:
@ -958,14 +962,18 @@ def _formsemestre_recapcomplet_json(
T = nt.get_table_moyennes_triees() T = nt.get_table_moyennes_triees()
for t in T: for t in T:
etudid = t[-1] etudid = t[-1]
bulletins.append( if is_apc:
sco_bulletins_json.formsemestre_bulletinetud_published_dict( etud = Identite.query.get(etudid)
r = bulletin_but.ResultatsSemestreBUT(formsemestre)
bul = r.bulletin_etud(etud, formsemestre)
else:
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre_id, formsemestre_id,
etudid, etudid,
force_publishing=force_publishing, force_publishing=force_publishing,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
) )
) bulletins.append(bul)
return J, "", "json" return J, "", "json"

View File

@ -408,7 +408,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
if not dialog_confirmed: if not dialog_confirmed:
nb_changed, nb_suppress, existing_decisions = notes_add( nb_changed, nb_suppress, existing_decisions = notes_add(
current_user, evaluation_id, notes, do_it=False current_user, evaluation_id, notes, do_it=False, check_inscription=False
) )
msg = ( msg = (
"<p>Confirmer la suppression des %d notes ? <em>(peut affecter plusieurs groupes)</em></p>" "<p>Confirmer la suppression des %d notes ? <em>(peut affecter plusieurs groupes)</em></p>"
@ -426,7 +426,11 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
# modif # modif
nb_changed, nb_suppress, existing_decisions = notes_add( nb_changed, nb_suppress, existing_decisions = notes_add(
current_user, evaluation_id, notes, comment="effacer tout" current_user,
evaluation_id,
notes,
comment="effacer tout",
check_inscription=False,
) )
assert nb_changed == nb_suppress assert nb_changed == nb_suppress
H = ["<p>%s notes supprimées</p>" % nb_suppress] H = ["<p>%s notes supprimées</p>" % nb_suppress]
@ -454,7 +458,14 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer() return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
def notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True) -> tuple: def notes_add(
user,
evaluation_id: int,
notes: list,
comment=None,
do_it=True,
check_inscription=True,
) -> tuple:
""" """
Insert or update notes Insert or update notes
notes is a list of tuples (etudid,value) notes is a list of tuples (etudid,value)
@ -475,7 +486,7 @@ def notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True) -
) )
} }
for (etudid, value) in notes: for (etudid, value) in notes:
if etudid not in inscrits: if check_inscription and (etudid not in inscrits):
raise NoteProcessError("etudiant non inscrit dans ce module") raise NoteProcessError("etudiant non inscrit dans ce module")
if not ((value is None) or (type(value) == type(1.0))): if not ((value is None) or (type(value) == type(1.0))):
raise NoteProcessError( raise NoteProcessError(

View File

@ -16,7 +16,7 @@ main{
--couleurPrincipale: rgb(240,250,255); --couleurPrincipale: rgb(240,250,255);
--couleurFondTitresUE: rgb(206,255,235); --couleurFondTitresUE: rgb(206,255,235);
--couleurFondTitresRes: rgb(125, 170, 255); --couleurFondTitresRes: rgb(125, 170, 255);
--couleurFondTitresSAE: rgb(255, 190, 69); --couleurFondTitresSAE: rgb(211, 255, 255);
--couleurSecondaire: #fec; --couleurSecondaire: #fec;
--couleurIntense: #c09; --couleurIntense: #c09;
--couleurSurlignage: rgba(232, 255, 132, 0.47); --couleurSurlignage: rgba(232, 255, 132, 0.47);

View File

@ -1476,6 +1476,9 @@ span.evalindex {
margin-left: 3px; margin-left: 3px;
} }
table.moduleimpl_evaluations td.eval_poids {
color:rgb(0, 0, 255);
}
/* Formulaire edition des partitions */ /* Formulaire edition des partitions */
form#editpart table { form#editpart table {

View File

@ -12,7 +12,7 @@
{% endblock %} {% endblock %}
{% block title %} {% block title %}
{% if title %}{{ title }} - ScoDoc{% else %}Welcome to ScoDoc{% endif %} {% if title %}{{ title }} - ScoDoc{% else %}ScoDoc{% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -31,9 +31,9 @@
{% endwith %} {% endwith %}
</div> </div>
{% if sco.sem %} {% if sco.sem %}
{% block formsemestre_header %} {% block formsemestre_header %}
{% include "formsemestre_header.html" %} {% include "formsemestre_header.html" %}
{% endblock %} {% endblock %}
{% endif %} {% endif %}
{% block app_content %} {% block app_content %}

View File

@ -285,6 +285,8 @@ def formsemestre_bulletinetud(
prefer_mail_perso=False, prefer_mail_perso=False,
code_nip=None, code_nip=None,
): ):
if not formsemestre_id:
flask.abort(404, "argument manquant: formsemestre_id")
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if formsemestre.formation.is_apc() and format != "oldjson": if formsemestre.formation.is_apc() and format != "oldjson":
if etudid: if etudid:
@ -299,7 +301,7 @@ def formsemestre_bulletinetud(
elif format == "html": elif format == "html":
return render_template( return render_template(
"but/bulletin.html", "but/bulletin.html",
title="Bulletin BUT", title=f"Bul. {etud.nom} - BUT",
bul_url=url_for( bul_url=url_for(
"notes.formsemestre_bulletinetud", "notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
@ -1726,27 +1728,33 @@ def evaluation_create(moduleimpl_id):
@scodoc7func @scodoc7func
def evaluation_listenotes(): def evaluation_listenotes():
"""Affichage des notes d'une évaluation""" """Affichage des notes d'une évaluation"""
if request.args.get("format", "html") == "html": evaluation_id = None
moduleimpl_id = None
vals = scu.get_request_args()
if "evaluation_id" in vals:
evaluation_id = int(vals["evaluation_id"])
mode = "eval"
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
moduleimpl_id = int(vals["moduleimpl_id"])
mode = "module"
format = vals.get("format", "html")
B, page_title = sco_liste_notes.do_evaluation_listenotes(
evaluation_id=evaluation_id, moduleimpl_id=moduleimpl_id, format=format
)
if format == "html":
H = html_sco_header.sco_header( H = html_sco_header.sco_header(
page_title=page_title,
cssstyles=["css/verticalhisto.css"], cssstyles=["css/verticalhisto.css"],
javascripts=["js/etud_info.js"], javascripts=["js/etud_info.js"],
init_qtip=True, init_qtip=True,
) )
F = html_sco_header.sco_footer() F = html_sco_header.sco_footer()
else:
H, F = "", ""
B = sco_liste_notes.do_evaluation_listenotes()
if H:
return H + B + F return H + B + F
else: else:
return B return B
sco_publish(
"/do_evaluation_listenotes",
sco_liste_notes.do_evaluation_listenotes,
Permission.ScoView,
)
sco_publish( sco_publish(
"/evaluation_list_operations", "/evaluation_list_operations",
sco_undo_notes.evaluation_list_operations, sco_undo_notes.evaluation_list_operations,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.1.10" SCOVERSION = "9.1.11"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -1,4 +1,4 @@
# -*- coding: UTF-8 -* # -*- coding: UTF-8 -*-
"""Application Flask: ScoDoc """Application Flask: ScoDoc