Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
6 changed files with 113 additions and 72 deletions
Showing only changes of commit 78add09990 - Show all commits

3
.flake8 Normal file
View File

@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
ignore = E203,W503

View File

@ -85,7 +85,9 @@ Adresses d'origine:
) )
current_app.logger.info( 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} from sender {msg.sender}
""" """
) )
@ -98,7 +100,8 @@ def get_from_addr(dept_acronym: str = None):
"""L'adresse "from" à utiliser pour envoyer un mail """L'adresse "from" à utiliser pour envoyer un mail
Si le departement est spécifié, ou si l'attribut `g.scodoc_dept`existe, 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, utilise le paramètre global `email_from_addr`.
Sinon, la variable de config `SCODOC_MAIL_FROM`. Sinon, la variable de config `SCODOC_MAIL_FROM`.
""" """

View File

@ -318,7 +318,7 @@ def list_abs_in_range(
Returns: Returns:
List of absences List of absences
""" """
if matin != None: if matin is not None:
matin = _toboolean(matin) matin = _toboolean(matin)
ismatin = " AND A.MATIN = %(matin)s " ismatin = " AND A.MATIN = %(matin)s "
else: else:
@ -387,7 +387,7 @@ def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
Returns: Returns:
An integer. An integer.
""" """
if matin != None: if matin is not None:
matin = _toboolean(matin) matin = _toboolean(matin)
ismatin = " AND A.MATIN = %(matin)s " ismatin = " AND A.MATIN = %(matin)s "
else: else:
@ -482,7 +482,9 @@ def _get_abs_description(a, cursor=None):
else: else:
a["matin"] = False a["matin"] = False
cursor.execute( 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,
) )
A = cursor.dictfetchall() 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 req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A
WHERE A.jour = %(date)s WHERE A.jour = %(date)s
""" """
if is_abs != None: if is_abs is not None:
req += " AND A.estabs = %(is_abs)s" req += " AND A.estabs = %(is_abs)s"
if is_just != None: if is_just is not None:
req += " AND A.estjust = %(is_just)s" req += " AND A.estjust = %(is_just)s"
if not am: if not am:
req += " AND NOT matin " req += " AND NOT matin "
@ -883,7 +885,7 @@ def MonthTableBody(
descr = ev[4] descr = ev[4]
# #
cc = [] cc = []
if color != None: if color is not None:
cc.append('<td bgcolor="%s" class="calcell">' % color) cc.append('<td bgcolor="%s" class="calcell">' % color)
else: else:
cc.append('<td class="calcell">') cc.append('<td class="calcell">')
@ -896,7 +898,7 @@ def MonthTableBody(
cc.append("<a %s %s>" % (href, descr)) cc.append("<a %s %s>" % (href, descr))
if legend or d == 1: if legend or d == 1:
if pad_width != None: if pad_width is not None:
n = pad_width - len(legend) # pad to 8 cars n = pad_width - len(legend) # pad to 8 cars
if n > 0: if n > 0:
legend = ( legend = (
@ -959,7 +961,7 @@ def MonthTableBody(
ev_year = int(ev[0][:4]) ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7]) ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10]) ev_day = int(ev[0][8:10])
if ev[4] != None: if ev[4] is not None:
ev_half = int(ev[4]) ev_half = int(ev[4])
else: else:
ev_half = 0 ev_half = 0
@ -978,7 +980,7 @@ def MonthTableBody(
if len(ev) > 5 and ev[5]: if len(ev) > 5 and ev[5]:
descr = ev[5] descr = ev[5]
# #
if color != None: if color is not None:
cc.append('<td bgcolor="%s" class="calcell">' % (color)) cc.append('<td bgcolor="%s" class="calcell">' % (color))
else: else:
cc.append('<td class="calcell">') cc.append('<td class="calcell">')
@ -1072,7 +1074,8 @@ def invalidate_abs_count_sem(sem):
def invalidate_abs_etud_date(etudid, date): # was invalidateAbsEtudDate 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 Invalide cache absence et caches semestre
date: date au format ISO date: date au format ISO
""" """

View File

@ -180,8 +180,12 @@ def SignaleAbsenceEtud(): # etudid implied
"abs_require_module" "abs_require_module"
) # on utilise la pref globale car pas de sem courant ) # on utilise la pref globale car pas de sem courant
if require_module: if require_module:
menu_module = """<div class="ue_warning">Pas inscrit dans un semestre courant, menu_module = """<div class="ue_warning">Pas
et l'indication du module est requise. Donc pas de saisie d'absence possible !</div>""" inscrit dans un semestre courant,
et l'indication du module est requise.
Donc pas de saisie d'absence possible !
</div>
"""
disabled = True disabled = True
else: else:
menu_module = "" menu_module = ""
@ -250,7 +254,10 @@ def SignaleAbsenceEtud(): # etudid implied
<p> <p>
<table><tr> <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> <td>
<input type="text" name="datedebut" size="10" class="datepicker"/>
<em>j/m/a</em>
</td>
<td>&nbsp;&nbsp;&nbsp;Date fin (optionnelle):</td> <td>&nbsp;&nbsp;&nbsp;Date fin (optionnelle):</td>
<td><input type="text" name="datefin" size="10" class="datepicker"/> <em>j/m/a</em></td> <td><input type="text" name="datefin" size="10" class="datepicker"/> <em>j/m/a</em></td>
</tr> </tr>
@ -354,7 +361,10 @@ def doJustifAbsence(
) )
H.append( 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="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="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li>
<li><a href="ListeAbsEtud?etudid=%(etudid)s">Liste de ses absences</a></li> <li><a href="ListeAbsEtud?etudid=%(etudid)s">Liste de ses absences</a></li>
@ -394,7 +404,7 @@ def JustifAbsenceEtud(): # etudid implied
<p> <p>
<table><tr> <table><tr>
<td>Date d&eacute;but : </td> <td>Date début : </td>
<td> <td>
<input type="text" name="datedebut" size="10" class="datepicker"/> <input type="text" name="datedebut" size="10" class="datepicker"/>
</td> </td>
@ -458,8 +468,10 @@ def doAnnuleAbsence(datedebut, datefin, demijournee, etudid=False): # etudid im
H.append( H.append(
"""<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une """<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une
autre absence pour <b>%(nomprenom)s</b></a></li> 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="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li> absence</a></li>
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses
absences</a></li>
</ul> </ul>
<hr>""" <hr>"""
% etud % etud
@ -480,10 +492,11 @@ def AnnuleAbsenceEtud(): # etudid implied
page_title="Annulation d'une absence pour %(nomprenom)s" % etud, page_title="Annulation d'une absence pour %(nomprenom)s" % etud,
), ),
"""<table><tr><td> """<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> </td><td>
""" """
% etud, # " % etud,
"""<a href="%s">""" """<a href="%s">"""
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid), % url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
sco_photos.etud_photo_html( sco_photos.etud_photo_html(
@ -491,8 +504,11 @@ def AnnuleAbsenceEtud(): # etudid implied
title="fiche de " + etud["nomprenom"], title="fiche de " + etud["nomprenom"],
), ),
"""</a></td></tr></table>""", """</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>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que
<p><font color="#FF0000">Si plusieurs modules sont affectés, les absences seront toutes effacées. </font></p> 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, % etud,
"""<table frame="border" border="1"><tr><td> """<table frame="border" border="1"><tr><td>
@ -500,7 +516,7 @@ def AnnuleAbsenceEtud(): # etudid implied
<input type="hidden" name="etudid" value="%(etudid)s"> <input type="hidden" name="etudid" value="%(etudid)s">
<p> <p>
<table><tr> <table><tr>
<td>Date d&eacute;but : </td> <td>Date début : </td>
<td> <td>
<input type="text" name="datedebut" size="10" class="datepicker"/> <em>j/m/a</em> <input type="text" name="datedebut" size="10" class="datepicker"/> <em>j/m/a</em>
</td> </td>
@ -511,7 +527,7 @@ def AnnuleAbsenceEtud(): # etudid implied
</tr> </tr>
</table> </table>
<input type="radio" name="demijournee" value="2" checked>journ&eacute;e(s) <input type="radio" name="demijournee" value="2" checked>journée(s)
&nbsp;<input type="radio" name="demijournee" value="1">Matin(s) &nbsp;<input type="radio" name="demijournee" value="1">Matin(s)
&nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi &nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi
@ -526,7 +542,7 @@ def AnnuleAbsenceEtud(): # etudid implied
<input type="hidden" name="etudid" value="%(etudid)s"> <input type="hidden" name="etudid" value="%(etudid)s">
<p> <p>
<table><tr> <table><tr>
<td>Date d&eacute;but : </td> <td>Date début : </td>
<td> <td>
<input type="text" name="datedebut0" size="10" class="datepicker"/> <em>j/m/a</em> <input type="text" name="datedebut0" size="10" class="datepicker"/> <em>j/m/a</em>
</td> </td>
@ -538,14 +554,15 @@ def AnnuleAbsenceEtud(): # etudid implied
</table> </table>
<p> <p>
<input type="radio" name="demijournee" value="2" checked>journ&eacute;e(s) <input type="radio" name="demijournee" value="2" checked>journée(s)
&nbsp;<input type="radio" name="demijournee" value="1">Matin(s) &nbsp;<input type="radio" name="demijournee" value="1">Matin(s)
&nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi &nbsp;<input type="radio" name="demijournee" value="0">Apr&egrave;s midi
<p> <p>
<input type="submit" value="Supprimer les justificatifs"> <input type="submit" value="Supprimer les justificatifs">
<i>(utiliser ceci en cas de justificatif erron&eacute; saisi ind&eacute;pendemment d'une absence)</i> <i>(utiliser ceci en cas de justificatif erroné saisi indépendemment
d'une absence)</i>
</form> </form>
</td></tr></table>""" </td></tr></table>"""
% etud, % etud,
@ -591,8 +608,10 @@ def doAnnuleJustif(datedebut0, datefin0, demijournee): # etudid implied
H.append( H.append(
"""<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une """<ul><li><a href="AnnuleAbsenceEtud?etudid=%(etudid)s">Annulation d'une
autre absence pour <b>%(nomprenom)s</b></a></li> 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="SignaleAbsenceEtud?etudid=%(etudid)s">Ajout d'une
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses absences</a></li> absence</a></li>
<li><a href="CalAbs?etudid=%(etudid)s">Calendrier de ses
absences</a></li>
</ul> </ul>
<hr>""" <hr>"""
% etud % etud
@ -635,7 +654,10 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None):
for date in dates: for date in dates:
cursor.execute( cursor.execute(
"""DELETE FROM absences """DELETE FROM absences
WHERE etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s WHERE etudid=%(etudid)s
AND (not estjust)
AND jour=%(date)s
AND moduleimpl_id=%(moduleimpl_id)s
""", """,
vars(), vars(),
) )
@ -643,8 +665,11 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None):
# s'assure que les justificatifs ne sont pas "absents" # s'assure que les justificatifs ne sont pas "absents"
for date in dates: for date in dates:
cursor.execute( cursor.execute(
"""UPDATE absences SET estabs=FALSE """UPDATE absences
WHERE etudid=%(etudid)s AND jour=%(date)s AND moduleimpl_id=%(moduleimpl_id)s SET estabs=FALSE
WHERE etudid=%(etudid)s
AND jour=%(date)s
AND moduleimpl_id=%(moduleimpl_id)s
""", """,
vars(), vars(),
) )
@ -724,7 +749,7 @@ def _convert_sco_year(year) -> int:
year = int(year) year = int(year)
if year > 1900 and year < 2999: if year > 1900 and year < 2999:
return year return year
except: except ValueError:
raise ScoValueError("année scolaire invalide") 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> """<b><font color="#EE0000">A : absence NON justifiée</font><br>
<font color="#F8B7B0">a : absence justifiée</font><br> <font color="#F8B7B0">a : absence justifiée</font><br>
<font color="#8EA2C6">X : justification sans absence</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> </p>
""" """
% (nbabs, nbabsjust, nbabs - nbabsjust, len(justifs_noabs)), % (nbabs, nbabsjust, nbabs - nbabsjust, len(justifs_noabs)),
@ -790,7 +816,8 @@ def CalAbs(etudid, sco_year=None):
"""<form method="GET" action="CalAbs" name="f">""", """<form method="GET" action="CalAbs" name="f">""",
"""<input type="hidden" name="etudid" value="%s"/>""" % etudid, """<input type="hidden" name="etudid" value="%s"/>""" % etudid,
"""Année scolaire %s-%s""" % (annee_scolaire, annee_scolaire + 1), """Année scolaire %s-%s""" % (annee_scolaire, annee_scolaire + 1),
"""&nbsp;&nbsp;Changer année: <select name="sco_year" onchange="document.f.submit()">""", """&nbsp;&nbsp;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): for y in range(annee_courante, min(annee_courante - 6, annee_scolaire - 6), -1):
H.append("""<option value="%s" """ % y) H.append("""<option value="%s" """ % y)
@ -819,7 +846,8 @@ def ListeAbsEtud(
etudid: etudid:
with_evals: indique les evaluations aux dates d'absences with_evals: indique les evaluations aux dates d'absences
absjust_only: si vrai, renvoie table absences justifiées 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) # si absjust_only, table absjust seule (export xls ou pdf)
absjust_only = scu.to_bool(absjust_only) absjust_only = scu.to_bool(absjust_only)
@ -941,7 +969,9 @@ def _tables_abs_etud(
for a in absnonjust + absjust: for a in absnonjust + absjust:
cursor.execute( cursor.execute(
"""SELECT eval.* """SELECT eval.*
FROM notes_evaluation eval, notes_moduleimpl_inscription mi, notes_moduleimpl m FROM notes_evaluation eval,
notes_moduleimpl_inscription mi,
notes_moduleimpl m
WHERE eval.jour = %(jour)s WHERE eval.jour = %(jour)s
and eval.moduleimpl_id = m.id and eval.moduleimpl_id = m.id
and mi.moduleimpl_id = m.id and mi.moduleimpl_id = m.id
@ -984,9 +1014,10 @@ def _tables_abs_etud(
)[0] )[0]
if format == "html": if format == "html":
ex.append( ex.append(
f"""<a title="{mod['module']['titre']}" href="{url_for('notes.moduleimpl_status', f"""<a title="{mod['module']['titre']}" href="{
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])} url_for('notes.moduleimpl_status',
">{mod["module"]["code"] or "(module sans code)"}</a>""" scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])
}">{mod["module"]["code"] or "(module sans code)"}</a>"""
) )
else: else:
ex.append(mod["module"]["code"] or "(module sans code)") ex.append(mod["module"]["code"] or "(module sans code)")

