Compare commits

..

11 Commits

Author SHA1 Message Date
root
5cad38d6c8 yMerge branch 'master' of https://scodoc.org/git/fares.amer/ScoDoc 2021-06-10 16:10:52 +02:00
root
401bbf103d unicité des noms etudiants 2021-06-10 16:10:02 +02:00
b89277a9a8 Supprimer 'scotests/test_etudiant1.py' 2021-06-10 16:06:32 +02:00
11618196b3 Supprimer 'scotests/test_edit.py' 2021-06-10 16:06:15 +02:00
root
2c7d55713a ajout de nouveaux tests 2021-06-09 19:27:12 +02:00
root
01820b5a91 ajout des commentaires 2021-06-09 19:21:58 +02:00
root
600016d1e0 ajout de nouveaux tests 2021-05-18 11:31:39 +02:00
root
87f136d6b9 Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc 2021-05-05 11:44:48 +02:00
root
699b482c30 mini test parametre demijournee 2021-05-03 14:57:58 +02:00
root
34e34b188c test basé sur les groupes 2021-05-03 14:29:40 +02:00
root
65221f247c test des fonctions lié aux abs 2021-05-03 13:43:11 +02:00
234 changed files with 10637 additions and 11137 deletions

1
.gitignore vendored
View File

@ -131,7 +131,6 @@ venv/
ENV/
env.bak/
venv.bak/
envsco8/
# Spyder project settings
.spyderproject

View File

