diff --git a/app/scodoc/sco_evaluation_check_abs.py b/app/scodoc/sco_evaluation_check_abs.py index 957ba963c..a59cc44c8 100644 --- a/app/scodoc/sco_evaluation_check_abs.py +++ b/app/scodoc/sco_evaluation_check_abs.py @@ -30,16 +30,17 @@ from flask import url_for, g from app import db -from app.models import Evaluation, FormSemestre, Identite +from app.models import Evaluation, FormSemestre, Identite, Assiduite import app.scodoc.sco_utils as scu from app.scodoc import html_sco_header from app.scodoc import sco_evaluations from app.scodoc import sco_evaluation_db -from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import sco_groups +from flask_sqlalchemy.query import Query +from sqlalchemy import or_, and_ + -# XXX TODO-ASSIDUITE https://scodoc.org/git/ScoDoc/ScoDoc/issues/685 def evaluation_check_absences(evaluation: Evaluation): """Vérifie les absences au moment de cette évaluation. Cas incohérents que l'on peut rencontrer pour chaque étudiant: @@ -50,28 +51,32 @@ def evaluation_check_absences(evaluation: Evaluation): EXC et pas justifie Ramene 5 listes d'etudid """ - raise ScoValueError("Fonction non disponible, patience !") # XXX TODO-ASSIDUITE - if not evaluation.date_debut: return [], [], [], [], [] # evaluation sans date - am, pm = evaluation.is_matin(), evaluation.is_apresmidi() + etudids = [ + etudid + for etudid, _ in sco_groups.do_evaluation_listeetuds_groups( + evaluation.id, getallstudents=True + ) + ] - # Liste les absences à ce moment: - absences = sco_abs.list_abs_jour(evaluation.date_debut, am=am, pm=pm) - abs_etudids = set([x["etudid"] for x in absences]) # ensemble des etudiants absents - abs_non_just = sco_abs.list_abs_non_just_jour( - evaluation.date_debut.date(), am=am, pm=pm + deb, fin = scu.localize_datetime(evaluation.date_debut), scu.localize_datetime( + evaluation.date_fin ) - abs_nj_etudids = set( - [x["etudid"] for x in abs_non_just] - ) # ensemble des etudiants absents non justifies - justifs = sco_abs.list_abs_jour( - evaluation.date_debut.date(), am=am, pm=pm, is_abs=None, is_just=True + + assiduites: Query = Assiduite.query.filter( + Assiduite.etudid.in_(etudids), + Assiduite.etat == scu.EtatAssiduite.ABSENT, + or_( + and_(Assiduite.date_debut >= deb, Assiduite.date_debut <= fin), + and_(Assiduite.date_fin >= deb, Assiduite.date_fin <= fin), + ), ) - just_etudids = set( - [x["etudid"] for x in justifs] - ) # ensemble des etudiants avec justif + + abs_etudids = set(assi.etudid for assi in assiduites) + abs_nj_etudids = set(assi.etudid for assi in assiduites if assi.est_just is False) + just_etudids = set(assi.etudid for assi in assiduites if assi.est_just is True) # Les notes: notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id) @@ -80,9 +85,7 @@ def evaluation_check_absences(evaluation: Evaluation): ExcNonSignalee = [] # note EXC mais pas noté absent ExcNonJust = [] # note EXC mais absent non justifie AbsButExc = [] # note ABS mais justifié - for etudid, _ in sco_groups.do_evaluation_listeetuds_groups( - evaluation.id, getallstudents=True - ): + for etudid in etudids: if etudid in notes_db: val = notes_db[etudid]["value"] if ( @@ -170,14 +173,10 @@ def evaluation_check_absences_html( ) if linkabs: url = url_for( - "absences.doSignaleAbsence", # XXX TODO-ASSIDUITE - scodoc_dept=g.scodoc_dept, + "assiduites.signal_evaluation_abs", etudid=etudid, - # par defaut signale le jour du début de l'éval - datedebut=evaluation.date_debut.strftime("%d/%m/%Y"), - datefin=evaluation.date_debut.strftime("%d/%m/%Y"), - demijournee=demijournee, - moduleimpl_id=evaluation.moduleimpl_id, + evaluation_id=evaluation.id, + scodoc_dept=g.scodoc_dept, ) H.append( f"""signaler cette absence""" diff --git a/app/templates/assiduites/pages/signal_assiduites_etud.j2 b/app/templates/assiduites/pages/signal_assiduites_etud.j2 index 2222cbe42..71dd19962 100644 --- a/app/templates/assiduites/pages/signal_assiduites_etud.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_etud.j2 @@ -32,6 +32,18 @@
+ + {% if saisie_eval %} +
+
+

+ La saisie de l'assiduité a été préconfigurée en fonction de l'évaluation.
+ Une fois la saisie finie, cliquez sur le lien si dessous pour revenir sur la gestion de l'évaluation +

