From 14d329fb0f5afebc9ae1f28e1b12d52a03a92fd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 19 Jun 2021 23:21:37 +0200 Subject: [PATCH] Massive refactoring/reorganization of imports. Work in progress. --- .pylintrc | 4 + README.md | 12 - app/decorators.py | 21 +- app/scodoc/ImportScolars.py | 54 +- app/scodoc/TrivialFormulator.py | 2 +- app/scodoc/ZAbsences.py | 1981 ----------- app/scodoc/ZScoDoc.py | 25 +- app/scodoc/ZScoUsers.py | 28 +- app/scodoc/__init__.py | 1 - app/scodoc/debug.py | 44 +- app/scodoc/dutrules.py | 2 +- app/scodoc/gen_tables.py | 12 +- app/scodoc/html_sco_header.py | 19 +- app/scodoc/html_sidebar.py | 15 +- app/scodoc/htmlutils.py | 4 +- app/scodoc/mails.py | 4 +- app/scodoc/notes_cache.py | 4 +- app/scodoc/notes_log.py | 12 - app/scodoc/notes_table.py | 62 +- app/scodoc/notesdb.py | 6 +- app/scodoc/pe_avislatex.py | 20 +- app/scodoc/pe_jurype.py | 26 +- app/scodoc/pe_semestretag.py | 12 +- app/scodoc/pe_settag.py | 8 +- app/scodoc/pe_tagtable.py | 4 +- app/scodoc/pe_tools.py | 6 +- app/scodoc/pe_view.py | 24 +- app/scodoc/sco_abs.py | 460 ++- app/scodoc/sco_abs_notification.py | 27 +- app/scodoc/sco_abs_views.py | 302 +- app/scodoc/sco_apogee_compare.py | 15 +- app/scodoc/sco_apogee_csv.py | 47 +- app/scodoc/sco_archives.py | 34 +- app/scodoc/sco_archives_etud.py | 28 +- app/scodoc/sco_bulletins.py | 87 +- app/scodoc/sco_bulletins_example.py | 12 +- app/scodoc/sco_bulletins_generator.py | 40 +- app/scodoc/sco_bulletins_json.py | 28 +- app/scodoc/sco_bulletins_legacy.py | 23 +- app/scodoc/sco_bulletins_pdf.py | 26 +- app/scodoc/sco_bulletins_standard.py | 34 +- app/scodoc/sco_bulletins_ucac.py | 17 +- app/scodoc/sco_bulletins_xml.py | 32 +- app/scodoc/sco_cache.py | 3 +- app/scodoc/sco_compute_moy.py | 36 +- app/scodoc/sco_config.py | 2 +- app/scodoc/sco_config_load.py | 4 +- app/scodoc/sco_core.py | 41 +- app/scodoc/sco_cost_formation.py | 17 +- app/scodoc/sco_debouche.py | 39 +- app/scodoc/sco_dept.py | 21 +- app/scodoc/sco_dump_db.py | 21 +- app/scodoc/sco_edit_formation.py | 29 +- app/scodoc/sco_edit_matiere.py | 43 +- app/scodoc/sco_edit_module.py | 47 +- app/scodoc/sco_edit_ue.py | 55 +- app/scodoc/sco_edt_cal.py | 19 +- app/scodoc/sco_entreprises.py | 4 +- app/scodoc/sco_etape_apogee.py | 51 +- app/scodoc/sco_etape_apogee_view.py | 43 +- app/scodoc/sco_etape_bilan.py | 8 +- app/scodoc/{scolars.py => sco_etud.py} | 110 +- app/scodoc/sco_evaluations.py | 107 +- app/scodoc/sco_excel.py | 15 +- app/scodoc/sco_export_results.py | 38 +- app/scodoc/sco_find_etud.py | 32 +- app/scodoc/sco_formations.py | 26 +- app/scodoc/sco_formsemestre.py | 20 +- app/scodoc/sco_formsemestre_custommenu.py | 16 +- app/scodoc/sco_formsemestre_edit.py | 66 +- app/scodoc/sco_formsemestre_exterieurs.py | 44 +- app/scodoc/sco_formsemestre_inscriptions.py | 61 +- app/scodoc/sco_formsemestre_status.py | 85 +- app/scodoc/sco_formsemestre_validation.py | 78 +- app/scodoc/sco_formulas.py | 6 +- app/scodoc/sco_groups.py | 49 +- app/scodoc/sco_groups_edit.py | 14 +- app/scodoc/sco_groups_view.py | 41 +- app/scodoc/sco_import_users.py | 16 +- app/scodoc/sco_inscr_passage.py | 42 +- app/scodoc/sco_liste_notes.py | 65 +- app/scodoc/sco_lycee.py | 20 +- app/scodoc/sco_modalites.py | 8 +- app/scodoc/sco_moduleimpl.py | 51 +- app/scodoc/sco_moduleimpl_inscriptions.py | 49 +- app/scodoc/sco_moduleimpl_status.py | 84 +- app/scodoc/sco_news.py | 28 +- app/scodoc/sco_page_etud.py | 65 +- app/scodoc/sco_parcours_dut.py | 60 +- app/scodoc/sco_pdf.py | 15 +- app/scodoc/sco_permissions.py | 64 - app/scodoc/sco_permissions_check.py | 139 + app/scodoc/sco_photos.py | 27 +- app/scodoc/sco_placement.py | 45 +- app/scodoc/sco_portal_apogee.py | 25 +- app/scodoc/sco_poursuite_dut.py | 20 +- app/scodoc/sco_preferences.py | 3419 ++++++++++--------- app/scodoc/sco_prepajury.py | 28 +- app/scodoc/sco_pvjury.py | 63 +- app/scodoc/sco_pvpdf.py | 29 +- app/scodoc/sco_recapcomplet.py | 44 +- app/scodoc/sco_report.py | 48 +- app/scodoc/sco_roles_default.py | 4 +- app/scodoc/sco_saisie_notes.py | 141 +- app/scodoc/sco_semset.py | 25 +- app/scodoc/sco_synchro_etuds.py | 70 +- app/scodoc/sco_tag_module.py | 23 +- app/scodoc/sco_trombino.py | 46 +- app/scodoc/sco_trombino_tours.py | 37 +- app/scodoc/sco_ue_external.py | 52 +- app/scodoc/sco_undo_notes.py | 28 +- app/scodoc/sco_up_to_date.py | 5 +- app/scodoc/sco_users.py | 9 +- app/scodoc/sco_utils.py | 79 +- app/scodoc/sco_zope.py | 67 - app/scodoc/scolog.py | 20 +- app/views/__init__.py | 1 - app/views/absences.py | 781 +---- app/views/entreprises.py | 12 +- app/views/notes.py | 167 +- app/views/scolar.py | 189 +- scodoc_manager.py | 32 + 122 files changed, 4670 insertions(+), 6487 deletions(-) delete mode 100644 app/scodoc/ZAbsences.py rename app/scodoc/{scolars.py => sco_etud.py} (94%) create mode 100644 app/scodoc/sco_permissions_check.py delete mode 100644 app/scodoc/sco_zope.py diff --git a/.pylintrc b/.pylintrc index 1bda9b54..a69fc72b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,2 +1,6 @@ +[[MESSAGES CONTROL] +# pylint and black disagree... +disable=bad-continuation + [TYPECHECK] ignored-classes=Permission diff --git a/README.md b/README.md index 48336d5d..cd6a0134 100644 --- a/README.md +++ b/README.md @@ -56,15 +56,3 @@ Installer le bon vieux `pyExcelerator` dans l'environnement: ### Méthodes qui ne devraient plus être publiées: - - - - - - - - - - - - diff --git a/app/decorators.py b/app/decorators.py index 3d90aa21..5199f85e 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -48,14 +48,17 @@ class ZRequest(object): self.REQUEST_METHOD = request.method.encode("utf-8") self.AUTHENTICATED_USER = current_user if request.method == "POST": - self.form = request.form + self.form = request.form # xxx encode en utf-8 ! + # Encode en utf-8 pour ScoDoc8 #sco8 + self.form = {k: v.encode("utf-8") for (k, v) in request.form.items()} if request.files: # Add files in form: must copy to get a mutable version # request.form is a werkzeug.datastructures.ImmutableMultiDict - self.form = self.form.copy() + # self.form = self.form.copy() self.form.update(request.files) elif request.method == "GET": - self.form = request.args + # Encode en utf-8 pour ScoDoc8 #sco8 + self.form = {k: v.encode("utf-8") for (k, v) in request.args.items()} self.RESPONSE = ZResponse() def __str__(self): @@ -158,7 +161,11 @@ def scodoc7func(context): elif arg_name == "context": pos_arg_values.append(context) else: - pos_arg_values.append(req_args[arg_name]) + # XXX Convert to regular string for ScoDoc8/Python 2 + if type(req_args[arg_name]) == types.UnicodeType: + pos_arg_values.append(req_args[arg_name].encode("utf-8")) + else: + pos_arg_values.append(req_args[arg_name]) current_app.logger.info("pos_arg_values=%s" % pos_arg_values) # Add keyword arguments if nb_default_args: @@ -167,7 +174,11 @@ def scodoc7func(context): kwargs[arg_name] = REQUEST elif arg_name in req_args: # set argument kw optionnel - kwargs[arg_name] = req_args[arg_name] + # XXX Convert to regular string for ScoDoc8/Python 2 + if type(req_args[arg_name]) == types.UnicodeType: + kwargs[arg_name] = req_args[arg_name].encode("utf-8") + else: + kwargs[arg_name] = req_args[arg_name] current_app.logger.info( "scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s" % (top_level, pos_arg_values, kwargs) diff --git a/app/scodoc/ImportScolars.py b/app/scodoc/ImportScolars.py index 20e0180d..8e4f1d57 100644 --- a/app/scodoc/ImportScolars.py +++ b/app/scodoc/ImportScolars.py @@ -36,12 +36,14 @@ import collections import types import re -import sco_utils as scu -import notesdb as ndb -from notes_log import log -from sco_formsemestre_inscriptions import do_formsemestre_inscription_with_modules -from gen_tables import GenTable -from sco_exceptions import ( +import app.scodoc.sco_utils as scu +import app.scodoc.notesdb as ndb +from app.scodoc.notes_log import log +from app.scodoc.sco_formsemestre_inscriptions import ( + do_formsemestre_inscription_with_modules, +) +from app.scodoc.gen_tables import GenTable +from app.scodoc.sco_exceptions import ( AccessDenied, FormatError, ScoException, @@ -50,14 +52,14 @@ from sco_exceptions import ( ScoLockedFormError, ScoGenError, ) -import html_sco_header -import scolars -import sco_formsemestre -import sco_groups -import sco_excel -import sco_groups_view -import sco_news -import sco_preferences +from app.scodoc import html_sco_header +from app.scodoc import sco_etud +from app.scodoc import sco_formsemestre +from app.scodoc import sco_groups +from app.scodoc import sco_excel +from app.scodoc import sco_groups_view +from app.scodoc import sco_news +from app.scodoc import sco_preferences # format description (relative to Product directory)) FORMAT_FILE = "misc/format_import_etudiants.txt" @@ -199,7 +201,7 @@ def sco_import_generate_excel_sample( # rempli table avec données actuelles lines = [] for i in members: - etud = scolars.get_etud_info(etudid=i["etudid"], filled=True)[0] + etud = sco_etud.get_etud_info(etudid=i["etudid"], filled=True)[0] l = [] for field in titles: if field == "groupes": @@ -371,7 +373,7 @@ def scolars_import_excel_file( # xxx Ad-hoc checks (should be in format description) if scu.strlower(titleslist[i]) == "sexe": try: - val = scolars.input_civilite(val) + val = sco_etud.input_civilite(val) except: raise ScoValueError( "valeur invalide pour 'SEXE' (doit etre 'M', 'F', ou 'MME', 'H', 'X' ou vide, mais pas '%s') ligne %d, colonne %s" @@ -405,7 +407,7 @@ def scolars_import_excel_file( if values["code_ine"] and not is_new_ine: raise ScoValueError("Code INE dupliqué (%s)" % values["code_ine"]) # Check nom/prenom - ok, NbHomonyms = scolars.check_nom_prenom( + ok, NbHomonyms = sco_etud.check_nom_prenom( cnx, nom=values["nom"], prenom=values["prenom"] ) if not ok: @@ -513,16 +515,16 @@ def _import_one_student( ) # Identite args = values.copy() - etudid = scolars.identite_create(cnx, args, context=context, REQUEST=REQUEST) + etudid = sco_etud.identite_create(cnx, args, context=context, REQUEST=REQUEST) created_etudids.append(etudid) # Admissions args["etudid"] = etudid args["annee"] = annee_courante - _ = scolars.admission_create(cnx, args) + _ = sco_etud.admission_create(cnx, args) # Adresse args["typeadresse"] = "domicile" args["description"] = "(infos admission)" - _ = scolars.adresse_create(cnx, args) + _ = sco_etud.adresse_create(cnx, args) # Inscription au semestre args["etat"] = "I" # etat insc. semestre if formsemestre_id: @@ -561,7 +563,7 @@ def _import_one_student( def _is_new_ine(cnx, code_ine): "True if this code is not in DB" - etuds = scolars.identite_list(cnx, {"code_ine": code_ine}) + etuds = sco_etud.identite_list(cnx, {"code_ine": code_ine}) return not etuds @@ -642,7 +644,7 @@ def scolars_import_admission( ) else: etud = etuds_by_nomprenom[(nom, prenom)] - cur_adm = scolars.admission_list(cnx, args={"etudid": etud["etudid"]})[0] + cur_adm = sco_etud.admission_list(cnx, args={"etudid": etud["etudid"]})[0] # peuple les champs presents dans le tableau args = {} for idx in fields: @@ -665,17 +667,17 @@ def scolars_import_admission( # Type admission: traitement particulier if not cur_adm["type_admission"] and not args.get("type_admission"): args["type_admission"] = type_admission - scolars.etudident_edit(cnx, args) - adr = scolars.adresse_list(cnx, args={"etudid": etud["etudid"]}) + sco_etud.etudident_edit(cnx, args) + adr = sco_etud.adresse_list(cnx, args={"etudid": etud["etudid"]}) if adr: args["adresse_id"] = adr[0]["adresse_id"] - scolars.adresse_edit( + sco_etud.adresse_edit( cnx, args ) # ne passe pas le contexte: pas de notification ici else: args["typeadresse"] = "domicile" args["description"] = "(infos admission)" - adresse_id = scolars.adresse_create(cnx, args) + adresse_id = sco_etud.adresse_create(cnx, args) # log('import_adm: %s' % args ) # Change les groupes si nécessaire: if args["groupes"]: diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py index 5cadf929..dd279afa 100644 --- a/app/scodoc/TrivialFormulator.py +++ b/app/scodoc/TrivialFormulator.py @@ -485,7 +485,7 @@ class TF: lem.append("") for i in range(len(labels)): if input_type == "checkbox": - # from notes_log import log # debug only + # from app.scodoc.notes_log import log # debug only # log('checkbox: values[%s] = "%s"' % (field,repr(values[field]) )) # log("descr['allowed_values'][%s] = '%s'" % (i, repr(descr['allowed_values'][i]))) if descr["allowed_values"][i] in values[field]: diff --git a/app/scodoc/ZAbsences.py b/app/scodoc/ZAbsences.py deleted file mode 100644 index 58d8046c..00000000 --- a/app/scodoc/ZAbsences.py +++ /dev/null @@ -1,1981 +0,0 @@ -# -*- mode: python -*- -# -*- coding: utf-8 -*- - -############################################################################## -# -# Gestion scolarite IUT -# -# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Emmanuel Viennet emmanuel.viennet@viennet.net -# -############################################################################## - -""" Gestion des absences (v4) - -C'est la partie la plus ancienne de ScoDoc, et elle est à revoir. - -L'API de plus bas niveau est en gros: - - AnnuleAbsencesDatesNoJust(etudid, dates) - CountAbs(etudid, debut, fin, matin=None, moduleimpl_id=None) - CountAbsJust(etudid, debut, fin, matin=None, moduleimpl_id=None) - ListeAbsJust(etudid, datedebut) [pas de fin ?] - ListeAbsNonJust(etudid, datedebut) [pas de fin ?] - ListeJustifs(etudid, datedebut, datefin=None, only_no_abs=True) - - ListeAbsJour(date, am=True, pm=True, is_abs=None, is_just=None) - ListeAbsNonJustJour(date, am=True, pm=True) - - -""" - -import string -import re -import time -import datetime -import dateutil -import dateutil.parser -import calendar -import urllib -import cgi -import jaxml - -# --------------- -from sco_zope import * # pylint: disable=unused-wildcard-import - -# --------------- -import sco_utils as scu -import notesdb -from notes_log import log -from scolog import logdb -from sco_permissions import ScoAbsAddBillet, ScoAbsChange, ScoView -from sco_exceptions import ScoValueError, ScoInvalidDateError -from TrivialFormulator import TrivialFormulator, TF -from gen_tables import GenTable -import html_sco_header -import scolars -import sco_formsemestre -import sco_moduleimpl -import sco_groups -import sco_groups_view -import sco_excel -import sco_abs_notification, sco_abs_views -import sco_compute_moy -import sco_abs -from sco_abs import ddmmyyyy - -CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS - - -def _toboolean(x): - "convert a value to boolean (ensure backward compat with OLD intranet code)" - if type(x) == type(""): - x = x.lower() - if x and x != "false": # backward compat... - return True - else: - return False - - -class ZAbsences( - ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit -): - - "ZAbsences object" - - meta_type = "ZAbsences" - security = ClassSecurityInfo() - - # This is the list of the methods associated to 'tabs' in the ZMI - # Be aware that The first in the list is the one shown by default, so if - # the 'View' tab is the first, you will never see your tabs by cliquing - # on the object. - manage_options = ( - ({"label": "Contents", "action": "manage_main"},) - + PropertyManager.manage_options # add the 'Properties' tab - + ({"label": "View", "action": "index_html"},) - + Item.manage_options # add the 'Undo' & 'Owner' tab - + RoleManager.manage_options # add the 'Security' tab - ) - - # no permissions, only called from python - def __init__(self, id, title): - "initialise a new instance" - self.id = id - self.title = title - - # The form used to edit this object - # def manage_editZAbsences(self, title, RESPONSE=None): - # "Changes the instance values" - # self.title = title - # self._p_changed = 1 - # RESPONSE.redirect("manage_editForm") - - # -------------------------------------------------------------------- - # - # ABSENCES (top level) - # - # -------------------------------------------------------------------- - # used to view content of the object - security.declareProtected(ScoView, "index_html") - index_html = sco_abs_views.index_html - - security.declareProtected(ScoView, "EtatAbsences") - EtatAbsences = sco_abs_views.EtatAbsences - - security.declareProtected(ScoView, "CalAbs") - CalAbs = sco_abs_views.CalAbs - - security.declareProtected(ScoAbsChange, "SignaleAbsenceEtud") - SignaleAbsenceEtud = sco_abs_views.SignaleAbsenceEtud - security.declareProtected(ScoAbsChange, "doSignaleAbsence") - doSignaleAbsence = sco_abs_views.doSignaleAbsence - - security.declareProtected(ScoAbsChange, "JustifAbsenceEtud") - JustifAbsenceEtud = sco_abs_views.JustifAbsenceEtud - security.declareProtected(ScoAbsChange, "doJustifAbsence") - doJustifAbsence = sco_abs_views.doJustifAbsence - - security.declareProtected(ScoAbsChange, "AnnuleAbsenceEtud") - AnnuleAbsenceEtud = sco_abs_views.AnnuleAbsenceEtud - security.declareProtected(ScoAbsChange, "doAnnuleAbsence") - doAnnuleAbsence = sco_abs_views.doAnnuleAbsence - security.declareProtected(ScoAbsChange, "doAnnuleJustif") - doAnnuleJustif = sco_abs_views.doAnnuleJustif - - security.declareProtected(ScoView, "ListeAbsEtud") - ListeAbsEtud = sco_abs_views.ListeAbsEtud - - # -------------------------------------------------------------------- - # - # SQL METHODS - # - # -------------------------------------------------------------------- - - def _AddAbsence( - self, - etudid, - jour, - matin, - estjust, - REQUEST, - description=None, - moduleimpl_id=None, - ): - "Ajoute une absence dans la bd" - # unpublished - if self._isFarFutur(jour): - raise ScoValueError("date absence trop loin dans le futur !") - estjust = _toboolean(estjust) - matin = _toboolean(matin) - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - "insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )", - vars(), - ) - logdb( - REQUEST, - cnx, - "AddAbsence", - etudid=etudid, - msg="JOUR=%(jour)s,MATIN=%(matin)s,ESTJUST=%(estjust)s,description=%(description)s,moduleimpl_id=%(moduleimpl_id)s" - % vars(), - ) - cnx.commit() - sco_abs.invalidateAbsEtudDate(self, etudid, jour) - sco_abs_notification.abs_notify(self, etudid, jour) - - def _AddJustif(self, etudid, jour, matin, REQUEST, description=None): - "Ajoute un justificatif dans la base" - # unpublished - if self._isFarFutur(jour): - raise ScoValueError("date justificatif trop loin dans le futur !") - matin = _toboolean(matin) - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - "insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )", - vars(), - ) - logdb( - REQUEST, - cnx, - "AddJustif", - etudid=etudid, - msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), - ) - cnx.commit() - sco_abs.invalidateAbsEtudDate(self, etudid, jour) - - def _AnnuleAbsence(self, etudid, jour, matin, moduleimpl_id=None, REQUEST=None): - """Annule une absence ds base - Si moduleimpl_id, n'annule que pour ce module - """ - # unpublished - matin = _toboolean(matin) - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - req = "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and estabs" - if moduleimpl_id: - req += " and moduleimpl_id=%(moduleimpl_id)s" - cursor.execute(req, vars()) - logdb( - REQUEST, - cnx, - "AnnuleAbsence", - etudid=etudid, - msg="JOUR=%(jour)s,MATIN=%(matin)s,moduleimpl_id=%(moduleimpl_id)s" - % vars(), - ) - cnx.commit() - sco_abs.invalidateAbsEtudDate(self, etudid, jour) - - def _AnnuleJustif(self, etudid, jour, matin, REQUEST=None): - "Annule un justificatif" - # unpublished - matin = _toboolean(matin) - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and ESTJUST AND NOT ESTABS", - vars(), - ) - cursor.execute( - "update absences set estjust=false where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s", - vars(), - ) - logdb( - REQUEST, - cnx, - "AnnuleJustif", - etudid=etudid, - msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), - ) - cnx.commit() - sco_abs.invalidateAbsEtudDate(self, etudid, jour) - - # Fonction inutile à supprimer (gestion moduleimpl_id incorrecte): - # def _AnnuleAbsencesPeriodNoJust(self, etudid, datedebut, datefin, - # moduleimpl_id=None, REQUEST=None): - # """Supprime les absences entre ces dates (incluses). - # mais ne supprime pas les justificatifs. - # """ - # # unpublished - # cnx = self.GetDBConnexion() - # cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - # # supr les absences non justifiees - # cursor.execute("delete from absences where etudid=%(etudid)s and (not estjust) and moduleimpl_id=(moduleimpl_id)s and jour BETWEEN %(datedebut)s AND %(datefin)s", - # vars() ) - # # s'assure que les justificatifs ne sont pas "absents" - # cursor.execute("update absences set estabs=FALSE where etudid=%(etudid)s and jour and moduleimpl_id=(moduleimpl_id)s BETWEEN %(datedebut)s AND %(datefin)s", vars()) - # logdb(REQUEST, cnx, 'AnnuleAbsencesPeriodNoJust', etudid=etudid, - # msg='%(datedebut)s - %(datefin)s - (moduleimpl_id)s'%vars()) - # cnx.commit() - # sco_abs.invalidateAbsEtudDate(self, etudid, datedebut) - # sco_abs.invalidateAbsEtudDate(self, etudid, datefin) # si un semestre commence apres datedebut et termine avant datefin, il ne sera pas invalide. Tant pis ;-) - - security.declareProtected(ScoAbsChange, "AnnuleAbsencesDatesNoJust") - - def AnnuleAbsencesDatesNoJust( - self, etudid, dates, moduleimpl_id=None, REQUEST=None - ): - """Supprime les absences aux dates indiquées - mais ne supprime pas les justificatifs. - """ - # log('AnnuleAbsencesDatesNoJust: moduleimpl_id=%s' % moduleimpl_id) - if not dates: - return - date0 = dates[0] - if len(date0.split(":")) == 2: - # am/pm is present - for date in dates: - jour, ampm = date.split(":") - if ampm == "am": - matin = 1 - elif ampm == "pm": - matin = 0 - else: - raise ValueError("invalid ampm !") - self._AnnuleAbsence(etudid, jour, matin, moduleimpl_id, REQUEST) - return - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - # 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", - vars(), - ) - sco_abs.invalidateAbsEtudDate(self, etudid, date) - # 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", - vars(), - ) - if dates: - date0 = dates[0] - else: - date0 = None - if len(dates) > 1: - date1 = dates[1] - else: - date1 = None - logdb( - REQUEST, - cnx, - "AnnuleAbsencesDatesNoJust", - etudid=etudid, - msg="%s - %s - %s" % (date0, date1, moduleimpl_id), - ) - cnx.commit() - - def ListAbsInRange( - self, etudid, debut, fin, matin=None, moduleimpl_id=None, cursor=None - ): - """Liste des absences entre deux dates. - - Args: - etudid - debut string iso date ("2020-03-12") - end string iso date ("2020-03-12") - matin None, True, False - moduleimpl_id - """ - if matin != None: - matin = _toboolean(matin) - ismatin = " AND A.MATIN = %(matin)s " - else: - ismatin = "" - if moduleimpl_id: - modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s " - else: - modul = "" - if not cursor: - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - """ - SELECT DISTINCT A.JOUR, A.MATIN - FROM ABSENCES A - WHERE A.ETUDID = %(etudid)s - AND A.ESTABS""" - + ismatin - + modul - + """ - AND A.JOUR BETWEEN %(debut)s AND %(fin)s - """, - vars(), - ) - res = cursor.dictfetchall() - return res - - security.declareProtected(ScoView, "CountAbs") - - def CountAbs(self, etudid, debut, fin, matin=None, moduleimpl_id=None): - """CountAbs - matin= 1 ou 0. - - Returns: - An integer. - """ - return len( - self.ListAbsInRange( - etudid, debut, fin, matin=matin, moduleimpl_id=moduleimpl_id - ) - ) - - security.declareProtected(ScoView, "CountAbsJust") - - def CountAbsJust(self, etudid, debut, fin, matin=None, moduleimpl_id=None): - "Count just. abs" - if matin != None: - matin = _toboolean(matin) - ismatin = " AND A.MATIN = %(matin)s " - else: - ismatin = "" - if moduleimpl_id: - modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s " - else: - modul = "" - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - """SELECT COUNT(*) AS NbAbsJust FROM ( - SELECT DISTINCT A.JOUR, A.MATIN - FROM ABSENCES A, ABSENCES B - WHERE A.ETUDID = %(etudid)s - AND A.ETUDID = B.ETUDID - AND A.JOUR = B.JOUR AND A.MATIN = B.MATIN - AND A.JOUR BETWEEN %(debut)s AND %(fin)s - AND A.ESTABS AND (A.ESTJUST OR B.ESTJUST)""" - + ismatin - + modul - + """ -) AS tmp - """, - vars(), - ) - res = cursor.fetchone()[0] - return res - - def _ListeAbsDate(self, etudid, beg_date, end_date): - # Liste des absences et justifs entre deux dates - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - """SELECT jour, matin, estabs, estjust, description FROM ABSENCES A - WHERE A.ETUDID = %(etudid)s - AND A.jour >= %(beg_date)s - AND A.jour <= %(end_date)s - """, - vars(), - ) - Abs = cursor.dictfetchall() - # remove duplicates - A = {} # { (jour, matin) : abs } - for a in Abs: - jour, matin = a["jour"], a["matin"] - if (jour, matin) in A: - # garde toujours la description - a["description"] = a["description"] or A[(jour, matin)]["description"] - # et la justif: - a["estjust"] = a["estjust"] or A[(jour, matin)]["estjust"] - a["estabs"] = a["estabs"] or A[(jour, matin)]["estabs"] - A[(jour, matin)] = a - else: - A[(jour, matin)] = a - if A[(jour, matin)]["description"] is None: - A[(jour, matin)]["description"] = "" - # add hours: matin = 8:00 - 12:00, apresmidi = 12:00 - 18:00 - dat = "%04d-%02d-%02d" % (a["jour"].year, a["jour"].month, a["jour"].day) - if a["matin"]: - A[(jour, matin)]["begin"] = dat + " 08:00:00" - A[(jour, matin)]["end"] = dat + " 11:59:59" - else: - A[(jour, matin)]["begin"] = dat + " 12:00:00" - A[(jour, matin)]["end"] = dat + " 17:59:59" - # sort - R = A.values() - R.sort(key=lambda x: (x["begin"])) - return R - - security.declareProtected(ScoView, "ListeAbsJust") - - def ListeAbsJust(self, etudid, datedebut): - "Liste des absences justifiees (par ordre chronologique)" - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - """SELECT DISTINCT A.ETUDID, A.JOUR, A.MATIN FROM ABSENCES A, ABSENCES B - WHERE A.ETUDID = %(etudid)s - AND A.ETUDID = B.ETUDID - AND A.JOUR = B.JOUR AND A.MATIN = B.MATIN AND A.JOUR >= %(datedebut)s - AND A.ESTABS AND (A.ESTJUST OR B.ESTJUST) - ORDER BY A.JOUR - """, - vars(), - ) - A = cursor.dictfetchall() - for a in A: - a["description"] = self._GetAbsDescription(a, cursor=cursor) - return A - - security.declareProtected(ScoView, "ListeAbsNonJust") - - def ListeAbsNonJust(self, etudid, datedebut): - "Liste des absences NON justifiees (par ordre chronologique)" - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - cursor.execute( - """SELECT ETUDID, JOUR, MATIN FROM ABSENCES A - WHERE A.ETUDID = %(etudid)s - AND A.estabs - AND A.jour >= %(datedebut)s - EXCEPT SELECT ETUDID, JOUR, MATIN FROM ABSENCES B - WHERE B.estjust - AND B.ETUDID = %(etudid)s - ORDER BY JOUR - """, - vars(), - ) - A = cursor.dictfetchall() - for a in A: - a["description"] = self._GetAbsDescription(a, cursor=cursor) - return A - - security.declareProtected(ScoView, "ListeJustifs") - - def ListeJustifs(self, etudid, datedebut, datefin=None, only_no_abs=False): - """Liste des justificatifs (sans absence relevée) à partir d'une date, - ou, si datefin spécifié, entre deux dates. - Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées. - """ - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - req = """SELECT DISTINCT ETUDID, JOUR, MATIN FROM ABSENCES A - WHERE A.ETUDID = %(etudid)s - AND A.ESTJUST - AND A.JOUR >= %(datedebut)s""" - if datefin: - req += """AND A.JOUR <= %(datefin)s""" - if only_no_abs: - req += """ - EXCEPT SELECT ETUDID, JOUR, MATIN FROM ABSENCES B - WHERE B.estabs - AND B.ETUDID = %(etudid)s - """ - cursor.execute(req, vars()) - A = cursor.dictfetchall() - for a in A: - a["description"] = self._GetAbsDescription(a, cursor=cursor) - - return A - - def _GetAbsDescription(self, a, cursor=None): - "Description associee a l'absence" - if not cursor: - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - a = a.copy() - # a['jour'] = a['jour'].date() - if a["matin"]: # devrait etre booleen... :-( - a["matin"] = True - 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""", - a, - ) - A = cursor.dictfetchall() - desc = None - module = "" - for a in A: - if a["description"]: - desc = a["description"] - if a["moduleimpl_id"] and a["moduleimpl_id"] != "NULL": - # Trouver le nom du module - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - self.Notes, moduleimpl_id=a["moduleimpl_id"] - ) - if Mlist: - M = Mlist[0] - module += "%s " % M["module"]["code"] - - if desc: - return "(%s) %s" % (desc, module) - if module: - return module - return "" - - security.declareProtected(ScoView, "ListeAbsJour") - - def ListeAbsJour(self, date, am=True, pm=True, is_abs=True, is_just=None): - """Liste des absences et/ou justificatifs ce jour. - is_abs: None (peu importe), True, False - is_just: idem - """ - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A - WHERE A.jour = %(date)s - """ - if is_abs != None: - req += " AND A.estabs = %(is_abs)s" - if is_just != None: - req += " AND A.estjust = %(is_just)s" - if not am: - req += " AND NOT matin " - if not pm: - req += " AND matin" - - cursor.execute(req, {"date": date, "is_just": is_just, "is_abs": is_abs}) - A = cursor.dictfetchall() - for a in A: - a["description"] = self._GetAbsDescription(a, cursor=cursor) - return A - - security.declareProtected(ScoView, "ListeAbsNonJustJour") - - def ListeAbsNonJustJour(self, date, am=True, pm=True): - "Liste des absences non justifiees ce jour" - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - reqa = "" - if not am: - reqa += " AND NOT matin " - if not pm: - reqa += " AND matin " - req = ( - """SELECT etudid, jour, matin FROM ABSENCES A - WHERE A.estabs - AND A.jour = %(date)s - """ - + reqa - + """EXCEPT SELECT etudid, jour, matin FROM ABSENCES B - WHERE B.estjust AND B.jour = %(date)s""" - + reqa - ) - - cursor.execute(req, {"date": date}) - A = cursor.dictfetchall() - for a in A: - a["description"] = self._GetAbsDescription(a, cursor=cursor) - return A - - security.declareProtected(ScoAbsChange, "doSignaleAbsenceGrSemestre") - - def doSignaleAbsenceGrSemestre( - self, - moduleimpl_id=None, - abslist=[], - dates="", - etudids="", - destination=None, - REQUEST=None, - ): - """Enregistre absences aux dates indiquees (abslist et dates). - dates est une liste de dates ISO (séparées par des ','). - Efface les absences aux dates indiquées par dates, - ou bien ajoute celles de abslist. - """ - if etudids: - etudids = etudids.split(",") - else: - etudids = [] - if dates: - dates = dates.split(",") - else: - dates = [] - - # 1- Efface les absences - if dates: - for etudid in etudids: - self.AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id, REQUEST) - return "Absences effacées" - - # 2- Ajoute les absences - if abslist: - self._add_abslist(abslist, REQUEST, moduleimpl_id) - return "Absences ajoutées" - - return "" - - def _add_abslist(self, abslist, REQUEST, moduleimpl_id=None): - for a in abslist: - etudid, jour, ampm = a.split(":") - if ampm == "am": - matin = 1 - elif ampm == "pm": - matin = 0 - else: - raise ValueError("invalid ampm !") - # ajoute abs si pas deja absent - if self.CountAbs(etudid, jour, jour, matin, moduleimpl_id) == 0: - self._AddAbsence(etudid, jour, matin, 0, REQUEST, "", moduleimpl_id) - - # --- Misc tools.... ------------------ - - def _isFarFutur(self, jour): - # check si jour est dans le futur "lointain" - # pour autoriser les saisies dans le futur mais pas a plus de 6 mois - y, m, d = [int(x) for x in jour.split("-")] - j = datetime.date(y, m, d) - # 6 mois ~ 182 jours: - return j - datetime.date.today() > datetime.timedelta(182) - - # ------------ HTML Interfaces - security.declareProtected(ScoAbsChange, "SignaleAbsenceGrHebdo") - - def SignaleAbsenceGrHebdo( - self, datelundi, group_ids=[], destination="", moduleimpl_id=None, REQUEST=None - ): - "Saisie hebdomadaire des absences" - if not moduleimpl_id: - moduleimpl_id = None - - groups_infos = sco_groups_view.DisplayedGroupsInfos( - self, group_ids, moduleimpl_id=moduleimpl_id, REQUEST=REQUEST - ) - if not groups_infos.members: - return ( - self.sco_header(page_title="Saisie des absences", REQUEST=REQUEST) - + "

Aucun étudiant !

" - + self.sco_footer(REQUEST) - ) - - base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % ( - datelundi, - groups_infos.groups_query_args, - urllib.quote(destination), - ) - - formsemestre_id = groups_infos.formsemestre_id - require_module = self.get_preference("abs_require_module", formsemestre_id) - etuds = [ - self.getEtudInfo(etudid=m["etudid"], filled=True)[0] - for m in groups_infos.members - ] - # Restreint aux inscrits au module sélectionné - if moduleimpl_id: - mod_inscrits = set( - [ - x["etudid"] - for x in sco_moduleimpl.do_moduleimpl_inscription_list( - self.Notes, moduleimpl_id=moduleimpl_id - ) - ] - ) - etuds_inscrits_module = [e for e in etuds if e["etudid"] in mod_inscrits] - if etuds_inscrits_module: - etuds = etuds_inscrits_module - else: - # Si aucun etudiant n'est inscrit au module choisi... - moduleimpl_id = None - nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id) - sem = sco_formsemestre.do_formsemestre_list( - self, {"formsemestre_id": formsemestre_id} - )[0] - - # calcule dates jours de cette semaine - # liste de dates iso "yyyy-mm-dd" - datessem = [notesdb.DateDMYtoISO(datelundi)] - for _ in sco_abs.day_names(self)[1:]: - datessem.append(sco_abs.next_iso_day(self, datessem[-1])) - # - if groups_infos.tous_les_etuds_du_sem: - gr_tit = "en" - else: - if len(groups_infos.group_ids) > 1: - p = "des groupes" - else: - p = "du groupe" - gr_tit = ( - p + ' ' + groups_infos.groups_titles + "" - ) - - H = [ - self.sco_header( - page_title="Saisie hebdomadaire des absences", - init_qtip=True, - javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS - + [ - "js/etud_info.js", - "js/abs_ajax.js", - "js/groups_view.js", - ], - cssstyles=CSSSTYLES, - no_side_bar=1, - REQUEST=REQUEST, - ), - """
-

Saisie des absences %s %s, - semaine du lundi %s

-
-
- - - - - Groupes: %s -
-
- """ - % ( - gr_tit, - sem["titre_num"], - datelundi, - groups_infos.formsemestre_id, - datelundi, - destination, - moduleimpl_id or "", - sco_groups_view.menu_groups_choice( - self, groups_infos, submit_on_change=True - ), - ), - ] - # - modimpls_list = [] - # Initialize with first student - ues = nt.get_ues(etudid=etuds[0]["etudid"]) - for ue in ues: - modimpls_list += nt.get_modimpls(ue_id=ue["ue_id"]) - - # Add modules other students are subscribed to - for etud in etuds[1:]: - modimpls_etud = [] - ues = nt.get_ues(etudid=etud["etudid"]) - for ue in ues: - modimpls_etud += nt.get_modimpls(ue_id=ue["ue_id"]) - modimpls_list += [m for m in modimpls_etud if m not in modimpls_list] - - menu_module = "" - for modimpl in modimpls_list: - if modimpl["moduleimpl_id"] == moduleimpl_id: - sel = "selected" - else: - sel = "" - menu_module += ( - """\n""" - % { - "modimpl_id": modimpl["moduleimpl_id"], - "modname": modimpl["module"]["code"] - + " " - + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]), - "sel": sel, - } - ) - if moduleimpl_id: - sel = "" - else: - sel = "selected" # aucun module specifie - - H.append( - """Module concerné: - -
""" - % {"menu_module": menu_module, "url": base_url, "sel": sel} - ) - - H += self._gen_form_saisie_groupe( - etuds, datessem, destination, moduleimpl_id, require_module - ) - - H.append(self.sco_footer(REQUEST)) - return "\n".join(H) - - security.declareProtected(ScoAbsChange, "SignaleAbsenceGrSemestre") - - def SignaleAbsenceGrSemestre( - self, - datedebut, - datefin, - destination="", - group_ids=[], # list of groups to display - nbweeks=4, # ne montre que les nbweeks dernieres semaines - moduleimpl_id=None, - REQUEST=None, - ): - """Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier""" - groups_infos = sco_groups_view.DisplayedGroupsInfos( - self, group_ids, REQUEST=REQUEST - ) - if not groups_infos.members: - return ( - self.sco_header(page_title="Saisie des absences", REQUEST=REQUEST) - + "

Aucun étudiant !

" - + self.sco_footer(REQUEST) - ) - formsemestre_id = groups_infos.formsemestre_id - require_module = self.get_preference("abs_require_module", formsemestre_id) - etuds = [ - self.getEtudInfo(etudid=m["etudid"], filled=True)[0] - for m in groups_infos.members - ] - # Restreint aux inscrits au module sélectionné - if moduleimpl_id: - mod_inscrits = set( - [ - x["etudid"] - for x in sco_moduleimpl.do_moduleimpl_inscription_list( - self.Notes, moduleimpl_id=moduleimpl_id - ) - ] - ) - etuds = [e for e in etuds if e["etudid"] in mod_inscrits] - if not moduleimpl_id: - moduleimpl_id = None - base_url_noweeks = ( - "SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s" - % ( - datedebut, - datefin, - groups_infos.groups_query_args, - urllib.quote(destination), - ) - ) - base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id - - if etuds: - nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id) - sem = sco_formsemestre.do_formsemestre_list( - self, {"formsemestre_id": formsemestre_id} - )[0] - work_saturday = sco_abs.is_work_saturday(self) - jourdebut = ddmmyyyy(datedebut, work_saturday=work_saturday) - jourfin = ddmmyyyy(datefin, work_saturday=work_saturday) - today = ddmmyyyy( - time.strftime("%d/%m/%Y", time.localtime()), - work_saturday=work_saturday, - ) - today.next() - if jourfin > today: # ne propose jamais les semaines dans le futur - jourfin = today - if jourdebut > today: - raise ScoValueError("date de début dans le futur (%s) !" % jourdebut) - # - if not jourdebut.iswork() or jourdebut > jourfin: - raise ValueError( - "date debut invalide (%s, ouvrable=%d)" - % (str(jourdebut), jourdebut.iswork()) - ) - # calcule dates - dates = [] # ddmmyyyy instances - d = ddmmyyyy(datedebut, work_saturday=work_saturday) - while d <= jourfin: - dates.append(d) - d = d.next(7) # avance d'une semaine - # - msg = "Montrer seulement les 4 dernières semaines" - nwl = 4 - if nbweeks: - nbweeks = int(nbweeks) - if nbweeks > 0: - dates = dates[-nbweeks:] - msg = "Montrer toutes les semaines" - nwl = 0 - url_link_semaines = base_url_noweeks + "&nbweeks=%s" % nwl - if moduleimpl_id: - url_link_semaines += "&moduleimpl_id=" + moduleimpl_id - # - dates = [x.ISO() for x in dates] - dayname = sco_abs.day_names(self)[jourdebut.weekday] - - if groups_infos.tous_les_etuds_du_sem: - gr_tit = "en" - else: - if len(groups_infos.group_ids) > 1: - p = "des groupes " - else: - p = "du groupe " - gr_tit = ( - p + '' + groups_infos.groups_titles + "" - ) - - H = [ - self.sco_header( - page_title="Saisie des absences", - init_qtip=True, - javascripts=["js/etud_info.js", "js/abs_ajax.js"], - no_side_bar=1, - REQUEST=REQUEST, - ), - """
-

Saisie des absences %s %s, - les %s

-

- %s - - """ - % (gr_tit, sem["titre_num"], dayname, url_link_semaines, msg), - ] - # - if etuds: - modimpls_list = [] - # Initialize with first student - ues = nt.get_ues(etudid=etuds[0]["etudid"]) - for ue in ues: - modimpls_list += nt.get_modimpls(ue_id=ue["ue_id"]) - - # Add modules other students are subscribed to - for etud in etuds[1:]: - modimpls_etud = [] - ues = nt.get_ues(etudid=etud["etudid"]) - for ue in ues: - modimpls_etud += nt.get_modimpls(ue_id=ue["ue_id"]) - modimpls_list += [m for m in modimpls_etud if m not in modimpls_list] - - menu_module = "" - for modimpl in modimpls_list: - if modimpl["moduleimpl_id"] == moduleimpl_id: - sel = "selected" - else: - sel = "" - menu_module += ( - """\n""" - % { - "modimpl_id": modimpl["moduleimpl_id"], - "modname": modimpl["module"]["code"] - + " " - + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]), - "sel": sel, - } - ) - if moduleimpl_id: - sel = "" - else: - sel = "selected" # aucun module specifie - H.append( - """

- Module concerné par ces absences (%(optionel_txt)s): - -

""" - % { - "menu_module": menu_module, - "url": base_url, - "sel": sel, - "optionel_txt": 'requis' - if require_module - else "optionnel", - } - ) - - H += self._gen_form_saisie_groupe( - etuds, dates, destination, moduleimpl_id, require_module - ) - H.append(self.sco_footer(REQUEST)) - return "\n".join(H) - - def _gen_form_saisie_groupe( - self, etuds, dates, destination="", moduleimpl_id=None, require_module=False - ): - """Formulaire saisie absences - - Args: - etuds: liste des étudiants - dates: liste ordonnée de dates iso, par exemple: [ '2020-12-24', ... ] - moduleimpl_id: optionnel, module concerné. - """ - H = [ - """ - -
-
- - - """ - % ( - "true" if (require_module and not moduleimpl_id) else "false", - len(etuds), - ) - ] - # Dates - odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates] - begin = dates[0] - end = dates[-1] - # Titres colonnes - noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ] - jn = sco_abs.day_names(self) - for d in odates: - idx_jour = d.weekday() - noms_jours.append(jn[idx_jour]) - for jour in noms_jours: - H.append( - '" - ) - H.append("") - for d in odates: - H.append( - '" - ) - H.append("") - H.append("" * len(dates)) - H.append("") - # - if not etuds: - H.append( - '' - ) - i = 1 - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - for etud in etuds: - i += 1 - etudid = etud["etudid"] - # UE capitalisee dans semestre courant ? - cap = [] - if etud["cursem"]: - nt = self.Notes._getNotesCache().get_NotesTable( - self.Notes, etud["cursem"]["formsemestre_id"] - ) # > get_ues, get_etud_ue_status - for ue in nt.get_ues(): - status = nt.get_etud_ue_status(etudid, ue["ue_id"]) - if status["is_capitalized"]: - cap.append(ue["acronyme"]) - if cap: - capstr = ' (%s cap.)' % ", ".join(cap) - else: - capstr = "" - - tr_class = ("row_1", "row_2", "row_3")[i % 3] - td_matin_class = ("matin_1", "matin_2", "matin_3")[i % 3] - - H.append( - '' - % (tr_class, etudid, etudid, etud["nomprenom"], capstr) - ) - etud_abs = self.ListAbsInRange( - etudid, begin, end, moduleimpl_id=moduleimpl_id, cursor=cursor - ) - for d in odates: - date = d.strftime("%Y-%m-%d") - # matin - is_abs = {"jour": d, "matin": True} in etud_abs - if is_abs: - checked = "checked" - else: - checked = "" - # bulle lors du passage souris - coljour = sco_abs.DAYNAMES[(calendar.weekday(d.year, d.month, d.day))] - datecol = coljour + " " + d.strftime("%d/%m/%Y") - bulle_am = '"' + etud["nomprenom"] + " - " + datecol + ' (matin)"' - bulle_pm = '"' + etud["nomprenom"] + " - " + datecol + ' (ap.midi)"' - - H.append( - '' - % ( - td_matin_class, - bulle_am, - etudid + ":" + date + ":" + "am", - checked, - etudid, - date + ":am", - ) - ) - # après-midi - is_abs = {"jour": d, "matin": False} in etud_abs - if is_abs: - checked = "checked" - else: - checked = "" - H.append( - '' - % ( - bulle_pm, - etudid + ":" + date + ":" + "pm", - checked, - etudid, - date + ":pm", - ) - ) - H.append("") - H.append("
%d étudiants' - + jour - + "
 ' - + d.strftime("%d/%m/%Y") - + "
 AMPM
Aucun étudiant inscrit !
%s%s
") - # place la liste des etudiants et les dates pour pouvoir effacer les absences - H.append( - '' - % ",".join([etud["etudid"] for etud in etuds]) - ) - H.append('' % dates[0]) - H.append('' % dates[-1]) - H.append('' % ",".join(dates)) - H.append( - '' - % urllib.quote(destination) - ) - # - # version pour formulaire avec AJAX (Yann LB) - H.append( - """ -

-

- -

-
-

Les cases cochées correspondent à des absences. - Les absences saisies ne sont pas justifiées (sauf si un justificatif a été entré - par ailleurs). -

Si vous "décochez" une case, l'absence correspondante sera supprimée. - Attention, les modifications sont automatiquement entregistrées au fur et à mesure. -

- """ - % destination - ) - return H - - def _TablesAbsEtud( - self, - etudid, - datedebut, - with_evals=True, - format="html", - absjust_only=0, - REQUEST=None, - ): - """Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours""" - absjust = self.ListeAbsJust(etudid=etudid, datedebut=datedebut) - absnonjust = self.ListeAbsNonJust(etudid=etudid, datedebut=datedebut) - # examens ces jours là ? - if with_evals: - cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor) - 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 and eval.moduleimpl_id = m.moduleimpl_id - and mi.moduleimpl_id = m.moduleimpl_id and mi.etudid = %(etudid)s""", - {"jour": a["jour"].strftime("%Y-%m-%d"), "etudid": etudid}, - ) - a["evals"] = cursor.dictfetchall() - cursor.execute( - """SELECT mi.moduleimpl_id - from absences abs, notes_moduleimpl_inscription mi, notes_moduleimpl m - where abs.matin = %(matin)s and abs.jour = %(jour)s and abs.etudid=%(etudid)s and abs.moduleimpl_id=mi.moduleimpl_id and mi.moduleimpl_id=m.moduleimpl_id - and mi.etudid = %(etudid)s""", - { - "matin": bool(a["matin"]), - "jour": a["jour"].strftime("%Y-%m-%d"), - "etudid": etudid, - }, - ) - a["absent"] = cursor.dictfetchall() - - def matin(x): - if x: - return "matin" - else: - return "après-midi" - - def descr_exams(a): - if not a.has_key("evals"): - return "" - ex = [] - for ev in a["evals"]: - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( - self.Notes, moduleimpl_id=ev["moduleimpl_id"] - )[0] - if format == "html": - ex.append( - '%s' - % (mod["moduleimpl_id"], mod["module"]["code"]) - ) - else: - ex.append(mod["module"]["code"]) - if ex: - return ", ".join(ex) - return "" - - def descr_abs(a): - ex = [] - for ev in a.get("absent", []): - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( - self.Notes, moduleimpl_id=ev["moduleimpl_id"] - )[0] - if format == "html": - ex.append( - '%s' - % (mod["moduleimpl_id"], mod["module"]["code"]) - ) - else: - ex.append(mod["module"]["code"]) - if ex: - return ", ".join(ex) - return "" - - # ajoute date formatée et évaluations - for L in (absnonjust, absjust): - for a in L: - if with_evals: - a["exams"] = descr_exams(a) - a["datedmy"] = a["jour"].strftime("%d/%m/%Y") - a["ampm"] = int(a["matin"]) - a["matin"] = matin(a["matin"]) - index = a["description"].find(")") - if index != -1: - a["motif"] = a["description"][1:index] - else: - a["motif"] = "" - a["description"] = descr_abs(a) or "" - - # ajoute lien pour justifier - if format == "html": - for a in absnonjust: - a["justlink"] = "justifier" - a["_justlink_target"] = ( - "doJustifAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s" - % (etudid, a["datedmy"], a["datedmy"], a["ampm"]) - ) - # - titles = { - "datedmy": "Date", - "matin": "", - "exams": "Examens ce jour", - "justlink": "", - "description": "Modules", - "motif": "Motif", - } - columns_ids = ["datedmy", "matin"] - if format in ("json", "xml"): - columns_ids += ["jour", "ampm"] - if with_evals: - columns_ids.append("exams") - - columns_ids.append("description") - columns_ids.append("motif") - if format == "html": - columns_ids.append("justlink") - - return titles, columns_ids, absnonjust, absjust - - security.declareProtected(ScoView, "EtatAbsencesGr") # ported from dtml - - def EtatAbsencesGr( - self, - group_ids=[], # list of groups to display - debut="", - fin="", - with_boursier=True, # colonne boursier - format="html", - REQUEST=None, - ): - """Liste les absences de groupes""" - datedebut = notesdb.DateDMYtoISO(debut) - datefin = notesdb.DateDMYtoISO(fin) - # Informations sur les groupes à afficher: - groups_infos = sco_groups_view.DisplayedGroupsInfos( - self, group_ids, REQUEST=REQUEST - ) - formsemestre_id = groups_infos.formsemestre_id - sem = groups_infos.formsemestre - - # Construit tableau (etudid, statut, nomprenom, nbJust, nbNonJust, NbTotal) - T = [] - for m in groups_infos.members: - etud = self.getEtudInfo(etudid=m["etudid"], filled=True)[0] - nbabs = self.CountAbs(etudid=etud["etudid"], debut=datedebut, fin=datefin) - nbabsjust = self.CountAbsJust( - etudid=etud["etudid"], debut=datedebut, fin=datefin - ) - nbjustifs_noabs = len( - self.ListeJustifs( - etudid=etud["etudid"], datedebut=datedebut, only_no_abs=True - ) - ) - # retrouve sem dans etud['sems'] - s = None - for s in etud["sems"]: - if s["formsemestre_id"] == formsemestre_id: - break - if not s or s["formsemestre_id"] != formsemestre_id: - raise ValueError( - "EtatAbsencesGr: can't retreive sem" - ) # bug or malicious arg - T.append( - { - "etudid": etud["etudid"], - "etatincursem": s["ins"]["etat"], - "nomprenom": etud["nomprenom"], - "nbabsjust": nbabsjust, - "nbabsnonjust": nbabs - nbabsjust, - "nbabs": nbabs, - "nbjustifs_noabs": nbjustifs_noabs, - "_nomprenom_target": "CalAbs?etudid=%s" % etud["etudid"], - "_nomprenom_td_attrs": 'id="%s" class="etudinfo"' % etud["etudid"], - "boursier": etud["boursier"], - } - ) - if s["ins"]["etat"] == "D": - T[-1]["_css_row_class"] = "etuddem" - T[-1]["nomprenom"] += " (dem)" - columns_ids = [ - "nomprenom", - "nbjustifs_noabs", - "nbabsjust", - "nbabsnonjust", - "nbabs", - ] - if with_boursier: - columns_ids[1:1] = ["boursier"] - if groups_infos.tous_les_etuds_du_sem: - gr_tit = "" - else: - if len(groups_infos.group_ids) > 1: - p = "des groupes" - else: - p = "du groupe" - if format == "html": - h = ' ' + groups_infos.groups_titles + "" - else: - h = groups_infos.groups_titles - gr_tit = p + h - - title = "Etat des absences %s" % gr_tit - if format == "xls" or format == "xml" or format == "json": - columns_ids = ["etudid"] + columns_ids - tab = GenTable( - columns_ids=columns_ids, - rows=T, - preferences=self.get_preferences(formsemestre_id), - titles={ - "etatincursem": "Etat", - "nomprenom": "Nom", - "nbabsjust": "Justifiées", - "nbabsnonjust": "Non justifiées", - "nbabs": "Total", - "nbjustifs_noabs": "Justifs non utilisés", - "boursier": "Bourse", - }, - html_sortable=True, - html_class="table_leftalign", - html_header=self.sco_header( - REQUEST, - page_title=title, - init_qtip=True, - javascripts=["js/etud_info.js"], - ), - html_title=self.Notes.html_sem_header( - REQUEST, "%s" % title, sem, with_page_header=False - ) - + "

