forked from ScoDoc/ScoDoc
Work in progress: new updater and code cleaning
This commit is contained in:
parent
1c718dffde
commit
415810496f
@ -93,7 +93,7 @@ ADMISSION_MODIFIABLE_FIELDS = (
|
|||||||
def sco_import_format(with_codesemestre=True):
|
def sco_import_format(with_codesemestre=True):
|
||||||
"returns tuples (Attribut, Type, Table, AllowNulls, Description)"
|
"returns tuples (Attribut, Type, Table, AllowNulls, Description)"
|
||||||
r = []
|
r = []
|
||||||
for l in open(SCO_SRCDIR + "/" + FORMAT_FILE):
|
for l in open(SCO_SRC_DIR + "/" + FORMAT_FILE):
|
||||||
l = l.strip()
|
l = l.strip()
|
||||||
if l and l[0] != "#":
|
if l and l[0] != "#":
|
||||||
fs = l.split(";")
|
fs = l.split(";")
|
||||||
|
55
ZScoDoc.py
55
ZScoDoc.py
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
import time, string, glob, re, inspect
|
import time, string, glob, re, inspect
|
||||||
import urllib, urllib2, cgi, xml
|
import urllib, urllib2, cgi, xml
|
||||||
|
import datetime
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
@ -42,13 +43,22 @@ from zipfile import ZipFile
|
|||||||
import os.path, glob
|
import os.path, glob
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.MIMEBase import MIMEBase
|
from email.mime.base import MIMEBase
|
||||||
from email.Header import Header
|
from email.header import Header
|
||||||
from email import Encoders
|
|
||||||
|
|
||||||
from sco_zope import *
|
from sco_zope import (
|
||||||
|
ObjectManager,
|
||||||
|
PropertyManager,
|
||||||
|
RoleManager,
|
||||||
|
Item,
|
||||||
|
Persistent,
|
||||||
|
Implicit,
|
||||||
|
ClassSecurityInfo,
|
||||||
|
DTMLFile,
|
||||||
|
Globals,
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
@ -56,7 +66,21 @@ try:
|
|||||||
except:
|
except:
|
||||||
import ZPsycopgDA.DA as ZopeDA # interp.py
|
import ZPsycopgDA.DA as ZopeDA # interp.py
|
||||||
|
|
||||||
from sco_utils import *
|
from sco_utils import (
|
||||||
|
SCO_DEFAULT_SQL_USERS_CNX,
|
||||||
|
SCO_ENCODING,
|
||||||
|
CUSTOM_HTML_HEADER_CNX,
|
||||||
|
SCO_USERS_LIST,
|
||||||
|
SCO_WEBSITE,
|
||||||
|
SCO_EXC_MAIL,
|
||||||
|
SCO_DEV_MAIL,
|
||||||
|
scodoc_html2txt,
|
||||||
|
get_svn_version,
|
||||||
|
VERSION,
|
||||||
|
SCODOC_CFG_DIR,
|
||||||
|
)
|
||||||
|
from sco_permissions import ScoView, ScoSuperAdmin
|
||||||
|
from sco_exceptions import AccessDenied
|
||||||
from notes_log import log
|
from notes_log import log
|
||||||
import sco_find_etud
|
import sco_find_etud
|
||||||
from ZScoUsers import pwdFascistCheck
|
from ZScoUsers import pwdFascistCheck
|
||||||
@ -111,7 +135,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
|
|||||||
def _check_users_folder(self, REQUEST=None):
|
def _check_users_folder(self, REQUEST=None):
|
||||||
"""Vérifie UserFolder et le crée s'il le faut"""
|
"""Vérifie UserFolder et le crée s'il le faut"""
|
||||||
try:
|
try:
|
||||||
udb = self.UsersDB
|
self.UsersDB
|
||||||
return "<!-- uf ok -->"
|
return "<!-- uf ok -->"
|
||||||
except:
|
except:
|
||||||
e = self._check_admin_perm(REQUEST)
|
e = self._check_admin_perm(REQUEST)
|
||||||
@ -164,11 +188,11 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
|
|||||||
pass
|
pass
|
||||||
# add missing getAuthFailedMessage (bug in exUserFolder ?)
|
# add missing getAuthFailedMessage (bug in exUserFolder ?)
|
||||||
try:
|
try:
|
||||||
x = self.getAuthFailedMessage
|
self.getAuthFailedMessage
|
||||||
except:
|
except:
|
||||||
log("adding getAuthFailedMessage to Zope install")
|
log("adding getAuthFailedMessage to Zope install")
|
||||||
parent = self.aq_parent
|
parent = self.aq_parent
|
||||||
from OFS.DTMLMethod import addDTMLMethod
|
from OFS.DTMLMethod import addDTMLMethod # pylint: disable=import-error
|
||||||
|
|
||||||
addDTMLMethod(parent, "getAuthFailedMessage", file="Identification")
|
addDTMLMethod(parent, "getAuthFailedMessage", file="Identification")
|
||||||
|
|
||||||
@ -264,7 +288,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
|
|||||||
r = []
|
r = []
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
try:
|
try:
|
||||||
s = folder.Scolarite
|
_ = folder.Scolarite
|
||||||
r.append(folder)
|
r.append(folder)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -646,8 +670,8 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
|
|||||||
**kv
|
**kv
|
||||||
):
|
):
|
||||||
"Recuperation des exceptions Zope"
|
"Recuperation des exceptions Zope"
|
||||||
sco_exc_mail = SCO_EXC_MAIL
|
sco_exc_mail = SCO_EXC_MAIL # pylint: disable=unused-variable
|
||||||
sco_dev_mail = SCO_DEV_MAIL
|
sco_dev_mail = SCO_DEV_MAIL # pylint: disable=unused-variable
|
||||||
# neat (or should I say dirty ?) hack to get REQUEST
|
# neat (or should I say dirty ?) hack to get REQUEST
|
||||||
# in fact, our caller (probably SimpleItem.py) has the REQUEST variable
|
# 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.
|
# that we'd like to use for our logs, but does not pass it as an argument.
|
||||||
@ -714,6 +738,7 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
|
|||||||
)
|
)
|
||||||
# display error traceback (? may open a security risk via xss attack ?)
|
# display error traceback (? may open a security risk via xss attack ?)
|
||||||
# log('exc B')
|
# log('exc B')
|
||||||
|
# pylint: disable=unused-variable
|
||||||
txt_html = self._report_request(REQUEST, fmt="html")
|
txt_html = self._report_request(REQUEST, fmt="html")
|
||||||
H.append(
|
H.append(
|
||||||
"""<h4 class="scodoc">Zope Traceback (à envoyer par mail à <a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>)</h4><div style="background-color: rgb(153,153,204); border: 1px;">
|
"""<h4 class="scodoc">Zope Traceback (à envoyer par mail à <a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>)</h4><div style="background-color: rgb(153,153,204); border: 1px;">
|
||||||
@ -734,6 +759,7 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# --- Mail:
|
# --- Mail:
|
||||||
|
# pylint: disable=unused-variable
|
||||||
error_traceback_txt = scodoc_html2txt(error_tb)
|
error_traceback_txt = scodoc_html2txt(error_tb)
|
||||||
txt = (
|
txt = (
|
||||||
"""
|
"""
|
||||||
@ -752,6 +778,7 @@ ErrorType: %(error_type)s
|
|||||||
|
|
||||||
def _report_request(self, REQUEST, fmt="txt"):
|
def _report_request(self, REQUEST, fmt="txt"):
|
||||||
"""string describing current request for bug reports"""
|
"""string describing current request for bug reports"""
|
||||||
|
# pylint: disable=unused-variable
|
||||||
AUTHENTICATED_USER = REQUEST.get("AUTHENTICATED_USER", "")
|
AUTHENTICATED_USER = REQUEST.get("AUTHENTICATED_USER", "")
|
||||||
dt = time.asctime()
|
dt = time.asctime()
|
||||||
URL = REQUEST.get("URL", "")
|
URL = REQUEST.get("URL", "")
|
||||||
@ -887,7 +914,7 @@ subversion: %(svn_version)s
|
|||||||
"""Liste de id de departements definis par create_dept.sh
|
"""Liste de id de departements definis par create_dept.sh
|
||||||
(fichiers depts/*.cfg)
|
(fichiers depts/*.cfg)
|
||||||
"""
|
"""
|
||||||
filenames = glob.glob(SCODOC_VAR_DIR + "/config/depts/*.cfg")
|
filenames = glob.glob(SCODOC_CFG_DIR + "/config/depts/*.cfg")
|
||||||
ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames]
|
ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames]
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
128
config/scodoc_config_tmpl.py
Normal file
128
config/scodoc_config_tmpl.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configuration globale de ScoDoc (version juin 2009)
|
||||||
|
# Ce fichier est copié dans /opt/scodoc/var/scodoc/config
|
||||||
|
# par les scripts d'installation/mise à jour.
|
||||||
|
|
||||||
|
# La plupart des réglages sont stoqués en base de donnée et accessibles via le web
|
||||||
|
# (pages de paramètres ou préférences).
|
||||||
|
# Les valeurs indiquées ici sont les valeurs initiales que prendront
|
||||||
|
# les paramètres lors de la création d'un nouveau département,
|
||||||
|
# elles ne sont plus utilisées ensuite.
|
||||||
|
|
||||||
|
# Nota: il y a aussi des réglages dans sco_utils.py, mais ils nécessitent
|
||||||
|
# souvent de comprendre le code qui les utilise pour ne pas faire d'erreur: attention.
|
||||||
|
|
||||||
|
|
||||||
|
class CFG:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG = CFG()
|
||||||
|
|
||||||
|
# set to 1 if you want to require INE:
|
||||||
|
# CONFIG.always_require_ine = 0
|
||||||
|
|
||||||
|
# The base URL, use only if you are behind a proxy
|
||||||
|
# eg "https://scodoc.example.net/ScoDoc"
|
||||||
|
# CONFIG.ABSOLUTE_URL = ""
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# -------------- Documents PDF
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
# Taille du l'image logo: largeur/hauteur (ne pas oublier le . !!!)
|
||||||
|
# W/H XXX provisoire: utilisera PIL pour connaitre la taille de l'image
|
||||||
|
# CONFIG.LOGO_FOOTER_ASPECT = 326 / 96.0
|
||||||
|
# Taille dans le document en millimetres
|
||||||
|
# CONFIG.LOGO_FOOTER_HEIGHT = 10
|
||||||
|
# Proportions logo (donné ici pour IUTV)
|
||||||
|
# CONFIG.LOGO_HEADER_ASPECT = 549 / 346.0
|
||||||
|
# Taille verticale dans le document en millimetres
|
||||||
|
# CONFIG.LOGO_HEADER_HEIGHT = 28
|
||||||
|
|
||||||
|
# Pied de page PDF : un format Python, %(xxx)s est remplacé par la variable xxx.
|
||||||
|
# Les variables définies sont:
|
||||||
|
# day : Day of the month as a decimal number [01,31]
|
||||||
|
# month : Month as a decimal number [01,12].
|
||||||
|
# year : Year without century as a decimal number [00,99].
|
||||||
|
# Year : Year with century as a decimal number.
|
||||||
|
# hour : Hour (24-hour clock) as a decimal number [00,23].
|
||||||
|
# minute: Minute as a decimal number [00,59].
|
||||||
|
#
|
||||||
|
# server_url: URL du serveur ScoDoc
|
||||||
|
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir VERSION.py)
|
||||||
|
|
||||||
|
# CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = "Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s sur %(server_url)s"
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# ------------- Calcul bonus modules optionnels (sport, culture...) -------------
|
||||||
|
#
|
||||||
|
|
||||||
|
# CONFIG.compute_bonus = bonus_sport.bonus_iutv
|
||||||
|
# Mettre "bonus_demo" pour logguer des informations utiles au developpement...
|
||||||
|
|
||||||
|
#
|
||||||
|
# ------------- Capitalisation des UEs -------------
|
||||||
|
# Deux écoles:
|
||||||
|
# - règle "DUT": capitalisation des UE obtenues avec moyenne UE >= 10 ET de toutes les UE
|
||||||
|
# des semestres validés (ADM, ADC, AJ). (conforme à l'arrêté d'août 2005)
|
||||||
|
#
|
||||||
|
# - règle "LMD": capitalisation uniquement des UE avec moy. > 10
|
||||||
|
|
||||||
|
# Si vrai, capitalise toutes les UE des semestres validés (règle "DUT").
|
||||||
|
# CONFIG.CAPITALIZE_ALL_UES = True
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------
|
||||||
|
#
|
||||||
|
# -------------- Personnalisation des pages
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Nom (chemin complet) d'un fichier .html à inclure juste après le <body>
|
||||||
|
# le <body> des pages ScoDoc
|
||||||
|
# CONFIG.CUSTOM_HTML_HEADER = ""
|
||||||
|
|
||||||
|
# Fichier html a inclure en fin des pages (juste avant le </body>)
|
||||||
|
# CONFIG.CUSTOM_HTML_FOOTER = ""
|
||||||
|
|
||||||
|
# Fichier .html à inclure dans la pages connexion/déconnexion (accueil)
|
||||||
|
# si on veut que ce soit différent (par défaut la même chose)
|
||||||
|
# CONFIG.CUSTOM_HTML_HEADER_CNX = CONFIG.CUSTOM_HTML_HEADER
|
||||||
|
# CONFIG.CUSTOM_HTML_FOOTER_CNX = CONFIG.CUSTOM_HTML_FOOTER
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# -------------- Noms de Lycées
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Fichier de correspondance codelycee -> noms
|
||||||
|
# (chemin relatif au repertoire d'install des sources)
|
||||||
|
# CONFIG.ETABL_FILENAME = "config/etablissements.csv"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# -------------- Divers:
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# True for UCAC (étudiants camerounais sans prénoms)
|
||||||
|
# CONFIG.ALLOW_NULL_PRENOM = False
|
||||||
|
|
||||||
|
# Taille max des fichiers archive etudiants (en octets)
|
||||||
|
# CONFIG.ETUD_MAX_FILE_SIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
|
# Si pas de photo et portail, publie l'url (était vrai jusqu'en oct 2016)
|
||||||
|
# CONFIG.PUBLISH_PORTAL_PHOTO_URL = False
|
||||||
|
|
||||||
|
# Si > 0: longueur minimale requise des nouveaux mots de passe
|
||||||
|
# (le test cracklib.FascistCheck s'appliquera dans tous les cas)
|
||||||
|
# CONFIG.MIN_PASSWORD_LENGTH = 0
|
||||||
|
|
||||||
|
|
||||||
|
# Ce dictionnaire est fusionné à celui de sco_codes_parcours
|
||||||
|
# pour définir les codes jury et explications associées
|
||||||
|
# CONFIG.CODES_EXPL = {
|
||||||
|
# # AJ : 'Ajourné (échec)',
|
||||||
|
# }
|
@ -118,6 +118,19 @@ if [ $? -ne 0 ]
|
|||||||
then
|
then
|
||||||
/opt/zope213/bin/pip install requests
|
/opt/zope213/bin/pip install requests
|
||||||
fi
|
fi
|
||||||
|
/opt/zope213/bin/python -c "import attrdict" >& /dev/null
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
/opt/zope213/bin/pip install attrdict
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that local configuration file is installed
|
||||||
|
LOCAL_CONFIG_FILENAME="/opt/scodoc/var/scodoc/config/scodoc_local.py"
|
||||||
|
if [ ! -e "$LOCAL_CONFIG_FILENAME" ]
|
||||||
|
then
|
||||||
|
cp "$SCODOC_DIR"/config/scodoc_config_tmpl.py "$LOCAL_CONFIG_FILENAME"
|
||||||
|
chmod 600 "$LOCAL_CONFIG_FILENAME"
|
||||||
|
fi
|
||||||
|
|
||||||
# Ensure www-data can duplicate databases (for dumps)
|
# Ensure www-data can duplicate databases (for dumps)
|
||||||
su -c $'psql -c \'alter role "www-data" with CREATEDB;\'' "$POSTGRES_SUPERUSER"
|
su -c $'psql -c \'alter role "www-data" with CREATEDB;\'' "$POSTGRES_SUPERUSER"
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import pdb, os, sys, time, re, inspect
|
import pdb, os, sys, time, re, inspect
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
|
||||||
from email.MIMEText import MIMEText
|
|
||||||
from email.Header import Header
|
|
||||||
import traceback
|
import traceback
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
|
from email.header import Header
|
||||||
|
|
||||||
# Simple & stupid file logguer, used only to debug
|
# Simple & stupid file logguer, used only to debug
|
||||||
# (logging to SQL is done in scolog)
|
# (logging to SQL is done in scolog)
|
||||||
@ -88,7 +89,7 @@ def retreive_dept():
|
|||||||
return ""
|
return ""
|
||||||
try:
|
try:
|
||||||
url = REQUEST.URL
|
url = REQUEST.URL
|
||||||
m = re.match("^.*ScoDoc/(\w+).*$", url)
|
m = re.match(r"^.*ScoDoc/(\w+).*$", url)
|
||||||
return m.group(1)
|
return m.group(1)
|
||||||
except:
|
except:
|
||||||
return ""
|
return ""
|
||||||
|
@ -50,7 +50,7 @@ DONNEE_MANQUANTE = (
|
|||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def get_code_latex_from_modele(fichier):
|
def get_code_latex_from_modele(fichier):
|
||||||
"""Lit le code latex à partir d'un modèle. Renvoie une chaine unicode.
|
"""Lit le code latex à partir d'un modèle. Renvoie une chaine unicode.
|
||||||
|
|
||||||
Le fichier doit contenir le chemin relatif
|
Le fichier doit contenir le chemin relatif
|
||||||
vers le modele : attention pas de vérification du format d'encodage
|
vers le modele : attention pas de vérification du format d'encodage
|
||||||
Le fichier doit donc etre enregistré avec le même codage que ScoDoc (utf-8)
|
Le fichier doit donc etre enregistré avec le même codage que ScoDoc (utf-8)
|
||||||
@ -85,7 +85,7 @@ def get_tags_latex(code_latex):
|
|||||||
à la lecture d'un modèle d'avis pe).
|
à la lecture d'un modèle d'avis pe).
|
||||||
Ces tags sont répérés par les balises **, débutant et finissant le tag
|
Ces tags sont répérés par les balises **, débutant et finissant le tag
|
||||||
et sont renvoyés sous la forme d'une liste.
|
et sont renvoyés sous la forme d'une liste.
|
||||||
|
|
||||||
result: liste de chaines unicode
|
result: liste de chaines unicode
|
||||||
"""
|
"""
|
||||||
if code_latex:
|
if code_latex:
|
||||||
@ -144,7 +144,7 @@ def comp_latex_parcourstimeline(etudiant, promo, taille=17):
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def interprete_tag_latex(tag):
|
def interprete_tag_latex(tag):
|
||||||
"""Découpe les tags latex de la forme S1:groupe:dut:min et renvoie si possible
|
"""Découpe les tags latex de la forme S1:groupe:dut:min et renvoie si possible
|
||||||
le résultat sous la forme d'un quadruplet.
|
le résultat sous la forme d'un quadruplet.
|
||||||
"""
|
"""
|
||||||
infotag = tag.split(":")
|
infotag = tag.split(":")
|
||||||
@ -164,7 +164,7 @@ def get_code_latex_avis_etudiant(
|
|||||||
donnees_etudiant, un_avis_latex, annotationPE, footer_latex, prefs
|
donnees_etudiant, un_avis_latex, annotationPE, footer_latex, prefs
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Renvoie le code latex permettant de générer l'avis d'un étudiant en utilisant ses
|
Renvoie le code latex permettant de générer l'avis d'un étudiant en utilisant ses
|
||||||
donnees_etudiant contenu dans le dictionnaire de synthèse du jury PE et en suivant un
|
donnees_etudiant contenu dans le dictionnaire de synthèse du jury PE et en suivant un
|
||||||
fichier modele donné
|
fichier modele donné
|
||||||
|
|
||||||
@ -228,8 +228,8 @@ def get_code_latex_avis_etudiant(
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def get_annotation_PE(context, etudid, tag_annotation_pe):
|
def get_annotation_PE(context, etudid, tag_annotation_pe):
|
||||||
"""Renvoie l'annotation PE dans la liste de ces annotations ;
|
"""Renvoie l'annotation PE dans la liste de ces annotations ;
|
||||||
Cette annotation est reconnue par la présence d'un tag **PE**
|
Cette annotation est reconnue par la présence d'un tag **PE**
|
||||||
(cf. context.get_preferences -> pe_tag_annotation_avis_latex).
|
(cf. context.get_preferences -> pe_tag_annotation_avis_latex).
|
||||||
|
|
||||||
Result: chaine unicode
|
Result: chaine unicode
|
||||||
@ -269,8 +269,8 @@ def get_annotation_PE(context, etudid, tag_annotation_pe):
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def str_from_syntheseJury(donnees_etudiant, aggregat, groupe, tag_scodoc, champ):
|
def str_from_syntheseJury(donnees_etudiant, aggregat, groupe, tag_scodoc, champ):
|
||||||
"""Extrait du dictionnaire de synthèse du juryPE pour un étudiant donnée,
|
"""Extrait du dictionnaire de synthèse du juryPE pour un étudiant donnée,
|
||||||
une valeur indiquée par un champ ;
|
une valeur indiquée par un champ ;
|
||||||
si champ est une liste, renvoie la liste des valeurs extraites.
|
si champ est une liste, renvoie la liste des valeurs extraites.
|
||||||
|
|
||||||
Result: chaine unicode ou liste de chaines unicode
|
Result: chaine unicode ou liste de chaines unicode
|
||||||
@ -322,7 +322,7 @@ def str_from_syntheseJury(donnees_etudiant, aggregat, groupe, tag_scodoc, champ)
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def get_bilanParTag(donnees_etudiant, groupe="groupe"):
|
def get_bilanParTag(donnees_etudiant, groupe="groupe"):
|
||||||
"""Renvoie le code latex d'un tableau récapitulant, pour tous les tags trouvés dans
|
"""Renvoie le code latex d'un tableau récapitulant, pour tous les tags trouvés dans
|
||||||
les données étudiants, ses résultats.
|
les données étudiants, ses résultats.
|
||||||
result: chaine unicode
|
result: chaine unicode
|
||||||
"""
|
"""
|
||||||
@ -460,11 +460,11 @@ def get_templates_from_distrib(template="avis"):
|
|||||||
|
|
||||||
if template in ["avis", "footer"]:
|
if template in ["avis", "footer"]:
|
||||||
# pas de preference pour le template: utilise fichier du serveur
|
# pas de preference pour le template: utilise fichier du serveur
|
||||||
p = os.path.join(SCO_SRCDIR, pe_local_tmpl)
|
p = os.path.join(SCO_SRC_DIR, pe_local_tmpl)
|
||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
template_latex = get_code_latex_from_modele(p)
|
template_latex = get_code_latex_from_modele(p)
|
||||||
else:
|
else:
|
||||||
p = os.path.join(SCO_SRCDIR, pe_default_tmpl)
|
p = os.path.join(SCO_SRC_DIR, pe_default_tmpl)
|
||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
template_latex = get_code_latex_from_modele(p)
|
template_latex = get_code_latex_from_modele(p)
|
||||||
else:
|
else:
|
||||||
@ -474,8 +474,7 @@ def get_templates_from_distrib(template="avis"):
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def table_syntheseAnnotationPE(context, syntheseJury, tag_annotation_pe):
|
def table_syntheseAnnotationPE(context, syntheseJury, tag_annotation_pe):
|
||||||
"""Génère un fichier excel synthétisant les annotations PE telles qu'inscrites dans les fiches de chaque étudiant
|
"""Génère un fichier excel synthétisant les annotations PE telles qu'inscrites dans les fiches de chaque étudiant"""
|
||||||
"""
|
|
||||||
sT = SeqGenTable() # le fichier excel à générer
|
sT = SeqGenTable() # le fichier excel à générer
|
||||||
|
|
||||||
# Les etudids des étudiants à afficher, triés par ordre alphabétiques de nom+prénom
|
# Les etudids des étudiants à afficher, triés par ordre alphabétiques de nom+prénom
|
||||||
|
10
pe_tools.py
10
pe_tools.py
@ -97,7 +97,7 @@ def print_semestres_description(sems, avec_affichage_debug=False):
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def calcul_age(born):
|
def calcul_age(born):
|
||||||
"""Calcule l'age à partir de la date de naissance sous forme d'une chaine de caractère 'jj/mm/aaaa'.
|
"""Calcule l'age à partir de la date de naissance sous forme d'une chaine de caractère 'jj/mm/aaaa'.
|
||||||
Aucun test de validité sur le format de la date n'est fait.
|
Aucun test de validité sur le format de la date n'est fait.
|
||||||
"""
|
"""
|
||||||
if not isinstance(born, str) or born == "":
|
if not isinstance(born, str) or born == "":
|
||||||
@ -122,8 +122,7 @@ def remove_accents(input_unicode_str):
|
|||||||
|
|
||||||
|
|
||||||
def escape_for_latex(s):
|
def escape_for_latex(s):
|
||||||
"""Protège les caractères pour inclusion dans du source LaTeX
|
"""Protège les caractères pour inclusion dans du source LaTeX"""
|
||||||
"""
|
|
||||||
if not s:
|
if not s:
|
||||||
return ""
|
return ""
|
||||||
conv = {
|
conv = {
|
||||||
@ -162,8 +161,7 @@ def list_directory_filenames(path):
|
|||||||
|
|
||||||
|
|
||||||
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
||||||
"""Read pathname server file and add content to zip under path_in_zip
|
"""Read pathname server file and add content to zip under path_in_zip"""
|
||||||
"""
|
|
||||||
rooted_path_in_zip = os.path.join(ziproot, path_in_zip)
|
rooted_path_in_zip = os.path.join(ziproot, path_in_zip)
|
||||||
data = open(pathname).read()
|
data = open(pathname).read()
|
||||||
zipfile.writestr(rooted_path_in_zip, data)
|
zipfile.writestr(rooted_path_in_zip, data)
|
||||||
@ -177,7 +175,7 @@ def add_pe_stuff_to_zip(context, zipfile, ziproot):
|
|||||||
|
|
||||||
Also copy logos
|
Also copy logos
|
||||||
"""
|
"""
|
||||||
PE_AUX_DIR = os.path.join(SCO_SRCDIR, "config/doc_poursuites_etudes")
|
PE_AUX_DIR = os.path.join(SCO_SRC_DIR, "config/doc_poursuites_etudes")
|
||||||
distrib_dir = os.path.join(PE_AUX_DIR, "distrib")
|
distrib_dir = os.path.join(PE_AUX_DIR, "distrib")
|
||||||
distrib_pathnames = list_directory_filenames(
|
distrib_pathnames = list_directory_filenames(
|
||||||
distrib_dir
|
distrib_dir
|
||||||
|
@ -32,10 +32,9 @@
|
|||||||
Il suffit d'appeler abs_notify() après chaque ajout d'absence.
|
Il suffit d'appeler abs_notify() après chaque ajout d'absence.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.Header import Header
|
from email.header import Header
|
||||||
from email import Encoders
|
|
||||||
|
|
||||||
from notesdb import *
|
from notesdb import *
|
||||||
from sco_utils import *
|
from sco_utils import *
|
||||||
@ -47,7 +46,7 @@ import sco_formsemestre
|
|||||||
|
|
||||||
def abs_notify(context, etudid, date):
|
def abs_notify(context, etudid, date):
|
||||||
"""Check if notifications are requested and send them
|
"""Check if notifications are requested and send them
|
||||||
Considère le nombre d'absence dans le semestre courant
|
Considère le nombre d'absence dans le semestre courant
|
||||||
(s'il n'y a pas de semestre courant, ne fait rien,
|
(s'il n'y a pas de semestre courant, ne fait rien,
|
||||||
car l'etudiant n'est pas inscrit au moment de l'absence!).
|
car l'etudiant n'est pas inscrit au moment de l'absence!).
|
||||||
"""
|
"""
|
||||||
@ -64,8 +63,7 @@ def abs_notify(context, etudid, date):
|
|||||||
|
|
||||||
|
|
||||||
def do_abs_notify(context, sem, etudid, date, nbabs, nbabsjust):
|
def do_abs_notify(context, sem, etudid, date, nbabs, nbabsjust):
|
||||||
"""Given new counts of absences, check if notifications are requested and send them.
|
"""Given new counts of absences, check if notifications are requested and send them."""
|
||||||
"""
|
|
||||||
# prefs fallback to global pref if sem is None:
|
# prefs fallback to global pref if sem is None:
|
||||||
if sem:
|
if sem:
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
formsemestre_id = sem["formsemestre_id"]
|
||||||
@ -131,8 +129,7 @@ def abs_notify_send(
|
|||||||
|
|
||||||
|
|
||||||
def abs_notify_get_destinations(context, sem, prefs, etudid, date, nbabs, nbabsjust):
|
def abs_notify_get_destinations(context, sem, prefs, etudid, date, nbabs, nbabsjust):
|
||||||
"""Returns set of destination emails to be notified
|
"""Returns set of destination emails to be notified"""
|
||||||
"""
|
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
formsemestre_id = sem["formsemestre_id"]
|
||||||
|
|
||||||
destinations = [] # list of email address to notify
|
destinations = [] # list of email address to notify
|
||||||
@ -176,8 +173,8 @@ def abs_notify_is_above_threshold(context, etudid, nbabs, nbabsjust, formsemestr
|
|||||||
|
|
||||||
nbabs: nombre d'absence (de tous types, unité de compte = demi-journée)
|
nbabs: nombre d'absence (de tous types, unité de compte = demi-journée)
|
||||||
nbabsjust: nombre d'absences justifiées
|
nbabsjust: nombre d'absences justifiées
|
||||||
|
|
||||||
(nbabs > abs_notify_abs_threshold)
|
(nbabs > abs_notify_abs_threshold)
|
||||||
(nbabs - nbabs_last_notified) > abs_notify_abs_increment
|
(nbabs - nbabs_last_notified) > abs_notify_abs_increment
|
||||||
"""
|
"""
|
||||||
abs_notify_abs_threshold = context.get_preference(
|
abs_notify_abs_threshold = context.get_preference(
|
||||||
@ -282,8 +279,7 @@ def retreive_current_formsemestre(context, etudid, cur_date):
|
|||||||
|
|
||||||
|
|
||||||
def mod_with_evals_at_date(context, date_abs, etudid):
|
def mod_with_evals_at_date(context, date_abs, etudid):
|
||||||
"""Liste des moduleimpls avec des evaluations a la date indiquée
|
"""Liste des moduleimpls avec des evaluations a la date indiquée"""
|
||||||
"""
|
|
||||||
req = """SELECT m.* FROM notes_moduleimpl m, notes_evaluation e, notes_moduleimpl_inscription i
|
req = """SELECT m.* FROM notes_moduleimpl m, notes_evaluation e, notes_moduleimpl_inscription i
|
||||||
WHERE m.moduleimpl_id = e.moduleimpl_id AND e.moduleimpl_id = i.moduleimpl_id
|
WHERE m.moduleimpl_id = e.moduleimpl_id AND e.moduleimpl_id = i.moduleimpl_id
|
||||||
AND i.etudid = %(etudid)s AND e.jour = %(date_abs)s"""
|
AND i.etudid = %(etudid)s AND e.jour = %(date_abs)s"""
|
||||||
|
@ -28,11 +28,11 @@
|
|||||||
"""Génération des bulletins de notes
|
"""Génération des bulletins de notes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
import email
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEBase import MIMEBase
|
from email.mime.text import MIMEText
|
||||||
from email.Header import Header
|
from email.mime.base import MIMEBase
|
||||||
from email import Encoders
|
from email.header import Header
|
||||||
|
|
||||||
import htmlutils, time
|
import htmlutils, time
|
||||||
from reportlab.lib.colors import Color
|
from reportlab.lib.colors import Color
|
||||||
@ -331,7 +331,7 @@ def formsemestre_bulletinetud_dict(
|
|||||||
context, ue_status["formsemestre_id"]
|
context, ue_status["formsemestre_id"]
|
||||||
) # > toutes notes
|
) # > toutes notes
|
||||||
|
|
||||||
u["modules_capitalized"], junk = _ue_mod_bulletin(
|
u["modules_capitalized"], _ = _ue_mod_bulletin(
|
||||||
context,
|
context,
|
||||||
etudid,
|
etudid,
|
||||||
formsemestre_id,
|
formsemestre_id,
|
||||||
@ -990,7 +990,7 @@ def mail_bulletin(context, formsemestre_id, I, pdfdata, filename, recipient_addr
|
|||||||
att = MIMEBase("application", "pdf")
|
att = MIMEBase("application", "pdf")
|
||||||
att.add_header("Content-Disposition", "attachment", filename=filename)
|
att.add_header("Content-Disposition", "attachment", filename=filename)
|
||||||
att.set_payload(pdfdata)
|
att.set_payload(pdfdata)
|
||||||
Encoders.encode_base64(att)
|
email.encoders.encode_base64(att)
|
||||||
msg.attach(att)
|
msg.attach(att)
|
||||||
log("mail bulletin a %s" % msg["To"])
|
log("mail bulletin a %s" % msg["To"])
|
||||||
context.sendEmail(msg)
|
context.sendEmail(msg)
|
||||||
|
@ -116,7 +116,8 @@ CODES_EXPL = {
|
|||||||
RAT: "En attente d'un rattrapage",
|
RAT: "En attente d'un rattrapage",
|
||||||
DEF: "Défaillant",
|
DEF: "Défaillant",
|
||||||
}
|
}
|
||||||
# Nota: ces explications sont personnalisables via le fichier de config scodoc_config.py
|
# Nota: ces explications sont personnalisables via le fichier
|
||||||
|
# de config locale /opt/scodoc/var/scodoc/config/scodoc_local.py
|
||||||
# variable: CONFIG.CODES_EXP
|
# variable: CONFIG.CODES_EXP
|
||||||
|
|
||||||
CODES_SEM_VALIDES = {ADM: True, ADC: True, ADJ: True} # semestre validé
|
CODES_SEM_VALIDES = {ADM: True, ADC: True, ADJ: True} # semestre validé
|
||||||
|
109
sco_config.py
Normal file
109
sco_config.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Configuration de ScoDoc (version 2020)
|
||||||
|
NE PAS MODIFIER localement ce fichier !
|
||||||
|
mais éditer /opt/scodoc/var/scodoc/config/scodoc_local.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
from attrdict import AttrDict
|
||||||
|
import bonus_sport
|
||||||
|
|
||||||
|
CONFIG = AttrDict()
|
||||||
|
|
||||||
|
# set to 1 if you want to require INE:
|
||||||
|
CONFIG.always_require_ine = 0
|
||||||
|
|
||||||
|
# The base URL, use only if you are behind a proxy
|
||||||
|
# eg "https://scodoc.example.net/ScoDoc"
|
||||||
|
CONFIG.ABSOLUTE_URL = ""
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# -------------- Documents PDF
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
# Taille du l'image logo: largeur/hauteur (ne pas oublier le . !!!)
|
||||||
|
# W/H XXX provisoire: utilisera PIL pour connaitre la taille de l'image
|
||||||
|
CONFIG.LOGO_FOOTER_ASPECT = 326 / 96.0
|
||||||
|
# Taille dans le document en millimetres
|
||||||
|
CONFIG.LOGO_FOOTER_HEIGHT = 10
|
||||||
|
# Proportions logo (donné ici pour IUTV)
|
||||||
|
CONFIG.LOGO_HEADER_ASPECT = 549 / 346.0
|
||||||
|
# Taille verticale dans le document en millimetres
|
||||||
|
CONFIG.LOGO_HEADER_HEIGHT = 28
|
||||||
|
|
||||||
|
|
||||||
|
# Pied de page PDF : un format Python, %(xxx)s est remplacé par la variable xxx.
|
||||||
|
# Les variables définies sont:
|
||||||
|
# day : Day of the month as a decimal number [01,31]
|
||||||
|
# month : Month as a decimal number [01,12].
|
||||||
|
# year : Year without century as a decimal number [00,99].
|
||||||
|
# Year : Year with century as a decimal number.
|
||||||
|
# hour : Hour (24-hour clock) as a decimal number [00,23].
|
||||||
|
# minute: Minute as a decimal number [00,59].
|
||||||
|
#
|
||||||
|
# server_url: URL du serveur ScoDoc
|
||||||
|
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir VERSION.py)
|
||||||
|
CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = "Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s sur %(server_url)s"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ------------- Calcul bonus modules optionnels (sport, culture...) -------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG.compute_bonus = bonus_sport.bonus_iutv
|
||||||
|
# Mettre "bonus_demo" pour logguer des informations utiles au developpement...
|
||||||
|
|
||||||
|
# ------------- Capitalisation des UEs -------------
|
||||||
|
# Deux écoles:
|
||||||
|
# - règle "DUT": capitalisation des UE obtenues avec moyenne UE >= 10 ET de toutes les UE
|
||||||
|
# des semestres validés (ADM, ADC, AJ). (conforme à l'arrêté d'août 2005)
|
||||||
|
#
|
||||||
|
# - règle "LMD": capitalisation uniquement des UE avec moy. > 10
|
||||||
|
|
||||||
|
# Si vrai, capitalise toutes les UE des semestres validés (règle "DUT").
|
||||||
|
# CONFIG.CAPITALIZE_ALL_UES = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# -------------- Personnalisation des pages
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Nom (chemin complet) d'un fichier .html à inclure juste après le <body>
|
||||||
|
# le <body> des pages ScoDoc
|
||||||
|
CONFIG.CUSTOM_HTML_HEADER = ""
|
||||||
|
|
||||||
|
# Fichier html a inclure en fin des pages (juste avant le </body>)
|
||||||
|
CONFIG.CUSTOM_HTML_FOOTER = ""
|
||||||
|
|
||||||
|
# Fichier .html à inclure dans la pages connexion/déconnexion (accueil)
|
||||||
|
# si on veut que ce soit différent (par défaut la même chose)
|
||||||
|
CONFIG.CUSTOM_HTML_HEADER_CNX = CONFIG.CUSTOM_HTML_HEADER
|
||||||
|
CONFIG.CUSTOM_HTML_FOOTER_CNX = CONFIG.CUSTOM_HTML_FOOTER
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# -------------- Noms de Lycées
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
# Fichier de correspondance codelycee -> noms
|
||||||
|
# (chemin relatif au repertoire d'install des sources)
|
||||||
|
CONFIG.ETABL_FILENAME = "config/etablissements.csv"
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# -------------- Divers:
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# True for UCAC (étudiants camerounais sans prénoms)
|
||||||
|
CONFIG.ALLOW_NULL_PRENOM = False
|
||||||
|
|
||||||
|
# Taille max des fichiers archive etudiants (en octets)
|
||||||
|
# CONFIG.ETUD_MAX_FILE_SIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
|
# Si pas de photo et portail, publie l'url (était vrai jusqu'en oct 2016)
|
||||||
|
CONFIG.PUBLISH_PORTAL_PHOTO_URL = False
|
||||||
|
|
||||||
|
# Si > 0: longueur minimale requise des nouveaux mots de passe
|
||||||
|
# (le test cracklib.FascistCheck s'appliquera dans tous les cas)
|
||||||
|
CONFIG.MIN_PASSWORD_LENGTH = 0
|
||||||
|
|
||||||
|
# Ce dictionnaire est fusionné à celui de sco_codes_parcours
|
||||||
|
# pour définir les codes jury et explications associées
|
||||||
|
CONFIG.CODES_EXPL = {
|
||||||
|
# AJ : 'Ajourné (échec)',
|
||||||
|
}
|
44
sco_config_load.py
Normal file
44
sco_config_load.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Chargement de la configuration locale
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import sco_utils
|
||||||
|
from sco_utils import log, SCODOC_CFG_DIR
|
||||||
|
import sco_config
|
||||||
|
|
||||||
|
# scodoc_local defines a CONFIG object
|
||||||
|
# here we check if there is a local config file
|
||||||
|
|
||||||
|
|
||||||
|
def load_local_configuration():
|
||||||
|
"""Load local configuration file (if exists)
|
||||||
|
and merge it with CONFIG.
|
||||||
|
"""
|
||||||
|
# this path should be synced with upgrade.sh
|
||||||
|
LOCAL_CONFIG_FILENAME = os.path.join(SCODOC_CFG_DIR, "scodoc_local.py")
|
||||||
|
LOCAL_CONFIG = None
|
||||||
|
if os.path.exists(LOCAL_CONFIG_FILENAME):
|
||||||
|
if not SCODOC_CFG_DIR in sys.path:
|
||||||
|
sys.path.insert(1, SCODOC_CFG_DIR)
|
||||||
|
try:
|
||||||
|
from scodoc_local import CONFIG as LOCAL_CONFIG
|
||||||
|
|
||||||
|
log("imported %s" % LOCAL_CONFIG_FILENAME)
|
||||||
|
except ImportError:
|
||||||
|
log("Error: can't import %s" % LOCAL_CONFIG_FILENAME)
|
||||||
|
del sys.path[1]
|
||||||
|
if LOCAL_CONFIG is None:
|
||||||
|
return
|
||||||
|
# Now merges local config in our CONFIG
|
||||||
|
for x in [x for x in dir(LOCAL_CONFIG) if x[0] != "_"]:
|
||||||
|
v = getattr(LOCAL_CONFIG, x)
|
||||||
|
if not v in sco_config.CONFIG:
|
||||||
|
log("Warning: local config setting unused parameter %s (skipped)" % x)
|
||||||
|
else:
|
||||||
|
if v != sco_config.CONFIG[x]:
|
||||||
|
log("Setting parameter %s from %s" % (x, LOCAL_CONFIG_FILENAME))
|
||||||
|
sco_config.CONFIG[x] = v
|
@ -50,11 +50,10 @@ pg_dump SCORT | psql ANORT
|
|||||||
import fcntl
|
import fcntl
|
||||||
import subprocess
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.MIMEBase import MIMEBase
|
from email.mime.base import MIMEBase
|
||||||
from email.Header import Header
|
from email.header import Header
|
||||||
from email import Encoders
|
|
||||||
|
|
||||||
from notesdb import *
|
from notesdb import *
|
||||||
from sco_utils import *
|
from sco_utils import *
|
||||||
@ -64,8 +63,7 @@ SCO_DUMP_LOCK = "/tmp/scodump.lock"
|
|||||||
|
|
||||||
|
|
||||||
def sco_dump_and_send_db(context, REQUEST=None):
|
def sco_dump_and_send_db(context, REQUEST=None):
|
||||||
"""Dump base de données du département courant et l'envoie anonymisée pour debug
|
"""Dump base de données du département courant et l'envoie anonymisée pour debug"""
|
||||||
"""
|
|
||||||
H = [context.sco_header(REQUEST, page_title="Assistance technique")]
|
H = [context.sco_header(REQUEST, page_title="Assistance technique")]
|
||||||
# get currect (dept) DB name:
|
# get currect (dept) DB name:
|
||||||
cursor = SimpleQuery(context, "SELECT current_database()", {})
|
cursor = SimpleQuery(context, "SELECT current_database()", {})
|
||||||
@ -150,9 +148,8 @@ def _duplicate_db(db_name, ano_db_name):
|
|||||||
|
|
||||||
|
|
||||||
def _anonymize_db(ano_db_name):
|
def _anonymize_db(ano_db_name):
|
||||||
"""Anonymize a departement database
|
"""Anonymize a departement database"""
|
||||||
"""
|
cmd = os.path.join(SCO_TOOLS_DIR, "anonymize_db.py")
|
||||||
cmd = os.path.join(SCO_CONFIG_DIR, "anonymize_db.py")
|
|
||||||
log("_anonymize_db: {}".format(cmd))
|
log("_anonymize_db: {}".format(cmd))
|
||||||
try:
|
try:
|
||||||
out = subprocess.check_output([cmd, ano_db_name])
|
out = subprocess.check_output([cmd, ano_db_name])
|
||||||
@ -171,8 +168,7 @@ def _get_scodoc_serial(context):
|
|||||||
|
|
||||||
|
|
||||||
def _send_db(context, REQUEST, ano_db_name):
|
def _send_db(context, REQUEST, ano_db_name):
|
||||||
"""Dump this (anonymized) database and send it to tech support
|
"""Dump this (anonymized) database and send it to tech support"""
|
||||||
"""
|
|
||||||
log("dumping anonymized database {}".format(ano_db_name))
|
log("dumping anonymized database {}".format(ano_db_name))
|
||||||
try:
|
try:
|
||||||
data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1)
|
data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1)
|
||||||
@ -195,7 +191,7 @@ def _send_db(context, REQUEST, ano_db_name):
|
|||||||
"nomcomplet"
|
"nomcomplet"
|
||||||
],
|
],
|
||||||
"sco_version": SCOVERSION,
|
"sco_version": SCOVERSION,
|
||||||
"sco_subversion": get_svn_version(SCO_CONFIG_DIR),
|
"sco_subversion": get_svn_version(SCO_TOOLS_DIR),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return r
|
return r
|
||||||
|
11
sco_news.py
11
sco_news.py
@ -33,11 +33,9 @@ from cStringIO import StringIO
|
|||||||
import datetime, re
|
import datetime, re
|
||||||
import time
|
import time
|
||||||
from stripogram import html2text, html2safehtml
|
from stripogram import html2text, html2safehtml
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.Header import Header
|
from email.header import Header
|
||||||
from email import Encoders
|
|
||||||
|
|
||||||
|
|
||||||
from notesdb import *
|
from notesdb import *
|
||||||
from notes_log import log
|
from notes_log import log
|
||||||
@ -252,8 +250,7 @@ def scolar_news_summary_rss(context, title, sco_url, n=5):
|
|||||||
|
|
||||||
|
|
||||||
def _send_news_by_mail(context, n):
|
def _send_news_by_mail(context, n):
|
||||||
"""Notify by email
|
"""Notify by email"""
|
||||||
"""
|
|
||||||
infos = _get_formsemestre_infos_from_news(context, n)
|
infos = _get_formsemestre_infos_from_news(context, n)
|
||||||
formsemestre_id = infos.get("formsemestre_id", None)
|
formsemestre_id = infos.get("formsemestre_id", None)
|
||||||
prefs = context.get_preferences(formsemestre_id=formsemestre_id)
|
prefs = context.get_preferences(formsemestre_id=formsemestre_id)
|
||||||
|
@ -53,7 +53,7 @@ from PIL import Image as PILImage
|
|||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
from sco_utils import CONFIG, SCO_SRCDIR
|
from sco_utils import CONFIG, SCO_SRC_DIR
|
||||||
from notes_log import log
|
from notes_log import log
|
||||||
|
|
||||||
import scolars
|
import scolars
|
||||||
@ -62,7 +62,7 @@ from scolog import logdb
|
|||||||
|
|
||||||
# Full paths on server's filesystem. Something like "/opt/scodoc/var/scodoc/photos"
|
# Full paths on server's filesystem. Something like "/opt/scodoc/var/scodoc/photos"
|
||||||
PHOTO_DIR = os.path.join(os.environ["INSTANCE_HOME"], "var", "scodoc", "photos")
|
PHOTO_DIR = os.path.join(os.environ["INSTANCE_HOME"], "var", "scodoc", "photos")
|
||||||
ICONS_DIR = os.path.join(SCO_SRCDIR, "static", "icons")
|
ICONS_DIR = os.path.join(SCO_SRC_DIR, "static", "icons")
|
||||||
UNKNOWN_IMAGE_PATH = os.path.join(ICONS_DIR, "unknown.jpg")
|
UNKNOWN_IMAGE_PATH = os.path.join(ICONS_DIR, "unknown.jpg")
|
||||||
UNKNOWN_IMAGE_URL = "get_photo_image?etudid=" # with empty etudid => unknown face image
|
UNKNOWN_IMAGE_URL = "get_photo_image?etudid=" # with empty etudid => unknown face image
|
||||||
IMAGE_EXT = ".jpg"
|
IMAGE_EXT = ".jpg"
|
||||||
|
@ -37,9 +37,9 @@ import datetime
|
|||||||
|
|
||||||
import sco_utils
|
import sco_utils
|
||||||
from sco_utils import ScoEtudInscrit, log, ScoValueError, DictDefault
|
from sco_utils import ScoEtudInscrit, log, ScoValueError, DictDefault
|
||||||
from sco_utils import SCO_TMPDIR, SCO_ENCODING
|
from sco_utils import SCO_TMP_DIR, SCO_ENCODING
|
||||||
|
|
||||||
SCO_CACHE_ETAPE_FILENAME = os.path.join(SCO_TMPDIR, "last_etapes.xml")
|
SCO_CACHE_ETAPE_FILENAME = os.path.join(SCO_TMP_DIR, "last_etapes.xml")
|
||||||
|
|
||||||
|
|
||||||
def has_portal(context):
|
def has_portal(context):
|
||||||
|
@ -49,7 +49,7 @@ Chaque parametre est défini dans la base de données SQL par:
|
|||||||
Au niveau du code interface, on défini pour chaque préférence:
|
Au niveau du code interface, on défini pour chaque préférence:
|
||||||
- name (clé)
|
- name (clé)
|
||||||
- title : titre en français
|
- title : titre en français
|
||||||
- initvalue : valeur initiale, chargée depuis config/scodoc_config.py
|
- initvalue : valeur initiale
|
||||||
- explanation: explication en français
|
- explanation: explication en français
|
||||||
- size: longueur du chap texte
|
- size: longueur du chap texte
|
||||||
- input_type: textarea,separator,... type de widget TrivialFormulator a utiliser
|
- input_type: textarea,separator,... type de widget TrivialFormulator a utiliser
|
||||||
@ -90,13 +90,6 @@ sinon, elle ne concerne que le semestre indiqué.
|
|||||||
- editer les preferences d'un semestre:
|
- editer les preferences d'un semestre:
|
||||||
sem_preferences(context,formsemestre_id).edit()
|
sem_preferences(context,formsemestre_id).edit()
|
||||||
|
|
||||||
* Valeurs par défaut:
|
|
||||||
On a deux valeurs par défaut possibles:
|
|
||||||
- via le fichier scodoc_config.py, qui peut être modifié localement.
|
|
||||||
- si rien dans scodoc_config.py, la valeur définie par
|
|
||||||
sco_preferences.py est utilisée (ne pas modifier ce fichier).
|
|
||||||
|
|
||||||
|
|
||||||
* Implémentation: sco_preferences.py
|
* Implémentation: sco_preferences.py
|
||||||
|
|
||||||
PREF_CATEGORIES : définition des catégories de préférences (pour
|
PREF_CATEGORIES : définition des catégories de préférences (pour
|
||||||
|
133
sco_utils.py
133
sco_utils.py
@ -186,53 +186,84 @@ def get_mention(moy):
|
|||||||
return NOTES_MENTIONS_LABS[bisect.bisect_right(NOTES_MENTIONS_TH, moy)]
|
return NOTES_MENTIONS_LABS[bisect.bisect_right(NOTES_MENTIONS_TH, moy)]
|
||||||
|
|
||||||
|
|
||||||
|
class DictDefault(dict): # obsolete, use collections.defaultdict
|
||||||
|
"""A dictionnary with default value for all keys
|
||||||
|
Each time a non existent key is requested, it is added to the dict.
|
||||||
|
(used in python 2.4, can't use new __missing__ method)
|
||||||
|
"""
|
||||||
|
|
||||||
|
defaultvalue = 0
|
||||||
|
|
||||||
|
def __init__(self, defaultvalue=0, kv_dict={}):
|
||||||
|
dict.__init__(self)
|
||||||
|
self.defaultvalue = defaultvalue
|
||||||
|
self.update(kv_dict)
|
||||||
|
|
||||||
|
def __getitem__(self, k):
|
||||||
|
if self.has_key(k):
|
||||||
|
return self.get(k)
|
||||||
|
value = copy.copy(self.defaultvalue)
|
||||||
|
self[k] = value
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class WrapDict:
|
||||||
|
"""Wrap a dict so that getitem returns '' when values are None"""
|
||||||
|
|
||||||
|
def __init__(self, adict, NoneValue=""):
|
||||||
|
self.dict = adict
|
||||||
|
self.NoneValue = NoneValue
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
value = self.dict[key]
|
||||||
|
if value is None:
|
||||||
|
return self.NoneValue
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def group_by_key(d, key):
|
||||||
|
g = DictDefault(defaultvalue=[])
|
||||||
|
for e in d:
|
||||||
|
g[e[key]].append(e)
|
||||||
|
return g
|
||||||
|
|
||||||
|
|
||||||
# ----- Global lock for critical sections (except notes_tables caches)
|
# ----- Global lock for critical sections (except notes_tables caches)
|
||||||
GSL = thread.allocate_lock() # Global ScoDoc Lock
|
GSL = thread.allocate_lock() # Global ScoDoc Lock
|
||||||
|
|
||||||
if "INSTANCE_HOME" in os.environ:
|
if "INSTANCE_HOME" in os.environ:
|
||||||
# ----- Repertoire "var" (local)
|
# ----- Repertoire "var" (local)
|
||||||
SCODOC_VAR_DIR = os.path.join(os.environ["INSTANCE_HOME"], "var", "scodoc")
|
SCODOC_VAR_DIR = os.path.join(os.environ["INSTANCE_HOME"], "var", "scodoc")
|
||||||
|
# ----- Repertoire "config" modifiable
|
||||||
|
# /opt/scodoc/var/scodoc/config
|
||||||
|
SCODOC_CFG_DIR = os.path.join(SCODOC_VAR_DIR, "config")
|
||||||
# ----- Version information
|
# ----- Version information
|
||||||
SCODOC_VERSION_DIR = os.path.join(SCODOC_VAR_DIR, "config", "version")
|
SCODOC_VERSION_DIR = os.path.join(SCODOC_CFG_DIR, "version")
|
||||||
# ----- Repertoire tmp
|
# ----- Repertoire tmp
|
||||||
SCO_TMPDIR = os.path.join(SCODOC_VAR_DIR, "tmp")
|
SCO_TMP_DIR = os.path.join(SCODOC_VAR_DIR, "tmp")
|
||||||
if not os.path.exists(SCO_TMPDIR):
|
if not os.path.exists(SCO_TMP_DIR):
|
||||||
os.mkdir(SCO_TMPDIR, 0o755)
|
os.mkdir(SCO_TMP_DIR, 0o755)
|
||||||
# ----- Les logos: /opt/scodoc/var/scodoc/config/logos
|
# ----- Les logos: /opt/scodoc/var/scodoc/config/logos
|
||||||
SCODOC_LOGOS_DIR = os.path.join(SCODOC_VAR_DIR, "config", "logos")
|
SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos")
|
||||||
|
|
||||||
# ----- Repertoire "config" (devrait s'appeler "tools"...)
|
# Dans les sources:
|
||||||
SCO_CONFIG_DIR = os.path.join(
|
SCO_SRC_DIR = os.path.join(os.environ["INSTANCE_HOME"], "Products", "ScoDoc")
|
||||||
os.environ["INSTANCE_HOME"], "Products", "ScoDoc", "config"
|
# - Les outils distribués
|
||||||
)
|
SCO_TOOLS_DIR = os.path.join(SCO_SRC_DIR, "config")
|
||||||
|
|
||||||
|
|
||||||
# ----- Lecture du fichier de configuration
|
# ----- Lecture du fichier de configuration
|
||||||
SCO_SRCDIR = os.path.split(VERSION.__file__)[0]
|
import sco_config
|
||||||
if SCO_SRCDIR:
|
import sco_config_load
|
||||||
SCO_SRCDIR += "/"
|
|
||||||
else:
|
|
||||||
SCO_SRCDIR = "/opt/scodoc/Products/ScoDoc/" # debug mode
|
|
||||||
CONFIG = None
|
|
||||||
try:
|
|
||||||
_config_filename = SCO_SRCDIR + "config/scodoc_config.py"
|
|
||||||
_config_text = open(_config_filename).read()
|
|
||||||
except:
|
|
||||||
sys.stderr.write("sco_utils: cannot open configuration file %s" % _config_filename)
|
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
|
||||||
exec(_config_text)
|
|
||||||
except:
|
|
||||||
sys.stderr.write("sco_utils: error in configuration file %s" % _config_filename)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
sco_config_load.load_local_configuration()
|
||||||
|
CONFIG = sco_config.CONFIG
|
||||||
if hasattr(CONFIG, "CODES_EXPL"):
|
if hasattr(CONFIG, "CODES_EXPL"):
|
||||||
CODES_EXPL.update(
|
CODES_EXPL.update(
|
||||||
CONFIG.CODES_EXPL
|
CONFIG.CODES_EXPL
|
||||||
) # permet de customiser les explications de codes
|
) # permet de customiser les explications de codes
|
||||||
|
|
||||||
|
|
||||||
if CONFIG.CUSTOM_HTML_HEADER:
|
if CONFIG.CUSTOM_HTML_HEADER:
|
||||||
CUSTOM_HTML_HEADER = open(CONFIG.CUSTOM_HTML_HEADER).read()
|
CUSTOM_HTML_HEADER = open(CONFIG.CUSTOM_HTML_HEADER).read()
|
||||||
else:
|
else:
|
||||||
@ -297,50 +328,6 @@ JSON_MIMETYPE = "application/json"
|
|||||||
|
|
||||||
LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "png") # remind that PIL does not read pdf
|
LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "png") # remind that PIL does not read pdf
|
||||||
|
|
||||||
|
|
||||||
class DictDefault(dict): # obsolete, use collections.defaultdict
|
|
||||||
"""A dictionnary with default value for all keys
|
|
||||||
Each time a non existent key is requested, it is added to the dict.
|
|
||||||
(used in python 2.4, can't use new __missing__ method)
|
|
||||||
"""
|
|
||||||
|
|
||||||
defaultvalue = 0
|
|
||||||
|
|
||||||
def __init__(self, defaultvalue=0, kv_dict={}):
|
|
||||||
dict.__init__(self)
|
|
||||||
self.defaultvalue = defaultvalue
|
|
||||||
self.update(kv_dict)
|
|
||||||
|
|
||||||
def __getitem__(self, k):
|
|
||||||
if self.has_key(k):
|
|
||||||
return self.get(k)
|
|
||||||
value = copy.copy(self.defaultvalue)
|
|
||||||
self[k] = value
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class WrapDict:
|
|
||||||
"""Wrap a dict so that getitem returns '' when values are None"""
|
|
||||||
|
|
||||||
def __init__(self, adict, NoneValue=""):
|
|
||||||
self.dict = adict
|
|
||||||
self.NoneValue = NoneValue
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
value = self.dict[key]
|
|
||||||
if value is None:
|
|
||||||
return self.NoneValue
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def group_by_key(d, key):
|
|
||||||
g = DictDefault(defaultvalue=[])
|
|
||||||
for e in d:
|
|
||||||
g[e[key]].append(e)
|
|
||||||
return g
|
|
||||||
|
|
||||||
|
|
||||||
# Admissions des étudiants
|
# Admissions des étudiants
|
||||||
# Différents types de voies d'admission:
|
# Différents types de voies d'admission:
|
||||||
# (stocké en texte libre dans la base, mais saisie par menus pour harmoniser)
|
# (stocké en texte libre dans la base, mais saisie par menus pour harmoniser)
|
||||||
@ -791,7 +778,7 @@ def icontag(name, file_format="png", **attrs):
|
|||||||
"""
|
"""
|
||||||
if ("width" not in attrs) or ("height" not in attrs):
|
if ("width" not in attrs) or ("height" not in attrs):
|
||||||
if name not in ICONSIZES:
|
if name not in ICONSIZES:
|
||||||
img_file = SCO_SRCDIR + "/static/icons/%s.%s" % (name, file_format)
|
img_file = SCO_SRC_DIR + "/static/icons/%s.%s" % (name, file_format)
|
||||||
im = PILImage.open(img_file)
|
im = PILImage.open(img_file)
|
||||||
width, height = im.size[0], im.size[1]
|
width, height = im.size[0], im.size[1]
|
||||||
ICONSIZES[name] = (width, height) # cache
|
ICONSIZES[name] = (width, height) # cache
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
"""Imports et configuration des composants Zope
|
"""Imports et configuration des composants Zope
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Allows code linting on platforms without Zope:
|
||||||
|
# pylint: disable=import-error
|
||||||
from OFS.SimpleItem import Item # Basic zope object
|
from OFS.SimpleItem import Item # Basic zope object
|
||||||
from OFS.PropertyManager import PropertyManager # provide the 'Properties' tab with the
|
from OFS.PropertyManager import PropertyManager # provide the 'Properties' tab with the
|
||||||
|
|
||||||
|
11
scolars.py
11
scolars.py
@ -42,11 +42,10 @@ import locale
|
|||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, ("en_US", SCO_ENCODING))
|
locale.setlocale(locale.LC_ALL, ("en_US", SCO_ENCODING))
|
||||||
|
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.MIMEBase import MIMEBase
|
from email.header import Header
|
||||||
from email.Header import Header
|
from email.mime.base import MIMEBase
|
||||||
from email import Encoders
|
|
||||||
|
|
||||||
abbrvmonthsnames = [
|
abbrvmonthsnames = [
|
||||||
"Jan ",
|
"Jan ",
|
||||||
@ -713,7 +712,7 @@ appreciations_edit = _appreciationsEditor.edit
|
|||||||
|
|
||||||
# -------- Noms des Lycées à partir du code
|
# -------- Noms des Lycées à partir du code
|
||||||
def read_etablissements():
|
def read_etablissements():
|
||||||
filename = SCO_SRCDIR + "/" + CONFIG.ETABL_FILENAME
|
filename = SCO_SRC_DIR + "/" + CONFIG.ETABL_FILENAME
|
||||||
log("reading %s" % filename)
|
log("reading %s" % filename)
|
||||||
f = open(filename)
|
f = open(filename)
|
||||||
L = [x[:-1].split(";") for x in f]
|
L = [x[:-1].split(";") for x in f]
|
||||||
|
Loading…
Reference in New Issue
Block a user