forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -78,7 +78,11 @@ def compute_sem_moys_apc_using_ects(
|
||||
else:
|
||||
ects = ects_df.to_numpy()
|
||||
# ects est maintenant un array nb_etuds x nb_ues
|
||||
|
||||
moy_gen = (etud_moy_ue_df * ects).sum(axis=1) / ects.sum(axis=1)
|
||||
except ZeroDivisionError:
|
||||
# peut arriver si aucun module... on ignore
|
||||
moy_gen = pd.Series(np.NaN, index=etud_moy_ue_df.index)
|
||||
except TypeError:
|
||||
if None in ects:
|
||||
formation = db.session.get(Formation, formation_id)
|
||||
|
@ -79,13 +79,15 @@ Adresses d'origine:
|
||||
to : {orig_to}
|
||||
cc : {orig_cc}
|
||||
bcc: {orig_bcc}
|
||||
---
|
||||
---
|
||||
\n\n"""
|
||||
+ msg.body
|
||||
)
|
||||
|
||||
current_app.logger.info(
|
||||
f"""email sent to{' (mode test)' if email_test_mode_address else ''}: {msg.recipients}
|
||||
f"""email sent to{
|
||||
' (mode test)' if email_test_mode_address else ''
|
||||
}: {msg.recipients}
|
||||
from sender {msg.sender}
|
||||
"""
|
||||
)
|
||||
@ -98,7 +100,8 @@ def get_from_addr(dept_acronym: str = None):
|
||||
"""L'adresse "from" à utiliser pour envoyer un mail
|
||||
|
||||
Si le departement est spécifié, ou si l'attribut `g.scodoc_dept`existe,
|
||||
prend le `email_from_addr` des préférences de ce département si ce champ est non vide.
|
||||
prend le `email_from_addr` des préférences de ce département si ce champ
|
||||
est non vide.
|
||||
Sinon, utilise le paramètre global `email_from_addr`.
|
||||
Sinon, la variable de config `SCODOC_MAIL_FROM`.
|
||||
"""
|
||||
|
@ -1,19 +1,17 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import html
|
||||
import traceback
|
||||
|
||||
from flask import g, current_app, abort
|
||||
import psycopg2
|
||||
import psycopg2.pool
|
||||
import psycopg2.extras
|
||||
|
||||
from flask import g, current_app, abort
|
||||
|
||||
import app
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import ScoException, ScoValueError, NoteProcessError
|
||||
import datetime
|
||||
|
||||
quote_html = html.escape
|
||||
|
||||
|
@ -318,7 +318,7 @@ def list_abs_in_range(
|
||||
Returns:
|
||||
List of absences
|
||||
"""
|
||||
if matin != None:
|
||||
if matin is not None:
|
||||
matin = _toboolean(matin)
|
||||
ismatin = " AND A.MATIN = %(matin)s "
|
||||
else:
|
||||
@ -387,7 +387,7 @@ def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
|
||||
Returns:
|
||||
An integer.
|
||||
"""
|
||||
if matin != None:
|
||||
if matin is not None:
|
||||
matin = _toboolean(matin)
|
||||
ismatin = " AND A.MATIN = %(matin)s "
|
||||
else:
|
||||
@ -482,7 +482,9 @@ def _get_abs_description(a, cursor=None):
|
||||
else:
|
||||
a["matin"] = False
|
||||
cursor.execute(
|
||||
"""select * from absences where etudid=%(etudid)s and jour=%(jour)s and matin=%(matin)s order by entry_date desc""",
|
||||
"""SELECT * FROM absences
|
||||
WHERE etudid=%(etudid)s AND jour=%(jour)s AND matin=%(matin)s
|
||||
ORDER BY entry_date desc""",
|
||||
a,
|
||||
)
|
||||
A = cursor.dictfetchall()
|
||||
@ -517,9 +519,9 @@ def list_abs_jour(date, am=True, pm=True, is_abs=True, is_just=None):
|
||||
req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A
|
||||
WHERE A.jour = %(date)s
|
||||
"""
|
||||
if is_abs != None:
|
||||
if is_abs is not None:
|
||||
req += " AND A.estabs = %(is_abs)s"
|
||||
if is_just != None:
|
||||
if is_just is not None:
|
||||
req += " AND A.estjust = %(is_just)s"
|
||||
if not am:
|
||||
req += " AND NOT matin "
|
||||
@ -883,7 +885,7 @@ def MonthTableBody(
|
||||
descr = ev[4]
|
||||
#
|
||||
cc = []
|
||||
if color != None:
|
||||
if color is not None:
|
||||
cc.append('<td bgcolor="%s" class="calcell">' % color)
|
||||
else:
|
||||
cc.append('<td class="calcell">')
|
||||
@ -896,7 +898,7 @@ def MonthTableBody(
|
||||
cc.append("<a %s %s>" % (href, descr))
|
||||
|
||||
if legend or d == 1:
|
||||
if pad_width != None:
|
||||
if pad_width is not None:
|
||||
n = pad_width - len(legend) # pad to 8 cars
|
||||
if n > 0:
|
||||
legend = (
|
||||
@ -959,7 +961,7 @@ def MonthTableBody(
|
||||
ev_year = int(ev[0][:4])
|
||||
ev_month = int(ev[0][5:7])
|
||||
ev_day = int(ev[0][8:10])
|
||||
if ev[4] != None:
|
||||
if ev[4] is not None:
|
||||
ev_half = int(ev[4])
|
||||
else:
|
||||
ev_half = 0
|
||||
@ -978,7 +980,7 @@ def MonthTableBody(
|
||||
if len(ev) > 5 and ev[5]:
|
||||
descr = ev[5]
|
||||
#
|
||||
if color != None:
|
||||
if color is not None:
|
||||
cc.append('<td bgcolor="%s" class="calcell">' % (color))
|
||||
else:
|
||||
cc.append('<td class="calcell">')
|
||||
@ -1072,7 +1074,8 @@ def invalidate_abs_count_sem(sem):
|
||||
|
||||
|
||||
def invalidate_abs_etud_date(etudid, date): # was invalidateAbsEtudDate
|
||||
"""Doit etre appelé à chaque modification des absences pour cet étudiant et cette date.
|
||||
"""Doit etre appelé à chaque modification des absences
|
||||
pour cet étudiant et cette date.
|
||||
Invalide cache absence et caches semestre
|
||||
date: date au format ISO
|
||||
"""
|
||||
|
@ -137,14 +137,14 @@ def doSignaleAbsence(
|
||||
]
|
||||
if dates:
|
||||
H.append(
|
||||
f"""<p>Ajout de {nbadded} absences <b>{just_str}justifiées</b>
|
||||
f"""<p>Ajout de {nbadded} absences <b>{just_str}justifiées</b>
|
||||
du {datedebut} au {datefin} {indication_module}
|
||||
</p>
|
||||
"""
|
||||
)
|
||||
else:
|
||||
H.append(
|
||||
f"""<p class="warning">Aucune date ouvrable
|
||||
f"""<p class="warning">Aucune date ouvrable
|
||||
entre le {datedebut} et le {datefin} !
|
||||
</p>
|
||||
"""
|
||||
@ -152,11 +152,11 @@ def doSignaleAbsence(
|
||||
|
||||
H.append(
|
||||
f"""<ul>
|
||||
<li><a class="stdlink" href="{url_for("absences.SignaleAbsenceEtud",
|
||||
<li><a class="stdlink" href="{url_for("absences.SignaleAbsenceEtud",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||
)}">Autre absence pour <b>{etud.nomprenom}</b></a>
|
||||
</li>
|
||||
<li><a class="stdlink" href="{url_for("absences.CalAbs",
|
||||
<li><a class="stdlink" href="{url_for("absences.CalAbs",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||
)}">Calendrier de ses absences</a>
|
||||
</li>
|
||||
@ -180,8 +180,12 @@ def SignaleAbsenceEtud(): # etudid implied
|
||||
"abs_require_module"
|
||||
) # on utilise la pref globale car pas de sem courant
|
||||
if require_module:
|
||||
menu_module = """<div class="ue_warning">Pas inscrit dans un semestre courant,
|
||||
et l'indication du module est requise. Donc pas de saisie d'absence possible !</div>"""
|
||||
menu_module = """<div class="ue_warning">Pas
|
||||
inscrit dans un semestre courant,
|
||||
et l'indication du module est requise.
|
||||
Donc pas de saisie d'absence possible !
|
||||
</div>
|
||||
"""
|
||||
disabled = True
|
||||
else:
|
||||
menu_module = ""
|
||||
@ -197,17 +201,17 @@ def SignaleAbsenceEtud(): # etudid implied
|
||||
menu_module = """
|
||||
<script type="text/javascript">
|
||||
function form_enable_disable() {
|
||||
if ( $("select#sel_moduleimpl_id").val() == "" ) {
|
||||
$("#butsubmit").prop("disabled", true);
|
||||
} else {
|
||||
$("#butsubmit").prop("disabled", false);
|
||||
if ( $("select#sel_moduleimpl_id").val() == "" ) {
|
||||
$("#butsubmit").prop("disabled", true);
|
||||
} else {
|
||||
$("#butsubmit").prop("disabled", false);
|
||||
};
|
||||
}
|
||||
$(document).ready(function() {
|
||||
form_enable_disable();
|
||||
});
|
||||
</script>
|
||||
<p>Module:
|
||||
<p>Module:
|
||||
<select id="sel_moduleimpl_id" name="moduleimpl_id"
|
||||
onChange="form_enable_disable();">"""
|
||||
else:
|
||||
@ -250,7 +254,10 @@ def SignaleAbsenceEtud(): # etudid implied
|
||||
<p>
|
||||
<table><tr>
|
||||
<td>Date début : </td>
|
||||
<td><input type="text" name="datedebut" size="10" class="datepicker"/> <em>j/m/a</em></td>
|
||||
<td>
|
||||
<input type="text" name="datedebut" size="10" class="datepicker"/>
|
||||
<em>j/m/a</em>
|
||||
</td>
|
||||
<td> Date fin (optionnelle):</td>
|
||||
<td><input type="text" name="datefin" size="10" class="datepicker"/> <em>j/m/a</em></td>
|
||||
</tr>
|
||||
@ -269,14 +276,14 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input id="butsubmit" type="submit" value="Envoyer" disable="%(disabled)s"/>
|
||||
<input id="butsubmit" type="submit" value="Envoyer" disable="%(disabled)s"/>
|
||||
<em>
|
||||
<p>Seuls les modules du semestre en cours apparaissent.</p>
|
||||
<p>Évitez de saisir une absence pour un module qui n'est pas en place à cette date.</p>
|
||||
<p>Toutes les dates sont au format jour/mois/annee.</p>
|
||||
</em>
|
||||
|
||||
</form>
|
||||
</form>
|
||||
"""
|
||||
% {
|
||||
"etudid": etud["etudid"],
|
||||
@ -354,7 +361,10 @@ def doJustifAbsence(
|
||||
)
|
||||
|
||||
H.append(
|
||||
"""<ul><li><a href="JustifAbsenceEtud?etudid=%(etudid)s">Autre justification pour <b>%(nomprenom)s</b></a></li>
|
||||
"""<ul>
|
||||
<li><a href="JustifAbsenceEtud?etudid=%(etudid)s">Autre justification
|
||||
pour <b>%(nomprenom)s</b>
|
||||
</a></li>
|
||||
<li><a href="SignaleAbsenceEtud?etudid=%(etudid)s">Signaler une absence</a></li>
|
||||
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li>
|
||||
<li><a href="ListeAbsEtud?etudid=%(etudid)s">Liste de ses absences</a></li>
|
||||
@ -389,12 +399,12 @@ def JustifAbsenceEtud(): # etudid implied
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""
|
||||
<form action="doJustifAbsence" method="get">
|
||||
<form action="doJustifAbsence" method="get">
|
||||
<input type="hidden" name="etudid" value="%(etudid)s">
|
||||
|
||||
<p>
|
||||
<table><tr>
|
||||
<td>Date début : </td>
|
||||
<td>Date début : </td>
|
||||
<td>
|
||||
<input type="text" name="datedebut" size="10" class="datepicker"/>
|
||||
</td>
|
||||
@ -412,7 +422,7 @@ def JustifAbsenceEtud(): # etudid implied
|
||||
Raison: <input type="text" name="description" size="42"/> (optionnel)
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Envoyer">
|
||||
<input type="submit" value="Envoyer">
|
||||
|
||||
</form> """
|
||||
% etud,
|
||||
@ -458,8 +468,10 @@ def doAnnuleAbsence(datedebut, datefin, demijournee, etudid=False): # etudid im
|
||||
H.append(
|
||||
"""<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une
|
||||
autre absence pour <b>%(nomprenom)s</b></a></li>
|
||||
<li><a href="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une absence</a></li>
|
||||
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li>
|
||||
<li><a href="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une
|
||||
absence</a></li>
|
||||
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses
|
||||
absences</a></li>
|
||||
</ul>
|
||||
<hr>"""
|
||||
% etud
|
||||
@ -480,10 +492,11 @@ def AnnuleAbsenceEtud(): # etudid implied
|
||||
page_title="Annulation d'une absence pour %(nomprenom)s" % etud,
|
||||
),
|
||||
"""<table><tr><td>
|
||||
<h2><font color="#FF0000">Annulation</font> d'une absence pour %(nomprenom)s</h2>
|
||||
<h2><font color="#FF0000">Annulation</font> d'une absence
|
||||
pour %(nomprenom)s</h2>
|
||||
</td><td>
|
||||
"""
|
||||
% etud, # "
|
||||
% etud,
|
||||
"""<a href="%s">"""
|
||||
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
@ -491,16 +504,19 @@ def AnnuleAbsenceEtud(): # etudid implied
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p>
|
||||
<p><font color="#FF0000">Si plusieurs modules sont affectés, les absences seront toutes effacées. </font></p>
|
||||
"""
|
||||
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que
|
||||
l'étudiant était en fait présent.
|
||||
</p>
|
||||
<p><font color="#FF0000">Si plusieurs modules sont affectés,
|
||||
les absences seront toutes effacées. </font></p>
|
||||
"""
|
||||
% etud,
|
||||
"""<table frame="border" border="1"><tr><td>
|
||||
<form action="doAnnuleAbsence" method="get">
|
||||
<form action="doAnnuleAbsence" method="get">
|
||||
<input type="hidden" name="etudid" value="%(etudid)s">
|
||||
<p>
|
||||
<table><tr>
|
||||
<td>Date début : </td>
|
||||
<td>Date début : </td>
|
||||
<td>
|
||||
<input type="text" name="datedebut" size="10" class="datepicker"/> <em>j/m/a</em>
|
||||
</td>
|
||||
@ -511,22 +527,22 @@ def AnnuleAbsenceEtud(): # etudid implied
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input type="radio" name="demijournee" value="2" checked>journée(s)
|
||||
<input type="radio" name="demijournee" value="2" checked>journée(s)
|
||||
<input type="radio" name="demijournee" value="1">Matin(s)
|
||||
<input type="radio" name="demijournee" value="0">Après midi
|
||||
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Supprimer les absences">
|
||||
</form>
|
||||
<input type="submit" value="Supprimer les absences">
|
||||
</form>
|
||||
</td></tr>
|
||||
|
||||
<tr><td>
|
||||
<form action="doAnnuleJustif" method="get">
|
||||
<form action="doAnnuleJustif" method="get">
|
||||
<input type="hidden" name="etudid" value="%(etudid)s">
|
||||
<p>
|
||||
<table><tr>
|
||||
<td>Date début : </td>
|
||||
<td>Date début : </td>
|
||||
<td>
|
||||
<input type="text" name="datedebut0" size="10" class="datepicker"/> <em>j/m/a</em>
|
||||
</td>
|
||||
@ -538,15 +554,16 @@ def AnnuleAbsenceEtud(): # etudid implied
|
||||
</table>
|
||||
<p>
|
||||
|
||||
<input type="radio" name="demijournee" value="2" checked>journée(s)
|
||||
<input type="radio" name="demijournee" value="2" checked>journée(s)
|
||||
<input type="radio" name="demijournee" value="1">Matin(s)
|
||||
<input type="radio" name="demijournee" value="0">Après midi
|
||||
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Supprimer les justificatifs">
|
||||
<i>(utiliser ceci en cas de justificatif erroné saisi indépendemment d'une absence)</i>
|
||||
</form>
|
||||
<input type="submit" value="Supprimer les justificatifs">
|
||||
<i>(utiliser ceci en cas de justificatif erroné saisi indépendemment
|
||||
d'une absence)</i>
|
||||
</form>
|
||||
</td></tr></table>"""
|
||||
% etud,
|
||||
html_sco_header.sco_footer(),
|
||||
@ -591,8 +608,10 @@ def doAnnuleJustif(datedebut0, datefin0, demijournee): # etudid implied
|
||||
H.append(
|
||||
"""<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une
|
||||
autre absence pour <b>%(nomprenom)s</b></a></li>
|
||||
<li><a href="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une absence</a></li>
|
||||
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li>
|
||||
<li><a href="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une
|
||||
absence</a></li>
|
||||
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses
|
||||
absences</a></li>
|
||||
</ul>
|
||||
<hr>"""
|
||||
% etud
|
||||
@ -634,8 +653,11 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None):
|
||||
# supr les absences non justifiees
|
||||
for date in dates:
|
||||
cursor.execute(
|
||||
"""DELETE FROM absences
|
||||
WHERE etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s
|
||||
"""DELETE FROM absences
|
||||
WHERE etudid=%(etudid)s
|
||||
AND (not estjust)
|
||||
AND jour=%(date)s
|
||||
AND moduleimpl_id=%(moduleimpl_id)s
|
||||
""",
|
||||
vars(),
|
||||
)
|
||||
@ -643,8 +665,11 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None):
|
||||
# s'assure que les justificatifs ne sont pas "absents"
|
||||
for date in dates:
|
||||
cursor.execute(
|
||||
"""UPDATE absences SET estabs=FALSE
|
||||
WHERE etudid=%(etudid)s AND jour=%(date)s AND moduleimpl_id=%(moduleimpl_id)s
|
||||
"""UPDATE absences
|
||||
SET estabs=FALSE
|
||||
WHERE etudid=%(etudid)s
|
||||
AND jour=%(date)s
|
||||
AND moduleimpl_id=%(moduleimpl_id)s
|
||||
""",
|
||||
vars(),
|
||||
)
|
||||
@ -724,7 +749,7 @@ def _convert_sco_year(year) -> int:
|
||||
year = int(year)
|
||||
if year > 1900 and year < 2999:
|
||||
return year
|
||||
except:
|
||||
except ValueError:
|
||||
raise ScoValueError("année scolaire invalide")
|
||||
|
||||
|
||||
@ -771,7 +796,8 @@ def CalAbs(etudid, sco_year=None):
|
||||
"""<b><font color="#EE0000">A : absence NON justifiée</font><br>
|
||||
<font color="#F8B7B0">a : absence justifiée</font><br>
|
||||
<font color="#8EA2C6">X : justification sans absence</font><br>
|
||||
%d absences sur l'année, dont %d justifiées (soit %d non justifiées)</b> <em>(%d justificatifs inutilisés)</em>
|
||||
%d absences sur l'année, dont %d justifiées (soit %d non justifiées)</b>
|
||||
<em>(%d justificatifs inutilisés)</em>
|
||||
</p>
|
||||
"""
|
||||
% (nbabs, nbabsjust, nbabs - nbabsjust, len(justifs_noabs)),
|
||||
@ -790,7 +816,8 @@ def CalAbs(etudid, sco_year=None):
|
||||
"""<form method="GET" action="CalAbs" name="f">""",
|
||||
"""<input type="hidden" name="etudid" value="%s"/>""" % etudid,
|
||||
"""Année scolaire %s-%s""" % (annee_scolaire, annee_scolaire + 1),
|
||||
""" Changer année: <select name="sco_year" onchange="document.f.submit()">""",
|
||||
""" Changer année:
|
||||
<select name="sco_year" onchange="document.f.submit()">""",
|
||||
]
|
||||
for y in range(annee_courante, min(annee_courante - 6, annee_scolaire - 6), -1):
|
||||
H.append("""<option value="%s" """ % y)
|
||||
@ -819,7 +846,8 @@ def ListeAbsEtud(
|
||||
etudid:
|
||||
with_evals: indique les evaluations aux dates d'absences
|
||||
absjust_only: si vrai, renvoie table absences justifiées
|
||||
sco_year: année scolaire à utiliser. Si non spécifier, utilie l'année en cours. e.g. "2005"
|
||||
sco_year: année scolaire à utiliser.
|
||||
Si non spécifier, utilie l'année en cours. e.g. "2005"
|
||||
"""
|
||||
# si absjust_only, table absjust seule (export xls ou pdf)
|
||||
absjust_only = scu.to_bool(absjust_only)
|
||||
@ -941,10 +969,12 @@ def _tables_abs_etud(
|
||||
for a in absnonjust + absjust:
|
||||
cursor.execute(
|
||||
"""SELECT eval.*
|
||||
FROM notes_evaluation eval, notes_moduleimpl_inscription mi, notes_moduleimpl m
|
||||
WHERE eval.jour = %(jour)s
|
||||
FROM notes_evaluation eval,
|
||||
notes_moduleimpl_inscription mi,
|
||||
notes_moduleimpl m
|
||||
WHERE eval.jour = %(jour)s
|
||||
and eval.moduleimpl_id = m.id
|
||||
and mi.moduleimpl_id = m.id
|
||||
and mi.moduleimpl_id = m.id
|
||||
and mi.etudid = %(etudid)s
|
||||
""",
|
||||
{"jour": a["jour"].strftime("%Y-%m-%d"), "etudid": etudid},
|
||||
@ -984,9 +1014,10 @@ def _tables_abs_etud(
|
||||
)[0]
|
||||
if format == "html":
|
||||
ex.append(
|
||||
f"""<a title="{mod['module']['titre']}" href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"] or "(module sans code)"}</a>"""
|
||||
f"""<a title="{mod['module']['titre']}" href="{
|
||||
url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])
|
||||
}">{mod["module"]["code"] or "(module sans code)"}</a>"""
|
||||
)
|
||||
else:
|
||||
ex.append(mod["module"]["code"] or "(module sans code)")
|
||||
@ -1003,7 +1034,7 @@ def _tables_abs_etud(
|
||||
if format == "html":
|
||||
ex.append(
|
||||
f"""<a title="{mod['module']['titre']}"
|
||||
href="{url_for('notes.moduleimpl_status',
|
||||
href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"] or '(module sans code)'}</a>"""
|
||||
)
|
||||
|
@ -321,9 +321,10 @@ def filter_by_formsemestre(
|
||||
def justifies(justi: Justificatif, obj: bool = False) -> list[int] or Query:
|
||||
"""
|
||||
Retourne la liste des assiduite_id qui sont justifié par la justification
|
||||
Une assiduité est justifiée si elle est COMPLETEMENT ou PARTIELLEMENT comprise dans la plage du justificatif
|
||||
et que l'état du justificatif est "valide"
|
||||
renvoie des id si obj == False, sinon les Assiduités
|
||||
Une assiduité est justifiée si elle est COMPLETEMENT ou PARTIELLEMENT
|
||||
comprise dans la plage du justificatif
|
||||
et que l'état du justificatif est "valide".
|
||||
Renvoie des id si obj == False, sinon les Assiduités
|
||||
"""
|
||||
|
||||
if justi.etat != scu.EtatJustificatif.VALIDE:
|
||||
@ -427,7 +428,7 @@ def invalidate_assiduites_count(etudid, sem):
|
||||
"""Invalidate (clear) cached counts"""
|
||||
date_debut = sem["date_debut_iso"]
|
||||
date_fin = sem["date_fin_iso"]
|
||||
for met in sco_preferences.ASSIDUITES_METRIC_LABEL.values():
|
||||
for met in scu.AssiduitesMetrics.TAG:
|
||||
key = str(etudid) + "_" + date_debut + "_" + date_fin + f"{met}_assiduites"
|
||||
sco_cache.AbsSemEtudCache.delete(key)
|
||||
|
||||
@ -444,9 +445,9 @@ def invalidate_assiduites_count_sem(sem):
|
||||
|
||||
|
||||
def invalidate_assiduites_etud_date(etudid, date: datetime):
|
||||
"""Doit etre appelé à chaque modification des assiduites pour cet étudiant et cette date.
|
||||
"""Doit etre appelé à chaque modification des assiduites
|
||||
pour cet étudiant et cette date.
|
||||
Invalide cache absence et caches semestre
|
||||
date: date au format ISO
|
||||
"""
|
||||
from app.scodoc import sco_compute_moy
|
||||
|
||||
|
@ -112,7 +112,7 @@ def formsemestre_bulletinetud_published_dict(
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
if not etudid in nt.identdict:
|
||||
if etudid not in nt.identdict:
|
||||
abort(404, "etudiant non inscrit dans ce semestre")
|
||||
d = {"type": "classic", "version": "0"}
|
||||
if (not sem["bul_hide_xml"]) or force_publishing:
|
||||
|
@ -198,13 +198,6 @@ def _get_pref_default_value_from_config(name, pref_spec):
|
||||
|
||||
_INSTALLED_FONTS = ", ".join(sco_pdf.get_available_font_names())
|
||||
|
||||
ASSIDUITES_METRIC_LABEL = {
|
||||
# l'ordre est important, c'est celui-du menu. Le defaut en 1er donc.
|
||||
"1/2 J.": "demi",
|
||||
"J.": "journee",
|
||||
"H.": "heure",
|
||||
}
|
||||
|
||||
PREF_CATEGORIES = (
|
||||
# sur page "Paramètres"
|
||||
("general", {"title": ""}), # voir paramètre titlr de TrivialFormulator
|
||||
@ -666,8 +659,8 @@ class BasePreferences(object):
|
||||
{
|
||||
"initvalue": "1/2 J.",
|
||||
"input_type": "menu",
|
||||
"labels": list(ASSIDUITES_METRIC_LABEL.keys()),
|
||||
"allowed_values": list(ASSIDUITES_METRIC_LABEL.keys()),
|
||||
"labels": scu.AssiduitesMetrics.LONG,
|
||||
"allowed_values": scu.AssiduitesMetrics.SHORT,
|
||||
"title": "Métrique de l'assiduité",
|
||||
"explanation": "Unité utilisée dans la fiche étudiante, les bilans et les calculs",
|
||||
"category": "assi",
|
||||
|
@ -166,6 +166,11 @@ class BiDirectionalEnum(Enum):
|
||||
"""Vérifie sur un attribut existe dans l'enum"""
|
||||
return attr.upper() in cls._member_names_
|
||||
|
||||
@classmethod
|
||||
def all(cls, keys=True):
|
||||
"""Retourne toutes les clés de l'enum"""
|
||||
return cls._member_names_ if keys else list(cls._value2member_map_.keys())
|
||||
|
||||
@classmethod
|
||||
def get(cls, attr: str, default: any = None):
|
||||
"""Récupère une valeur à partir de son attribut"""
|
||||
@ -254,15 +259,54 @@ def is_period_overlapping(
|
||||
return p_deb < i_fin and p_fin > i_deb
|
||||
|
||||
|
||||
def translate_assiduites_metric(hr_metric) -> str:
|
||||
if hr_metric == "1/2 J.":
|
||||
return "demi"
|
||||
if hr_metric == "J.":
|
||||
return "journee"
|
||||
if hr_metric == "N.":
|
||||
return "compte"
|
||||
if hr_metric == "H.":
|
||||
return "heure"
|
||||
class AssiduitesMetrics:
|
||||
"""Labels associés au métrique de l'assiduité"""
|
||||
|
||||
SHORT: list[str] = ["1/2 J.", "J.", "H."]
|
||||
LONG: list[str] = ["Demi-journée", "Journée", "Heure"]
|
||||
TAG: list[str] = ["demi", "journee", "heure"]
|
||||
|
||||
|
||||
def translate_assiduites_metric(metric, inverse=True, short=True) -> str:
|
||||
"""
|
||||
translate_assiduites_metric
|
||||
|
||||
SHORT[true] : "J." "H." "N." "1/2 J."
|
||||
SHORT[false] : "Journée" "Heure" "Nombre" "Demi-Journée"
|
||||
|
||||
inverse[false] : "demi" -> "1/2 J."
|
||||
inverse[true] : "1/2 J." -> "demi"
|
||||
|
||||
|
||||
Args:
|
||||
metric (str): la métrique à traduire
|
||||
inverse (bool, optional). Defaults to True.
|
||||
short (bool, optional). Defaults to True.
|
||||
|
||||
Returns:
|
||||
str: la métrique traduite
|
||||
"""
|
||||
index: int = None
|
||||
if not inverse:
|
||||
try:
|
||||
index = AssiduitesMetrics.TAG.index(metric)
|
||||
return (
|
||||
AssiduitesMetrics.SHORT[index]
|
||||
if short
|
||||
else AssiduitesMetrics.LONG[index]
|
||||
)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
try:
|
||||
index = (
|
||||
AssiduitesMetrics.SHORT.index(metric)
|
||||
if short
|
||||
else AssiduitesMetrics.LONG.index(metric)
|
||||
)
|
||||
return AssiduitesMetrics.TAG[index]
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
# Types de modules
|
||||
|
@ -125,8 +125,8 @@ class RowAssi(tb.Row):
|
||||
"absent": ["Absences", 0.0, 0.0],
|
||||
}
|
||||
|
||||
assi_metric = sco_preferences.ASSIDUITES_METRIC_LABEL.get(
|
||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id)
|
||||
assi_metric = scu.translate_assiduites_metric(
|
||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
||||
)
|
||||
|
||||
for etat, valeur in retour.items():
|
||||
|
@ -336,19 +336,21 @@
|
||||
|
||||
}
|
||||
const defAnnee = {{ annee }}
|
||||
let annees = {{ annees | safe }}
|
||||
annees = annees.filter((x, i) => annees.indexOf(x) === i)
|
||||
const etudid = {{ sco.etud.id }};
|
||||
const nonwork = [{{ nonworkdays | safe }}];
|
||||
window.onload = () => {
|
||||
const select = document.querySelector('#annee');
|
||||
for (let i = defAnnee + 1; i > defAnnee - 6; i--) {
|
||||
annees.forEach((a) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = i + "",
|
||||
opt.textContent = i + "";
|
||||
if (i === defAnnee) {
|
||||
opt.value = a + "",
|
||||
opt.textContent = `${a} - ${a + 1}`;
|
||||
if (a === defAnnee) {
|
||||
opt.selected = true;
|
||||
}
|
||||
select.appendChild(opt)
|
||||
}
|
||||
})
|
||||
setterAnnee(defAnnee)
|
||||
};
|
||||
|
||||
|
@ -162,7 +162,11 @@
|
||||
userIdDiv.textContent = `saisi le ${formatDateModal(
|
||||
assiduite.entry_date,
|
||||
"à"
|
||||
)} \npar ${assiduite.user_id}`;
|
||||
)}`;
|
||||
|
||||
if (assiduite.user_id != null) {
|
||||
userIdDiv.textContent += `\npar ${assiduite.user_id}`
|
||||
}
|
||||
bubble.appendChild(userIdDiv);
|
||||
|
||||
bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`;
|
||||
|
@ -327,8 +327,8 @@ def bilan_etud():
|
||||
date_debut: str = f"{scu.annee_scolaire()}-09-01"
|
||||
date_fin: str = f"{scu.annee_scolaire()+1}-06-30"
|
||||
|
||||
assi_metric = sco_preferences.ASSIDUITES_METRIC_LABEL.get(
|
||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id)
|
||||
assi_metric = scu.translate_assiduites_metric(
|
||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
||||
)
|
||||
|
||||
return HTMLBuilder(
|
||||
@ -417,6 +417,16 @@ def calendrier_etud():
|
||||
],
|
||||
)
|
||||
|
||||
annees: list[int] = sorted(
|
||||
[ins.formsemestre.date_debut.year for ins in etud.formsemestre_inscriptions],
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
annees_str: str = "["
|
||||
for ann in annees:
|
||||
annees_str += f"{ann},"
|
||||
annees_str += "]"
|
||||
|
||||
return HTMLBuilder(
|
||||
header,
|
||||
render_template(
|
||||
@ -425,6 +435,7 @@ def calendrier_etud():
|
||||
annee=scu.annee_scolaire(),
|
||||
nonworkdays=_non_work_days(),
|
||||
minitimeline=_mini_timeline(),
|
||||
annees=annees_str,
|
||||
),
|
||||
).build()
|
||||
|
||||
@ -840,8 +851,14 @@ def visu_assi_group():
|
||||
|
||||
return render_template(
|
||||
"assiduites/pages/visu_assi.j2",
|
||||
assi_metric=sco_preferences.ASSIDUITES_METRIC_LABEL.get(
|
||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id)
|
||||
assi_metric=scu.translate_assiduites_metric(
|
||||
scu.translate_assiduites_metric(
|
||||
sco_preferences.get_preference(
|
||||
"assi_metrique", dept_id=g.scodoc_dept_id
|
||||
),
|
||||
),
|
||||
inverse=False,
|
||||
short=False,
|
||||
),
|
||||
date_debut=dates["debut"],
|
||||
date_fin=dates["fin"],
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.9"
|
||||
SCOVERSION = "9.6.11"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
15
scodoc.py
15
scodoc.py
@ -661,7 +661,12 @@ def profile(host, port, length, profile_dir):
|
||||
@click.option(
|
||||
"-n",
|
||||
"--noon",
|
||||
help="Spécifie l'heure de fin du matin (et donc début de l'après-midi) format `hh:mm`",
|
||||
help="Spécifie l'heure de fin du matin format `hh:mm`",
|
||||
)
|
||||
@click.option(
|
||||
"-a",
|
||||
"--afternoon",
|
||||
help="Spécifie l'heure de début de l'après-midi format `hh:mm` valeur identique à --noon si non spécifié",
|
||||
)
|
||||
@click.option(
|
||||
"-e",
|
||||
@ -670,10 +675,14 @@ def profile(host, port, length, profile_dir):
|
||||
)
|
||||
@with_appcontext
|
||||
def migrate_abs_to_assiduites(
|
||||
dept: str = None, morning: str = None, noon: str = None, evening: str = None
|
||||
dept: str = None,
|
||||
morning: str = None,
|
||||
noon: str = None,
|
||||
afternoon: str = None,
|
||||
evening: str = None,
|
||||
): # migrate-abs-to-assiduites
|
||||
"""Permet de migrer les absences vers le nouveau module d'assiduités"""
|
||||
tools.migrate_abs_to_assiduites(dept, morning, noon, evening)
|
||||
tools.migrate_abs_to_assiduites(dept, morning, noon, afternoon, evening)
|
||||
# import cProfile
|
||||
# cProfile.runctx(
|
||||
# f"tools.migrate_abs_to_assiduites({dept})",
|
||||
|
@ -4,6 +4,22 @@
|
||||
# Prend la version dans le code source local et cherche une release gitea de même tag.
|
||||
# Lance ensuite les tests unitaires locaux.
|
||||
|
||||
SKIP_TESTS=0
|
||||
while getopts "s" opt; do
|
||||
case "$opt" in
|
||||
s)
|
||||
SKIP_TESTS=1
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
:)
|
||||
echo "Option -$OPTARG requires an argument." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Le répertoire de ce script: .../scodoc/tools
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
@ -57,13 +73,17 @@ SCODOC_USER=scodoc
|
||||
[ -z "$FACTORY_DIR" ] && die "empty FACTORY_DIR"
|
||||
[ "$(id -nu)" != "$SCODOC_USER" ] && die "Erreur: le script $0 doit être lancé par l'utilisateur $SCODOC_USER"
|
||||
|
||||
# Tests unitaires lancés dans le répertoire de travail
|
||||
echo "TESTS UNITAIRES"
|
||||
(cd "$UNIT_TESTS_DIR"; pytest tests/unit) || terminate "Erreur dans tests unitaires"
|
||||
|
||||
# Tests API
|
||||
(cd "$UNIT_TESTS_DIR"; tools/test_api.sh) || terminate "Erreur dans tests unitaires API"
|
||||
if [ "$SKIP_TESTS" = 1 ]
|
||||
then
|
||||
echo "SKIPPING UNIT TESTS !"
|
||||
else
|
||||
# Tests unitaires lancés dans le répertoire de travail
|
||||
echo "TESTS UNITAIRES"
|
||||
(cd "$UNIT_TESTS_DIR"; pytest tests/unit) || terminate "Erreur dans tests unitaires"
|
||||
|
||||
# Tests API
|
||||
(cd "$UNIT_TESTS_DIR"; tools/test_api.sh) || terminate "Erreur dans tests unitaires API"
|
||||
fi
|
||||
|
||||
# Création répertoire du paquet, et de opt
|
||||
slash="$FACTORY_DIR"/"$DEST_DIR"
|
||||
|
@ -45,7 +45,7 @@ then
|
||||
PSQL=/usr/lib/postgresql/15/bin/psql
|
||||
#export POSTGRES_SERVICE="postgresql@11-main.service"
|
||||
else
|
||||
die "unsupported Debian version"
|
||||
die "unsupported Debian version (${debian_version}, expected 12)"
|
||||
fi
|
||||
export PSQL
|
||||
|
||||
|
@ -47,6 +47,7 @@ class _glob:
|
||||
|
||||
MORNING: time = None
|
||||
NOON: time = None
|
||||
AFTERNOON: time = None
|
||||
EVENING: time = None
|
||||
|
||||
|
||||
@ -93,7 +94,7 @@ class _Merger:
|
||||
time_ = _glob.NOON if end else _glob.MORNING
|
||||
date_ = datetime.combine(couple[0], time_)
|
||||
else:
|
||||
time_ = _glob.EVENING if end else _glob.NOON
|
||||
time_ = _glob.EVENING if end else _glob.AFTERNOON
|
||||
date_ = datetime.combine(couple[0], time_)
|
||||
d = localize_datetime(date_)
|
||||
return d
|
||||
@ -229,6 +230,7 @@ def migrate_abs_to_assiduites(
|
||||
dept: str = None,
|
||||
morning: str = None,
|
||||
noon: str = None,
|
||||
afternoon: str = None,
|
||||
evening: str = None,
|
||||
debug: bool = False,
|
||||
):
|
||||
@ -266,6 +268,12 @@ def migrate_abs_to_assiduites(
|
||||
noon: list[str] = str(noon).split(":")
|
||||
_glob.NOON = time(int(noon[0]), int(noon[1]))
|
||||
|
||||
if afternoon is None:
|
||||
afternoon = ScoDocSiteConfig.get("assi_lunch_time", time(13, 0))
|
||||
|
||||
afternoon: list[str] = str(afternoon).split(":")
|
||||
_glob.AFTERNOON = time(int(afternoon[0]), int(afternoon[1]))
|
||||
|
||||
if evening is None:
|
||||
evening = ScoDocSiteConfig.get("assi_afternoon_time", time(18, 0))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user