View File

@ -321,9 +321,10 @@ def filter_by_formsemestre(
def justifies(justi: Justificatif, obj: bool = False) -> list[int] or Query: def justifies(justi: Justificatif, obj: bool = False) -> list[int] or Query:
""" """
Retourne la liste des assiduite_id qui sont justifié par la justification 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 Une assiduité est justifiée si elle est COMPLETEMENT ou PARTIELLEMENT
et que l'état du justificatif est "valide" comprise dans la plage du justificatif
renvoie des id si obj == False, sinon les Assiduités et que l'état du justificatif est "valide".
Renvoie des id si obj == False, sinon les Assiduités
""" """
if justi.etat != scu.EtatJustificatif.VALIDE: if justi.etat != scu.EtatJustificatif.VALIDE:
@ -444,9 +445,9 @@ def invalidate_assiduites_count_sem(sem):
def invalidate_assiduites_etud_date(etudid, date: datetime): 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 Invalide cache absence et caches semestre
date: date au format ISO
""" """
from app.scodoc import sco_compute_moy from app.scodoc import sco_compute_moy

View File

@ -112,7 +112,7 @@ def formsemestre_bulletinetud_published_dict(
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) 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") abort(404, "etudiant non inscrit dans ce semestre")
d = {"type": "classic", "version": "0"} d = {"type": "classic", "version": "0"}
if (not sem["bul_hide_xml"]) or force_publishing: if (not sem["bul_hide_xml"]) or force_publishing: