forked from ScoDoc/ScoDoc
Liste des saisies de notes sur le user_board
This commit is contained in:
parent
d7f4209a5a
commit
2642a25cf7
@ -122,6 +122,7 @@ from app.api import (
|
||||
justificatifs,
|
||||
logos,
|
||||
moduleimpl,
|
||||
operations,
|
||||
partitions,
|
||||
semset,
|
||||
users,
|
||||
|
100
app/api/operations.py
Normal file
100
app/api/operations.py
Normal file
@ -0,0 +1,100 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
ScoDoc 9 API : liste opérations effectuées par un utilisateur
|
||||
|
||||
CATEGORY
|
||||
--------
|
||||
Operations
|
||||
"""
|
||||
|
||||
from flask import url_for
|
||||
from flask_json import as_json
|
||||
from flask_login import login_required
|
||||
|
||||
import app
|
||||
from app import db
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.api import api_permission_required as permission_required
|
||||
from app.decorators import scodoc
|
||||
from app.models import NotesNotes
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
MAX_QUERY_LENGTH = 10000
|
||||
|
||||
|
||||
@bp.route("/operations/user/<int:uid>/notes")
|
||||
@api_web_bp.route("/operations/user/<int:uid>/notes")
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@as_json
|
||||
def operations_user_notes(uid: int):
|
||||
"""Liste les opérations de saisie de notes effectuées par utilisateur.
|
||||
|
||||
QUERY
|
||||
-----
|
||||
start: indice de début de la liste
|
||||
length: nombre d'éléments à retourner
|
||||
draw: numéro de la requête (pour pagination, renvoyé tel quel)
|
||||
order[dir]: desc ou asc
|
||||
search[value]: chaîne à chercher (dans évaluation et étudiant)
|
||||
PARAMS
|
||||
-----
|
||||
uid: l'id de l'utilisateur
|
||||
"""
|
||||
start = int(app.request.args.get("start", 0))
|
||||
length = min(int(app.request.args.get("length", 10)), MAX_QUERY_LENGTH)
|
||||
order = app.request.args.get("order[dir]", "desc")
|
||||
draw = int(app.request.args.get("draw", 1))
|
||||
search = app.request.args.get("search[value]", "")
|
||||
query = db.session.query(NotesNotes).filter(NotesNotes.uid == uid)
|
||||
if order == "asc":
|
||||
query = query.order_by(NotesNotes.date.asc())
|
||||
else:
|
||||
query = query.order_by(NotesNotes.date.desc())
|
||||
|
||||
# Pour l'efficacité, limite si pas de recherche en python
|
||||
limited_query = query.offset(start).limit(length) if not search else query
|
||||
|
||||
data = []
|
||||
for note in limited_query:
|
||||
obj = {
|
||||
"date": note.date.isoformat(),
|
||||
"date_dmy": note.date.strftime(scu.DATEATIME_FMT),
|
||||
"operation": "Saisie de note",
|
||||
"value": scu.fmt_note(note.value),
|
||||
"id": note.id,
|
||||
"uid": note.uid,
|
||||
"etudiant": note.etudiant.to_dict_short(),
|
||||
"etudiant_link": note.etudiant.html_link_fiche(),
|
||||
"evaluation": note.evaluation.to_dict_api(),
|
||||
"evaluation_link": f"""<a href="{
|
||||
url_for('notes.evaluation_listenotes',
|
||||
scodoc_dept=note.evaluation.moduleimpl.formsemestre.departement.acronym,
|
||||
evaluation_id=note.evaluation_id)
|
||||
}">{note.evaluation.descr()}</a>""",
|
||||
}
|
||||
if search:
|
||||
search = search.lower()
|
||||
if (
|
||||
search not in note.etudiant.nomprenom.lower()
|
||||
and search not in note.evaluation.descr().lower()
|
||||
and search not in obj["date_dmy"]
|
||||
):
|
||||
continue # skip
|
||||
|
||||
data.append(obj)
|
||||
|
||||
result = data[start : start + length] if search else data
|
||||
return {
|
||||
"draw": draw,
|
||||
"recordsTotal": query.count(), # unfiltered
|
||||
"recordsFiltered": len(data) if search else query.count(),
|
||||
"data": result,
|
||||
}
|
@ -121,6 +121,12 @@ class Identite(models.ScoDocModel):
|
||||
cascade="all, delete-orphan",
|
||||
lazy="dynamic",
|
||||
)
|
||||
notes = db.relationship(
|
||||
"NotesNotes",
|
||||
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"
|
||||
|
@ -58,6 +58,12 @@ class Evaluation(models.ScoDocModel):
|
||||
# ordre de presentation (par défaut, le plus petit numero
|
||||
# est la plus ancienne eval):
|
||||
numero = db.Column(db.Integer, nullable=False, default=0)
|
||||
notes = db.relationship(
|
||||
"NotesNotes",
|
||||
backref="evaluation",
|
||||
cascade="all, delete-orphan",
|
||||
lazy="dynamic",
|
||||
)
|
||||
ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True)
|
||||
|
||||
_sco_dept_relations = ("ModuleImpl", "FormSemestre") # accès au dept_id
|
||||
@ -380,6 +386,13 @@ class Evaluation(models.ScoDocModel):
|
||||
return f"""du {self.date_debut.strftime('%d/%m/%Y')} à {
|
||||
_h(self.date_debut)} au {self.date_fin.strftime('%d/%m/%Y')} à {_h(self.date_fin)}"""
|
||||
|
||||
def descr(self) -> str:
|
||||
"Description de l'évaluation pour affichage (avec module et semestre)"
|
||||
return f"""{self.description} {self.descr_date()} en {
|
||||
self.moduleimpl.module.titre_str()} du {
|
||||
self.moduleimpl.formsemestre.titre_formation(with_sem_idx=True)
|
||||
}"""
|
||||
|
||||
def heure_debut(self) -> str:
|
||||
"""L'heure de début (sans la date), en ISO.
|
||||
Chaine vide si non renseignée."""
|
||||
|
@ -1,8 +1,10 @@
|
||||
{# Tableau de bord utilisateur #}
|
||||
|
||||
{% extends "base.j2" %}
|
||||
|
||||
{% block app_content %}
|
||||
{% block styles %}
|
||||
{{super()}}
|
||||
<link type="text/css" rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/qtip/jquery.qtip-3.0.3.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{scu.STATIC_DIR}}/DataTables/datatables.min.css" />
|
||||
<style>
|
||||
.ub-formsemestres {
|
||||
display: flex;
|
||||
@ -60,7 +62,14 @@
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.scobox.saisies-notes {
|
||||
background-color: rgb(243, 255, 255);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block app_content %}
|
||||
|
||||
<div class="tab-content">
|
||||
<h2>{{user.get_nomcomplet()}}</h2>
|
||||
@ -105,6 +114,61 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<div class="scobox saisies-notes">
|
||||
<div class="scobox-title">
|
||||
Dernières saisies de notes par {{user.get_prenomnom()}}
|
||||
</div>
|
||||
<table id="saisies-notes" class="display" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Évaluation</th>
|
||||
<th>Étudiant</th>
|
||||
<th>Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Data will be loaded dynamically via JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock app_content %}
|
||||
|
||||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script>
|
||||
<script src="{{scu.STATIC_DIR}}/DataTables/datatables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#saisies-notes').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
"ajax": {
|
||||
"url": "{{ url_for('apiweb.operations_user_notes',
|
||||
scodoc_dept=current_user.dept or g.scodoc_dept or fallback_dept.acronym,
|
||||
uid=user.id) }}",
|
||||
"type": "GET"
|
||||
},
|
||||
"columns": [
|
||||
{ "data": "date_dmy", "orderable": false },
|
||||
{ "data": "evaluation_link", "orderable": false },
|
||||
{ "data": "etudiant_link", "orderable": false },
|
||||
{ "data": "value", "orderable": false }
|
||||
],
|
||||
"language": {
|
||||
search: "Chercher (date, titre, étudiant) :", // Change the "Search:" label
|
||||
lengthMenu: "Show _MENU_ records per page"
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -14,6 +14,7 @@ from app.decorators import (
|
||||
permission_required,
|
||||
)
|
||||
from app.models import Departement, FormSemestre
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc import sco_preferences
|
||||
from app.views import scodoc_bp as bp
|
||||
@ -25,6 +26,9 @@ from app.views import ScoData
|
||||
@login_required
|
||||
def user_board(user_name: str):
|
||||
"""Tableau de bord utilisateur: liens vers ses objets"""
|
||||
fallback_dept = db.session.query(Departement).first()
|
||||
if not fallback_dept:
|
||||
raise ScoValueError("Aucun département existant")
|
||||
user = User.query.filter_by(user_name=user_name).first_or_404()
|
||||
(
|
||||
formsemestres_by_dept,
|
||||
@ -47,6 +51,7 @@ def user_board(user_name: str):
|
||||
"user_board/user_board.j2",
|
||||
depts=depts,
|
||||
dept_names=dept_names_sorted,
|
||||
fallback_dept=fallback_dept,
|
||||
formsemestres_by_dept=formsemestres_by_dept,
|
||||
modimpls_by_formsemestre=modimpls_by_formsemestre,
|
||||
sco=ScoData(),
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
"Infos sur version ScoDoc"
|
||||
|
||||
SCOVERSION = "9.7.37"
|
||||
SCOVERSION = "9.7.38"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user