From 6a4d6e5109c599a590f42ed1e8405f0b9e819e35 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 21 Jun 2021 10:17:16 +0200 Subject: [PATCH] WIP suite de la migration --- app/scodoc/ZScoDoc.py | 838 ------------------ app/scodoc/debug.py | 2 +- app/scodoc/gen_tables.py | 7 +- app/scodoc/notes_log.py | 4 +- app/scodoc/pe_jurype.py | 7 +- app/scodoc/pe_semestretag.py | 5 +- app/scodoc/sco_abs.py | 13 +- app/scodoc/sco_abs_notification.py | 4 +- app/scodoc/sco_abs_views.py | 5 +- app/scodoc/sco_archives_etud.py | 6 +- app/scodoc/sco_bulletins.py | 5 +- app/scodoc/sco_compute_moy.py | 5 +- app/scodoc/sco_edit_formation.py | 2 +- app/scodoc/{mails.py => sco_emails.py} | 1 + app/scodoc/sco_etape_apogee.py | 2 +- app/scodoc/sco_etud.py | 8 +- app/scodoc/sco_evaluations.py | 191 +++- app/scodoc/sco_excel.py | 1 + app/scodoc/sco_find_etud.py | 13 +- app/scodoc/sco_formations.py | 40 +- app/scodoc/sco_formsemestre.py | 49 +- app/scodoc/sco_formsemestre_edit.py | 24 +- app/scodoc/sco_formsemestre_exterieurs.py | 4 +- app/scodoc/sco_formsemestre_inscriptions.py | 16 +- app/scodoc/sco_formsemestre_status.py | 17 +- app/scodoc/sco_groups.py | 58 +- app/scodoc/sco_groups_edit.py | 4 +- .../{ImportScolars.py => sco_import_etuds.py} | 1 + app/scodoc/sco_import_users.py | 4 +- app/scodoc/sco_inscr_passage.py | 8 +- app/scodoc/sco_moduleimpl_inscriptions.py | 4 +- app/scodoc/sco_news.py | 4 +- app/scodoc/sco_page_etud.py | 8 +- app/scodoc/sco_permissions_check.py | 67 +- app/scodoc/sco_saisie_notes.py | 1 + app/scodoc/sco_trombino.py | 6 +- app/scodoc/sco_ue_external.py | 2 +- app/scodoc/sco_utils.py | 9 + app/views/absences.py | 12 +- app/views/essais.py | 2 +- app/views/notes.py | 247 +----- app/views/scolar.py | 18 +- refactor.py | 10 +- 43 files changed, 494 insertions(+), 1240 deletions(-) delete mode 100644 app/scodoc/ZScoDoc.py rename app/scodoc/{mails.py => sco_emails.py} (99%) rename app/scodoc/{ImportScolars.py => sco_import_etuds.py} (99%) mode change 100644 => 100755 refactor.py diff --git a/app/scodoc/ZScoDoc.py b/app/scodoc/ZScoDoc.py deleted file mode 100644 index 9fd28741..00000000 --- a/app/scodoc/ZScoDoc.py +++ /dev/null @@ -1,838 +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 -# -############################################################################## - -"""Site ScoDoc pour plusieurs departements: - gestion de l'installation et des creation de départements. - - Chaque departement est géré par un ZScolar sous ZScoDoc. -""" - -import time -import datetime -import string -import glob -import re -import inspect -import urllib -import urllib2 -import cgi -import xml - -from cStringIO import StringIO -from zipfile import ZipFile -import os.path -import traceback - -from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error - MIMEMultipart, -) -from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error -from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error -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 app.scodoc.sco_zope import ( - ObjectManager, - PropertyManager, - RoleManager, - Item, - Persistent, - Implicit, - ClassSecurityInfo, - DTMLFile, - Globals, -) - -try: - import Products.ZPsycopgDA.DA as ZopeDA -except: - import ZPsycopgDA.DA as ZopeDA # interp.py - -import app.scodoc.sco_utils as scu -from app.scodoc import VERSION -import mails -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, - ScoChangeFormation, - ScoObservateur, - ScoEtudInscrit, - ScoEtudChangeGroups, - ScoEtudChangeAdr, - ScoEtudSupprAnnotations, - ScoEditAllEvals, - ScoEditAllNotes, - ScoEditFormationTags, - ScoEditApo, - ScoSuperAdmin, -) -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): - - "ZScoDoc object" - - meta_type = "ZScoDoc" - security = ClassSecurityInfo() - file_path = Globals.package_home(globals()) - - # 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 - ) - - def __init__(self, id, title): - "Initialise a new instance of ZScoDoc" - self.id = id - self.title = title - self.manage_addProperty("admin_password_initialized", "0", "string") - - security.declareProtected(ScoView, "ScoDocURL") - - def ScoDocURL(self): - "base URL for this instance (top level for ScoDoc site)" - return self.absolute_url() - - def _check_admin_perm(self, REQUEST): - """Check if user has permission to add/delete departements""" - authuser = REQUEST.AUTHENTICATED_USER - if authuser.has_role("manager") or authuser.has_permission( - Permission.ScoSuperAdmin, self - ): - return "" - else: - return """

Vous n'avez pas le droit d'accéder à cette page

