diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 44cf1fc1e..c920bbe44 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -141,7 +141,7 @@ class Assiduite(ScoDocModel): }""" def __repr__(self) -> str: - return f"" + return f"" @classmethod def create_assiduite( diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 669a4b031..8d6e02664 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -127,6 +127,12 @@ class Identite(models.ScoDocModel): cascade="all, delete-orphan", lazy="dynamic", ) + notes_log = db.relationship( + "NotesNotesLog", + backref="etudiant", + cascade="all, delete-orphan", + lazy="dynamic", + ) # Relations avec les assiduites et les justificatifs assiduites = db.relationship( "Assiduite", back_populates="etudiant", lazy="dynamic", cascade="all, delete" diff --git a/app/models/evaluations.py b/app/models/evaluations.py index cf998a56a..a27f43090 100644 --- a/app/models/evaluations.py +++ b/app/models/evaluations.py @@ -64,6 +64,13 @@ class Evaluation(models.ScoDocModel): cascade="all, delete-orphan", lazy="dynamic", ) + notes_log = db.relationship( + "NotesNotesLog", + backref="evaluation", + cascade="all, delete-orphan", + lazy="dynamic", + primaryjoin="Evaluation.id == foreign(NotesNotesLog.evaluation_id)", + ) ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True) _sco_dept_relations = ("ModuleImpl", "FormSemestre") # accès au dept_id diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 5820d20a3..3161c9cbc 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -499,11 +499,7 @@ class GenTable: H.append(caption) if self.base_url: H.append('') - if self.xls_link: - H.append( - f""" {scu.ICON_XLS}""" - ) + H.append(self.xls_export_button()) if self.xls_link and self.pdf_link: H.append(" ") if self.pdf_link: @@ -517,6 +513,15 @@ class GenTable: H.append(self.html_next_section) return "\n".join(H) + def xls_export_button(self) -> str: + "markup pour export excel" + return ( + f""" {scu.ICON_XLS}""" + if self.xls_link + else "" + ) + def excel(self, wb=None): """Simple Excel representation of the table""" if wb is None: diff --git a/app/scodoc/sco_undo_notes.py b/app/scodoc/sco_undo_notes.py index 9d9102b2a..d34b84385 100644 --- a/app/scodoc/sco_undo_notes.py +++ b/app/scodoc/sco_undo_notes.py @@ -46,9 +46,11 @@ Opérations: """ import datetime -from flask import g, request, url_for +from flask import g, render_template, request, url_for -from app.models import Evaluation, FormSemestre +from app import db +from app.auth.models import User +from app.models import Evaluation, FormSemestre, ModuleImpl, NotesNotes, NotesNotesLog from app.scodoc.intervals import intervalmap import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb @@ -178,36 +180,67 @@ def evaluation_list_operations(evaluation_id: int): return tab.make_page() -def formsemestre_list_saisies_notes(formsemestre_id, fmt="html"): +def formsemestre_list_saisies_notes(formsemestre_id, only_modifs=False, fmt="html"): """Table listant toutes les opérations de saisies de notes, dans toutes les évaluations du semestre. """ - formsemestre: FormSemestre = FormSemestre.get_or_404(formsemestre_id) - rows = ndb.SimpleDictFetch( - """SELECT i.nom, i.prenom, code_nip, n.*, mod.titre, e.description, e.date_debut, - u.user_name, e.id as evaluation_id - FROM notes_notes n, notes_evaluation e, notes_moduleimpl mi, - notes_modules mod, identite i, "user" u - WHERE mi.id = e.moduleimpl_id - and mi.module_id = mod.id - and e.id = n.evaluation_id - and i.id = n.etudid - and u.id = n.uid - and mi.formsemestre_id = %(formsemestre_id)s - ORDER BY date desc - """, - {"formsemestre_id": formsemestre_id}, + formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id) + only_modifs = scu.to_bool(only_modifs) + model = NotesNotesLog if only_modifs else NotesNotes + notes_query = ( + db.session.query(model) + .join(Evaluation, Evaluation.id == model.evaluation_id) + .join(ModuleImpl) + .filter_by(formsemestre_id=formsemestre.id) + .order_by(model.date.desc()) ) + # Formate les notes keep_numeric = fmt in scu.FORMATS_NUMERIQUES - for row in rows: - row["value"] = scu.fmt_note(row["value"], keep_numeric=keep_numeric) - row["date_evaluation"] = ( - row["date_debut"].strftime("%d/%m/%Y %H:%M") if row["date_debut"] else "" - ) - row["_date_evaluation_order"] = ( - row["date_debut"].isoformat() if row["date_debut"] else "" + rows = [] + for note in notes_query: + ens = User.get_user(note.uid) + evaluation = note.evaluation + rows.append( + { + "date": note.date.strftime(scu.DATEATIME_FMT), + "_date_order": note.date.isoformat(), + "code_nip": note.etudiant.code_nip, + "nom": note.etudiant.nom_disp(), + "prenom": note.etudiant.prenom_str, + "date_evaluation": ( + evaluation.date_debut.strftime(scu.DATEATIME_FMT) + if evaluation and note.evaluation.date_debut + else "" + ), + "_date_evaluation_order": ( + note.evaluation.date_debut.isoformat() + if evaluation and note.evaluation.date_debut + else "" + ), + "value": scu.fmt_note(note.value, keep_numeric=keep_numeric), + "module": ( + ( + note.evaluation.moduleimpl.module.code + or note.evaluation.moduleimpl.module.titre + ) + if evaluation + else "" + ), + "evaluation": note.evaluation.description if evaluation else "", + "_evaluation_target": ( + url_for( + "notes.evaluation_listenotes", + scodoc_dept=g.scodoc_dept, + evaluation_id=note.evaluation_id, + ) + if evaluation + else "" + ), + "user_name": ens.user_name if ens else "", + } ) + columns_ids = ( "date", "code_nip", @@ -215,9 +248,8 @@ def formsemestre_list_saisies_notes(formsemestre_id, fmt="html"): "prenom", "value", "user_name", - "titre", - "evaluation_id", - "description", + "module", + "evaluation", "date_evaluation", "comment", ) @@ -230,11 +262,11 @@ def formsemestre_list_saisies_notes(formsemestre_id, fmt="html"): "comment": "Remarque", "user_name": "Enseignant", "evaluation_id": "evaluation_id", - "titre": "Module", - "description": "Evaluation", + "module": "Module", + "evaluation": "Evaluation", "date_evaluation": "Date éval.", } - tab = GenTable( + table = GenTable( titles=titles, columns_ids=columns_ids, rows=rows, @@ -244,11 +276,25 @@ def formsemestre_list_saisies_notes(formsemestre_id, fmt="html"): html_sortable=True, caption=f"Saisies de notes dans {formsemestre.titre_annee()}", preferences=sco_preferences.SemPreferences(formsemestre_id), - base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id), + base_url=f"""{request.base_url}?formsemestre_id={ + formsemestre_id}&only_modifs={int(only_modifs)}""", origin=f"Généré par {sco_version.SCONAME} le " + scu.timedate_human_repr() + "", table_id="formsemestre_list_saisies_notes", + filename=( + f"modifs_notes_S{formsemestre.semestre_id}" + if only_modifs + else f"saisies_notes_S{formsemestre.semestre_id}" + ), ) - return tab.make_page(fmt=fmt) + if fmt == "html": + return render_template( + "formsemestre/list_saisies_notes.j2", + table=table, + title="Opérations de saisies de notes", + only_modifs=only_modifs, + formsemestre_id=formsemestre.id, + ) + return table.make_page(fmt=fmt, page_title="Opérations de saisies de notes") def get_note_history(evaluation_id, etudid, fmt=""): diff --git a/app/templates/formsemestre/list_saisies_notes.j2 b/app/templates/formsemestre/list_saisies_notes.j2 new file mode 100644 index 000000000..517d2142e --- /dev/null +++ b/app/templates/formsemestre/list_saisies_notes.j2 @@ -0,0 +1,48 @@ +{% extends "sco_page.j2" %} + +{% block styles %} +{{super()}} + +{% endblock %} + +{% block app_content %} +