Période du %s au %s (nombre de demi-journées)
" - % (debut, fin), - base_url="%s&formsemestre_id=%s&debut=%s&fin=%s" - % (groups_infos.base_url, formsemestre_id, debut, fin), - filename="etat_abs_" - + scu.make_filename( - "%s de %s" % (groups_infos.groups_filename, sem["titreannee"]) - ), - caption=title, - html_next_section="""

-

-Justifs non utilisés: nombre de demi-journées avec justificatif mais sans absences relevées. -

-

-Cliquez sur un nom pour afficher le calendrier des absences
-ou entrez une date pour visualiser les absents un jour donné : -

-
-
- -%s - - -
- """ - % (REQUEST.URL0, formsemestre_id, groups_infos.get_form_elem()), - ) - return tab.make_page(self, format=format, REQUEST=REQUEST) - - security.declareProtected(ScoView, "EtatAbsencesDate") # ported from dtml - - def EtatAbsencesDate( - self, group_ids=[], date=None, REQUEST=None # list of groups to display - ): - """Etat des absences pour un groupe à une date donnée""" - # Informations sur les groupes à afficher: - groups_infos = sco_groups_view.DisplayedGroupsInfos( - self, group_ids, REQUEST=REQUEST - ) - H = [self.sco_header(page_title="Etat des absences", REQUEST=REQUEST)] - if date: - dateiso = notesdb.DateDMYtoISO(date) - nbetud = 0 - t_nbabsjustam = 0 - t_nbabsam = 0 - t_nbabsjustpm = 0 - t_nbabspm = 0 - H.append("

État des absences le %s

" % date) - H.append( - """ - - - """ - ) - for etud in groups_infos.members: - nbabsam = self.CountAbs( - etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=1 - ) - nbabspm = self.CountAbs( - etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=0 - ) - if (nbabsam != 0) or (nbabspm != 0): - nbetud += 1 - nbabsjustam = self.CountAbsJust( - etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=1 - ) - nbabsjustpm = self.CountAbsJust( - etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=0 - ) - H.append( - """") - H.append( - """""" - % (t_nbabsam, t_nbabsjustam, t_nbabspm, t_nbabsjustpm) - ) - H.append("
 MatinAprès-midi
- %(nomprenom)s""" - % etud - ) # """ - if nbabsam != 0: - if nbabsjustam: - H.append("Just.") - t_nbabsjustam += 1 - else: - H.append("Abs.") - t_nbabsam += 1 - else: - H.append("") - H.append('') - if nbabspm != 0: - if nbabsjustpm: - H.append("Just.") - t_nbabsjustam += 1 - else: - H.append("Abs.") - t_nbabspm += 1 - else: - H.append("") - H.append("
%d abs, %d just.%d abs, %d just.
") - if nbetud == 0: - H.append("