""" - - def _check_users_folder(self, REQUEST=None): - """Vérifie UserFolder et le crée s'il le faut""" - try: - _ = self.UsersDB - return "" - except: - e = self._check_admin_perm(REQUEST) - if not e: # admin permissions: - self.create_users_cnx(REQUEST) - self.create_users_folder(REQUEST) - return '
Création du connecteur utilisateurs réussie
' - else: - return """
Installation non terminée: connectez vous avec les droits d'administrateur
""" - - security.declareProtected("View", "create_users_folder") - - def create_users_folder(self, REQUEST=None): - """Create Zope user folder""" - e = self._check_admin_perm(REQUEST) - if e: - return e - - if REQUEST is None: - REQUEST = {} - - REQUEST.form["pgauth_connection"] = "UsersDB" - REQUEST.form["pgauth_table"] = "sco_users" - REQUEST.form["pgauth_usernameColumn"] = "user_name" - REQUEST.form["pgauth_passwordColumn"] = "passwd" - REQUEST.form["pgauth_rolesColumn"] = "roles" - - add_method = self.manage_addProduct["OFSP"].manage_addexUserFolder - log("create_users_folder: in %s" % self.id) - return add_method( - authId="pgAuthSource", - propId="nullPropSource", - memberId="nullMemberSource", - groupId="nullGroupSource", - cryptoId="MD51", - # doAuth='1', doProp='1', doMember='1', doGroup='1', allDone='1', - cookie_mode=2, - session_length=500, - not_session_length=0, - REQUEST=REQUEST, - ) - - def _fix_users_folder(self): - """removes docLogin and docLogout dtml methods from exUserFolder, so that we use ours. - (called each time be index_html, to fix old ScoDoc installations.) - """ - try: - self.acl_users.manage_delObjects(ids=["docLogin", "docLogout"]) - except: - pass - # add missing getAuthFailedMessage (bug in exUserFolder ?) - try: - _ = self.getAuthFailedMessage - except: - log("adding getAuthFailedMessage to Zope install") - parent = self.aq_parent - from OFS.DTMLMethod import addDTMLMethod # pylint: disable=import-error - - addDTMLMethod(parent, "getAuthFailedMessage", file="Identification") - - security.declareProtected("View", "create_users_cnx") - - def create_users_cnx(self, REQUEST=None): - """Create Zope connector to UsersDB - - Note: la connexion est fixée (SCOUSERS) (base crée par l'installeur) ! - Les utilisateurs avancés pourront la changer ensuite. - """ - # ce connecteur zope - db est encore pour l'instant utilisé par exUserFolder.pgAuthSource - # (en lecture seule en principe) - oid = "UsersDB" - log("create_users_cnx: in %s" % self.id) - da = ZopeDA.Connection( - oid, - "Cnx bd utilisateurs", - scu.SCO_DEFAULT_SQL_USERS_CNX, - False, - check=1, - tilevel=2, - encoding="LATIN1", - ) - self._setObject(oid, da) - - security.declareProtected("View", "change_admin_user") - - def change_admin_user(self, password, REQUEST=None): - """Change password of admin user""" - # note: controle sur le role et non pas sur une permission - # (non definies au top level) - if not REQUEST.AUTHENTICATED_USER.has_role("Manager"): - log("user %s is not Manager" % REQUEST.AUTHENTICATED_USER) - log("roles=%s" % REQUEST.AUTHENTICATED_USER.getRolesInContext(self)) - raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") - log("trying to change admin password") - # 1-- check strong password - if not sco_users.is_valid_password(password): - log("refusing weak password") - return REQUEST.RESPONSE.redirect( - "change_admin_user_form?message=Mot%20de%20passe%20trop%20simple,%20recommencez" - ) - # 2-- change password for admin user - username = "admin" - acl_users = self.aq_parent.acl_users - user = acl_users.getUser(username) - r = acl_users._changeUser( - username, password, password, user.roles, user.domains - ) - if not r: - # OK, set property to indicate we changed the password - log("admin password changed successfully") - self.manage_changeProperties(admin_password_initialized="1") - return r or REQUEST.RESPONSE.redirect("index_html") - - security.declareProtected("View", "change_admin_user_form") - - def change_admin_user_form(self, message="", REQUEST=None): - """Form allowing to change the ScoDoc admin password""" - # note: controle sur le role et non pas sur une permission - # (non definies au top level) - if not REQUEST.AUTHENTICATED_USER.has_role("Manager"): - raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") - H = [ - html_sco_header.scodoc_top_html_header( - self, REQUEST, page_title="ScoDoc: changement mot de passe" - ) - ] - if message: - H.append('
%s
' % message) - H.append( - """

Changement du mot de passe administrateur (utilisateur admin)

-

-

- - -
Nouveau mot de passe:
Confirmation:
- -""" - ) - H.append("""""") - return "\n".join(H) - - security.declareProtected("View", "list_depts") - - def list_depts(self, viewable=True, format=None, REQUEST=None): - """List departments - If viewable, list only depts viewable the current user. - """ - authuser = REQUEST.AUTHENTICATED_USER - viewable = int(viewable) - return scu.sendResult( - REQUEST, - [ - d.id - for d in self._list_depts() - if (not viewable) - or authuser.has_permission(Permission.ScoView, d.Scolarite) - ], - name="depts", - format=format, - ) - - def _list_depts(self, REQUEST=None): # not published - # List departments folders - # (returns a list of Zope folders containing a ZScolar instance) - folders = self.objectValues("Folder") - # select folders with Scolarite object: - r = [] - for folder in folders: - try: - _ = folder.Scolarite - r.append(folder) - except: - pass - return r - - security.declareProtected("View", "create_dept") - - def create_dept(self, REQUEST=None, DeptId="", pass2=False): - """Creation (ajout) d'un site departement - (instance ZScolar + dossier la contenant) - """ - e = self._check_admin_perm(REQUEST) - if e: - return e - - if not DeptId: - raise ValueError("nom de departement invalide") - if not pass2: - # 1- Creation de repertoire Dept - log("creating Zope folder " + DeptId) - add_method = self.manage_addProduct["OFSP"].manage_addFolder - add_method(DeptId, title="Site dept. " + DeptId) - - DeptFolder = self[DeptId] - - if not pass2: - # 2- Creation du repertoire Fotos - log("creating Zope folder %s/Fotos" % DeptId) - add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder - add_method("Fotos", title="Photos identites " + DeptId) - - # 3- Creation instance ScoDoc - log("creating Zope ZScolar instance") - add_method = DeptFolder.manage_addProduct["ScoDoc"].manage_addZScolarForm - return add_method(DeptId, REQUEST=REQUEST) - - security.declareProtected("View", "delete_dept") - - def delete_dept(self, REQUEST=None, DeptId="", force=False): - """Supprime un departement (de Zope seulement, ne touche pas la BD)""" - e = self._check_admin_perm(REQUEST) - if e: - return e - - if not force and DeptId not in [x.id for x in self._list_depts()]: - raise ValueError("nom de departement invalide") - - self.manage_delObjects(ids=[DeptId]) - - return ( - "

Département " - + DeptId - + """ supprimé du serveur web (la base de données n'est pas affectée)!

Continuer

""" - ) - - security.declareProtected("View", "index_html") - - def index_html(self, REQUEST=None, message=None): - """Top level page for ScoDoc""" - authuser = REQUEST.AUTHENTICATED_USER - deptList = self._list_depts() - self._fix_users_folder() # fix our exUserFolder - isAdmin = not self._check_admin_perm(REQUEST) - try: - admin_password_initialized = self.admin_password_initialized - except: - admin_password_initialized = "0" - if isAdmin and admin_password_initialized != "1": - return REQUEST.RESPONSE.redirect( - "ScoDoc/change_admin_user_form?message=Le%20mot%20de%20passe%20administrateur%20doit%20etre%20change%20!" - ) - - # Si l'URL indique que l'on est dans un folder, affiche page login du departement - try: - deptfoldername = REQUEST.URL0.split("ScoDoc")[1].split("/")[1] - if deptfoldername in [x.id for x in self._list_depts()]: - return self.index_dept(deptfoldername=deptfoldername, REQUEST=REQUEST) - except: - pass - - H = [ - html_sco_header.scodoc_top_html_header( - self, REQUEST, page_title="ScoDoc: bienvenue" - ), - self._check_users_folder(REQUEST=REQUEST), # ensure setup is done - ] - if message: - H.append('
%s
' % message) - - if isAdmin and not message: - H.append('
Attention: connecté comme administrateur
') - - H.append( - """ -
-

ScoDoc: gestion scolarité

- """ - ) - if authuser.has_role("Authenticated"): - H.append( - """

Bonjour %s.

""" % str(authuser) - ) - H.append( - """

N'oubliez pas de vous déconnecter après usage.

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

Ce site est réservé au personnel autorisé

""" - ) - H.append(self.authentication_form(destination=".")) - - if not deptList: - H.append("aucun département existant !") - # si pas de dept et pas admin, propose lien pour loger admin - if not isAdmin: - H.append( - """

Identifiez vous comme administrateur (au début: nom 'admin', mot de passe 'scodoc')

""" - ) - else: - H.append('
    ') - if isAdmin: - dest_folder = "/Scolarite" - else: - dest_folder = "" - for deptFolder in self._list_depts(): - if authuser.has_permission(Permission.ScoView, deptFolder.Scolarite): - link_cls = "link_accessible" - else: - link_cls = "link_unauthorized" - # Essai de recuperer le nom du departement dans ses preferences - try: - DeptName = ( - deptFolder.Scolarite.get_preference("DeptName") or deptFolder.id - ) - except: - DeptName = deptFolder.id - H.append( - '
  • Département %s' - % (link_cls, deptFolder.absolute_url(), dest_folder, DeptName) - ) - # check if roles are initialized in this depts, and do it if necessary - if deptFolder.Scolarite.roles_initialized == "0": - if isAdmin: - deptFolder.Scolarite._setup_initial_roles_and_permissions() - else: - H.append(" (non initialisé, connectez vous comme admin)") - H.append("
  • ") - H.append("
") - # Recherche etudiant - H.append(sco_find_etud.form_search_etud_in_accessible_depts(self, REQUEST)) - - if isAdmin: - H.append('

Administration de ScoDoc

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

Se connecter comme administrateur

' - % REQUEST.BASE0 - ) - - # Lien expérimental temporaire: - H.append( - '

Version mobile (expérimentale, à vos risques et périls)

' - ) - - H.append( - """ -
-

ScoDoc est un logiciel libre de suivi de la scolarité des étudiants conçu par -E. Viennet (Université Paris 13).

-
-
""" - % (scu.SCO_WEBSITE,) - ) - - H.append("""""") - return "\n".join(H) - - def authentication_form(self, destination=""): - """html snippet for authentication""" - return ( - """ - - -

- - - - - - - - - -
Nom:
Mot de passe:
-

-
""" - % destination - ) - - security.declareProtected("View", "index_dept") - - def index_dept(self, deptfoldername="", REQUEST=None): - """Page d'accueil departement""" - authuser = REQUEST.AUTHENTICATED_USER - try: - dept = getattr(self, deptfoldername) - if authuser.has_permission(Permission.ScoView, dept): - return REQUEST.RESPONSE.redirect("ScoDoc/%s/Scolarite" % deptfoldername) - except: - log( - "*** problem in index_dept (%s) user=%s" - % (deptfoldername, str(authuser)) - ) - - H = [ - html_sco_header.standard_html_header(), - """
-

Scolarité du département %s

""" - % deptfoldername, - """

Ce site est -réservé au personnel du département. -

""", - self.authentication_form(destination="Scolarite"), - """ -

Pour quitter, logout

-

Retour à l'accueil

-
-""" - % self.ScoDocURL(), - html_sco_header.standard_html_footer(), - ] - return "\n".join(H) - - security.declareProtected("View", "doLogin") - - def doLogin(self, REQUEST=None, destination=None): - "redirect to destination after login" - if destination: - return REQUEST.RESPONSE.redirect(destination) - - security.declareProtected("View", "docLogin") - docLogin = DTMLFile("dtml/docLogin", globals()) - security.declareProtected("View", "docLogout") - docLogout = DTMLFile("dtml/docLogout", globals()) - - security.declareProtected("View", "query_string_to_form_inputs") - - def query_string_to_form_inputs(self, query_string=""): - """Return html snippet representing the query string as POST form hidden inputs. - This is useful in conjonction with exUserfolder to correctly redirect the response - after authentication. - """ - H = [] - for a in query_string.split("&"): - if a: - nv = a.split("=") - if len(nv) == 2: - name, value = nv - H.append( - '' - ) - - return "\n" + "\n".join(H) - - security.declareProtected("View", "standard_error_message") - - def standard_error_message( - self, - error_value=None, - error_message=None, # unused ? - error_type=None, - error_traceback=None, - error_tb=None, - **kv - ): - "Recuperation des exceptions Zope" - # neat (or should I say dirty ?) hack to get REQUEST - # in fact, our caller (probably SimpleItem.py) has the REQUEST variable - # that we'd like to use for our logs, but does not pass it as an argument. - try: - frame = inspect.currentframe() - REQUEST = frame.f_back.f_locals["REQUEST"] - except: - REQUEST = {} - - # Authentication uses exceptions, pass them up - HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") - if error_type == "LoginRequired": - log("LoginRequired from %s" % HTTP_X_FORWARDED_FOR) - self.login_page = error_value - return error_value - elif error_type == "Unauthorized": - log("Unauthorized from %s" % HTTP_X_FORWARDED_FOR) - return self.acl_users.docLogin(self, REQUEST=REQUEST) - - log("exception caught: %s" % error_type) - log(traceback.format_exc()) - - params = { - "error_type": error_type, - "error_value": error_value, - "error_tb": error_tb, - "sco_exc_mail": scu.SCO_EXC_MAIL, - "sco_dev_mail": scu.SCO_DEV_MAIL, - } - - if error_type == "ScoGenError": - return "

" + str(error_value) + "

" - elif error_type in ("ScoValueError", "FormatError"): - # Not a bug, presents a gentle message to the user: - H = [ - html_sco_header.standard_html_header(), - """

Erreur !

%s

""" % error_value, - ] - if error_value.dest_url: - H.append('

Continuer

' % error_value.dest_url) - H.append(html_sco_header.standard_html_footer()) - return "\n".join(H) - else: # Other exceptions, try carefully to build an error page... - # log('exc A') - H = [] - try: - H.append(html_sco_header.standard_html_header()) - except: - pass - H.append( - """ - - -

Erreur !

-

Une erreur est survenue

-

- Error Type: %(error_type)s
- Error Value: %(error_value)s
-

-
-

L'URL est peut-etre incorrecte ?

- -

Si l'erreur persiste, contactez Emmanuel Viennet: - %(sco_dev_mail)s - en copiant ce message d'erreur et le contenu du cadre bleu ci-dessous si possible. -

-
""" - % params - ) - # display error traceback (? may open a security risk via xss attack ?) - params["txt_html"] = self._report_request(REQUEST, fmt="html") - H.append( - """

Zope Traceback (à envoyer par mail à %(sco_dev_mail)s)

-%(error_tb)s -

Informations:
-%(txt_html)s -

-
- -

Merci de votre patience !

-""" - % params - ) - try: - H.append(html_sco_header.standard_html_footer()) - except: - log("no footer found for error page") - pass - - # --- Mail: - params["error_traceback_txt"] = scu.scodoc_html2txt(error_tb) - txt = ( - """ -ErrorType: %(error_type)s - -%(error_traceback_txt)s -""" - % params - ) - - mails.send_debug_alert(context, txt, REQUEST=REQUEST) - # --- - log("done processing exception") - # log( '\n page=\n' + '\n'.join(H) ) - return "\n".join(H) - - security.declareProtected("View", "scodoc_admin") - - def scodoc_admin(self, REQUEST=None): - """Page Operations d'administration""" - e = self._check_admin_perm(REQUEST) - if e: - return e - - H = [ - html_sco_header.scodoc_top_html_header( - self, REQUEST, page_title="ScoDoc: bienvenue" - ), - """ -

Administration ScoDoc

- -

changer le mot de passe super-administrateur

-

retour à la page d'accueil

- -

Création d'un département

-

Le département doit avoir été créé au préalable sur le serveur en utilisant le script -create_dept.sh (à lancer comme root dans le répertoire config de ScoDoc). -

""" - % self.absolute_url(), - ] - - deptList = [x.id for x in self._list_depts()] # definis dans Zope - deptIds = set(self._list_depts_ids()) # definis sur le filesystem - existingDepts = set(deptList) - addableDepts = deptIds - existingDepts - - if not addableDepts: - # aucun departement defini: aide utilisateur - H.append("

Aucun département à ajouter !

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

Suppression d'un département

-

Ceci permet de supprimer le site web associé à un département, mais n'affecte pas la base de données -(le site peut donc être recréé sans perte de données). -

-
- - - -
""" - ) - - H.append("""""") - return "\n".join(H) - - def _list_depts_ids(self): - """Liste de id de departements definis par create_dept.sh - (fichiers depts/*.cfg) - """ - filenames = glob.glob(scu.SCODOC_VAR_DIR + "/config/depts/*.cfg") - ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames] - return ids - - security.declareProtected("View", "http_expiration_date") - - def http_expiration_date(self): - "http expiration date for cachable elements (css, ...)" - d = datetime.timedelta(minutes=10) - return (datetime.datetime.utcnow() + d).strftime("%a, %d %b %Y %H:%M:%S GMT") - - security.declareProtected("View", "get_etud_dept") - - def get_etud_dept(self, REQUEST=None): - """Returns the dept id (eg "GEII") of an etud (identified by etudid, INE or NIP in REQUEST). - Warning: This function is inefficient and its result should be cached. - """ - depts = self._list_depts() - depts_etud = [] # liste des depts où l'etud est defini - for dept in depts: - etuds = dept.Scolarite.getEtudInfo(REQUEST=REQUEST) - if etuds: - depts_etud.append((dept, etuds)) - if not depts_etud: - return "" # not found - elif len(depts_etud) == 1: - return depts_etud[0][0].id - # inscriptions dans plusieurs departements: cherche la plus recente - last_dept = None - last_date = None - for (dept, etuds) in depts_etud: - 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): - last_date = etud["sems"][0]["date_fin_iso"] - last_dept = dept - if not last_dept: - # est present dans plusieurs semestres mais inscrit dans aucun - return depts_etud[0][0] - return last_dept.id - - security.declareProtected("View", "table_etud_in_accessible_depts") - table_etud_in_accessible_depts = sco_find_etud.table_etud_in_accessible_depts - - security.declareProtected("View", "search_inscr_etud_by_nip") - search_inscr_etud_by_nip = sco_find_etud.search_inscr_etud_by_nip - - -def manage_addZScoDoc(self, id="ScoDoc", title="Site ScoDoc", REQUEST=None): - "Add a ZScoDoc instance to a folder." - log("============== creating a new ScoDoc instance =============") - zscodoc = ZScoDoc( - id, title - ) # ne cree (presque rien), tout se passe lors du 1er accès - self._setObject(id, zscodoc) - if REQUEST is not None: - REQUEST.RESPONSE.redirect("/ScoDoc/manage_workspace") - return id diff --git a/app/scodoc/debug.py b/app/scodoc/debug.py index 2f8d9949..2c529885 100644 --- a/app/scodoc/debug.py +++ b/app/scodoc/debug.py @@ -27,7 +27,7 @@ for sem in sems: print sem['formsemestre_id'], sem['titre_num'] # Recupere la table de notes: -nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_id) +nt = sco_core.get_notes_cache(context).get_NotesTable(context.Notes, formsemestre_id) """ diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 93586e70..c43f56f2 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -54,9 +54,10 @@ 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 app.scodoc.sco_utils as scu -import app.scodoc.sco_excel -import app.scodoc.sco_pdf +from app.scodoc import html_sco_header +from app.scodoc import sco_utils as scu +from app.scodoc import sco_excel +from app.scodoc import sco_pdf from app.scodoc.sco_pdf import SU from app.scodoc.notes_log import log diff --git a/app/scodoc/notes_log.py b/app/scodoc/notes_log.py index 1fdae938..abd8723e 100644 --- a/app/scodoc/notes_log.py +++ b/app/scodoc/notes_log.py @@ -100,7 +100,7 @@ def retreive_dept(): # Alarms by email: def sendAlarm(context, subj, txt): import sco_utils - import mails + import sco_emails import sco_preferences msg = MIMEMultipart() @@ -111,7 +111,7 @@ def sendAlarm(context, subj, txt): msg.epilogue = "" txt = MIMEText(txt, "plain", sco_utils.SCO_ENCODING) msg.attach(txt) - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) # Debug: log call stack diff --git a/app/scodoc/pe_jurype.py b/app/scodoc/pe_jurype.py index e9c60b16..f0270cea 100644 --- a/app/scodoc/pe_jurype.py +++ b/app/scodoc/pe_jurype.py @@ -57,7 +57,8 @@ import pprint from app.scodoc.gen_tables import GenTable, SeqGenTable import app.scodoc.sco_utils as scu from app.scodoc import sco_codes_parcours # sco_codes_parcours.NEXT -> sem suivant - +from app.scodoc import sco_core +from app.scodoc import sco_etud from app.scodoc import pe_tagtable from app.scodoc import pe_tools from app.scodoc import pe_semestretag @@ -331,7 +332,7 @@ class JuryPE: nt = self.get_cache_notes_d_un_semestre( self.context, sem["formsemestre_id"] ) - # context.Notes._getNotesCache().get_NotesTable(context.Notes, sem['formsemestre_id']) + # sco_core.get_notes_cache(context).get_NotesTable(context.Notes, sem['formsemestre_id']) etudiantsDuSemestre = ( nt.get_etudids() ) # nt.identdict.keys() # identification des etudiants du semestre @@ -1139,7 +1140,7 @@ class JuryPE: self, context, formsemestre_id ): # inutile en realité ! """Charge la table des notes d'un formsemestre""" - return context.Notes._getNotesCache().get_NotesTable( + return sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) diff --git a/app/scodoc/pe_semestretag.py b/app/scodoc/pe_semestretag.py index abbde8c5..13c36d21 100644 --- a/app/scodoc/pe_semestretag.py +++ b/app/scodoc/pe_semestretag.py @@ -38,6 +38,7 @@ Created on Fri Sep 9 09:15:05 2016 from app.scodoc.notes_log import log from app.scodoc import sco_codes_parcours +from app.scodoc import sco_core from app.scodoc import sco_tag_module from app.scodoc import pe_tagtable @@ -269,8 +270,8 @@ class SemestreTag(pe_tagtable.TableTag): # => le formsemestre_id du semestre dont vient la capitalisation fid_prec = fids_prec[0] # Lecture des notes de ce semestre - nt_prec = self.context.Notes._getNotesCache().get_NotesTable( - self.context.Notes, fid_prec + nt_prec = sco_core.get_notes_cache(self.context).get_NotesTable( + self.context, fid_prec ) # le tableau de note du semestre considéré # Y-a-t-il un module équivalent c'est à dire correspondant au même code (le module_id n'étant pas significatif en cas de changement de PPN) diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 1ab18dd7..520820f7 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -41,9 +41,10 @@ from app.scodoc import notesdb as ndb from app.scodoc.scolog import logdb from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidDateError from app.scodoc import sco_abs_notification +from app.scodoc import sco_core +from app.scodoc import sco_etud from app.scodoc import sco_formsemestre from app.scodoc import sco_preferences -from app.scodoc import sco_etud # --- Misc tools.... ------------------ @@ -968,7 +969,7 @@ class CAbsSemEtud: self.etudid = etudid self._loaded = False formsemestre_id = sem["formsemestre_id"] - context.Notes._getNotesCache().add_listener( + sco_core.get_notes_cache(context).add_listener( self.invalidate, formsemestre_id, (etudid, formsemestre_id) ) @@ -992,11 +993,11 @@ class CAbsSemEtud: debut_sem = ndb.DateDMYtoISO(self.sem["date_debut"]) fin_sem = ndb.DateDMYtoISO(self.sem["date_fin"]) - self._CountAbs = self.sco_abs.CountAbs( - context, etudid=self.etudid, debut=debut_sem, fin=fin_sem + self._CountAbs = CountAbs( + self.context, etudid=self.etudid, debut=debut_sem, fin=fin_sem ) - self._CountAbsJust = self.sco_abs.CountAbsJust( - context, etudid=self.etudid, debut=debut_sem, fin=fin_sem + self._CountAbsJust = CountAbsJust( + self.context, etudid=self.etudid, debut=debut_sem, fin=fin_sem ) self._loaded = True diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index 21465a70..1f498121 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -37,7 +37,7 @@ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header -from app.scodoc import mails +from app.scodoc import sco_emails import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log @@ -119,7 +119,7 @@ def abs_notify_send( for email in destinations: del msg["To"] msg["To"] = email - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) ndb.SimpleQuery( context, """insert into absences_notifications (etudid, email, nbabs, nbabsjust, formsemestre_id) values (%(etudid)s, %(email)s, %(nbabs)s, %(nbabsjust)s, %(formsemestre_id)s)""", diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index 8ef32ee9..654c9049 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -39,6 +39,7 @@ from app.scodoc.scolog import logdb from app.scodoc.gen_tables import GenTable from app.scodoc import html_sco_header from app.scodoc import sco_abs +from app.scodoc import sco_core from app.scodoc import sco_etud from app.scodoc import sco_find_etud from app.scodoc import sco_formsemestre @@ -124,7 +125,7 @@ def doSignaleAbsence( if moduleimpl_id and moduleimpl_id != "NULL": mod = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] formsemestre_id = mod["formsemestre_id"] - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) ues = nt.get_ues(etudid=etudid) @@ -185,7 +186,7 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied require_module = sco_preferences.get_preference( context, "abs_require_module", formsemestre_id ) - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) ues = nt.get_ues(etudid=etudid) diff --git a/app/scodoc/sco_archives_etud.py b/app/scodoc/sco_archives_etud.py index ee495d48..4671176b 100644 --- a/app/scodoc/sco_archives_etud.py +++ b/app/scodoc/sco_archives_etud.py @@ -31,7 +31,7 @@ """ import app.scodoc.sco_utils as scu -from app.scodoc import ImportScolars +from app.scodoc import sco_import_etuds from app.scodoc import sco_groups from app.scodoc import sco_trombino from app.scodoc import sco_excel @@ -227,8 +227,8 @@ def etud_get_archived_file(context, REQUEST, etudid, archive_name, filename): # --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants) def etudarchive_generate_excel_sample(context, group_id=None, REQUEST=None): """Feuille excel pour import fichiers etudiants (utilisé pour admissions)""" - fmt = ImportScolars.sco_import_format() - data = ImportScolars.sco_import_generate_excel_sample( + fmt = sco_import_etuds.sco_import_format() + data = sco_import_etuds.sco_import_generate_excel_sample( fmt, context=context, group_ids=[group_id], diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index bf6e5f35..f1175fb4 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -41,13 +41,14 @@ from email.header import Header from reportlab.lib.colors import Color -from app.scodoc import mails +from app.scodoc import sco_emails import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import AccessDenied from app.scodoc import html_sco_header +from app.scodoc import sco_abs from app.scodoc import sco_abs_views from app.scodoc import sco_bulletins_generator from app.scodoc import sco_bulletins_json @@ -1048,7 +1049,7 @@ def mail_bulletin(context, formsemestre_id, I, pdfdata, filename, recipient_addr email.encoders.encode_base64(att) msg.attach(att) log("mail bulletin a %s" % msg["To"]) - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) def _formsemestre_bulletinetud_header_html( diff --git a/app/scodoc/sco_compute_moy.py b/app/scodoc/sco_compute_moy.py index 42e1b30a..f0d0e3ae 100644 --- a/app/scodoc/sco_compute_moy.py +++ b/app/scodoc/sco_compute_moy.py @@ -47,6 +47,7 @@ from app.scodoc import sco_abs from app.scodoc import sco_edit_module from app.scodoc import sco_evaluations from app.scodoc import sco_formsemestre +from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formulas from app.scodoc import sco_moduleimpl from app.scodoc import sco_etud @@ -211,8 +212,8 @@ def do_moduleimpl_moyennes(context, nt, mod): inssem_set = set( [ x["etudid"] - for x in context.do_formsemestre_inscription_listinscrits( - mod["formsemestre_id"] + for x in sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( + context, mod["formsemestre_id"] ) ] ) diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index 3b43d53e..2231a14c 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -254,7 +254,7 @@ def formation_edit(context, formation_id=None, create=False, REQUEST=None): ) # if create: - formation_id = context.do_formation_create(tf[2], REQUEST) + formation_id = do_formation_create(context, tf[2], REQUEST) else: do_formation_edit(context, tf[2]) return REQUEST.RESPONSE.redirect("ue_list?formation_id=%s" % formation_id) diff --git a/app/scodoc/mails.py b/app/scodoc/sco_emails.py similarity index 99% rename from app/scodoc/mails.py rename to app/scodoc/sco_emails.py index b13199c5..681d8218 100644 --- a/app/scodoc/mails.py +++ b/app/scodoc/sco_emails.py @@ -41,6 +41,7 @@ from email import Encoders # pylint: disable=no-name-in-module,import-error import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log +from app.scodoc import VERSION def sendEmail(context, msg): # TODO A REECRIRE ScoDoc8 diff --git a/app/scodoc/sco_etape_apogee.py b/app/scodoc/sco_etape_apogee.py index a18ba57e..89b189ac 100644 --- a/app/scodoc/sco_etape_apogee.py +++ b/app/scodoc/sco_etape_apogee.py @@ -389,7 +389,7 @@ apo_csv_store(context, csv_data, annee_scolaire, sem_id) groups_infos = sco_groups_view.DisplayedGroupsInfos(context, [sco_groups.get_default_group(context, formsemestre_id)], formsemestre_id=formsemestre_id, REQUEST=REQUEST) -nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_id) +nt = sco_core.get_notes_cache(context).get_NotesTable(context.Notes, formsemestre_id) # s = SemSet(context, 'NSS29902') diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index e3d051d5..931c3fc3 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -37,7 +37,7 @@ from email.mime.text import MIMEText from email.header import Header from email.mime.base import MIMEBase -from app.scodoc import mails +from app.scodoc import sco_emails import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import SCO_ENCODING import app.scodoc.notesdb as ndb @@ -46,7 +46,6 @@ from app.scodoc.sco_exceptions import ScoGenError, ScoValueError from app.scodoc.notes_log import log from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc import safehtml -from app.scodoc import sco_formsemestre from app.scodoc import sco_preferences from app.scodoc.scolog import logdb @@ -450,7 +449,7 @@ def notify_etud_change(context, email_addr, etud, before, after, subject): msg["To"] = email_addr mime_txt = MIMEText(txt, "plain", SCO_ENCODING) msg.attach(mime_txt) - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) return txt @@ -901,6 +900,7 @@ def fill_etuds_info(etuds): Pour chaque etudiant, ajoute ou formatte les champs -> informations pour fiche etudiant ou listes diverses """ + from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_inscriptions context = None # XXX en attendant la suppression du context ScoDoc7 @@ -1004,6 +1004,8 @@ def fill_etuds_info(etuds): def descr_situation_etud(context, etudid, ne=""): """chaine decrivant la situation actuelle de l'etudiant""" + from app.scodoc import sco_formsemestre + cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index bbe98c2c..b275e9f6 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -45,6 +45,7 @@ from app.scodoc import sco_core from app.scodoc import sco_edit_module from app.scodoc import sco_edit_ue from app.scodoc import sco_formsemestre +from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_groups from app.scodoc import sco_moduleimpl from app.scodoc import sco_news @@ -180,6 +181,146 @@ def do_evaluation_list_in_formsemestre(context, formsemestre_id): return evals +def _check_evaluation_args(context, args): + "Check coefficient, dates and duration, raises exception if invalid" + moduleimpl_id = args["moduleimpl_id"] + # check bareme + note_max = args.get("note_max", None) + if note_max is None: + raise ScoValueError("missing note_max") + try: + note_max = float(note_max) + except ValueError: + raise ScoValueError("Invalid note_max value") + if note_max < 0: + raise ScoValueError("Invalid note_max value (must be positive or null)") + # check coefficient + coef = args.get("coefficient", None) + if coef is None: + raise ScoValueError("missing coefficient") + try: + coef = float(coef) + except ValueError: + raise ScoValueError("Invalid coefficient value") + if coef < 0: + raise ScoValueError("Invalid coefficient value (must be positive or null)") + # check date + jour = args.get("jour", None) + args["jour"] = jour + if jour: + M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) + d, m, y = [int(x) for x in sem["date_debut"].split("/")] + date_debut = datetime.date(y, m, d) + d, m, y = [int(x) for x in sem["date_fin"].split("/")] + date_fin = datetime.date(y, m, d) + # passe par ndb.DateDMYtoISO pour avoir date pivot + y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")] + jour = datetime.date(y, m, d) + if (jour > date_fin) or (jour < date_debut): + raise ScoValueError( + "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !" + % (d, m, y) + ) + heure_debut = args.get("heure_debut", None) + args["heure_debut"] = heure_debut + heure_fin = args.get("heure_fin", None) + args["heure_fin"] = heure_fin + if jour and ((not heure_debut) or (not heure_fin)): + raise ScoValueError("Les heures doivent être précisées") + d = ndb.TimeDuration(heure_debut, heure_fin) + if d and ((d < 0) or (d > 60 * 12)): + raise ScoValueError("Heures de l'évaluation incohérentes !") + + +def do_evaluation_create( + context, + moduleimpl_id=None, + jour=None, + heure_debut=None, + heure_fin=None, + description=None, + note_max=None, + coefficient=None, + visibulletin=None, + publish_incomplete=None, + evaluation_type=None, + numero=None, + REQUEST=None, +): + """Create an evaluation""" + if not sco_permissions_check.can_edit_evaluation( + context, REQUEST, moduleimpl_id=moduleimpl_id + ): + raise AccessDenied( + "Modification évaluation impossible pour %s" + % scu.get_current_user_name(REQUEST) + ) + args = locals() + log("do_evaluation_create: args=" + str(args)) + _check_evaluation_args(context, args) + # Check numeros + module_evaluation_renumber( + context, moduleimpl_id, REQUEST=REQUEST, only_if_unumbered=True + ) + if not "numero" in args or args["numero"] is None: + n = None + # determine le numero avec la date + # Liste des eval existantes triees par date, la plus ancienne en tete + ModEvals = do_evaluation_list( + context, + args={"moduleimpl_id": moduleimpl_id}, + sortkey="jour asc, heure_debut asc", + ) + if args["jour"]: + next_eval = None + t = ( + ndb.DateDMYtoISO(args["jour"]), + ndb.TimetoISO8601(args["heure_debut"]), + ) + for e in ModEvals: + if ( + ndb.DateDMYtoISO(e["jour"]), + ndb.TimetoISO8601(e["heure_debut"]), + ) > t: + next_eval = e + break + if next_eval: + n = module_evaluation_insert_before( + context, ModEvals, next_eval, REQUEST + ) + else: + n = None # a placer en fin + if n is None: # pas de date ou en fin: + if ModEvals: + log(pprint.pformat(ModEvals[-1])) + n = ModEvals[-1]["numero"] + 1 + else: + n = 0 # the only one + # log("creating with numero n=%d" % n) + args["numero"] = n + + # + cnx = ndb.GetDBConnexion() + r = _evaluationEditor.create(cnx, args) + + # news + M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] + mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0] + mod["moduleimpl_id"] = M["moduleimpl_id"] + mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod + sco_news.add( + context, + REQUEST, + typ=sco_news.NEWS_NOTE, + object=moduleimpl_id, + text='Création d\'une évaluation dans %(titre)s' % mod, + url=mod["url"], + ) + + return r + + def do_evaluation_edit(context, REQUEST, args): "edit an evaluation" evaluation_id = args["evaluation_id"] @@ -187,9 +328,16 @@ def do_evaluation_edit(context, REQUEST, args): if not the_evals: raise ValueError("evaluation inexistante !") moduleimpl_id = the_evals[0]["moduleimpl_id"] + if not sco_permissions_check.can_edit_evaluation( + context, REQUEST, moduleimpl_id=moduleimpl_id + ): + raise AccessDenied( + "Modification évaluation impossible pour %s" + % scu.get_current_user_name(REQUEST) + ) args["moduleimpl_id"] = moduleimpl_id - context._check_evaluation_args(args) - context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) + _check_evaluation_args(context, args) + cnx = ndb.GetDBConnexion() context._evaluationEditor.edit(cnx, args) # inval cache pour ce semestre @@ -204,7 +352,14 @@ def do_evaluation_delete(context, REQUEST, evaluation_id): the_evals = do_evaluation_list(context, {"evaluation_id": evaluation_id}) if not the_evals: raise ValueError("evaluation inexistante !") - + moduleimpl_id = the_evals[0]["moduleimpl_id"] + if not sco_permissions_check.can_edit_evaluation( + context, REQUEST, moduleimpl_id=moduleimpl_id + ): + raise AccessDenied( + "Modification évaluation impossible pour %s" + % scu.get_current_user_name(REQUEST) + ) NotesDB = do_evaluation_get_all_notes(context, evaluation_id) # { etudid : value } notes = [x["value"] for x in NotesDB.values()] if notes: @@ -212,8 +367,6 @@ def do_evaluation_delete(context, REQUEST, evaluation_id): "Impossible de supprimer cette évaluation: il reste des notes" ) - moduleimpl_id = the_evals[0]["moduleimpl_id"] - context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) cnx = ndb.GetDBConnexion() context._evaluationEditor.delete(cnx, evaluation_id) @@ -295,7 +448,9 @@ def do_evaluation_etat( # Il faut considerer les inscriptions au semestre # (pour avoir l'etat et le groupe) et aussi les inscriptions # au module (pour gerer les modules optionnels correctement) - insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id) + insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( + context, formsemestre_id + ) insmod = sco_moduleimpl.do_moduleimpl_inscription_list( context, moduleimpl_id=E["moduleimpl_id"] ) @@ -676,7 +831,7 @@ def evaluation_date_first_completion(context, evaluation_id): # E = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0] # M = sco_moduleimpl.do_moduleimpl_list(context,moduleimpl_id=E["moduleimpl_id"])[0] # formsemestre_id = M["formsemestre_id"] - # insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id) + # insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(context, formsemestre_id) # insmod = sco_moduleimpl.do_moduleimpl_inscription_list(context,moduleimpl_id=E["moduleimpl_id"]) # insmodset = set([x["etudid"] for x in insmod]) # retire de insem ceux qui ne sont pas inscrits au module @@ -814,9 +969,14 @@ def module_evaluation_move(context, evaluation_id, after=0, REQUEST=None, redire """ e = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0] redirect = int(redirect) - - # access: can change eval ? (raises exception) - context._evaluation_check_write_access(REQUEST, moduleimpl_id=e["moduleimpl_id"]) + # access: can change eval ? + if not sco_permissions_check.can_edit_evaluation( + context, REQUEST, moduleimpl_id=e["moduleimpl_id"] + ): + raise AccessDenied( + "Modification évaluation impossible pour %s" + % scu.get_current_user_name(REQUEST) + ) module_evaluation_renumber( context, e["moduleimpl_id"], REQUEST=REQUEST, only_if_unumbered=True @@ -977,13 +1137,14 @@ def evaluation_create_form( formsemestre_id = M["formsemestre_id"] min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible if not readonly: - try: - context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) - except AccessDenied as e: + if not sco_permissions_check.can_edit_evaluation( + context, REQUEST, moduleimpl_id=moduleimpl_id + ): return ( html_sco_header.sco_header(context, REQUEST) + "

Opération non autorisée

" - + str(e) + + "Modification évaluation impossible pour %s" + % scu.get_current_user_name(REQUEST) + "

" + '

Revenir

' % (str(REQUEST.HTTP_REFERER),) + html_sco_header.sco_footer(context, REQUEST) @@ -1227,7 +1388,7 @@ def evaluation_create_form( tf[2]["visibulletin"] = 0 if not edit: # creation d'une evaluation - evaluation_id = context.do_evaluation_create(REQUEST=REQUEST, **tf[2]) + evaluation_id = do_evaluation_create(context, REQUEST=REQUEST, **tf[2]) return REQUEST.RESPONSE.redirect(dest_url) else: do_evaluation_edit(context, REQUEST, tf[2]) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index a3d6bcbf..a2fb9cc6 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -38,6 +38,7 @@ from app.scodoc import notesdb from app.scodoc.notes_log import log from app.scodoc.scolog import logdb from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc import sco_preferences # colors, voir exemple format.py diff --git a/app/scodoc/sco_find_etud.py b/app/scodoc/sco_find_etud.py index d5994b5a..b5f6bf1c 100644 --- a/app/scodoc/sco_find_etud.py +++ b/app/scodoc/sco_find_etud.py @@ -28,6 +28,7 @@ """Recherche d'étudiants """ +from scodoc_manager import sco_mgr import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.gen_tables import GenTable @@ -266,12 +267,6 @@ def form_search_etud_in_accessible_depts(context, REQUEST): """ -def can_view_dept(context, REQUEST): - """True if auth user can access (View) this context""" - authuser = REQUEST.AUTHENTICATED_USER - return authuser.has_permission(Permission.ScoView) - - def search_etud_in_accessible_depts(context, expnom=None, code_nip=None, REQUEST=None): """ context est le ZScoDoc @@ -279,10 +274,10 @@ def search_etud_in_accessible_depts(context, expnom=None, code_nip=None, REQUEST """ result = [] accessible_depts = [] - deptList = context._list_depts() # definis dans Zope - for dept in deptList: + dept_list = sco_mgr.get_dept_ids() + for dept in dept_list: # log('%s searching %s' % (str(REQUEST.AUTHENTICATED_USER),dept)) - if can_view_dept(dept, REQUEST): + if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoView, dept=dept): if expnom or code_nip: accessible_depts.append(dept.Scolarite.DeptId()) etuds = search_etuds_infos( diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 878cede7..e4ac0acb 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -35,16 +35,17 @@ import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log from app.scodoc import sco_codes_parcours -from app.scodoc import sco_formsemestre -from app.scodoc import sco_tag_module -from app.scodoc import sco_preferences -from app.scodoc.gen_tables import GenTable -from app.scodoc.sco_exceptions import ScoValueError -from app.scodoc.sco_permissions import Permission -from app.scodoc import VERSION 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_formsemestre +from app.scodoc import sco_news +from app.scodoc import sco_preferences +from app.scodoc import sco_tag_module +from app.scodoc import VERSION +from app.scodoc.gen_tables import GenTable +from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc.sco_permissions import Permission _formationEditor = ndb.EditableTable( "notes_formations", @@ -164,6 +165,8 @@ def formation_import_xml( """Create a formation from XML representation (format dumped by formation_export( format='xml' )) """ + from app.scodoc import sco_edit_formation + log("formation_import_xml: doc=%s" % doc) try: dom = xml.dom.minidom.parseString(doc) @@ -199,7 +202,7 @@ def formation_import_xml( # create formation # F_unquoted = F.copy() # unescape_html_dict(F_unquoted) - formation_id = context.do_formation_create(F, REQUEST) + formation_id = sco_edit_formation.do_formation_create(context, F, REQUEST) log("formation %s created" % formation_id) ues_old2new = {} # xml ue_id : new ue_id modules_old2new = {} # xml module_id : new module_id @@ -362,3 +365,24 @@ def formation_list_table(context, formation_id=None, args={}, REQUEST=None): context, ), ) + + +def formation_create_new_version(context, formation_id, redirect=True, REQUEST=None): + "duplicate formation, with new version number" + xml = formation_export(context, formation_id, export_ids=True, format="xml") + new_id, modules_old2new, ues_old2new = formation_import_xml(context, REQUEST, xml) + # news + F = formation_list(context, args={"formation_id": new_id})[0] + sco_news.add( + context, + REQUEST, + typ=sco_news.NEWS_FORM, + object=new_id, + text="Nouvelle version de la formation %(acronyme)s" % F, + ) + if redirect: + return REQUEST.RESPONSE.redirect( + "ue_list?formation_id=" + new_id + "&msg=Nouvelle version !" + ) + else: + return new_id, modules_old2new, ues_old2new \ No newline at end of file diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 84264eed..c2a89b73 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -29,16 +29,16 @@ """ import time -import app.scodoc.sco_utils as scu -import app.scodoc.notesdb as ndb -from app.scodoc.notes_log import log -from app.scodoc.gen_tables import GenTable - +from scodoc_manager import sco_mgr from app.scodoc import sco_codes_parcours -from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID from app.scodoc import sco_core from app.scodoc import sco_preferences +from app.scodoc.gen_tables import GenTable +from app.scodoc.notes_log import log +from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID from app.scodoc.sco_exceptions import ScoValueError +import app.scodoc.notesdb as ndb +import app.scodoc.sco_utils as scu _formsemestreEditor = ndb.EditableTable( "notes_formsemestre", @@ -219,6 +219,41 @@ def etapes_apo_str(etapes): return ", ".join([str(x) for x in etapes]) +def do_formsemestre_create(context, args, REQUEST, silent=False): + "create a formsemestre" + cnx = ndb.GetDBConnexion() + formsemestre_id = _formsemestreEditor.create(cnx, args) + if args["etapes"]: + args["formsemestre_id"] = formsemestre_id + write_formsemestre_etapes(context, args) + if args["responsables"]: + args["formsemestre_id"] = formsemestre_id + write_formsemestre_responsables(context, args) + + # create default partition + partition_id = sco_groups.partition_create( + context, formsemestre_id, default=True, redirect=0, REQUEST=REQUEST + ) + _group_id = sco_groups.createGroup( + context, partition_id, default=True, REQUEST=REQUEST + ) + + # news + if not args.has_key("titre"): + args["titre"] = "sans titre" + args["formsemestre_id"] = formsemestre_id + args["url"] = "Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % args + if not silent: + sco_news.add( + context, + REQUEST, + typ=sco_news.NEWS_SEM, + text='Création du semestre %(titre)s' % args, + url=args["url"], + ) + return formsemestre_id + + def do_formsemestre_edit(context, sem, cnx=None, **kw): """Apply modifications to formsemestre. Update etapes and resps. Invalidate cache.""" @@ -500,7 +535,7 @@ def sem_est_courant(context, sem): def scodoc_get_all_unlocked_sems(context): """Liste de tous les semestres non verrouillés de tous les départements""" - depts = context._list_depts() + depts = sco_mgr.get_dept_ids() semdepts = [] for dept in depts: semdepts += [ diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 4e3cb6d2..3cec2e1e 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -715,7 +715,9 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False): ) if not edit: # creation du semestre - formsemestre_id = context.do_formsemestre_create(tf[2], REQUEST) + formsemestre_id = sco_formsemestre.do_formsemestre_create( + context, tf[2], REQUEST + ) # creation des modules for module_id in tf[2]["tf-checked"]: modargs = { @@ -1014,7 +1016,7 @@ def do_formsemestre_clone( args["date_debut"] = date_debut args["date_fin"] = date_fin args["etat"] = 1 # non verrouillé - formsemestre_id = context.do_formsemestre_create(args, REQUEST) + formsemestre_id = sco_formsemestre.do_formsemestre_create(context, args, REQUEST) log("created formsemestre %s" % formsemestre_id) # 2- create moduleimpls mods_orig = sco_moduleimpl.do_moduleimpl_list( @@ -1041,7 +1043,7 @@ def do_formsemestre_clone( args = e.copy() del args["jour"] # erase date args["moduleimpl_id"] = mid - _ = context.do_evaluation_create(REQUEST=REQUEST, **args) + _ = sco_evaluations.do_evaluation_create(context, REQUEST=REQUEST, **args) # 3- copy uecoefs objs = sco_formsemestre.formsemestre_uecoef_list( @@ -1196,14 +1198,18 @@ def do_formsemestres_associate_new_version(context, formsemestre_ids, REQUEST=No cnx = ndb.GetDBConnexion() # New formation: - formation_id, modules_old2new, ues_old2new = context.formation_create_new_version( - formation_id, redirect=False, REQUEST=REQUEST + ( + formation_id, + modules_old2new, + ues_old2new, + ) = sco_formations.formation_create_new_version( + context, formation_id, redirect=False, REQUEST=REQUEST ) for formsemestre_id in formsemestre_ids: sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) sem["formation_id"] = formation_id - context.do_formsemestre_edit(sem, cnx=cnx, html_quote=False) + sco_formsemestre.do_formsemestre_edit(context, sem, cnx=cnx, html_quote=False) _reassociate_moduleimpls( context, cnx, formsemestre_id, ues_old2new, modules_old2new ) @@ -1339,7 +1345,7 @@ def do_formsemestre_delete(context, formsemestre_id, REQUEST): sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # --- Destruction des modules de ce semestre - mods = context.do_moduleimpl_list(formsemestre_id=formsemestre_id) + mods = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) for mod in mods: # evaluations evals = sco_evaluations.do_evaluation_list( @@ -1474,7 +1480,7 @@ def formsemestre_change_lock( if etat not in (0, 1): raise ScoValueError("formsemestre_lock: invalid value for etat (%s)" % etat) args = {"formsemestre_id": formsemestre_id, "etat": etat} - context.do_formsemestre_edit(args) + sco_formsemestre.do_formsemestre_edit(context, args) if REQUEST: return REQUEST.RESPONSE.redirect( "formsemestre_status?formsemestre_id=%s" % formsemestre_id @@ -1517,7 +1523,7 @@ def formsemestre_change_publication_bul( "formsemestre_change_publication_bul: invalid value for etat (%s)" % etat ) args = {"formsemestre_id": formsemestre_id, "bul_hide_xml": etat} - context.do_formsemestre_edit(args) + sco_formsemestre.do_formsemestre_edit(context, args) if REQUEST: return REQUEST.RESPONSE.redirect( "formsemestre_status?formsemestre_id=%s" % formsemestre_id diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py index 1d09a858..ee93d8a4 100644 --- a/app/scodoc/sco_formsemestre_exterieurs.py +++ b/app/scodoc/sco_formsemestre_exterieurs.py @@ -63,7 +63,9 @@ def formsemestre_ext_create(context, etudid, sem_params, REQUEST=None): sem_params["modalite"] = "EXT" sem_params["etapes"] = None sem_params["responsables"] = [str(REQUEST.AUTHENTICATED_USER)] - formsemestre_id = context.do_formsemestre_create(sem_params, REQUEST, silent=True) + formsemestre_id = sco_formsemestre.do_formsemestre_create( + context, sem_params, REQUEST, silent=True + ) # nota: le semestre est créé vide: pas de modules # Inscription au semestre diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index 3a45091c..19d36d40 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -43,7 +43,6 @@ from app.scodoc import sco_moduleimpl from app.scodoc import sco_groups from app.scodoc import sco_etud from app.scodoc import sco_core -from app.scodoc import sco_formsemestre_edit from app.scodoc import html_sco_header @@ -62,6 +61,19 @@ def do_formsemestre_inscription_list(context, *args, **kw): return _formsemestre_inscriptionEditor.list(cnx, *args, **kw) +def do_formsemestre_inscription_listinscrits(context, formsemestre_id): + """Liste les inscrits (état I) à ce semestre et cache le résultat""" + cache = sco_core.get_formsemestre_inscription_cache(context) + r = cache.get(formsemestre_id) + if r is None: + # retreive list + r = do_formsemestre_inscription_list( + context, args={"formsemestre_id": formsemestre_id, "etat": "I"} + ) + cache.set(formsemestre_id, r) + return r + + def do_formsemestre_inscription_create(context, args, REQUEST, method=None): "create a formsemestre_inscription (and sco event)" cnx = ndb.GetDBConnexion() @@ -126,6 +138,8 @@ def do_formsemestre_desinscription(context, etudid, formsemestre_id, REQUEST=Non """Désinscription d'un étudiant. Si semestre extérieur et dernier inscrit, suppression de ce semestre. """ + from app.scodoc import sco_formsemestre_edit + sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # -- check lock if sem["etat"] != "1": diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index cb08b9bd..047c6786 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -346,7 +346,9 @@ def formsemestre_status_menubar(context, sem, REQUEST): "title": "Créer/modifier les partitions...", "endpoint": "scolar.editPartitionForm", "args": {"formsemestre_id": formsemestre_id}, - "enabled": sco_groups.can_change_groups(context, REQUEST, formsemestre_id), + "enabled": sco_groups.sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ), }, ] # 1 item / partition: @@ -355,7 +357,10 @@ def formsemestre_status_menubar(context, sem, REQUEST): ) submenu = [] enabled = ( - sco_groups.can_change_groups(context, REQUEST, formsemestre_id) and partitions + sco_groups.sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ) + and partitions ) for partition in partitions: submenu.append( @@ -873,13 +878,17 @@ def _make_listes_sem(context, sem, REQUEST=None, with_absences=True): H.append("") else: H.append('