{{title_h2}}

+ +
{{table.get_nb_rows()}} + {% if only_modifs %}modifications{% else %}saisies{% endif %} + de notes dans ce semestre. +
+
+ + {{table.xls_export_button()|safe}} excel +
+ +{{table.html()|safe}} + + +{% endblock %} + +{% block scripts %} +{{super()}} + + +{% endblock %} \ No newline at end of file diff --git a/sco_version.py b/sco_version.py index 8110b6d01..9e646dd9c 100644 --- a/sco_version.py +++ b/sco_version.py @@ -3,7 +3,7 @@ "Infos sur version ScoDoc" -SCOVERSION = "9.7.39" +SCOVERSION = "9.7.40" SCONAME = "ScoDoc" diff --git a/tests/api/test_api_permissions.py b/tests/api/test_api_permissions.py index 53393654a..c459aeed8 100755 --- a/tests/api/test_api_permissions.py +++ b/tests/api/test_api_permissions.py @@ -94,6 +94,7 @@ def test_permissions(api_headers): "/ScoDoc/api/justificatif/1/list", # demande AbsJustifView "/ScoDoc/api/justificatif/1/justifies", # demande ScoJustifChange "/ScoDoc/api/justificatif/1/export", # demande AbsChange + "/ScoDoc/api/operations/user/", # demande superamin ou user lui même ] ): # On passe ces routes spéciales