WIP migration vues en cours / tout est en vrac !

This commit is contained in:
Emmanuel Viennet 2021-06-02 22:40:34 +02:00
parent 77f68d1c4c
commit dcb53e9c35
25 changed files with 4503 additions and 85 deletions

View File

@ -50,6 +50,27 @@ Installer le bon vieux `pyExcelerator` dans l'environnement:
python -m unittest tests.test_users python -m unittest tests.test_users
# Work in Progress
## Migration ZScolar
### Méthodes qui ne devraient plus être publiées:
security.declareProtected(ScoView, "get_preferences")
def get_preferences(context, formsemestre_id=None):
"Get preferences for this instance (a dict-like instance)"
return sco_preferences.sem_preferences(context, formsemestre_id)
security.declareProtected(ScoView, "get_preference")
def get_preference(context, name, formsemestre_id=None):
"""Returns value of named preference.
All preferences have a sensible default value (see sco_preferences.py),
this function always returns a usable value for all defined preferences names.
"""
return sco_preferences.get_base_preferences(context).get(formsemestre_id, name)

View File

@ -904,15 +904,16 @@ class ZAbsences(
etuds = [e for e in etuds if e["etudid"] in mod_inscrits] etuds = [e for e in etuds if e["etudid"] in mod_inscrits]
if not moduleimpl_id: if not moduleimpl_id:
moduleimpl_id = None moduleimpl_id = None
base_url_noweeks = "SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s" % ( base_url_noweeks = (
datedebut, "SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s"
datefin, % (
groups_infos.groups_query_args, datedebut,
urllib.quote(destination), datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
)
) )
base_url = ( base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id
base_url_noweeks + "&nbweeks=%s" % nbweeks
) # sans le moduleimpl_id
if etuds: if etuds:
nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id) nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id)

View File

@ -557,7 +557,8 @@ def formsemestre_delete_archive(
dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id) dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id)
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression de l'archive du %s ?</h2> """<h2>Confirmer la suppression de l'archive du %s ?</h2>
<p>La suppression sera définitive.</p>""" <p>La suppression sera définitive.</p>"""
% PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M"), % PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M"),

View File

@ -201,7 +201,8 @@ def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed
archive_id = EtudsArchive.get_id_from_name(context, etudid, archive_name) archive_id = EtudsArchive.get_id_from_name(context, etudid, archive_name)
dest_url = "ficheEtud?etudid=%s" % etudid dest_url = "ficheEtud?etudid=%s" % etudid
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression des fichiers ?</h2> """<h2>Confirmer la suppression des fichiers ?</h2>
<p>Fichier associé le %s à l'étudiant %s</p> <p>Fichier associé le %s à l'étudiant %s</p>
<p>La suppression sera définitive.</p>""" <p>La suppression sera définitive.</p>"""

View File

@ -65,7 +65,8 @@ def formation_delete(context, formation_id=None, dialog_confirmed=False, REQUEST
H.append('</ul><p><a href="%s">Revenir</a></p>' % context.NotesURL()) H.append('</ul><p><a href="%s">Revenir</a></p>' % context.NotesURL())
else: else:
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression de la formation %(titre)s (%(acronyme)s) ?</h2> """<h2>Confirmer la suppression de la formation %(titre)s (%(acronyme)s) ?</h2>
<p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b> et implique la supression de toutes les UE, matières et modules de la formation ! <p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b> et implique la supression de toutes les UE, matières et modules de la formation !
</p> </p>

View File

