Compare commits

...

28 Commits

Author SHA1 Message Date
b2e5fccec1 Ajouter 'toto' 2020-12-30 00:49:51 +01:00
1f4a32a485 Calcul formule dans modules sans note (pour malus) [contrib. JMP] 2020-12-29 23:16:40 +01:00
97f69ebc7d Bulletins: ajuste l affichage de la ligne module pour les modules malus (contrib. JMP) 2020-12-29 18:53:03 +01:00
d6bf8efdee TEST: set default sem resp. 2020-12-29 17:32:26 +01:00
8bf2255d82 typo 2020-12-28 23:47:36 +01:00
4847a2d12b black 2020-12-28 22:59:59 +01:00
978d4e64af ajout bouton "saisie à la semaine" dans le tab "Absences et feuilles" 2020-12-28 22:59:02 +01:00
7ad2a10894 shellcheck 2020-12-28 22:09:20 +01:00
dea24d0257 TESTS: scenario standard capitalisation 2020-12-28 21:49:53 +01:00
e7ccdee992 TESTS: scenario standard capitalisation 2020-12-28 21:42:22 +01:00
62a813494c Fix: bulletins 2020-12-28 14:43:12 +01:00
274d39a3ec Test basic: modules, sems, notes 2020-12-27 13:45:24 +01:00
0864917a5f Fix: moduleimpl_status ordre des partitions 2020-12-26 00:39:45 +01:00
ea388ea494 New testing tools 2020-12-26 00:11:55 +01:00
6af4c6a2fd Fix: Affichage moyenne promo par évaluation sur bulletin 2020-12-25 12:10:37 +01:00
2b5a470516 itre form saisie + pylint 2020-12-24 14:56:01 +01:00
7efe8cd194 Fix: partition par defaut sur tableau bord module 2020-12-24 11:47:13 +01:00
de6d0233ac Lien mail enseignants sur tableau bord semestre 2020-12-24 01:29:50 +01:00
fcbbc877bc Liste enseignants dans table description semestre (DS, Le Havre) 2020-12-24 01:00:41 +01:00
2f94c41821 documente debouche comme obsolete 2020-12-24 00:19:35 +01:00
d386f82146 pylint 2020-12-24 00:07:17 +01:00
ec006442c4 pylint 2020-12-23 23:49:11 +01:00
cf091d2c1a Fix: removed debug message raising an exception 2020-12-23 23:48:11 +01:00
d6e7bbc713 Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc 2020-12-23 22:28:26 +01:00
7794fff4a5 Fix: export excel des evaluations en attente 2020-12-23 22:27:21 +01:00
65575e3562 Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc 2020-12-23 17:22:14 +01:00
3b75dcfa13 Fix mode 2020-12-23 17:21:59 +01:00
9b5f766500 Fix: export Excel n'affiche pas certains DS 2020-12-23 17:19:22 +01:00
155 changed files with 1196 additions and 367 deletions

View File

