diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py
index 9e45d0ba770377a92b5cf2693b25c04fc69432fa..7839f7f75a46eb29912d82506b15a0f7ae5d918d 100644
--- a/app/scodoc/sco_page_etud.py
+++ b/app/scodoc/sco_page_etud.py
@@ -108,13 +108,15 @@ def _menu_scolarite(
and authuser.has_permission(Permission.EtudInscrit)
and not locked
)
- note_enabled = etat_inscription == scu.INSCRIT and (
- etat_inscription != scu.DEMISSION
+ note_enabled = (
+ (etat_inscription == scu.INSCRIT)
+ and (not locked)
and (
authuser.has_permission(Permission.EditAllNotes)
or (authuser.id in [resp.id for resp in formsemestre.responsables])
)
)
+
items = [
{
"title": dem_title,
@@ -171,7 +173,7 @@ def _menu_scolarite(
"enabled": authuser.has_permission(Permission.EtudInscrit),
},
{
- "title": "Editer toutes les notes",
+ "title": "Éditer toutes les notes",
"endpoint": "notes.form_saisie_notes_par_etu",
"args": {"etu_id": etudid, "semestre_id": formsemestre.id},
"enabled": note_enabled,
diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py
index 0e65b1dca4c1bf90e65f5e68b3d069f52dbc3fd7..54933d7a526a4fc3299f4940713800509b9991fc 100644
--- a/app/scodoc/sco_saisie_notes.py
+++ b/app/scodoc/sco_saisie_notes.py
@@ -42,7 +42,6 @@ from app.comp.res_compat import NotesTableCompat
from app.models import (
Evaluation,
FormSemestre,
- Module,
ModuleImpl,
ScolarNews,
Assiduite,
@@ -793,13 +792,8 @@ class DataModimpl:
moduleimpl_id=modimpl.id,
)
self.link = (
- f'<a class="stdlink" href="{href}">{self.module_code} {self.titre}</a>.'
+ f'<a class="stdlink" href="{href}">{self.module_code} {self.titre}</a>'
)
- # self.cursus = {}
- formation_id = modimpl.formsemestre.formation_id
- # for ins in etudiant.inscriptions():
- # if ins.formsemestre.formation_id == formation_id:
- # self.cursus[ins.formsemestre.id] = ins.formsemestre
def add_eval(self, data_eval: DataSection):
self.data_evals.append(data_eval)
@@ -814,11 +808,8 @@ class DataEval:
self.evaluation_id = evaluation.id
self.description = evaluation.description
self.note_min, self.note_max = evaluation.get_bounds()
- self.evaluation_type: EvaluationType = evaluation.evaluation_type
+ self.evaluation_type: int = evaluation.evaluation_type
self.note = get_evaluation_etud_note(evaluation, etudid)
- # self.data_debut = evaluation.date_debut
- # self.date_fin = evaluation.date_fin
- # self.ue_poids = evaluation.get_ue_poids_dict()
self.note_value = (
""
if self.note is None
@@ -840,8 +831,9 @@ class DataEval:
"""
def forminput(self) -> str:
- return f"""<input type="text" size="5" id="formnotes_note_{self.evaluation_id}"
- class="note" onkeypress="return enter_focus_next(this, event);"
+ "Le champ html input"
+ return f"""<input type="text" size="5" id="formnotes_note_{self.evaluation_id}"
+ class="note" onkeypress="return enter_focus_next(this, event);"
note_min="{self.note_min}" note_max="{self.note_max}" evaluation_id="{self.evaluation_id}"
original-value="{self.note_value}" value="{self.note_value}" />"""
@@ -854,6 +846,7 @@ class DataEval:
)
def history(self):
+ "le span html avec le menu historique des saisies"
return f"""<span id="formnotes_hist_{self.evaluation_id}">
{get_note_history_menu(self.evaluation.id, self.etudid)}</span>"""
@@ -862,8 +855,9 @@ class DataForm:
"""
Class qui décrit les données d'une page de saisie par étudiant:
decision_jury: bool = indique si une décision de jury a été prise pour cet étudiant
- data_sections: dict[ModuleTYpe, DataSection] = pour chaque section (ressource, SAE, Malus, Standard),
- la liste de ses modimpl concernés
+ data_sections: dict[ModuleTYpe, DataSection] =
+ pour chaque section (ressource, SAE, Malus, Standard),
+ la liste de ses modimpl concernés
"""
def __init__(self, etudiant: Identite, semestre: FormSemestre, decision_jury: bool):
@@ -872,10 +866,8 @@ class DataForm:
self.decision_jury = decision_jury
self.data_sections = {}
- def add_section(self, data_section: DataSection):
- self.data_sections.append(data_section)
-
def link_to_etudiant(self) -> str:
+ "Lien html vers le bulletin de l'étudiant"
link: str = url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
@@ -897,7 +889,7 @@ def get_data(semestre: FormSemestre, etudiant: Identite) -> DataForm:
for module_type in ModuleType:
data.data_sections[module_type] = DataSection(module_type)
else:
- for module_type in [ ModuleType.STANDARD, ModuleType.MALUS]:
+ for module_type in [ModuleType.STANDARD, ModuleType.MALUS]:
data.data_sections[module_type] = DataSection(module_type)
for modimpl in semestre.modimpls_sorted:
module_type = modimpl.module.module_type
@@ -909,30 +901,33 @@ def get_data(semestre: FormSemestre, etudiant: Identite) -> DataForm:
return data
-def saisie_notes_par_etu(semestre: FormSemestre, etudiant: Identite):
+def saisie_notes_par_etu(formsemestre: FormSemestre, etud: Identite):
+ "Formulaire de saisie de toutes les notes d'un semestre pour un étudiant"
# Check access
# (admin, respformation, and responsable_id)
- locked = not semestre.etat
- # readonly peut être uinutile si l accès à cette page n est pas autorisée au public
- readonly = locked or not (
+ if not formsemestre.etat or not (
current_user.has_permission(Permission.EditAllNotes)
- or (current_user in semestre.responsables)
- )
- inscriptions = etudiant.formsemestre_inscriptions
- inscription = None
- for ins in inscriptions:
- if ins.formsemestre_id == semestre.id:
- inscription = ins
- # assert ins is not None
- if ins is None:
- raise ScoValueError("Tentative d accès à une inscription inexistante") # bug
- if ins.etat != scu.INSCRIT:
- raise ValueError(
- "Etudiant démissionnaire ou défaillant"
- ) # ne devrait pas être accessible. bug
- "Formulaire de saisie de note centré sur l'étudiant"
- data = get_data(semestre, etudiant)
- if semestre.formation.is_apc():
+ or (current_user.id in [resp.id for resp in formsemestre.responsables])
+ ):
+ return render_template(
+ "sco_page.j2",
+ content=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>Retour à la fiche de {etud.html_link_fiche()}
+ </p>
+ """,
+ sco=ScoData(formsemestre=formsemestre, etud=etud),
+ )
+ inscription = formsemestre.etuds_inscriptions.get(etud.id)
+ if inscription is None:
+ raise ScoValueError("Étudiant non inscrit à ce semestre")
+ if inscription.etat != scu.INSCRIT:
+ raise ScoValueError("Étudiant démissionnaire ou défaillant")
+ data = get_data(formsemestre, etud)
+ if formsemestre.formation.is_apc():
module_type_order = [
ModuleType.RESSOURCE,
ModuleType.SAE,
@@ -948,18 +943,15 @@ def saisie_notes_par_etu(semestre: FormSemestre, etudiant: Identite):
"etud/saisie_notes_par_etu.j2",
title="Saisie notes par étudiant",
javascripts=["js/saisie_notes.js", "js/saisie_notes_par_etu.js"],
- sco=ScoData(formsemestre=semestre),
+ sco=ScoData(formsemestre=formsemestre, etud=etud),
data=data,
- readonly=readonly,
get_note_history_menu=get_note_history_menu,
module_type_order=module_type_order,
)
-# US 1030 - FIN
-
-
-def get_evaluation_etud_note(evaluation: Evaluation, etudid: int) -> float | str | None:
+def get_evaluation_etud_note(evaluation: Evaluation, etudid: int) -> NotesNotes | None:
+ "La note d'un étudiant à l'évaluation, None si pas de note"
return NotesNotes.query.filter_by(
evaluation_id=evaluation.id, etudid=etudid
).first()
diff --git a/app/templates/etud/saisie_notes_par_etu.j2 b/app/templates/etud/saisie_notes_par_etu.j2
index b0c6d6948cfb6bc228862096c462a73f1cfd2b04..df358c3b7d5df7c5fa573c5c8725de1e6225b868 100644
--- a/app/templates/etud/saisie_notes_par_etu.j2
+++ b/app/templates/etud/saisie_notes_par_etu.j2
@@ -8,6 +8,8 @@
display:inline-grid;
grid-template-columns: 40px 40px minmax(350px, 1fr) 70px minmax(200px, 1fr);
align-items: baseline;
+ border: 1px solid black;
+ padding: 5px;
}
#formnotes .section {
grid-column: 1 / span 5;
@@ -23,6 +25,37 @@
}
#formnotes .label {
grid-column: 3;
+ display: flex;
+ align-items: center;
+ white-space: nowrap;
+ }
+
+ #formnotes .label:hover {
+ background-color: lightgray;
+ }
+ #formnotes .label a::before {
+ content: "•";
+ }
+ #formnotes .label a {
+ color: black;
+ flex-shrink: 0;
+ margin-right: 0.5em;
+ }
+ .dots {
+ flex-grow: 1;
+ border-bottom: 1px dotted #000; /* fallback look */
+ background-image: repeating-linear-gradient(to right, black 0 1px, transparent 1px 4px);
+ background-position: bottom;
+ background-repeat: repeat-x;
+ background-size: 4px 1px;
+ height: 1em;
+ margin-right: 0.5em;
+ }
+ #formnotes .commentaire {
+ grid-column: 3;
+ text-align: right;
+ white-space: nowrap;
+ margin-right: 0.5em;
}
#formnotes .input { grid-column: 4 }
#formnotes .history { grid-column: 5;}
@@ -33,7 +66,6 @@
{% block app_content %}
<div id="saisie_notes_par_etu" class="table-container">
- <span class="titre">{{title}}</span>
<h3> Saisie des notes de l'étudiant : {{data.link_to_etudiant() | safe }} </h3>
{% if data.decision_jury %}
<div class="warning">
@@ -48,7 +80,7 @@
<input type="hidden" id="formnotes_formsemestre_id" value="{{data.semestre.id}}" />
{% if data.decision_jury %}
{% endif %}
- <div class="label">commentaire</div>
+ <div class="commentaire">commentaire :</div>
<div class="long_input">
<input type="text" id="formnotes_comment" name="comment" class="input" size="44" onkeypress="return enter_focus_next(this, event);">
</div>
@@ -60,6 +92,7 @@
{% for data_eval in data_modimpl.data_evals %}
<div class="label">
{{ data_eval.titre() | safe }}
+ <span class="dots"></span>
{# <input type="hidden" id="date_debut_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_debut }}">#}
{# <input type="hidden" id="date_fin_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_fin }}">#}
{# {% for ue in data_eval.ue_poids %}#}
@@ -95,7 +128,7 @@
</form>
</div>
<div class="sco_help">
- <p>
+ <p>
Les modifications sont enregistrées au fur et à mesure.
</p>
<h4> Codes spéciaux: </h4>
diff --git a/app/views/notes.py b/app/views/notes.py
index 7dfa03bd23ff942b820e08bf4a21d4231460cf6d..8f5929a270c991e4dc5a292e244886b80831d465 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -1816,20 +1816,14 @@ def form_saisie_notes(evaluation_id: int):
return sco_saisie_notes.saisie_notes(evaluation, group_ids)
-# modification US 1030 - DEB
-
-
-@bp.route("/form_saisie_notes_par_etu/<int:semestre_id>/<int:etu_id>")
+@bp.route("/form_saisie_notes_par_etu/<int:formsemestre_id>/<int:etudid>")
@scodoc
@permission_required(Permission.EnsView)
-def form_saisie_notes_par_etu(etu_id: int, semestre_id: int):
+def form_saisie_notes_par_etu(etudid: int, formsemestre_id: int):
"Formulaire de saisie des notes centré sur l'étudiant"
- etudiant = Identite.get_etud(etu_id)
- semestre = FormSemestre.get_formsemestre(semestre_id)
- return sco_saisie_notes.saisie_notes_par_etu(semestre, etudiant)
-
-
-# modification US 1030 - FIN
+ formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
+ etud = Identite.get_etud(etudid)
+ return sco_saisie_notes.saisie_notes_par_etu(formsemestre, etud)
@bp.route("/formsemestre_import_notes/<int:formsemestre_id>", methods=["GET", "POST"])