Aucune absence !

") - else: - H.append( - """

Erreur: vous n'avez pas choisi de date !

- Continuer""" - % REQUEST.HTTP_REFERER - ) - - return "\n".join(H) + self.sco_footer(REQUEST) - - # ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail) - security.declareProtected(ScoAbsAddBillet, "AddBilletAbsence") - - def AddBilletAbsence( - self, - begin, - end, - description, - etudid=False, - code_nip=None, - code_ine=None, - justified=True, - REQUEST=None, - xml_reply=True, - ): - """Memorise un "billet" - begin et end sont au format ISO (eg "1999-01-08 04:05:06") - """ - t0 = time.time() - # check etudid - etuds = self.getEtudInfo( - etudid=etudid, code_nip=code_nip, REQUEST=REQUEST, filled=True - ) - if not etuds: - return scu.log_unknown_etud(self, REQUEST=REQUEST) - etud = etuds[0] - # check dates - begin_date = dateutil.parser.isoparse(begin) # may raises ValueError - end_date = dateutil.parser.isoparse(end) - if begin_date > end_date: - raise ValueError("invalid dates") - # - justified = int(justified) - # - cnx = self.GetDBConnexion() - billet_id = sco_abs.billet_absence_create( - cnx, - { - "etudid": etud["etudid"], - "abs_begin": begin, - "abs_end": end, - "description": description, - "etat": 0, - "justified": justified, - }, - ) - if xml_reply: - # Renvoie le nouveau billet en XML - if REQUEST: - REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) - - billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) - tab = self._tableBillets(billets, etud=etud) - log( - "AddBilletAbsence: new billet_id=%s (%gs)" - % (billet_id, time.time() - t0) - ) - return tab.make_page(self, REQUEST=REQUEST, format="xml") - else: - return billet_id - - security.declareProtected(ScoAbsAddBillet, "AddBilletAbsenceForm") - - def AddBilletAbsenceForm(self, etudid, REQUEST=None): - """Formulaire ajout billet (pour tests seulement, le vrai formulaire accessible aux etudiants - étant sur le portail étudiant). - """ - etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] - H = [ - self.sco_header( - REQUEST, page_title="Billet d'absence de %s" % etud["nomprenom"] - ) - ] - tf = TrivialFormulator( - REQUEST.URL0, - REQUEST.form, - ( - ("etudid", {"input_type": "hidden"}), - ("begin", {"input_type": "date"}), - ("end", {"input_type": "date"}), - ( - "justified", - {"input_type": "boolcheckbox", "default": 0, "title": "Justifiée"}, - ), - ("description", {"input_type": "textarea"}), - ), - ) - if tf[0] == 0: - return "\n".join(H) + tf[1] + self.sco_footer(REQUEST) - elif tf[0] == -1: - return REQUEST.RESPONSE.redirect(self.ScoURL()) - else: - e = tf[2]["begin"].split("/") - begin = e[2] + "-" + e[1] + "-" + e[0] + " 00:00:00" - e = tf[2]["end"].split("/") - end = e[2] + "-" + e[1] + "-" + e[0] + " 00:00:00" - log( - self.AddBilletAbsence( - begin, - end, - tf[2]["description"], - etudid=etudid, - xml_reply=True, - justified=tf[2]["justified"], - ) - ) - return REQUEST.RESPONSE.redirect("listeBilletsEtud?etudid=" + etudid) - - def _tableBillets(self, billets, etud=None, title=""): - for b in billets: - if b["abs_begin"].hour < 12: - m = " matin" - else: - m = " après-midi" - b["abs_begin_str"] = b["abs_begin"].strftime("%d/%m/%Y") + m - if b["abs_end"].hour < 12: - m = " matin" - else: - m = " après-midi" - b["abs_end_str"] = b["abs_end"].strftime("%d/%m/%Y") + m - if b["etat"] == 0: - if b["justified"] == 0: - b["etat_str"] = "à traiter" - else: - b["etat_str"] = "à justifier" - b["_etat_str_target"] = ( - "ProcessBilletAbsenceForm?billet_id=%s" % b["billet_id"] - ) - if etud: - b["_etat_str_target"] += "&etudid=%s" % etud["etudid"] - b["_billet_id_target"] = b["_etat_str_target"] - else: - b["etat_str"] = "ok" - if not etud: - # ajoute info etudiant - e = self.getEtudInfo(etudid=b["etudid"], filled=1) - if not e: - b["nomprenom"] = "???" # should not occur - else: - b["nomprenom"] = e[0]["nomprenom"] - b["_nomprenom_target"] = "ficheEtud?etudid=%s" % b["etudid"] - if etud and not title: - title = "Billets d'absence déclarés par %(nomprenom)s" % etud - else: - title = title - columns_ids = ["billet_id"] - if not etud: - columns_ids += ["nomprenom"] - columns_ids += ["abs_begin_str", "abs_end_str", "description", "etat_str"] - - tab = GenTable( - titles={ - "billet_id": "Numéro", - "abs_begin_str": "Début", - "abs_end_str": "Fin", - "description": "Raison de l'absence", - "etat_str": "Etat", - }, - columns_ids=columns_ids, - page_title=title, - html_title="

%s

" % title, - preferences=self.get_preferences(), - rows=billets, - html_sortable=True, - ) - return tab - - security.declareProtected(ScoView, "listeBilletsEtud") - - def listeBilletsEtud(self, etudid=False, REQUEST=None, format="html"): - """Liste billets pour un etudiant""" - etuds = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST) - if not etuds: - return scu.log_unknown_etud(self, format=format, REQUEST=REQUEST) - - etud = etuds[0] - cnx = self.GetDBConnexion() - billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]}) - tab = self._tableBillets(billets, etud=etud) - return tab.make_page(self, REQUEST=REQUEST, format=format) - - security.declareProtected(ScoView, "XMLgetBilletsEtud") - - def XMLgetBilletsEtud(self, etudid=False, REQUEST=None): - """Liste billets pour un etudiant""" - if not self.get_preference("handle_billets_abs"): - return "" - t0 = time.time() - r = self.listeBilletsEtud(etudid, REQUEST=REQUEST, format="xml") - log("XMLgetBilletsEtud (%gs)" % (time.time() - t0)) - return r - - security.declareProtected(ScoView, "listeBillets") - - def listeBillets(self, REQUEST=None): - """Page liste des billets non traités et formulaire recherche d'un billet""" - cnx = self.GetDBConnexion() - billets = sco_abs.billet_absence_list(cnx, {"etat": 0}) - tab = self._tableBillets(billets) - T = tab.html() - H = [ - self.sco_header(REQUEST, page_title="Billet d'absence non traités"), - "