@ -45,17 +45,19 @@ L'API de plus bas niveau est en gros:
""" """
import urllib import urllib
import datetime
import jaxml
# ---------------
from sco_zope import * from sco_zope import *
# --------------- # ---------------
import sco_utils as scu
from notesdb import * import notesdb
from notes_log import log from notes_log import log
from scolog import logdb from scolog import logdb
from sco_utils import * from sco_permissions import ScoAbsAddBillet, ScoAbsChange, ScoView
from sco_exceptions import ScoValueError, ScoInvalidDateError
# import notes_users
from TrivialFormulator import TrivialFormulator, TF from TrivialFormulator import TrivialFormulator, TF
from gen_tables import GenTable from gen_tables import GenTable
import scolars import scolars
@ -201,7 +203,7 @@ class ddmmyyyy:
return self.prev(self.weekday) return self.prev(self.weekday)
def __cmp__(self, other): def __cmp__(self, other):
"""return a negative integer if self < other, """return a negative integer if self < other,
zero if self == other, a positive integer if self > other""" zero if self == other, a positive integer if self > other"""
return int(self.time - other.time) return int(self.time - other.time)
@ -356,7 +358,7 @@ class ZAbsences(
estjust = _toboolean(estjust) estjust = _toboolean(estjust)
matin = _toboolean(matin) matin = _toboolean(matin)
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )", "insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )",
vars(), vars(),
@ -380,7 +382,7 @@ class ZAbsences(
raise ScoValueError("date justificatif trop loin dans le futur !") raise ScoValueError("date justificatif trop loin dans le futur !")
matin = _toboolean(matin) matin = _toboolean(matin)
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )", "insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )",
vars(), vars(),
@ -402,7 +404,7 @@ class ZAbsences(
# unpublished # unpublished
matin = _toboolean(matin) matin = _toboolean(matin)
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and estabs" req = "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and estabs"
if moduleimpl_id: if moduleimpl_id:
req += " and moduleimpl_id=%(moduleimpl_id)s" req += " and moduleimpl_id=%(moduleimpl_id)s"
@ -423,7 +425,7 @@ class ZAbsences(
# unpublished # unpublished
matin = _toboolean(matin) matin = _toboolean(matin)
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and ESTJUST AND NOT ESTABS", "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and ESTJUST AND NOT ESTABS",
vars(), vars(),
@ -450,7 +452,7 @@ class ZAbsences(
# """ # """
# # unpublished # # unpublished
# cnx = self.GetDBConnexion() # cnx = self.GetDBConnexion()
# cursor = cnx.cursor(cursor_factory=ScoDocCursor) # cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
# # supr les absences non justifiees # # supr les absences non justifiees
# cursor.execute("delete from absences where etudid=%(etudid)s and (not estjust) and moduleimpl_id=(moduleimpl_id)s and jour BETWEEN %(datedebut)s AND %(datefin)s", # cursor.execute("delete from absences where etudid=%(etudid)s and (not estjust) and moduleimpl_id=(moduleimpl_id)s and jour BETWEEN %(datedebut)s AND %(datefin)s",
# vars() ) # vars() )
@ -487,7 +489,7 @@ class ZAbsences(
self._AnnuleAbsence(etudid, jour, matin, moduleimpl_id, REQUEST) self._AnnuleAbsence(etudid, jour, matin, moduleimpl_id, REQUEST)
return return
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
# supr les absences non justifiees # supr les absences non justifiees
for date in dates: for date in dates:
cursor.execute( cursor.execute(
@ -534,7 +536,7 @@ class ZAbsences(
else: else:
modul = "" modul = ""
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"""SELECT COUNT(*) AS NbAbs FROM ( """SELECT COUNT(*) AS NbAbs FROM (
SELECT DISTINCT A.JOUR, A.MATIN SELECT DISTINCT A.JOUR, A.MATIN
@ -565,7 +567,7 @@ class ZAbsences(
else: else:
modul = "" modul = ""
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"""SELECT COUNT(*) AS NbAbsJust FROM ( """SELECT COUNT(*) AS NbAbsJust FROM (
SELECT DISTINCT A.JOUR, A.MATIN SELECT DISTINCT A.JOUR, A.MATIN
@ -588,7 +590,7 @@ class ZAbsences(
def _ListeAbsDate(self, etudid, beg_date, end_date): def _ListeAbsDate(self, etudid, beg_date, end_date):
# Liste des absences et justifs entre deux dates # Liste des absences et justifs entre deux dates
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"""SELECT jour, matin, estabs, estjust, description FROM ABSENCES A """SELECT jour, matin, estabs, estjust, description FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s WHERE A.ETUDID = %(etudid)s
@ -598,7 +600,6 @@ class ZAbsences(
vars(), vars(),
) )
Abs = cursor.dictfetchall() Abs = cursor.dictfetchall()
# log('ListeAbsDate: abs=%s' % Abs)
# remove duplicates # remove duplicates
A = {} # { (jour, matin) : abs } A = {} # { (jour, matin) : abs }
for a in Abs: for a in Abs:
@ -625,7 +626,6 @@ class ZAbsences(
# sort # sort
R = A.values() R = A.values()
R.sort(key=lambda x: (x["begin"])) R.sort(key=lambda x: (x["begin"]))
# log('R=%s' % R)
return R return R
security.declareProtected(ScoView, "ListeAbsJust") security.declareProtected(ScoView, "ListeAbsJust")
@ -633,7 +633,7 @@ class ZAbsences(
def ListeAbsJust(self, etudid, datedebut): def ListeAbsJust(self, etudid, datedebut):
"Liste des absences justifiees (par ordre chronologique)" "Liste des absences justifiees (par ordre chronologique)"
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"""SELECT DISTINCT A.ETUDID, A.JOUR, A.MATIN FROM ABSENCES A, ABSENCES B """SELECT DISTINCT A.ETUDID, A.JOUR, A.MATIN FROM ABSENCES A, ABSENCES B
WHERE A.ETUDID = %(etudid)s WHERE A.ETUDID = %(etudid)s
@ -654,7 +654,7 @@ class ZAbsences(
def ListeAbsNonJust(self, etudid, datedebut): def ListeAbsNonJust(self, etudid, datedebut):
"Liste des absences NON justifiees (par ordre chronologique)" "Liste des absences NON justifiees (par ordre chronologique)"
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
cursor.execute( cursor.execute(
"""SELECT ETUDID, JOUR, MATIN FROM ABSENCES A """SELECT ETUDID, JOUR, MATIN FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s WHERE A.ETUDID = %(etudid)s
@ -680,7 +680,7 @@ class ZAbsences(
Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées. Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées.
""" """
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = """SELECT DISTINCT ETUDID, JOUR, MATIN FROM ABSENCES A req = """SELECT DISTINCT ETUDID, JOUR, MATIN FROM ABSENCES A
WHERE A.ETUDID = %(etudid)s WHERE A.ETUDID = %(etudid)s
AND A.ESTJUST AND A.ESTJUST
@ -704,7 +704,7 @@ class ZAbsences(
"Description associee a l'absence" "Description associee a l'absence"
if not cursor: if not cursor:
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
a = a.copy() a = a.copy()
# a['jour'] = a['jour'].date() # a['jour'] = a['jour'].date()
if a["matin"]: # devrait etre booleen... :-( if a["matin"]: # devrait etre booleen... :-(
@ -732,7 +732,6 @@ class ZAbsences(
if desc: if desc:
return "(%s) %s" % (desc, module) return "(%s) %s" % (desc, module)
return desc
if module: if module:
return module return module
return "" return ""
@ -745,7 +744,7 @@ class ZAbsences(
is_just: idem is_just: idem
""" """
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A
WHERE A.jour = %(date)s WHERE A.jour = %(date)s
""" """
@ -769,7 +768,7 @@ class ZAbsences(
def ListeAbsNonJustJour(self, date, am=True, pm=True): def ListeAbsNonJustJour(self, date, am=True, pm=True):
"Liste des absences non justifiees ce jour" "Liste des absences non justifiees ce jour"
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
reqa = "" reqa = ""
if not am: if not am:
reqa += " AND NOT matin " reqa += " AND NOT matin "
@ -849,7 +848,7 @@ class ZAbsences(
def CalSelectWeek(self, year=None, REQUEST=None): def CalSelectWeek(self, year=None, REQUEST=None):
"display calendar allowing week selection" "display calendar allowing week selection"
if not year: if not year:
year = AnneeScolaire(REQUEST) year = scu.AnneeScolaire(REQUEST)
sems = sco_formsemestre.do_formsemestre_list(self) sems = sco_formsemestre.do_formsemestre_list(self)
if not sems: if not sems:
js = "" js = ""
@ -886,10 +885,9 @@ class ZAbsences(
security.declareProtected(ScoView, "ListMondays") security.declareProtected(ScoView, "ListMondays")
def ListMondays(self, year=None, REQUEST=None): def ListMondays(self, year=None, REQUEST=None):
"""return list of mondays (ISO dates), from september to june """return list of mondays (ISO dates), from september to june"""
"""
if not year: if not year:
year = AnneeScolaire(REQUEST) year = scu.AnneeScolaire(REQUEST)
d = ddmmyyyy("1/9/%d" % year, work_saturday=self.is_work_saturday()) d = ddmmyyyy("1/9/%d" % year, work_saturday=self.is_work_saturday())
while d.weekday != 0: while d.weekday != 0:
d = d.next() d = d.next()
@ -934,7 +932,7 @@ class ZAbsences(
): ):
"Saisie hebdomadaire des absences" "Saisie hebdomadaire des absences"
if not moduleimpl_id: if not moduleimpl_id:
moduleimp_id = None moduleimpl_id = None
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST self, group_ids, REQUEST=REQUEST
@ -963,10 +961,10 @@ class ZAbsences(
)[0] )[0]
# calcule dates jours de cette semaine # calcule dates jours de cette semaine
datessem = [DateDMYtoISO(datelundi)] # liste de dates iso "yyyy-mm-dd"
for jour in self.day_names()[1:]: datessem = [notesdb.DateDMYtoISO(datelundi)]
for _ in self.day_names()[1:]:
datessem.append(self.NextISODay(datessem[-1])) datessem.append(self.NextISODay(datessem[-1]))
# #
if groups_infos.tous_les_etuds_du_sem: if groups_infos.tous_les_etuds_du_sem:
gr_tit = "en" gr_tit = "en"
@ -1044,9 +1042,7 @@ class ZAbsences(
% {"menu_module": menu_module, "url": base_url, "sel": sel} % {"menu_module": menu_module, "url": base_url, "sel": sel}
) )
H += self._gen_form_saisie_groupe( H += self._gen_form_saisie_groupe(etuds, datessem, destination, moduleimpl_id)
etuds, self.day_names(), datessem, destination, None, moduleimpl_id
)
H.append(self.sco_footer(REQUEST)) H.append(self.sco_footer(REQUEST))
return "\n".join(H) return "\n".join(H)
@ -1063,8 +1059,7 @@ class ZAbsences(
moduleimpl_id=None, moduleimpl_id=None,
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)) # 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
@ -1083,15 +1078,12 @@ class ZAbsences(
] ]
if not moduleimpl_id: if not moduleimpl_id:
moduleimp_id = None moduleimpl_id = None
base_url_noweeks = ( base_url_noweeks = "SignaleAbsenceGrSemestre?datedebut=%s&amp;datefin=%s&amp;%s&amp;destination=%s" % (
"SignaleAbsenceGrSemestre?datedebut=%s&amp;datefin=%s&amp;%s&amp;destination=%s" datedebut,
% ( datefin,
datedebut, groups_infos.groups_query_args,
datefin, urllib.quote(destination),
groups_infos.groups_query_args,
urllib.quote(destination),
)
) )
base_url = ( base_url = (
base_url_noweeks + "&amp;nbweeks=%s" % nbweeks base_url_noweeks + "&amp;nbweeks=%s" % nbweeks
@ -1139,7 +1131,6 @@ class ZAbsences(
if moduleimpl_id: if moduleimpl_id:
url_link_semaines += "&amp;moduleimpl_id=" + moduleimpl_id url_link_semaines += "&amp;moduleimpl_id=" + moduleimpl_id
# #
colnames = [str(x) for x in dates]
dates = [x.ISO() for x in dates] dates = [x.ISO() for x in dates]
dayname = self.day_names()[jourdebut.weekday] dayname = self.day_names()[jourdebut.weekday]
@ -1147,9 +1138,9 @@ class ZAbsences(
gr_tit = "en" gr_tit = "en"
else: else:
if len(groups_infos.group_ids) > 1: if len(groups_infos.group_ids) > 1:
p = "des groupes" p = "des groupes "
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>"
) )
@ -1217,15 +1208,18 @@ class ZAbsences(
% {"menu_module": menu_module, "url": base_url, "sel": sel} % {"menu_module": menu_module, "url": base_url, "sel": sel}
) )
H += self._gen_form_saisie_groupe( H += self._gen_form_saisie_groupe(etuds, dates, destination, moduleimpl_id)
etuds, colnames, dates, destination, dayname, moduleimpl_id
)
H.append(self.sco_footer(REQUEST)) H.append(self.sco_footer(REQUEST))
return "\n".join(H) return "\n".join(H)
def _gen_form_saisie_groupe( def _gen_form_saisie_groupe(self, etuds, dates, destination="", moduleimpl_id=None):
self, etuds, colnames, dates, destination="", dayname="", moduleimpl_id=None """Formulaire saisie absences
):
Args:
etuds: liste des étudiants
dates: liste de dates iso, par exemple: [ '2020-12-24', ... ]
moduleimpl_id: optionnel, module concerné.
"""
H = [ H = [
""" """
<script type="text/javascript"> <script type="text/javascript">
@ -1252,25 +1246,29 @@ class ZAbsences(
""" """
% len(etuds) % len(etuds)
] ]
# Dates
odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates]
# Titres colonnes # Titres colonnes
if dayname: noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ]
for jour in colnames: jn = self.day_names()
H.append( for d in odates:
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">' idx_jour = d.weekday()
+ dayname noms_jours.append(jn[idx_jour])
+ "</th>" for jour in noms_jours:
)
H.append("</tr><tr><td>&nbsp;</td>")
for jour in colnames:
H.append( H.append(
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">' '<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">'
+ jour + jour
+ "</th>" + "</th>"
) )
H.append("</tr><tr><td>&nbsp;</td>") H.append("</tr><tr><td>&nbsp;</td>")
H.append("<th>AM</th><th>PM</th>" * len(colnames)) for d in odates:
H.append(
'<th colspan="2" width="100px" style="padding-left: 5px; padding-right: 5px;">'
+ d.strftime("%d/%m/%Y")
+ "</th>"
)
H.append("</tr><tr><td>&nbsp;</td>")
H.append("<th>AM</th><th>PM</th>" * len(dates))
H.append("</tr>") H.append("</tr>")
# #
if not etuds: if not etuds:
@ -1373,14 +1371,13 @@ class ZAbsences(
absjust_only=0, absjust_only=0,
REQUEST=None, REQUEST=None,
): ):
"""Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours """Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours"""
"""
absjust = self.ListeAbsJust(etudid=etudid, datedebut=datedebut) absjust = self.ListeAbsJust(etudid=etudid, datedebut=datedebut)
absnonjust = self.ListeAbsNonJust(etudid=etudid, datedebut=datedebut) absnonjust = self.ListeAbsNonJust(etudid=etudid, datedebut=datedebut)
# examens ces jours là ? # examens ces jours là ?
if with_evals: if with_evals:
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=notesdb.ScoDocCursor)
for a in absnonjust + absjust: for a in absnonjust + absjust:
cursor.execute( cursor.execute(
"""select eval.* """select eval.*
@ -1499,10 +1496,9 @@ class ZAbsences(
format="html", format="html",
REQUEST=None, REQUEST=None,
): ):
"""Liste les absences de groupes """Liste les absences de groupes"""
""" datedebut = notesdb.DateDMYtoISO(debut)
datedebut = DateDMYtoISO(debut) datefin = notesdb.DateDMYtoISO(fin)
datefin = DateDMYtoISO(fin)
# Informations sur les groupes à afficher: # Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST self, group_ids, REQUEST=REQUEST
@ -1603,7 +1599,7 @@ class ZAbsences(
base_url="%s&amp;formsemestre_id=%s&amp;debut=%s&amp;fin=%s" base_url="%s&amp;formsemestre_id=%s&amp;debut=%s&amp;fin=%s"
% (groups_infos.base_url, formsemestre_id, debut, fin), % (groups_infos.base_url, formsemestre_id, debut, fin),
filename="etat_abs_" filename="etat_abs_"
+ make_filename( + scu.make_filename(
"%s de %s" % (groups_infos.groups_filename, sem["titreannee"]) "%s de %s" % (groups_infos.groups_filename, sem["titreannee"])
), ),
caption=title, caption=title,
@ -1632,26 +1628,20 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
def EtatAbsencesDate( def EtatAbsencesDate(
self, group_ids=[], date=None, REQUEST=None # list of groups to display self, group_ids=[], date=None, REQUEST=None # list of groups to display
): ):
"""Etat des absences pour un groupe à une date donnée """Etat des absences pour un groupe à une date donnée"""
"""
# Informations sur les groupes à afficher: # Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
self, group_ids, REQUEST=REQUEST self, group_ids, REQUEST=REQUEST
) )
formsemestre_id = groups_infos.formsemestre_id
sem = sco_formsemestre.do_formsemestre_list(
self, {"formsemestre_id": formsemestre_id}
)[0]
H = [self.sco_header(page_title="Etat des absences", REQUEST=REQUEST)] H = [self.sco_header(page_title="Etat des absences", REQUEST=REQUEST)]
if date: if date:
dateiso = DateDMYtoISO(date) dateiso = notesdb.DateDMYtoISO(date)
nbetud = 0 nbetud = 0
t_nbabsjustam = 0 t_nbabsjustam = 0
t_nbabsam = 0 t_nbabsam = 0
t_nbabsjustpm = 0 t_nbabsjustpm = 0
t_nbabspm = 0 t_nbabspm = 0
etuds = self.getEtudInfoGroupes(groups_infos.group_ids) H.append("<h2>État des absences le %s</h2>" % date)
H.append("<h2>Etat des absences le %s</h2>" % date)
H.append( H.append(
"""<table border="0" cellspacing="4" cellpadding="0"> """<table border="0" cellspacing="4" cellpadding="0">
<tr><th>&nbsp;</th> <tr><th>&nbsp;</th>
@ -1738,7 +1728,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
etudid=etudid, code_nip=code_nip, REQUEST=REQUEST, filled=True etudid=etudid, code_nip=code_nip, REQUEST=REQUEST, filled=True
) )
if not etuds: if not etuds:
return log_unknown_etud(self, REQUEST=REQUEST) return scu.log_unknown_etud(self, REQUEST=REQUEST)
etud = etuds[0] etud = etuds[0]
# check dates # check dates
begin_date = ParseDateTimeUTC(begin) # may raises ValueError begin_date = ParseDateTimeUTC(begin) # may raises ValueError
@ -1763,7 +1753,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
if xml_reply: if xml_reply:
# Renvoie le nouveau billet en XML # Renvoie le nouveau billet en XML
if REQUEST: if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
billets = billet_absence_list(cnx, {"billet_id": billet_id}) billets = billet_absence_list(cnx, {"billet_id": billet_id})
tab = self._tableBillets(billets, etud=etud) tab = self._tableBillets(billets, etud=etud)
@ -1884,11 +1874,10 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoView, "listeBilletsEtud") security.declareProtected(ScoView, "listeBilletsEtud")
def listeBilletsEtud(self, etudid=False, REQUEST=None, format="html"): def listeBilletsEtud(self, etudid=False, REQUEST=None, format="html"):
"""Liste billets pour un etudiant """Liste billets pour un etudiant"""
"""
etuds = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST) etuds = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)
if not etuds: if not etuds:
return log_unknown_etud(self, format=format, REQUEST=REQUEST) return scu.log_unknown_etud(self, format=format, REQUEST=REQUEST)
etud = etuds[0] etud = etuds[0]
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
@ -1899,8 +1888,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoView, "XMLgetBilletsEtud") security.declareProtected(ScoView, "XMLgetBilletsEtud")
def XMLgetBilletsEtud(self, etudid=False, REQUEST=None): def XMLgetBilletsEtud(self, etudid=False, REQUEST=None):
"""Liste billets pour un etudiant """Liste billets pour un etudiant"""
"""
if not self.get_preference("handle_billets_abs"): if not self.get_preference("handle_billets_abs"):
return "" return ""
t0 = time.time() t0 = time.time()
@ -1937,8 +1925,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
security.declareProtected(ScoAbsChange, "deleteBilletAbsence") security.declareProtected(ScoAbsChange, "deleteBilletAbsence")
def deleteBilletAbsence(self, billet_id, REQUEST=None, dialog_confirmed=False): def deleteBilletAbsence(self, billet_id, REQUEST=None, dialog_confirmed=False):
"""Supprime un billet. """Supprime un billet."""
"""
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
billets = billet_absence_list(cnx, {"billet_id": billet_id}) billets = billet_absence_list(cnx, {"billet_id": billet_id})
if not billets: if not billets:
@ -2107,8 +2094,8 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
Abs = self._ListeAbsDate(etud["etudid"], beg_date, end_date) Abs = self._ListeAbsDate(etud["etudid"], beg_date, end_date)
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
doc = jaxml.XML_document(encoding=SCO_ENCODING) doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
doc.absences(etudid=etud["etudid"], beg_date=beg_date, end_date=end_date) doc.absences(etudid=etud["etudid"], beg_date=beg_date, end_date=end_date)
doc._push() doc._push()
for a in Abs: for a in Abs:
@ -2126,7 +2113,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
return repr(doc) return repr(doc)
_billet_absenceEditor = EditableTable( _billet_absenceEditor = notesdb.EditableTable(
"billet_absence", "billet_absence",
"billet_id", "billet_id",
( (
@ -2210,7 +2197,6 @@ def MonthTableTail():
def MonthTableBody( def MonthTableBody(
month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8 month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8
): ):
# log('XXX events=%s' % events)
firstday, nbdays = calendar.monthrange(year, month) firstday, nbdays = calendar.monthrange(year, month)
localtime = time.localtime() localtime = time.localtime()
current_weeknum = time.strftime("%U", localtime) current_weeknum = time.strftime("%U", localtime)
@ -2443,8 +2429,8 @@ class CAbsSemEtud:
self.sem = sco_formsemestre.get_formsemestre( self.sem = sco_formsemestre.get_formsemestre(
self.context, self.sem["formsemestre_id"] self.context, self.sem["formsemestre_id"]
) )
debut_sem = DateDMYtoISO(self.sem["date_debut"]) debut_sem = notesdb.DateDMYtoISO(self.sem["date_debut"])
fin_sem = DateDMYtoISO(self.sem["date_fin"]) fin_sem = notesdb.DateDMYtoISO(self.sem["date_fin"])
self._CountAbs = self.context.Absences.CountAbs( self._CountAbs = self.context.Absences.CountAbs(
etudid=self.etudid, debut=debut_sem, fin=fin_sem etudid=self.etudid, debut=debut_sem, fin=fin_sem

View File

@ -1617,7 +1617,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
security.declareProtected(ScoView, "view_module_abs") security.declareProtected(ScoView, "view_module_abs")
def view_module_abs(self, REQUEST, moduleimpl_id, format="html"): def view_module_abs(self, REQUEST, moduleimpl_id, format="html"):
"""Visulalisation des absences a un module""" """Visualisation des absences a un module"""
M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"])
debut_sem = DateDMYtoISO(sem["date_debut"]) debut_sem = DateDMYtoISO(sem["date_debut"])

View File

@ -284,6 +284,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
raise ValueError("nom de departement invalide") raise ValueError("nom de departement invalide")
if not pass2: if not pass2:
# 1- Creation de repertoire Dept # 1- Creation de repertoire Dept
log("creating Zope folder " + DeptId)
add_method = self.manage_addProduct["OFSP"].manage_addFolder add_method = self.manage_addProduct["OFSP"].manage_addFolder
add_method(DeptId, title="Site dept. " + DeptId) add_method(DeptId, title="Site dept. " + DeptId)
@ -291,10 +292,12 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
if not pass2: if not pass2:
# 2- Creation du repertoire Fotos # 2- Creation du repertoire Fotos
log("creating Zope folder %s/Fotos" % DeptId)
add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder
add_method("Fotos", title="Photos identites " + DeptId) add_method("Fotos", title="Photos identites " + DeptId)
# 3- Creation instance ScoDoc # 3- Creation instance ScoDoc
log("creating Zope ZScolar instance")
add_method = DeptFolder.manage_addProduct["ScoDoc"].manage_addZScolarForm add_method = DeptFolder.manage_addProduct["ScoDoc"].manage_addZScolarForm
return add_method(DeptId, REQUEST=REQUEST) return add_method(DeptId, REQUEST=REQUEST)

View File

@ -2037,49 +2037,8 @@ function tweakmenu( gname ) {
) )
if not edit: if not edit:
# creation d'un etudiant etud = scolars.create_etud(self, cnx, args=tf[2], REQUEST=REQUEST)
etudid = scolars.etudident_create( etudid = etud["etudid"]
cnx, tf[2], context=self, REQUEST=REQUEST
)
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
adresse_id = scolars.adresse_create(
cnx,
{
"etudid": etudid,
"typeadresse": "domicile",
"description": "(creation individuelle)",
},
)
# event
scolars.scolar_events_create(
cnx,
args={
"etudid": etudid,
"event_date": time.strftime("%d/%m/%Y"),
"formsemestre_id": None,
"event_type": "CREATION",
},
)
# log
logdb(
REQUEST,
cnx,
method="etudident_edit_form",
etudid=etudid,
msg="creation initiale",
)
etud = scolars.etudident_list(cnx, {"etudid": etudid})[0]
self.fillEtudsInfo([etud])
etud["url"] = "ficheEtud?etudid=%(etudid)s" % etud
sco_news.add(
self,
REQUEST,
typ=NEWS_INSCR,
object=None, # pas d'object pour ne montrer qu'un etudiant
text='Nouvel étudiant <a href="%(url)s">%(nomprenom)s</a>' % etud,
url=etud["url"],
)
else: else:
# modif d'un etudiant # modif d'un etudiant
scolars.etudident_edit(cnx, tf[2], context=self, REQUEST=REQUEST) scolars.etudident_edit(cnx, tf[2], context=self, REQUEST=REQUEST)

View File

@ -13,27 +13,45 @@
source config.sh source config.sh
source utils.sh source utils.sh
check_uid_root $0 check_uid_root "$0"
usage() {
echo "$0 [-n DEPT]"
echo "(default to interactive mode)"
exit 1
}
[ $# = 0 ] || [ $# = 2 ] || usage
echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): " if [ "$1" = "-n" ]
read DEPT then
interactive=0
if [ $# -lt 2 ]
then
usage
fi
DEPT=$2
else
interactive=1
echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
fi
if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]]
then then
echo 'Nom de departement invalide !' echo 'Nom de departement invalide !'
exit 1 exit 2
fi fi
export DEPT export DEPT
export db_name=SCO$(to_upper "$DEPT") db_name=SCO$(to_upper "$DEPT")
export db_name
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" ]
then then
echo 'Erreur: Il existe deja une configuration pour "'$DEPT'"' echo 'Erreur: Il existe deja une configuration pour "'"$DEPT"'"'
exit 1 exit 1
fi fi
@ -41,29 +59,39 @@ fi
init_postgres_user init_postgres_user
# ----------------------- Create database # ----------------------- Create database
su -c ./create_database.sh $POSTGRES_SUPERUSER su -c ./create_database.sh "$POSTGRES_SUPERUSER"
# ----------------------- Create tables # ----------------------- Create tables
# POSTGRES_USER == regular unix user (www-data) # POSTGRES_USER == regular unix user (www-data)
su -c ./initialize_database.sh $POSTGRES_USER if [ "$interactive" = 1 ]
# ----------------------- Enregistre fichier config
echo "dbname="$db_name > $cfg_pathname
# ----------------------- Force mise à jour
echo -n "Voulez vous mettre a jour ScoDoc (tres recommande) ? (y/n) [y] "
read ans
if [ "$(norm_ans "$ans")" != 'N' ]
then then
(cd "$SCODOC_DIR/config"; ./upgrade.sh) su -c ./initialize_database.sh "$POSTGRES_USER"
else
su -c ./initialize_database.sh "$POSTGRES_USER" > /dev/null 2>&1
fi fi
# -----------------------
echo
echo " Departement $DEPT cree" # ----------------------- Enregistre fichier config
echo echo "dbname=${db_name}" > "$cfg_pathname"
echo " Attention: la base de donnees n'a pas de copies de sauvegarde"
echo
echo " Maintenant, vous pouvez ajouter le departement via l'application web" if [ "$interactive" = 1 ]
echo " en suivant le lien \"Administration de ScoDoc\" sur la page d'accueil." then
echo # ----------------------- Force mise à jour
echo -n "Voulez vous mettre a jour ScoDoc (tres recommande) ? (y/n) [y] "
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
(cd "$SCODOC_DIR/config" || terminate "no config directory"; ./upgrade.sh)
fi
# -----------------------
echo
echo " Departement $DEPT cree"
echo
echo " Attention: la base de donnees n'a pas de copies de sauvegarde"
echo
echo " Maintenant, vous pouvez ajouter le departement via l'application web"
echo " en suivant le lien \"Administration de ScoDoc\" sur la page d'accueil."
echo
fi

View File

@ -18,46 +18,64 @@ source config.sh
source utils.sh source utils.sh
check_uid_root $0 check_uid_root $0
usage() {
echo echo "$0 [-n DEPT]"
echo "Ce script supprime la base de donnees ScoDoc d'un departement" echo "(default to interactive mode)"
echo exit 1
echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !" }
echo "faites le AVANT d'executer ce script !!!" [ $# = 0 ] || [ $# = 2 ] || usage
echo if [ "$1" = "-n" ]
echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): " then
read DEPT interactive=0
if [ $# -lt 2 ]
then
usage
fi
DEPT=$2
else
interactive=1
echo
echo "Ce script supprime la base de donnees ScoDoc d'un departement"
echo
echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !"
echo "faites le AVANT d'executer ce script !!!"
echo
echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): "
read -r DEPT
fi
if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]]
then then
echo "Nom de departement invalide !" echo "Nom de departement invalide !"
exit 1 exit 1
fi fi
export DEPT export DEPT
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" ]
then then
# arret de ScoDoc # arret de ScoDoc
scodocctl stop scodocctl stop
# suppression de la base postgres # suppression de la base postgres
db_name=$(cat $cfg_pathname | sed '/^dbname=*/!d; s///;q') db_name=$(cat "$cfg_pathname" | sed '/^dbname=*/!d; s///;q')
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"
# suppression du fichier de config # suppression du fichier de config
/bin/rm -f $cfg_pathname || terminate "ne peux supprimer $cfg_pathname" /bin/rm -f "$cfg_pathname" || terminate "ne peux supprimer $cfg_pathname"
# relance ScoDoc # relance ScoDoc
echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]" if [ "$interactive" = 1 ]
read ans
if [ "$(norm_ans "$ans")" = 'Y' ]
then then
scodocctl start echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]"
read -r ans
if [ "$(norm_ans "$ans")" = 'Y' ]
then
scodocctl start
fi
fi fi
exit 0 exit 0
else else
echo 'Erreur: pas de configuration trouvee pour "'$DEPT'"' echo 'Erreur: pas de configuration trouvee pour "'"$DEPT"'"'
exit 1 exit 1
fi fi

0
config/migre-7-a-8.sh Executable file → Normal file
View File

View File

@ -15,7 +15,7 @@ SCODOC_DIR="${INSTANCE_DIR}/Products/ScoDoc"
SCODOC_VAR_DIR="${INSTANCE_DIR}/var/scodoc" SCODOC_VAR_DIR="${INSTANCE_DIR}/var/scodoc"
source utils.sh source utils.sh
check_uid_root $0 check_uid_root "$0"
# Safety check # Safety check
echo "Ce script va remplacer les donnees de votre installation ScoDoc par celles" echo "Ce script va remplacer les donnees de votre installation ScoDoc par celles"
@ -28,7 +28,7 @@ echo
echo "TOUTES LES BASES POSTGRESQL SERONT EFFACEES !!!" echo "TOUTES LES BASES POSTGRESQL SERONT EFFACEES !!!"
echo echo
echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]" echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]"
read ans read -r ans
if [ ! "$(norm_ans "$ans")" = 'Y' ] if [ ! "$(norm_ans "$ans")" = 'Y' ]
then then
echo "Annulation" echo "Annulation"
@ -57,7 +57,7 @@ then
echo "Opening tgz archive..." echo "Opening tgz archive..."
tmp=$(mktemp -d) tmp=$(mktemp -d)
chmod a+rx "$tmp" chmod a+rx "$tmp"
cd "$tmp" cd "$tmp" || terminate "directory error"
tar xfz "$SRC" tar xfz "$SRC"
SRC=$(ls -1d "$tmp"/*) SRC=$(ls -1d "$tmp"/*)
IS_TMP=1 IS_TMP=1
@ -83,18 +83,18 @@ su -c "$SCODOC_DIR/config/psql_restore_databases.sh $PG_DUMPFILE" postgres
# #
echo Copying data files... echo Copying data files...
rm -rf "$INSTANCE_DIR/var" rm -rf "${INSTANCE_DIR:?}/var"
$COPY "$SRC/var" "$INSTANCE_DIR" $COPY "$SRC/var" "$INSTANCE_DIR"
if [ ! -e "${SCODOC_VAR_DIR}/config/" ] if [ ! -e "${SCODOC_VAR_DIR:?}/config/" ]
then then
mkdir "${SCODOC_VAR_DIR}/config/" mkdir "${SCODOC_VAR_DIR:?}/config/"
chown www-data.www-data "${SCODOC_VAR_DIR}/config/" chown www-data.www-data "${SCODOC_VAR_DIR:?}/config/"
chmod 775 "${SCODOC_VAR_DIR}/config/" chmod 775 "${SCODOC_VAR_DIR:?}/config/"
fi fi
rm -rf "$SCODOC_DIR/config/depts" rm -rf "${SCODOC_DIR:?}/config/depts"
if [ -e "$SRC/depts" ] if [ -e "${SRC:?}/depts" ]
then then
# legacy depts => move them to var # legacy depts => move them to var
$COPY "$SRC/depts" "${SCODOC_VAR_DIR}/config/" $COPY "$SRC/depts" "${SCODOC_VAR_DIR}/config/"
@ -107,7 +107,7 @@ then
$COPY "$SRC/photos" "${SCODOC_VAR_DIR}/" $COPY "$SRC/photos" "${SCODOC_VAR_DIR}/"
fi fi
rm -rf "$SCODOC_DIR/logos" rm -rf "${SCODOC_DIR:?}/logos"
$COPY "$SRC/logos" "$SCODOC_DIR/" $COPY "$SRC/logos" "$SCODOC_DIR/"
mv "$SCODOC_DIR/config/scodoc_config.py" "$SCODOC_DIR/config/scodoc_config.py.$(date +%Y%m%d-%H%M%S)" mv "$SCODOC_DIR/config/scodoc_config.py" "$SCODOC_DIR/config/scodoc_config.py.$(date +%Y%m%d-%H%M%S)"
@ -119,7 +119,7 @@ then
iconv -f iso8859-15 -t utf-8 "$SCODOC_DIR/config/scodoc_config.py.orig" > "$SCODOC_DIR/config/scodoc_config.py" iconv -f iso8859-15 -t utf-8 "$SCODOC_DIR/config/scodoc_config.py.orig" > "$SCODOC_DIR/config/scodoc_config.py"
fi fi
rm -rf "$INSTANCE_DIR/log" rm -rf "${INSTANCE_DIR:?}/log"
$COPY "$SRC/log" "$INSTANCE_DIR/" $COPY "$SRC/log" "$INSTANCE_DIR/"
# Fix file ownership and access rights # Fix file ownership and access rights
@ -130,13 +130,13 @@ chown -R www-data.root "$SCODOC_DIR"
chmod -R 775 "$SCODOC_DIR" chmod -R 775 "$SCODOC_DIR"
# Remove tmp directory # Remove tmp directory
if [ $IS_TMP = "1" ] if [ "$IS_TMP" = "1" ]
then then
rm -rf $tmp rm -rf "${tmp}"
fi fi
# Mise a jour BD ScoDoc # Mise a jour BD ScoDoc
cd $SCODOC_DIR/config cd ${SCODOC_DIR:?}/config || terminate "no config directory"
./upgrade.sh ./upgrade.sh
# #

View File

@ -35,7 +35,7 @@ INSTANCE_DIR=/opt/scodoc
SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc" SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc"
source utils.sh source utils.sh
check_uid_root $0 check_uid_root "$0"
echo "Stopping ScoDoc..." echo "Stopping ScoDoc..."
scodocctl stop scodocctl stop
@ -44,9 +44,9 @@ scodocctl stop
echo "Dumping SQL database..." echo "Dumping SQL database..."
chown postgres "$DEST" chown postgres "$DEST"
su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres
if [ ! $? -eq 0 ] if [ ! "$?" -eq 0 ]
then then
echo "Error dumping postgresql database\nPlease check that SQL server is running\nAborting." printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting."
exit 1 exit 1
fi fi
chown root "$DEST" chown root "$DEST"
@ -57,7 +57,7 @@ cp -rp "$INSTANCE_DIR/var" "$DEST"
# Depts db config (now in .../var) # Depts db config (now in .../var)
shopt -s nullglob shopt -s nullglob
if [ ! -z "$(echo ${SCODOC_DIR}/config/depts/*.cfg)" ] if [ -n "$(echo ${SCODOC_DIR}/config/depts/*.cfg)" ]
then then
echo "Copying legacy depts configs..." echo "Copying legacy depts configs..."
cp -rp "$SCODOC_DIR/config/depts" "$DEST" cp -rp "$SCODOC_DIR/config/depts" "$DEST"
@ -86,6 +86,6 @@ cp -rp "$INSTANCE_DIR/log" "$DEST"
echo echo
echo "Archiving backup files in a $DEST.tgz..." echo "Archiving backup files in a $DEST.tgz..."
base=$(basename "$DEST") base=$(basename "$DEST")
(cd "$DEST"/..; tar cfz "$DEST".tgz "$base") (cd "$DEST"/.. || terminate "directory error"; tar cfz "$DEST".tgz "$base")
echo "Done (you can copy " "$DEST"".tgz to destination machine)." echo "Done (you can copy " "$DEST"".tgz to destination machine)."

View File

@ -31,6 +31,8 @@ nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_i
""" """
import pdb
from notesdb import * from notesdb import *
from notes_log import log from notes_log import log
from sco_utils import * from sco_utils import *
@ -78,6 +80,9 @@ class FakeUser:
def has_permission(self, op, context): def has_permission(self, op, context):
return True return True
def has_role(self, role):
return True
class DummyResponse: class DummyResponse:
"""Emulation vide de Reponse http Zope""" """Emulation vide de Reponse http Zope"""
@ -104,6 +109,7 @@ class DummyRequest:
self.URL1 = self.URL self.URL1 = self.URL
self.URL0 = self.URL self.URL0 = self.URL
self.BASE0 = "localhost" self.BASE0 = "localhost"
self.REMOTE_HOST = "localhost"
self.REMOTE_ADDR = "127.0.0.1" self.REMOTE_ADDR = "127.0.0.1"
self.HTTP_REFERER = "" self.HTTP_REFERER = ""
self.REQUEST_METHOD = "get" self.REQUEST_METHOD = "get"

View File

@ -394,7 +394,7 @@ CREATE TABLE notes_formsemestre (
elt_annee_apo text -- code element annee Apogee, eg VRT1A ou V2INLA,V2INCA elt_annee_apo text -- code element annee Apogee, eg VRT1A ou V2INLA,V2INCA
) WITH OIDS; ) WITH OIDS;
-- id des utilsateurs responsables (aka directeurs des etudes) du semestre: -- id des utilisateurs responsables (aka directeurs des etudes) du semestre:
CREATE TABLE notes_formsemestre_responsables ( CREATE TABLE notes_formsemestre_responsables (
formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE,
responsable_id text NOT NULL, responsable_id text NOT NULL,

View File

@ -51,5 +51,5 @@ telephone; text; adresse; 1; num. telephone (fixe)
telephonemobile; text; adresse; 1; num. telephone (mobile) telephonemobile; text; adresse; 1; num. telephone (mobile)
# #
# Pas tout à fait admission: # Pas tout à fait admission:
debouche;text; admissions;1;situation APRES être passé par chez nous; debouche;text; admissions;1;(OBSOLETE, ne plus utiliser) situation APRES être passé par chez nous;

View File

@ -34,7 +34,7 @@ class _logguer:
if LOG_FILENAME: if LOG_FILENAME:
path = os.path.join(self.directory, LOG_FILENAME) path = os.path.join(self.directory, LOG_FILENAME)
self.file = open(path, "a") self.file = open(path, "a")
self("new _logguer") self("new _logguer (%s)" % path)
else: else:
self.file = None # logging disabled self.file = None # logging disabled

View File

@ -248,8 +248,7 @@ def DBDelete(cnx, table, colid, val, commit=False):
class EditableTable: class EditableTable:
""" --- generic class: SQL table with create/edit/list/delete """--- generic class: SQL table with create/edit/list/delete"""
"""
def __init__( def __init__(
self, self,
@ -377,7 +376,11 @@ class EditableTable:
# format value # format value
for title in vals.keys(): for title in vals.keys():
if self.input_formators.has_key(title): if self.input_formators.has_key(title):
vals[title] = self.input_formators[title](vals[title]) try:
vals[title] = self.input_formators[title](vals[title])
except:
log("exception while converting %s=%s" % (title, vals[title]))
raise
DBUpdateArgs( DBUpdateArgs(
cnx, cnx,
self.table_name, self.table_name,

View File

@ -53,8 +53,7 @@ def doSignaleAbsence(
description=None, description=None,
REQUEST=None, REQUEST=None,
): # etudid implied ): # etudid implied
"""Signalement d'une absence """Signalement d'une absence"""
"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -124,8 +123,7 @@ def doSignaleAbsence(
def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple de signalement d'une absence """Formulaire individuel simple de signalement d'une absence"""
"""
# brute-force portage from very old dtml code ... # brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -162,7 +160,10 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
% etud, % etud,
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]), """<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html( sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
), ),
"""</a></td></tr></table>""", """</a></td></tr></table>""",
""" """
@ -207,8 +208,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
def doJustifAbsence( def doJustifAbsence(
context, datedebut, datefin, demijournee, description=None, REQUEST=None context, datedebut, datefin, demijournee, description=None, REQUEST=None
): # etudid implied ): # etudid implied
"""Justification d'une absence """Justification d'une absence"""
"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
description_abs = description description_abs = description
@ -274,8 +274,7 @@ def doJustifAbsence(
def JustifAbsenceEtud(context, REQUEST=None): # etudid implied def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple de justification d'une absence """Formulaire individuel simple de justification d'une absence"""
"""
# brute-force portage from very old dtml code ... # brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -290,7 +289,10 @@ def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
% etud, % etud,
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]), """<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html( sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
), ),
"""</a></td></tr></table>""", """</a></td></tr></table>""",
""" """
@ -329,8 +331,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
def doAnnuleAbsence( def doAnnuleAbsence(
context, datedebut, datefin, demijournee, REQUEST=None context, datedebut, datefin, demijournee, REQUEST=None
): # etudid implied ): # etudid implied
"""Annulation des absences pour une demi journée """Annulation des absences pour une demi journée"""
"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -378,8 +379,7 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
"""Formulaire individuel simple d'annulation d'une absence """Formulaire individuel simple d'annulation d'une absence"""
"""
# brute-force portage from very old dtml code ... # brute-force portage from very old dtml code ...
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -395,7 +395,10 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
% etud, # " % etud, # "
"""<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]), """<a href="%s/ficheEtud?etudid=%s">""" % (context.ScoURL(), etud["etudid"]),
sco_photos.etud_photo_html( sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
), ),
"""</a></td></tr></table>""", """</a></td></tr></table>""",
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p> """<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p>
@ -464,8 +467,7 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
def doAnnuleJustif( def doAnnuleJustif(
context, datedebut0, datefin0, demijournee, REQUEST=None context, datedebut0, datefin0, demijournee, REQUEST=None
): # etudid implied ): # etudid implied
"""Annulation d'une justification """Annulation d'une justification"""
"""
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
dates = context.DateRangeISO(datedebut0, datefin0) dates = context.DateRangeISO(datedebut0, datefin0)
@ -569,8 +571,7 @@ def formChoixSemestreGroupe(context, all=False):
def CalAbs(context, REQUEST=None): # etud implied def CalAbs(context, REQUEST=None): # etud implied
"""Calendrier des absences d un etudiant """Calendrier des absences d un etudiant"""
"""
# crude portage from 1999 DTML # crude portage from 1999 DTML
etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -621,7 +622,10 @@ def CalAbs(context, REQUEST=None): # etud implied
context.ScoURL(), context.ScoURL(),
etudid, etudid,
sco_photos.etud_photo_html( sco_photos.etud_photo_html(
context, etudid=etudid, title="fiche de " + etud["nomprenom"], REQUEST=REQUEST context,
etudid=etudid,
title="fiche de " + etud["nomprenom"],
REQUEST=REQUEST,
), ),
), ),
CalHTML, CalHTML,

View File

@ -435,8 +435,15 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
if mod["mod_moy_txt"][:2] == "NA": if mod["mod_moy_txt"][:2] == "NA":
mod["mod_moy_txt"] = "-" mod["mod_moy_txt"] = "-"
if is_malus: if is_malus:
mod["mod_moy_txt"] = "" if mod_moy > 0:
mod["mod_coef_txt"] = "" mod["mod_moy_txt"] = fmt_note(mod_moy)
mod["mod_coef_txt"] = "Malus"
elif mod_moy < 0:
mod["mod_moy_txt"] = fmt_note(-mod_moy)
mod["mod_coef_txt"] = "Bonus"
else:
mod["mod_moy_txt"] = "-"
mod["mod_coef_txt"] = "-"
else: else:
mod["mod_coef_txt"] = fmt_coef(modimpl["module"]["coefficient"]) mod["mod_coef_txt"] = fmt_coef(modimpl["module"]["coefficient"])
if mod["mod_moy_txt"] != "NI": # ne montre pas les modules 'non inscrit' if mod["mod_moy_txt"] != "NI": # ne montre pas les modules 'non inscrit'

View File

@ -48,10 +48,22 @@ Balises img: actuellement interdites.
""" """
import traceback, re import traceback, re
import sco_utils as scu
import sco_formsemestre import sco_formsemestre
from sco_pdf import * import sco_pdf
from sco_pdf import Color, Paragraph, Spacer, Table
from sco_pdf import blue, cm, mm
from sco_pdf import SU
import sco_preferences import sco_preferences
from notes_log import log from notes_log import log
from sco_permissions import ScoEtudInscrit
from sco_codes_parcours import (
UE_COLORS,
UE_DEFAULT_COLOR,
UE_ELECTIVE,
UE_SPORT,
UE_STANDARD,
)
import sco_bulletins_generator import sco_bulletins_generator
import sco_bulletins_pdf import sco_bulletins_pdf
import sco_groups import sco_groups
@ -271,7 +283,6 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
context = self.context context = self.context
P = [] # elems pour générer table avec gen_table (liste de dicts) P = [] # elems pour générer table avec gen_table (liste de dicts)
formsemestre_id = I["formsemestre_id"] formsemestre_id = I["formsemestre_id"]
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
prefs = context.get_preferences(formsemestre_id) prefs = context.get_preferences(formsemestre_id)
# Colonnes à afficher: # Colonnes à afficher:
@ -328,8 +339,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
linktmpl = ( linktmpl = (
'<span onclick="toggle_vis_ue(this);" class="toggle_ue">%s</span>&nbsp;' '<span onclick="toggle_vis_ue(this);" class="toggle_ue">%s</span>&nbsp;'
) )
minuslink = linktmpl % icontag("minus_img", border="0", alt="-") minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-")
pluslink = linktmpl % icontag("plus_img", border="0", alt="+") pluslink = linktmpl % scu.icontag("plus_img", border="0", alt="+")
# 1er ligne titres # 1er ligne titres
t = { t = {
@ -384,9 +395,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
P.append(t) P.append(t)
# Rangs dans les partitions: # Rangs dans les partitions:
partitions, partitions_etud_groups = sco_groups.get_formsemestre_groups( partitions, _ = sco_groups.get_formsemestre_groups(context, formsemestre_id)
context, formsemestre_id
)
for partition in partitions: for partition in partitions:
if partition["bul_show_rank"]: if partition["bul_show_rank"]:
partition_id = partition["partition_id"] partition_id = partition["partition_id"]
@ -486,16 +495,16 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
t["_css_row_class"] += " notes_bulletin_row_ue_cur" t["_css_row_class"] += " notes_bulletin_row_ue_cur"
t["_titre_help"] = "(en cours, non prise en compte)" t["_titre_help"] = "(en cours, non prise en compte)"
if prefs["bul_show_minmax"]: if prefs["bul_show_minmax"]:
t["min"] = fmt_note(ue["min"]) t["min"] = scu.fmt_note(ue["min"])
t["max"] = fmt_note(ue["max"]) t["max"] = scu.fmt_note(ue["max"])
if prefs["bul_show_moypromo"]: if prefs["bul_show_moypromo"]:
t["moy"] = fmt_note(ue["moy"]).replace("NA", "-") t["moy"] = scu.fmt_note(ue["moy"]).replace("NA", "-")
# Cas particulier des UE sport (bonus) # Cas particulier des UE sport (bonus)
if ue["type"] == UE_SPORT and not ue_descr: if ue["type"] == UE_SPORT and not ue_descr:
del t["module"] del t["module"]
del t["coef"] del t["coef"]
t["_pdf_style"].append(("SPAN", (colidx["note"], 0), (-1, 0))) t["_pdf_style"].append(("SPAN", (colidx["note"], 0), (-1, 0)))
# t["_module_colspan"] = 3 # non car bug si aucune colonne additionnelle # t["_module_colspan"] = 3 # non car bug si aucune colonne additionnelle
# UE électives # UE électives
if ue["type"] == UE_ELECTIVE: if ue["type"] == UE_ELECTIVE:
t["module"] += " <i>(élective)</i>" t["module"] += " <i>(élective)</i>"
@ -544,8 +553,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
rowstyle="", rowstyle="",
hidden=False, hidden=False,
): ):
"""Liste dans la table les descriptions des modules et, si version != short, des évaluations. """Liste dans la table les descriptions des modules et, si version != short, des évaluations."""
"""
if ue_type == "cur": # UE courante non prise en compte (car capitalisee) if ue_type == "cur": # UE courante non prise en compte (car capitalisee)
pdf_style_bg = [("BACKGROUND", (0, 0), (-1, 0), self.PDF_UE_CUR_BG)] pdf_style_bg = [("BACKGROUND", (0, 0), (-1, 0), self.PDF_UE_CUR_BG)]
else: else:
@ -596,10 +604,10 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
"_pdf_style": pdf_style, "_pdf_style": pdf_style,
} }
if prefs["bul_show_minmax_mod"]: if prefs["bul_show_minmax_mod"]:
t["min"] = fmt_note(mod["stats"]["min"]) t["min"] = scu.fmt_note(mod["stats"]["min"])
t["max"] = fmt_note(mod["stats"]["max"]) t["max"] = scu.fmt_note(mod["stats"]["max"])
if prefs["bul_show_moypromo"]: if prefs["bul_show_moypromo"]:
t["moy"] = fmt_note(mod["stats"]["moy"]).replace("NA", "-") t["moy"] = scu.fmt_note(mod["stats"]["moy"]).replace("NA", "-")
P.append(t) P.append(t)
if self.version != "short": if self.version != "short":
@ -660,12 +668,15 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
t["note"] = "<i>" + e["note_txt"] + "</i>" t["note"] = "<i>" + e["note_txt"] + "</i>"
else: else:
t["_module_colspan"] = 2 t["_module_colspan"] = 2
if prefs["bul_show_minmax_eval"]: if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
etat = sco_evaluations.do_evaluation_etat( etat = sco_evaluations.do_evaluation_etat(
self.context, e["evaluation_id"] self.context, e["evaluation_id"]
) )
t["min"] = fmt_note(etat["mini"]) if prefs["bul_show_minmax_eval"]:
t["max"] = fmt_note(etat["maxi"]) t["min"] = scu.fmt_note(etat["mini"])
t["max"] = scu.fmt_note(etat["maxi"])
if prefs["bul_show_moypromo"]:
t["moy"] = scu.fmt_note(etat["moy"])
P.append(t) P.append(t)
nbeval += 1 nbeval += 1
return nbeval return nbeval

View File

@ -315,7 +315,7 @@ def do_moduleimpl_moyennes(context, nt, mod):
notes.append(0.0) notes.append(0.0)
coefs.append(0.0) coefs.append(0.0)
coefs_mask.append(0) coefs_mask.append(0)
if nb_notes > 0: if nb_notes > 0 or formula_use_abs:
user_moy = compute_user_formula( user_moy = compute_user_formula(
context, context,
sem, sem,

View File

@ -28,12 +28,14 @@
""" """
Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant
""" """
from types import StringType
import safehtml import safehtml
from notesdb import * import sco_utils as scu
from sco_utils import * import notesdb
from notes_log import log from notes_log import log
import VERSION
from sco_exceptions import AccessDenied
from scolog import logdb from scolog import logdb
from gen_tables import GenTable from gen_tables import GenTable
import sco_formsemestre import sco_formsemestre
@ -42,8 +44,7 @@ import sco_tag_module
def report_debouche_date(context, start_year=None, format="html", REQUEST=None): def report_debouche_date(context, start_year=None, format="html", REQUEST=None):
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée. """Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
"""
if not start_year: if not start_year:
return report_debouche_ask_date(context, REQUEST=REQUEST) return report_debouche_ask_date(context, REQUEST=REQUEST)
if format == "xls": if format == "xls":
@ -54,8 +55,8 @@ def report_debouche_date(context, start_year=None, format="html", REQUEST=None):
etudids = get_etudids_with_debouche(context, start_year) etudids = get_etudids_with_debouche(context, start_year)
tab = table_debouche_etudids(context, etudids, keep_numeric=keep_numeric) tab = table_debouche_etudids(context, etudids, keep_numeric=keep_numeric)
tab.filename = make_filename("debouche_scodoc_%s" % start_year) tab.filename = scu.make_filename("debouche_scodoc_%s" % start_year)
tab.origin = "Généré par %s le " % VERSION.SCONAME + timedate_human_repr() + "" tab.origin = "Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + ""
tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year
tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year) tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year)
return tab.make_page( return tab.make_page(
@ -77,7 +78,7 @@ def get_etudids_with_debouche(context, start_year):
start_date = str(start_year) + "-01-01" start_date = str(start_year) + "-01-01"
# Recupere tous les etudid avec un debouché renseigné et une inscription dans un semestre # Recupere tous les etudid avec un debouché renseigné et une inscription dans un semestre
# posterieur à la date de depart: # posterieur à la date de depart:
# r = SimpleDictFetch(context, # r = notesdb.SimpleDictFetch(context,
# """SELECT DISTINCT i.etudid # """SELECT DISTINCT i.etudid
# FROM notes_formsemestre_inscription i, admissions adm, notes_formsemestre s # FROM notes_formsemestre_inscription i, admissions adm, notes_formsemestre s
# WHERE adm.debouche is not NULL # WHERE adm.debouche is not NULL
@ -86,7 +87,7 @@ def get_etudids_with_debouche(context, start_year):
# """, # """,
# {'start_date' : start_date }) # {'start_date' : start_date })
r = SimpleDictFetch( r = notesdb.SimpleDictFetch(
context, context,
"""SELECT DISTINCT i.etudid """SELECT DISTINCT i.etudid
FROM notes_formsemestre_inscription i, notes_formsemestre s, itemsuivi it FROM notes_formsemestre_inscription i, notes_formsemestre s, itemsuivi it
@ -100,8 +101,7 @@ def get_etudids_with_debouche(context, start_year):
def table_debouche_etudids(context, etudids, keep_numeric=True): def table_debouche_etudids(context, etudids, keep_numeric=True):
"""Rapport pour ces etudiants """Rapport pour ces etudiants"""
"""
L = [] L = []
for etudid in etudids: for etudid in etudids:
etud = context.getEtudInfo(filled=1, etudid=etudid)[0] etud = context.getEtudInfo(filled=1, etudid=etudid)[0]
@ -122,7 +122,7 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
"_prenom_target": "ficheEtud?etudid=" + etud["etudid"], "_prenom_target": "ficheEtud?etudid=" + etud["etudid"],
"_nom_td_attrs": 'id="%s" class="etudinfo"' % (etud["etudid"]), "_nom_td_attrs": 'id="%s" class="etudinfo"' % (etud["etudid"]),
# 'debouche' : etud['debouche'], # 'debouche' : etud['debouche'],
"moy": fmt_note(nt.get_etud_moy_gen(etudid), keep_numeric=keep_numeric), "moy": scu.fmt_note(nt.get_etud_moy_gen(etudid), keep_numeric=keep_numeric),
"rang": nt.get_etud_rang(etudid), "rang": nt.get_etud_rang(etudid),
"effectif": len(nt.T), "effectif": len(nt.T),
"semestre_id": last_sem["semestre_id"], "semestre_id": last_sem["semestre_id"],
@ -189,8 +189,7 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
def report_debouche_ask_date(context, REQUEST=None): def report_debouche_ask_date(context, REQUEST=None):
"""Formulaire demande date départ """Formulaire demande date départ"""
"""
return ( return (
context.sco_header(REQUEST) context.sco_header(REQUEST)
+ """<form method="GET"> + """<form method="GET">
@ -223,14 +222,17 @@ def report_debouche_ask_date(context, REQUEST=None):
# admission_edit(cnx, adm) # admission_edit(cnx, adm)
_itemsuiviEditor = EditableTable( _itemsuiviEditor = notesdb.EditableTable(
"itemsuivi", "itemsuivi",
"itemsuivi_id", "itemsuivi_id",
("itemsuivi_id", "etudid", "item_date", "situation"), ("itemsuivi_id", "etudid", "item_date", "situation"),
sortkey="item_date desc", sortkey="item_date desc",
convert_null_outputs_to_empty=True, convert_null_outputs_to_empty=True,
output_formators={"situation": safehtml.HTML2SafeHTML, "item_date": DateISOtoDMY}, output_formators={
input_formators={"item_date": DateDMYtoISO}, "situation": safehtml.HTML2SafeHTML,
"item_date": notesdb.DateISOtoDMY,
},
input_formators={"item_date": notesdb.DateDMYtoISO},
) )
_itemsuivi_create = _itemsuiviEditor.create _itemsuivi_create = _itemsuiviEditor.create
@ -240,8 +242,7 @@ _itemsuivi_edit = _itemsuiviEditor.edit
class ItemSuiviTag(sco_tag_module.ScoTag): class ItemSuiviTag(sco_tag_module.ScoTag):
"""Les tags sur les items """Les tags sur les items"""
"""
tag_table = "itemsuivi_tags" # table (tag_id, title) tag_table = "itemsuivi_tags" # table (tag_id, title)
assoc_table = "itemsuivi_tags_assoc" # table (tag_id, object_id) assoc_table = "itemsuivi_tags_assoc" # table (tag_id, object_id)
@ -259,8 +260,7 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
def itemsuivi_suppress(context, itemsuivi_id, REQUEST=None): def itemsuivi_suppress(context, itemsuivi_id, REQUEST=None):
"""Suppression d'un item """Suppression d'un item"""
"""
if not context.can_edit_suivi(REQUEST): if not context.can_edit_suivi(REQUEST):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
cnx = context.GetDBConnexion() cnx = context.GetDBConnexion()
@ -285,7 +285,7 @@ def itemsuivi_create(
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid)) log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
item = itemsuivi_get(cnx, itemsuivi_id) item = itemsuivi_get(cnx, itemsuivi_id)
if format == "json": if format == "json":
return sendJSON(REQUEST, item) return scu.sendJSON(REQUEST, item)
return item return item
@ -313,7 +313,7 @@ def itemsuivi_set_situation(context, object, value, REQUEST=None):
item = itemsuivi_get(cnx, itemsuivi_id) item = itemsuivi_get(cnx, itemsuivi_id)
item["situation"] = situation item["situation"] = situation
_itemsuivi_edit(cnx, item) _itemsuivi_edit(cnx, item)
return situation or IT_SITUATION_MISSING_STR return situation or scu.IT_SITUATION_MISSING_STR
def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None): def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None):
@ -323,13 +323,13 @@ def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None):
for it in items: for it in items:
it["tags"] = ", ".join(itemsuivi_tag_list(context, it["itemsuivi_id"])) it["tags"] = ", ".join(itemsuivi_tag_list(context, it["itemsuivi_id"]))
if format == "json": if format == "json":
return sendJSON(REQUEST, items) return scu.sendJSON(REQUEST, items)
return items return items
def itemsuivi_tag_list(context, itemsuivi_id): def itemsuivi_tag_list(context, itemsuivi_id):
"""les noms de tags associés à cet item""" """les noms de tags associés à cet item"""
r = SimpleDictFetch( r = notesdb.SimpleDictFetch(
context, context,
"""SELECT t.title """SELECT t.title
FROM itemsuivi_tags_assoc a, itemsuivi_tags t FROM itemsuivi_tags_assoc a, itemsuivi_tags t
@ -344,17 +344,17 @@ def itemsuivi_tag_list(context, itemsuivi_id):
def itemsuivi_tag_search(context, term, REQUEST=None): def itemsuivi_tag_search(context, term, REQUEST=None):
"""List all used tag names (for auto-completion)""" """List all used tag names (for auto-completion)"""
# restrict charset to avoid injections # restrict charset to avoid injections
if not ALPHANUM_EXP.match(term.decode(SCO_ENCODING)): if not scu.ALPHANUM_EXP.match(term.decode(scu.SCO_ENCODING)):
data = [] data = []
else: else:
r = SimpleDictFetch( r = notesdb.SimpleDictFetch(
context, context,
"SELECT title FROM itemsuivi_tags WHERE title LIKE %(term)s", "SELECT title FROM itemsuivi_tags WHERE title LIKE %(term)s",
{"term": term + "%"}, {"term": term + "%"},
) )
data = [x["title"] for x in r] data = [x["title"] for x in r]
return sendJSON(REQUEST, data) return scu.sendJSON(REQUEST, data)
def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None): def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None):
@ -372,7 +372,7 @@ def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None):
# log('itemsuivi_tag_set: itemsuivi_id=%s taglist=%s' % (itemsuivi_id, taglist)) # log('itemsuivi_tag_set: itemsuivi_id=%s taglist=%s' % (itemsuivi_id, taglist))
# Sanity check: # Sanity check:
cnx = context.GetDBConnexion() cnx = context.GetDBConnexion()
item = itemsuivi_get(cnx, itemsuivi_id) _ = itemsuivi_get(cnx, itemsuivi_id)
newtags = set(taglist) newtags = set(taglist)
oldtags = set(itemsuivi_tag_list(context, itemsuivi_id)) oldtags = set(itemsuivi_tag_list(context, itemsuivi_id))

View File

@ -27,15 +27,23 @@
"""Evaluations """Evaluations
""" """
import time
import urllib
import operator
import datetime
from notes_log import log, logCallStack from notes_log import log, logCallStack
from sco_utils import * import sco_utils as scu
from notesdb import * from notesdb import ScoDocCursor
from sco_exceptions import AccessDenied, ScoValueError
import VERSION
from gen_tables import GenTable from gen_tables import GenTable
from TrivialFormulator import TrivialFormulator
import sco_news import sco_news
import sco_formsemestre import sco_formsemestre
import sco_groups import sco_groups
import ZAbsences import ZAbsences
import sco_evaluations
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# #
@ -47,7 +55,7 @@ def notes_moyenne_median_mini_maxi(notes):
notes = [ notes = [
x x
for x in notes for x in notes
if (x != None) and (x != NOTES_NEUTRALISE) and (x != NOTES_ATTENTE) if (x != None) and (x != scu.NOTES_NEUTRALISE) and (x != scu.NOTES_ATTENTE)
] ]
n = len(notes) n = len(notes)
if not n: if not n:
@ -136,8 +144,8 @@ def do_evaluation_etat(
NotesDB = context._notes_getall(evaluation_id) # { etudid : value } NotesDB = context._notes_getall(evaluation_id) # { etudid : value }
notes = [x["value"] for x in NotesDB.values()] notes = [x["value"] for x in NotesDB.values()]
nb_abs = len([x for x in notes if x is None]) nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == NOTES_NEUTRALISE]) nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
nb_att = len([x for x in notes if x == NOTES_ATTENTE]) nb_att = len([x for x in notes if x == scu.NOTES_ATTENTE])
moy_num, median_num, mini_num, maxi_num = notes_moyenne_median_mini_maxi(notes) moy_num, median_num, mini_num, maxi_num = notes_moyenne_median_mini_maxi(notes)
if moy_num is None: if moy_num is None:
median, moy = "", "" median, moy = "", ""
@ -145,10 +153,10 @@ def do_evaluation_etat(
mini, maxi = "", "" mini, maxi = "", ""
mini_num, maxi_num = None, None mini_num, maxi_num = None, None
else: else:
median = fmt_note(median_num) median = scu.fmt_note(median_num)
moy = fmt_note(moy_num) moy = scu.fmt_note(moy_num)
mini = fmt_note(mini_num) mini = scu.fmt_note(mini_num)
maxi = fmt_note(maxi_num) maxi = scu.fmt_note(maxi_num)
# cherche date derniere modif note # cherche date derniere modif note
if len(NotesDB): if len(NotesDB):
t = [x["date"] for x in NotesDB.values()] t = [x["date"] for x in NotesDB.values()]
@ -159,7 +167,7 @@ def do_evaluation_etat(
E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0] E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0] Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
is_malus = Mod["module_type"] == MODULE_MALUS # True si module de malus is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
# Si partition_id is None, prend 'all' ou bien la premiere: # Si partition_id is None, prend 'all' ou bien la premiere:
if partition_id is None: if partition_id is None:
@ -186,8 +194,8 @@ def do_evaluation_etat(
# On considere une note "manquante" lorsqu'elle n'existe pas # On considere une note "manquante" lorsqu'elle n'existe pas
# ou qu'elle est en attente (ATT) # ou qu'elle est en attente (ATT)
GrNbMissing = DictDefault() # group_id : nb notes manquantes GrNbMissing = scu.DictDefault() # group_id : nb notes manquantes
GrNotes = DictDefault(defaultvalue=[]) # group_id: liste notes valides GrNotes = scu.DictDefault(defaultvalue=[]) # group_id: liste notes valides
TotalNbMissing = 0 TotalNbMissing = 0
TotalNbAtt = 0 TotalNbAtt = 0
groups = {} # group_id : group groups = {} # group_id : group
@ -201,14 +209,14 @@ def do_evaluation_etat(
isMissing = False isMissing = False
if NotesDB.has_key(i["etudid"]): if NotesDB.has_key(i["etudid"]):
val = NotesDB[i["etudid"]]["value"] val = NotesDB[i["etudid"]]["value"]
if val == NOTES_ATTENTE: if val == scu.NOTES_ATTENTE:
isMissing = True isMissing = True
TotalNbAtt += 1 TotalNbAtt += 1
if group: if group:
GrNotes[group["group_id"]].append(val) GrNotes[group["group_id"]].append(val)
else: else:
if group: if group:
junk = GrNotes[group["group_id"]] # create group _ = GrNotes[group["group_id"]] # create group
isMissing = True isMissing = True
if isMissing: if isMissing:
TotalNbMissing += 1 TotalNbMissing += 1
@ -219,7 +227,7 @@ def do_evaluation_etat(
gr_incomplets.sort() gr_incomplets.sort()
if ( if (
(TotalNbMissing > 0) (TotalNbMissing > 0)
and (E["evaluation_type"] != EVALUATION_RATTRAPAGE) and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
and not is_malus and not is_malus
): ):
complete = False complete = False
@ -244,15 +252,15 @@ def do_evaluation_etat(
"group_id": group_id, "group_id": group_id,
"group_name": groups[group_id]["group_name"], "group_name": groups[group_id]["group_name"],
"gr_moy_num": gr_moy, "gr_moy_num": gr_moy,
"gr_moy": fmt_note(gr_moy), "gr_moy": scu.fmt_note(gr_moy),
"gr_median_num": gr_median, "gr_median_num": gr_median,
"gr_median": fmt_note(gr_median), "gr_median": scu.fmt_note(gr_median),
"gr_mini": fmt_note(gr_mini), "gr_mini": scu.fmt_note(gr_mini),
"gr_maxi": fmt_note(gr_maxi), "gr_maxi": scu.fmt_note(gr_maxi),
"gr_mini": gr_mini, "gr_mini_num": gr_mini,
"gr_maxi": gr_maxi, "gr_maxi_num": gr_maxi,
"gr_nb_notes": len(notes), "gr_nb_notes": len(notes),
"gr_nb_att": len([x for x in notes if x == NOTES_ATTENTE]), "gr_nb_att": len([x for x in notes if x == scu.NOTES_ATTENTE]),
} }
) )
gr_moyennes.sort(key=operator.itemgetter("group_name")) gr_moyennes.sort(key=operator.itemgetter("group_name"))
@ -337,7 +345,7 @@ def do_evaluation_list_in_sem(context, formsemestre_id):
'visibulletin': 1} ] 'visibulletin': 1} ]
""" """
req = "select E.* from notes_evaluation E, notes_moduleimpl MI where MI.formsemestre_id = %(formsemestre_id)s and MI.moduleimpl_id = E.moduleimpl_id" req = "select E.* from notes_evaluation E, notes_moduleimpl MI where MI.formsemestre_id = %(formsemestre_id)s and MI.moduleimpl_id = E.moduleimpl_id order by moduleimpl_id, numero desc, jour desc, heure_debut desc"
cnx = context.GetDBConnexion() cnx = context.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor.execute(req, {"formsemestre_id": formsemestre_id}) cursor.execute(req, {"formsemestre_id": formsemestre_id})
@ -380,7 +388,7 @@ def _eval_etat(evals):
nb_evals_en_cours += 1 nb_evals_en_cours += 1
dates.append(e["etat"]["last_modif"]) dates.append(e["etat"]["last_modif"])
dates = sort_dates(dates) dates = scu.sort_dates(dates)
if len(dates): if len(dates):
last_modif = dates[-1] # date de derniere modif d'une note dans un module last_modif = dates[-1] # date de derniere modif d'une note dans un module
@ -511,18 +519,18 @@ def evaluation_date_first_completion(context, evaluation_id):
if not etat["evalcomplete"]: if not etat["evalcomplete"]:
return None return None
E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0] # XXX inachevé ou à revoir ?
M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"]
# Il faut considerer les inscriptions au semestre # Il faut considerer les inscriptions au semestre
# (pour avoir l'etat et le groupe) et aussi les inscriptions # (pour avoir l'etat et le groupe) et aussi les inscriptions
# au module (pour gerer les modules optionnels correctement) # au module (pour gerer les modules optionnels correctement)
insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id) # E = context.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
insmod = context.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"]) # M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
insmodset = set([x["etudid"] for x in insmod]) # formsemestre_id = M["formsemestre_id"]
# insem = context.do_formsemestre_inscription_listinscrits(formsemestre_id)
# insmod = context.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"])
# insmodset = set([x["etudid"] for x in insmod])
# retire de insem ceux qui ne sont pas inscrits au module # retire de insem ceux qui ne sont pas inscrits au module
ins = [i for i in insem if i["etudid"] in insmodset] # ins = [i for i in insem if i["etudid"] in insmodset]
notes = context._notes_getall(evaluation_id, filter_suppressed=False).values() notes = context._notes_getall(evaluation_id, filter_suppressed=False).values()
notes_log = context._notes_getall( notes_log = context._notes_getall(
@ -560,8 +568,8 @@ def formsemestre_evaluations_delai_correction(
for e in evals: for e in evals:
M = context.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0] M = context.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0] Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
if (e["evaluation_type"] != EVALUATION_NORMALE) or ( if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or (
Mod["module_type"] == MODULE_MALUS Mod["module_type"] == scu.MODULE_MALUS
): ):
continue continue
e["date_first_complete"] = evaluation_date_first_completion( e["date_first_complete"] = evaluation_date_first_completion(
@ -612,8 +620,8 @@ def formsemestre_evaluations_delai_correction(
caption="Correction des évaluations du semestre", caption="Correction des évaluations du semestre",
preferences=context.get_preferences(formsemestre_id), preferences=context.get_preferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id), base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
origin="Généré par %s le " % VERSION.SCONAME + timedate_human_repr() + "", origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
filename=make_filename("evaluations_delais_" + sem["titreannee"]), filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]),
) )
return tab.make_page(context, format=format, REQUEST=REQUEST) return tab.make_page(context, format=format, REQUEST=REQUEST)
@ -727,7 +735,6 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
M = context.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = context.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0] Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
u = context.Users.user_info(M["responsable_id"]) u = context.Users.user_info(M["responsable_id"])
resp = u["prenomnom"] resp = u["prenomnom"]
nomcomplet = u["nomcomplet"] nomcomplet = u["nomcomplet"]
@ -747,13 +754,13 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
etit = E["description"] or "" etit = E["description"] or ""
if etit: if etit:
etit = ' "' + etit + '"' etit = ' "' + etit + '"'
if Mod["module_type"] == MODULE_MALUS: if Mod["module_type"] == scu.MODULE_MALUS:
etit += ' <span class="eval_malus">(points de malus)</span>' etit += ' <span class="eval_malus">(points de malus)</span>'
H = [ H = [
'<span class="eval_title">Evaluation%s</span><p><b>Module : %s</b></p>' '<span class="eval_title">Evaluation%s</span><p><b>Module : %s</b></p>'
% (etit, mod_descr) % (etit, mod_descr)
] ]
if Mod["module_type"] == MODULE_MALUS: if Mod["module_type"] == scu.MODULE_MALUS:
# Indique l'UE # Indique l'UE
ue = context.do_ue_list(args={"ue_id": Mod["ue_id"]})[0] ue = context.do_ue_list(args={"ue_id": Mod["ue_id"]})[0]
H.append("<p><b>UE : %(acronyme)s</b></p>" % ue) H.append("<p><b>UE : %(acronyme)s</b></p>" % ue)
@ -804,9 +811,9 @@ def evaluation_create_form(
moduleimpl_id = the_eval["moduleimpl_id"] moduleimpl_id = the_eval["moduleimpl_id"]
# #
M = context.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = context.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
is_malus = M["module"]["module_type"] == MODULE_MALUS # True si module de malus is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
min_note_max = NOTES_PRECISION # le plus petit bareme possible min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
if not readonly: if not readonly:
try: try:
context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) context._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id)
@ -853,17 +860,16 @@ def evaluation_create_form(
# Note maximale actuelle dans cette eval ? # Note maximale actuelle dans cette eval ?
etat = do_evaluation_etat(context, evaluation_id) etat = do_evaluation_etat(context, evaluation_id)
if etat["maxi_num"] is not None: if etat["maxi_num"] is not None:
min_note_max = max(NOTES_PRECISION, etat["maxi_num"]) min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"])
else: else:
min_note_max = NOTES_PRECISION min_note_max = scu.NOTES_PRECISION
# #
if min_note_max > NOTES_PRECISION: if min_note_max > scu.NOTES_PRECISION:
min_note_max_str = fmt_note(min_note_max) min_note_max_str = scu.fmt_note(min_note_max)
else: else:
min_note_max_str = "0" min_note_max_str = "0"
# #
Mod = context.do_module_list(args={"module_id": M["module_id"]})[0] Mod = context.do_module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
# #
help = """<div class="help"><p class="help"> help = """<div class="help"><p class="help">
Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module. Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module.
@ -973,7 +979,7 @@ def evaluation_create_form(
"title": "Notes de 0 à", "title": "Notes de 0 à",
"explanation": "barème (note max actuelle: %s)" % min_note_max_str, "explanation": "barème (note max actuelle: %s)" % min_note_max_str,
"allow_null": False, "allow_null": False,
"max_value": NOTES_MAX, "max_value": scu.NOTES_MAX,
"min_value": min_note_max, "min_value": min_note_max,
}, },
), ),
@ -1008,7 +1014,7 @@ def evaluation_create_form(
{ {
"input_type": "menu", "input_type": "menu",
"title": "Modalité", "title": "Modalité",
"allowed_values": (EVALUATION_NORMALE, EVALUATION_RATTRAPAGE), "allowed_values": (scu.EVALUATION_NORMALE, scu.EVALUATION_RATTRAPAGE),
"type": "int", "type": "int",
"labels": ("Normale", "Rattrapage"), "labels": ("Normale", "Rattrapage"),
}, },

View File

@ -644,6 +644,12 @@ def formsemestre_description_table(
ModInscrits = context.do_moduleimpl_inscription_list( ModInscrits = context.do_moduleimpl_inscription_list(
moduleimpl_id=M["moduleimpl_id"] moduleimpl_id=M["moduleimpl_id"]
) )
enseignants = ", ".join(
[
context.Users.user_info(m["ens_id"], REQUEST)["nomprenom"]
for m in M["ens"]
]
)
l = { l = {
"UE": M["ue"]["acronyme"], "UE": M["ue"]["acronyme"],
"Code": M["module"]["code"], "Code": M["module"]["code"],
@ -652,6 +658,8 @@ def formsemestre_description_table(
"Inscrits": len(ModInscrits), "Inscrits": len(ModInscrits),
"Responsable": context.Users.user_info(M["responsable_id"])["nomprenom"], "Responsable": context.Users.user_info(M["responsable_id"])["nomprenom"],
"_Responsable_class": "scotext", "_Responsable_class": "scotext",
"Enseignants": enseignants,
"_Enseignants_class": "scotext",
"Coef.": M["module"]["coefficient"], "Coef.": M["module"]["coefficient"],
# 'ECTS' : M['module']['ects'], # 'ECTS' : M['module']['ects'],
# Lien sur titre -> module # Lien sur titre -> module
@ -689,7 +697,7 @@ def formsemestre_description_table(
columns_ids = ["UE", "Code", "Module", "Coef."] columns_ids = ["UE", "Code", "Module", "Coef."]
if context.get_preference("bul_show_ects", formsemestre_id): if context.get_preference("bul_show_ects", formsemestre_id):
columns_ids += ["ects"] columns_ids += ["ects"]
columns_ids += ["Inscrits", "Responsable"] columns_ids += ["Inscrits", "Responsable", "Enseignants"]
if with_evals: if with_evals:
columns_ids += [ columns_ids += [
"jour", "jour",
@ -899,6 +907,12 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
<th class="resp">Responsable</th> <th class="resp">Responsable</th>
<th class="evals">Evaluations</th></tr>""" <th class="evals">Evaluations</th></tr>"""
) )
mails_enseignants = set(
[
context.Users.user_info(ens_id, REQUEST)["email"]
for ens_id in sem["responsables"]
]
) # adr. mail des enseignants
for M in Mlist: for M in Mlist:
Mod = M["module"] Mod = M["module"]
ModDescr = ( ModDescr = (
@ -915,6 +929,12 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
ModInscrits = context.do_moduleimpl_inscription_list( ModInscrits = context.do_moduleimpl_inscription_list(
moduleimpl_id=M["moduleimpl_id"] moduleimpl_id=M["moduleimpl_id"]
) )
mails_enseignants.add(
context.Users.user_info(M["responsable_id"], REQUEST)["email"]
)
mails_enseignants |= set(
[context.Users.user_info(m["ens_id"], REQUEST)["email"] for m in M["ens"]]
)
ue = M["ue"] ue = M["ue"]
if prev_ue_id != ue["ue_id"]: if prev_ue_id != ue["ue_id"]:
prev_ue_id = ue["ue_id"] prev_ue_id = ue["ue_id"]
@ -1039,5 +1059,11 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
) )
# --- LISTE DES ETUDIANTS # --- LISTE DES ETUDIANTS
H += ['<div id="groupes">', context.make_listes_sem(sem, REQUEST), "</div>"] H += ['<div id="groupes">', context.make_listes_sem(sem, REQUEST), "</div>"]
# --- Lien mail enseignants:
adrlist = list(mails_enseignants - set([""]))
if adrlist:
H.append(
'<p><a class="stdlink" href="mailto:?cc=%s">Courrier aux %d enseignants du semestre</a></p>'
% (",".join(adrlist), len(adrlist))
)
return "".join(H) + context.sco_footer(REQUEST) return "".join(H) + context.sco_footer(REQUEST)

View File

@ -431,10 +431,10 @@ def groups_table(
format: csv, json, xml, xls, allxls, xlsappel, moodlecsv, pdf format: csv, json, xml, xls, allxls, xlsappel, moodlecsv, pdf
Si with_codes, ajoute 4 colonnes avec les codes etudid, NIP, INE et etape Si with_codes, ajoute 4 colonnes avec les codes etudid, NIP, INE et etape
""" """
log( # log(
"enter groups_table %s: %s" # "enter groups_table %s: %s"
% (groups_infos.members[0]["nom"], groups_infos.members[0].get("etape", "-")) # % (groups_infos.members[0]["nom"], groups_infos.members[0].get("etape", "-"))
) # )
authuser = REQUEST.AUTHENTICATED_USER authuser = REQUEST.AUTHENTICATED_USER
with_codes = int(with_codes) with_codes = int(with_codes)
@ -798,6 +798,11 @@ def tab_absences_html(context, groups_infos, etat=None, REQUEST=None):
"<h3>Absences</h3>", "<h3>Absences</h3>",
'<ul class="ul_abs">', '<ul class="ul_abs">',
"<li>", "<li>",
form_choix_saisie_semaine(
context, groups_infos, REQUEST=REQUEST
), # Ajout Le Havre
"</li>",
"<li>",
form_choix_jour_saisie_hebdo(context, groups_infos, REQUEST=REQUEST), form_choix_jour_saisie_hebdo(context, groups_infos, REQUEST=REQUEST),
"</li>", "</li>",
"""<li><a class="stdlink" href="Absences/EtatAbsencesGr?%s&amp;debut=%s&amp;fin=%s">Etat des absences du groupe</a></li>""" """<li><a class="stdlink" href="Absences/EtatAbsencesGr?%s&amp;debut=%s&amp;fin=%s">Etat des absences du groupe</a></li>"""
@ -888,6 +893,37 @@ def form_choix_jour_saisie_hebdo(context, groups_infos, REQUEST=None):
return "\n".join(FA) return "\n".join(FA)
# Ajout Le Havre
# Formulaire saisie absences semaine
def form_choix_saisie_semaine(context, groups_infos, REQUEST=None):
authuser = REQUEST.AUTHENTICATED_USER
if not authuser.has_permission(ScoAbsChange, context):
return ""
sem = groups_infos.formsemestre
# construit l'URL "destination"
# (a laquelle on revient apres saisie absences)
query_args = cgi.parse_qs(REQUEST.QUERY_STRING)
moduleimpl_id = query_args.get("moduleimpl_id", [""])[0]
if "head_message" in query_args:
del query_args["head_message"]
destination = "%s?%s" % (REQUEST.URL, urllib.urlencode(query_args, True))
destination = destination.replace(
"%", "%%"
) # car ici utilisee dans un format string !
DateJour = time.strftime("%d/%m/%Y")
datelundi = ZAbsences.ddmmyyyy(DateJour).prev_monday()
FA = [] # formulaire avec menu saisi hebdo des absences
FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">')
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="destination" value="%s"/>' % destination)
FA.append(groups_infos.get_form_elem())
FA.append('<input type="submit" class="button" value="Saisie à la semaine" />')
FA.append("</form>")
return "\n".join(FA)
def export_groups_as_moodle_csv(context, formsemestre_id=None, REQUEST=None): def export_groups_as_moodle_csv(context, formsemestre_id=None, REQUEST=None):
"""Export all students/groups, in a CSV format suitable for Moodle """Export all students/groups, in a CSV format suitable for Moodle
Each (student,group) will be listed on a separate line Each (student,group) will be listed on a separate line

View File

@ -262,7 +262,11 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
H.append( H.append(
"""Afficher les groupes de&nbsp;<select name="partition_id" onchange="document.f.submit();">""" """Afficher les groupes de&nbsp;<select name="partition_id" onchange="document.f.submit();">"""
) )
been_selected = False
for partition in partitions: for partition in partitions:
if not partition_id and not been_selected:
selected = "selected"
been_selected = True
if partition["partition_id"] == partition_id: if partition["partition_id"] == partition_id:
selected = "selected" selected = "selected"
else: else:
@ -337,12 +341,11 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
% etat["last_modif"].strftime("%d/%m/%Y à %Hh%M") % etat["last_modif"].strftime("%d/%m/%Y à %Hh%M")
) )
H.append('<span class="evalindex_cont">') H.append('<span class="evalindex_cont">')
if has_expression: if has_expression or True:
H.append( H.append(
"""<span class="evalindex" title="Indice dans les vecteurs (formules)">%02d</span>""" """<span class="evalindex" title="Indice dans les vecteurs (formules)">%2d</span>"""
% eval_index % eval_index
) )
# Fleches: # Fleches:
H.append('<span class="eval_arrows_chld">') H.append('<span class="eval_arrows_chld">')
if eval_index != (len(ModEvals) - 1) and caneditevals: if eval_index != (len(ModEvals) - 1) and caneditevals:

View File

@ -764,12 +764,17 @@ def _list_notes_evals(context, evals, etudid):
""" """
L = [] L = []
for e in evals: for e in evals:
if e["etat"]["evalcomplete"]: if (
e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
NotesDB = context._notes_getall(e["evaluation_id"]) NotesDB = context._notes_getall(e["evaluation_id"])
if NotesDB.has_key(etudid): if NotesDB.has_key(etudid):
val = NotesDB[etudid]["value"] val = NotesDB[etudid]["value"]
else: else:
val = None # Note manquante mais prise en compte immédiate: affiche ATT
val = NOTES_ATTENTE
val_fmt = fmt_note(val, keep_numeric=True) val_fmt = fmt_note(val, keep_numeric=True)
L.append(val_fmt) L.append(val_fmt)
return L return L
@ -778,9 +783,15 @@ def _list_notes_evals(context, evals, etudid):
def _list_notes_evals_titles(context, codemodule, evals): def _list_notes_evals_titles(context, codemodule, evals):
"""Liste des titres des evals completes""" """Liste des titres des evals completes"""
L = [] L = []
eval_index = len(evals) - 1
for e in evals: for e in evals:
if e["etat"]["evalcomplete"]: if (
L.append(codemodule + "-" + DateISOtoDMY(e["jour"])) e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
L.append(codemodule + "-" + str(eval_index) + "-" + e["jour"].isoformat())
eval_index -= 1
return L return L
@ -788,7 +799,11 @@ def _list_notes_evals_stats(context, evals, key):
"""Liste des stats (moy, ou rien!) des evals completes""" """Liste des stats (moy, ou rien!) des evals completes"""
L = [] L = []
for e in evals: for e in evals:
if e["etat"]["evalcomplete"]: if (
e["etat"]["evalcomplete"]
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
if key == "moy": if key == "moy":
val = e["etat"]["moy_num"] val = e["etat"]["moy_num"]
L.append(fmt_note(val, keep_numeric=True)) L.append(fmt_note(val, keep_numeric=True))

View File

@ -28,6 +28,8 @@
"""Synchronisation des listes d'étudiants avec liste portail (Apogée) """Synchronisation des listes d'étudiants avec liste portail (Apogée)
""" """
import time
import pprint
from sco_utils import ScoEtudInscrit, annee_scolaire_debut, log, ScoValueError from sco_utils import ScoEtudInscrit, annee_scolaire_debut, log, ScoValueError
from notesdb import ScoDocCursor from notesdb import ScoDocCursor
@ -42,8 +44,6 @@ import sco_formsemestre_inscriptions
import sco_formsemestre_status import sco_formsemestre_status
from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC
import time
# Clés utilisées pour la synchro # Clés utilisées pour la synchro
EKEY_APO = "nip" EKEY_APO = "nip"
EKEY_SCO = "code_nip" EKEY_SCO = "code_nip"
@ -695,7 +695,7 @@ def do_import_etud_admission(
"codelycee": get_opt_str(etud, "lycee"), "codelycee": get_opt_str(etud, "lycee"),
"boursier": get_opt_str(etud, "bourse"), "boursier": get_opt_str(etud, "bourse"),
} }
log("do_import_etud_admission: etud=%s" % etud) log("do_import_etud_admission: etud=%s" % pprint.pformat(etud))
al = scolars.admission_list(cnx, args={"etudid": etudid}) al = scolars.admission_list(cnx, args={"etudid": etudid})
if not al: if not al:
scolars.admission_create(cnx, args) # -> adm_id scolars.admission_create(cnx, args) # -> adm_id

View File

@ -25,7 +25,7 @@
# #
############################################################################## ##############################################################################
""" Acces donnees etudiants """ Accès donnees etudiants
""" """
from sco_utils import * from sco_utils import *
@ -35,7 +35,7 @@ from TrivialFormulator import TrivialFormulator
import safehtml import safehtml
from scolog import logdb from scolog import logdb
from notes_table import * from notes_table import *
import sco_news
# XXXXXXXXX HACK: zope 2.7.7 bug turaround ? # XXXXXXXXX HACK: zope 2.7.7 bug turaround ?
import locale import locale
@ -618,6 +618,59 @@ def make_etud_args(etudid=None, code_nip=None, REQUEST=None, raise_exc=True):
return args return args
def create_etud(context, cnx, args={}, REQUEST=None):
"""Creation d'un étudiant. génère aussi évenement et "news".
Args:
args: dict avec les attributs de l'étudiant
Returns:
etud, l'étudiant créé.
"""
# creation d'un etudiant
etudid = etudident_create(cnx, args, context=context, REQUEST=REQUEST)
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
_ = adresse_create(
cnx,
{
"etudid": etudid,
"typeadresse": "domicile",
"description": "(creation individuelle)",
},
)
# event
scolar_events_create(
cnx,
args={
"etudid": etudid,
"event_date": time.strftime("%d/%m/%Y"),
"formsemestre_id": None,
"event_type": "CREATION",
},
)
# log
logdb(
REQUEST,
cnx,
method="etudident_edit_form",
etudid=etudid,
msg="creation initiale",
)
etud = scolars.etudident_list(cnx, {"etudid": etudid})[0]
context.fillEtudsInfo([etud])
etud["url"] = "ficheEtud?etudid=%(etudid)s" % etud
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_INSCR,
object=None, # pas d'object pour ne montrer qu'un etudiant
text='Nouvel étudiant <a href="%(url)s">%(nomprenom)s</a>' % etud,
url=etud["url"],
)
return etud
# ---------- "EVENTS" # ---------- "EVENTS"
_scolar_eventsEditor = EditableTable( _scolar_eventsEditor = EditableTable(
"scolar_events", "scolar_events",

1
scotests/__init__.py Normal file
View File

@ -0,0 +1 @@
#

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

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