Aucun groupe dans cette partition') - if sco_groups.can_change_groups(context, REQUEST, formsemestre_id): + if sco_groups.sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ): H.append( ' (créer)' % partition["partition_id"] ) H.append("

") - if sco_groups.can_change_groups(context, REQUEST, formsemestre_id): + if sco_groups.sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ): H.append( '

Ajouter une partition

' % formsemestre_id diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 657994b0..ea7be9bb 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -51,26 +51,12 @@ from app.scodoc import html_sco_header from app.scodoc import sco_codes_parcours from app.scodoc import sco_core from app.scodoc import sco_etud -from app.scodoc import sco_formsemestre +from app.scodoc import sco_permissions_check from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.TrivialFormulator import TrivialFormulator -def can_change_groups(context, REQUEST, formsemestre_id): - "Vrai si l'utilisateur peut changer les groupes dans ce semestre" - sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": - return False # semestre verrouillé - authuser = REQUEST.AUTHENTICATED_USER - if authuser.has_permission(Permission.ScoEtudChangeGroups): - return True # admin, chef dept - uid = str(authuser) - if uid in sem["responsables"]: - return True - return False - - def checkGroupName( groupName, ): # XXX unused: now allow any string as a group or partition name @@ -275,6 +261,8 @@ def get_group_members(context, group_id, etat=None): def get_group_infos(context, group_id, etat=None): # was _getlisteetud """legacy code: used by group_list and trombino""" + from app.scodoc import sco_formsemestre + cnx = ndb.GetDBConnexion() group = get_group(context, group_id) sem = sco_formsemestre.get_formsemestre(context, group["formsemestre_id"]) @@ -446,12 +434,14 @@ def XMLgetGroupsInPartition(context, partition_id, REQUEST=None): # was XMLgetG """ + from app.scodoc import sco_formsemestre + t0 = time.time() partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) groups = get_partition_groups(context, partition) - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) # > inscrdict etuds_set = set(nt.inscrdict) @@ -621,9 +611,11 @@ def setGroups( groupsToCreate: lignes "group_name;etudid;...\n" groupsToDelete: group_id;group_id;... """ + from app.scodoc import sco_formsemestre + partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") log("***setGroups: partition_id=%s" % partition_id) log("groupsLists=%s" % groupsLists) @@ -710,7 +702,9 @@ def createGroup(context, partition_id, group_name="", default=False, REQUEST=Non """ partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if REQUEST and not can_change_groups(context, REQUEST, formsemestre_id): + if REQUEST and not sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") # if group_name: @@ -747,7 +741,9 @@ def suppressGroup(context, group_id, partition_id=None, REQUEST=None): else: partition_id = group["partition_id"] partition = get_partition(context, partition_id) - if not can_change_groups(context, REQUEST, partition["formsemestre_id"]): + if not sco_permissions_check.can_change_groups( + context, REQUEST, partition["formsemestre_id"] + ): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") log( "suppressGroup: group_id=%s group_name=%s partition_name=%s" @@ -766,7 +762,9 @@ def partition_create( redirect=1, ): """Create a new partition""" - if REQUEST and not can_change_groups(context, REQUEST, formsemestre_id): + if REQUEST and not sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") if partition_name: partition_name = partition_name.strip() @@ -810,7 +808,7 @@ def getArrowIconsTags(context, REQUEST): def editPartitionForm(context, formsemestre_id=None, REQUEST=None): """Form to create/suppress partitions""" # ad-hoc form - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") partitions = get_partitions_list(context, formsemestre_id) arrow_up, arrow_down, arrow_none = getArrowIconsTags(context, REQUEST) @@ -940,7 +938,7 @@ def partition_set_attr(context, partition_id, attr, value, REQUEST=None): partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") log("partition_set_attr(%s, %s, %s)" % (partition_id, attr, value)) @@ -963,7 +961,7 @@ def partition_delete( default partition cannot be suppressed (unless force)""" partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") if not partition["partition_name"] and not force: @@ -1007,7 +1005,7 @@ def partition_move(context, partition_id, after=0, REQUEST=None, redirect=1): """Move before/after previous one (decrement/increment numero)""" partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") # redirect = int(redirect) @@ -1042,7 +1040,7 @@ def partition_rename(context, partition_id, REQUEST=None): """Form to rename a partition""" partition = get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") H = ["