Billets d'absence en attente de traitement (%d)

" % len(billets), - ] - - tf = TrivialFormulator( - REQUEST.URL0, - REQUEST.form, - (("billet_id", {"input_type": "text", "title": "Numéro du billet"}),), - submitbutton=False, - ) - if tf[0] == 0: - return "\n".join(H) + tf[1] + T + self.sco_footer(REQUEST) - else: - return REQUEST.RESPONSE.redirect( - "ProcessBilletAbsenceForm?billet_id=" + tf[2]["billet_id"] - ) - - security.declareProtected(ScoAbsChange, "deleteBilletAbsence") - - def deleteBilletAbsence(self, billet_id, REQUEST=None, dialog_confirmed=False): - """Supprime un billet.""" - cnx = self.GetDBConnexion() - billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) - if not billets: - return REQUEST.RESPONSE.redirect( - "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id - ) - if not dialog_confirmed: - tab = self._tableBillets(billets) - return self.confirmDialog( - """

Supprimer ce billet ?

""" + tab.html(), - dest_url="", - REQUEST=REQUEST, - cancel_url="listeBillets", - parameters={"billet_id": billet_id}, - ) - - sco_abs.billet_absence_delete(cnx, billet_id) - - return REQUEST.RESPONSE.redirect("listeBillets?head_message=Billet%20supprimé") - - def _ProcessBilletAbsence(self, billet, estjust, description, REQUEST): - """Traite un billet: ajoute absence(s) et éventuellement justificatifs, - et change l'état du billet à 1. - NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après-midi. - """ - cnx = self.GetDBConnexion() - if billet["etat"] != 0: - log("billet=%s" % billet) - log("billet deja traité !") - return -1 - n = 0 # nombre de demi-journées d'absence ajoutées - # 1-- ajout des absences (et justifs) - datedebut = billet["abs_begin"].strftime("%d/%m/%Y") - datefin = billet["abs_end"].strftime("%d/%m/%Y") - dates = sco_abs.DateRangeISO(self, datedebut, datefin) - # commence après-midi ? - if dates and billet["abs_begin"].hour > 11: - self._AddAbsence( - billet["etudid"], dates[0], 0, estjust, REQUEST, description=description - ) - n += 1 - dates = dates[1:] - # termine matin ? - if dates and billet["abs_end"].hour < 12: - self._AddAbsence( - billet["etudid"], - dates[-1], - 1, - estjust, - REQUEST, - description=description, - ) - n += 1 - dates = dates[:-1] - - for jour in dates: - self._AddAbsence( - billet["etudid"], jour, 0, estjust, REQUEST, description=description - ) - self._AddAbsence( - billet["etudid"], jour, 1, estjust, REQUEST, description=description - ) - n += 2 - - # 2- change etat du billet - sco_abs.billet_absence_edit(cnx, {"billet_id": billet["billet_id"], "etat": 1}) - - return n - - security.declareProtected(ScoAbsChange, "ProcessBilletAbsenceForm") - - def ProcessBilletAbsenceForm(self, billet_id, REQUEST=None): - """Formulaire traitement d'un billet""" - cnx = self.GetDBConnexion() - billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) - if not billets: - return REQUEST.RESPONSE.redirect( - "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id - ) - billet = billets[0] - etudid = billet["etudid"] - etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] - - H = [ - self.sco_header( - REQUEST, - page_title="Traitement billet d'absence de %s" % etud["nomprenom"], - ), - '