@ -108,7 +108,7 @@ ADMISSION_MODIFIABLE_FIELDS = (
def sco_import_format(with_codesemestre=True):
"returns tuples (Attribut, Type, Table, AllowNulls, Description)"
r = []
for l in open(scu.SCO_SRC_DIR + "/" + FORMAT_FILE):
for l in open(scu.SCO_SRCDIR + "/" + FORMAT_FILE):
l = l.strip()
if l and l[0] != "#":
fs = l.split(";")

View File

@ -1,5 +1,5 @@
# ScoDoc - Gestion de la scolarité
# SCODOC - gestion de la scolarité
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
@ -8,68 +8,7 @@ Installation: voir instructions à jour sur <https://scodoc.org>
Documentation utilisateur: <https://scodoc.org>
## Branche ScoDoc 8 expérimentale
N'utiliser que pour les développements et tests, dans le cadre de la migration de Zope vers Flask.
Basée sur **python 2.7**.
## Setup (sur Debian 10 / python2.7)
virtualenv envsco8
source envsco8/bin/activate
installation:
pip install flask
# et pas mal d'autres paquets
donc utiliser:
pip install -r requirements.txt
pour régénerer ce fichier:
pip freeze > requirements.txt
### Bidouilles temporaires
Installer le bon vieux `pyExcelerator` dans l'environnement:
(cd /tmp; tar xfz /opt/scodoc/Products/ScoDoc/config/softs/pyExcelerator-0.6.3a.patched.tgz )
(cd /tmp/pyExcelerator-0.6.3a.patched/; python setup.py install)
## Lancement serveur (développement, sur VM Linux)
export FLASK_APP=scodoc.py
export FLASK_ENV=development
flask run --host=0.0.0.0
## Tests
python -m unittest tests.test_users
# Work in Progress
## Migration ZScolar
### Méthodes qui ne devraient plus être publiées:
security.declareProtected(ScoView, "get_preferences")
def get_preferences(context, formsemestre_id=None):
"Get preferences for this instance (a dict-like instance)"
return sco_preferences.sem_preferences(context, formsemestre_id)
security.declareProtected(ScoView, "get_preference")
def get_preference(context, name, formsemestre_id=None):
"""Returns value of named preference.
All preferences have a sensible default value (see sco_preferences.py),
this function always returns a usable value for all defined preferences names.
"""
return sco_preferences.get_base_preferences(context).get(formsemestre_id, name)
Ce logiciel est un produit pour Zope 2.13 écrit en Python (2.4, passé à 2.7 pour ScoDoc7).

238
TODO Normal file
View File

@ -0,0 +1,238 @@
NOTES EN VRAC / Brouillon / Trucs obsoletes
#do_moduleimpl_list\(\{"([a-z_]*)"\s*:\s*(.*)\}\)
#do_moduleimpl_list( $1 = $2 )
#do_moduleimpl_list\([\s\n]*args[\s\n]*=[\s\n]*\{"([a-z_]*)"[\s\n]*:[\s\n]*(.*)[\s\n]*\}[\s\n]*\)
Upgrade JavaScript
- jquery-ui-1.12.1 introduit un problème d'affichage de la barre de menu.
Il faudrait la revoir entièrement pour upgrader.
On reste donc à jquery-ui-1.10.4.custom
Or cette version est incompatible avec jQuery 3 (messages d'erreur dans la console)
On reste donc avec jQuery 1.12.14
Suivi des requêtes utilisateurs:
table sql: id, ip, authuser, request
* Optim:
porcodeb4, avant memorisation des moy_ue:
S1 SEM14133 cold start: min 9s, max 12s, avg > 11s
inval (add note): 1.33s (pas de recalcul des autres)
inval (add abs) : min8s, max 12s (recalcule tout :-()
LP SEM14946 cold start: 0.7s - 0.86s
----------------- LISTE OBSOLETE (très ancienne, à trier) -----------------------
BUGS
----
- formsemestre_inscription_with_modules
si inscription 'un etud deja inscrit, IntegrityError
FEATURES REQUESTS
-----------------
* Bulletins:
. logos IUT et Univ sur bull PDF
. nom departement: nom abbrégé (CJ) ou complet (Carrière Juridiques)
. bulletin: deplacer la barre indicateur (cf OLDGEA S2: gêne)
. bulletin: click nom titre -> ficheEtud
. formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5"
et valider correctement le form !
* Jury
. recapcomplet: revenir avec qq lignes au dessus de l'étudiant en cours
* Divers
. formsemestre_editwithmodules: confirmer suppression modules
(et pour l'instant impossible si evaluations dans le module)
* Modules et UE optionnelles:
. UE capitalisées: donc dispense possible dans semestre redoublé.
traitable en n'inscrivant pas l'etudiant au modules
de cette UE: faire interface utilisateur
. page pour inscription d'un etudiant a un module
. page pour visualiser les modules auquel un etudiant est inscrit,
et le desinscrire si besoin.
. ficheEtud indiquer si inscrit au module sport
* Absences
. EtatAbsences : verifier dates (en JS)
. Listes absences pdf et listes groupes pdf + emargements (cf mail Nathalie)
. absences par demi-journées sur EtatAbsencesDate (? à vérifier)
. formChoixSemestreGroupe: utilisé par Absences/index_html
a améliorer
* Notes et évaluations:
. Exception "Not an OLE file": generer page erreur plus explicite
. Dates evaluation: utiliser JS pour calendrier
. Saisie des notes: si une note invalide, l'indiquer dans le listing (JS ?)
. et/ou: notes invalides: afficher les noms des etudiants concernes
dans le message d'erreur.
. upload excel: message erreur peu explicite:
* Feuille "Saisie notes", 17 lignes
* Erreur: la feuille contient 1 notes invalides
* Notes invalides pour les id: ['10500494']
(pas de notes modifiées)
Notes chargées. <<< CONTRADICTOIRE !!
. recap complet semestre:
Options:
- choix groupes
- critère de tri (moy ou alphab)
- nb de chiffres a afficher
+ definir des "catégories" d'évaluations (eg "théorie","pratique")
afin de n'afficher que des moyennes "de catégorie" dans
le bulletin.
. liste des absents à une eval et croisement avec BD absences
. notes_evaluation_listenotes
- afficher groupes, moyenne, #inscrits, #absents, #manquantes dans l'en-tete.
- lien vers modif notes (selon role)
. Export excel des notes d'evaluation: indiquer date, et autres infos en haut.
. Génération PDF listes notes
. Page recap notes moyennes par groupes (choisir type de groupe?)
. (GEA) edition tableau notes avec tous les evals d'un module
(comme notes_evaluation_listenotes mais avec tt les evals)
* Non prioritaire:
. optimiser scolar_news_summary
. recapitulatif des "nouvelles"
- dernieres notes
- changement de statuts (demissions,inscriptions)
- annotations
- entreprises
. notes_table: pouvoir changer decision sans invalider tout le cache
. navigation: utiliser Session pour montrer historique pages vues ?
------------------------------------------------------------------------
A faire:
- fiche etud: code dec jury sur ligne 1
si ancien, indiquer autorisation inscription sous le parcours
- saisie notes: undo
- saisie notes: validation
- ticket #18:
UE capitalisées: donc dispense possible dans semestre redoublé. Traitable en n'inscrivant pas l'etudiant aux modules de cette UE: faire interface utilisateur.
Prévoir d'entrer une UE capitalisée avec sa note, date d'obtention et un commentaire. Coupler avec la désincription aux modules (si l'étudiant a été inscrit avec ses condisciples).
- Ticket #4: Afin d'éviter les doublons, vérifier qu'il n'existe pas d'homonyme proche lors de la création manuelle d'un étudiant. (confirmé en ScoDoc 6, vérifier aussi les imports Excel)
- Ticket #74: Il est possible d'inscrire un étudiant sans prénom par un import excel !!!
- Ticket #64: saisir les absences pour la promo entiere (et pas par groupe). Des fois, je fais signer une feuille de presence en amphi a partir de la liste de tous les etudiants. Ensuite pour reporter les absents par groupe, c'est galere.
- Ticket #62: Lors des exports Excel, le format des cellules n'est pas reconnu comme numérique sous Windows (pas de problèmes avec Macintosh et Linux).
A confirmer et corriger.
- Ticket #75: On peut modifier une décision de jury (et les autorisations de passage associées), mais pas la supprimer purement et simplement.
Ajoute ce choix dans les "décisions manuelles".
- Ticket #37: Page recap notes moyennes par groupes
Construire une page avec les moyennes dans chaque UE ou module par groupe d'étudiants.
Et aussi pourquoi pas ventiler par type de bac, sexe, parcours (nombre de semestre de parcours) ?
redemandé par CJ: à faire avant mai 2008 !
- Ticket #75: Synchro Apogée: choisir les etudiants
Sur la page de syncho Apogée (formsemestre_synchro_etuds), on peut choisir (cocher) les étudiants Apogée à importer. mais on ne peut pas le faire s'ils sont déjà dans ScoDoc: il faudrait ajouter des checkboxes dans toutes les listes.
- Ticket #9: Format des valeurs de marges des bulletins.
formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5" et valider correctement le form !
- Ticket #17: Suppression modules dans semestres
formsemestre_editwithmodules: confirmer suppression modules
- Ticket #29: changer le stoquage des photos, garder une version HD.
- bencher NotesTable sans calcul de moyennes. Etudier un cache des moyennes de modules.
- listes d'utilisateurs (modules): remplacer menus par champs texte + completions javascript
- documenter archives sur Wiki
- verifier paquet Debian pour font pdf (reportab: helvetica ... plante si font indisponible)
- chercher comment obtenir une page d'erreur correcte pour les pages POST
(eg: si le font n'existe pas, archive semestre echoue sans page d'erreur)
? je ne crois pas que le POST soit en cause. HTTP status=500
ne se produit pas avec Safari
- essayer avec IE / Win98
- faire apparaitre les diplômés sur le graphe des parcours
- démission: formulaire: vérifier que la date est bien dans le semestre
+ graphe parcours: aligner en colonnes selon les dates (de fin), placer les diplomes
dans la même colone que le semestre terminal.
- modif gestion utilisateurs (donner droits en fct du dept. d'appartenance, bug #57)
- modif form def. utilisateur (dept appartenance)
- utilisateurs: source externe
- archivage des semestres
o-------------------------------------o
* Nouvelle gestion utilisateurs:
objectif: dissocier l'authentification de la notion "d'enseignant"
On a une source externe "d'utilisateurs" (annuaire LDAP ou base SQL)
qui permet seulement de:
- authentifier un utilisateur (login, passwd)
- lister un utilisateur: login => firstname, lastname, email
- lister les utilisateurs
et une base interne ScoDoc "d'acteurs" (enseignants, administratifs).
Chaque acteur est défini par:
- actor_id, firstname, lastname
date_creation, date_expiration,
roles, departement,
email (+flag indiquant s'il faut utiliser ce mail ou celui de
l'utilisateur ?)
state (on, off) (pour desactiver avant expiration ?)
user_id (login) => lien avec base utilisateur
On offrira une source d'utilisateurs SQL (base partagée par tous les dept.
d'une instance ScoDoc), mais dans la plupart des cas les gens utiliseront
un annuaire LDAP.
La base d'acteurs remplace ScoUsers. Les objets ScoDoc (semestres,
modules etc) font référence à des acteurs (eg responsable_id est un actor_id).
Le lien entre les deux ?
Loger un utilisateur => authentification utilisateur + association d'un acteur
Cela doit se faire au niveau d'un UserFolder Zope, pour avoir les
bons rôles et le contrôle d'accès adéquat.
(Il faut donc coder notre propre UserFolder).
On ne peut associer qu'un acteur à l'état 'on' et non expiré.
Opérations ScoDoc:
- paramétrage: choisir et paramétrer source utilisateurs
- ajouter utilisateur: choisir un utilisateur dans la liste
et lui associer un nouvel acteur (choix des rôles, des dates)
+ éventuellement: synchro d'un ensemble d'utilisateurs, basé sur
une requête (eg LDAP) précise (quelle interface utilisateur proposer ?)
- régulièrement (cron) aviser quelqu'un (le chef) de l'expiration des acteurs.
- changer etat d'un acteur (on/off)
o-------------------------------------o

View File

@ -1,14 +1,13 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "8.01a"
SCOVERSION = "7.24"
SCONAME = "ScoDoc"
SCONEWS = """
<h4>Année 2021</h4>
<ul>
<li>Version mobile (en test)</li>
<li>Évaluations de type "deuxième session"</li>
<li>Gestion du genre neutre (pas d'affichage de la civilité)</li>
<li>Diverses corrections (PV de jurys, ...)</li>

View File

@ -720,7 +720,7 @@ class ZAbsences(
+ self.sco_footer(REQUEST)
)
base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % (
base_url = "SignaleAbsenceGrHebdo?datelundi=%s&amp;%s&amp;destination=%s" % (
datelundi,
groups_infos.groups_query_args,
urllib.quote(destination),
@ -904,16 +904,15 @@ class ZAbsences(
etuds = [e for e in etuds if e["etudid"] in mod_inscrits]
if not moduleimpl_id:
moduleimpl_id = None
base_url_noweeks = (
"SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s"
% (
datedebut,
datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
)
base_url_noweeks = "SignaleAbsenceGrSemestre?datedebut=%s&amp;datefin=%s&amp;%s&amp;destination=%s" % (
datedebut,
datefin,
groups_infos.groups_query_args,
urllib.quote(destination),
)
base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id
base_url = (
base_url_noweeks + "&amp;nbweeks=%s" % nbweeks
) # sans le moduleimpl_id
if etuds:
nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id)
@ -953,9 +952,9 @@ class ZAbsences(
dates = dates[-nbweeks:]
msg = "Montrer toutes les semaines"
nwl = 0
url_link_semaines = base_url_noweeks + "&nbweeks=%s" % nwl
url_link_semaines = base_url_noweeks + "&amp;nbweeks=%s" % nwl
if moduleimpl_id:
url_link_semaines += "&moduleimpl_id=" + moduleimpl_id
url_link_semaines += "&amp;moduleimpl_id=" + moduleimpl_id
#
dates = [x.ISO() for x in dates]
dayname = sco_abs.day_names(self)[jourdebut.weekday]
@ -1028,7 +1027,7 @@ class ZAbsences(
"""<p>
Module concerné par ces absences (%(optionel_txt)s):
<select id="moduleimpl_id" name="moduleimpl_id"
onchange="document.location='%(url)s&moduleimpl_id='+document.getElementById('moduleimpl_id').value">
onchange="document.location='%(url)s&amp;moduleimpl_id='+document.getElementById('moduleimpl_id').value">
<option value="" %(sel)s>non spécifié</option>
%(menu_module)s
</select>
@ -1328,7 +1327,7 @@ class ZAbsences(
for a in absnonjust:
a["justlink"] = "<em>justifier</em>"
a["_justlink_target"] = (
"doJustifAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s"
"doJustifAbsence?etudid=%s&amp;datedebut=%s&amp;datefin=%s&amp;demijournee=%s"
% (etudid, a["datedmy"], a["datedmy"], a["ampm"])
)
#
@ -1464,7 +1463,7 @@ class ZAbsences(
)
+ "<p>Période du %s au %s (nombre de <b>demi-journées</b>)<br/>"
% (debut, fin),
base_url="%s&formsemestre_id=%s&debut=%s&fin=%s"
base_url="%s&amp;formsemestre_id=%s&amp;debut=%s&amp;fin=%s"
% (groups_infos.base_url, formsemestre_id, debut, fin),
filename="etat_abs_"
+ scu.make_filename(
@ -1701,7 +1700,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
"ProcessBilletAbsenceForm?billet_id=%s" % b["billet_id"]
)
if etud:
b["_etat_str_target"] += "&etudid=%s" % etud["etudid"]
b["_etat_str_target"] += "&amp;etudid=%s" % etud["etudid"]
b["_billet_id_target"] = b["_etat_str_target"]
else:
b["etat_str"] = "ok"

3307
ZNotes.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -55,17 +55,7 @@ from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-
from email.Header import Header # pylint: disable=no-name-in-module,import-error
from email import Encoders # pylint: disable=no-name-in-module,import-error
from sco_zope import (
ObjectManager,
PropertyManager,
RoleManager,
Item,
Persistent,
Implicit,
ClassSecurityInfo,
DTMLFile,
Globals,
)
from sco_zope import * # pylint: disable=unused-wildcard-import
try:
import Products.ZPsycopgDA.DA as ZopeDA
@ -514,11 +504,6 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
% REQUEST.BASE0
)
# Lien expérimental temporaire:
H.append(
'<p><a href="/ScoDoc/static/mobile">Version mobile (expérimentale, à vos risques et périls)</a></p>'
)
H.append(
"""
<div id="scodoc_attribution">
@ -768,6 +753,7 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
% params
)
# display error traceback (? may open a security risk via xss attack ?)
# log('exc B')
params["txt_html"] = self._report_request(REQUEST, fmt="html")
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;">
@ -841,6 +827,8 @@ REFERER: %(REFERER)s
Form: %(form)s
Origin: %(HTTP_X_FORWARDED_FOR)s
Agent: %(HTTP_USER_AGENT)s
subversion: %(svn_version)s
"""
% params
)

View File

@ -261,7 +261,7 @@ class ZScoUsers(
security.declareProtected(ScoUsersAdmin, "user_info")
def user_info(self, user_name=None, user=None, format=None, REQUEST=None):
def user_info(self, user_name=None, user=None):
"""Donne infos sur l'utilisateur (qui peut ne pas etre dans notre base).
Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict.
"""
@ -322,7 +322,7 @@ class ZScoUsers(
# nomnoacc est le nom en minuscules sans accents
info["nomnoacc"] = scu.suppress_accents(scu.strlower(info["nom"]))
return scu.sendResult(REQUEST, info, name="user", format=format)
return info
def _can_handle_passwd(self, authuser, user_name, allow_admindepts=False):
"""true if authuser can see or change passwd of user_name.
@ -523,7 +523,7 @@ class ZScoUsers(
if authuser.has_permission(ScoUsersAdmin, self):
H.append(
"""
<li><a class="stdlink" href="create_user_form?user_name=%(user_name)s&edit=1">modifier/déactiver ce compte</a></li>
<li><a class="stdlink" href="create_user_form?user_name=%(user_name)s&amp;edit=1">modifier/déactiver ce compte</a></li>
<li><a class="stdlink" href="delete_user_form?user_name=%(user_name)s">supprimer cet utilisateur</a> <em>(à n'utiliser qu'en cas d'erreur !)</em></li>
"""
% info[0]

View File

@ -279,7 +279,7 @@ class ZScolar(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Programme DUT TEST</title>
<title>Programme DUT R&amp;T</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="LANG" content="fr" />
@ -416,6 +416,11 @@ REQUEST.URL0=%s<br/>
# GESTION DE LA BD
#
# --------------------------------------------------------------------
security.declareProtected(ScoSuperAdmin, "GetDBConnexionString")
def GetDBConnexionString(self):
# should not be published (but used from contained classes via acquisition)
return self._db_cnx_string
security.declareProtected(ScoSuperAdmin, "GetDBConnexion")
GetDBConnexion = ndb.GetDBConnexion
@ -462,9 +467,9 @@ REQUEST.URL0=%s<br/>
H = [
"""<h2>Système de gestion scolarité</h2>
<p>&copy; Emmanuel Viennet 1997-2021</p>
<p>Version %s</p>
<p>Version %s (subversion %s)</p>
"""
% (scu.get_scodoc_version())
% (SCOVERSION, scu.get_svn_version(file_path))
]
H.append(
'<p>Logiciel libre écrit en <a href="http://www.python.org">Python</a>.</p><p>Utilise <a href="http://www.reportlab.org/">ReportLab</a> pour générer les documents PDF, et <a href="http://sourceforge.net/projects/pyexcelerator">pyExcelerator</a> pour le traitement des documents Excel.</p>'
@ -674,7 +679,7 @@ REQUEST.URL0=%s<br/>
date = date.next()
FA.append("</select>")
FA.append(
'<a href="Absences/EtatAbsencesGr?group_ids=%%(group_id)s&debut=%(date_debut)s&fin=%(date_fin)s">état</a>'
'<a href="Absences/EtatAbsencesGr?group_ids=%%(group_id)s&amp;debut=%(date_debut)s&amp;fin=%(date_fin)s">état</a>'
% sem
)
FA.append("</form></td>")
@ -710,8 +715,8 @@ REQUEST.URL0=%s<br/>
"""<td>
<a href="%(url)s/groups_view?group_ids=%(group_id)s">%(label)s</a>
</td><td>
(<a href="%(url)s/groups_view?group_ids=%(group_id)s&format=xls">format tableur</a>)
<a href="%(url)s/groups_view?curtab=tab-photos&group_ids=%(group_id)s&etat=I">Photos</a>
(<a href="%(url)s/groups_view?group_ids=%(group_id)s&amp;format=xls">format tableur</a>)
<a href="%(url)s/groups_view?curtab=tab-photos&amp;group_ids=%(group_id)s&amp;etat=I">Photos</a>
</td>"""
% group
)
@ -775,9 +780,7 @@ REQUEST.URL0=%s<br/>
# -------------------------- INFOS SUR ETUDIANTS --------------------------
security.declareProtected(ScoView, "getEtudInfo")
def getEtudInfo(
self, etudid=False, code_nip=False, filled=False, REQUEST=None, format=None
):
def getEtudInfo(self, etudid=False, code_nip=False, filled=False, REQUEST=None, format=None):
"""infos sur un etudiant pour utilisation en Zope DTML
On peut specifier etudid
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
@ -1171,7 +1174,7 @@ REQUEST.URL0=%s<br/>
scolars.etud_annotations_delete(cnx, annotation_id)
return REQUEST.RESPONSE.redirect(
"ficheEtud?etudid=%s&head_message=Annotation%%20supprimée" % (etudid)
"ficheEtud?etudid=%s&amp;head_message=Annotation%%20supprimée" % (etudid)
)
security.declareProtected(ScoEtudChangeAdr, "formChangeCoordonnees")
@ -2773,7 +2776,7 @@ def _simple_error_page(context, msg, DeptId=None):
H = [context.standard_html_header(context), "<h2>Erreur !</h2>", "<p>", msg, "</p>"]
if DeptId:
H.append(
'<p><a href="delete_dept?DeptId=%s&force=1">Supprimer le dossier %s</a>(très recommandé !)</p>'
'<p><a href="delete_dept?DeptId=%s&amp;force=1">Supprimer le dossier %s</a>(très recommandé !)</p>'
% (DeptId, DeptId)
)
H.append(context.standard_html_footer(context))

View File

@ -25,6 +25,33 @@
#
##############################################################################
"""ScoDoc core package
"""
# from app.scodoc import sco_core
from ZScolar import ZScolar, manage_addZScolarForm, manage_addZScolar
from ZScoDoc import ZScoDoc, manage_addZScoDoc
# from sco_zope import *
# from notes_log import log
# log.set_log_directory( INSTANCE_HOME + '/log' )
__version__ = "1.0.0"
def initialize(context):
"""initialize the Scolar products"""
# called at each startup (context is a ProductContext instance, basically useless)
# --- ZScolars
context.registerClass(
ZScolar,
constructors=(
manage_addZScolarForm, # this is called when someone adds the product
manage_addZScolar,
),
icon="static/icons/sco_icon.png",
)
# --- ZScoDoc
context.registerClass(
ZScoDoc, constructors=(manage_addZScoDoc,), icon="static/icons/sco_icon.png"
)

View File

@ -1,105 +0,0 @@
# -*- coding: UTF-8 -*
# pylint: disable=invalid-name
import os
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler
from flask import request
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager()
login.login_view = "auth.login"
login.login_message = "Please log in to access this page."
mail = Mail()
bootstrap = Bootstrap(app)
moment = Moment()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix="/auth")
from app.views import essais_bp
app.register_blueprint(essais_bp, url_prefix="/Essais")
from app.views import scolar_bp
from app.views import notes_bp
from app.views import absences_bp
# https://scodoc.fr/ScoDoc/RT/Scolarite/...
app.register_blueprint(scolar_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite")
# https://scodoc.fr/ScoDoc/RT/Scolarite/Notes/...
app.register_blueprint(notes_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Notes")
# https://scodoc.fr/ScoDoc/RT/Scolarite/Absences/...
app.register_blueprint(
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
)
from app.main import bp as main_bp
app.register_blueprint(main_bp)
if not app.debug and not app.testing:
if app.config["MAIL_SERVER"]:
auth = None
if app.config["MAIL_USERNAME"] or app.config["MAIL_PASSWORD"]:
auth = (app.config["MAIL_USERNAME"], app.config["MAIL_PASSWORD"])
secure = None
if app.config["MAIL_USE_TLS"]:
secure = ()
mail_handler = SMTPHandler(
mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]),
fromaddr="no-reply@" + app.config["MAIL_SERVER"],
toaddrs=[app.config["ADMINS"]],
subject="ScoDoc8 Failure",
credentials=auth,
secure=secure,
)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not os.path.exists("logs"):
os.mkdir("logs")
file_handler = RotatingFileHandler(
"logs/scodoc.log", maxBytes=10240, backupCount=10
)
file_handler.setFormatter(
logging.Formatter(
"%(asctime)s %(levelname)s: %(message)s " "[in %(pathname)s:%(lineno)d]"
)
)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info("ScoDoc8 startup")
return app
# from app import models

View File

@ -1,6 +0,0 @@
# ScoDoc User Authentication Blueprint
Code borrowed and adapted from
https://courses.miguelgrinberg.com/p/flask-mega-tutorial

View File

@ -1,8 +0,0 @@
"""auth.__init__
"""
from flask import Blueprint
bp = Blueprint("auth", __name__)
from app.auth import routes

View File

@ -1,15 +0,0 @@
# -*- coding: UTF-8 -*
from flask import render_template, current_app
from flask_babel import _
from app.email import send_email
def send_password_reset_email(user):
token = user.get_reset_password_token()
send_email(
"[ScoDoc] Reset Your Password",
sender=current_app.config["ADMINS"][0],
recipients=[user.email],
text_body=render_template("email/reset_password.txt", user=user, token=token),
html_body=render_template("email/reset_password.html", user=user, token=token),
)

View File

@ -1,55 +0,0 @@
# -*- coding: UTF-8 -*
"""Formulaires authentification
TODO: à revoir complètement pour reprendre ZScoUsers et les pages d'authentification
"""
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.auth.models import User
_ = lambda x: x # sans babel
_l = _
class LoginForm(FlaskForm):
username = StringField(_l("Username"), validators=[DataRequired()])
password = PasswordField(_l("Password"), validators=[DataRequired()])
remember_me = BooleanField(_l("Remember Me"))
submit = SubmitField(_l("Sign In"))
class UserCreationForm(FlaskForm):
username = StringField(_l("Username"), validators=[DataRequired()])
email = StringField(_l("Email"), validators=[DataRequired(), Email()])
password = PasswordField(_l("Password"), validators=[DataRequired()])
password2 = PasswordField(
_l("Repeat Password"), validators=[DataRequired(), EqualTo("password")]
)
submit = SubmitField(_l("Register"))
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user is not None:
raise ValidationError(_("Please use a different username."))
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is not None:
raise ValidationError(_("Please use a different email address."))
class ResetPasswordRequestForm(FlaskForm):
email = StringField(_l("Email"), validators=[DataRequired(), Email()])
submit = SubmitField(_l("Request Password Reset"))
class ResetPasswordForm(FlaskForm):
password = PasswordField(_l("Password"), validators=[DataRequired()])
password2 = PasswordField(
_l("Repeat Password"), validators=[DataRequired(), EqualTo("password")]
)
submit = SubmitField(_l("Request Password Reset"))

View File

@ -1,262 +0,0 @@
# -*- coding: UTF-8 -*
"""Users and Roles models for ScoDoc
"""
import base64
from datetime import datetime, timedelta
from hashlib import md5
import json
import os
from time import time
from flask import current_app, url_for
from flask_login import UserMixin, AnonymousUserMixin
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
from app import db, login
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
class User(UserMixin, db.Model):
"""ScoDoc users, handled by Flask / SQLAlchemy"""
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
token = db.Column(db.String(32), index=True, unique=True)
token_expiration = db.Column(db.DateTime)
roles = db.relationship("Role", secondary="user_role", viewonly=True)
Permission = Permission
def __init__(self, **kwargs):
self.roles = []
super(User, self).__init__(**kwargs)
if (
not self.roles
and self.email
and self.email == current_app.config["SCODOC_ADMIN_MAIL"]
):
# super-admin
admin_role = Role.query.filter_by(name="Admin").first()
assert admin_role
self.add_role(admin_role, None)
db.session.commit()
current_app.logger.info("creating user with roles={}".format(self.roles))
def __repr__(self):
return "<User {}>".format(self.username)
def __str__(self):
return self.username
def set_password(self, password):
"Set password"
if password:
self.password_hash = generate_password_hash(password)
else:
self.password_hash = None
def check_password(self, password):
"""Check given password vs current one.
Returns `True` if the password matched, `False` otherwise.
"""
if not self.password_hash: # user without password can't login
return False
return check_password_hash(self.password_hash, password)
def get_reset_password_token(self, expires_in=600):
return jwt.encode(
{"reset_password": self.id, "exp": time() + expires_in},
current_app.config["SECRET_KEY"],
algorithm="HS256",
).decode("utf-8")
@staticmethod
def verify_reset_password_token(token):
try:
id = jwt.decode(
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
)["reset_password"]
except:
return
return User.query.get(id)
def to_dict(self, include_email=False):
data = {
"id": self.id,
"username": self.username,
"last_seen": self.last_seen.isoformat() + "Z",
"about_me": self.about_me,
}
if include_email:
data["email"] = self.email
return data
def from_dict(self, data, new_user=False):
for field in ["username", "email", "about_me"]:
if field in data:
setattr(self, field, data[field])
if new_user and "password" in data:
self.set_password(data["password"])
def get_token(self, expires_in=3600):
now = datetime.utcnow()
if self.token and self.token_expiration > now + timedelta(seconds=60):
return self.token
self.token = base64.b64encode(os.urandom(24)).decode("utf-8")
self.token_expiration = now + timedelta(seconds=expires_in)
db.session.add(self)
return self.token
def revoke_token(self):
self.token_expiration = datetime.utcnow() - timedelta(seconds=1)
@staticmethod
def check_token(token):
user = User.query.filter_by(token=token).first()
if user is None or user.token_expiration < datetime.utcnow():
return None
return user
# Permissions management:
def has_permission(self, perm, dept):
"""Check if user has permission `perm` in given `dept`.
Emulate Zope `has_permission``
Args:
perm: integer, one of the value defined in Permission class.
context:
"""
# les role liés à ce département, et les roles avec dept=None (super-admin)
roles_in_dept = (
UserRole.query.filter_by(user_id=self.id)
.filter((UserRole.dept == dept) | (UserRole.dept == None))
.all()
)
for user_role in roles_in_dept:
if user_role.role.has_permission(perm):
return True
return False
# Role management
def add_role(self, role, dept):
"""Add a role to this user.
:param role: Role to add.
"""
self.user_roles.append(UserRole(user=self, role=role, dept=dept))
def add_roles(self, roles, dept):
"""Add roles to this user.
:param roles: Roles to add.
"""
for role in roles:
self.add_role(role, dept)
def set_roles(self, roles, dept):
self.user_roles = [UserRole(user=self, role=r, dept=dept) for r in roles]
def get_roles(self):
for role in self.roles:
yield role
def is_administrator(self):
return self.has_permission(Permission.ScoSuperAdmin, None)
class AnonymousUser(AnonymousUserMixin):
def has_permission(self, perm, dept=None):
return False
def is_administrator(self):
return False
login.anonymous_user = AnonymousUser
class Role(db.Model):
"""Roles for ScoDoc"""
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.BigInteger) # 64 bits
users = db.relationship("User", secondary="user_role", viewonly=True)
# __table_args__ = (db.UniqueConstraint("name", "dept", name="_rolename_dept_uc"),)
def __init__(self, **kwargs):
super(Role, self).__init__(**kwargs)
if self.permissions is None:
self.permissions = 0
def __repr__(self):
return "<Role {} perm={:0{w}b}>".format(
self.name,
self.permissions & ((1 << Permission.NBITS) - 1),
w=Permission.NBITS,
)
def add_permission(self, perm):
self.permissions |= perm
def remove_permission(self, perm):
self.permissions = self.permissions & ~perm
def reset_permissions(self):
self.permissions = 0
def has_permission(self, perm):
return self.permissions & perm == perm
@staticmethod
def insert_roles():
"""Create default roles"""
default_role = "Observateur"
for r, permissions in SCO_ROLES_DEFAULTS.items():
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.reset_permissions()
for perm in permissions:
role.add_permission(perm)
role.default = role.name == default_role
db.session.add(role)
db.session.commit()
@staticmethod
def get_named_role(name):
"""Returns existing role with given name, or None."""
return Role.query.filter_by(name=name).first()
class UserRole(db.Model):
"""Associate user to role, in a dept.
If dept is None, the role applies to all departments (eg super admin).
"""
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
role_id = db.Column(db.Integer, db.ForeignKey("role.id"))
dept = db.Column(db.String(64))
user = db.relationship(
User, backref=db.backref("user_roles", cascade="all, delete-orphan")
)
role = db.relationship(
Role, backref=db.backref("user_roles", cascade="all, delete-orphan")
)
def __repr__(self):
return "<UserRole u={} r={} dept={}>".format(self.user, self.role, self.dept)
@login.user_loader
def load_user(id):
return User.query.get(int(id))

View File

@ -1,100 +0,0 @@
# -*- coding: UTF-8 -*
"""
auth.routes.py
"""
from flask import render_template, redirect, url_for, current_app, flash, request
from werkzeug.urls import url_parse
from flask_login import login_user, logout_user, current_user
from app import db
from app.auth import bp
from app.auth.forms import (
LoginForm,
UserCreationForm,
ResetPasswordRequestForm,
ResetPasswordForm,
)
from app.auth.models import User
from app.auth.email import send_password_reset_email
from app.decorators import scodoc7func, admin_required
_ = lambda x: x # sans babel
_l = _
@bp.route("/login", methods=["GET", "POST"])
def login():
if current_user.is_authenticated:
return redirect(url_for("main.index"))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash(_("Invalid username or password"))
return redirect(url_for("auth.login"))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get("next")
if not next_page or url_parse(next_page).netloc != "":
next_page = url_for("main.index")
return redirect(next_page)
return render_template("auth/login.html", title=_("Sign In"), form=form)
@bp.route("/logout")
def logout():
logout_user()
return redirect(url_for("main.index"))
@bp.route("/create_user", methods=["GET", "POST"])
@admin_required
def create_user():
"Form creating new user"
form = UserCreationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash("User {} created".format(user.username))
return redirect(url_for("main.index"))
return render_template(
"auth/register.html", title=u"Création utilisateur", form=form
)
@bp.route("/reset_password_request", methods=["GET", "POST"])
def reset_password_request():
if current_user.is_authenticated:
return redirect(url_for("main.index"))
form = ResetPasswordRequestForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user:
send_password_reset_email(user)
else:
current_app.logger.info(
"reset_password_request: for unkown user '{}'".format(form.email.data)
)
flash(_("Check your email for the instructions to reset your password"))
return redirect(url_for("auth.login"))
return render_template(
"auth/reset_password_request.html", title=_("Reset Password"), form=form
)
@bp.route("/reset_password/<token>", methods=["GET", "POST"])
def reset_password(token):
if current_user.is_authenticated:
return redirect(url_for("main.index"))
user = User.verify_reset_password_token(token)
if not user:
return redirect(url_for("main.index"))
form = ResetPasswordForm()
if form.validate_on_submit():
user.set_password(form.password.data)
db.session.commit()
flash(_("Your password has been reset."))
return redirect(url_for("auth.login"))
return render_template("auth/reset_password.html", form=form)

View File

@ -1,7 +0,0 @@
# -*- coding: UTF-8 -*
import os
import click
def register(app):
pass

View File

@ -1,206 +0,0 @@
# -*- coding: UTF-8 -*
"""Decorators for permissions, roles and ScoDoc7 Zope compatibility
"""
import functools
from functools import wraps
import inspect
import flask
from flask import g
from flask import abort, current_app
from flask import request
from flask_login import current_user
from flask_login import login_required
from flask import current_app
from werkzeug.exceptions import BadRequest
from app.auth.models import Permission
class ZUser(object):
"Emulating Zope User"
def __init__(self):
"create, based on `flask_login.current_user`"
self.username = current_user.username
def __str__(self):
return self.username
def has_permission(self, perm, context):
"""check if this user as the permission `perm`
in departement given by `g.scodoc_dept`.
"""
raise NotImplementedError()
class ZRequest(object):
"Emulating Zope 2 REQUEST"
def __init__(self):
self.URL = request.base_url
self.URL0 = self.URL
self.BASE0 = request.url_root
self.QUERY_STRING = request.query_string
self.REQUEST_METHOD = request.method
self.AUTHENTICATED_USER = current_user
if request.method == "POST":
self.form = request.form
if request.files:
# Add files in form: must copy to get a mutable version
# request.form is a werkzeug.datastructures.ImmutableMultiDict
self.form = self.form.copy()
self.form.update(request.files)
elif request.method == "GET":
self.form = request.args
self.RESPONSE = ZResponse()
def __str__(self):
return """REQUEST
URL={r.URL}
QUERY_STRING={r.QUERY_STRING}
REQUEST_METHOD={r.REQUEST_METHOD}
AUTHENTICATED_USER={r.AUTHENTICATED_USER}
form={r.form}
""".format(
r=self
)
class ZResponse(object):
"Emulating Zope 2 RESPONSE"
def __init__(self):
self.headers = {}
def redirect(self, url):
return flask.redirect(url) # http 302
def setHeader(self, header, value):
self.headers[header.tolower()] = value
def permission_required(permission):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if "scodoc_dept" in kwargs:
g.scodoc_dept = kwargs["scodoc_dept"]
del kwargs["scodoc_dept"]
current_app.logger.info(
"permission_required: %s in %s" % (permission, g.scodoc_dept)
)
if not current_user.has_permission(permission, g.scodoc_dept):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def admin_required(f):
return permission_required(Permission.ScoSuperAdmin)(f)
def scodoc7func(context):
"""Décorateur pour intégrer les fonctions Zope 2 de ScoDoc 7.
Si on a un kwarg `scodoc_dept`(venant de la route), le stocke dans `g.scodoc_dept`.
Ajoute l'argument REQUEST s'il est dans la signature de la fonction.
Les paramètres de la query string deviennent des (keywords) paramètres de la fonction.
"""
def s7_decorator(func):
@wraps(func)
def scodoc7func_decorator(*args, **kwargs):
"""Decorator allowing legacy Zope published methods to be called via Flask
routes without modification.
There are two cases: the function can be called
1. via a Flask route ("top level call")
2. or be called directly from Python.
If called via a route, this decorator setups a REQUEST object (emulating Zope2 REQUEST)
and `g.scodoc_dept` if present in the argument (for routes like `/<scodoc_dept>/Scolarite/sco_exemple`).
"""
assert not args
# Détermine si on est appelé via une route ("toplevel")
# ou par un appel de fonction python normal.
top_level = not hasattr(g, "zrequest")
if top_level:
g.zrequest = None
#
if "scodoc_dept" in kwargs:
g.scodoc_dept = kwargs["scodoc_dept"]
del kwargs["scodoc_dept"]
elif not hasattr(g, "scodoc_dept"): # if toplevel call
g.scodoc_dept = None
# --- Emulate Zope's REQUEST
REQUEST = ZRequest()
g.zrequest = REQUEST
req_args = REQUEST.form # args from query string (get) or form (post)
# --- Add positional arguments
pos_arg_values = []
# PY3 à remplacer par inspect.getfullargspec en py3:
argspec = inspect.getargspec(func)
current_app.logger.info("argspec=%s" % str(argspec))
nb_default_args = len(argspec.defaults) if argspec.defaults else 0
if nb_default_args:
arg_names = argspec.args[:-nb_default_args]
else:
arg_names = argspec.args
for arg_name in arg_names:
if arg_name == "REQUEST": # special case
pos_arg_values.append(REQUEST)
elif arg_name == "context":
pos_arg_values.append(context)
else:
pos_arg_values.append(req_args[arg_name])
current_app.logger.info("pos_arg_values=%s" % pos_arg_values)
# Add keyword arguments
if nb_default_args:
for arg_name in argspec.args[-nb_default_args:]:
if arg_name == "REQUEST": # special case
kwargs[arg_name] = REQUEST
elif arg_name in req_args:
# set argument kw optionnel
kwargs[arg_name] = req_args[arg_name]
current_app.logger.info(
"scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s"
% (top_level, pos_arg_values, kwargs)
)
value = func(*pos_arg_values, **kwargs)
if not top_level:
return value
else:
# Build response, adding collected http headers:
headers = []
kw = {"response": value, "status": 200}
if g.zrequest:
headers = g.zrequest.RESPONSE.headers
if not headers:
# no customized header, speedup:
return value
if "content-type" in headers:
kw["mimetype"] = headers["content-type"]
r = flask.Response(**kw)
for h in headers:
r.headers[h] = headers[h]
return r
return scodoc7func_decorator
return s7_decorator
# Le "context" de ScoDoc7
class ScoDoc7Context(object):
"""Context object for legacy Zope methods.
Mainly used to call published methods, as context.function(...)
"""
def __init__(self, globals_dict):
self.__dict__ = globals_dict
def __repr__(self):
return "ScoDoc7Context()"

View File

@ -1,19 +0,0 @@
# -*- coding: UTF-8 -*
from threading import Thread
from flask import current_app
from flask_mail import Message
from app import mail
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
Thread(
target=send_async_email, args=(current_app._get_current_object(), msg)
).start()

View File

@ -1,8 +0,0 @@
# main Blueprint
Quelques essais pour la migration.
TODO: Ne sera pas conservé.

View File

@ -1,6 +0,0 @@
# -*- coding: UTF-8 -*
from flask import Blueprint
bp = Blueprint("main", __name__)
from app.main import routes

View File

@ -1,145 +0,0 @@
# -*- coding: UTF-8 -*
import pprint
from pprint import pprint as pp
import functools
import thread # essai
from zipfile import ZipFile
from StringIO import StringIO
import flask
from flask import request, render_template, redirect
from flask_login import login_required
from app.main import bp
from app.decorators import scodoc7func, admin_required
context = None
@bp.route("/")
@bp.route("/index")
def index():
return render_template("main/index.html", title=u"Essai Flask")
@bp.route("/test_vue")
@login_required
def test_vue():
return """Vous avez vu. <a href="/">Retour à l'accueil</a>"""
def get_request_infos():
return [
"<p>request.base_url=%s</p>" % request.base_url,
"<p>request.url_root=%s</p>" % request.url_root,
"<p>request.query_string=%s</p>" % request.query_string,
]
D = {"count": 0}
# @app.route("/")
# @app.route("/index")
# def index():
# sleep(8)
# D["count"] = D.get("count", 0) + 1
# return "Hello, World! %s count=%s" % (thread.get_ident(), D["count"])
@bp.route("/zopefunction", methods=["POST", "GET"])
@login_required
@scodoc7func(context)
def a_zope_function(y, x="defaut", REQUEST=None):
"""Une fonction typique de ScoDoc7"""
H = get_request_infos() + [
"<p><b>x=<tt>%s</tt></b></p>" % x,
"<p><b>y=<tt>%s</tt></b></p>" % y,
"<p><b>URL=<tt>%s</tt></b></p>" % REQUEST.URL,
"<p><b>QUERY_STRING=<tt>%s</tt></b></p>" % REQUEST.QUERY_STRING,
"<p><b>AUTHENTICATED_USER=<tt>%s</tt></b></p>" % REQUEST.AUTHENTICATED_USER,
]
H.append("<p><b>form=<tt>%s</tt></b></p>" % REQUEST.form)
H.append("<p><b>form[x]=<tt>%s</tt></b></p>" % REQUEST.form.get("x", "non fourni"))
return "\n".join(H)
@bp.route("/zopeform_get")
@scodoc7func(context)
def a_zope_form_get(REQUEST=None):
H = [
"""<h2>Formulaire GET</h2>
<form action="%s" method="get">
x : <input type="text" name="x"/><br/>
y : <input type="text" name="y"/><br/>
fichier : <input type="file" name="fichier"/><br/>
<input type="submit" value="Envoyer"/>
</form>
"""
% flask.url_for("main.a_zope_function")
]
return "\n".join(H)
@bp.route("/zopeform_post")
@scodoc7func(context)
def a_zope_form_post(REQUEST=None):
H = [
"""<h2>Formulaire POST</h2>
<form action="%s" method="post" enctype="multipart/form-data">
x : <input type="text" name="x"/><br/>
y : <input type="text" name="y"/><br/>
fichier : <input type="file" name="fichier"/><br/>
<input type="submit" value="Envoyer"/>
</form>
"""
% flask.url_for("main.a_zope_function")
]
return "\n".join(H)
@bp.route("/ScoDoc/<dept_id>/Scolarite/Notes/formsemestre_status")
@scodoc7func(context)
def formsemestre_status(dept_id=None, formsemestre_id=None, REQUEST=None):
"""Essai méthode de département
Le contrôle d'accès doit vérifier les bons rôles : ici Ens<dept_id>
"""
return u"""dept_id=%s , formsemestre_id=%s <a href="/">Retour à l'accueil</a>""" % (
dept_id,
formsemestre_id,
)
@bp.route("/hello/world")
def hello():
H = get_request_infos() + [
"<p>Hello, World! %s count=%s</p>" % (thread.get_ident(), D["count"]),
]
# print(pprint.pformat(dir(request)))
return "\n".join(H)
@bp.route("/getzip")
def getzip():
"""Essai renvoi d'un ZIP en Flask"""
# La version Zope:
# REQUEST.RESPONSE.setHeader("content-type", "application/zip")
# REQUEST.RESPONSE.setHeader("content-length", size)
# REQUEST.RESPONSE.setHeader(
# "content-disposition", 'attachement; filename="monzip.zip"'
# )
zipdata = StringIO()
zipfile = ZipFile(zipdata, "w")
zipfile.writestr("fichier1", "un contenu")
zipfile.writestr("fichier2", "deux contenus")
zipfile.close()
data = zipdata.getvalue()
size = len(data)
# open("/tmp/toto.zip", "w").write(data)
# Flask response:
r = flask.Response(response=data, status=200, mimetype="application/zip")
r.headers["Content-Type"] = "application/zip"
r.headers["content-length"] = size
r.headers["content-disposition"] = 'attachement; filename="monzip.zip"'
return r

View File

@ -1,7 +0,0 @@
# -*- coding: UTF-8 -*
"""ScoDoc8 models
"""
# None, at this point
# see auth.models for user/role related models

View File

@ -1,109 +0,0 @@
# -*- 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)',
}

View File

@ -1,43 +0,0 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""Chargement de la configuration locale
"""
import os
import sys
from notes_log import log
import sco_config
# scodoc_local defines a CONFIG object
# here we check if there is a local config file
def load_local_configuration(scodoc_cfg_dir):
"""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

View File

@ -1,19 +0,0 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""essai: ceci serait un module scodoc/sco_xxx.py
"""
import types
import sco_utils as scu
def sco_get_version(context, REQUEST=None):
"""Une fonction typique de ScoDoc7"""
return """<html><body><p>%s</p></body></html>""" % scu.SCOVERSION
def test_refactor(context, x=1):
x = context.toto()
y = ("context=" + context.module_is_locked("alpha")) + "23"

View File

@ -1,58 +0,0 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""Definition of ScoDoc 8 permissions
used by auth
"""
# Définition des permissions: ne pas changer les numéros ou l'ordre des lignes !
_SCO_PERMISSIONS = (
# permission bit, symbol, description
# ScoSuperAdmin est utilisé pour:
# - ZScoDoc: add/delete departments
# - tous rôles lors creation utilisateurs
(1 << 1, "ScoSuperAdmin", "Super Administrateur"),
(1 << 2, "ScoView", "Voir"),
(1 << 3, "ScoEnsView", "Voir les parties pour les enseignants"),
(1 << 4, "ScoObservateur", "Observer (accès lecture restreint aux bulletins)"),
(1 << 5, "ScoUsersAdmin", "Gérer les utilisateurs"),
(1 << 6, "ScoUsersView", "Voir les utilisateurs"),
(1 << 7, "ScoChangePreferences", "Modifier les préférences"),
(1 << 8, "ScoChangeFormation", "Changer les formations"),
(1 << 9, "ScoEditFormationTags", "Tagguer les formations"),
(1 << 10, "ScoEditAllNotes", "Modifier toutes les notes"),
(1 << 11, "ScoEditAllEvals", "Modifier toutes les evaluations"),
(1 << 12, "ScoImplement", "Mettre en place une formation (créer un semestre)"),
(1 << 13, "ScoAbsChange", "Saisir des absences"),
(1 << 14, "ScoAbsAddBillet", "Saisir des billets d'absences"),
# changer adresse/photo ou pour envoyer bulletins par mail ou pour debouche
(1 << 15, "ScoEtudChangeAdr", "Changer les addresses d'étudiants"),
(1 << 16, "ScoEtudChangeGroups", "Modifier les groupes"),
# aussi pour demissions, diplomes:
(1 << 17, "ScoEtudInscrit", "Inscrire des étudiants"),
# aussi pour archives:
(1 << 18, "ScoEtudAddAnnotations", "Éditer les annotations"),
(1 << 19, "ScoEntrepriseView", "Voir la section 'entreprises'"),
(1 << 20, "ScoEntrepriseChange", "Modifier les entreprises"),
(1 << 21, "ScoEditPVJury", "Éditer les PV de jury"),
# ajouter maquettes Apogee (=> chef dept et secr):
(1 << 22, "ScoEditApo", "Ajouter des maquettes Apogées"),
)
class Permission:
"Permissions for ScoDoc"
NBITS = 1 # maximum bits used (for formatting)
ALL_PERMISSIONS = [-1]
description = {} # { symbol : blah blah }
@staticmethod
def init_permissions():
for (perm, symbol, description) in _SCO_PERMISSIONS:
setattr(Permission, symbol, perm)
# Crée aussi les attributs dans le module (ScoDoc7 compat)
globals()[symbol] = perm
Permission.description[symbol] = description
Permission.NBITS = len(_SCO_PERMISSIONS)
Permission.init_permissions()

View File

@ -1,58 +0,0 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""Definition of ScoDoc default roles
"""
from sco_permissions import Permission as p
SCO_ROLES_DEFAULTS = {
"Observateur": (p.ScoObservateur,),
"Ens": (
p.ScoObservateur,
p.ScoView,
p.ScoEnsView,
p.ScoUsersView,
p.ScoEtudAddAnnotations,
p.ScoAbsChange,
p.ScoAbsAddBillet,
p.ScoEntrepriseView,
),
"Secr": (
p.ScoObservateur,
p.ScoView,
p.ScoUsersView,
p.ScoEtudAddAnnotations,
p.ScoAbsChange,
p.ScoAbsAddBillet,
p.ScoEntrepriseView,
p.ScoEntrepriseChange,
p.ScoEtudChangeAdr,
),
# Admin est le chef du département, pas le "super admin"
# on dit donc lister toutes ses permissions:
"Admin": (
p.ScoObservateur,
p.ScoView,
p.ScoEnsView,
p.ScoUsersView,
p.ScoEtudAddAnnotations,
p.ScoAbsChange,
p.ScoAbsAddBillet,
p.ScoEntrepriseView,
p.ScoEntrepriseChange,
p.ScoEtudChangeAdr,
p.ScoChangeFormation,
p.ScoEditFormationTags,
p.ScoEditAllNotes,
p.ScoEditAllEvals,
p.ScoImplement,
p.ScoEtudChangeGroups,
p.ScoEtudInscrit,
p.ScoUsersAdmin,
p.ScoChangePreferences,
),
# Super Admin est un root: création/suppression de départements
# _tous_ les droits
"SuperAdmin": p.ALL_PERMISSIONS,
}

View File

@ -1,15 +0,0 @@
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Sign In</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
<br>
Forgot Your Password?
<a href="{{ url_for('auth.reset_password_request') }}">Click to Reset It</a>
</p>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Création utilisateur</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Reset Your Password</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Reset Password</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -1,60 +0,0 @@
{% extends 'bootstrap/base.html' %}
{% block title %}
{% if title %}{{ title }} - ScoDoc{% else %}Welcome to ScoDoc{% endif %}
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ url_for('main.index') }}">ScoDoc</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('main.index') }}">Home</a></li>
<li><a href="{{ url_for('main.hello') }}">Hello</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_anonymous %}
<li><a href="{{ url_for('auth.login') }}">Login</a></li>
{% else %}
<li>{{current_user.username}}</li>
<li><a href="{{ url_for('auth.logout') }}">Logout</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock %}
{% block content %}
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-info" role="alert">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{# application content needs to be provided in the app_content block #}
{% block app_content %}{% endblock %}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.lang(g.locale) }}
<script>
</script>
{% endblock %}

View File

@ -1,16 +0,0 @@
<p>Bonjour {{ user.username }},</p>
<p>
Pour réinitialiser votre mot de passe ScoDoc,
<a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
cliquez sur ce lien
</a>.
</p>
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web::</p>
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
<p>Si vous n'avez pas demandé à réinitialiser votre mot de passe sur
ScoDoc, vous pouvez simplement ignorer ce message.
</p>
<p>A bientôt !</p>

View File

@ -1,12 +0,0 @@
Bonjour {{ user.username }},
Pour réinitialiser votre mot de passe ScoDoc, suivre le lien:
{{ url_for('auth.reset_password', token=token, _external=True) }}
Si vous n'avez pas demandé à réinitialiser votre mot de passe sur
ScoDoc, vous pouvez simplement ignorer ce message.
A bientôt !

View File

@ -1,35 +0,0 @@
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Protoype ScoDoc 8: accueil</h1>
<div class="row">
<h2>Avec login requis</h2>
<ul>
<li><a href="{{ url_for('main.test_vue') }}"><tt>test_vue</tt>, login requis</a></li>
<li><a href="{{ url_for('main.a_zope_function', y=22) }}">a_zope_function</a> : affichage objets "Zope"</li>
<li><a href="{{ url_for('main.a_zope_function', x=11, y=22) }}">a_zope_function?x=22</a> : avec parametre
<tt>x=11, y=22</tt>
</li>
</ul>
<h2>Sans login</h2>
<ul>
<li><a href="{{ url_for('main.a_zope_form_get') }}"><tt>a_zope_form_get</tt></a> : formulaire GET ala ScoDoc non
protégé renvoyant vers une page protégée</li>
<li><a href="{{ url_for('main.a_zope_form_post') }}"><tt>a_zope_form_post</tt></a> : formulaire POST ala ScoDoc
non protégé renvoyant vers une page protégée</li>
<li><a href="{{ url_for('main.formsemestre_status', dept_id='RT') }}"><tt>formsemestre_status</tt></a> : a-t-on
le rôle EnsRT ?
</li>
<li><a href="ScoDoc/RT/Scolarite/Notes/essai"><tt>Notes/essai</tt> : test "notes", url
manuelle</a></li>
<li><a href="{{ url_for('notes.essai2' , scodoc_dept='RT') }}"><tt>Notes/essai2</tt></a> : permission
ScoImplement dans RT
</li>
</ul>
</div>
{% endblock %}

View File

@ -1,11 +0,0 @@
# -*- coding: UTF-8 -*
"""ScoDoc Flask views
"""
from flask import Blueprint
scolar_bp = Blueprint("scolar", __name__)
notes_bp = Blueprint("notes", __name__)
absences_bp = Blueprint("absences", __name__)
essais_bp = Blueprint("essais", __name__)
from app.views import notes, scolar, absences

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +0,0 @@
# -*- coding: UTF-8 -*
"""
Module Essais: divers essais pour la migration vers Flask
Emmanuel Viennet, 2021
"""
from flask import g
from flask import current_app
from app.decorators import (
scodoc7func,
ScoDoc7Context,
permission_required,
admin_required,
login_required,
)
from app.auth.models import Permission
from app.views import notes_bp as bp
# import sco_core deviendra:
from app.scodoc import sco_core
context = ScoDoc7Context(globals())
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple")
@scodoc7func(context)
def sco_exemple(etudid="NON"):
"""Un exemple de fonction ScoDoc 7"""
return """<html>
<body><h1>ScoDoc 7 rules !</h1>
<p>etudid=%(etudid)s</p>
<p>g.scodoc_dept=%(scodoc_dept)s</p>
</body>
</html>
""" % {
"etudid": etudid,
"scodoc_dept": g.scodoc_dept,
}
# En ScoDoc 7, on a souvent des vues qui en appellent d'autres
# avec context.sco_exemple( etudid="E12" )
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple2")
@login_required
@scodoc7func(context)
def sco_exemple2():
return "Exemple 2" + context.sco_exemple(etudid="deux")
# Test avec un seul argument REQUEST positionnel
@bp.route("/<scodoc_dept>/Scolarite/sco_get_version")
@scodoc7func(context)
def sco_get_version(REQUEST):
return sco_core.sco_get_version(REQUEST)
# Fonction ressemblant à une méthode Zope protégée
@bp.route("/<scodoc_dept>/Scolarite/sco_test_view")
@scodoc7func(context)
@permission_required(Permission.ScoView)
def sco_test_view(REQUEST=None):
return """Vous avez vu sco_test_view !"""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -45,16 +45,9 @@ def bonus_iutv(notes_sport, coefs, infos=None):
return bonus
def bonus_direct(notes_sport, coefs, infos=None):
"""Un bonus direct et sans chichis: les points sont directement ajoutés à la moyenne générale.
Les coefficients sont ignorés: tous les points de bonus sont sommés.
(rappel: la note est ramenée sur 20 avant application).
"""
return sum(notes_sport)
def bonus_iut_stdenis(notes_sport, coefs, infos=None):
"""Semblable à bonus_iutv mais sans coefficients et total limité à 0.5 points."""
"""Semblable à bonus_iutv mais sans coefficients et total limité à 0.5 points.
"""
points = sum([x - 10 for x in notes_sport if x > 10]) # points au dessus de 10
bonus = points * 0.05 # ou / 20
return min(bonus, 0.5) # bonus limité à 1/2 point
@ -69,7 +62,7 @@ def bonus_colmar(notes_sport, coefs, infos=None):
sur 20 obtenus dans chacune des matières optionnelles sont cumulés
dans la limite de 10 points. 5% de ces points cumulés s'ajoutent à
la moyenne générale du semestre déjà obtenue par l'étudiant.
"""
# les coefs sont ignorés
points = sum([x - 10 for x in notes_sport if x > 10])
@ -80,7 +73,7 @@ def bonus_colmar(notes_sport, coefs, infos=None):
def bonus_iutva(notes_sport, coefs, infos=None):
"""Calcul bonus modules optionels (sport, culture), règle IUT Ville d'Avray
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
de l'Université Paris 10 (C2I) non rattachés à une unité d'enseignement.
Si la note est >= 10 et < 12, bonus de 0.1 point
@ -100,13 +93,42 @@ def bonus_iutva(notes_sport, coefs, infos=None):
return 0
# XXX Inutilisé (mai 2020) ? à confirmer avant suppression XXX
# def bonus_iut1grenoble_v0(notes_sport, coefs, infos=None):
# """Calcul bonus sport IUT Grenoble sur la moyenne générale
#
# La note de sport de nos étudiants va de 0 à 5 points.
# Chaque point correspond à un % qui augmente la moyenne de chaque UE et la moyenne générale.
# Par exemple : note de sport 2/5 : chaque UE sera augmentée de 2%, ainsi que la moyenne générale.
#
# Calcul ici du bonus sur moyenne générale et moyennes d'UE non capitalisées.
# """
# # les coefs sont ignorés
# # notes de 0 à 5
# points = sum([x for x in notes_sport])
# factor = (points / 4.0) / 100.0
# bonus = infos["moy"] * factor
# # Modifie les moyennes de toutes les UE:
# for ue_id in infos["moy_ues"]:
# ue_status = infos["moy_ues"][ue_id]
# if ue_status["sum_coefs"] > 0:
# # modifie moyenne UE ds semestre courant
# ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * (1.0 + factor)
# if not ue_status["is_capitalized"]:
# # si non capitalisee, modifie moyenne prise en compte
# ue_status["moy"] = ue_status["cur_moy_ue"]
#
# # open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' )
# return bonus
def bonus_iut1grenoble_2017(notes_sport, coefs, infos=None):
"""Calcul bonus sport IUT Grenoble sur la moyenne générale (version 2017)
La note de sport de nos étudiants va de 0 à 5 points.
La note de sport de nos étudiants va de 0 à 5 points.
Chaque point correspond à un % qui augmente la moyenne de chaque UE et la moyenne générale.
Par exemple : note de sport 2/5 : la moyenne générale sera augmentée de 2%.
Calcul ici du bonus sur moyenne générale
"""
# les coefs sont ignorés
@ -140,14 +162,14 @@ def bonus_lille(notes_sport, coefs, infos=None):
def bonus_iutlh(notes_sport, coefs, infos=None):
"""Calcul bonus sport IUT du Havre sur moyenne générale et UE
La note de sport de nos étudiants va de 0 à 20 points.
m2=m1*(1+0.005*((10-N1)+(10-N2))
m2 : Nouvelle moyenne de l'unité d'enseignement si note de sport et/ou de langue supérieure à 10
m1 : moyenne de l'unité d'enseignement avant bonification
N1 : note de sport si supérieure à 10
N2 : note de seconde langue si supérieure à 10
Par exemple : sport 15/20 et langue 12/20 : chaque UE sera multipliée par 1+0.005*7, ainsi que la moyenne générale.
Calcul ici de la moyenne générale et moyennes d'UE non capitalisées.
La note de sport de nos étudiants va de 0 à 20 points.
m2=m1*(1+0.005*((10-N1)+(10-N2))
m2 : Nouvelle moyenne de l'unité d'enseignement si note de sport et/ou de langue supérieure à 10
m1 : moyenne de l'unité d'enseignement avant bonification
N1 : note de sport si supérieure à 10
N2 : note de seconde langue si supérieure à 10
Par exemple : sport 15/20 et langue 12/20 : chaque UE sera multipliée par 1+0.005*7, ainsi que la moyenne générale.
Calcul ici de la moyenne générale et moyennes d'UE non capitalisées.
"""
# les coefs sont ignorés
points = sum([x - 10 for x in notes_sport if x > 10])
@ -183,8 +205,8 @@ def bonus_tours(notes_sport, coefs, infos=None):
def bonus_iutr(notes_sport, coefs, infos=None):
"""Calcul du bonus , regle de l'IUT de Roanne (contribuée par Raphael C., nov 2012)
Le bonus est compris entre 0 et 0.35 point.
cette procédure modifie la moyenne de chaque UE capitalisable.
Le bonus est compris entre 0 et 0.35 point.
cette procédure modifie la moyenne de chaque UE capitalisable.
"""
# modifie les moyennes de toutes les UE:
@ -238,7 +260,7 @@ def bonus_saint_etienne(notes_sport, coefs, infos=None):
"""IUT de Saint-Etienne (jan 2014)
Nous avons différents types de bonification
bonfication Sport / Associations
coopératives de département / Bureau Des Étudiants
coopératives de département / Bureau Des Étudiants
/ engagement citoyen / Langues optionnelles
Nous ajoutons sur le bulletin une bonification qui varie entre 0,1 et 0,3 ou 0,35 pour chaque item
la bonification totale ne doit pas excéder les 0,6 point.
@ -256,9 +278,9 @@ def bonus_saint_etienne(notes_sport, coefs, infos=None):
def bonus_iutTarbes(notes_sport, coefs, infos=None):
"""Calcul bonus modules optionnels
"""Calcul bonus modules optionnels
(sport, Langues, action sociale, Théâtre), règle IUT Tarbes
Les coefficients ne sont pas pris en compte,
Les coefficients ne sont pas pris en compte,
seule la meilleure note est prise en compte
le 1/30ème des points au-dessus de 10 sur 20 est retenu et s'ajoute à
la moyenne générale du semestre déjà obtenue par l'étudiant.
@ -386,7 +408,7 @@ def bonus_iutbethune(notes_sport, coefs, infos=None):
def bonus_demo(notes_sport, coefs, infos=None):
"""Fausse fonction "bonus" pour afficher les informations disponibles
et aider les développeurs.
Les informations sont placées dans le fichier /tmp/scodoc_bonus.log
Les informations sont placées dans le fichier /tmp/scodoc_bonus.log
qui est ECRASE à chaque appel.
*** Ne pas utiliser en production !!! ***
"""

View File

@ -1,43 +0,0 @@
# -*- coding: UTF-8 -*
import os
from dotenv import load_dotenv
BASEDIR = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(BASEDIR, ".env"))
class ConfigClass(object):
"""General configuration. Mostly loaded from environment via .env"""
SECRET_KEY = os.environ.get("SECRET_KEY") or "un-grand-secret-introuvable"
SQLALCHEMY_DATABASE_URI = (
os.environ.get("USERS_DATABASE_URI")
or "postgresql://scodoc@localhost:5432/SCO8USERS"
)
SQLALCHEMY_TRACK_MODIFICATIONS = False
LOG_TO_STDOUT = os.environ.get("LOG_TO_STDOUT")
MAIL_SERVER = os.environ.get("MAIL_SERVER")
MAIL_PORT = int(os.environ.get("MAIL_PORT") or 25)
MAIL_USE_TLS = os.environ.get("MAIL_USE_TLS") is not None
MAIL_USERNAME = os.environ.get("MAIL_USERNAME")
MAIL_PASSWORD = os.environ.get("MAIL_PASSWORD")
LANGUAGES = ["fr", "en"] # unused for now
SCODOC_ADMIN_MAIL = os.environ.get("SCODOC_ADMIN_MAIL")
SCODOC_ADMIN_LOGIN = os.environ.get("SCODOC_ADMIN_LOGIN") or "admin"
ADMINS = [SCODOC_ADMIN_MAIL]
SCODOC_ERR_MAIL = os.environ.get("SCODOC_ERR_MAIL")
BOOTSTRAP_SERVE_LOCAL = os.environ.get("BOOTSTRAP_SERVE_LOCAL")
# for ScoDoc 7 compat (à changer)
INSTANCE_HOME = os.environ.get("INSTANCE_HOME", "/opt/scodoc")
# For legacy ScoDoc7 installs: postgresql user
SCODOC7_SQL_USER = os.environ.get("SCODOC7_SQL_USER", "www-data")
DEFAULT_SQL_PORT = os.environ.get("DEFAULT_SQL_PORT", "5432")
def __init__(self):
"""Used to build some config variable at startup time"""
self.SCODOC_VAR_DIR = os.path.join(self.INSTANCE_HOME, "var", "scodoc")
Config = ConfigClass()

View File

@ -34,11 +34,9 @@ export POSTGRES_USER=www-data
if [ "${debian_version}" = "10" ]
then
PSQL=/usr/lib/postgresql/11/bin/psql
export POSTGRES_SERVICE="postgresql@11-main.service"
elif [ "${debian_version}" = "9" ]
then
PSQL=/usr/lib/postgresql/9.6/bin/psql
export POSTGRES_SERVICE="postgresql"
elif [ "${debian_version}" = "8" ]
then
PSQL=/usr/lib/postgresql/9.4/bin/psql
@ -54,7 +52,7 @@ elif [ "${debian_version}" = "5" ]
else
PSQL=/usr/lib/postgresql/8.1/bin/psql
fi
export PSQL
# tcp port for SQL server (under Debian 4, 5432 or 5433 for 8.1 if 7.4 also installed !)
# Important note: if changed, you should probably also change it in

View File

@ -5,16 +5,10 @@
source config.sh
source utils.sh
check_uid_root "$0"
check_uid_root $0
echo 'Installation du demarrage automatique de ScoDoc (systemd)'
# La variable POSTGRES_SERVICE doit être positionnée dans config.sh
# suivant la version de Debian et de postgresql
[ -z "${POSTGRES_SERVICE}" ] && die "incompatible Debian version"
cat "$SCODOC_DIR/config/etc/scodoc.service" | sed 's/{{postgresql}}/'"${POSTGRES_SERVICE}"'/g' > /etc/systemd/system/scodoc.service
cp $SCODOC_DIR/config/etc/scodoc.service /etc/systemd/system
systemctl enable scodoc.service
echo "A partir de maintenant, utiliser"

View File

@ -11,6 +11,6 @@ source utils.sh
echo 'Creating postgresql database'
# ---
echo 'Creating postgresql database ' "$db_name"
createdb -E UTF-8 -p "$POSTGRES_PORT" -O "$POSTGRES_USER" "$db_name"
echo 'Creating postgresql database ' $db_name
createdb -E UTF-8 -p $POSTGRES_PORT -O $POSTGRES_USER $db_name

View File

@ -12,7 +12,7 @@
source config.sh
source utils.sh
check_uid_root "$0"
check_uid_root $0
# --- Ensure postgres user www-data exists
init_postgres_user
@ -21,8 +21,8 @@ db_name=SCOUSERS
echo 'Creating postgresql database ' $db_name
su -c "createdb -E UTF-8 -O $POSTGRES_USER -p $POSTGRES_PORT $db_name" "$POSTGRES_SUPERUSER"
su -c "createdb -E UTF-8 -O $POSTGRES_USER -p $POSTGRES_PORT $db_name" $POSTGRES_SUPERUSER
echo 'Initializing tables in database ' "$db_name"
echo su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" "$POSTGRES_USER"
su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" "$POSTGRES_USER"
echo 'Initializing tables in database ' $db_name
echo su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" $POSTGRES_USER
su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" $POSTGRES_USER

View File

@ -6,7 +6,7 @@
# Avec option:
# -a : sauve aussi les bases de données
#
DEST_ADDRESS=emmanuel.viennet@gmail.com
DEST_ADDRESS=emmanuel.viennet@univ-paris13.fr
INSTANCE_DIR=/opt/scodoc
@ -36,7 +36,7 @@ while getopts ":d:aunh" opt; do
SEND_BY_MAIL=0
;;
d)
DEPTS_TO_SAVE=$( join_by ' ' "$DEPTS_TO_SAVE" "$OPTARG" )
DEPTS_TO_SAVE=$( join_by ' ' $DEPTS_TO_SAVE $OPTARG )
;;
h)
echo "Diagnostic installation ScoDoc"
@ -71,7 +71,7 @@ then
apt-get install sharutils
fi
mkdir "$TMP"
mkdir $TMP
# Files to copy:
FILES="/etc/hosts /etc/debian_version /etc/apt /etc/apache2"
@ -91,33 +91,33 @@ echo "left in ${TMP}"
# -------------------------------------
copy_log() {
if [ -e "$1" ]
if [ -e $1 ]
then
cp "$1" "$TMP"/scodoc_logs/
cp $1 $TMP/scodoc_logs/
fi
}
mkdir "$TMP"/scodoc_logs/
copy_log /opt/scodoc/log/event.log
copy_log /opt/scodoc/log/event.log.1
copy_log /opt/scodoc/log/notes.log
copy_log /opt/scodoc/log/notes.log.1
mkdir $TMP/scodoc_logs/
copy_log /opt/scodoc/instance/log/event.log
copy_log /opt/scodoc/instance/log/event.log.1
copy_log /opt/scodoc/instance/log/notes.log
copy_log /opt/scodoc/instance/log/notes.log.1
# -------------------------------------
# Linux System Configuration
# -------------------------------------
iptables -L > "$TMP"/iptables.out
ip a > "$TMP"/ifconfig.out
ps auxww > "$TMP"/ps.out
df -h > "$TMP"/df.out
dpkg -l > "$TMP"/dpkg.lst
iptables -L > $TMP/iptables.out
ip a > $TMP/ifconfig.out
ps auxww > $TMP/ps.out
df -h > $TMP/df.out
dpkg -l > $TMP/dpkg.lst
(cd /opt/scodoc/Products/ScoDoc; svn status > "$TMP"/svn.status)
(cd /opt/scodoc/Products/ScoDoc; svn diff > "$TMP"/svn.diff)
(cd /opt/scodoc/instance/Products/ScoDoc; svn status > $TMP/svn.status)
(cd /opt/scodoc/instance/Products/ScoDoc; svn diff > $TMP/svn.diff)
(cd /opt/scodoc/Products/ScoDoc; svnversion > "$TMP"/svn.version)
ls -laR /opt/scodoc/Products/ScoDoc > "$TMP"/ls-laR
(cd /opt/scodoc/instance/Products/ScoDoc; svnversion > $TMP/svn.version)
ls -laR /opt/scodoc/instance/Products/ScoDoc > $TMP/ls-laR
# -------------------------------------
@ -126,7 +126,7 @@ ls -laR /opt/scodoc/Products/ScoDoc > "$TMP"/ls-laR
(su postgres -c "psql -l") > "${TMP}/psql-l.out"
for dept in "${INSTANCE_DIR}"/var/scodoc/config/depts/*.cfg
do
cnx=$(cat "$dept")
cnx=$(cat $dept)
(su postgres -c "echo '\dt' | psql -d $cnx") > "${TMP}/psql-$(basename ${dept%%.*}).out"
done
@ -137,14 +137,14 @@ done
# copy files:
for f in $FILES
do
cp -R "$f" "$TMP"
cp -R $f $TMP
done
# -------------------------------------
# Optionally save dept(s) database(s)
# -------------------------------------
DEPTS_TO_SAVE=$(echo "${DEPTS_TO_SAVE}" | tr ' ' '\n' | sort | uniq)
DEPTS_TO_SAVE=$(echo ${DEPTS_TO_SAVE} | tr ' ' '\n' | sort | uniq)
# Dump database of a dept (eg "RT")
function dump_dept_db {
@ -176,10 +176,10 @@ fi
# Archive all stuff to /tmp
# -------------------------------------
tar cfz "$TMP".tgz "$TMP"
tar cfz $TMP.tgz $TMP
echo
echo "Fichier de diagnostic: "$TMP".tgz"
echo "Fichier de diagnostic: $TMP.tgz"
echo
# If no mail, stop here
@ -197,9 +197,9 @@ fi
#requires: basename,date,md5sum,sed,sendmail,uuencode
function fappend {
echo "$2">>"$1";
echo "$2">>$1;
}
YYYYMMDD=$(date +%Y%m%d)
YYYYMMDD=`date +%Y%m%d`
# CHANGE THESE
TOEMAIL=$DEST_ADDRESS
@ -212,45 +212,45 @@ MIMETYPE="application/gnutar" #if not sure, use http://www.webmaster-toolkit.com
# DON'T CHANGE ANYTHING BELOW
TMP="/tmp/tmpfil_123"$RANDOM;
BOUNDARY=$(date +%s|md5sum)
BOUNDARY=`date +%s|md5sum`
BOUNDARY=${BOUNDARY:0:32}
FILENAME=$(basename "$ATTACHMENT")
FILENAME=`basename $ATTACHMENT`
rm -rf "$TMP"
uuencode --base64 "$FILENAME" < "$ATTACHMENT" >"$TMP"
sed -i -e '1,1d' -e '$d' "$TMP"; #removes first & last lines from "$TMP"
DATA=$(cat "$TMP")
rm -rf $TMP;
cat $ATTACHMENT|uuencode --base64 $FILENAME>$TMP;
sed -i -e '1,1d' -e '$d' $TMP;#removes first & last lines from $TMP
DATA=`cat $TMP`
rm -rf "$TMP";
fappend "$TMP" "From: $FREMAIL";
fappend "$TMP" "To: $TOEMAIL";
fappend "$TMP" "Reply-To: $FREMAIL";
fappend "$TMP" "Subject: $SUBJECT";
fappend "$TMP" "Content-Type: multipart/mixed; boundary=\""$BOUNDARY"\"";
fappend "$TMP" "";
fappend "$TMP" "This is a MIME formatted message. If you see this text it means that your";
fappend "$TMP" "email software does not support MIME formatted messages.";
fappend "$TMP" "";
fappend "$TMP" "--$BOUNDARY";
fappend "$TMP" "Content-Type: text/plain; charset=ISO-8859-1; format=flowed";
fappend "$TMP" "Content-Transfer-Encoding: 7bit";
fappend "$TMP" "Content-Disposition: inline";
fappend "$TMP" "";
fappend "$TMP" "$MSGBODY";
fappend "$TMP" "";
fappend "$TMP" "";
fappend "$TMP" "--$BOUNDARY";
fappend "$TMP" "Content-Type: $MIMETYPE; name=\"$FILENAME\"";
fappend "$TMP" "Content-Transfer-Encoding: base64";
fappend "$TMP" "Content-Disposition: attachment; filename=\"$FILENAME\";";
fappend "$TMP" "";
fappend "$TMP" "$DATA";
fappend "$TMP" "";
fappend "$TMP" "";
fappend "$TMP" "--$BOUNDARY--";
fappend "$TMP" "";
fappend "$TMP" "";
#cat "$TMP">out.txt
cat "$TMP"|sendmail -t -f none@example.com;
rm "$TMP";
rm -rf $TMP;
fappend $TMP "From: $FREMAIL";
fappend $TMP "To: $TOEMAIL";
fappend $TMP "Reply-To: $FREMAIL";
fappend $TMP "Subject: $SUBJECT";
fappend $TMP "Content-Type: multipart/mixed; boundary=\""$BOUNDARY"\"";
fappend $TMP "";
fappend $TMP "This is a MIME formatted message. If you see this text it means that your";
fappend $TMP "email software does not support MIME formatted messages.";
fappend $TMP "";
fappend $TMP "--$BOUNDARY";
fappend $TMP "Content-Type: text/plain; charset=ISO-8859-1; format=flowed";
fappend $TMP "Content-Transfer-Encoding: 7bit";
fappend $TMP "Content-Disposition: inline";
fappend $TMP "";
fappend $TMP "$MSGBODY";
fappend $TMP "";
fappend $TMP "";
fappend $TMP "--$BOUNDARY";
fappend $TMP "Content-Type: $MIMETYPE; name=\"$FILENAME\"";
fappend $TMP "Content-Transfer-Encoding: base64";
fappend $TMP "Content-Disposition: attachment; filename=\"$FILENAME\";";
fappend $TMP "";
fappend $TMP "$DATA";
fappend $TMP "";
fappend $TMP "";
fappend $TMP "--$BOUNDARY--";
fappend $TMP "";
fappend $TMP "";
#cat $TMP>out.txt
cat $TMP|sendmail -t -f none@example.com;
rm $TMP;

View File

@ -15,15 +15,15 @@ then
fi
echo "Changing to directory " "$SCODOC_DIR"/config
cd "$SCODOC_DIR"/config || { echo "directory does not exist"; exit 1; }
echo "Changing to directory " $SCODOC_DIR/config
cd $SCODOC_DIR/config
echo "Stopping ScoDoc..."
scodocctl stop
# DROITS
echo -n "Verification des droits: proprietaire www-data ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo 'changing owner to www-data'
@ -31,7 +31,7 @@ then
fi
echo -n 'Suppression des backups des sources (*~) ? (y/n) [y] '
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
/bin/rm -f ../*~ ../*/*~
@ -40,7 +40,7 @@ fi
# SVN
echo -n "svn update ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo 'Updating from SVN...'
@ -50,7 +50,7 @@ fi
# DEPARTEMENTS (maintenant inutile car dans /var)
echo -n "Supprimer les (anciennes) configs de departements ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo "moving " depts/*.cfg "to /tmp"
@ -59,7 +59,7 @@ fi
# .../var/
echo -n "Supprimer et recréer .../var (archives, photos, configs, ...) ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo "moving ../../../var/scodoc to /tmp"
@ -73,7 +73,7 @@ fi
# LOGS ZOPE
echo -n "Effacer les logs de Zope et ScoDoc ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
(cd ../../../log/; ./purge)
@ -81,7 +81,7 @@ fi
# IMAGE Data.fs
echo -n "Recopier le Data.fs original ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo "moving Data.fs to /tmp"

View File

@ -1,15 +1,13 @@
# ScoDoc7 service
# Zope based
# Depends on {{postgresql}} (replaced by installation script by
# postgresql@11-main.service on Debian 10
# postgresql on Debian <= 9
# => is restarted when {{postgresql}} restarts
# Depends on postgresql
# => is restarted when postgresql restarts
#
[Unit]
Description=ScoDoc 7 service
After=network.target {{postgresql}}
Requires={{postgresql}}
PartOf={{postgresql}}
After=network.target postgresql@11-main.service
Requires=postgresql@11-main.service
PartOf=postgresql@11-main.service
StartLimitIntervalSec=0
[Service]
@ -23,4 +21,4 @@ ExecStop=/opt/scodoc/bin/zopectl stop
ExecReload=/opt/scodoc/bin/zopectl restart
[Install]
WantedBy={{postgresql}}
WantedBy=postgresql@11-main.service

View File

@ -1,50 +0,0 @@
#!/bin/bash
# Get version information
# Use VERSION.py, last commit, diff, and last upstream commit date
source config.sh
source utils.sh
# Source code version:
x=$(grep SCOVERSION ../VERSION.py) || terminate "can't access VERSION.py" 1
x=${x#*\"}
src_version=${x%\"*}
# last commit
git_last_commit_hash=$(git log -1 --format=%h)
git_last_commit_date=$(git log -1 --format=%ci)
git_up_commit_hash=$(git log -1 --format=%h origin/ScoDoc8)
git_up_commit_date=$(git log -1 --format=%ci origin/ScoDoc8)
# Check if git has local changes
nchanges=$(git status --porcelain | grep -c -v '^??')
if [ "$nchanges" -gt 0 ]
then
has_local_changes="yes"
else
has_local_changes="no"
fi
# Synthetic one-line version:
sco_version="$src_version ($git_up_commit_hash) $git_up_commit_date"
if [ "$has_local_changes" = "yes" ]
then
sco_version="$sco_version (modified)"
fi
#
if [ "$1" = "-s" ]
then
echo "$sco_version"
else
echo src_version: "$src_version"
echo git_last_commit_hash: "$git_last_commit_hash"
echo git_last_commit_date: "$git_last_commit_date"
echo git_up_commit_hash: "$git_up_commit_hash"
echo git_up_commit_date: "$git_up_commit_date"
echo has_local_diffs: "$has_local_changes"
echo sco_version: "$sco_version"
fi

View File

@ -8,16 +8,15 @@
source config.sh
source utils.sh
if [ "$(id -nu)" != "$POSTGRES_USER" ]
if [ $(id -nu) != $POSTGRES_USER ]
then
echo "$0: script must be runned as user $POSTGRES_USER"
exit 1
fi
# shellcheck disable=SC2154
echo 'Initializing tables in database ' "$db_name"
$PSQL -U "$POSTGRES_USER" -p "$POSTGRES_PORT" "$db_name" -f "$SCODOC_DIR"/misc/createtables.sql
echo 'Initializing tables in database ' $db_name
$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name -f $SCODOC_DIR/misc/createtables.sql
# Set DeptName in preferences:
echo "insert into sco_prefs (name, value) values ('DeptName', '"${DEPT}\'\) | $PSQL -U "$POSTGRES_USER" -p "$POSTGRES_PORT" "$db_name"
echo "insert into sco_prefs (name, value) values ('DeptName', '"${DEPT}\'\) | $PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name

View File

@ -2,10 +2,9 @@
#
# ScoDoc: install third-party software necessary for our installation
# starting for a minimal Debian (Buster, 10.0) install.
# starting for a minimal Debian (Stretch, 9.0) install.
#
# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013, Mar 2017, Jul 2017,
# Jun 2019, Oct 2019, Dec 2020
# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013, Mar 2017, Jul 2017, Jun 2019, Oct 2019
#
source config.sh
@ -13,14 +12,16 @@ source utils.sh
check_uid_root $0
PYTHON=/opt/zope213/bin/python
# ------------ Safety checks
if [ "${debian_version}" != "10" ]
if [ ${debian_version} != "10" ]
then
echo "Version du systeme Linux Debian incompatible"
exit 1
fi
if [ "$(arch)" != "x86_64" ]
if [ $(arch) != "x86_64" ]
then
echo "Version du systeme Linux Debian incompatible (pas X86 64 bits)"
exit 1
@ -28,16 +29,14 @@ fi
# ------------ Permissions & directories
# source dir should be writable by scodoc to write bytecode files
chgrp www-data "$SCODOC_DIR" "$SCODOC_DIR"/ZopeProducts/*
chmod g+w "$SCODOC_DIR" "$SCODOC_DIR"/ZopeProducts/*
chgrp www-data $SCODOC_DIR $SCODOC_DIR/ZopeProducts/*
chmod g+w $SCODOC_DIR $SCODOC_DIR/ZopeProducts/*
if [ -d "${SCODOC_VAR_DIR}"/photos ]; then
chgrp -R www-data "${SCODOC_VAR_DIR}"/photos
chmod -R g+w "${SCODOC_VAR_DIR}"/photos
fi
chgrp -R www-data "${SCODOC_VAR_DIR}"/photos
chmod -R g+w "${SCODOC_VAR_DIR}"/photos
if [ ! -e "${SCODOC_VERSION_DIR}" ]; then
mkdir -p "${SCODOC_VERSION_DIR}"
mkdir "${SCODOC_VERSION_DIR}"
chown www-data.www-data "${SCODOC_VERSION_DIR}"
fi
@ -54,8 +53,8 @@ fi
for locname in en_US.UTF-8 en_US.ISO-8859-15 en_US.ISO-8859-1
do
outname=$(echo ${locname//-/} | tr 'A-Z' 'a-z')
if [ "$(locale -a | grep -E -i ^${outname}$ | wc -l)" -lt 1 ]
outname=$(echo ${locname//-/} | tr '[A-Z]' '[a-z]')
if [ $(locale -a | egrep -i ^${outname}$ | wc -l) -lt 1 ]
then
echo adding $locname
echo "$locname ${locname##*.}" >> /etc/locale.gen
@ -87,7 +86,7 @@ apt-get -y install postgresql
apt-get -y install graphviz
# ------------ INSTALL DES EXTENSIONS PYTHON (2.7)
# XXX to fix: pip in our env
apt-get -y install python-docutils
apt-get -y install python-jaxml
apt-get -y install python-psycopg2
@ -100,15 +99,15 @@ apt-get -y install python-requests
# ------------
SVNVERSION=$(cd ..; svnversion)
SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn="$SVNVERSION")
echo "$SVERSION" > "${SCODOC_VERSION_DIR}/scodoc.sn"
SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn=$SVNVERSION)
echo $SVERSION > "${SCODOC_VERSION_DIR}/scodoc.sn"
# ------------ POSTFIX
echo
echo "ScoDoc a besoin de pouvoir envoyer des messages par mail."
echo -n "Voulez vous configurer la messagerie (tres recommande) ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
apt-get -y install postfix
@ -118,7 +117,7 @@ fi
echo
echo "Le firewall aide a proteger votre serveur d'intrusions indesirables."
echo -n "Voulez vous configurer un firewall minimal (ufw) ? (y/n) [n] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" = 'Y' ]
then
echo 'Installation du firewall IP ufw (voir documentation Debian)'
@ -143,16 +142,16 @@ a2enmod rewrite
echo
echo "La configuration du serveur web va modifier votre installation Apache pour supporter ScoDoc."
echo -n "Voulez vous configurer le serveur web Apache maintenant (tres conseille) ? (y/n) [y] "
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
echo "Configuration d'Apache"
server_name=""
while [ -z "$server_name" ]
while [ -z $server_name ]
do
echo "Le nom de votre serveur doit normalement etre connu dans le DNS."
echo -n "Nom complet de votre serveur (exemple: notes.univ.fr): "
read -r server_name
read server_name
done
# --- CERTIFICATS AUTO-SIGNES
echo
@ -160,7 +159,7 @@ then
echo "auto-signes, qui ne seront pas reconnus comme de confiance"
echo "par les navigateurs, mais offrent une certaine securite."
echo -n 'Voulez vous generer des certificats ssl auto-signes ? (y/n) [y] '
read -r ans
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
# attention: utilise dans scodoc-site-ssl.orig
@ -176,7 +175,7 @@ then
fi
# ---
echo 'generation de /etc/apache2/sites-available/scodoc-site-ssl'
cat "$SCODOC_DIR"/config/etc/scodoc-site-ssl-apache2.4.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl.conf
cat $SCODOC_DIR/config/etc/scodoc-site-ssl-apache2.4.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl.conf
echo 'activation du site...'
a2ensite scodoc-site-ssl
@ -186,7 +185,7 @@ then
then
mv $fn $fn.bak
fi
cp "$SCODOC_DIR"/config/etc/scodoc-site.orig $fn
cp $SCODOC_DIR/config/etc/scodoc-site.orig $fn
if [ -z "$(grep Listen /etc/apache2/ports.conf | grep 443)" ]
then
@ -219,7 +218,9 @@ read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
# ScoDoc 7.19+ uses systemd
$SCODOC_DIR/config/configure_systemd.sh
echo 'Installation du demarrage automatique de ScoDoc (systemd)'
cp $SCODOC_DIR/config/etc/scodoc.service /etc/systemd/system
systemctl enable scodoc.service
fi
@ -229,8 +230,8 @@ echo -n "Mises a jour hebdomadaires (tres recommande) ? (y/n) [y] "
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
cp "$SCODOC_DIR"/config/etc/scodoc-updater.service /etc/systemd/system
cp "$SCODOC_DIR"/config/etc/scodoc-updater.timer /etc/systemd/system
cp $SCODOC_DIR/config/etc/scodoc-updater.service /etc/systemd/system
cp $SCODOC_DIR/config/etc/scodoc-updater.timer /etc/systemd/system
systemctl enable scodoc-updater.timer
systemctl start scodoc-updater.timer
fi

View File

@ -8,7 +8,7 @@ PG_DUMPFILE=$1
# Check locale of installation. If invalid, reinitialize all system
is_latin1=$(psql -l | grep postgres | grep iso88591 | wc -l)
if [ "$is_latin1" -gt 1 ]
if [ $is_latin1 -gt 1 ]
then
echo "Recreating postgres cluster using UTF-8"
@ -20,8 +20,8 @@ fi
# Drop all current ScoDoc databases, if any:
for f in $(psql -l --no-align --field-separator . | grep SCO | cut -f 1 -d.); do
echo dropping "$f"
dropdb "$f"
echo dropping $f
dropdb $f
done
echo "Restoring postgres data..."
psql -f "$PG_DUMPFILE" postgres

View File

@ -10,8 +10,6 @@
#
source utils.sh
check_uid_root "$0"
# Destination directory
if [ ! $# -eq 1 ]
then
@ -36,12 +34,9 @@ fi
INSTANCE_DIR=/opt/scodoc
SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc"
<<<<<<< HEAD
=======
source utils.sh
check_uid_root "$0"
>>>>>>> e2a2b0f0836fc6de922c35b77b236379783e7590
echo "Stopping ScoDoc..."
scodocctl stop
@ -51,16 +46,12 @@ chown postgres "$DEST"
su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres
if [ ! "$?" -eq 0 ]
then
<<<<<<< HEAD
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting.\n"
=======
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting."
>>>>>>> e2a2b0f0836fc6de922c35b77b236379783e7590
exit 1
fi
chown root "$DEST"
# Zope DB, ScoDoc archives, configuration, photos, etc.
# Zope DB and ScoDoc archives:
echo "Copying var/ ..."
cp -rp "$INSTANCE_DIR/var" "$DEST"

View File

@ -1,128 +0,0 @@
# -*- 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)',
# }

View File

@ -4,7 +4,7 @@
# ScoDoc: reglage du mot de passe admin Zope
# (in Zope terminology, an emergency user)
#
# Doit <EFBFBD>tre lanc<6E> par l'utilisateur unix root dans le repertoire .../config
# Doit être lancé par l'utilisateur unix root dans le repertoire .../config
# ^^^^^^^^^^^^^^^^^^^^^
# E. Viennet, Juin 2008, Jul 2019
#
@ -22,14 +22,14 @@ fi
echo "Creation d'un utilisateur d'urgence pour ScoDoc"
echo "(utile en cas de perte de votre mot de passe admin)"
if [ "${debian_version}" != "10" ]
if [ ${debian_version} != "10" ]
then
mdir=/opt/zope213/lib/python2.7/site-packages/Zope2-2.13.21-py2.7.egg/Zope2/utilities
else
mdir=/opt/zope213/lib/python2.7/site-packages/Zope2/utilities
fi
python $mdir/zpasswd.py "$SCODOC_DIR"/../../access
python $mdir/zpasswd.py $SCODOC_DIR/../../access
echo
echo "redemarrer scodoc pour prendre en compte le mot de passe"

View File

@ -1,16 +1,16 @@
#!/bin/bash
# Upgrade ScoDoc installation using GIT
# GIT must be properly configured and have read access to ScoDoc repository
# Upgrade ScoDoc installation using SVN
# SVN must be properly configured and have read access to ScoDoc repository
# This script STOP and RESTART ScoDoc and should be runned as root
#
# Upgrade also the Linux system using apt.
#
# Script for ScoDoc 8 (10)
# Script for ScoDoc 7 (Debian 7, 8, 9, 10)
#
# E. Viennet, sep 2013, mar 2017, jun 2019, aug 2020, dec 2020
# E. Viennet, sep 2013, mar 2017, jun 2019, aug 2020
cd /opt/scodoc/Products/ScoDoc/config || { echo "Invalid directory"; exit 1; }
cd /opt/scodoc/Products/ScoDoc/config
source config.sh
source utils.sh
@ -32,8 +32,8 @@ fi
scodocctl stop
echo
echo "Using git to update $SCODOC_DIR..."
(cd "$SCODOC_DIR"; git checkout ScoDoc8; git pull origin master)
echo "Using SVN to update $SCODOC_DIR..."
(cd "$SCODOC_DIR"; svn update)
SVNVERSION=$(cd ..; svnversion)
@ -119,19 +119,6 @@ if [ $? -ne 0 ]
then
/opt/zope213/bin/pip install requests
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
# upgrade old dateutil (check version manually to speedup)
v=$(/opt/zope213/bin/python -c "import dateutil; print dateutil.__version__")

View File

@ -1,17 +1,17 @@
#!/bin/bash
# Misc utilities for ScoDoc install shell scripts
to_lower() {
echo "$1" | tr "[:upper:]" "[:lower:]"
echo $1 | tr "[:upper:]" "[:lower:]"
}
to_upper() {
echo "$1" | tr "[:lower:]" "[:upper:]"
echo $1 | tr "[:lower:]" "[:upper:]"
}
norm_ans() {
x=$(to_upper "$1" | tr O Y)
echo "${x:0:1}"
x=$(to_upper $1 | tr O Y)
echo ${x:0:1}
}
check_uid_root() {
@ -23,11 +23,10 @@ check_uid_root() {
}
terminate() {
status=${2:-1} # default: exit 1
echo
echo "Erreur: $1"
echo
exit $status
exit 1
}
# Start/stop scodoc, using sysv or systemd
@ -45,7 +44,7 @@ scodocctl() {
systemctl $1 scodoc
else
echo "(using legacy SystemV)"
/etc/init.d/scodoc "$1"
/etc/init.d/scodoc $1
fi
}
@ -55,7 +54,7 @@ init_postgres_user() { # run as root
then
# add database user
echo "Creating postgresql user $POSTGRES_USER"
su -c "createuser -p $POSTGRES_PORT --no-superuser --no-createdb --no-adduser --no-createrole ${POSTGRES_USER}" "$POSTGRES_SUPERUSER"
su -c "createuser -p $POSTGRES_PORT --no-superuser --no-createdb --no-adduser --no-createrole ${POSTGRES_USER}" $POSTGRES_SUPERUSER
fi
}
@ -70,8 +69,8 @@ gen_passwd() {
password=""
while [ "$n" -le "$PASSWORD_LENGTH" ]
do
password="$password${ALLOWABLE_ASCII:$((RANDOM%${#ALLOWABLE_ASCII})):1}"
n=$((n+1))
password="$password${ALLOWABLE_ASCII:$(($RANDOM%${#ALLOWABLE_ASCII})):1}"
n=$((n+1))
done
echo "$password"
echo $password
}

View File

@ -67,11 +67,7 @@ def go(app, n=0, verbose=True):
def go_dept(app, dept, verbose=True):
objs = app.ScoDoc.objectValues("Folder")
for o in objs:
try:
context = o.Scolarite
except AttributeError:
# ignore other folders, like old "icons"
continue
context = o.Scolarite
if context.DeptId() == dept:
if verbose:
print("context in dept ", context.DeptId())

49
dtml/docLogin.dtml Normal file
View File

@ -0,0 +1,49 @@
<dtml-var standard_html_header>
<center>
<dtml-if authFailedCode>
<dtml-call "REQUEST.set('loginTitle', getAuthFailedMessage(authFailedCode))">
<dtml-else>
<dtml-call "REQUEST.set('loginTitle', 'Identifiez vous sur ScoDoc')">
</dtml-if>
<dtml-var "DialogHeader(_.None,_,DialogTitle=loginTitle)">
<P>
<dtml-if destination>
<FORM ACTION="&dtml-destination;" METHOD="POST">
<dtml-else>
<FORM ACTION="&dtml-URL;" METHOD="POST">
</dtml-if>
<dtml-var "query_string_to_form_inputs(QUERY_STRING)"> <dtml-comment> Added by Emmanuel for ScoDoc</dtml-comment>
<TABLE>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG><dtml-babel src="'en'">Nom</dtml-babel></STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="TEXT" NAME="__ac_name" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG><dtml-babel src="'en'">Mot de passe</dtml-babel></STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="PASSWORD" NAME="__ac_password" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
</TD>
</TR>
</TABLE>
<center>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE=" <dtml-babel src="'en'">Ok</dtml-babel> ">
</center>
</FORM>
<br>
<dtml-var DialogFooter>
</center>
<dtml-var standard_html_footer>

16
dtml/docLogout.dtml Normal file
View File

@ -0,0 +1,16 @@
<dtml-var standard_html_header>
<P>
<CENTER>
<p>Vous êtes déconnecté de ScoDoc.
</p>
<p><a href="<dtml-var "BASE0">">revenir à l'accueil</a></p>
<br/>
<p><em style="color: red;">(Attention: si vous êtes administrateur, vous ne pouvez vous déconnecter complètement qu'en relançant votre navigateur)
</em></p>
</CENTER>
<dtml-var standard_html_footer>

View File

@ -0,0 +1,49 @@
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Notes',
help_product='ZNotes',
help_topic='ZNotes-add.stx'
)">
<div class="form-help">
<p>
Notes Objects are very usefull thus not documented yet...
</p>
</div>
<form name="form" action="manage_addZNotes"><br>
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<div class="form-element">
<input class="form-element"
type="submit"
name="submit"
value=" Add "
/>
</div>
</form>
<dtml-var manage_page_header>

View File

@ -0,0 +1,62 @@
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add ZScolar',
help_product='ZScolar',
help_topic='ZScolar-add.stx'
)">
<div class="form-help">
<p>
ZScolar: gestion scolarite d'un departement
</p>
</div>
<form name="form" action="manage_addZScolar"><br>
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
DB connexion string
</div>
</td>
<td align="left" valign="top">
<input type="text" name="db_cnx_string" size="80" value="user=zopeuser dbname=SCOGTR password=XXXX host=localhost" />
</td>
</tr>
<div class="form-element">
<input class="form-element"
type="submit"
name="submit"
value=" Add "
/>
</div>
</form>
<dtml-var manage_page_header>

View File

@ -0,0 +1,21 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-var "manage_form_title(this(), _,
form_title='Edit ZNotes',
help_product='ZNotes',
help_topic='ZNotes-edit.stx'
)">
<form name="form" action="manage_editAction" method="post"><br/>
id: <dtml-var id><br/>
title: <input type="text" name="title:string" size="30" value="<dtml-var title>"><br/>
<div class="form-element">
<input class="form-element" type="submit" value="Save Changes">
</div>
</form>
<dtml-var manage_page_footer>

View File

@ -0,0 +1,21 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-var "manage_form_title(this(), _,
form_title='Edit ZScolar',
help_product='ZScolar',
help_topic='ZScolar-edit.stx'
)">
<form name="form" action="manage_editAction" method="post"><br/>
id: <dtml-var id><br/>
title: <input type="text" name="title:string" size="30" value="<dtml-var title>"><br/>
<div class="form-element">
<input class="form-element" type="submit" value="Save Changes">
</div>
</form>
<dtml-var manage_page_footer>

View File

@ -445,14 +445,14 @@ class GenTable:
if self.base_url:
if self.xls_link:
H.append(
' <a href="%s&format=xls">%s</a>'
' <a href="%s&amp;format=xls">%s</a>'
% (self.base_url, scu.ICON_XLS)
)
if self.xls_link and self.pdf_link:
H.append("&nbsp;&nbsp;")
if self.pdf_link:
H.append(
' <a href="%s&format=pdf">%s</a>'
' <a href="%s&amp;format=pdf">%s</a>'
% (self.base_url, scu.ICON_PDF)
)
H.append("</p>")

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Check usage of (published) ScoDoc methods
Quick method: just grep method name in all constant strings from the source code base.
Usage:
check_zope_usage.py publishedmethods.csv string-constants.txt
publishedmethods.csv : fichier texte, module et un nom de méthode / ligne
ZScoUsers get_userlist
comme extrait par zopelistmethods.py
string-constants.txt : les constantes chaines, extraites par extract_code_strings.py
"scolars.py" "</li><li>"
E. Viennet 2021-01-09
"""
from __future__ import print_function
import sys
import glob
methods_filename = sys.argv[1]
constants_filename = sys.argv[2]
with open(methods_filename) as f:
# module, method_name, signature
methods = [l.strip().split("\t") for l in f]
print("%d methods" % len(methods))
with open(constants_filename) as f:
constants = [l[:-1].split("\t")[1] for l in f]
print("%d constants" % len(constants))
# Add JavaScripts
jss = []
for fn in glob.glob("static/js/*.js"):
jss.append(open(fn).read())
print("%d javascripts" % len(jss))
L = []
for method in methods:
n = 0
for c in constants:
if method[1] in c:
n += 1
nj = 0
for js in jss:
if method[1] in js:
nj += 1
L.append(method + [n, nj])
# Sort by decreasing popularity
L.sort(key=lambda x: (x[-1] + x[-2], x[1]), reverse=True)
print("\n".join(["%s\t%s\t%s\t%d\t%d" % tuple(l) for l in L]))

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Extract all string litterals from our code base.
Useful to check if an API function is used in a generated web page !
Usage:
extract_code_strings.py source.py ... > string-constants.txt
Résultat utilisé par check_zope_usage.py
E. Viennet 2021-01-09
"""
from __future__ import print_function
import sys
import ast
import types
# L = []
for srcfilename in sys.argv[1:]:
# print("processing %s" % srcfilename, file=sys.stderr)
with open(srcfilename) as f:
p = ast.parse(f.read())
# L.extend(x.s.strip() for x in ast.walk(p) if x.__class__ == ast.Str)
for x in ast.walk(p):
if x.__class__ == ast.Str:
if type(x.s) == types.StringType:
s = x.s
else:
s = x.s.encode("UTF-8")
# remove tabs and cr
s = s.translate(None, "\t\n")
if len(s):
print("%s\t%s" % (srcfilename, s))
# L = sorted(set(L)) # uniq | sort
# print("\n".join(L))

View File

@ -3,10 +3,11 @@
"""List Zope published methods (helps redesign ScoDoc's API).
Usage:
scotests/scointeractive.sh RT misc/zopelistmethods.py
Launch ScoDoc as follows: (as root)
(replace RT by an existing departement id)
/opt/scodoc/bin/zopectl debug
Then run this file
E. Viennet 2020-01-26
"""
@ -22,8 +23,6 @@ from ZAbsences import ZAbsences
from ZScoUsers import ZScoUsers
from ZEntreprises import ZEntreprises
RESFILENAME = "publishedmethods.csv"
def get_methods_description(klass):
D = klass.__dict__
@ -64,6 +63,4 @@ for module_name in published_by_module:
print("Total: \t ", N)
print("Writing %s" % RESFILENAME)
with open(RESFILENAME, "w") as f:
f.write("\n".join(["\t".join(l) for l in lines]))
open("publishedmethods.csv", "w").write("\n".join(["\t".join(l) for l in lines]))

View File

@ -8,10 +8,6 @@ import re
import inspect
import time
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
from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
MIMEMultipart,

View File

@ -462,11 +462,11 @@ def get_templates_from_distrib(template="avis"):
if template in ["avis", "footer"]:
# pas de preference pour le template: utilise fichier du serveur
p = os.path.join(scu.SCO_SRC_DIR, pe_local_tmpl)
p = os.path.join(scu.SCO_SRCDIR, pe_local_tmpl)
if os.path.exists(p):
template_latex = get_code_latex_from_modele(p)
else:
p = os.path.join(scu.SCO_SRC_DIR, pe_default_tmpl)
p = os.path.join(scu.SCO_SRCDIR, pe_default_tmpl)
if os.path.exists(p):
template_latex = get_code_latex_from_modele(p)
else:

View File

@ -177,7 +177,7 @@ def add_pe_stuff_to_zip(context, zipfile, ziproot):
Also copy logos
"""
PE_AUX_DIR = os.path.join(scu.SCO_SRC_DIR, "config/doc_poursuites_etudes")
PE_AUX_DIR = os.path.join(scu.SCO_SRCDIR, "config/doc_poursuites_etudes")
distrib_dir = os.path.join(PE_AUX_DIR, "distrib")
distrib_pathnames = list_directory_filenames(
distrib_dir

View File

@ -1,107 +0,0 @@
# -*- coding: UTF-8 -*
"""Outil pour migration ScoDoc 7 => ScoDoc 8
Pour chaque module dans views:
- construire la liste des fonctions définies dans ce module:
get_module_functions
Pour chaque module dans views et dans scodoc:
- remplacer context.xxx par app.views.M.xxx
M est le module de views définissant xxx
Si xxx n'est pas trouvé, erreur !
"""
from __future__ import print_function
import re
from pprint import pprint as pp
import sys
import types
import click
import flask
import app
from app import create_app, cli, db
from app.auth.models import User, Role, UserRole
from config import Config
from app.views import notes
TYPES_TO_SCAN = {
types.FunctionType,
# types.ClassType,
# types.DictionaryType,
# types.FloatType,
# types.IntType,
# types.ListType,
# types.StringType,
# types.TupleType,
}
def get_module_symbols(module):
"""returns list of symbols (functions and constants) defined in the given module"""
return [
f.__name__
for f in [getattr(module, name) for name in dir(module)]
if (type(f) in TYPES_TO_SCAN)
and ((type(f) != types.FunctionType) or (f.__module__ == module.__name__))
]
# print("\n".join(f.__name__ for f in get_module_functions(notes)))
def scan_views_symbols():
"""Scan modules in app.views and returns
{ }
"""
views_modules = [
getattr(app.views, mod_name)
for mod_name in dir(app.views)
if type(getattr(app.views, mod_name)) == types.ModuleType
]
sym2mod = {} # symbole_name : module
for module in views_modules:
start = "app.views."
assert module.__name__.startswith(start)
module_name = module.__name__[len(start) :]
symbols = set(get_module_symbols(module))
print("%d symbols defined in %s" % (len(symbols), module))
dups = symbols.intersection(sym2mod)
if len(dups):
print("duplicated symbols !")
for dup in dups:
print("%s:\t%s\t%s" % (dup, sym2mod[dup], module_name))
sym2mod.update({s: module_name for s in symbols})
return sym2mod
def replace_context_calls(sourcefilename, sym2mod):
undefined_list = [] # noms de fonctions non présents dans les modules "views"
def repl(m):
funcname = m.group(1)
module = sym2mod.get(funcname, False)
if module:
return module + "." + funcname
else:
undefined_list.append((sourcefilename, funcname))
return m.group(0) # leave unchanged
print("reading %s" % sourcefilename)
source = open(sourcefilename).read()
exp = re.compile(r"context\.([a-zA-Z0-9_]+)")
source2 = exp.sub(repl, source)
return source2, undefined_list
sym2mod = scan_views_symbols()
source2, undefined_list = replace_context_calls("app/scodoc/sco_core.py", sym2mod)

View File

@ -1,46 +0,0 @@
alembic==1.5.5
attrdict==2.0.1
Babel==2.9.0
blinker==1.4
certifi==2021.5.30
chardet==4.0.0
click==7.1.2
dnspython==1.16.0
dominate==2.6.0
email-validator==1.1.2
Flask==1.1.4
Flask-Babel==2.0.0
Flask-Bootstrap==3.3.7.1
Flask-Login==0.5.0
Flask-Mail==0.9.1
Flask-Migrate==2.7.0
Flask-Moment==0.11.0
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
icalendar==4.0.7
idna==2.10
itsdangerous==1.1.0
jaxml==3.2
Jinja2==2.11.2
Mako==1.1.4
MarkupSafe==1.1.1
Pillow==6.2.2
pkg-resources==0.0.0
psycopg2==2.8.6
pyExcelerator==0.6.3a0
PyJWT==1.7.1
PyRSS2Gen==1.1
python-dateutil==2.8.1
python-dotenv==0.15.0
python-editor==1.0.4
pytz==2021.1
reportlab==3.5.59
requests==2.25.1
six==1.15.0
SQLAlchemy==1.3.23
stripogram==1.5
typing==3.7.4.3
urllib3==1.26.5
visitor==0.1.3
Werkzeug==1.0.1
WTForms==2.3.3

View File

@ -32,11 +32,13 @@
Il suffit d'appeler abs_notify() après chaque ajout d'absence.
"""
import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
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
import notesdb as ndb
import sco_utils as scu

Some files were not shown because too many files have changed in this diff Show More