+ retourner sur la page de l'évaluation +
+ {% endif %} + {{diff | safe}}
@@ -118,7 +130,20 @@ window.forceModule = "{{ forcer_module }}" window.forceModule = window.forceModule == "True" ? true : false + const date_deb = "{{date_deb}}"; + const date_fin = "{{date_fin}}"; + + {% if saisie_eval %} + createColumn( + date_deb, + date_fin, + {{ moduleimpl_id }} + ); + window.location.href = "#saisie_eval" + getAndUpdateCol(1) + {% else %} createColumn(); + {% endif %} diff --git a/app/templates/assiduites/widgets/differee.j2 b/app/templates/assiduites/widgets/differee.j2 index 8d96c5bc4..523c4a827 100644 --- a/app/templates/assiduites/widgets/differee.j2 +++ b/app/templates/assiduites/widgets/differee.j2 @@ -278,7 +278,7 @@ currentDate = moment(currentDate).tz(TIMEZONE).format("YYYY-MM-DDTHH:mm"); } - function createColumn(dateStart = "", dateEnd = "") { + function createColumn(dateStart = "", dateEnd = "", moduleimpl_id = "") { let table = document.getElementById("studentTable"); let th = document.createElement("div"); th.classList.add("th", "error"); @@ -343,6 +343,10 @@ editModuleImpl(sl); }) + if (moduleimpl_id != "") { + sl.value = moduleimpl_id; + } + let rows = table.querySelector(".tbody").querySelectorAll(".tr"); for (let i = 0; i < rows.length; i++) { let td = document.createElement("div"); diff --git a/app/views/assiduites.py b/app/views/assiduites.py index ebf9dbd31..4e13e8076 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -1,7 +1,7 @@ import datetime -from flask import g, request, render_template -from flask import abort, url_for +from flask import g, request, render_template, flash +from flask import abort, url_for, redirect from flask_login import current_user from app import db @@ -18,6 +18,7 @@ from app.models import ( Assiduite, Departement, FormSemestreInscription, + Evaluation, ) from app.views import assiduites_bp as bp from app.views import ScoData @@ -203,6 +204,27 @@ def signal_assiduites_etud(): if etud.dept_id != g.scodoc_dept_id: abort(404, "étudiant inexistant dans ce département") + date = request.args.get("date", datetime.date.today().isoformat()) + + # gestion évaluations + + saisie_eval: bool = request.args.get("saisie_eval") is not None + + date_deb: str = request.args.get("date_deb") + date_fin: str = request.args.get("date_fin") + moduleimpl_id: int = request.args.get("moduleimpl_id", "") + evaluation_id: int = request.args.get("evaluation_id") + + redirect_url: str = ( + "#" + if not saisie_eval + else url_for( + "notes.evaluation_check_absences_html", + evaluation_id=evaluation_id, + scodoc_dept=g.scodoc_dept, + ) + ) + header: str = html_sco_header.sco_header( page_title="Saisie Assiduités", init_qtip=True, @@ -235,7 +257,7 @@ def signal_assiduites_etud(): render_template( "assiduites/pages/signal_assiduites_etud.j2", sco=ScoData(etud), - date=datetime.date.today().isoformat(), + date=date, morning=morning, lunch=lunch, timeline=_timeline(), @@ -249,6 +271,11 @@ def signal_assiduites_etud(): etudiants=[sco_etud.get_etud_info(etudid=etud.etudid, filled=True)[0]], moduleimpl_select=select, ), + saisie_eval=saisie_eval, + date_deb=date_deb, + date_fin=date_fin, + redirect_url=redirect_url, + moduleimpl_id=moduleimpl_id, ), ).build() @@ -995,6 +1022,66 @@ def signal_assiduites_diff(): ).build() +@bp.route("/SignalEvaluationAbs//") +@scodoc +@permission_required(Permission.ScoView) +def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None): + """ + Signale l'absence d'un étudiant à une évaluation + Si la durée de l'évaluation est inférieur à 1 jour + Alors l'absence sera sur la période de l'évaluation + Sinon L'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant + """ + etud: Identite = Identite.query.get_or_404(etudid) + if etud.dept_id != g.scodoc_dept_id: + abort(404, "étudiant inexistant dans ce département") + + evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id) + + delta: datetime.timedelta = evaluation.date_fin - evaluation.date_debut + if delta > datetime.timedelta(days=1): + # rediriger vers page saisie + flash("Redirection pour saisie abs") + return redirect( + url_for( + "assiduites.signal_assiduites_etud", + etudid=etudid, + evaluation_id=evaluation.id, + date_deb=evaluation.date_debut.strftime("%Y-%m-%dT%H:%M:%S"), + date_fin=evaluation.date_fin.strftime("%Y-%m-%dT%H:%M:%S"), + moduleimpl_id=evaluation.moduleimpl.id, + saisie_eval="true", + scodoc_dept=g.scodoc_dept, + ) + ) + + # créer l'assiduité + + assiduite_unique: Assiduite = Assiduite.create_assiduite( + etud=etud, + date_debut=scu.localize_datetime(evaluation.date_debut), + date_fin=scu.localize_datetime(evaluation.date_fin), + etat=scu.EtatAssiduite.ABSENT, + moduleimpl=evaluation.moduleimpl, + ) + + db.session.add(assiduite_unique) + db.session.commit() + + flash("L'absence a bien été créée") + # rediriger vers la page d'évaluation + return redirect( + url_for( + "notes.evaluation_check_absences_html", + evaluation_id=evaluation.id, + scodoc_dept=g.scodoc_dept, + ) + ) + + +# --- Fonctions internes --- + + def _get_days_between_dates(deb: str, fin: str): if deb is None or fin is None: return "null"