Traitement du billet %s : %s

' - % (billet_id, etudid, etud["nomprenom"]), - ] - - tf = TrivialFormulator( - REQUEST.URL0, - REQUEST.form, - ( - ("billet_id", {"input_type": "hidden"}), - ( - "etudid", - {"input_type": "hidden"}, - ), # pour centrer l'UI sur l'étudiant - ( - "estjust", - {"input_type": "boolcheckbox", "title": "Absences justifiées"}, - ), - ("description", {"input_type": "text", "size": 42, "title": "Raison"}), - ), - initvalues={ - "description": billet["description"], - "estjust": billet["justified"], - "etudid": etudid, - }, - submitlabel="Enregistrer ces absences", - ) - if tf[0] == 0: - tab = self._tableBillets([billet], etud=etud) - H.append(tab.html()) - if billet["justified"] == 1: - H.append( - """

L'étudiant pense pouvoir justifier cette absence.
Vérifiez le justificatif avant d'enregistrer.

""" - ) - F = ( - """

Supprimer ce billet (utiliser en cas d'erreur, par ex. billet en double)

""" - % billet_id - ) - F += '

Liste de tous les billets en attente

' - - return "\n".join(H) + "
" + tf[1] + F + self.sco_footer(REQUEST) - elif tf[0] == -1: - return REQUEST.RESPONSE.redirect(self.ScoURL()) - else: - n = self._ProcessBilletAbsence( - billet, tf[2]["estjust"], tf[2]["description"], REQUEST - ) - if tf[2]["estjust"]: - j = "justifiées" - else: - j = "non justifiées" - H.append('
') - if n > 0: - H.append("%d absences (1/2 journées) %s ajoutées" % (n, j)) - elif n == 0: - H.append("Aucun jour d'absence dans les dates indiquées !") - elif n < 0: - H.append("Ce billet avait déjà été traité !") - H.append( - '

Autre billets en attente

Billets déclarés par %s

' - % (etud["nomprenom"]) - ) - billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]}) - tab = self._tableBillets(billets, etud=etud) - H.append(tab.html()) - return "\n".join(H) + self.sco_footer(REQUEST) - - security.declareProtected(ScoView, "XMLgetAbsEtud") - - def XMLgetAbsEtud(self, beg_date="", end_date="", REQUEST=None): - """returns list of absences in date interval""" - t0 = time.time() - etud = self.getEtudInfo(REQUEST=REQUEST)[0] - exp = re.compile(r"^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$") - if not exp.match(beg_date): - raise ScoValueError("invalid date: %s" % beg_date) - if not exp.match(end_date): - raise ScoValueError("invalid date: %s" % end_date) - - Abs = self._ListeAbsDate(etud["etudid"], beg_date, end_date) - - REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) - doc = jaxml.XML_document(encoding=scu.SCO_ENCODING) - doc.absences(etudid=etud["etudid"], beg_date=beg_date, end_date=end_date) - doc._push() - for a in Abs: - if a["estabs"]: # ne donne pas les justifications si pas d'absence - doc._push() - doc.abs( - begin=a["begin"], - end=a["end"], - description=a["description"], - justified=a["estjust"], - ) - doc._pop() - doc._pop() - log("XMLgetAbsEtud (%gs)" % (time.time() - t0)) - return repr(doc) diff --git a/app/scodoc/ZScoDoc.py b/app/scodoc/ZScoDoc.py index 68ffa34d..9fd28741 100644 --- a/app/scodoc/ZScoDoc.py +++ b/app/scodoc/ZScoDoc.py @@ -55,7 +55,7 @@ from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import- from email.Header import Header # pylint: disable=no-name-in-module,import-error from email import Encoders # pylint: disable=no-name-in-module,import-error -from sco_zope import ( +from app.scodoc.sco_zope import ( ObjectManager, PropertyManager, RoleManager, @@ -72,13 +72,13 @@ try: except: import ZPsycopgDA.DA as ZopeDA # interp.py -import sco_utils as scu -import VERSION +import app.scodoc.sco_utils as scu +from app.scodoc import VERSION import mails -from notes_log import log -import sco_find_etud -import sco_users -from sco_permissions import ( +from app.scodoc.notes_log import log +from app.scodoc import sco_find_etud +from app.scodoc import sco_users +from app.scodoc.sco_permissions import ( ScoView, ScoEnsView, ScoImplement, @@ -94,8 +94,13 @@ from sco_permissions import ( ScoEditApo, ScoSuperAdmin, ) -from sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError, AccessDenied -import html_sco_header +from app.scodoc.sco_exceptions import ( + ScoValueError, + ScoLockedFormError, + ScoGenError, + AccessDenied, +) +from app.scodoc import html_sco_header class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit): @@ -803,7 +808,7 @@ ErrorType: %(error_type)s last_dept = None last_date = None for (dept, etuds) in depts_etud: - scolars.fillEtudsInfo(self, etuds) + sco_etud.fill_etuds_info(self, etuds) etud = etuds[0] if etud["sems"]: if (not last_date) or (etud["sems"][0]["date_fin_iso"] > last_date): diff --git a/app/scodoc/ZScoUsers.py b/app/scodoc/ZScoUsers.py index 6f226a94..a8418ed4 100644 --- a/app/scodoc/ZScoUsers.py +++ b/app/scodoc/ZScoUsers.py @@ -34,23 +34,23 @@ import md5 import base64 import jaxml -from sco_zope import * # pylint: disable=unused-wildcard-import +from app.scodoc.sco_zope import * # pylint: disable=unused-wildcard-import # --------------- -import sco_utils as scu -import notesdb as ndb -from notes_log import log -from scolog import logdb +import app.scodoc.sco_utils as scu +import app.scodoc.notesdb as ndb +from app.scodoc.notes_log import log +from app.scodoc.scolog import logdb from scolars import format_prenom, format_nom -import sco_import_users -import sco_excel -from TrivialFormulator import TrivialFormulator, TF, tf_error_message -from gen_tables import GenTable -import scolars -import sco_cache -import sco_users +from app.scodoc import sco_import_users +from app.scodoc import sco_excel +from app.scodoc.TrivialFormulator import TrivialFormulator, TF, tf_error_message +from app.scodoc.gen_tables import GenTable +from app.scodoc import sco_etud +from app.scodoc import sco_cache +from app.scodoc import sco_users -from sco_exceptions import ( +from app.scodoc.sco_exceptions import ( AccessDenied, ScoException, ScoValueError, @@ -305,7 +305,7 @@ class ZScoUsers( info["prenomnom"] = (prenom_abbrv + " " + n).strip() # nom_fmt et prenom_fmt: minuscule capitalisé info["nom_fmt"] = n - info["prenom_fmt"] = scolars.format_prenom(p) + info["prenom_fmt"] = sco_etud.format_prenom(p) # nomcomplet est le prenom et le nom complets info["nomcomplet"] = info["prenom_fmt"] + " " + info["nom_fmt"] # nomplogin est le nom en majuscules suivi du prénom et du login diff --git a/app/scodoc/__init__.py b/app/scodoc/__init__.py index 24917c89..0481bb53 100644 --- a/app/scodoc/__init__.py +++ b/app/scodoc/__init__.py @@ -27,4 +27,3 @@ """ScoDoc core package """ -# from app.scodoc import sco_core diff --git a/app/scodoc/debug.py b/app/scodoc/debug.py index 55605211..2f8d9949 100644 --- a/app/scodoc/debug.py +++ b/app/scodoc/debug.py @@ -34,25 +34,37 @@ nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_i import pdb # pylint: disable=unused-import import pprint -import app.scodoc.notesdb as ndb -from app.scodoc.notesdb import * # pylint: disable=unused-wildcard-import +import app.scodoc.notesdb as ndb # pylint: disable=unused-import +from app.scodoc.notesdb import * # pylint: disable=wildcard-import,unused-wildcard-import from app.scodoc.notes_log import log import app.scodoc.sco_utils as scu -from app.scodoc.sco_utils import * # pylint: disable=unused-wildcard-import +from app.scodoc.sco_utils import * # pylint: disable=wildcard-import, unused-wildcard-import -from gen_tables import GenTable -import sco_archives -import sco_groups -import sco_evaluations -import sco_formsemestre -import sco_formsemestre_edit -import sco_compute_moy -import sco_parcours_dut -import sco_codes_parcours -import sco_bulletins -import sco_excel -import sco_formsemestre_status -import sco_bulletins_xml +from app.scodoc.gen_tables import GenTable +from app.scodoc import html_sco_header +from app.scodoc import sco_archives +from app.scodoc import sco_bulletins +from app.scodoc import sco_bulletins_xml +from app.scodoc import sco_codes_parcours +from app.scodoc import sco_compute_moy +from app.scodoc import sco_core +from app.scodoc import sco_edit_matiere +from app.scodoc import sco_edit_module +from app.scodoc import sco_edit_ue +from app.scodoc import sco_excel +from app.scodoc import sco_formations +from app.scodoc import sco_formsemestre +from app.scodoc import sco_formsemestre_edit +from app.scodoc import sco_formsemestre_status +from app.scodoc import sco_formsemestre_validation +from app.scodoc import sco_groups +from app.scodoc import sco_moduleimpl +from app.scodoc import sco_news +from app.scodoc import sco_parcours_dut +from app.scodoc import sco_permissions +from app.scodoc import sco_preferences +from app.scodoc import sco_tag_module +from app.scodoc import sco_etud # Prend le premier departement comme context diff --git a/app/scodoc/dutrules.py b/app/scodoc/dutrules.py index 9e524058..ed281bb9 100644 --- a/app/scodoc/dutrules.py +++ b/app/scodoc/dutrules.py @@ -4,7 +4,7 @@ # # Command: ./csv2rules.py misc/parcoursDUT.csv # -from sco_codes_parcours import ( +from app.scodoc.sco_codes_parcours import ( DUTRule, ADC, ADJ, diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 08523794..93586e70 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -54,11 +54,11 @@ from reportlab.lib import styles from reportlab.lib.units import inch, cm, mm from reportlab.rl_config import defaultPageSize # pylint: disable=no-name-in-module -import sco_utils as scu -import sco_excel -import sco_pdf -from sco_pdf import SU -from notes_log import log +import app.scodoc.sco_utils as scu +import app.scodoc.sco_excel +import app.scodoc.sco_pdf +from app.scodoc.sco_pdf import SU +from app.scodoc.notes_log import log def mark_paras(L, tags): @@ -71,6 +71,8 @@ def mark_paras(L, tags): class DEFAULT_TABLE_PREFERENCES: + """Default preferences for tables created without preferences argument""" + values = { "SCOLAR_FONT": "Helvetica", # used for PDF, overriden by preferences argument "SCOLAR_FONT_SIZE": 10, diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index 77a20ec0..a22275a3 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -25,16 +25,15 @@ # ############################################################################## +"""HTML Header/Footer for ScoDoc pages +""" + import cgi -import sco_utils as scu -from notes_log import log -import html_sidebar -import VERSION - -""" -HTML Header/Footer for ScoDoc pages -""" +import app.scodoc.sco_utils as scu +from app.scodoc.notes_log import log +from app.scodoc import html_sidebar +from app.scodoc import VERSION # Some constants: @@ -155,6 +154,7 @@ def sco_header( user_check=True, # verifie passwords temporaires ): "Main HTML page header for ScoDoc" + from app.scodoc.sco_formsemestre_status import formsemestre_page_title # context est une instance de ZScolar. container est une instance qui "acquiert" ZScolar if container: @@ -364,6 +364,3 @@ def html_sem_header( return h + """

%s

""" % (title) else: return h - - -from sco_formsemestre_status import formsemestre_page_title diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index 1343d079..4083a75d 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -25,16 +25,14 @@ # ############################################################################## -import sco_utils as scu -import sco_preferences -from sco_abs import getAbsSemEtud -from app.scodoc.sco_permissions import Permission -import scolars - """ Génération de la "sidebar" (marge gauche des pages HTML) """ +import app.scodoc.sco_utils as scu +from app.scodoc import sco_preferences +from app.scodoc.sco_permissions import Permission + def sidebar_common(context, REQUEST=None): "partie commune a toutes les sidebar" @@ -78,6 +76,9 @@ def sidebar_common(context, REQUEST=None): def sidebar(context, REQUEST=None): "Main HTML page sidebar" # rewritten from legacy DTML code + from app.scodoc.sco_abs import getAbsSemEtud + from app.scodoc import sco_etud + params = {"ScoURL": scu.ScoURL(), "SCO_USER_MANUAL": scu.SCO_USER_MANUAL} H = ['