Renommer une partition

"] tf = TrivialFormulator( @@ -1103,7 +1101,7 @@ def partition_set_name(context, partition_id, partition_name, REQUEST=None, redi "Partition %s déjà existante dans ce semestre !" % partition_name ) - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") redirect = int(redirect) cnx = ndb.GetDBConnexion() @@ -1128,7 +1126,7 @@ def group_set_name(context, group_id, group_name, REQUEST=None, redirect=1): if group["group_name"] is None: raise ValueError("can't set a name to default group") formsemestre_id = group["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") redirect = int(redirect) cnx = ndb.GetDBConnexion() @@ -1145,7 +1143,7 @@ def group_rename(context, group_id, REQUEST=None): """Form to rename a group""" group = get_group(context, group_id) formsemestre_id = group["formsemestre_id"] - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") H = ["

Renommer un groupe de %s

" % group["partition_name"]] tf = TrivialFormulator( @@ -1193,7 +1191,7 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None): formsemestre_id = partition["formsemestre_id"] # renvoie sur page édition groupes dest_url = "affectGroups?partition_id=%s" % partition_id - if not can_change_groups(context, REQUEST, formsemestre_id): + if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) @@ -1261,7 +1259,7 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None): createGroup(context, partition_id, group_name, REQUEST=REQUEST) ) # - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) # > identdict identdict = nt.identdict diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py index 4fd0f7b9..bd60a69f 100644 --- a/app/scodoc/sco_groups_edit.py +++ b/app/scodoc/sco_groups_edit.py @@ -40,7 +40,9 @@ def affectGroups(context, partition_id, REQUEST=None): # Ported from DTML and adapted to new group management (nov 2009) partition = sco_groups.get_partition(context, partition_id) formsemestre_id = partition["formsemestre_id"] - if not sco_groups.can_change_groups(context, REQUEST, formsemestre_id): + if not sco_groups.sco_permissions_check.can_change_groups( + context, REQUEST, formsemestre_id + ): raise AccessDenied("vous n'avez pas la permission d'effectuer cette opération") H = [ diff --git a/app/scodoc/ImportScolars.py b/app/scodoc/sco_import_etuds.py similarity index 99% rename from app/scodoc/ImportScolars.py rename to app/scodoc/sco_import_etuds.py index 8e4f1d57..1c7675df 100644 --- a/app/scodoc/ImportScolars.py +++ b/app/scodoc/sco_import_etuds.py @@ -53,6 +53,7 @@ from app.scodoc.sco_exceptions import ( ScoGenError, ) from app.scodoc import html_sco_header +from app.scodoc import sco_core from app.scodoc import sco_etud from app.scodoc import sco_formsemestre from app.scodoc import sco_groups diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index c489984c..00679a67 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -31,7 +31,7 @@ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header -from app.scodoc import mails +from app.scodoc import sco_emails import app.scodoc.sco_utils as scu from app.scodoc.notes_log import log from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoException @@ -228,4 +228,4 @@ Pour plus d'informations sur ce logiciel, voir %s msg.epilogue = "" txt = MIMEText(txt, "plain", scu.SCO_ENCODING) msg.attach(txt) - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index c3a9e64a..48e1df0c 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -107,8 +107,8 @@ def list_inscrits(context, formsemestre_id, with_dems=False): { etudid : etud } """ if not with_dems: - ins = context.Notes.do_formsemestre_inscription_listinscrits( - formsemestre_id + ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( + context, formsemestre_id ) # optimized else: args = {"formsemestre_id": formsemestre_id} @@ -215,8 +215,8 @@ def do_inscrit(context, sem, etudids, REQUEST=None, inscrit_groupes=False): def do_desinscrit(context, sem, etudids, REQUEST): log("do_desinscrit: %s" % etudids) for etudid in etudids: - context.do_formsemestre_desinscription( - etudid, sem["formsemestre_id"], REQUEST=REQUEST + sco_formsemestre_inscriptions.do_formsemestre_desinscription( + context, etudid, sem["formsemestre_id"], REQUEST=REQUEST ) diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 623e713e..2761da24 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -91,7 +91,9 @@ def moduleimpl_inscriptions_edit( % (moduleimpl_id, mod["titre"], mod["code"]), ] # Liste des inscrits à ce semestre - inscrits = context.Notes.do_formsemestre_inscription_listinscrits(formsemestre_id) + inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( + context, formsemestre_id + ) for ins in inscrits: etuds_info = sco_etud.get_etud_info(etudid=ins["etudid"], filled=1) if not etuds_info: diff --git a/app/scodoc/sco_news.py b/app/scodoc/sco_news.py index a8d6b1f8..e99309c4 100644 --- a/app/scodoc/sco_news.py +++ b/app/scodoc/sco_news.py @@ -40,7 +40,7 @@ import PyRSS2Gen # pylint: disable=import-error import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.notes_log import log -from app.scodoc import mails +from app.scodoc import sco_emails from app.scodoc.sco_utils import SCO_ENCODING, SCO_ANNONCES_WEBSITE from app.scodoc import sco_formsemestre from app.scodoc import sco_moduleimpl @@ -301,4 +301,4 @@ def _send_news_by_mail(context, n): del msg["To"] msg["To"] = email_addr # log('xxx mail: %s' % msg) - mails.sendEmail(context, msg) + sco_emails.sendEmail(context, msg) diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py index e52b4278..7ae06b65 100644 --- a/app/scodoc/sco_page_etud.py +++ b/app/scodoc/sco_page_etud.py @@ -70,18 +70,18 @@ def _menuScolarite(context, authuser, sem, etudid): if ins["etat"] != "D": dem_title = "Démission" - dem_url = "formDem" + dem_url = "scolar.formDem" else: dem_title = "Annuler la démission" - dem_url = "doCancelDem" + dem_url = "scolar.doCancelDem" # Note: seul un etudiant inscrit (I) peut devenir défaillant. if ins["etat"] != sco_codes_parcours.DEF: def_title = "Déclarer défaillance" - def_url = "formDef" + def_url = "scolar.formDef" elif ins["etat"] == sco_codes_parcours.DEF: def_title = "Annuler la défaillance" - def_url = "doCancelDef" + def_url = "scolar.doCancelDef" def_enabled = ( (ins["etat"] != "D") and authuser.has_permission(Permission.ScoEtudInscrit) diff --git a/app/scodoc/sco_permissions_check.py b/app/scodoc/sco_permissions_check.py index ef5ed90d..a780a469 100644 --- a/app/scodoc/sco_permissions_check.py +++ b/app/scodoc/sco_permissions_check.py @@ -9,9 +9,7 @@ from app.scodoc.sco_permissions import Permission from app.scodoc import html_sco_header from app.scodoc import sco_etud from app.scodoc import sco_exceptions -from app.scodoc import sco_formsemestre from app.scodoc import sco_moduleimpl -from app.scodoc import sco_parcours_dut def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True): @@ -21,6 +19,9 @@ def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True): Si des décisions de jury ont déjà été saisies dans ce semestre, seul le directeur des études peut saisir des notes (et il ne devrait pas). """ + from app.scodoc import sco_formsemestre + from app.scodoc import sco_parcours_dut + uid = str(authuser) M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) @@ -49,6 +50,39 @@ def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True): return True +def can_edit_evaluation(context, REQUEST, moduleimpl_id=None): + """Vérifie que l'on a le droit de modifier, créer ou détruire une + évaluation dans ce module. + Sinon, lance une exception. + (nb: n'implique pas le droit de saisir ou modifier des notes) + """ + # was _evaluation_check_write_access + # AccessDenied("Modification évaluation impossible pour %s" % (uid,)) + from app.scodoc import sco_formsemestre + from app.scodoc import sco_moduleimpl + + # acces pour resp. moduleimpl et resp. form semestre (dir etud) + if moduleimpl_id is None: + raise ValueError("no moduleimpl specified") # bug + authuser = REQUEST.AUTHENTICATED_USER + uid = str(authuser) + M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) + + if ( + authuser.has_permission(Permission.ScoEditAllEvals) + or uid == M["responsable_id"] + or uid in sem["responsables"] + ): + return True + elif sem["ens_can_edit_eval"]: + for ens in M["ens"]: + if ens["ens_id"] == uid: + return True + + return False + + def can_suppress_annotation(context, annotation_id, REQUEST): """True if current user can suppress this annotation Seuls l'auteur de l'annotation et le chef de dept peuvent supprimer @@ -60,13 +94,9 @@ def can_suppress_annotation(context, annotation_id, REQUEST): raise sco_exceptions.ScoValueError("annotation inexistante !") anno = annos[0] authuser = REQUEST.AUTHENTICATED_USER - # note: les anciennes installations n'ont pas le role ScoEtudSupprAnnotations - # c'est pourquoi on teste aussi ScoEtudInscrit (normalement détenue par le chef) return ( - (str(authuser) == anno["zope_authenticated_user"]) - or authuser.has_permission(Permission.ScoEtudSupprAnnotations) - or authuser.has_permission(Permission.ScoEtudInscrit) - ) + str(authuser) == anno["zope_authenticated_user"] + ) or authuser.has_permission(Permission.ScoEtudAddAnnotations) def can_edit_suivi(context, REQUEST=None): @@ -77,6 +107,8 @@ def can_edit_suivi(context, REQUEST=None): def can_validate_sem(context, REQUEST, formsemestre_id): "Vrai si utilisateur peut saisir decision de jury dans ce semestre" + from app.scodoc import sco_formsemestre + sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) if sem["etat"] != "1": return False # semestre verrouillé @@ -86,6 +118,7 @@ def can_validate_sem(context, REQUEST, formsemestre_id): def can_edit_pv(context, REQUEST, formsemestre_id): "Vrai si utilisateur peut editer un PV de jury de ce semestre" + from app.scodoc import sco_formsemestre sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) if is_chef_or_diretud(context, REQUEST, sem): @@ -114,6 +147,8 @@ def check_access_diretud( """Check if access granted: responsable or ScoImplement Return True|False, HTML_error_page """ + from app.scodoc import sco_formsemestre + authuser = REQUEST.AUTHENTICATED_USER sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) header = html_sco_header.sco_header( @@ -137,3 +172,19 @@ def check_access_diretud( ) else: return True, "" + + +def can_change_groups(context, REQUEST, formsemestre_id): + "Vrai si l'utilisateur peut changer les groupes dans ce semestre" + from app.scodoc import sco_formsemestre + + sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) + if sem["etat"] != "1": + return False # semestre verrouillé + authuser = REQUEST.AUTHENTICATED_USER + if authuser.has_permission(Permission.ScoEtudChangeGroups): + return True # admin, chef dept + uid = str(authuser) + if uid in sem["responsables"]: + return True + return False diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index 3501bc05..5a40c636 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -46,6 +46,7 @@ from app.scodoc.sco_permissions import Permission from app.scodoc.TrivialFormulator import TrivialFormulator, TF from app.scodoc import html_sco_header from app.scodoc import htmlutils +from app.scodoc import sco_abs from app.scodoc import sco_core from app.scodoc import sco_edit_module from app.scodoc import sco_evaluations diff --git a/app/scodoc/sco_trombino.py b/app/scodoc/sco_trombino.py index af8ff0a4..cecfc961 100644 --- a/app/scodoc/sco_trombino.py +++ b/app/scodoc/sco_trombino.py @@ -50,7 +50,7 @@ from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_pdf import SU from app.scodoc import html_sco_header from app.scodoc import htmlutils -from app.scodoc import ImportScolars +from app.scodoc import sco_import_etuds from app.scodoc import sco_excel from app.scodoc import sco_formsemestre from app.scodoc import sco_groups @@ -470,8 +470,8 @@ def _listeappel_photos_pdf(context, groups_infos, REQUEST): # --------------------- Upload des photos de tout un groupe def photos_generate_excel_sample(context, group_ids=[], REQUEST=None): """Feuille excel pour import fichiers photos""" - fmt = ImportScolars.sco_import_format() - data = ImportScolars.sco_import_generate_excel_sample( + fmt = sco_import_etuds.sco_import_format() + data = sco_import_etuds.sco_import_generate_excel_sample( fmt, context=context, group_ids=group_ids, diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py index 81313bbf..16edd46b 100644 --- a/app/scodoc/sco_ue_external.py +++ b/app/scodoc/sco_ue_external.py @@ -164,7 +164,7 @@ def external_ue_inscrit_et_note( evaluation_id = ModEvals[0]["evaluation_id"] else: # crée une évaluation: - evaluation_id = context.do_evaluation_create( + evaluation_id = sco_evaluations.do_evaluation_create(context, REQUEST=REQUEST, moduleimpl_id=moduleimpl_id, note_max=20.0, diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 5fa0e6e1..0c9b6e44 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -385,6 +385,15 @@ def UsersURL(): return "NotImplemented" +def get_current_user_name(REQUEST): + """return a displayable string identifying the current user. + XXX For now, the login, but will be the name. #sco8 + """ + authuser = REQUEST.AUTHENTICATED_USER + uid = str(authuser) + return uid + + # ---- Simple python utilities diff --git a/app/views/absences.py b/app/views/absences.py index 4e824f60..871ed4e0 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -81,8 +81,10 @@ from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.gen_tables import GenTable from app.scodoc import html_sco_header from app.scodoc import sco_abs -from app.scodoc import sco_abs_notification, sco_abs_views +from app.scodoc import sco_abs_notification +from app.scodoc import sco_abs_views from app.scodoc import sco_compute_moy +from app.scodoc import sco_core from app.scodoc import sco_etud from app.scodoc import sco_excel from app.scodoc import sco_formsemestre @@ -248,7 +250,9 @@ def SignaleAbsenceGrHebdo( else: # Si aucun etudiant n'est inscrit au module choisi... moduleimpl_id = None - nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_id) + nt = sco_core.get_notes_cache(context).get_NotesTable( + context.Notes, formsemestre_id + ) sem = sco_formsemestre.do_formsemestre_list( context, {"formsemestre_id": formsemestre_id} )[0] @@ -421,7 +425,7 @@ def SignaleAbsenceGrSemestre( base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id if etuds: - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, formsemestre_id ) sem = sco_formsemestre.do_formsemestre_list( @@ -638,7 +642,7 @@ def _gen_form_saisie_groupe( # UE capitalisee dans semestre courant ? cap = [] if etud["cursem"]: - nt = context.Notes._getNotesCache().get_NotesTable( + nt = sco_core.get_notes_cache(context).get_NotesTable( context.Notes, etud["cursem"]["formsemestre_id"] ) # > get_ues, get_etud_ue_status for ue in nt.get_ues(): diff --git a/app/views/essais.py b/app/views/essais.py index 2964349f..fb913233 100644 --- a/app/views/essais.py +++ b/app/views/essais.py @@ -41,7 +41,7 @@ def sco_exemple(etudid="NON"): } -# En ScoDoc 7, on a souvent des vues qui en appellent d'autres +# En ScoDoc 7, on avait des vues qui en appellaient d'autres # avec context.sco_exemple( etudid="E12" ) @bp.route("//Scolarite/sco_exemple2") @login_required diff --git a/app/views/notes.py b/app/views/notes.py index 07dd7171..cb3b9364 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -486,33 +486,11 @@ def formation_import_xml_form(context, REQUEST): ) -@bp.route("/formation_create_new_version") -@permission_required(Permission.ScoChangeFormation) -@scodoc7func(context) -def formation_create_new_version(context, formation_id, redirect=True, REQUEST=None): - "duplicate formation, with new version number" - xml = sco_formations.formation_export( - context, formation_id, export_ids=True, format="xml" - ) - new_id, modules_old2new, ues_old2new = sco_formations.formation_import_xml( - context, REQUEST, xml - ) - # news - F = sco_formations.formation_list(context, args={"formation_id": new_id})[0] - sco_news.add( - context, - REQUEST, - typ=sco_news.NEWS_FORM, - object=new_id, - text="Nouvelle version de la formation %(acronyme)s" % F, - ) - if redirect: - return REQUEST.RESPONSE.redirect( - "ue_list?formation_id=" + new_id + "&msg=Nouvelle version !" - ) - else: - return new_id, modules_old2new, ues_old2new - +sco_publish( + "/formation_create_new_version", + sco_formations.formation_create_new_version, + Permission.ScoChangeFormation, +) # --- UE sco_publish( @@ -579,42 +557,6 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.ScoChangeFormatio # --- Semestres de formation -@bp.route("/do_formsemestre_create") -@permission_required(Permission.ScoImplement) -@scodoc7func(context) -def do_formsemestre_create(context, args, REQUEST, silent=False): - "create a formsemestre" - cnx = ndb.GetDBConnexion() - formsemestre_id = sco_formsemestre._formsemestreEditor.create(cnx, args) - if args["etapes"]: - args["formsemestre_id"] = formsemestre_id - sco_formsemestre.write_formsemestre_etapes(context, args) - if args["responsables"]: - args["formsemestre_id"] = formsemestre_id - sco_formsemestre.write_formsemestre_responsables(context, args) - - # create default partition - partition_id = sco_groups.partition_create( - context, formsemestre_id, default=True, redirect=0, REQUEST=REQUEST - ) - _group_id = sco_groups.createGroup( - context, partition_id, default=True, REQUEST=REQUEST - ) - - # news - if not args.has_key("titre"): - args["titre"] = "sans titre" - args["formsemestre_id"] = formsemestre_id - args["url"] = "Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % args - if not silent: - sco_news.add( - context, - REQUEST, - typ=sco_news.NEWS_SEM, - text='Création du semestre %(titre)s' % args, - url=args["url"], - ) - return formsemestre_id @bp.route("/formsemestre_list") @@ -1285,14 +1227,9 @@ def do_formsemestre_inscription_listinscrits( context, formsemestre_id, format=None, REQUEST=None ): """Liste les inscrits (état I) à ce semestre et cache le résultat""" - cache = sco_core.get_formsemestre_inscription_cache(context) - r = cache.get(formsemestre_id) - if r is None: - # retreive list - r = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( - context, args={"formsemestre_id": formsemestre_id, "etat": "I"} - ) - cache.set(formsemestre_id, r) + r = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( + context, formsemestre_id + ) return scu.sendResult(REQUEST, r, format=format, name="inscrits") @@ -1365,7 +1302,9 @@ def formsemestre_desinscription( parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, ) - context.do_formsemestre_desinscription(etudid, formsemestre_id, REQUEST=REQUEST) + sco_formsemestre_inscriptions.do_formsemestre_desinscription( + context, etudid, formsemestre_id, REQUEST=REQUEST + ) return ( html_sco_header.sco_header(context, REQUEST) @@ -1458,170 +1397,6 @@ sco_publish( # --- Evaluations -def _evaluation_check_write_access(context, REQUEST, moduleimpl_id=None): - """Vérifie que l'on a le droit de modifier, créer ou détruire une - évaluation dans ce module. - Sinon, lance une exception. - (nb: n'implique pas le droit de saisir ou modifier des notes) - """ - # acces pour resp. moduleimpl et resp. form semestre (dir etud) - if moduleimpl_id is None: - raise ValueError("no moduleimpl specified") # bug - authuser = REQUEST.AUTHENTICATED_USER - uid = str(authuser) - M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] - sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) - - if ( - (not authuser.has_permission(Permission.ScoEditAllEvals)) - and uid != M["responsable_id"] - and uid not in sem["responsables"] - ): - if sem["ens_can_edit_eval"]: - for ens in M["ens"]: - if ens["ens_id"] == uid: - return # ok - raise AccessDenied("Modification évaluation impossible pour %s" % (uid,)) - - -@bp.route("/do_evaluation_create") -@permission_required(Permission.ScoEnsView) -@scodoc7func(context) -def do_evaluation_create( - context, - moduleimpl_id=None, - jour=None, - heure_debut=None, - heure_fin=None, - description=None, - note_max=None, - coefficient=None, - visibulletin=None, - publish_incomplete=None, - evaluation_type=None, - numero=None, - REQUEST=None, - **kw -): - """Create an evaluation""" - args = locals() - log("do_evaluation_create: args=" + str(args)) - context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) - context._check_evaluation_args(args) - # Check numeros - sco_evaluations.module_evaluation_renumber( - context, moduleimpl_id, REQUEST=REQUEST, only_if_unumbered=True - ) - if not "numero" in args or args["numero"] is None: - n = None - # determine le numero avec la date - # Liste des eval existantes triees par date, la plus ancienne en tete - ModEvals = sco_evaluations.do_evaluation_list( - context, - args={"moduleimpl_id": moduleimpl_id}, - sortkey="jour asc, heure_debut asc", - ) - if args["jour"]: - next_eval = None - t = ( - ndb.DateDMYtoISO(args["jour"]), - ndb.TimetoISO8601(args["heure_debut"]), - ) - for e in ModEvals: - if ( - ndb.DateDMYtoISO(e["jour"]), - ndb.TimetoISO8601(e["heure_debut"]), - ) > t: - next_eval = e - break - if next_eval: - n = sco_evaluations.module_evaluation_insert_before( - context, ModEvals, next_eval, REQUEST - ) - else: - n = None # a placer en fin - if n is None: # pas de date ou en fin: - if ModEvals: - log(pprint.pformat(ModEvals[-1])) - n = ModEvals[-1]["numero"] + 1 - else: - n = 0 # the only one - # log("creating with numero n=%d" % n) - args["numero"] = n - - # - cnx = ndb.GetDBConnexion() - r = sco_evaluations._evaluationEditor.create(cnx, args) - - # news - M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] - mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0] - mod["moduleimpl_id"] = M["moduleimpl_id"] - mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod - sco_news.add( - context, - REQUEST, - typ=sco_news.NEWS_NOTE, - object=moduleimpl_id, - text='Création d\'une évaluation dans %(titre)s' % mod, - url=mod["url"], - ) - - return r - - -def _check_evaluation_args(context, args): - "Check coefficient, dates and duration, raises exception if invalid" - moduleimpl_id = args["moduleimpl_id"] - # check bareme - note_max = args.get("note_max", None) - if note_max is None: - raise ScoValueError("missing note_max") - try: - note_max = float(note_max) - except ValueError: - raise ScoValueError("Invalid note_max value") - if note_max < 0: - raise ScoValueError("Invalid note_max value (must be positive or null)") - # check coefficient - coef = args.get("coefficient", None) - if coef is None: - raise ScoValueError("missing coefficient") - try: - coef = float(coef) - except ValueError: - raise ScoValueError("Invalid coefficient value") - if coef < 0: - raise ScoValueError("Invalid coefficient value (must be positive or null)") - # check date - jour = args.get("jour", None) - args["jour"] = jour - if jour: - M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] - sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) - d, m, y = [int(x) for x in sem["date_debut"].split("/")] - date_debut = datetime.date(y, m, d) - d, m, y = [int(x) for x in sem["date_fin"].split("/")] - date_fin = datetime.date(y, m, d) - # passe par ndb.DateDMYtoISO pour avoir date pivot - y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")] - jour = datetime.date(y, m, d) - if (jour > date_fin) or (jour < date_debut): - raise ScoValueError( - "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !" - % (d, m, y) - ) - heure_debut = args.get("heure_debut", None) - args["heure_debut"] = heure_debut - heure_fin = args.get("heure_fin", None) - args["heure_fin"] = heure_fin - if jour and ((not heure_debut) or (not heure_fin)): - raise ScoValueError("Les heures doivent être précisées") - d = ndb.TimeDuration(heure_debut, heure_fin) - if d and ((d < 0) or (d > 60 * 12)): - raise ScoValueError("Heures de l'évaluation incohérentes !") - - @bp.route("/evaluation_delete") @permission_required(Permission.ScoEnsView) @scodoc7func(context) diff --git a/app/views/scolar.py b/app/views/scolar.py index eebb7b9b..5230ce02 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -85,7 +85,7 @@ from app.scodoc.gen_tables import GenTable from app.scodoc import html_sco_header from app.scodoc import html_sidebar from app.scodoc import imageresize -from app.scodoc import ImportScolars +from app.scodoc import sco_import_etuds from app.scodoc import sco_abs from app.scodoc import sco_archives_etud from app.scodoc import sco_codes_parcours @@ -1736,7 +1736,7 @@ Les champs avec un astérisque (*) doivent être présents (nulls non autorisés """ ] - for t in ImportScolars.sco_import_format( + for t in sco_import_etuds.sco_import_format( with_codesemestre=(formsemestre_id == None) ): if int(t[3]): @@ -1752,7 +1752,7 @@ Les champs avec un astérisque (*) doivent être présents (nulls non autorisés elif tf[0] == -1: return REQUEST.RESPONSE.redirect(dest_url) else: - return ImportScolars.students_import_excel( + return sco_import_etuds.students_import_excel( context, tf[2]["csvfile"], REQUEST=REQUEST, @@ -1771,8 +1771,8 @@ def import_generate_excel_sample(context, REQUEST, with_codesemestre="1"): with_codesemestre = int(with_codesemestre) else: with_codesemestre = 0 - format = ImportScolars.sco_import_format() - data = ImportScolars.sco_import_generate_excel_sample( + format = sco_import_etuds.sco_import_format() + data = sco_import_etuds.sco_import_generate_excel_sample( format, with_codesemestre, exclude_cols=["photo_filename"], REQUEST=REQUEST ) return sco_excel.sendExcelFile(REQUEST, data, "ImportEtudiants.xls") @@ -1787,8 +1787,8 @@ def import_generate_admission_sample(context, REQUEST, formsemestre_id): group = sco_groups.get_group( context, sco_groups.get_default_group(context, formsemestre_id) ) - fmt = ImportScolars.sco_import_format() - data = ImportScolars.sco_import_generate_excel_sample( + fmt = sco_import_etuds.sco_import_format() + data = sco_import_etuds.sco_import_generate_excel_sample( fmt, only_tables=["identite", "admissions", "adresse"], exclude_cols=["nationalite", "foto", "photo_filename"], @@ -1889,7 +1889,7 @@ def form_students_import_infos_admissions(context, REQUEST, formsemestre_id=None Seule la première feuille du classeur sera utilisée.
""" - + ImportScolars.adm_table_description_format(context).html() + + sco_import_etuds.adm_table_description_format(context).html() + """
""" ) @@ -1913,7 +1913,7 @@ def _students_import_admission( context, csvfile, type_admission="", REQUEST=None, formsemestre_id=None ): "import donnees admission from Excel file (v2016)" - diag = ImportScolars.scolars_import_admission( + diag = sco_import_etuds.scolars_import_admission( csvfile, context.Notes, REQUEST, diff --git a/refactor.py b/refactor.py old mode 100644 new mode 100755 index 2a4af4f4..81f4cf16 --- a/refactor.py +++ b/refactor.py @@ -9,7 +9,7 @@ - remplace context.xxx par module.xxx -./refactor.py refactor method module app/scodoc/*.py +./refactor.py refactor module.method app/scodoc/*.py @@ -34,14 +34,6 @@ import tempfile import shutil import click -# import flask - -# import app -# from app import create_app, cli, db -# from app.auth.models import User, Role, UserRole - -# from app.views import notes - TYPES_TO_SCAN = { types.FunctionType, # types.ClassType,
AttributTypeDescription