forked from ScoDoc/ScoDoc
Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into ScoDoc8
Update to rel. 1997.
This commit is contained in:
commit
e61a9752d3
@ -8,13 +8,14 @@ SCONAME = "ScoDoc"
|
|||||||
SCONEWS = """
|
SCONEWS = """
|
||||||
<h4>Année 2021</h4>
|
<h4>Année 2021</h4>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Évaluations de type "deuxième session"</li>
|
||||||
<li>Gestion du genre neutre (pas d'affichage de la civilité)</li>
|
<li>Gestion du genre neutre (pas d'affichage de la civilité)</li>
|
||||||
<li>Diverses corrections (PV de jurys, ...)</li>
|
<li>Diverses corrections (PV de jurys, ...)</li>
|
||||||
<li>Modernisation du code Python</li>
|
<li>Modernisation du code Python</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4>Année 2020</h4>
|
<h4>Année 2020</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Corrections d'erreurs, améliorations saise absences< et affichage bulletins</li>
|
<li>Corrections d'erreurs, améliorations saisie absences et affichage bulletins</li>
|
||||||
<li>Nouveau site <a href="https://scodoc.org">scodoc.org</a> pour la documentation</li>
|
<li>Nouveau site <a href="https://scodoc.org">scodoc.org</a> pour la documentation</li>
|
||||||
<li>Enregistrement de semestres extérieurs</li>
|
<li>Enregistrement de semestres extérieurs</li>
|
||||||
<li>Améliorations PV de Jury</li>
|
<li>Améliorations PV de Jury</li>
|
||||||
|
117
ZAbsences.py
117
ZAbsences.py
@ -67,6 +67,7 @@ from sco_permissions import ScoAbsAddBillet, ScoAbsChange, ScoView
|
|||||||
from sco_exceptions import ScoValueError, ScoInvalidDateError
|
from sco_exceptions import ScoValueError, ScoInvalidDateError
|
||||||
from TrivialFormulator import TrivialFormulator, TF
|
from TrivialFormulator import TrivialFormulator, TF
|
||||||
from gen_tables import GenTable
|
from gen_tables import GenTable
|
||||||
|
import html_sco_header
|
||||||
import scolars
|
import scolars
|
||||||
import sco_formsemestre
|
import sco_formsemestre
|
||||||
import sco_moduleimpl
|
import sco_moduleimpl
|
||||||
@ -78,6 +79,8 @@ import sco_compute_moy
|
|||||||
import sco_abs
|
import sco_abs
|
||||||
from sco_abs import ddmmyyyy
|
from sco_abs import ddmmyyyy
|
||||||
|
|
||||||
|
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
|
||||||
|
|
||||||
|
|
||||||
def _toboolean(x):
|
def _toboolean(x):
|
||||||
"convert a value to boolean (ensure backward compat with OLD intranet code)"
|
"convert a value to boolean (ensure backward compat with OLD intranet code)"
|
||||||
@ -343,11 +346,17 @@ class ZAbsences(
|
|||||||
)
|
)
|
||||||
cnx.commit()
|
cnx.commit()
|
||||||
|
|
||||||
security.declareProtected(ScoView, "CountAbs")
|
def ListAbsInRange(
|
||||||
|
self, etudid, debut, fin, matin=None, moduleimpl_id=None, cursor=None
|
||||||
|
):
|
||||||
|
"""Liste des absences entre deux dates.
|
||||||
|
|
||||||
def CountAbs(self, etudid, debut, fin, matin=None, moduleimpl_id=None):
|
Args:
|
||||||
"""CountAbs
|
etudid
|
||||||
matin= 1 ou 0.
|
debut string iso date ("2020-03-12")
|
||||||
|
end string iso date ("2020-03-12")
|
||||||
|
matin None, True, False
|
||||||
|
moduleimpl_id
|
||||||
"""
|
"""
|
||||||
if matin != None:
|
if matin != None:
|
||||||
matin = _toboolean(matin)
|
matin = _toboolean(matin)
|
||||||
@ -358,10 +367,11 @@ class ZAbsences(
|
|||||||
modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s "
|
modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s "
|
||||||
else:
|
else:
|
||||||
modul = ""
|
modul = ""
|
||||||
|
if not cursor:
|
||||||
cnx = self.GetDBConnexion()
|
cnx = self.GetDBConnexion()
|
||||||
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
|
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""SELECT COUNT(*) AS NbAbs FROM (
|
"""
|
||||||
SELECT DISTINCT A.JOUR, A.MATIN
|
SELECT DISTINCT A.JOUR, A.MATIN
|
||||||
FROM ABSENCES A
|
FROM ABSENCES A
|
||||||
WHERE A.ETUDID = %(etudid)s
|
WHERE A.ETUDID = %(etudid)s
|
||||||
@ -370,13 +380,27 @@ class ZAbsences(
|
|||||||
+ modul
|
+ modul
|
||||||
+ """
|
+ """
|
||||||
AND A.JOUR BETWEEN %(debut)s AND %(fin)s
|
AND A.JOUR BETWEEN %(debut)s AND %(fin)s
|
||||||
) AS tmp
|
|
||||||
""",
|
""",
|
||||||
vars(),
|
vars(),
|
||||||
)
|
)
|
||||||
res = cursor.fetchone()[0]
|
res = cursor.dictfetchall()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
security.declareProtected(ScoView, "CountAbs")
|
||||||
|
|
||||||
|
def CountAbs(self, etudid, debut, fin, matin=None, moduleimpl_id=None):
|
||||||
|
"""CountAbs
|
||||||
|
matin= 1 ou 0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer.
|
||||||
|
"""
|
||||||
|
return len(
|
||||||
|
self.ListAbsInRange(
|
||||||
|
etudid, debut, fin, matin=matin, moduleimpl_id=moduleimpl_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
security.declareProtected(ScoView, "CountAbsJust")
|
security.declareProtected(ScoView, "CountAbsJust")
|
||||||
|
|
||||||
def CountAbsJust(self, etudid, debut, fin, matin=None, moduleimpl_id=None):
|
def CountAbsJust(self, etudid, debut, fin, matin=None, moduleimpl_id=None):
|
||||||
@ -718,7 +742,12 @@ class ZAbsences(
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
etuds = [e for e in etuds if e["etudid"] in mod_inscrits]
|
etuds_inscrits_module = [e for e in etuds if e["etudid"] in mod_inscrits]
|
||||||
|
if etuds_inscrits_module:
|
||||||
|
etuds = etuds_inscrits_module
|
||||||
|
else:
|
||||||
|
# Si aucun etudiant n'est inscrit au module choisi...
|
||||||
|
moduleimpl_id = None
|
||||||
nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id)
|
nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id)
|
||||||
sem = sco_formsemestre.do_formsemestre_list(
|
sem = sco_formsemestre.do_formsemestre_list(
|
||||||
self, {"formsemestre_id": formsemestre_id}
|
self, {"formsemestre_id": formsemestre_id}
|
||||||
@ -738,27 +767,48 @@ class ZAbsences(
|
|||||||
else:
|
else:
|
||||||
p = "du groupe"
|
p = "du groupe"
|
||||||
gr_tit = (
|
gr_tit = (
|
||||||
p + '<span class="fontred">' + groups_infos.groups_titles + "</span>"
|
p + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
||||||
)
|
)
|
||||||
|
|
||||||
H = [
|
H = [
|
||||||
self.sco_header(
|
self.sco_header(
|
||||||
page_title="Saisie hebdomadaire des absences",
|
page_title="Saisie hebdomadaire des absences",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
javascripts=["js/etud_info.js", "js/abs_ajax.js"],
|
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
|
||||||
|
+ [
|
||||||
|
"js/etud_info.js",
|
||||||
|
"js/abs_ajax.js",
|
||||||
|
"js/groups_view.js",
|
||||||
|
],
|
||||||
|
cssstyles=CSSSTYLES,
|
||||||
no_side_bar=1,
|
no_side_bar=1,
|
||||||
REQUEST=REQUEST,
|
REQUEST=REQUEST,
|
||||||
),
|
),
|
||||||
"""<table border="0" cellspacing="16"><tr><td>
|
"""<table border="0" cellspacing="16"><tr><td>
|
||||||
<h2>Saisie des absences %s %s,
|
<h2>Saisie des absences %s %s,
|
||||||
<span class="fontred">semaine du lundi %s</span></h2>
|
<span class="fontred">semaine du lundi %s</span></h2>
|
||||||
|
<div>
|
||||||
<p><a href="index_html">Annuler</a></p>
|
<form id="group_selector" method="get">
|
||||||
|
<input type="hidden" name="formsemestre_id" id="formsemestre_id" value="%s"/>
|
||||||
<p>
|
<input type="hidden" name="datelundi" id="datelundi" value="%s"/>
|
||||||
<form action="doSignaleAbsenceGrHebdo" method="post" action="%s">
|
<input type="hidden" name="destination" id="destination" value="%s"/>
|
||||||
|
<input type="hidden" name="moduleimpl_id" id="moduleimpl_id_o" value="%s"/>
|
||||||
|
Groupes: %s
|
||||||
|
</form>
|
||||||
|
<form id="abs_form">
|
||||||
"""
|
"""
|
||||||
% (gr_tit, sem["titre_num"], datelundi, REQUEST.URL0),
|
% (
|
||||||
|
gr_tit,
|
||||||
|
sem["titre_num"],
|
||||||
|
datelundi,
|
||||||
|
groups_infos.formsemestre_id,
|
||||||
|
datelundi,
|
||||||
|
destination,
|
||||||
|
moduleimpl_id or "",
|
||||||
|
sco_groups_view.menu_groups_choice(
|
||||||
|
self, groups_infos, submit_on_change=True
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
#
|
#
|
||||||
modimpls_list = []
|
modimpls_list = []
|
||||||
@ -797,12 +847,12 @@ class ZAbsences(
|
|||||||
sel = "selected" # aucun module specifie
|
sel = "selected" # aucun module specifie
|
||||||
|
|
||||||
H.append(
|
H.append(
|
||||||
"""
|
"""Module concerné:
|
||||||
Module concerné par ces absences (optionnel): <select id="moduleimpl_id" name="moduleimpl_id" onchange="document.location='%(url)s&moduleimpl_id='+document.getElementById('moduleimpl_id').value">
|
<select id="moduleimpl_id" name="moduleimpl_id" onchange="change_moduleimpl('%(url)s')">
|
||||||
<option value="" %(sel)s>non spécifié</option>
|
<option value="" %(sel)s>non spécifié</option>
|
||||||
%(menu_module)s
|
%(menu_module)s
|
||||||
</select>
|
</select>
|
||||||
</p>"""
|
</div>"""
|
||||||
% {"menu_module": menu_module, "url": base_url, "sel": sel}
|
% {"menu_module": menu_module, "url": base_url, "sel": sel}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -826,7 +876,6 @@ class ZAbsences(
|
|||||||
REQUEST=None,
|
REQUEST=None,
|
||||||
):
|
):
|
||||||
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier"""
|
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier"""
|
||||||
# log('SignaleAbsenceGrSemestre: moduleimpl_id=%s destination=%s' % (moduleimpl_id, destination))
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
self, group_ids, REQUEST=REQUEST
|
self, group_ids, REQUEST=REQUEST
|
||||||
)
|
)
|
||||||
@ -934,7 +983,7 @@ class ZAbsences(
|
|||||||
les <span class="fontred">%s</span></h2>
|
les <span class="fontred">%s</span></h2>
|
||||||
<p>
|
<p>
|
||||||
<a href="%s">%s</a>
|
<a href="%s">%s</a>
|
||||||
<form action="doSignaleAbsenceGrSemestre" method="post">
|
<form id="abs_form" action="doSignaleAbsenceGrSemestre" method="post">
|
||||||
"""
|
"""
|
||||||
% (gr_tit, sem["titre_num"], dayname, url_link_semaines, msg),
|
% (gr_tit, sem["titre_num"], dayname, url_link_semaines, msg),
|
||||||
]
|
]
|
||||||
@ -1006,7 +1055,7 @@ class ZAbsences(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
etuds: liste des étudiants
|
etuds: liste des étudiants
|
||||||
dates: liste de dates iso, par exemple: [ '2020-12-24', ... ]
|
dates: liste ordonnée de dates iso, par exemple: [ '2020-12-24', ... ]
|
||||||
moduleimpl_id: optionnel, module concerné.
|
moduleimpl_id: optionnel, module concerné.
|
||||||
"""
|
"""
|
||||||
H = [
|
H = [
|
||||||
@ -1043,6 +1092,8 @@ class ZAbsences(
|
|||||||
]
|
]
|
||||||
# Dates
|
# Dates
|
||||||
odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates]
|
odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates]
|
||||||
|
begin = dates[0]
|
||||||
|
end = dates[-1]
|
||||||
# Titres colonnes
|
# Titres colonnes
|
||||||
noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ]
|
noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ]
|
||||||
jn = sco_abs.day_names(self)
|
jn = sco_abs.day_names(self)
|
||||||
@ -1071,6 +1122,8 @@ class ZAbsences(
|
|||||||
'<tr><td><span class="redboldtext">Aucun étudiant inscrit !</span></td></tr>'
|
'<tr><td><span class="redboldtext">Aucun étudiant inscrit !</span></td></tr>'
|
||||||
)
|
)
|
||||||
i = 1
|
i = 1
|
||||||
|
cnx = self.GetDBConnexion()
|
||||||
|
cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
|
||||||
for etud in etuds:
|
for etud in etuds:
|
||||||
i += 1
|
i += 1
|
||||||
etudid = etud["etudid"]
|
etudid = etud["etudid"]
|
||||||
@ -1096,17 +1149,20 @@ class ZAbsences(
|
|||||||
'<tr class="%s"><td><b class="etudinfo" id="%s"><a class="discretelink" href="ficheEtud?etudid=%s" target="new">%s</a></b>%s</td>'
|
'<tr class="%s"><td><b class="etudinfo" id="%s"><a class="discretelink" href="ficheEtud?etudid=%s" target="new">%s</a></b>%s</td>'
|
||||||
% (tr_class, etudid, etudid, etud["nomprenom"], capstr)
|
% (tr_class, etudid, etudid, etud["nomprenom"], capstr)
|
||||||
)
|
)
|
||||||
for date in dates:
|
etud_abs = self.ListAbsInRange(
|
||||||
|
etudid, begin, end, moduleimpl_id=moduleimpl_id, cursor=cursor
|
||||||
|
)
|
||||||
|
for d in odates:
|
||||||
|
date = d.strftime("%Y-%m-%d")
|
||||||
# matin
|
# matin
|
||||||
if self.CountAbs(etudid, date, date, True, moduleimpl_id=moduleimpl_id):
|
is_abs = {"jour": d, "matin": True} in etud_abs
|
||||||
|
if is_abs:
|
||||||
checked = "checked"
|
checked = "checked"
|
||||||
else:
|
else:
|
||||||
checked = ""
|
checked = ""
|
||||||
# bulle lors du passage souris
|
# bulle lors du passage souris
|
||||||
coljour = sco_abs.DAYNAMES[
|
coljour = sco_abs.DAYNAMES[(calendar.weekday(d.year, d.month, d.day))]
|
||||||
(calendar.weekday(int(date[:4]), int(date[5:7]), int(date[8:])))
|
datecol = coljour + " " + d.strftime("%d/%m/%Y")
|
||||||
]
|
|
||||||
datecol = coljour + " " + date[8:] + "/" + date[5:7] + "/" + date[:4]
|
|
||||||
bulle_am = '"' + etud["nomprenom"] + " - " + datecol + ' (matin)"'
|
bulle_am = '"' + etud["nomprenom"] + " - " + datecol + ' (matin)"'
|
||||||
bulle_pm = '"' + etud["nomprenom"] + " - " + datecol + ' (ap.midi)"'
|
bulle_pm = '"' + etud["nomprenom"] + " - " + datecol + ' (ap.midi)"'
|
||||||
|
|
||||||
@ -1122,9 +1178,8 @@ class ZAbsences(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
# après-midi
|
# après-midi
|
||||||
if self.CountAbs(
|
is_abs = {"jour": d, "matin": False} in etud_abs
|
||||||
etudid, date, date, False, moduleimpl_id=moduleimpl_id
|
if is_abs:
|
||||||
):
|
|
||||||
checked = "checked"
|
checked = "checked"
|
||||||
else:
|
else:
|
||||||
checked = ""
|
checked = ""
|
||||||
|
@ -394,6 +394,10 @@ REQUEST.URL0=%s<br/>
|
|||||||
"""
|
"""
|
||||||
return self.ScoURL() + "/Entreprises"
|
return self.ScoURL() + "/Entreprises"
|
||||||
|
|
||||||
|
def AbsencesURL(self):
|
||||||
|
"""URL of Absences"""
|
||||||
|
return self.ScoURL() + "/Absences"
|
||||||
|
|
||||||
def UsersURL(self):
|
def UsersURL(self):
|
||||||
"""URL of Users
|
"""URL of Users
|
||||||
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite/Users
|
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite/Users
|
||||||
|
@ -251,8 +251,8 @@ class exUserFolder(Folder,BasicUserFolder,BasicGroupFolderMixin,
|
|||||||
('Manager',)),
|
('Manager',)),
|
||||||
|
|
||||||
('View', ('manage_changePassword',
|
('View', ('manage_changePassword',
|
||||||
'manage_forgotPassword', 'docLogin','docLoginRedirect',
|
'manage_forgotPassword','docLoginRedirect',
|
||||||
'docLogout', 'logout', 'DialogHeader',
|
'logout', 'DialogHeader',
|
||||||
'DialogFooter', 'manage_signupUser',
|
'DialogFooter', 'manage_signupUser',
|
||||||
'MessageDialog', 'redirectToLogin','manage_changeProps'),
|
'MessageDialog', 'redirectToLogin','manage_changeProps'),
|
||||||
('Anonymous', 'Authenticated', 'Manager')),
|
('Anonymous', 'Authenticated', 'Manager')),
|
||||||
@ -269,7 +269,7 @@ class exUserFolder(Folder,BasicUserFolder,BasicGroupFolderMixin,
|
|||||||
('Access contents information', ('hasProperty', 'propertyIds',
|
('Access contents information', ('hasProperty', 'propertyIds',
|
||||||
'propertyValues','propertyItems',
|
'propertyValues','propertyItems',
|
||||||
'getProperty', 'getPropertyType',
|
'getProperty', 'getPropertyType',
|
||||||
'propertyMap', 'docLogin','docLoginRedirect',
|
'propertyMap', 'docLoginRedirect',
|
||||||
'DialogHeader', 'DialogFooter',
|
'DialogHeader', 'DialogFooter',
|
||||||
'MessageDialog', 'redirectToLogin',),
|
'MessageDialog', 'redirectToLogin',),
|
||||||
('Anonymous', 'Authenticated', 'Manager')),
|
('Anonymous', 'Authenticated', 'Manager')),
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
# E. Viennet, Juin 2008
|
# E. Viennet, Juin 2008
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
source config.sh
|
source config.sh
|
||||||
source utils.sh
|
source utils.sh
|
||||||
|
@ -60,7 +60,7 @@ then
|
|||||||
|
|
||||||
# suppression de la base postgres
|
# suppression de la base postgres
|
||||||
db_name=$(sed '/^dbname=*/!d; s///;q' < "$cfg_pathname")
|
db_name=$(sed '/^dbname=*/!d; s///;q' < "$cfg_pathname")
|
||||||
if su -c "psql -lt" "$POSTGRES_SUPERUSER" | cut -d \| -f 1 | grep -wq SCORT
|
if su -c "psql -lt" "$POSTGRES_SUPERUSER" | cut -d \| -f 1 | grep -wq "$db_name"
|
||||||
then
|
then
|
||||||
echo "Suppression de la base postgres $db_name ..."
|
echo "Suppression de la base postgres $db_name ..."
|
||||||
su -c "dropdb $db_name" "$POSTGRES_SUPERUSER" || terminate "ne peux supprimer base de donnees $db_name"
|
su -c "dropdb $db_name" "$POSTGRES_SUPERUSER" || terminate "ne peux supprimer base de donnees $db_name"
|
||||||
|
64
config/fix_bug70_db.py
Normal file
64
config/fix_bug70_db.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Fix bug #70
|
||||||
|
|
||||||
|
Utiliser comme:
|
||||||
|
scotests/scointeractive.sh DEPT config/fix_bug70_db.py
|
||||||
|
|
||||||
|
"""
|
||||||
|
context = context.Notes # pylint: disable=undefined-variable
|
||||||
|
REQUEST = REQUEST # pylint: disable=undefined-variable
|
||||||
|
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import sco_utils
|
||||||
|
import notesdb
|
||||||
|
import sco_formsemestre
|
||||||
|
import sco_formsemestre_edit
|
||||||
|
import sco_moduleimpl
|
||||||
|
|
||||||
|
G = sco_fake_gen.ScoFake(context.Notes)
|
||||||
|
|
||||||
|
|
||||||
|
def fix_formsemestre_formation_bug70(formsemestre_id):
|
||||||
|
"""Le bug #70 a pu entrainer des incohérences
|
||||||
|
lors du clonage avorté de semestres.
|
||||||
|
Cette fonction réassocie le semestre à la formation
|
||||||
|
à laquelle appartiennent ses modulesimpls.
|
||||||
|
2021-04-23
|
||||||
|
"""
|
||||||
|
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
|
||||||
|
cursor = notesdb.SimpleQuery(
|
||||||
|
context,
|
||||||
|
"""SELECT m.formation_id
|
||||||
|
FROM notes_modules m, notes_moduleimpl mi
|
||||||
|
WHERE mi.module_id = m.module_id
|
||||||
|
AND mi.formsemestre_id = %(formsemestre_id)s
|
||||||
|
""",
|
||||||
|
{"formsemestre_id": formsemestre_id},
|
||||||
|
)
|
||||||
|
modimpls_formations = set([x[0] for x in cursor])
|
||||||
|
if len(modimpls_formations) > 1:
|
||||||
|
# this is should not occur
|
||||||
|
G.log(
|
||||||
|
"Warning: fix_formsemestre_formation_bug70: modules from several formations in sem %s"
|
||||||
|
% formsemestre_id
|
||||||
|
)
|
||||||
|
elif len(modimpls_formations) == 1:
|
||||||
|
modimpls_formation_id = modimpls_formations.pop()
|
||||||
|
if modimpls_formation_id != sem["formation_id"]:
|
||||||
|
# Bug #70: fix
|
||||||
|
G.log("fix_formsemestre_formation_bug70: fixing %s" % formsemestre_id)
|
||||||
|
sem["formation_id"] = modimpls_formation_id
|
||||||
|
context.do_formsemestre_edit(sem, html_quote=False)
|
||||||
|
|
||||||
|
|
||||||
|
formsemestre_ids = [
|
||||||
|
x[0]
|
||||||
|
for x in notesdb.SimpleQuery(
|
||||||
|
context, "SELECT formsemestre_id FROM notes_formsemestre", {}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for formsemestre_id in formsemestre_ids:
|
||||||
|
fix_formsemestre_formation_bug70(formsemestre_id)
|
@ -583,27 +583,6 @@ for dept in get_depts():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# add etape_apo2
|
|
||||||
check_field(
|
|
||||||
cnx,
|
|
||||||
"notes_formsemestre",
|
|
||||||
"etape_apo2",
|
|
||||||
["alter table notes_formsemestre add column etape_apo2 text"],
|
|
||||||
)
|
|
||||||
# add etape_apo3
|
|
||||||
check_field(
|
|
||||||
cnx,
|
|
||||||
"notes_formsemestre",
|
|
||||||
"etape_apo3",
|
|
||||||
["alter table notes_formsemestre add column etape_apo3 text"],
|
|
||||||
)
|
|
||||||
# add etape_apo4
|
|
||||||
check_field(
|
|
||||||
cnx,
|
|
||||||
"notes_formsemestre",
|
|
||||||
"etape_apo4",
|
|
||||||
["alter table notes_formsemestre add column etape_apo4 text"],
|
|
||||||
)
|
|
||||||
# add publish_incomplete
|
# add publish_incomplete
|
||||||
check_field(
|
check_field(
|
||||||
cnx,
|
cnx,
|
||||||
|
27
config/postupgrade.py
Executable file → Normal file
27
config/postupgrade.py
Executable file → Normal file
@ -1,4 +1,5 @@
|
|||||||
#!/opt/zope213/bin/python
|
#!/opt/zope213/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ScoDoc post-upgrade script.
|
ScoDoc post-upgrade script.
|
||||||
@ -11,21 +12,22 @@ _before_ upgrading the database.
|
|||||||
E. Viennet, June 2008
|
E. Viennet, June 2008
|
||||||
Mar 2017: suppress upgrade of very old Apache configs
|
Mar 2017: suppress upgrade of very old Apache configs
|
||||||
Aug 2020: move photos to .../var/scodoc/
|
Aug 2020: move photos to .../var/scodoc/
|
||||||
|
Apr 2021: bug #70
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import glob
|
import glob
|
||||||
import shutil
|
import shutil
|
||||||
from scodocutils import log, SCODOC_DIR, SCODOC_VAR_DIR, SCODOC_LOGOS_DIR
|
from scodocutils import log, SCODOC_DIR, SCODOC_VAR_DIR, SCODOC_LOGOS_DIR, SCO_TMPDIR
|
||||||
|
|
||||||
if os.getuid() != 0:
|
if os.getuid() != 0:
|
||||||
log('postupgrade.py: must be run as root')
|
log("postupgrade.py: must be run as root")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
# Migrate photos (2020-08-16, svn 1908)
|
# Migrate photos (2020-08-16, svn 1908)
|
||||||
old_photo_dir = os.path.join(SCODOC_DIR, "static", "photos")
|
old_photo_dir = os.path.join(SCODOC_DIR, "static", "photos")
|
||||||
photo_dirs = glob.glob( old_photo_dir + "/F*")
|
photo_dirs = glob.glob(old_photo_dir + "/F*")
|
||||||
if photo_dirs:
|
if photo_dirs:
|
||||||
log("Moving photos to new <var> directory...")
|
log("Moving photos to new <var> directory...")
|
||||||
shutil.move(old_photo_dir, SCODOC_VAR_DIR)
|
shutil.move(old_photo_dir, SCODOC_VAR_DIR)
|
||||||
@ -33,7 +35,7 @@ if photo_dirs:
|
|||||||
# Migrate depts (2020-08-17, svn 1909)
|
# Migrate depts (2020-08-17, svn 1909)
|
||||||
|
|
||||||
old_depts_dir = os.path.join(SCODOC_DIR, "config", "depts")
|
old_depts_dir = os.path.join(SCODOC_DIR, "config", "depts")
|
||||||
cfg_files = glob.glob( old_depts_dir + "/*.cfg")
|
cfg_files = glob.glob(old_depts_dir + "/*.cfg")
|
||||||
depts_dir = os.path.join(SCODOC_VAR_DIR, "config/depts/")
|
depts_dir = os.path.join(SCODOC_VAR_DIR, "config/depts/")
|
||||||
for cfg in cfg_files:
|
for cfg in cfg_files:
|
||||||
log("Moving %s to new <var> directory..." % cfg)
|
log("Moving %s to new <var> directory..." % cfg)
|
||||||
@ -41,7 +43,7 @@ for cfg in cfg_files:
|
|||||||
|
|
||||||
# Move logos
|
# Move logos
|
||||||
if not os.path.exists(SCODOC_LOGOS_DIR):
|
if not os.path.exists(SCODOC_LOGOS_DIR):
|
||||||
old_logos = os.path.join(SCODOC_DIR,"logos")
|
old_logos = os.path.join(SCODOC_DIR, "logos")
|
||||||
if os.path.exists(old_logos):
|
if os.path.exists(old_logos):
|
||||||
log("Moving logos to new <var> directory...")
|
log("Moving logos to new <var> directory...")
|
||||||
dest = os.path.normpath(os.path.join(SCODOC_LOGOS_DIR, ".."))
|
dest = os.path.normpath(os.path.join(SCODOC_LOGOS_DIR, ".."))
|
||||||
@ -50,10 +52,23 @@ if not os.path.exists(SCODOC_LOGOS_DIR):
|
|||||||
log("Warning: logos directory is missing (%s)" % SCODOC_LOGOS_DIR)
|
log("Warning: logos directory is missing (%s)" % SCODOC_LOGOS_DIR)
|
||||||
|
|
||||||
# Move dept-specific logos
|
# Move dept-specific logos
|
||||||
for d in glob.glob( SCODOC_DIR + "/logos_*" ):
|
for d in glob.glob(SCODOC_DIR + "/logos_*"):
|
||||||
log("Moving %s to %s" % (d, SCODOC_LOGOS_DIR))
|
log("Moving %s to %s" % (d, SCODOC_LOGOS_DIR))
|
||||||
shutil.move(d, SCODOC_LOGOS_DIR)
|
shutil.move(d, SCODOC_LOGOS_DIR)
|
||||||
|
|
||||||
|
# Fix bug #70
|
||||||
|
depts = [
|
||||||
|
os.path.splitext(os.path.basename(f))[0] for f in glob.glob(depts_dir + "/*.cfg")
|
||||||
|
]
|
||||||
|
for dept in depts:
|
||||||
|
fixed_filename = SCO_TMPDIR + "/.%s_bug70_fixed" % dept
|
||||||
|
if not os.path.exists(fixed_filename):
|
||||||
|
log("fixing #70 on %s" % dept)
|
||||||
|
os.system("../scotests/scointeractive.sh -x %s config/fix_bug70_db.py" % dept)
|
||||||
|
# n'essaie qu'une fois, même en cas d'échec
|
||||||
|
f = open(fixed_filename, "a")
|
||||||
|
f.close()
|
||||||
|
|
||||||
# Continue here...
|
# Continue here...
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
8
config/scodocutils.py
Normal file → Executable file
8
config/scodocutils.py
Normal file → Executable file
@ -9,6 +9,12 @@ import sys, os, psycopg2, glob, subprocess, traceback, time
|
|||||||
|
|
||||||
sys.path.append("..")
|
sys.path.append("..")
|
||||||
|
|
||||||
|
# INSTANCE_HOME est nécessaire pour sco_utils.py
|
||||||
|
# note: avec le python 2.7 de Zope2, l'import de pyscopg2 change
|
||||||
|
# INSTANCE_HOME dans l'environnement !
|
||||||
|
# Ici on le fixe à la "bonne" valeur pour ScoDoc7.
|
||||||
|
os.environ["INSTANCE_HOME"] = "/opt/scodoc"
|
||||||
|
|
||||||
|
|
||||||
def log(msg):
|
def log(msg):
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@ -24,8 +30,10 @@ SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "")
|
|||||||
if not SCODOC_VAR_DIR:
|
if not SCODOC_VAR_DIR:
|
||||||
log("Error: environment variable SCODOC_VAR_DIR is not defined")
|
log("Error: environment variable SCODOC_VAR_DIR is not defined")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
SCO_TMPDIR = os.path.join(SCODOC_VAR_DIR, "tmp")
|
||||||
SCODOC_LOGOS_DIR = os.environ.get("SCODOC_LOGOS_DIR", "")
|
SCODOC_LOGOS_DIR = os.environ.get("SCODOC_LOGOS_DIR", "")
|
||||||
|
|
||||||
|
|
||||||
def get_dept_cnx_str(dept):
|
def get_dept_cnx_str(dept):
|
||||||
"db cnx string for dept"
|
"db cnx string for dept"
|
||||||
f = os.path.join(SCODOC_VAR_DIR, "config", "depts", dept + ".cfg")
|
f = os.path.join(SCODOC_VAR_DIR, "config", "depts", dept + ".cfg")
|
||||||
|
7
config/upgrade.sh
Executable file → Normal file
7
config/upgrade.sh
Executable file → Normal file
@ -23,6 +23,7 @@ fi
|
|||||||
|
|
||||||
# Upgrade svn working copy if possible
|
# Upgrade svn working copy if possible
|
||||||
svnver=$(svn --version --quiet)
|
svnver=$(svn --version --quiet)
|
||||||
|
# shellcheck disable=SC2072
|
||||||
if [[ ${svnver} > "1.7" ]]
|
if [[ ${svnver} > "1.7" ]]
|
||||||
then
|
then
|
||||||
(cd "$SCODOC_DIR"; find . -name .svn -type d -exec dirname {} \; | xargs svn upgrade)
|
(cd "$SCODOC_DIR"; find . -name .svn -type d -exec dirname {} \; | xargs svn upgrade)
|
||||||
@ -63,7 +64,7 @@ CMD="curl --fail --connect-timeout 5 --silent http://scodoc.iutv.univ-paris13.fr
|
|||||||
#echo $CMD
|
#echo $CMD
|
||||||
SVERSION="$(${CMD})"
|
SVERSION="$(${CMD})"
|
||||||
|
|
||||||
if [ $? == 0 ]; then
|
if [ "$?" == 0 ]; then
|
||||||
#echo "answer=${SVERSION}"
|
#echo "answer=${SVERSION}"
|
||||||
echo "${SVERSION}" > "${SCODOC_VERSION_DIR}"/scodoc.sn
|
echo "${SVERSION}" > "${SCODOC_VERSION_DIR}"/scodoc.sn
|
||||||
else
|
else
|
||||||
@ -132,6 +133,10 @@ then
|
|||||||
chmod 600 "$LOCAL_CONFIG_FILENAME"
|
chmod 600 "$LOCAL_CONFIG_FILENAME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# upgrade old dateutil (check version manually to speedup)
|
||||||
|
v=$(/opt/zope213/bin/python -c "import dateutil; print dateutil.__version__")
|
||||||
|
[[ "$v" < "2.8.1" ]] && /opt/zope213/bin/pip install --upgrade python-dateutil
|
||||||
|
|
||||||
# Ensure www-data can duplicate databases (for dumps)
|
# Ensure www-data can duplicate databases (for dumps)
|
||||||
su -c $'psql -c \'alter role "www-data" with CREATEDB;\'' "$POSTGRES_SUPERUSER"
|
su -c $'psql -c \'alter role "www-data" with CREATEDB;\'' "$POSTGRES_SUPERUSER"
|
||||||
#'
|
#'
|
||||||
|
6
debug.py
6
debug.py
@ -57,17 +57,19 @@ import sco_bulletins_xml
|
|||||||
# Prend le premier departement comme context
|
# Prend le premier departement comme context
|
||||||
|
|
||||||
|
|
||||||
def go(app, n=0):
|
def go(app, n=0, verbose=True):
|
||||||
context = app.ScoDoc.objectValues("Folder")[n].Scolarite
|
context = app.ScoDoc.objectValues("Folder")[n].Scolarite
|
||||||
|
if verbose:
|
||||||
print("context in dept ", context.DeptId())
|
print("context in dept ", context.DeptId())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def go_dept(app, dept):
|
def go_dept(app, dept, verbose=True):
|
||||||
objs = app.ScoDoc.objectValues("Folder")
|
objs = app.ScoDoc.objectValues("Folder")
|
||||||
for o in objs:
|
for o in objs:
|
||||||
context = o.Scolarite
|
context = o.Scolarite
|
||||||
if context.DeptId() == dept:
|
if context.DeptId() == dept:
|
||||||
|
if verbose:
|
||||||
print("context in dept ", context.DeptId())
|
print("context in dept ", context.DeptId())
|
||||||
return context
|
return context
|
||||||
raise ValueError("dep %s not found" % dept)
|
raise ValueError("dep %s not found" % dept)
|
||||||
|
@ -123,6 +123,7 @@ class GenTable:
|
|||||||
pdf_col_widths=None,
|
pdf_col_widths=None,
|
||||||
xml_outer_tag="table",
|
xml_outer_tag="table",
|
||||||
xml_row_tag="row",
|
xml_row_tag="row",
|
||||||
|
text_with_titles=False, # CSV with header line
|
||||||
text_fields_separator="\t",
|
text_fields_separator="\t",
|
||||||
preferences=None,
|
preferences=None,
|
||||||
):
|
):
|
||||||
@ -173,6 +174,7 @@ class GenTable:
|
|||||||
self.xml_row_tag = xml_row_tag
|
self.xml_row_tag = xml_row_tag
|
||||||
# TEXT parameters
|
# TEXT parameters
|
||||||
self.text_fields_separator = text_fields_separator
|
self.text_fields_separator = text_fields_separator
|
||||||
|
self.text_with_titles = text_with_titles
|
||||||
#
|
#
|
||||||
if preferences:
|
if preferences:
|
||||||
self.preferences = preferences
|
self.preferences = preferences
|
||||||
@ -265,8 +267,7 @@ class GenTable:
|
|||||||
|
|
||||||
def get_titles_list(self):
|
def get_titles_list(self):
|
||||||
"list of titles"
|
"list of titles"
|
||||||
l = []
|
return [self.titles.get(cid, "") for cid in self.columns_ids]
|
||||||
return l + [self.titles.get(cid, "") for cid in self.columns_ids]
|
|
||||||
|
|
||||||
def gen(self, format="html", columns_ids=None):
|
def gen(self, format="html", columns_ids=None):
|
||||||
"""Build representation of the table in the specified format.
|
"""Build representation of the table in the specified format.
|
||||||
@ -479,10 +480,14 @@ class GenTable:
|
|||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
"raw text representation of the table"
|
"raw text representation of the table"
|
||||||
|
if self.text_with_titles:
|
||||||
|
headline = [self.get_titles_list()]
|
||||||
|
else:
|
||||||
|
headline = []
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
self.text_fields_separator.join([x for x in line])
|
self.text_fields_separator.join([x for x in line])
|
||||||
for line in self.get_data_list()
|
for line in headline + self.get_data_list()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -534,14 +539,6 @@ class GenTable:
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
pdf_style_list += self.pdf_table_style
|
pdf_style_list += self.pdf_table_style
|
||||||
# log('len(Pt)=%s' % len(Pt))
|
|
||||||
# log( 'line lens=%s' % [ len(x) for x in Pt ] )
|
|
||||||
# log( 'style=\n%s' % pdf_style_list)
|
|
||||||
# col_min = min([x[1][0] for x in pdf_style_list])
|
|
||||||
# col_max = max([x[2][0] for x in pdf_style_list])
|
|
||||||
# lin_min = min([x[1][1] for x in pdf_style_list])
|
|
||||||
# lin_max = max([x[2][1] for x in pdf_style_list])
|
|
||||||
# log('col_min=%s col_max=%s lin_min=%s lin_max=%s' % (col_min, col_max, lin_min, lin_max))
|
|
||||||
T = Table(Pt, repeatRows=1, colWidths=self.pdf_col_widths, style=pdf_style_list)
|
T = Table(Pt, repeatRows=1, colWidths=self.pdf_col_widths, style=pdf_style_list)
|
||||||
|
|
||||||
objects = []
|
objects = []
|
||||||
|
@ -42,7 +42,13 @@ Génération de la "sidebar" (marge gauche des pages HTML)
|
|||||||
def sidebar_common(context, REQUEST=None):
|
def sidebar_common(context, REQUEST=None):
|
||||||
"partie commune a toutes les sidebar"
|
"partie commune a toutes les sidebar"
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
authuser = REQUEST.AUTHENTICATED_USER
|
||||||
params = {"ScoURL": context.ScoURL(), "authuser": str(authuser)}
|
params = {
|
||||||
|
"ScoURL": context.ScoURL(),
|
||||||
|
"UsersURL": context.UsersURL(),
|
||||||
|
"NotesURL": context.NotesURL(),
|
||||||
|
"AbsencesURL": context.AbsencesURL(),
|
||||||
|
"authuser": str(authuser),
|
||||||
|
}
|
||||||
H = [
|
H = [
|
||||||
'<a class="scodoc_title" href="about">ScoDoc</a>',
|
'<a class="scodoc_title" href="about">ScoDoc</a>',
|
||||||
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/userinfo">%(authuser)s</a><br/><a id="deconnectlink" href="%(ScoURL)s/acl_users/logout">déconnexion</a></div>'
|
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/userinfo">%(authuser)s</a><br/><a id="deconnectlink" href="%(ScoURL)s/acl_users/logout">déconnexion</a></div>'
|
||||||
@ -50,8 +56,8 @@ def sidebar_common(context, REQUEST=None):
|
|||||||
context.sidebar_dept(REQUEST),
|
context.sidebar_dept(REQUEST),
|
||||||
"""<h2 class="insidebar">Scolarité</h2>
|
"""<h2 class="insidebar">Scolarité</h2>
|
||||||
<a href="%(ScoURL)s" class="sidebar">Semestres</a> <br/>
|
<a href="%(ScoURL)s" class="sidebar">Semestres</a> <br/>
|
||||||
<a href="%(ScoURL)s/Notes" class="sidebar">Programmes</a> <br/>
|
<a href="%(NotesURL)s" class="sidebar">Programmes</a> <br/>
|
||||||
<a href="%(ScoURL)s/Absences" class="sidebar">Absences</a> <br/>
|
<a href="%(AbsencesURL)s" class="sidebar">Absences</a> <br/>
|
||||||
"""
|
"""
|
||||||
% params,
|
% params,
|
||||||
]
|
]
|
||||||
@ -60,14 +66,7 @@ def sidebar_common(context, REQUEST=None):
|
|||||||
ScoUsersView, context
|
ScoUsersView, context
|
||||||
):
|
):
|
||||||
H.append(
|
H.append(
|
||||||
"""<a href="%(ScoURL)s/Users" class="sidebar">Utilisateurs</a> <br/>"""
|
"""<a href="%(UsersURL)s" class="sidebar">Utilisateurs</a> <br/>""" % params
|
||||||
% params
|
|
||||||
)
|
|
||||||
|
|
||||||
if 0: # XXX experimental
|
|
||||||
H.append(
|
|
||||||
"""<a href="%(ScoURL)s/Notes/Services" class="sidebar">Services</a> <br/>"""
|
|
||||||
% params
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if authuser.has_permission(ScoChangePreferences, context):
|
if authuser.has_permission(ScoChangePreferences, context):
|
||||||
@ -88,8 +87,8 @@ def sidebar(context, REQUEST=None):
|
|||||||
|
|
||||||
H.append(
|
H.append(
|
||||||
"""<div class="box-chercheetud">Chercher étudiant:<br/>
|
"""<div class="box-chercheetud">Chercher étudiant:<br/>
|
||||||
<form id="form-chercheetud" action="%(ScoURL)s/search_etud_in_dept">
|
<form method="get" id="form-chercheetud" action="%(ScoURL)s/search_etud_in_dept">
|
||||||
<div><input type="text" size="12" id="in-expnom" name="expnom"></input></div>
|
<div><input type="text" size="12" id="in-expnom" name="expnom" spellcheck="false"></input></div>
|
||||||
</form></div>
|
</form></div>
|
||||||
<div class="etud-insidebar">
|
<div class="etud-insidebar">
|
||||||
"""
|
"""
|
||||||
|
@ -99,8 +99,7 @@ function get_semestre_info($sem, $dept) {
|
|||||||
// Renvoi les informations détaillées d'un semestre
|
// Renvoi les informations détaillées d'un semestre
|
||||||
// Ne nécessite pas d'authentification avec sco_user et sco_pw - Il est possible de choisir le format XML ou JSON.
|
// Ne nécessite pas d'authentification avec sco_user et sco_pw - Il est possible de choisir le format XML ou JSON.
|
||||||
// formsemestre_list
|
// formsemestre_list
|
||||||
// Paramètres (tous optionnels): formsesmestre_id, formation_id, etape_apo, etape_apo2
|
// Paramètres (tous optionnels): formsemestre_id, formation_id, etape_apo
|
||||||
// Coquille dans la doc : formsesmestre_id
|
|
||||||
// Résultat: liste des semestres correspondant.
|
// Résultat: liste des semestres correspondant.
|
||||||
// Exemple: formsemestre_list?format=xml&etape_apo=V1RT
|
// Exemple: formsemestre_list?format=xml&etape_apo=V1RT
|
||||||
global $sco_pw;
|
global $sco_pw;
|
||||||
|
@ -35,6 +35,18 @@ CREATE FUNCTION notes_newid_etud( text ) returns text as '
|
|||||||
as result;
|
as result;
|
||||||
' language SQL;
|
' language SQL;
|
||||||
|
|
||||||
|
-- Fonction pour anonymisation:
|
||||||
|
-- inspirée par https://www.simononsoftware.com/random-string-in-postgresql/
|
||||||
|
CREATE FUNCTION random_text_md5( integer ) returns text
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
select upper( substring( (SELECT string_agg(md5(random()::TEXT), '')
|
||||||
|
FROM generate_series(
|
||||||
|
1,
|
||||||
|
CEIL($1 / 32.)::integer)
|
||||||
|
), 1, $1) );
|
||||||
|
$$;
|
||||||
|
|
||||||
-- Preferences
|
-- Preferences
|
||||||
CREATE TABLE sco_prefs (
|
CREATE TABLE sco_prefs (
|
||||||
pref_id text DEFAULT notes_newid('PREF'::text) UNIQUE NOT NULL,
|
pref_id text DEFAULT notes_newid('PREF'::text) UNIQUE NOT NULL,
|
||||||
|
@ -1132,6 +1132,9 @@ class NotesTable:
|
|||||||
|
|
||||||
def sem_has_decisions(self):
|
def sem_has_decisions(self):
|
||||||
"""True si au moins une decision de jury dans ce semestre"""
|
"""True si au moins une decision de jury dans ce semestre"""
|
||||||
|
if [x for x in self.decisions_jury_ues.values() if x]:
|
||||||
|
return True
|
||||||
|
|
||||||
return len([x for x in self.decisions_jury_ues.values() if x]) > 0
|
return len([x for x in self.decisions_jury_ues.values() if x]) > 0
|
||||||
|
|
||||||
def etud_has_decision(self, etudid):
|
def etud_has_decision(self, etudid):
|
||||||
|
@ -813,6 +813,7 @@ class JuryPE:
|
|||||||
"nom": etudinfo["nom"],
|
"nom": etudinfo["nom"],
|
||||||
"prenom": etudinfo["prenom"],
|
"prenom": etudinfo["prenom"],
|
||||||
"civilite": etudinfo["civilite"],
|
"civilite": etudinfo["civilite"],
|
||||||
|
"civilite_str": etudinfo["civilite_str"],
|
||||||
"age": str(pe_tools.calcul_age(etudinfo["date_naissance"])),
|
"age": str(pe_tools.calcul_age(etudinfo["date_naissance"])),
|
||||||
"lycee": etudinfo["nomlycee"]
|
"lycee": etudinfo["nomlycee"]
|
||||||
+ (
|
+ (
|
||||||
|
@ -325,7 +325,7 @@ def do_formsemestre_archive(
|
|||||||
if data:
|
if data:
|
||||||
PVArchive.store(archive_id, "Decisions_Jury.xls", data)
|
PVArchive.store(archive_id, "Decisions_Jury.xls", data)
|
||||||
# Classeur bulletins (PDF)
|
# Classeur bulletins (PDF)
|
||||||
data, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
|
data, _ = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
|
||||||
context, formsemestre_id, REQUEST, version=bulVersion
|
context, formsemestre_id, REQUEST, version=bulVersion
|
||||||
)
|
)
|
||||||
if data:
|
if data:
|
||||||
@ -548,7 +548,7 @@ def formsemestre_delete_archive(
|
|||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||||
)
|
)
|
||||||
sem = sco_formsemestre.get_formsemestre(
|
_ = sco_formsemestre.get_formsemestre(
|
||||||
context, formsemestre_id
|
context, formsemestre_id
|
||||||
) # check formsemestre_id
|
) # check formsemestre_id
|
||||||
archive_id = PVArchive.get_id_from_name(context, formsemestre_id, archive_name)
|
archive_id = PVArchive.get_id_from_name(context, formsemestre_id, archive_name)
|
||||||
|
@ -546,6 +546,8 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
|
|||||||
e["coef_txt"] = scu.fmt_coef(e["coefficient"])
|
e["coef_txt"] = scu.fmt_coef(e["coefficient"])
|
||||||
if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
|
if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
|
||||||
e["coef_txt"] = "rat."
|
e["coef_txt"] = "rat."
|
||||||
|
elif e["evaluation_type"] == scu.EVALUATION_SESSION2:
|
||||||
|
e["coef_txt"] = "sess. 2"
|
||||||
if e["etat"]["evalattente"]:
|
if e["etat"]["evalattente"]:
|
||||||
mod_attente = True # une eval en attente dans ce module
|
mod_attente = True # une eval en attente dans ce module
|
||||||
if (not is_malus) or (val != "NP"):
|
if (not is_malus) or (val != "NP"):
|
||||||
|
@ -209,6 +209,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
value=scu.fmt_note(ue_status["cur_moy_ue"]),
|
value=scu.fmt_note(ue_status["cur_moy_ue"]),
|
||||||
min=scu.fmt_note(ue["min"]),
|
min=scu.fmt_note(ue["min"]),
|
||||||
max=scu.fmt_note(ue["max"]),
|
max=scu.fmt_note(ue["max"]),
|
||||||
|
moy=scu.fmt_note(ue["moy"]), # CM : ajout pour faire apparaitre la moyenne des UE
|
||||||
),
|
),
|
||||||
rang=str(nt.ue_rangs[ue["ue_id"]][0][etudid]),
|
rang=str(nt.ue_rangs[ue["ue_id"]][0][etudid]),
|
||||||
effectif=str(nt.ue_rangs[ue["ue_id"]][1]),
|
effectif=str(nt.ue_rangs[ue["ue_id"]][1]),
|
||||||
@ -275,6 +276,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
),
|
),
|
||||||
coefficient=e["coefficient"],
|
coefficient=e["coefficient"],
|
||||||
evaluation_type=e["evaluation_type"],
|
evaluation_type=e["evaluation_type"],
|
||||||
|
evaluation_id=e["evaluation_id"], # CM : ajout pour permettre de faire le lien sur les bulletins en ligne avec l'évaluation
|
||||||
description=scu.quote_xml_attr(e["description"]),
|
description=scu.quote_xml_attr(e["description"]),
|
||||||
note=val,
|
note=val,
|
||||||
)
|
)
|
||||||
|
@ -378,7 +378,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
P[-1]["_pdf_style"].append(
|
P[-1]["_pdf_style"].append(
|
||||||
("LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR)
|
("LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR)
|
||||||
)
|
)
|
||||||
|
# Espacement sous la ligne moyenne générale:
|
||||||
|
P[-1]["_pdf_style"].append(("BOTTOMPADDING", (0, 1), (-1, 1), 8))
|
||||||
# Moyenne générale:
|
# Moyenne générale:
|
||||||
nbabs = I["nbabs"]
|
nbabs = I["nbabs"]
|
||||||
nbabsjust = I["nbabsjust"]
|
nbabsjust = I["nbabsjust"]
|
||||||
@ -392,8 +393,10 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
"abs": "%s / %s" % (nbabs, nbabsjust),
|
"abs": "%s / %s" % (nbabs, nbabsjust),
|
||||||
"_css_row_class": "notes_bulletin_row_gen",
|
"_css_row_class": "notes_bulletin_row_gen",
|
||||||
"_titre_colspan": 2,
|
"_titre_colspan": 2,
|
||||||
"_pdf_row_markup": ['font size="12"', "b"], # bold, size 12
|
"_pdf_row_markup": ["b"], # bold. On peut ajouter 'font size="12"'
|
||||||
"_pdf_style": [("LINEABOVE", (0, 1), (-1, 1), 1, self.PDF_LINECOLOR)],
|
"_pdf_style": [
|
||||||
|
("LINEABOVE", (0, 1), (-1, 1), 1, self.PDF_LINECOLOR),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
P.append(t)
|
P.append(t)
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ from sco_utils import (
|
|||||||
NOTES_NEUTRALISE,
|
NOTES_NEUTRALISE,
|
||||||
EVALUATION_NORMALE,
|
EVALUATION_NORMALE,
|
||||||
EVALUATION_RATTRAPAGE,
|
EVALUATION_RATTRAPAGE,
|
||||||
|
EVALUATION_SESSION2,
|
||||||
)
|
)
|
||||||
from sco_exceptions import ScoException
|
from sco_exceptions import ScoException
|
||||||
from notesdb import EditableTable, quote_html
|
from notesdb import EditableTable, quote_html
|
||||||
@ -242,7 +243,10 @@ def do_moduleimpl_moyennes(context, nt, mod):
|
|||||||
|
|
||||||
if e["etat"]["evalattente"]:
|
if e["etat"]["evalattente"]:
|
||||||
attente = True
|
attente = True
|
||||||
if e["evaluation_type"] == EVALUATION_RATTRAPAGE:
|
if (
|
||||||
|
e["evaluation_type"] == EVALUATION_RATTRAPAGE
|
||||||
|
or e["evaluation_type"] == EVALUATION_SESSION2
|
||||||
|
):
|
||||||
if eval_rattr:
|
if eval_rattr:
|
||||||
# !!! plusieurs rattrapages !
|
# !!! plusieurs rattrapages !
|
||||||
diag_info.update(
|
diag_info.update(
|
||||||
@ -344,7 +348,7 @@ def do_moduleimpl_moyennes(context, nt, mod):
|
|||||||
if diag_info:
|
if diag_info:
|
||||||
diag_info["moduleimpl_id"] = moduleimpl_id
|
diag_info["moduleimpl_id"] = moduleimpl_id
|
||||||
R[etudid] = user_moy
|
R[etudid] = user_moy
|
||||||
# Note de rattrapage ?
|
# Note de rattrapage ou deuxième session ?
|
||||||
if eval_rattr:
|
if eval_rattr:
|
||||||
if eval_rattr["notes"].has_key(etudid):
|
if eval_rattr["notes"].has_key(etudid):
|
||||||
note = eval_rattr["notes"][etudid]["value"]
|
note = eval_rattr["notes"][etudid]["value"]
|
||||||
@ -353,9 +357,15 @@ def do_moduleimpl_moyennes(context, nt, mod):
|
|||||||
R[etudid] = note
|
R[etudid] = note
|
||||||
else:
|
else:
|
||||||
note_sur_20 = note * 20.0 / eval_rattr["note_max"]
|
note_sur_20 = note * 20.0 / eval_rattr["note_max"]
|
||||||
|
if eval_rattr["evaluation_type"] == EVALUATION_RATTRAPAGE:
|
||||||
|
# rattrapage classique: prend la meilleure note entre moyenne
|
||||||
|
# module et note eval rattrapage
|
||||||
if note_sur_20 > R[etudid]:
|
if note_sur_20 > R[etudid]:
|
||||||
# log('note_sur_20=%s' % note_sur_20)
|
# log('note_sur_20=%s' % note_sur_20)
|
||||||
R[etudid] = note_sur_20
|
R[etudid] = note_sur_20
|
||||||
|
elif eval_rattr["evaluation_type"] == EVALUATION_SESSION2:
|
||||||
|
# rattrapage type "deuxième session": remplace la note moyenne
|
||||||
|
R[etudid] = note_sur_20
|
||||||
|
|
||||||
return R, valid_evals, attente, diag_info
|
return R, valid_evals, attente, diag_info
|
||||||
|
|
||||||
|
19
sco_dept.py
19
sco_dept.py
@ -129,11 +129,12 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
|
|||||||
)
|
)
|
||||||
|
|
||||||
H.append(
|
H.append(
|
||||||
"""<p><form action="Notes/view_formsemestre_by_etape">
|
"""<p><form action="%s/view_formsemestre_by_etape">
|
||||||
Chercher étape courante: <input name="etape_apo" type="text" size="8"></input>
|
Chercher étape courante: <input name="etape_apo" type="text" size="8" spellcheck="false"></input>
|
||||||
</form
|
</form
|
||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
|
% context.NotesURL()
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
authuser = REQUEST.AUTHENTICATED_USER
|
||||||
@ -155,9 +156,10 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8"></input>
|
|||||||
"""<hr>
|
"""<hr>
|
||||||
<h3>Exports Apogée</h3>
|
<h3>Exports Apogée</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="stdlink" href="Notes/semset_page">Années scolaires / exports Apogée</a></li>
|
<li><a class="stdlink" href="%s/semset_page">Années scolaires / exports Apogée</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
"""
|
"""
|
||||||
|
% context.NotesURL()
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
H.append(
|
H.append(
|
||||||
@ -175,9 +177,9 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8"></input>
|
|||||||
def _sem_table(context, sems):
|
def _sem_table(context, sems):
|
||||||
"""Affiche liste des semestres, utilisée pour semestres en cours"""
|
"""Affiche liste des semestres, utilisée pour semestres en cours"""
|
||||||
tmpl = """<tr class="%(trclass)s">%(tmpcode)s
|
tmpl = """<tr class="%(trclass)s">%(tmpcode)s
|
||||||
<td class="semicon">%(lockimg)s <a href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s#groupes">%(groupicon)s</a></td>
|
<td class="semicon">%(lockimg)s <a href="%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s#groupes">%(groupicon)s</a></td>
|
||||||
<td class="datesem">%(mois_debut)s</td><td class="datesem"><a title="%(session_id)s">-</a> %(mois_fin)s</td>
|
<td class="datesem">%(mois_debut)s</td><td class="datesem"><a title="%(session_id)s">-</a> %(mois_fin)s</td>
|
||||||
<td><a class="stdlink" href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a>
|
<td><a class="stdlink" href="%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a>
|
||||||
<span class="respsem">(%(responsable_name)s)</span>
|
<span class="respsem">(%(responsable_name)s)</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -199,6 +201,7 @@ def _sem_table(context, sems):
|
|||||||
cur_idx = sem["semestre_id"]
|
cur_idx = sem["semestre_id"]
|
||||||
else:
|
else:
|
||||||
sem["trclass"] = ""
|
sem["trclass"] = ""
|
||||||
|
sem["notes_url"] = context.NotesURL()
|
||||||
H.append(tmpl % sem)
|
H.append(tmpl % sem)
|
||||||
H.append("</table>")
|
H.append("</table>")
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
@ -245,14 +248,16 @@ def _sem_table_gt(context, sems, showcodes=False):
|
|||||||
def _style_sems(context, sems):
|
def _style_sems(context, sems):
|
||||||
"""ajoute quelques attributs de présentation pour la table"""
|
"""ajoute quelques attributs de présentation pour la table"""
|
||||||
for sem in sems:
|
for sem in sems:
|
||||||
|
sem["notes_url"] = context.NotesURL()
|
||||||
sem["_groupicon_target"] = (
|
sem["_groupicon_target"] = (
|
||||||
"Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % sem
|
"%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s"
|
||||||
|
% sem
|
||||||
)
|
)
|
||||||
sem["_formsemestre_id_class"] = "blacktt"
|
sem["_formsemestre_id_class"] = "blacktt"
|
||||||
sem["dash_mois_fin"] = '<a title="%(session_id)s"></a> %(anneescolaire)s' % sem
|
sem["dash_mois_fin"] = '<a title="%(session_id)s"></a> %(anneescolaire)s' % sem
|
||||||
sem["_dash_mois_fin_class"] = "datesem"
|
sem["_dash_mois_fin_class"] = "datesem"
|
||||||
sem["titre_resp"] = (
|
sem["titre_resp"] = (
|
||||||
"""<a class="stdlink" href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a>
|
"""<a class="stdlink" href="%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a>
|
||||||
<span class="respsem">(%(responsable_name)s)</span>"""
|
<span class="respsem">(%(responsable_name)s)</span>"""
|
||||||
% sem
|
% sem
|
||||||
)
|
)
|
||||||
|
@ -108,7 +108,9 @@ def do_evaluation_delete(context, REQUEST, evaluation_id):
|
|||||||
# news
|
# news
|
||||||
mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
|
mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
|
||||||
mod["moduleimpl_id"] = M["moduleimpl_id"]
|
mod["moduleimpl_id"] = M["moduleimpl_id"]
|
||||||
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
|
mod["url"] = (
|
||||||
|
context.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
|
||||||
|
)
|
||||||
sco_news.add(
|
sco_news.add(
|
||||||
context,
|
context,
|
||||||
REQUEST,
|
REQUEST,
|
||||||
@ -133,11 +135,6 @@ def do_evaluation_etat(
|
|||||||
à ce module ont des notes)
|
à ce module ont des notes)
|
||||||
evalattente est vrai s'il ne manque que des notes en attente
|
evalattente est vrai s'il ne manque que des notes en attente
|
||||||
"""
|
"""
|
||||||
# global _DEE_TOT
|
|
||||||
# t0=time.time()
|
|
||||||
# if evaluation_id == 'GEAEVAL82883':
|
|
||||||
# log('do_evaluation_etat: evaluation_id=%s partition_id=%s sfp=%s' % (evaluation_id, partition_id, select_first_partition))
|
|
||||||
|
|
||||||
nb_inscrits = len(
|
nb_inscrits = len(
|
||||||
sco_groups.do_evaluation_listeetuds_groups(
|
sco_groups.do_evaluation_listeetuds_groups(
|
||||||
context, evaluation_id, getallstudents=True
|
context, evaluation_id, getallstudents=True
|
||||||
@ -232,6 +229,7 @@ def do_evaluation_etat(
|
|||||||
if (
|
if (
|
||||||
(TotalNbMissing > 0)
|
(TotalNbMissing > 0)
|
||||||
and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
|
and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
|
||||||
|
and (E["evaluation_type"] != scu.EVALUATION_SESSION2)
|
||||||
and not is_malus
|
and not is_malus
|
||||||
):
|
):
|
||||||
complete = False
|
complete = False
|
||||||
@ -245,6 +243,9 @@ def do_evaluation_etat(
|
|||||||
evalattente = True
|
evalattente = True
|
||||||
else:
|
else:
|
||||||
evalattente = False
|
evalattente = False
|
||||||
|
# mais ne met pas en attente les evals immediates sans aucune notes:
|
||||||
|
if E["publish_incomplete"] != "0" and nb_notes == 0:
|
||||||
|
evalattente = False
|
||||||
|
|
||||||
# Calcul moyenne dans chaque groupe de TD
|
# Calcul moyenne dans chaque groupe de TD
|
||||||
gr_moyennes = [] # group : {moy,median, nb_notes}
|
gr_moyennes = [] # group : {moy,median, nb_notes}
|
||||||
@ -268,11 +269,6 @@ def do_evaluation_etat(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
gr_moyennes.sort(key=operator.itemgetter("group_name"))
|
gr_moyennes.sort(key=operator.itemgetter("group_name"))
|
||||||
# log('gr_moyennes=%s' % gr_moyennes)
|
|
||||||
# _DEE_TOT += (time.time() - t0)
|
|
||||||
# log('%s\t_DEE_TOT=%f' % (evaluation_id, _DEE_TOT))
|
|
||||||
# if evaluation_id == 'GEAEVAL82883':
|
|
||||||
# logCallStack()
|
|
||||||
|
|
||||||
# retourne mapping
|
# retourne mapping
|
||||||
return {
|
return {
|
||||||
@ -896,12 +892,20 @@ def evaluation_create_form(
|
|||||||
notes, en sus des moyennes de modules. Attention, cette option n'empêche pas la publication sur
|
notes, en sus des moyennes de modules. Attention, cette option n'empêche pas la publication sur
|
||||||
les bulletins en version "longue" (la note est donc visible par les étudiants sur le portail).
|
les bulletins en version "longue" (la note est donc visible par les étudiants sur le portail).
|
||||||
</p><p class="help">
|
</p><p class="help">
|
||||||
La modalité "rattrapage" permet de définir une évaluation dont les notes remplaceront les moyennes du modules
|
Les modalités "rattrapage" et "deuxième session" définissent des évaluations prises en compte de
|
||||||
si elles sont meilleures que celles calculées. Dans ce cas, le coefficient est ignoré, et toutes les notes n'ont
|
façon spéciale: </p>
|
||||||
|
<ul>
|
||||||
|
<li>les notes d'une évaluation de "rattrapage" remplaceront les moyennes du module
|
||||||
|
<em>si elles sont meilleures que celles calculées</em>.</li>
|
||||||
|
<li>les notes de "deuxième session" remplacent, lorsqu'elles sont saisies, la moyenne de l'étudiant
|
||||||
|
à ce module, même si la note de deuxième session est plus faible.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="help">
|
||||||
|
Dans ces deux cas, le coefficient est ignoré, et toutes les notes n'ont
|
||||||
pas besoin d'être rentrées.
|
pas besoin d'être rentrées.
|
||||||
</p>
|
</p>
|
||||||
<p class="help">
|
<p class="help">
|
||||||
Les évaluations des modules de type "malus" sont spéciales: le coefficient n'est pas utilisé.
|
Par ailleurs, les évaluations des modules de type "malus" sont toujours spéciales: le coefficient n'est pas utilisé.
|
||||||
Les notes de malus sont toujours comprises entre -20 et 20. Les points sont soustraits à la moyenne
|
Les notes de malus sont toujours comprises entre -20 et 20. Les points sont soustraits à la moyenne
|
||||||
de l'UE à laquelle appartient le module malus (si la note est négative, la moyenne est donc augmentée).
|
de l'UE à laquelle appartient le module malus (si la note est négative, la moyenne est donc augmentée).
|
||||||
</p>
|
</p>
|
||||||
@ -1024,9 +1028,17 @@ def evaluation_create_form(
|
|||||||
{
|
{
|
||||||
"input_type": "menu",
|
"input_type": "menu",
|
||||||
"title": "Modalité",
|
"title": "Modalité",
|
||||||
"allowed_values": (scu.EVALUATION_NORMALE, scu.EVALUATION_RATTRAPAGE),
|
"allowed_values": (
|
||||||
|
scu.EVALUATION_NORMALE,
|
||||||
|
scu.EVALUATION_RATTRAPAGE,
|
||||||
|
scu.EVALUATION_SESSION2,
|
||||||
|
),
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"labels": ("Normale", "Rattrapage"),
|
"labels": (
|
||||||
|
"Normale",
|
||||||
|
"Rattrapage (remplace si meilleure note)",
|
||||||
|
"Deuxième session (remplace toujours)",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
109
sco_find_etud.py
109
sco_find_etud.py
@ -58,7 +58,7 @@ def form_search_etud(
|
|||||||
H.append(
|
H.append(
|
||||||
"""<form action="search_etud_in_dept" method="POST">
|
"""<form action="search_etud_in_dept" method="POST">
|
||||||
<b>%s</b>
|
<b>%s</b>
|
||||||
<input type="text" name="expnom" width=12 value="">
|
<input type="text" name="expnom" width="12" spellcheck="false" value="">
|
||||||
<input type="submit" value="Chercher">
|
<input type="submit" value="Chercher">
|
||||||
<br/>(entrer une partie du nom)
|
<br/>(entrer une partie du nom)
|
||||||
"""
|
"""
|
||||||
@ -96,71 +96,52 @@ def form_search_etud(
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
# was chercheEtud()
|
def search_etud_in_dept(context, expnom="", REQUEST=None):
|
||||||
def search_etud_in_dept(
|
"""Page recherche d'un etudiant.
|
||||||
context,
|
|
||||||
expnom=None,
|
Affiche la fiche de l'étudiant, ou, si la recherche donne plusieurs résultats, la liste des étudianst
|
||||||
dest_url="ficheEtud",
|
correspondants.
|
||||||
parameters={},
|
Appelée par boite de recherche barre latérale gauche.
|
||||||
parameters_keys="",
|
|
||||||
add_headers=True, # complete page
|
Args:
|
||||||
title=None,
|
expnom: string, regexp sur le nom ou un code_nip ou un etudid
|
||||||
REQUEST=None,
|
|
||||||
):
|
|
||||||
"""Page recherche d'un etudiant
|
|
||||||
expnom est un regexp sur le nom ou un code_nip
|
|
||||||
dest_url est la page sur laquelle on sera redirigé après choix
|
|
||||||
parameters spécifie des arguments additionnels à passer à l'URL (en plus de etudid)
|
|
||||||
"""
|
"""
|
||||||
if type(expnom) == ListType:
|
dest_url = "ficheEtud"
|
||||||
expnom = expnom[0]
|
if len(expnom) > 1:
|
||||||
q = []
|
etuds = context.getEtudInfo(filled=1, etudid=expnom, REQUEST=REQUEST)
|
||||||
if parameters:
|
if len(etuds) != 1:
|
||||||
for param in parameters.keys():
|
|
||||||
q.append("%s=%s" % (param, parameters[param]))
|
|
||||||
elif parameters_keys:
|
|
||||||
for key in parameters_keys.split(","):
|
|
||||||
v = REQUEST.form.get(key, False)
|
|
||||||
if v:
|
|
||||||
q.append("%s=%s" % (key, v))
|
|
||||||
query_string = "&".join(q)
|
|
||||||
|
|
||||||
no_side_bar = True
|
|
||||||
H = []
|
|
||||||
if title:
|
|
||||||
H.append("<h2>%s</h2>" % title)
|
|
||||||
|
|
||||||
if scu.is_valid_code_nip(expnom):
|
if scu.is_valid_code_nip(expnom):
|
||||||
etuds = search_etuds_infos(context, code_nip=expnom, REQUEST=REQUEST)
|
etuds = search_etuds_infos(context, code_nip=expnom, REQUEST=REQUEST)
|
||||||
elif expnom:
|
else:
|
||||||
etuds = search_etuds_infos(context, expnom=expnom, REQUEST=REQUEST)
|
etuds = search_etuds_infos(context, expnom=expnom, REQUEST=REQUEST)
|
||||||
else:
|
else:
|
||||||
etuds = []
|
etuds = [] # si expnom est trop court, n'affiche rien
|
||||||
|
|
||||||
if len(etuds) == 1:
|
if len(etuds) == 1:
|
||||||
# va directement a la destination
|
# va directement a la destination
|
||||||
return REQUEST.RESPONSE.redirect(
|
return context.ficheEtud(etudid=etuds[0]["etudid"], REQUEST=REQUEST)
|
||||||
dest_url + "?etudid=%s&" % etuds[0]["etudid"] + query_string
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(etuds) > 0:
|
H = [
|
||||||
# Choix dans la liste des résultats:
|
context.sco_header(
|
||||||
H.append(
|
page_title="Recherche d'un étudiant",
|
||||||
|
no_side_bar=True,
|
||||||
|
init_qtip=True,
|
||||||
|
javascripts=["js/etud_info.js"],
|
||||||
|
REQUEST=REQUEST,
|
||||||
|
),
|
||||||
"""<h2>%d résultats pour "%s": choisissez un étudiant:</h2>"""
|
"""<h2>%d résultats pour "%s": choisissez un étudiant:</h2>"""
|
||||||
% (len(etuds), expnom)
|
% (len(etuds), expnom),
|
||||||
)
|
|
||||||
H.append(
|
|
||||||
form_search_etud(
|
form_search_etud(
|
||||||
context,
|
context,
|
||||||
dest_url=dest_url,
|
dest_url=dest_url,
|
||||||
parameters=parameters,
|
|
||||||
parameters_keys=parameters_keys,
|
|
||||||
REQUEST=REQUEST,
|
REQUEST=REQUEST,
|
||||||
title="Autre recherche",
|
title="Autre recherche",
|
||||||
)
|
),
|
||||||
)
|
]
|
||||||
|
if len(etuds) > 0:
|
||||||
|
# Choix dans la liste des résultats:
|
||||||
for e in etuds:
|
for e in etuds:
|
||||||
target = dest_url + "?etudid=%s&" % e["etudid"] + query_string
|
target = dest_url + "?etudid=%s&" % e["etudid"]
|
||||||
e["_nomprenom_target"] = target
|
e["_nomprenom_target"] = target
|
||||||
e["inscription_target"] = target
|
e["inscription_target"] = target
|
||||||
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
||||||
@ -185,34 +166,16 @@ def search_etud_in_dept(
|
|||||||
form_search_etud(
|
form_search_etud(
|
||||||
context,
|
context,
|
||||||
dest_url=dest_url,
|
dest_url=dest_url,
|
||||||
parameters=parameters,
|
|
||||||
parameters_keys=parameters_keys,
|
|
||||||
REQUEST=REQUEST,
|
REQUEST=REQUEST,
|
||||||
title="Autre recherche",
|
title="Autre recherche",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
H.append('<h2 style="color: red;">Aucun résultat pour "%s".</h2>' % expnom)
|
H.append('<h2 style="color: red;">Aucun résultat pour "%s".</h2>' % expnom)
|
||||||
add_headers = True
|
|
||||||
no_side_bar = False
|
|
||||||
H.append(
|
H.append(
|
||||||
"""<p class="help">La recherche porte sur tout ou partie du NOM ou du NIP de l'étudiant</p>"""
|
"""<p class="help">La recherche porte sur tout ou partie du NOM ou du NIP de l'étudiant</p>"""
|
||||||
)
|
)
|
||||||
if add_headers:
|
return "\n".join(H) + context.sco_footer(REQUEST)
|
||||||
return (
|
|
||||||
context.sco_header(
|
|
||||||
REQUEST,
|
|
||||||
page_title="Choix d'un étudiant",
|
|
||||||
init_qtip=True,
|
|
||||||
javascripts=["js/etud_info.js"],
|
|
||||||
no_side_bar=no_side_bar,
|
|
||||||
)
|
|
||||||
+ "\n".join(H)
|
|
||||||
+ context.sco_footer(REQUEST)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
|
|
||||||
# Was chercheEtudsInfo()
|
# Was chercheEtudsInfo()
|
||||||
@ -268,14 +231,14 @@ def search_etud_by_name(context, term, REQUEST=None):
|
|||||||
else:
|
else:
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
context,
|
context,
|
||||||
"SELECT nom, prenom FROM identite WHERE nom LIKE %(beginning)s ORDER BY nom",
|
"SELECT etudid, nom, prenom FROM identite WHERE nom LIKE %(beginning)s ORDER BY nom",
|
||||||
{"beginning": term + "%"},
|
{"beginning": term + "%"},
|
||||||
)
|
)
|
||||||
|
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
"label": "%s %s" % (x["nom"], scolars.format_prenom(x["prenom"])),
|
"label": "%s %s" % (x["nom"], scolars.format_prenom(x["prenom"])),
|
||||||
"value": x["nom"],
|
"value": x["etudid"],
|
||||||
}
|
}
|
||||||
for x in r
|
for x in r
|
||||||
]
|
]
|
||||||
@ -294,7 +257,7 @@ def form_search_etud_in_accessible_depts(context, REQUEST):
|
|||||||
return ""
|
return ""
|
||||||
return """<form action="table_etud_in_accessible_depts" method="POST">
|
return """<form action="table_etud_in_accessible_depts" method="POST">
|
||||||
<b>Chercher étudiant:</b>
|
<b>Chercher étudiant:</b>
|
||||||
<input type="text" name="expnom" width=12 value="">
|
<input type="text" name="expnom" width="12" spellcheck="false" value="">
|
||||||
<input type="submit" value="Chercher">
|
<input type="submit" value="Chercher">
|
||||||
<br/>(entrer une partie du nom ou le code NIP, cherche dans tous les départements autorisés)
|
<br/>(entrer une partie du nom ou le code NIP, cherche dans tous les départements autorisés)
|
||||||
"""
|
"""
|
||||||
|
@ -312,8 +312,11 @@ def _write_formsemestre_aux(context, sem, fieldname, valuename):
|
|||||||
"""fieldname: 'etapes' ou 'responsables'
|
"""fieldname: 'etapes' ou 'responsables'
|
||||||
valuename: 'etape_apo' ou 'responsable_id'
|
valuename: 'etape_apo' ou 'responsable_id'
|
||||||
"""
|
"""
|
||||||
if not "etapes" in sem:
|
if not fieldname in sem:
|
||||||
return
|
return
|
||||||
|
# uniquify
|
||||||
|
values = set([str(x) for x in sem[fieldname]])
|
||||||
|
|
||||||
cnx = context.GetDBConnexion(autocommit=False)
|
cnx = context.GetDBConnexion(autocommit=False)
|
||||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||||
tablename = "notes_formsemestre_" + fieldname
|
tablename = "notes_formsemestre_" + fieldname
|
||||||
@ -322,7 +325,7 @@ def _write_formsemestre_aux(context, sem, fieldname, valuename):
|
|||||||
"DELETE from " + tablename + " where formsemestre_id = %(formsemestre_id)s",
|
"DELETE from " + tablename + " where formsemestre_id = %(formsemestre_id)s",
|
||||||
{"formsemestre_id": sem["formsemestre_id"]},
|
{"formsemestre_id": sem["formsemestre_id"]},
|
||||||
)
|
)
|
||||||
for item in sem[fieldname]:
|
for item in values:
|
||||||
if item:
|
if item:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO "
|
"INSERT INTO "
|
||||||
@ -332,7 +335,7 @@ def _write_formsemestre_aux(context, sem, fieldname, valuename):
|
|||||||
+ ") VALUES (%(formsemestre_id)s, %("
|
+ ") VALUES (%(formsemestre_id)s, %("
|
||||||
+ valuename
|
+ valuename
|
||||||
+ ")s)",
|
+ ")s)",
|
||||||
{"formsemestre_id": sem["formsemestre_id"], valuename: str(item)},
|
{"formsemestre_id": sem["formsemestre_id"], valuename: item},
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
log("Warning: exception in write_formsemestre_aux !")
|
log("Warning: exception in write_formsemestre_aux !")
|
||||||
|
@ -81,7 +81,7 @@ def formsemestre_custommenu_edit(context, formsemestre_id, REQUEST=None):
|
|||||||
"""Dialog to edit the custom menu"""
|
"""Dialog to edit the custom menu"""
|
||||||
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
|
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
|
||||||
dest_url = (
|
dest_url = (
|
||||||
context.NotesURL() + "formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
context.NotesURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||||
)
|
)
|
||||||
H = [
|
H = [
|
||||||
context.html_sem_header(REQUEST, "Modification du menu du semestre ", sem),
|
context.html_sem_header(REQUEST, "Modification du menu du semestre ", sem),
|
||||||
|
@ -758,8 +758,12 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
|
|||||||
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
||||||
% (module_id, moduleimpl_id, etudids)
|
% (module_id, moduleimpl_id, etudids)
|
||||||
)
|
)
|
||||||
context.do_moduleimpl_inscrit_etuds(
|
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||||
moduleimpl_id, formsemestre_id, etudids, REQUEST=REQUEST
|
context,
|
||||||
|
moduleimpl_id,
|
||||||
|
formsemestre_id,
|
||||||
|
etudids,
|
||||||
|
REQUEST=REQUEST,
|
||||||
)
|
)
|
||||||
msg += [
|
msg += [
|
||||||
"inscription de %d étudiants au module %s"
|
"inscription de %d étudiants au module %s"
|
||||||
@ -1195,7 +1199,7 @@ def _reassociate_moduleimpls(
|
|||||||
)
|
)
|
||||||
for mod in modimpls:
|
for mod in modimpls:
|
||||||
mod["module_id"] = modules_old2new[mod["module_id"]]
|
mod["module_id"] = modules_old2new[mod["module_id"]]
|
||||||
context.do_moduleimpl_edit(mod, formsemestre_id=formsemestre_id, cnx=cnx)
|
sco_moduleimpl.do_moduleimpl_edit(context, mod, formsemestre_id=formsemestre_id)
|
||||||
# update decisions:
|
# update decisions:
|
||||||
events = scolars.scolar_events_list(cnx, args={"formsemestre_id": formsemestre_id})
|
events = scolars.scolar_events_list(cnx, args={"formsemestre_id": formsemestre_id})
|
||||||
for e in events:
|
for e in events:
|
||||||
@ -1261,7 +1265,7 @@ def formsemestre_delete(context, formsemestre_id, REQUEST=None):
|
|||||||
elif tf[0] == -1: # cancel
|
elif tf[0] == -1: # cancel
|
||||||
return REQUEST.RESPONSE.redirect(
|
return REQUEST.RESPONSE.redirect(
|
||||||
context.NotesURL()
|
context.NotesURL()
|
||||||
+ "formsemestre_status?formsemestre_id="
|
+ "/formsemestre_status?formsemestre_id="
|
||||||
+ formsemestre_id
|
+ formsemestre_id
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -505,10 +505,7 @@ def formsemestre_page_title(context, REQUEST):
|
|||||||
|
|
||||||
def fill_formsemestre(context, sem, REQUEST=None):
|
def fill_formsemestre(context, sem, REQUEST=None):
|
||||||
"""Add some useful fields to help display formsemestres"""
|
"""Add some useful fields to help display formsemestres"""
|
||||||
# Notes URL
|
notes_url = context.NotesURL()
|
||||||
notes_url = context.absolute_url()
|
|
||||||
if "/Notes" not in notes_url:
|
|
||||||
notes_url += "/Notes"
|
|
||||||
sem["notes_url"] = notes_url
|
sem["notes_url"] = notes_url
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
formsemestre_id = sem["formsemestre_id"]
|
||||||
if sem["etat"] != "1":
|
if sem["etat"] != "1":
|
||||||
|
@ -1163,8 +1163,8 @@ def formsemestre_validate_previous_ue(context, formsemestre_id, etudid, REQUEST=
|
|||||||
return "\n".join(H) + tf[1] + X + warn + context.sco_footer(REQUEST)
|
return "\n".join(H) + tf[1] + X + warn + context.sco_footer(REQUEST)
|
||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
return REQUEST.RESPONSE.redirect(
|
return REQUEST.RESPONSE.redirect(
|
||||||
context.ScoURL()
|
context.NotesURL()
|
||||||
+ "/Notes/formsemestre_status?formsemestre_id="
|
+ "/formsemestre_status?formsemestre_id="
|
||||||
+ formsemestre_id
|
+ formsemestre_id
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -1310,8 +1310,8 @@ def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST
|
|||||||
_invalidate_etud_formation_caches(context, etudid, sem["formation_id"])
|
_invalidate_etud_formation_caches(context, etudid, sem["formation_id"])
|
||||||
|
|
||||||
return REQUEST.RESPONSE.redirect(
|
return REQUEST.RESPONSE.redirect(
|
||||||
context.ScoURL()
|
context.NotesURL()
|
||||||
+ "/Notes/formsemestre_validate_previous_ue?etudid=%s&formsemestre_id=%s"
|
+ "/formsemestre_validate_previous_ue?etudid=%s&formsemestre_id=%s"
|
||||||
% (etudid, formsemestre_id)
|
% (etudid, formsemestre_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -578,6 +578,7 @@ def groups_table(
|
|||||||
else:
|
else:
|
||||||
filename = "etudiants_%s" % groups_infos.groups_filename
|
filename = "etudiants_%s" % groups_infos.groups_filename
|
||||||
|
|
||||||
|
prefs = context.get_preferences(groups_infos.formsemestre_id)
|
||||||
tab = GenTable(
|
tab = GenTable(
|
||||||
rows=groups_infos.members,
|
rows=groups_infos.members,
|
||||||
columns_ids=columns_ids,
|
columns_ids=columns_ids,
|
||||||
@ -591,8 +592,9 @@ def groups_table(
|
|||||||
html_class="table_leftalign table_listegroupe",
|
html_class="table_leftalign table_listegroupe",
|
||||||
xml_outer_tag="group_list",
|
xml_outer_tag="group_list",
|
||||||
xml_row_tag="etud",
|
xml_row_tag="etud",
|
||||||
text_fields_separator=",", # pour csvmoodle
|
text_fields_separator=prefs["moodle_csv_separator"],
|
||||||
preferences=context.get_preferences(groups_infos.formsemestre_id),
|
text_with_titles=prefs["moodle_csv_with_headerline"],
|
||||||
|
preferences=prefs,
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
if format == "html":
|
if format == "html":
|
||||||
@ -672,7 +674,10 @@ def groups_table(
|
|||||||
% (tab.base_url,),
|
% (tab.base_url,),
|
||||||
'<li><a class="stdlink" href="%s&format=moodlecsv">Fichier CSV pour Moodle (groupe sélectionné)</a></li>'
|
'<li><a class="stdlink" href="%s&format=moodlecsv">Fichier CSV pour Moodle (groupe sélectionné)</a></li>'
|
||||||
% (tab.base_url,),
|
% (tab.base_url,),
|
||||||
'<li><a class="stdlink" href="export_groups_as_moodle_csv?formsemestre_id=%s">Fichier CSV pour Moodle (tous les groupes)</a></li>'
|
"""<li>
|
||||||
|
<a class="stdlink" href="export_groups_as_moodle_csv?formsemestre_id=%s">Fichier CSV pour Moodle (tous les groupes)</a>
|
||||||
|
<em>(voir le paramétrage pour modifier le format des fichiers Moodle exportés)</em>
|
||||||
|
</li>"""
|
||||||
% groups_infos.formsemestre_id,
|
% groups_infos.formsemestre_id,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -784,9 +789,7 @@ def groups_table(
|
|||||||
context.Notes, etud, groups_infos.formsemestre_id
|
context.Notes, etud, groups_infos.formsemestre_id
|
||||||
)
|
)
|
||||||
m["parcours"] = Se.get_parcours_descr()
|
m["parcours"] = Se.get_parcours_descr()
|
||||||
m["codeparcours"], decisions_jury = sco_report.get_codeparcoursetud(
|
m["codeparcours"], _ = sco_report.get_codeparcoursetud(context.Notes, etud)
|
||||||
context.Notes, etud
|
|
||||||
)
|
|
||||||
|
|
||||||
def dicttakestr(d, keys):
|
def dicttakestr(d, keys):
|
||||||
r = []
|
r = []
|
||||||
@ -933,7 +936,7 @@ def form_choix_saisie_semaine(context, groups_infos, REQUEST=None):
|
|||||||
|
|
||||||
DateJour = time.strftime("%d/%m/%Y")
|
DateJour = time.strftime("%d/%m/%Y")
|
||||||
datelundi = sco_abs.ddmmyyyy(DateJour).prev_monday()
|
datelundi = sco_abs.ddmmyyyy(DateJour).prev_monday()
|
||||||
FA = [] # formulaire avec menu saisi hebdo des absences
|
FA = [] # formulaire avec menu saisie hebdo des absences
|
||||||
FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">')
|
FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">')
|
||||||
FA.append('<input type="hidden" name="datelundi" value="%s"/>' % datelundi)
|
FA.append('<input type="hidden" name="datelundi" value="%s"/>' % datelundi)
|
||||||
FA.append('<input type="hidden" name="moduleimpl_id" value="%s"/>' % moduleimpl_id)
|
FA.append('<input type="hidden" name="moduleimpl_id" value="%s"/>' % moduleimpl_id)
|
||||||
@ -955,12 +958,13 @@ def export_groups_as_moodle_csv(context, formsemestre_id=None, REQUEST=None):
|
|||||||
"""
|
"""
|
||||||
if not formsemestre_id:
|
if not formsemestre_id:
|
||||||
raise ScoValueError("missing parameter: formsemestre_id")
|
raise ScoValueError("missing parameter: formsemestre_id")
|
||||||
partitions, partitions_etud_groups = sco_groups.get_formsemestre_groups(
|
_, partitions_etud_groups = sco_groups.get_formsemestre_groups(
|
||||||
context, formsemestre_id, with_default=True
|
context, formsemestre_id, with_default=True
|
||||||
)
|
)
|
||||||
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
|
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
|
||||||
moodle_sem_name = sem["session_id"]
|
moodle_sem_name = sem["session_id"]
|
||||||
|
|
||||||
|
columns_ids = ("email", "semestre_groupe")
|
||||||
T = []
|
T = []
|
||||||
for partition_id in partitions_etud_groups:
|
for partition_id in partitions_etud_groups:
|
||||||
partition = sco_groups.get_partition(context, partition_id)
|
partition = sco_groups.get_partition(context, partition_id)
|
||||||
@ -975,11 +979,14 @@ def export_groups_as_moodle_csv(context, formsemestre_id=None, REQUEST=None):
|
|||||||
elts.append(group_name)
|
elts.append(group_name)
|
||||||
T.append({"email": etud["email"], "semestre_groupe": "-".join(elts)})
|
T.append({"email": etud["email"], "semestre_groupe": "-".join(elts)})
|
||||||
# Make table
|
# Make table
|
||||||
|
prefs = context.get_preferences(formsemestre_id)
|
||||||
tab = GenTable(
|
tab = GenTable(
|
||||||
rows=T,
|
rows=T,
|
||||||
columns_ids=("email", "semestre_groupe"),
|
columns_ids=("email", "semestre_groupe"),
|
||||||
filename=moodle_sem_name + "-moodle",
|
filename=moodle_sem_name + "-moodle",
|
||||||
text_fields_separator=",",
|
titles={x: x for x in columns_ids},
|
||||||
preferences=context.get_preferences(formsemestre_id),
|
text_fields_separator=prefs["moodle_csv_separator"],
|
||||||
|
text_with_titles=prefs["moodle_csv_with_headerline"],
|
||||||
|
preferences=prefs,
|
||||||
)
|
)
|
||||||
return tab.make_page(context, format="csv", REQUEST=REQUEST)
|
return tab.make_page(context, format="csv", REQUEST=REQUEST)
|
||||||
|
@ -34,6 +34,7 @@ import sco_utils as scu
|
|||||||
from sco_utils import (
|
from sco_utils import (
|
||||||
EVALUATION_NORMALE,
|
EVALUATION_NORMALE,
|
||||||
EVALUATION_RATTRAPAGE,
|
EVALUATION_RATTRAPAGE,
|
||||||
|
EVALUATION_SESSION2,
|
||||||
)
|
)
|
||||||
from sco_permissions import ScoEtudInscrit, ScoAbsChange
|
from sco_permissions import ScoEtudInscrit, ScoAbsChange
|
||||||
from notes_log import log
|
from notes_log import log
|
||||||
@ -259,9 +260,10 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
|
|||||||
ScoAbsChange, context
|
ScoAbsChange, context
|
||||||
) and sco_formsemestre.sem_est_courant(context, sem):
|
) and sco_formsemestre.sem_est_courant(context, sem):
|
||||||
datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday()
|
datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday()
|
||||||
|
group_id = sco_groups.get_default_group(context, formsemestre_id)
|
||||||
H.append(
|
H.append(
|
||||||
'<span class="moduleimpl_abs_link"><a class="stdlink" href="Absences/SignaleAbsenceGrHebdo?formsemestre_id=%s&moduleimpl_id=%s&datelundi=%s">Saisie Absences hebdo.</a></span>'
|
'<span class="moduleimpl_abs_link"><a class="stdlink" href="Absences/SignaleAbsenceGrHebdo?formsemestre_id=%s&moduleimpl_id=%s&datelundi=%s&group_ids=%s">Saisie Absences hebdo.</a></span>'
|
||||||
% (formsemestre_id, moduleimpl_id, datelundi)
|
% (formsemestre_id, moduleimpl_id, datelundi, group_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
H.append("</td></tr></table>")
|
H.append("</td></tr></table>")
|
||||||
@ -340,8 +342,8 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
|
|||||||
partition_id=partition_id,
|
partition_id=partition_id,
|
||||||
select_first_partition=True,
|
select_first_partition=True,
|
||||||
)
|
)
|
||||||
if eval["evaluation_type"] == EVALUATION_RATTRAPAGE:
|
if eval["evaluation_type"] in (EVALUATION_RATTRAPAGE, EVALUATION_SESSION2):
|
||||||
tr_class = "mievr_rattr"
|
tr_class = "mievr mievr_rattr"
|
||||||
else:
|
else:
|
||||||
tr_class = "mievr"
|
tr_class = "mievr"
|
||||||
tr_class_1 = "mievr"
|
tr_class_1 = "mievr"
|
||||||
@ -360,7 +362,13 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
|
|||||||
)
|
)
|
||||||
H.append(" <em>%(description)s</em>" % eval)
|
H.append(" <em>%(description)s</em>" % eval)
|
||||||
if eval["evaluation_type"] == EVALUATION_RATTRAPAGE:
|
if eval["evaluation_type"] == EVALUATION_RATTRAPAGE:
|
||||||
H.append("""<span class="mievr_rattr">rattrapage</span>""")
|
H.append(
|
||||||
|
"""<span class="mievr_rattr" title="remplace si meilleure note">rattrapage</span>"""
|
||||||
|
)
|
||||||
|
elif eval["evaluation_type"] == EVALUATION_SESSION2:
|
||||||
|
H.append(
|
||||||
|
"""<span class="mievr_rattr" title="remplace autres notes">session 2</span>"""
|
||||||
|
)
|
||||||
if etat["last_modif"]:
|
if etat["last_modif"]:
|
||||||
H.append(
|
H.append(
|
||||||
"""<span class="mievr_lastmodif">(dernière modif le %s)</span>"""
|
"""<span class="mievr_lastmodif">(dernière modif le %s)</span>"""
|
||||||
|
@ -153,6 +153,10 @@ def ficheEtud(context, etudid=None, REQUEST=None):
|
|||||||
"fiche d'informations sur un etudiant"
|
"fiche d'informations sur un etudiant"
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
authuser = REQUEST.AUTHENTICATED_USER
|
||||||
cnx = context.GetDBConnexion()
|
cnx = context.GetDBConnexion()
|
||||||
|
if etudid and REQUEST:
|
||||||
|
# la sidebar est differente s'il y a ou pas un etudid
|
||||||
|
# voir html_sidebar.sidebar()
|
||||||
|
REQUEST.form["etudid"] = etudid
|
||||||
args = make_etud_args(etudid=etudid, REQUEST=REQUEST)
|
args = make_etud_args(etudid=etudid, REQUEST=REQUEST)
|
||||||
etuds = scolars.etudident_list(cnx, args)
|
etuds = scolars.etudident_list(cnx, args)
|
||||||
if not etuds:
|
if not etuds:
|
||||||
|
@ -168,7 +168,7 @@ PREF_CATEGORIES = (
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"feuilles",
|
"feuilles",
|
||||||
{"title": "Mise en forme des feuilles (Absences, Trombinoscopes, ...)"},
|
{"title": "Mise en forme des feuilles (Absences, Trombinoscopes, Moodle, ...)"},
|
||||||
),
|
),
|
||||||
("pe", {"title": "Avis de poursuites d'études"}),
|
("pe", {"title": "Avis de poursuites d'études"}),
|
||||||
("edt", {"title": "Connexion avec le logiciel d'emplois du temps"}),
|
("edt", {"title": "Connexion avec le logiciel d'emplois du temps"}),
|
||||||
@ -1637,6 +1637,28 @@ Année scolaire: %(anneescolaire)s
|
|||||||
"only_global": True,
|
"only_global": True,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
# Exports pour Moodle:
|
||||||
|
(
|
||||||
|
"moodle_csv_with_headerline",
|
||||||
|
{
|
||||||
|
"initvalue": 0,
|
||||||
|
"title": "Inclure une ligne d'en-têtes dans les fichiers CSV pour Moodle",
|
||||||
|
"input_type": "boolcheckbox",
|
||||||
|
"labels": ["non", "oui"],
|
||||||
|
"only_global": True,
|
||||||
|
"category": "feuilles",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"moodle_csv_separator",
|
||||||
|
{
|
||||||
|
"initvalue": ",",
|
||||||
|
"title": "séparateur de colonnes dans les fichiers CSV pour Moodle",
|
||||||
|
"size": 2,
|
||||||
|
"only_global": True,
|
||||||
|
"category": "feuilles",
|
||||||
|
},
|
||||||
|
),
|
||||||
# Experimental: avis poursuite d'études
|
# Experimental: avis poursuite d'études
|
||||||
(
|
(
|
||||||
"NomResponsablePE",
|
"NomResponsablePE",
|
||||||
@ -2040,7 +2062,7 @@ function set_global_pref(el, pref_name) {
|
|||||||
)
|
)
|
||||||
dest_url = (
|
dest_url = (
|
||||||
self.context.NotesURL()
|
self.context.NotesURL()
|
||||||
+ "formsemestre_status?formsemestre_id=%s" % self.formsemestre_id
|
+ "/formsemestre_status?formsemestre_id=%s" % self.formsemestre_id
|
||||||
)
|
)
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return "\n".join(H) + tf[1] + self.context.sco_footer(REQUEST)
|
return "\n".join(H) + tf[1] + self.context.sco_footer(REQUEST)
|
||||||
|
@ -824,10 +824,10 @@ def _pvjury_pdf_type(
|
|||||||
# Signature du directeur
|
# Signature du directeur
|
||||||
objects += sco_pdf.makeParas(
|
objects += sco_pdf.makeParas(
|
||||||
"""<para spaceBefore="10mm" align="right">
|
"""<para spaceBefore="10mm" align="right">
|
||||||
Le %s, %s</para>"""
|
%s, %s</para>"""
|
||||||
% (
|
% (
|
||||||
context.get_preference("DirectorTitle", formsemestre_id) or "",
|
|
||||||
context.get_preference("DirectorName", formsemestre_id) or "",
|
context.get_preference("DirectorName", formsemestre_id) or "",
|
||||||
|
context.get_preference("DirectorTitle", formsemestre_id) or "",
|
||||||
),
|
),
|
||||||
style,
|
style,
|
||||||
)
|
)
|
||||||
|
@ -416,7 +416,7 @@ def make_formsemestre_recapcomplet(
|
|||||||
l.append(fmtnum(scu.fmt_note(t[0], keep_numeric=keep_numeric))) # moy_gen
|
l.append(fmtnum(scu.fmt_note(t[0], keep_numeric=keep_numeric))) # moy_gen
|
||||||
# Ajoute rangs dans groupes seulement si CSV ou XLS
|
# Ajoute rangs dans groupes seulement si CSV ou XLS
|
||||||
if format[:3] == "xls" or format == "csv":
|
if format[:3] == "xls" or format == "csv":
|
||||||
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
rang_gr, _, gr_name = sco_bulletins.get_etud_rangs_groups(
|
||||||
context, etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
context, etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -758,7 +758,9 @@ def make_formsemestre_recapcomplet(
|
|||||||
H.append("</table>")
|
H.append("</table>")
|
||||||
return "\n".join(H), "", "html"
|
return "\n".join(H), "", "html"
|
||||||
elif format == "csv":
|
elif format == "csv":
|
||||||
CSV = scu.CSV_LINESEP.join([scu.CSV_FIELDSEP.join(str(x)) for x in F])
|
CSV = scu.CSV_LINESEP.join(
|
||||||
|
[scu.CSV_FIELDSEP.join([str(x) for x in l]) for l in F]
|
||||||
|
)
|
||||||
semname = sem["titre_num"].replace(" ", "_")
|
semname = sem["titre_num"].replace(" ", "_")
|
||||||
date = time.strftime("%d-%m-%Y")
|
date = time.strftime("%d-%m-%Y")
|
||||||
filename = "notes_modules-%s-%s.csv" % (semname, date)
|
filename = "notes_modules-%s-%s.csv" % (semname, date)
|
||||||
|
@ -111,6 +111,7 @@ NOTES_MENTIONS_LABS = (
|
|||||||
|
|
||||||
EVALUATION_NORMALE = 0
|
EVALUATION_NORMALE = 0
|
||||||
EVALUATION_RATTRAPAGE = 1
|
EVALUATION_RATTRAPAGE = 1
|
||||||
|
EVALUATION_SESSION2 = 2
|
||||||
|
|
||||||
|
|
||||||
def fmt_note(val, note_max=None, keep_numeric=False):
|
def fmt_note(val, note_max=None, keep_numeric=False):
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
# le département via l'interface web (Zope)
|
# le département via l'interface web (Zope)
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 [-r] dept [script...]"
|
echo "Usage: $0 [-h] [-r] [-x] dept [script...]"
|
||||||
echo "Lance un environnement interactif python/ScoDoc"
|
echo "Lance un environnement interactif python/ScoDoc"
|
||||||
echo " -r: supprime et recrée le département (attention: efface la base !)"
|
echo " -r: supprime et recrée le département (attention: efface la base !)"
|
||||||
|
echo " -x: exit après exécution des scripts, donc mode non interactif"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,24 +21,38 @@ cd /opt/scodoc/Products/ScoDoc || exit 2
|
|||||||
source config/config.sh
|
source config/config.sh
|
||||||
source config/utils.sh
|
source config/utils.sh
|
||||||
|
|
||||||
if [ $# -lt 1 ]
|
RECREATE_DEPT=0
|
||||||
then
|
PYTHON_INTERACTIVE="-i"
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" = "-r" ]
|
while [ -n "$1" ]; do
|
||||||
then
|
PARAM="$1"
|
||||||
|
[ "${PARAM::1}" != "-" ] && break
|
||||||
|
case $PARAM in
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-r)
|
||||||
|
RECREATE_DEPT=1
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
PYTHON_INTERACTIVE=""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: unknown parameter \"$PARAM\""
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
shift
|
shift
|
||||||
recreate_dept=1
|
done
|
||||||
else
|
|
||||||
recreate_dept=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
DEPT="$1"
|
DEPT="$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
if [ "$recreate_dept" = 1 ]
|
if [ "$RECREATE_DEPT" = 1 ]
|
||||||
then
|
then
|
||||||
cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg
|
cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg
|
||||||
if [ -e "$cfg_pathname" ]
|
if [ -e "$cfg_pathname" ]
|
||||||
@ -48,13 +63,17 @@ then
|
|||||||
# systemctl start scodoc
|
# systemctl start scodoc
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cmd="from __future__ import print_function;from Zope2 import configure;configure('/opt/scodoc/etc/zope.conf');import Zope2; app=Zope2.app();from debug import *;context = go_dept(app, '""$DEPT""');"
|
cmd="from __future__ import print_function;from Zope2 import configure;configure('/opt/scodoc/etc/zope.conf');import Zope2; app=Zope2.app();from debug import *;context = go_dept(app, '""$DEPT""', verbose=False);"
|
||||||
|
|
||||||
for f in "$@"
|
for f in "$@"
|
||||||
do
|
do
|
||||||
cmd="${cmd}exec(open(\"${f}\").read());"
|
cmd="${cmd}exec(open(\"${f}\").read());"
|
||||||
done
|
done
|
||||||
|
|
||||||
/opt/zope213/bin/python -i -c "$cmd"
|
if [ -z "$PYTHON_INTERACTIVE" ]
|
||||||
|
then
|
||||||
|
/opt/zope213/bin/python -c "$cmd"
|
||||||
|
else
|
||||||
|
/opt/zope213/bin/python "$PYTHON_INTERACTIVE" -c "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
@ -11,12 +11,21 @@ Utiliser comme:
|
|||||||
"""
|
"""
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
# La variable context est définie par le script de lancement
|
||||||
|
# l'affecte ainsi pour évietr les warnins pylint:
|
||||||
|
context = context # pylint: disable=undefined-variable
|
||||||
|
REQUEST = REQUEST # pylint: disable=undefined-variable
|
||||||
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
||||||
import sco_utils
|
import sco_utils
|
||||||
import sco_abs
|
import sco_abs
|
||||||
import sco_abs_views
|
import sco_abs_views
|
||||||
|
import sco_bulletins
|
||||||
|
import sco_evaluations
|
||||||
|
import sco_codes_parcours
|
||||||
|
import sco_parcours_dut
|
||||||
|
import sco_formsemestre_validation
|
||||||
|
|
||||||
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
|
G = sco_fake_gen.ScoFake(context.Notes)
|
||||||
G.verbose = False
|
G.verbose = False
|
||||||
|
|
||||||
# --- Création d'étudiants
|
# --- Création d'étudiants
|
||||||
@ -61,12 +70,65 @@ e = G.create_evaluation(
|
|||||||
coefficient=1.0,
|
coefficient=1.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Saisie notes
|
# --- Saisie toutes les notes de l'évaluation
|
||||||
for etud in etuds:
|
for etud in etuds:
|
||||||
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
evaluation=e, etud=etud, note=float(random.randint(0, 20))
|
evaluation=e, etud=etud, note=float(random.randint(0, 20))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- Vérifie que les notes sont prises en compte:
|
||||||
|
b = sco_bulletins.formsemestre_bulletinetud_dict(
|
||||||
|
context.Notes, sem["formsemestre_id"], etud["etudid"], REQUEST=REQUEST
|
||||||
|
)
|
||||||
|
# Toute les notes sont saisies, donc eval complète
|
||||||
|
etat = sco_evaluations.do_evaluation_etat(context.Notes, e["evaluation_id"])
|
||||||
|
assert etat["evalcomplete"]
|
||||||
|
# Un seul module, donc moy gen == note module
|
||||||
|
assert b["ues"][0]["cur_moy_ue_txt"] == b["ues"][0]["modules"][0]["mod_moy_txt"]
|
||||||
|
# Note au module égale à celle de l'éval
|
||||||
|
assert (
|
||||||
|
b["ues"][0]["modules"][0]["mod_moy_txt"]
|
||||||
|
== b["ues"][0]["modules"][0]["evaluations"][0]["note_txt"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Une autre évaluation
|
||||||
|
e2 = G.create_evaluation(
|
||||||
|
moduleimpl_id=mi["moduleimpl_id"],
|
||||||
|
jour="02/01/2020",
|
||||||
|
description="evaluation test 2",
|
||||||
|
coefficient=1.0,
|
||||||
|
)
|
||||||
|
# Saisie les notes des 5 premiers étudiants:
|
||||||
|
for etud in etuds[:5]:
|
||||||
|
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
|
evaluation=e2, etud=etud, note=float(random.randint(0, 20))
|
||||||
|
)
|
||||||
|
# Cette éval n'est pas complète
|
||||||
|
etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"])
|
||||||
|
assert etat["evalcomplete"] == False
|
||||||
|
# la première éval est toujours complète:
|
||||||
|
etat = sco_evaluations.do_evaluation_etat(context.Notes, e["evaluation_id"])
|
||||||
|
assert etat["evalcomplete"]
|
||||||
|
|
||||||
|
# Modifie l'évaluation 2 pour "prise en compte immédiate"
|
||||||
|
e2["publish_incomplete"] = "1"
|
||||||
|
context.Notes.do_evaluation_edit(REQUEST, e2)
|
||||||
|
etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"])
|
||||||
|
assert etat["evalcomplete"] == False
|
||||||
|
assert etat["nb_att"] == 0 # il n'y a pas de notes (explicitement) en attente
|
||||||
|
assert etat["evalattente"] # mais l'eval est en attente (prise en compte immédiate)
|
||||||
|
|
||||||
|
# Saisie des notes qui manquent:
|
||||||
|
for etud in etuds[5:]:
|
||||||
|
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
|
evaluation=e2, etud=etud, note=float(random.randint(0, 20))
|
||||||
|
)
|
||||||
|
etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"])
|
||||||
|
assert etat["evalcomplete"]
|
||||||
|
assert etat["nb_att"] == 0
|
||||||
|
assert not etat["evalattente"] # toutes les notes sont présentes
|
||||||
|
|
||||||
# --- Saisie absences
|
# --- Saisie absences
|
||||||
etudid = etuds[0]["etudid"]
|
etudid = etuds[0]["etudid"]
|
||||||
|
|
||||||
@ -91,3 +153,27 @@ _ = sco_abs_views.doJustifAbsence(
|
|||||||
a = sco_abs.getAbsSemEtud(context.Absences, sem, etudid)
|
a = sco_abs.getAbsSemEtud(context.Absences, sem, etudid)
|
||||||
assert a.CountAbs() == 3
|
assert a.CountAbs() == 3
|
||||||
assert a.CountAbsJust() == 1
|
assert a.CountAbsJust() == 1
|
||||||
|
|
||||||
|
# --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance
|
||||||
|
# on n'a pas encore saisi de décisions
|
||||||
|
assert not sco_parcours_dut.formsemestre_has_decisions(context, sem["formsemestre_id"])
|
||||||
|
# Saisie d'un décision AJ, non assidu
|
||||||
|
etudid = etuds[-1]["etudid"]
|
||||||
|
sco_parcours_dut.formsemestre_validate_ues(
|
||||||
|
context.Notes,
|
||||||
|
sem["formsemestre_id"],
|
||||||
|
etudid,
|
||||||
|
sco_codes_parcours.AJ,
|
||||||
|
False,
|
||||||
|
REQUEST=REQUEST,
|
||||||
|
)
|
||||||
|
assert sco_parcours_dut.formsemestre_has_decisions(
|
||||||
|
context.Notes, sem["formsemestre_id"]
|
||||||
|
)
|
||||||
|
# Suppression de la décision
|
||||||
|
sco_formsemestre_validation.formsemestre_validation_suppress_etud(
|
||||||
|
context.Notes, sem["formsemestre_id"], etudid
|
||||||
|
)
|
||||||
|
assert not sco_parcours_dut.formsemestre_has_decisions(
|
||||||
|
context.Notes, sem["formsemestre_id"]
|
||||||
|
)
|
||||||
|
@ -11,8 +11,10 @@ Utiliser comme:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# La variable context est définie par le script de lancement
|
||||||
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
# l'affecte ainsi pour évietr les warnins pylint:
|
||||||
|
context = context # pylint: disable=undefined-variable
|
||||||
|
import scotests.sco_fake_gen as sco_fake_gen
|
||||||
import sco_utils as scu
|
import sco_utils as scu
|
||||||
import sco_moduleimpl
|
import sco_moduleimpl
|
||||||
|
|
||||||
|
@ -10,13 +10,15 @@ Utiliser comme:
|
|||||||
scotests/scointeractive.sh -r TEST00 scotests/test_capitalisation.py
|
scotests/scointeractive.sh -r TEST00 scotests/test_capitalisation.py
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# La variable context est définie par le script de lancement
|
||||||
|
# l'affecte ainsi pour éviter les warnings pylint:
|
||||||
|
context = context # pylint: disable=undefined-variable
|
||||||
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
||||||
import sco_utils
|
import sco_utils
|
||||||
import sco_codes_parcours
|
import sco_codes_parcours
|
||||||
import sco_modalites
|
import sco_modalites
|
||||||
|
|
||||||
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
|
G = sco_fake_gen.ScoFake(context.Notes)
|
||||||
G.verbose = False
|
G.verbose = False
|
||||||
|
|
||||||
# --- Création d'étudiants
|
# --- Création d'étudiants
|
||||||
|
@ -12,16 +12,24 @@ Utiliser comme:
|
|||||||
scotests/scointeractive.sh -r TEST00 scotests/test_demissions.py
|
scotests/scointeractive.sh -r TEST00 scotests/test_demissions.py
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
# La variable context est définie par le script de lancement
|
||||||
|
# l'affecte ainsi pour évietr les warnins pylint:
|
||||||
|
context = context # pylint: disable=undefined-variable
|
||||||
|
REQUEST = REQUEST # pylint: disable=undefined-variable
|
||||||
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
|
||||||
import sco_utils
|
import sco_utils
|
||||||
import sco_bulletins
|
import sco_bulletins
|
||||||
|
|
||||||
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
|
G = sco_fake_gen.ScoFake(context.Notes)
|
||||||
G.verbose = False
|
G.verbose = False
|
||||||
|
|
||||||
|
nb_etuds = 10
|
||||||
# --- Création d'étudiants
|
# --- Création d'étudiants
|
||||||
etuds = [G.create_etud(code_nip=None) for _ in range(2)]
|
etuds = [G.create_etud(code_nip=None) for _ in range(nb_etuds)]
|
||||||
# --- Mise en place formation
|
# --- Mise en place formation
|
||||||
form, ue_list, mod_list = G.setup_formation(
|
form, ue_list, mod_list = G.setup_formation(
|
||||||
nb_semestre=1, titre="Essai 1", acronyme="ESS01"
|
nb_semestre=1, titre="Essai 1", acronyme="ESS01"
|
||||||
@ -59,3 +67,35 @@ print(bul["moy_gen"])
|
|||||||
|
|
||||||
assert bul["moy_gen"] == "NA"
|
assert bul["moy_gen"] == "NA"
|
||||||
assert bul["ins"][0]["etat"] == "D"
|
assert bul["ins"][0]["etat"] == "D"
|
||||||
|
|
||||||
|
# ------------ Billets d'absences
|
||||||
|
etud = etuds[1] # non demissionnaire
|
||||||
|
d = sem["date_debut_iso"]
|
||||||
|
d_beg = datetime.datetime(*[int(x) for x in d.split("-")])
|
||||||
|
d_end = d_beg + datetime.timedelta(2)
|
||||||
|
description = "billet test 0"
|
||||||
|
x = context.Absences.AddBilletAbsence(
|
||||||
|
d_beg.isoformat(),
|
||||||
|
d_end.isoformat(),
|
||||||
|
description=description,
|
||||||
|
etudid=etud["etudid"],
|
||||||
|
REQUEST=REQUEST,
|
||||||
|
)
|
||||||
|
#
|
||||||
|
billet_id = re.search(r"billet_id value=\"([A-Z0-9]+)\"", x).group(1)
|
||||||
|
context.Absences.deleteBilletAbsence(billet_id, REQUEST=REQUEST, dialog_confirmed=True)
|
||||||
|
j = context.Absences.listeBilletsEtud(
|
||||||
|
etudid=etud["etudid"], REQUEST=REQUEST, format="json"
|
||||||
|
)
|
||||||
|
assert len(json.loads(j)) == 0
|
||||||
|
x = context.Absences.AddBilletAbsence(
|
||||||
|
d_beg.isoformat(),
|
||||||
|
d_end.isoformat(),
|
||||||
|
description=description,
|
||||||
|
etudid=etud["etudid"],
|
||||||
|
REQUEST=REQUEST,
|
||||||
|
)
|
||||||
|
j = context.Absences.listeBilletsEtud(
|
||||||
|
etudid=etud["etudid"], REQUEST=REQUEST, format="json"
|
||||||
|
)
|
||||||
|
assert json.loads(j)[0]["description"] == description
|
||||||
|
@ -1261,9 +1261,14 @@ tr.mievr_rattr {
|
|||||||
background-color:#dddddd;
|
background-color:#dddddd;
|
||||||
}
|
}
|
||||||
span.mievr_rattr {
|
span.mievr_rattr {
|
||||||
|
display: inline-block;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: blue;
|
font-size: 80%;
|
||||||
|
color: white;
|
||||||
|
background-color: orangered;
|
||||||
margin-left: 2em;
|
margin-left: 2em;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 2px;;
|
||||||
border: 1px solid red;
|
border: 1px solid red;
|
||||||
padding: 1px 3px 1px 3px;
|
padding: 1px 3px 1px 3px;
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,20 @@
|
|||||||
// JS Ajax code for SignaleAbsenceGrSemestre
|
// JS Ajax code for SignaleAbsenceGrSemestre
|
||||||
// Contributed by YLB
|
// Contributed by YLB
|
||||||
|
|
||||||
function ajaxFunction(mod, etudid, dat){
|
function ajaxFunction(mod, etudid, dat) {
|
||||||
var ajaxRequest; // The variable that makes Ajax possible!
|
var ajaxRequest; // The variable that makes Ajax possible!
|
||||||
|
|
||||||
try{
|
try {
|
||||||
// Opera 8.0+, Firefox, Safari
|
// Opera 8.0+, Firefox, Safari
|
||||||
ajaxRequest = new XMLHttpRequest();
|
ajaxRequest = new XMLHttpRequest();
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
// Internet Explorer Browsers
|
// Internet Explorer Browsers
|
||||||
try{
|
try {
|
||||||
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
|
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try{
|
try {
|
||||||
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
// Something went wrong
|
// Something went wrong
|
||||||
alert("Your browser broke!");
|
alert("Your browser broke!");
|
||||||
return false;
|
return false;
|
||||||
@ -23,22 +23,25 @@ function ajaxFunction(mod, etudid, dat){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create a function that will receive data sent from the server
|
// Create a function that will receive data sent from the server
|
||||||
ajaxRequest.onreadystatechange = function(){
|
ajaxRequest.onreadystatechange = function () {
|
||||||
if(ajaxRequest.readyState == 4 && ajaxRequest.status == 200){
|
if (ajaxRequest.readyState == 4 && ajaxRequest.status == 200) {
|
||||||
document.getElementById("AjaxDiv").innerHTML=ajaxRequest.responseText;
|
document.getElementById("AjaxDiv").innerHTML = ajaxRequest.responseText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ajaxRequest.open("POST", "doSignaleAbsenceGrSemestre", true);
|
ajaxRequest.open("POST", "doSignaleAbsenceGrSemestre", true);
|
||||||
ajaxRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||||
oForm = document.forms[0];
|
var oSelectOne = $("#abs_form")[0].elements["moduleimpl_id"];
|
||||||
oSelectOne = oForm.elements["moduleimpl_id"];
|
var index = oSelectOne.selectedIndex;
|
||||||
index = oSelectOne.selectedIndex;
|
var modul_id = oSelectOne.options[index].value;
|
||||||
modul_id = oSelectOne.options[index].value;
|
|
||||||
if (mod == 'add') {
|
if (mod == 'add') {
|
||||||
ajaxRequest.send("reply=0&moduleimpl_id=" + modul_id +"&abslist:list=" + etudid + ":" + dat);
|
ajaxRequest.send("reply=0&moduleimpl_id=" + modul_id + "&abslist:list=" + etudid + ":" + dat);
|
||||||
}
|
}
|
||||||
if (mod == 'remove') {
|
if (mod == 'remove') {
|
||||||
ajaxRequest.send("reply=0&moduleimpl_id=" + modul_id +"&etudids=" + etudid + "&dates=" + dat);
|
ajaxRequest.send("reply=0&moduleimpl_id=" + modul_id + "&etudids=" + etudid + "&dates=" + dat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
function change_moduleimpl(url) {
|
||||||
|
document.location = url + '&moduleimpl_id=' + document.getElementById('moduleimpl_id').value;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// JS for all ScoDoc pages (using jQuery UI)
|
// JS for all ScoDoc pages (using jQuery UI)
|
||||||
|
|
||||||
|
|
||||||
$(function() {
|
$(function () {
|
||||||
// Autocomplete recherche etudiants par nom
|
// Autocomplete recherche etudiants par nom
|
||||||
$("#in-expnom").autocomplete(
|
$("#in-expnom").autocomplete(
|
||||||
{
|
{
|
||||||
@ -9,7 +9,8 @@ $(function() {
|
|||||||
minLength: 2, // min nb of chars before suggest
|
minLength: 2, // min nb of chars before suggest
|
||||||
position: { collision: 'flip' }, // automatic menu position up/down
|
position: { collision: 'flip' }, // automatic menu position up/down
|
||||||
source: "search_etud_by_name",
|
source: "search_etud_by_name",
|
||||||
select: function(event, ui) {
|
select: function (event, ui) {
|
||||||
|
$("#in-expnom").val(ui.item.value);
|
||||||
$("#form-chercheetud").submit();
|
$("#form-chercheetud").submit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -20,25 +21,25 @@ $(function() {
|
|||||||
buttonImage: '/ScoDoc/static/icons/calendar_img.png',
|
buttonImage: '/ScoDoc/static/icons/calendar_img.png',
|
||||||
buttonImageOnly: true,
|
buttonImageOnly: true,
|
||||||
dateFormat: 'dd/mm/yy',
|
dateFormat: 'dd/mm/yy',
|
||||||
duration : 'fast',
|
duration: 'fast',
|
||||||
});
|
});
|
||||||
$('.datepicker').datepicker('option', $.extend({showMonthAfterYear: false},
|
$('.datepicker').datepicker('option', $.extend({ showMonthAfterYear: false },
|
||||||
$.datepicker.regional['fr']));
|
$.datepicker.regional['fr']));
|
||||||
|
|
||||||
/* Barre menu */
|
/* Barre menu */
|
||||||
var sco_menu_position = {my: "left top", at: "left bottom"};
|
var sco_menu_position = { my: "left top", at: "left bottom" };
|
||||||
$("#sco_menu").menu({
|
$("#sco_menu").menu({
|
||||||
position: sco_menu_position,
|
position: sco_menu_position,
|
||||||
blur: function() {
|
blur: function () {
|
||||||
$(this).menu("option", "position", sco_menu_position);
|
$(this).menu("option", "position", sco_menu_position);
|
||||||
},
|
},
|
||||||
focus: function(e, ui) {
|
focus: function (e, ui) {
|
||||||
if ($("#sco_menu").get(0) !== $(ui).get(0).item.parent().get(0)) {
|
if ($("#sco_menu").get(0) !== $(ui).get(0).item.parent().get(0)) {
|
||||||
$(this).menu("option", "position", {my: "left top", at: "right top"});
|
$(this).menu("option", "position", { my: "left top", at: "right top" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).mouseleave(function(x, y) {
|
}).mouseleave(function (x, y) {
|
||||||
$( "#sco_menu" ).menu('collapseAll');
|
$("#sco_menu").menu('collapseAll');
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#sco_menu > li > a > span").switchClass("ui-icon-carat-1-e", "ui-icon-carat-1-s");
|
$("#sco_menu > li > a > span").switchClass("ui-icon-carat-1-e", "ui-icon-carat-1-s");
|
||||||
@ -46,8 +47,8 @@ $(function() {
|
|||||||
/* Les menus isoles dropdown */
|
/* Les menus isoles dropdown */
|
||||||
$(".sco_dropdown_menu").menu({
|
$(".sco_dropdown_menu").menu({
|
||||||
position: sco_menu_position
|
position: sco_menu_position
|
||||||
}).mouseleave(function(x, y) {
|
}).mouseleave(function (x, y) {
|
||||||
$( ".sco_dropdown_menu" ).menu('collapseAll');
|
$(".sco_dropdown_menu").menu('collapseAll');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$(".sco_dropdown_menu > li > a > span").switchClass("ui-icon-carat-1-e", "ui-icon-carat-1-s");
|
$(".sco_dropdown_menu > li > a > span").switchClass("ui-icon-carat-1-e", "ui-icon-carat-1-s");
|
||||||
@ -65,10 +66,10 @@ function sco_message(msg, color) {
|
|||||||
$('#sco_msg').css('color', color);
|
$('#sco_msg').css('color', color);
|
||||||
}
|
}
|
||||||
setTimeout(
|
setTimeout(
|
||||||
function() {
|
function () {
|
||||||
$('#sco_msg').fadeOut(
|
$('#sco_msg').fadeOut(
|
||||||
'slow',
|
'slow',
|
||||||
function() {
|
function () {
|
||||||
$('#sco_msg').html('');
|
$('#sco_msg').html('');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -83,7 +84,7 @@ function get_query_args() {
|
|||||||
var vars = {};
|
var vars = {};
|
||||||
s.replace(
|
s.replace(
|
||||||
/[?&]+([^=&]+)=?([^&]*)?/gi, // regexp
|
/[?&]+([^=&]+)=?([^&]*)?/gi, // regexp
|
||||||
function( m, key, value ) { // callback
|
function (m, key, value) { // callback
|
||||||
vars[key] = value !== undefined ? value : '';
|
vars[key] = value !== undefined ? value : '';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -92,19 +93,19 @@ function get_query_args() {
|
|||||||
|
|
||||||
|
|
||||||
// Tables (gen_tables)
|
// Tables (gen_tables)
|
||||||
$(function() {
|
$(function () {
|
||||||
$('table.gt_table').DataTable( {
|
$('table.gt_table').DataTable({
|
||||||
"paging" : false,
|
"paging": false,
|
||||||
"searching" : false,
|
"searching": false,
|
||||||
"info" : false,
|
"info": false,
|
||||||
/* "autoWidth" : false, */
|
/* "autoWidth" : false, */
|
||||||
"fixedHeader" : {
|
"fixedHeader": {
|
||||||
"header": true,
|
"header": true,
|
||||||
"footer": true
|
"footer": true
|
||||||
},
|
},
|
||||||
"orderCellsTop": true, // cellules ligne 1 pour tri
|
"orderCellsTop": true, // cellules ligne 1 pour tri
|
||||||
"aaSorting": [ ], // Prevent initial sorting
|
"aaSorting": [], // Prevent initial sorting
|
||||||
} );
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user