@ -267,7 +267,8 @@ def ue_delete(
ue = ue[0] ue = ue[0]
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue, "<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,

View File

@ -215,9 +215,9 @@ def apo_semset_maq_status(
) )
if apo_dups: if apo_dups:
url_list = ( url_list = "view_apo_etuds?semset_id=%s&title=Doublons%%20Apogee&nips=%s" % (
"view_apo_etuds?semset_id=%s&title=Doublons%%20Apogee&nips=%s" semset_id,
% (semset_id, "&nips=".join(apo_dups)) "&nips=".join(apo_dups),
) )
H.append( H.append(
'<li><a href="%s">%d étudiants</a> présents dans les <em>plusieurs</em> maquettes Apogée chargées</li>' '<li><a href="%s">%d étudiants</a> présents dans les <em>plusieurs</em> maquettes Apogée chargées</li>'
@ -659,7 +659,8 @@ def view_apo_csv_delete(
semset = sco_semset.SemSet(context, semset_id=semset_id) semset = sco_semset.SemSet(context, semset_id=semset_id)
dest_url = "apo_semset_maq_status?semset_id=" + semset_id dest_url = "apo_semset_maq_status?semset_id=" + semset_id
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression du fichier étape <tt>%s</tt>?</h2> """<h2>Confirmer la suppression du fichier étape <tt>%s</tt>?</h2>
<p>La suppression sera définitive.</p>""" <p>La suppression sera définitive.</p>"""
% (etape_apo,), % (etape_apo,),

View File

@ -66,6 +66,12 @@ class FormatError(ScoValueError):
pass pass
class ScoInvalidDept(ScoValueError):
"""departement invalide"""
pass
class ScoConfigurationError(ScoValueError): class ScoConfigurationError(ScoValueError):
"""Configuration invalid""" """Configuration invalid"""

View File

@ -1129,7 +1129,8 @@ def formsemestre_associate_new_version(
% (s["formsemestre_id"], checked, disabled, s["titremois"]) % (s["formsemestre_id"], checked, disabled, s["titremois"])
) )
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Associer à une nouvelle version de formation non verrouillée ?</h2> """<h2>Associer à une nouvelle version de formation non verrouillée ?</h2>
<p>Le programme pédagogique ("formation") va être dupliqué pour que vous puissiez le modifier sans affecter les autres semestres. Les autres paramètres (étudiants, notes...) du semestre seront inchangés.</p> <p>Le programme pédagogique ("formation") va être dupliqué pour que vous puissiez le modifier sans affecter les autres semestres. Les autres paramètres (étudiants, notes...) du semestre seront inchangés.</p>
<p>Veillez à ne pas abuser de cette possibilité, car créer trop de versions de formations va vous compliquer la gestion (à vous de garder trace des différences et à ne pas vous tromper par la suite...). <p>Veillez à ne pas abuser de cette possibilité, car créer trop de versions de formations va vous compliquer la gestion (à vous de garder trace des différences et à ne pas vous tromper par la suite...).
@ -1280,7 +1281,8 @@ def formsemestre_delete2(
"""Delete a formsemestre (confirmation)""" """Delete a formsemestre (confirmation)"""
# Confirmation dialog # Confirmation dialog
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Vous voulez vraiment supprimer ce semestre ???</h2><p>(opération irréversible)</p>""", """<h2>Vous voulez vraiment supprimer ce semestre ???</h2><p>(opération irréversible)</p>""",
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,
@ -1423,7 +1425,8 @@ def formsemestre_change_lock(
msg = "déverrouillage" msg = "déverrouillage"
else: else:
msg = "verrouillage" msg = "verrouillage"
return context.confirmDialog( return scu.confirm_dialog(
context,
"<h2>Confirmer le %s du semestre ?</h2>" % msg, "<h2>Confirmer le %s du semestre ?</h2>" % msg,
helpmsg="""Les notes d'un semestre verrouillé ne peuvent plus être modifiées. helpmsg="""Les notes d'un semestre verrouillé ne peuvent plus être modifiées.
Un semestre verrouillé peut cependant être déverrouillé facilement à tout moment Un semestre verrouillé peut cependant être déverrouillé facilement à tout moment
@ -1462,7 +1465,8 @@ def formsemestre_change_publication_bul(
msg = "non" msg = "non"
else: else:
msg = "" msg = ""
return context.confirmDialog( return scu.confirm_dialog(
context,
"<h2>Confirmer la %s publication des bulletins ?</h2>" % msg, "<h2>Confirmer la %s publication des bulletins ?</h2>" % msg,
helpmsg="""Il est parfois utile de désactiver la diffusion des bulletins, helpmsg="""Il est parfois utile de désactiver la diffusion des bulletins,
par exemple pendant la tenue d'un jury ou avant harmonisation des notes. par exemple pendant la tenue d'un jury ou avant harmonisation des notes.

View File

@ -725,12 +725,130 @@ def formsemestre_lists(context, formsemestre_id, REQUEST=None):
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
H = [ H = [
context.html_sem_header(REQUEST, "", sem), context.html_sem_header(REQUEST, "", sem),
context.make_listes_sem(sem, REQUEST), _make_listes_sem(context, sem, REQUEST),
context.sco_footer(REQUEST), context.sco_footer(REQUEST),
] ]
return "\n".join(H) return "\n".join(H)
# genere liste html pour accès aux groupes de ce semestre
# XXX #sco8 vérifier si c'est encore utilisé !
def _make_listes_sem(context, sem, REQUEST=None, with_absences=True):
context = context
authuser = REQUEST.AUTHENTICATED_USER
r = context.ScoURL() # root url
# construit l'URL "destination"
# (a laquelle on revient apres saisie absences)
query_args = cgi.parse_qs(REQUEST.QUERY_STRING)
if "head_message" in query_args:
del query_args["head_message"]
destination = "%s?%s" % (REQUEST.URL, urllib.urlencode(query_args, True))
destination = destination.replace(
"%", "%%"
) # car ici utilisee dans un format string !
#
H = []
# pas de menu absences si pas autorise:
if with_absences and not authuser.has_permission(ScoAbsChange, context):
with_absences = False
#
H.append(
'<h3>Listes de %(titre)s <span class="infostitresem">(%(mois_debut)s - %(mois_fin)s)</span></h3>'
% sem
)
formsemestre_id = sem["formsemestre_id"]
# calcule dates 1er jour semaine pour absences
try:
if with_absences:
first_monday = sco_abs.ddmmyyyy(sem["date_debut"]).prev_monday()
FA = [] # formulaire avec menu saisi absences
FA.append(
'<td><form action="Absences/SignaleAbsenceGrSemestre" method="get">'
)
FA.append(
'<input type="hidden" name="datefin" value="%(date_fin)s"/>' % sem
)
FA.append('<input type="hidden" name="group_ids" value="%(group_id)s"/>')
FA.append(
'<input type="hidden" name="destination" value="%s"/>' % destination
)
FA.append('<input type="submit" value="Saisir absences du" />')
FA.append('<select name="datedebut" class="noprint">')
date = first_monday
for jour in sco_abs.day_names(context):
FA.append('<option value="%s">%s</option>' % (date, jour))
date = date.next()
FA.append("</select>")
FA.append(
'<a href="Absences/EtatAbsencesGr?group_ids=%%(group_id)s&debut=%(date_debut)s&fin=%(date_fin)s">état</a>'
% sem
)
FA.append("</form></td>")
FormAbs = "\n".join(FA)
else:
FormAbs = ""
except ScoInvalidDateError: # dates incorrectes dans semestres ?
FormAbs = ""
#
H.append('<div id="grouplists">')
# Genere liste pour chaque partition (categorie de groupes)
for partition in sco_groups.get_partitions_list(context, sem["formsemestre_id"]):
if not partition["partition_name"]:
H.append("<h4>Tous les étudiants</h4>" % partition)
else:
H.append("<h4>Groupes de %(partition_name)s</h4>" % partition)
groups = sco_groups.get_partition_groups(context, partition)
if groups:
H.append("<table>")
for group in groups:
n_members = len(
sco_groups.get_group_members(context, group["group_id"])
)
group["url"] = r
if group["group_name"]:
group["label"] = "groupe %(group_name)s" % group
else:
group["label"] = "liste"
H.append('<tr class="listegroupelink">')
H.append(
"""<td>
<a href="%(url)s/groups_view?group_ids=%(group_id)s">%(label)s</a>
</td><td>
(<a href="%(url)s/groups_view?group_ids=%(group_id)s&format=xls">format tableur</a>)
<a href="%(url)s/groups_view?curtab=tab-photos&group_ids=%(group_id)s&etat=I">Photos</a>
</td>"""
% group
)
H.append("<td>(%d étudiants)</td>" % n_members)
if with_absences:
H.append(FormAbs % group)
H.append("</tr>")
H.append("</table>")
else:
H.append('<p class="help indent">Aucun groupe dans cette partition')
if sco_groups.can_change_groups(context, REQUEST, formsemestre_id):
H.append(
' (<a href="affectGroups?partition_id=%s" class="stdlink">créer</a>)'
% partition["partition_id"]
)
H.append("</p>")
if sco_groups.can_change_groups(context, REQUEST, formsemestre_id):
H.append(
'<h4><a href="editPartitionForm?formsemestre_id=%s">Ajouter une partition</a></h4>'
% formsemestre_id
)
H.append("</div>")
return "\n".join(H)
def html_expr_diagnostic(context, diagnostics): def html_expr_diagnostic(context, diagnostics):
"""Affiche messages d'erreur des formules utilisateurs""" """Affiche messages d'erreur des formules utilisateurs"""
H = [] H = []

View File

@ -973,7 +973,8 @@ def partition_delete(
grnames = "(" + ", ".join([g["group_name"] or "" for g in groups]) + ")" grnames = "(" + ", ".join([g["group_name"] or "" for g in groups]) + ")"
else: else:
grnames = "" grnames = ""
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Supprimer la partition "%s" ?</h2> """<h2>Supprimer la partition "%s" ?</h2>
<p>Les groupes %s de cette partition seront supprimés</p> <p>Les groupes %s de cette partition seront supprimés</p>
""" """

View File

@ -340,7 +340,8 @@ def formsemestre_inscr_passage(
if not a_inscrire and not a_desinscrire: if not a_inscrire and not a_desinscrire:
H.append("""<h3>Il n'y a rien à modifier !</h3>""") H.append("""<h3>Il n'y a rien à modifier !</h3>""")
H.append( H.append(
context.confirmDialog( scu.confirm_dialog(
context,
dest_url="formsemestre_inscr_passage", dest_url="formsemestre_inscr_passage",
add_headers=False, add_headers=False,
cancel_url="formsemestre_inscr_passage?formsemestre_id=" cancel_url="formsemestre_inscr_passage?formsemestre_id="

View File

@ -150,13 +150,27 @@ def _flatten_info(info):
return ids return ids
def _getEtudInfoGroupes(context, group_ids, etat=None):
"""liste triée d'infos (dict) sur les etudiants du groupe indiqué.
Attention: lent, car plusieurs requetes SQL par etudiant !
"""
etuds = []
for group_id in group_ids:
members = sco_groups.get_group_members(context, group_id, etat=etat)
for m in members:
etud = context.getEtudInfo(etudid=m["etudid"], filled=True)[0]
etuds.append(etud)
return etuds
def formsemestre_poursuite_report( def formsemestre_poursuite_report(
context, formsemestre_id, format="html", REQUEST=None context, formsemestre_id, format="html", REQUEST=None
): ):
"""Table avec informations "poursuite" """ """Table avec informations "poursuite" """
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
etuds = context.getEtudInfoGroupes( etuds = _getEtudInfoGroupes(
[sco_groups.get_default_group(context, formsemestre_id)] context, [sco_groups.get_default_group(context, formsemestre_id)]
) )
infos = [] infos = []

View File

@ -88,11 +88,22 @@ def formsemestre_etuds_stats(context, sem, only_primo=False):
bs.append(etud["specialite"]) bs.append(etud["specialite"])
etud["bac-specialite"] = " ".join(bs) etud["bac-specialite"] = " ".join(bs)
# #
if (not only_primo) or context.isPrimoEtud(etud, sem): if (not only_primo) or is_primo_etud(context, etud, sem):
etuds.append(etud) etuds.append(etud)
return etuds return etuds
def is_primo_etud(context, etud, sem):
"""Determine si un (filled) etud a ete inscrit avant ce semestre.
Regarde la liste des semestres dans lesquels l'étudiant est inscrit
"""
now = sem["dateord"]
for s in etud["sems"]: # le + recent d'abord
if s["dateord"] < now:
return False
return True
def _categories_and_results(etuds, category, result): def _categories_and_results(etuds, category, result):
categories = {} categories = {}
results = {} results = {}
@ -416,7 +427,7 @@ def table_suivi_cohorte(
and (not annee_bac or (annee_bac == str(etud["annee_bac"]))) and (not annee_bac or (annee_bac == str(etud["annee_bac"])))
and (not civilite or (civilite == etud["civilite"])) and (not civilite or (civilite == etud["civilite"]))
and (not statut or (statut == etud["statut"])) and (not statut or (statut == etud["statut"]))
and (not only_primo or context.isPrimoEtud(etud, sem)) and (not only_primo or is_primo_etud(context, etud, sem))
): ):
orig_set.add(etudid) orig_set.add(etudid)
# semestres suivants: # semestres suivants:
@ -708,14 +719,16 @@ def formsemestre_suivi_cohorte(
return t return t
base_url = REQUEST.URL0 base_url = REQUEST.URL0
burl = ( burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % (
"%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" base_url,
% (base_url, formsemestre_id, bac, bacspecialite, civilite, statut) formsemestre_id,
bac,
bacspecialite,
civilite,
statut,
) )
if percent: if percent:
pplink = ( pplink = '<p><a href="%s&percent=0">Afficher les résultats bruts</a></p>' % burl
'<p><a href="%s&percent=0">Afficher les résultats bruts</a></p>' % burl
)
else: else:
pplink = ( pplink = (
'<p><a href="%s&percent=1">Afficher les résultats en pourcentages</a></p>' '<p><a href="%s&percent=1">Afficher les résultats en pourcentages</a></p>'
@ -1028,7 +1041,7 @@ def tsp_etud_list(
and (not annee_bac or (annee_bac == str(etud["annee_bac"]))) and (not annee_bac or (annee_bac == str(etud["annee_bac"])))
and (not civilite or (civilite == etud["civilite"])) and (not civilite or (civilite == etud["civilite"]))
and (not statut or (statut == etud["statut"])) and (not statut or (statut == etud["statut"]))
and (not only_primo or context.isPrimoEtud(etud, sem)) and (not only_primo or is_primo_etud(context, etud, sem))
): ):
etuds.append(etud) etuds.append(etud)

View File

@ -349,7 +349,8 @@ def do_evaluation_set_missing(
) )
# Confirm action # Confirm action
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Mettre toutes les notes manquantes de l'évaluation """<h2>Mettre toutes les notes manquantes de l'évaluation
à la valeur %s ?</h2> à la valeur %s ?</h2>
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC) <p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
@ -418,7 +419,8 @@ def evaluation_suppress_alln(context, evaluation_id, REQUEST, dialog_confirmed=F
msg = "<p>Confirmer la suppression des %d notes ?</p>" % nb_suppress msg = "<p>Confirmer la suppression des %d notes ?</p>" % nb_suppress
if existing_decisions: if existing_decisions:
msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>""" msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>"""
return context.confirmDialog( return scu.confirm_dialog(
context,
msg, msg,
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,

View File

@ -362,7 +362,8 @@ def do_semset_delete(context, semset_id, dialog_confirmed=False, REQUEST=None):
raise ScoValueError("empty semset_id") raise ScoValueError("empty semset_id")
s = SemSet(context, semset_id=semset_id) s = SemSet(context, semset_id=semset_id)
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"<h2>Suppression de l'ensemble %(title)s ?</h2>" % s, "<h2>Suppression de l'ensemble %(title)s ?</h2>" % s,
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,

View File

@ -205,7 +205,8 @@ def formsemestre_synchro_etuds(
H.append("""<h3>Il n'y a rien à modifier !</h3>""") H.append("""<h3>Il n'y a rien à modifier !</h3>""")
H.append( H.append(
context.confirmDialog( scu.confirm_dialog(
context,
dest_url="formsemestre_synchro_etuds", dest_url="formsemestre_synchro_etuds",
add_headers=False, add_headers=False,
cancel_url="formsemestre_synchro_etuds?formsemestre_id=" cancel_url="formsemestre_synchro_etuds?formsemestre_id="

View File

@ -185,12 +185,12 @@ def check_local_photos_availability(context, groups_infos, REQUEST, format=""):
parameters = {"group_ids": groups_infos.group_ids, "format": format} parameters = {"group_ids": groups_infos.group_ids, "format": format}
return ( return (
False, False,
context.confirmDialog( scu.confirm_dialog(
context,
"""<p>Attention: %d photos ne sont pas disponibles et ne peuvent pas être exportées.</p><p>Vous pouvez <a class="stdlink" href="%s">exporter seulement les photos existantes</a>""" """<p>Attention: %d photos ne sont pas disponibles et ne peuvent pas être exportées.</p><p>Vous pouvez <a class="stdlink" href="%s">exporter seulement les photos existantes</a>"""
% ( % (
nb_missing, nb_missing,
groups_infos.base_url groups_infos.base_url + "&dialog_confirmed=1&format=%s" % format,
+ "&dialog_confirmed=1&format=%s" % format,
), ),
dest_url="trombino", dest_url="trombino",
OK="Exporter seulement les photos existantes", OK="Exporter seulement les photos existantes",
@ -252,7 +252,8 @@ def trombino_copy_photos(context, group_ids=[], REQUEST=None, dialog_confirmed=F
+ footer + footer
) )
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Copier les photos du portail vers ScoDoc ?</h2> """<h2>Copier les photos du portail vers ScoDoc ?</h2>
<p>Les photos du groupe %s présentes dans ScoDoc seront remplacées par celles du portail (si elles existent).</p> <p>Les photos du groupe %s présentes dans ScoDoc seront remplacées par celles du portail (si elles existent).</p>
<p>(les photos sont normalement automatiquement copiées lors de leur première utilisation, l'usage de cette fonction n'est nécessaire que si les photos du portail ont été modifiées)</p> <p>(les photos sont normalement automatiquement copiées lors de leur première utilisation, l'usage de cette fonction n'est nécessaire que si les photos du portail ont été modifiées)</p>

View File

@ -840,7 +840,25 @@ def log_unknown_etud(context, REQUEST=None, format="html"):
"unknown student: etudid=%s code_nip=%s code_ine=%s" "unknown student: etudid=%s code_nip=%s code_ine=%s"
% (etudid, code_nip, code_ine) % (etudid, code_nip, code_ine)
) )
return context.ScoErrorResponse("unknown student", format=format, REQUEST=REQUEST) return _sco_error_response("unknown student", format=format, REQUEST=REQUEST)
# XXX #sco8 à tester ou ré-écrire
def _sco_error_response(context, msg, format="html", REQUEST=None):
"""Send an error message to the client, in html or xml format."""
REQUEST.RESPONSE.setStatus(404, reason=msg)
if format == "html" or format == "pdf":
raise ScoValueError(msg)
elif format == "xml":
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
doc.error(msg=msg)
return repr(doc)
elif format == "json":
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
return "undefined" # XXX voir quoi faire en cas d'erreur json
else:
raise ValueError("ScoErrorResponse: invalid format")
# XXX # XXX
@ -869,3 +887,55 @@ def return_text_if_published(val, REQUEST):
if REQUEST and not isinstance(val, STRING_TYPES): if REQUEST and not isinstance(val, STRING_TYPES):
return sendJSON(REQUEST, val) return sendJSON(REQUEST, val)
return val return val
def confirm_dialog(
context,
message="<p>Confirmer ?</p>",
OK="OK",
Cancel="Annuler",
dest_url="",
cancel_url="",
target_variable="dialog_confirmed",
parameters={},
add_headers=True, # complete page
REQUEST=None, # required
helpmsg=None,
):
# dialog de confirmation simple
parameters[target_variable] = 1
# Attention: la page a pu etre servie en GET avec des parametres
# si on laisse l'url "action" vide, les parametres restent alors que l'on passe en POST...
if not dest_url:
dest_url = REQUEST.URL
# strip remaining parameters from destination url:
dest_url = urllib.splitquery(dest_url)[0]
H = [
"""<form action="%s" method="post">""" % dest_url,
message,
"""<input type="submit" value="%s"/>""" % OK,
]
if cancel_url:
H.append(
"""<input type ="button" value="%s"
onClick="document.location='%s';"/>"""
% (Cancel, cancel_url)
)
for param in parameters.keys():
if parameters[param] is None:
parameters[param] = ""
if type(parameters[param]) == type([]):
for e in parameters[param]:
H.append('<input type="hidden" name="%s" value="%s"/>' % (param, e))
else:
H.append(
'<input type="hidden" name="%s" value="%s"/>'
% (param, parameters[param])
)
H.append("</form>")
if helpmsg:
H.append('<p class="help">' + helpmsg + "</p>")
if add_headers and REQUEST:
return context.sco_header(REQUEST) + "\n".join(H) + context.sco_footer(REQUEST)
else:
return "\n".join(H)

View File

@ -357,7 +357,8 @@ def _check_duplicate_code(cnx, args, code_name, context, edit=True, REQUEST=None
dest_url = "" dest_url = ""
parameters = {} parameters = {}
if context: if context:
err_page = context.confirmDialog( err_page = scu.confirm_dialog(
context,
message="""<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name, message="""<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name,
helpmsg="""Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.<p><ul><li>""" helpmsg="""Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.<p><ul><li>"""
% (code_name, args[code_name]) % (code_name, args[code_name])
@ -866,3 +867,171 @@ for l in f:
o.close() o.close()
""" """
def list_scolog(context, etudid):
"liste des operations effectuees sur cet etudiant"
cnx = context.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute(
"select * from scolog where etudid=%(etudid)s ORDER BY DATE DESC",
{"etudid": etudid},
)
return cursor.dictfetchall()
def fillEtudsInfo(context, etuds):
"""etuds est une liste d'etudiants (mappings)
Pour chaque etudiant, ajoute ou formatte les champs
-> informations pour fiche etudiant ou listes diverses
"""
cnx = context.GetDBConnexion()
# open('/tmp/t','w').write( str(etuds) )
for etud in etuds:
etudid = etud["etudid"]
etud["dept"] = context.DeptId()
adrs = adresse_list(cnx, {"etudid": etudid})
if not adrs:
# certains "vieux" etudiants n'ont pas d'adresse
adr = {}.fromkeys(_adresseEditor.dbfields, "")
adr["etudid"] = etudid
else:
adr = adrs[0]
if len(adrs) > 1:
log("fillEtudsInfo: etudid=%s a %d adresses" % (etudid, len(adrs)))
etud.update(adr)
format_etud_ident(etud)
# Semestres dans lesquel il est inscrit
ins = context.Notes.do_formsemestre_inscription_list({"etudid": etudid})
etud["ins"] = ins
sems = []
cursem = None # semestre "courant" ou il est inscrit
for i in ins:
sem = sco_formsemestre.get_formsemestre(context, i["formsemestre_id"])
if sco_formsemestre.sem_est_courant(context, sem):
cursem = sem
curi = i
sem["ins"] = i
sems.append(sem)
# trie les semestres par date de debut, le plus recent d'abord
# (important, ne pas changer (suivi cohortes))
sems.sort(lambda x, y: cmp(y["dateord"], x["dateord"]))
etud["sems"] = sems
etud["cursem"] = cursem
if cursem:
etud["inscription"] = cursem["titremois"]
etud["inscriptionstr"] = "Inscrit en " + cursem["titremois"]
etud["inscription_formsemestre_id"] = cursem["formsemestre_id"]
etud["etatincursem"] = curi["etat"]
etud["situation"] = descr_situation_etud(context, etudid, etud["ne"])
# XXX est-ce utile ? sco_groups.etud_add_group_infos(context, etud, cursem)
else:
if etud["sems"]:
if etud["sems"][0]["dateord"] > time.strftime(
"%Y-%m-%d", time.localtime()
):
etud["inscription"] = "futur"
etud["situation"] = "futur élève"
else:
etud["inscription"] = "ancien"
etud["situation"] = "ancien élève"
else:
etud["inscription"] = "non inscrit"
etud["situation"] = etud["inscription"]
etud["inscriptionstr"] = etud["inscription"]
etud["inscription_formsemestre_id"] = None
# XXXetud['partitions'] = {} # ne va pas chercher les groupes des anciens semestres
etud["etatincursem"] = "?"
# nettoyage champs souvents vides
if etud["nomlycee"]:
etud["ilycee"] = "Lycée " + format_lycee(etud["nomlycee"])
if etud["villelycee"]:
etud["ilycee"] += " (%s)" % etud["villelycee"]
etud["ilycee"] += "<br/>"
else:
if etud["codelycee"]:
etud["ilycee"] = format_lycee_from_code(etud["codelycee"])
else:
etud["ilycee"] = ""
rap = ""
if etud["rapporteur"] or etud["commentaire"]:
rap = "Note du rapporteur"
if etud["rapporteur"]:
rap += " (%s)" % etud["rapporteur"]
rap += ": "
if etud["commentaire"]:
rap += "<em>%s</em>" % etud["commentaire"]
etud["rap"] = rap
# if etud['boursier_prec']:
# pass
if etud["telephone"]:
etud["telephonestr"] = "<b>Tél.:</b> " + format_telephone(etud["telephone"])
else:
etud["telephonestr"] = ""
if etud["telephonemobile"]:
etud["telephonemobilestr"] = "<b>Mobile:</b> " + format_telephone(
etud["telephonemobile"]
)
else:
etud["telephonemobilestr"] = ""
etud["debouche"] = etud["debouche"] or ""
def descr_situation_etud(context, etudid, ne=""):
"""chaine decrivant la situation actuelle de l'etudiant"""
cnx = context.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute(
"select I.formsemestre_id, I.etat from notes_formsemestre_inscription I, notes_formsemestre S where etudid=%(etudid)s and S.formsemestre_id = I.formsemestre_id and date_debut < now() and date_fin > now() order by S.date_debut desc;",
{"etudid": etudid},
)
r = cursor.dictfetchone()
if not r:
situation = "non inscrit"
else:
sem = sco_formsemestre.get_formsemestre(context, r["formsemestre_id"])
if r["etat"] == "I":
situation = "inscrit%s en %s" % (ne, sem["titremois"])
# Cherche la date d'inscription dans scolar_events:
events = scolars.scolar_events_list(
cnx,
args={
"etudid": etudid,
"formsemestre_id": sem["formsemestre_id"],
"event_type": "INSCRIPTION",
},
)
if not events:
log(
"*** situation inconsistante pour %s (inscrit mais pas d'event)"
% etudid
)
date_ins = "???" # ???
else:
date_ins = events[0]["event_date"]
situation += " le " + str(date_ins)
else:
situation = "démission de %s" % sem["titremois"]
# Cherche la date de demission dans scolar_events:
events = scolars.scolar_events_list(
cnx,
args={
"etudid": etudid,
"formsemestre_id": sem["formsemestre_id"],
"event_type": "DEMISSION",
},
)
if not events:
log(
"*** situation inconsistante pour %s (demission mais pas d'event)"
% etudid
)
date_dem = "???" # ???
else:
date_dem = events[0]["event_date"]
situation += " le " + str(date_dem)
return situation

File diff suppressed because it is too large Load Diff

View File

@ -130,8 +130,6 @@ from app.scodoc import notes_table as notes_table
from app.scodoc.notes_table import NOTES_CACHE_INST, CacheNotesTable from app.scodoc.notes_table import NOTES_CACHE_INST, CacheNotesTable
import app.scodoc.VERSION as VERSION import app.scodoc.VERSION as VERSION
context = ScoDoc7Context(globals())
def sco_publish(route, function, permission): def sco_publish(route, function, permission):
"""Declare a route for a python function, """Declare a route for a python function,
@ -687,7 +685,8 @@ def _do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=
cnx, args={"ue_id": ue_id} cnx, args={"ue_id": ue_id}
) )
if validations and not delete_validations and not force: if validations and not delete_validations and not force:
return context.confirmDialog( return scu.confirm_dialog(
context,
"<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>" "<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>"
% (len(validations), ue["acronyme"], ue["titre"]), % (len(validations), ue["acronyme"], ue["titre"]),
dest_url="", dest_url="",
@ -920,7 +919,8 @@ def do_module_delete(context, oid, REQUEST):
# S'il y a des moduleimpls, on ne peut pas detruire le module ! # S'il y a des moduleimpls, on ne peut pas detruire le module !
mods = sco_moduleimpl.do_moduleimpl_list(context, module_id=oid) mods = sco_moduleimpl.do_moduleimpl_list(context, module_id=oid)
if mods: if mods:
err_page = context.confirmDialog( err_page = scu.confirm_dialog(
context,
message="""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>""", message="""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>""",
helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""", helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""",
dest_url="ue_list", dest_url="ue_list",
@ -2037,7 +2037,8 @@ def formsemestre_desinscription(
car il n'a pas d'autre étudiant inscrit. car il n'a pas d'autre étudiant inscrit.
</p> </p>
""" """
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la demande de desinscription ?</h2>""" + msg_ext, """<h2>Confirmer la demande de desinscription ?</h2>""" + msg_ext,
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,
@ -2759,7 +2760,8 @@ def formsemestre_bulletins_mailetuds(
raise AccessDenied("vous n'avez pas le droit d'envoyer les bulletins") raise AccessDenied("vous n'avez pas le droit d'envoyer les bulletins")
# Confirmation dialog # Confirmation dialog
if not dialog_confirmed: if not dialog_confirmed:
return context.confirmDialog( return scu.confirm_dialog(
context,
"<h2>Envoyer les %d bulletins par e-mail aux étudiants ?" % len(etudids), "<h2>Envoyer les %d bulletins par e-mail aux étudiants ?" % len(etudids),
dest_url="", dest_url="",
REQUEST=REQUEST, REQUEST=REQUEST,
@ -2988,7 +2990,8 @@ def formsemestre_validation_etud(
): ):
"Enregistre choix jury pour un étudiant" "Enregistre choix jury pour un étudiant"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3023,7 +3026,8 @@ def formsemestre_validation_etud_manu(
): ):
"Enregistre choix jury pour un étudiant" "Enregistre choix jury pour un étudiant"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3052,7 +3056,8 @@ def formsemestre_validate_previous_ue(
): ):
"Form. saisie UE validée hors ScoDoc " "Form. saisie UE validée hors ScoDoc "
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3078,7 +3083,8 @@ def formsemestre_ext_edit_ue_validations(
): ):
"Form. edition UE semestre extérieur" "Form. edition UE semestre extérieur"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3102,7 +3108,8 @@ sco_publish(
def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST=None): def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST=None):
"""Suppress a validation (ue_id, etudid) and redirect to formsemestre""" """Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3119,7 +3126,8 @@ def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST
def formsemestre_validation_auto(context, formsemestre_id, REQUEST): def formsemestre_validation_auto(context, formsemestre_id, REQUEST):
"Formulaire saisie automatisee des decisions d'un semestre" "Formulaire saisie automatisee des decisions d'un semestre"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3137,7 +3145,8 @@ def formsemestre_validation_auto(context, formsemestre_id, REQUEST):
def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST): def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
"Formulaire saisie automatisee des decisions d'un semestre" "Formulaire saisie automatisee des decisions d'un semestre"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3155,7 +3164,8 @@ def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
def formsemestre_fix_validation_ues(context, formsemestre_id, REQUEST=None): def formsemestre_fix_validation_ues(context, formsemestre_id, REQUEST=None):
"Verif/reparation codes UE" "Verif/reparation codes UE"
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3175,7 +3185,8 @@ def formsemestre_validation_suppress_etud(
): ):
"""Suppression des decisions de jury pour un etudiant.""" """Suppression des decisions de jury pour un etudiant."""
if not context._can_validate_sem(REQUEST, formsemestre_id): if not context._can_validate_sem(REQUEST, formsemestre_id):
return context.confirmDialog( return scu.confirm_dialog(
context,
message="<p>Opération non autorisée pour %s</h2>" message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER, % REQUEST.AUTHENTICATED_USER,
dest_url=context.ScoURL(), dest_url=context.ScoURL(),
@ -3194,7 +3205,8 @@ def formsemestre_validation_suppress_etud(
) )
else: else:
existing = "" existing = ""
return context.confirmDialog( return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression des décisions du semestre %s (%s - %s) pour %s ?</h2>%s """<h2>Confirmer la suppression des décisions du semestre %s (%s - %s) pour %s ?</h2>%s
<p>Cette opération est irréversible. <p>Cette opération est irréversible.
</p> </p>

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@ alembic==1.5.5
attrdict==2.0.1 attrdict==2.0.1
Babel==2.9.0 Babel==2.9.0
blinker==1.4 blinker==1.4
certifi==2021.5.30
chardet==4.0.0
click==7.1.2 click==7.1.2
dnspython==1.16.0 dnspython==1.16.0
dominate==2.6.0 dominate==2.6.0
@ -33,10 +35,12 @@ python-dotenv==0.15.0
python-editor==1.0.4 python-editor==1.0.4
pytz==2021.1 pytz==2021.1
reportlab==3.5.59 reportlab==3.5.59
requests==2.25.1
six==1.15.0 six==1.15.0
SQLAlchemy==1.3.23 SQLAlchemy==1.3.23
stripogram==1.5 stripogram==1.5
typing==3.7.4.3 typing==3.7.4.3
urllib3==1.26.5
visitor==0.1.3 visitor==0.1.3
Werkzeug==1.0.1 Werkzeug==1.0.1
WTForms==2.3.3 WTForms==2.3.3

View File

@ -57,7 +57,7 @@ class ScoDocManager:
def get_dept_ids(self): def get_dept_ids(self):
"get (unsorted) dept ids" "get (unsorted) dept ids"
return [d.dept_id for d in descr_list] return self.dept_descriptions.keys()
def get_db_uri(self): def get_db_uri(self):
""" """