1
0
forked from ScoDoc/ScoDoc

code refactoring: sco_abs

This commit is contained in:
Emmanuel Viennet 2021-01-10 18:54:39 +01:00
parent 3afec00b5e
commit 0dcb117df0
17 changed files with 699 additions and 651 deletions

View File

@ -48,6 +48,13 @@ import urllib
import datetime import datetime
import jaxml import jaxml
import cgi import cgi
import string
import re
import time
import calendar
from mx.DateTime import DateTime as mxDateTime
from mx.DateTime.ISO import ParseDateTimeUTC
# --------------- # ---------------
from sco_zope import * from sco_zope import *
@ -68,10 +75,8 @@ import sco_groups_view
import sco_excel import sco_excel
import sco_abs_notification, sco_abs_views import sco_abs_notification, sco_abs_views
import sco_compute_moy import sco_compute_moy
import string, re import sco_abs
import time, calendar from sco_abs import ddmmyyyy
from mx.DateTime import DateTime as mxDateTime
from mx.DateTime.ISO import ParseDateTimeUTC
def _toboolean(x): def _toboolean(x):
@ -84,189 +89,6 @@ def _toboolean(x):
return False return False
def MonthNbDays(month, year):
"returns nb of days in month"
if month > 7:
month = month + 1
if month % 2:
return 31
elif month == 2:
if calendar.isleap(year):
return 29
else:
return 28
else:
return 30
class ddmmyyyy:
"""immutable dates"""
def __init__(self, date=None, fmt="ddmmyyyy", work_saturday=False):
self.work_saturday = work_saturday
if date is None:
return
try:
if fmt == "ddmmyyyy":
self.day, self.month, self.year = string.split(date, "/")
elif fmt == "iso":
self.year, self.month, self.day = string.split(date, "-")
else:
raise ValueError("invalid format spec. (%s)" % fmt)
self.year = string.atoi(self.year)
self.month = string.atoi(self.month)
self.day = string.atoi(self.day)
except:
raise ScoValueError("date invalide: %s" % date)
# accept years YYYY or YY, uses 1970 as pivot
if self.year < 1970:
if self.year > 100:
raise ScoInvalidDateError("Année invalide: %s" % self.year)
if self.year < 70:
self.year = self.year + 2000
else:
self.year = self.year + 1900
if self.month < 1 or self.month > 12:
raise ScoInvalidDateError("Mois invalide: %s" % self.month)
if self.day < 1 or self.day > MonthNbDays(self.month, self.year):
raise ScoInvalidDateError("Jour invalide: %s" % self.day)
# weekday in 0-6, where 0 is monday
self.weekday = calendar.weekday(self.year, self.month, self.day)
self.time = time.mktime((self.year, self.month, self.day, 0, 0, 0, 0, 0, 0))
def iswork(self):
"returns true if workable day"
if self.work_saturday:
nbdays = 6
else:
nbdays = 5
if (
self.weekday >= 0 and self.weekday < nbdays
): # monday-friday or monday-saturday
return 1
else:
return 0
def __repr__(self):
return "'%02d/%02d/%04d'" % (self.day, self.month, self.year)
def __str__(self):
return "%02d/%02d/%04d" % (self.day, self.month, self.year)
def ISO(self):
"iso8601 representation of the date"
return "%04d-%02d-%02d" % (self.year, self.month, self.day)
def next(self, days=1):
"date for the next day (nota: may be a non workable day)"
day = self.day + days
month = self.month
year = self.year
while day > MonthNbDays(month, year):
day = day - MonthNbDays(month, year)
month = month + 1
if month > 12:
month = 1
year = year + 1
return self.__class__(
"%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday
)
def prev(self, days=1):
"date for previous day"
day = self.day - days
month = self.month
year = self.year
while day <= 0:
month = month - 1
if month == 0:
month = 12
year = year - 1
day = day + MonthNbDays(month, year)
return self.__class__(
"%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday
)
def next_monday(self):
"date of next monday"
return self.next((7 - self.weekday) % 7)
def prev_monday(self):
"date of last monday, but on sunday, pick next monday"
if self.weekday == 6:
return self.next_monday()
else:
return self.prev(self.weekday)
def __cmp__(self, other):
"""return a negative integer if self < other,
zero if self == other, a positive integer if self > other"""
return int(self.time - other.time)
def __hash__(self):
"we are immutable !"
return hash(self.time) ^ hash(str(self))
# d = ddmmyyyy( '21/12/99' )
def YearTable(
context,
year,
events=[],
firstmonth=9,
lastmonth=7,
halfday=0,
dayattributes="",
pad_width=8,
):
"""Generate a calendar table
events = list of tuples (date, text, color, href [,halfday])
where date is a string in ISO format (yyyy-mm-dd)
halfday is boolean (true: morning, false: afternoon)
text = text to put in calendar (must be short, 1-5 cars) (optional)
if halfday, generate 2 cells per day (morning, afternoon)
"""
T = [
'<table id="maincalendar" class="maincalendar" border="3" cellpadding="1" cellspacing="1" frame="box">'
]
T.append("<tr>")
month = firstmonth
while 1:
T.append('<td valign="top">')
T.append(MonthTableHead(month))
T.append(
MonthTableBody(
month,
year,
events,
halfday,
dayattributes,
context.is_work_saturday(),
pad_width=pad_width,
)
)
T.append(MonthTableTail())
T.append("</td>")
if month == lastmonth:
break
month = month + 1
if month > 12:
month = 1
year = year + 1
T.append("</table>")
return string.join(T, "\n")
# ---------------
class ZAbsences( class ZAbsences(
ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit
): ):
@ -373,7 +195,7 @@ class ZAbsences(
% vars(), % vars(),
) )
cnx.commit() cnx.commit()
invalidateAbsEtudDate(self, etudid, jour) sco_abs.invalidateAbsEtudDate(self, etudid, jour)
sco_abs_notification.abs_notify(self, etudid, jour) sco_abs_notification.abs_notify(self, etudid, jour)
def _AddJustif(self, etudid, jour, matin, REQUEST, description=None): def _AddJustif(self, etudid, jour, matin, REQUEST, description=None):
@ -396,7 +218,7 @@ class ZAbsences(
msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(),
) )
cnx.commit() cnx.commit()
invalidateAbsEtudDate(self, etudid, jour) sco_abs.invalidateAbsEtudDate(self, etudid, jour)
def _AnnuleAbsence(self, etudid, jour, matin, moduleimpl_id=None, REQUEST=None): def _AnnuleAbsence(self, etudid, jour, matin, moduleimpl_id=None, REQUEST=None):
"""Annule une absence ds base """Annule une absence ds base
@ -419,7 +241,7 @@ class ZAbsences(
% vars(), % vars(),
) )
cnx.commit() cnx.commit()
invalidateAbsEtudDate(self, etudid, jour) sco_abs.invalidateAbsEtudDate(self, etudid, jour)
def _AnnuleJustif(self, etudid, jour, matin, REQUEST=None): def _AnnuleJustif(self, etudid, jour, matin, REQUEST=None):
"Annule un justificatif" "Annule un justificatif"
@ -443,7 +265,7 @@ class ZAbsences(
msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(),
) )
cnx.commit() cnx.commit()
invalidateAbsEtudDate(self, etudid, jour) sco_abs.invalidateAbsEtudDate(self, etudid, jour)
# Fonction inutile à supprimer (gestion moduleimpl_id incorrecte): # Fonction inutile à supprimer (gestion moduleimpl_id incorrecte):
# def _AnnuleAbsencesPeriodNoJust(self, etudid, datedebut, datefin, # def _AnnuleAbsencesPeriodNoJust(self, etudid, datedebut, datefin,
@ -462,8 +284,8 @@ class ZAbsences(
# logdb(REQUEST, cnx, 'AnnuleAbsencesPeriodNoJust', etudid=etudid, # logdb(REQUEST, cnx, 'AnnuleAbsencesPeriodNoJust', etudid=etudid,
# msg='%(datedebut)s - %(datefin)s - (moduleimpl_id)s'%vars()) # msg='%(datedebut)s - %(datefin)s - (moduleimpl_id)s'%vars())
# cnx.commit() # cnx.commit()
# invalidateAbsEtudDate(self, etudid, datedebut) # sco_abs.invalidateAbsEtudDate(self, etudid, datedebut)
# invalidateAbsEtudDate(self, etudid, datefin) # si un semestre commence apres datedebut et termine avant datefin, il ne sera pas invalide. Tant pis ;-) # sco_abs.invalidateAbsEtudDate(self, etudid, datefin) # si un semestre commence apres datedebut et termine avant datefin, il ne sera pas invalide. Tant pis ;-)
security.declareProtected(ScoAbsChange, "AnnuleAbsencesDatesNoJust") security.declareProtected(ScoAbsChange, "AnnuleAbsencesDatesNoJust")
@ -497,7 +319,7 @@ class ZAbsences(
"delete from absences where etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s", "delete from absences where etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s",
vars(), vars(),
) )
invalidateAbsEtudDate(self, etudid, date) sco_abs.invalidateAbsEtudDate(self, etudid, date)
# s'assure que les justificatifs ne sont pas "absents" # s'assure que les justificatifs ne sont pas "absents"
for date in dates: for date in dates:
cursor.execute( cursor.execute(
@ -855,7 +677,7 @@ class ZAbsences(
js = "" js = ""
else: else:
js = 'onmouseover="highlightweek(this);" onmouseout="deselectweeks();" onclick="wclick(this);"' js = 'onmouseover="highlightweek(this);" onmouseout="deselectweeks();" onclick="wclick(this);"'
C = YearTable(self, int(year), dayattributes=js) C = sco_abs.YearTable(self, int(year), dayattributes=js)
return C return C
# --- Misc tools.... ------------------ # --- Misc tools.... ------------------
@ -1352,7 +1174,7 @@ class ZAbsences(
else: else:
checked = "" checked = ""
# bulle lors du passage souris # bulle lors du passage souris
coljour = DAYNAMES[ coljour = sco_abs.DAYNAMES[
(calendar.weekday(int(date[:4]), int(date[5:7]), int(date[8:]))) (calendar.weekday(int(date[:4]), int(date[5:7]), int(date[8:])))
] ]
datecol = coljour + " " + date[8:] + "/" + date[5:7] + "/" + date[:4] datecol = coljour + " " + date[8:] + "/" + date[5:7] + "/" + date[:4]
@ -1798,7 +1620,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
justified = int(justified) justified = int(justified)
# #
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
billet_id = billet_absence_create( billet_id = sco_abs.billet_absence_create(
cnx, cnx,
{ {
"etudid": etud["etudid"], "etudid": etud["etudid"],
@ -1814,7 +1636,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
if REQUEST: if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
billets = billet_absence_list(cnx, {"billet_id": billet_id}) billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
tab = self._tableBillets(billets, etud=etud) tab = self._tableBillets(billets, etud=etud)
log( log(
"AddBilletAbsence: new billet_id=%s (%gs)" "AddBilletAbsence: new billet_id=%s (%gs)"
@ -1940,7 +1762,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
etud = etuds[0] etud = etuds[0]
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
billets = billet_absence_list(cnx, {"etudid": etud["etudid"]}) billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
tab = self._tableBillets(billets, etud=etud) tab = self._tableBillets(billets, etud=etud)
return tab.make_page(self, REQUEST=REQUEST, format=format) return tab.make_page(self, REQUEST=REQUEST, format=format)
@ -1960,7 +1782,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
def listeBillets(self, REQUEST=None): def listeBillets(self, REQUEST=None):
"""Page liste des billets non traités et formulaire recherche d'un billet""" """Page liste des billets non traités et formulaire recherche d'un billet"""
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
billets = billet_absence_list(cnx, {"etat": 0}) billets = sco_abs.billet_absence_list(cnx, {"etat": 0})
tab = self._tableBillets(billets) tab = self._tableBillets(billets)
T = tab.html() T = tab.html()
H = [ H = [
@ -1986,7 +1808,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
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 = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
if not billets: if not billets:
return REQUEST.RESPONSE.redirect( return REQUEST.RESPONSE.redirect(
"listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id
@ -2001,7 +1823,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
parameters={"billet_id": billet_id}, parameters={"billet_id": billet_id},
) )
billet_absence_delete(cnx, billet_id) sco_abs.billet_absence_delete(cnx, billet_id)
return REQUEST.RESPONSE.redirect("listeBillets?head_message=Billet%20supprimé") return REQUEST.RESPONSE.redirect("listeBillets?head_message=Billet%20supprimé")
@ -2050,7 +1872,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
n += 2 n += 2
# 2- change etat du billet # 2- change etat du billet
billet_absence_edit(cnx, {"billet_id": billet["billet_id"], "etat": 1}) sco_abs.billet_absence_edit(cnx, {"billet_id": billet["billet_id"], "etat": 1})
return n return n
@ -2059,7 +1881,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
def ProcessBilletAbsenceForm(self, billet_id, REQUEST=None): def ProcessBilletAbsenceForm(self, billet_id, REQUEST=None):
"""Formulaire traitement d'un billet""" """Formulaire traitement d'un billet"""
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
billets = billet_absence_list(cnx, {"billet_id": billet_id}) billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
if not billets: if not billets:
return REQUEST.RESPONSE.redirect( return REQUEST.RESPONSE.redirect(
"listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id
@ -2134,7 +1956,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
'</div><p><a class="stdlink" href="listeBillets">Autre billets en attente</a></p><h4>Billets déclarés par %s</h4>' '</div><p><a class="stdlink" href="listeBillets">Autre billets en attente</a></p><h4>Billets déclarés par %s</h4>'
% (etud["nomprenom"]) % (etud["nomprenom"])
) )
billets = billet_absence_list(cnx, {"etudid": etud["etudid"]}) billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
tab = self._tableBillets(billets, etud=etud) tab = self._tableBillets(billets, etud=etud)
H.append(tab.html()) H.append(tab.html())
return "\n".join(H) + self.sco_footer(REQUEST) return "\n".join(H) + self.sco_footer(REQUEST)
@ -2172,258 +1994,6 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
return repr(doc) return repr(doc)
_billet_absenceEditor = notesdb.EditableTable(
"billet_absence",
"billet_id",
(
"billet_id",
"etudid",
"abs_begin",
"abs_end",
"description",
"etat",
"entry_date",
"justified",
),
sortkey="entry_date desc",
)
billet_absence_create = _billet_absenceEditor.create
billet_absence_delete = _billet_absenceEditor.delete
billet_absence_list = _billet_absenceEditor.list
billet_absence_edit = _billet_absenceEditor.edit
# ------ HTML Calendar functions (see YearTable function)
# MONTH/DAY NAMES:
MONTHNAMES = (
"Janvier",
"F&eacute;vrier",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"Aout",
"Septembre",
"Octobre",
"Novembre",
"D&eacute;cembre",
)
MONTHNAMES_ABREV = (
"Jan.",
"F&eacute;v.",
"Mars",
"Avr.",
"Mai&nbsp;",
"Juin",
"Juil",
"Aout",
"Sept",
"Oct.",
"Nov.",
"D&eacute;c.",
)
DAYNAMES = ("Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche")
DAYNAMES_ABREV = ("L", "M", "M", "J", "V", "S", "D")
# COLORS:
WHITE = "#FFFFFF"
GRAY1 = "#EEEEEE"
GREEN3 = "#99CC99"
WEEKDAYCOLOR = GRAY1
WEEKENDCOLOR = GREEN3
def MonthTableHead(month):
color = WHITE
return """<table class="monthcalendar" border="0" cellpadding="0" cellspacing="0" frame="box">
<tr bgcolor="%s"><td class="calcol" colspan="2" align="center">%s</td></tr>\n""" % (
color,
MONTHNAMES_ABREV[month - 1],
)
def MonthTableTail():
return "</table>\n"
def MonthTableBody(
month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8
):
firstday, nbdays = calendar.monthrange(year, month)
localtime = time.localtime()
current_weeknum = time.strftime("%U", localtime)
current_year = localtime[0]
T = []
# cherche date du lundi de la 1ere semaine de ce mois
monday = ddmmyyyy("1/%d/%d" % (month, year))
while monday.weekday != 0:
monday = monday.prev()
if work_saturday:
weekend = ("D",)
else:
weekend = ("S", "D")
if not halfday:
for d in range(1, nbdays + 1):
weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
)
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
if day in weekend:
bgcolor = WEEKENDCOLOR
weekclass = "wkend"
attrs = ""
else:
bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes
color = None
legend = ""
href = ""
descr = ""
# event this day ?
# each event is a tuple (date, text, color, href)
# where date is a string in ISO format (yyyy-mm-dd)
for ev in events:
ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10])
if year == ev_year and month == ev_month and ev_day == d:
if ev[1]:
legend = ev[1]
if ev[2]:
color = ev[2]
if ev[3]:
href = ev[3]
if len(ev) > 4 and ev[4]:
descr = ev[4]
#
cc = []
if color != None:
cc.append('<td bgcolor="%s" class="calcell">' % color)
else:
cc.append('<td class="calcell">')
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
if pad_width != None:
n = pad_width - len(legend) # pad to 8 cars
if n > 0:
legend = "&nbsp;" * (n / 2) + legend + "&nbsp;" * ((n + 1) / 2)
else:
legend = "&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>")
cell = string.join(cc, "")
if day == "D":
monday = monday.next(7)
if (
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weekclass += " currentweek"
T.append(
'<tr bgcolor="%s" class="%s" %s><td class="calday">%d%s</td>%s</tr>'
% (bgcolor, weekclass, attrs, d, day, cell)
)
else:
# Calendar with 2 cells / day
for d in range(1, nbdays + 1):
weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
)
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
if day in weekend:
bgcolor = WEEKENDCOLOR
weekclass = "wkend"
attrs = ""
else:
bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes
if (
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weeknum += " currentweek"
if day == "D":
monday = monday.next(7)
T.append(
'<tr bgcolor="%s" class="wk%s" %s><td class="calday">%d%s</td>'
% (bgcolor, weekclass, attrs, d, day)
)
cc = []
for morning in (1, 0):
color = None
legend = ""
href = ""
descr = ""
for ev in events:
ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10])
if ev[4] != None:
ev_half = int(ev[4])
else:
ev_half = 0
if (
year == ev_year
and month == ev_month
and ev_day == d
and morning == ev_half
):
if ev[1]:
legend = ev[1]
if ev[2]:
color = ev[2]
if ev[3]:
href = ev[3]
if len(ev) > 5 and ev[5]:
descr = ev[5]
#
if color != None:
cc.append('<td bgcolor="%s" class="calcell">' % (color))
else:
cc.append('<td class="calcell">')
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
n = 3 - len(legend) # pad to 3 cars
if n > 0:
legend = "&nbsp;" * (n / 2) + legend + "&nbsp;" * ((n + 1) / 2)
else:
legend = "&nbsp;&nbsp;&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>\n")
T.append(string.join(cc, "") + "</tr>")
return string.join(T, "\n")
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# #
# Zope Product Administration # Zope Product Administration
@ -2441,121 +2011,3 @@ def manage_addZAbsences(
# The form used to get the instance id from the user. # The form used to get the instance id from the user.
# manage_addZAbsencesForm = DTMLFile('dtml/manage_addZAbsencesForm', globals()) # manage_addZAbsencesForm = DTMLFile('dtml/manage_addZAbsencesForm', globals())
# --------------------------------------------------------------------
#
# Cache absences
#
# On cache simplement (à la demande) le nombre d'absences de chaque etudiant
# dans un semestre donné.
# Toute modification du semestre (invalidation) invalide le cache
# (simple mécanisme de "listener" sur le cache de semestres)
# Toute modification des absences d'un étudiant invalide les caches des semestres
# concernés à cette date (en général un seul semestre)
#
# On ne cache pas la liste des absences car elle est rarement utilisée (calendrier,
# absences à une date donnée).
#
# --------------------------------------------------------------------
class CAbsSemEtud:
"""Comptes d'absences d'un etudiant dans un semestre"""
def __init__(self, context, sem, etudid):
self.context = context
self.sem = sem
self.etudid = etudid
self._loaded = False
formsemestre_id = sem["formsemestre_id"]
context.Notes._getNotesCache().add_listener(
self.invalidate, formsemestre_id, (etudid, formsemestre_id)
)
def CountAbs(self):
if not self._loaded:
self.load()
return self._CountAbs
def CountAbsJust(self):
if not self._loaded:
self.load()
return self._CountAbsJust
def load(self):
"Load state from DB"
# log('loading CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id']))
# Reload sem, it may have changed
self.sem = sco_formsemestre.get_formsemestre(
self.context, self.sem["formsemestre_id"]
)
debut_sem = notesdb.DateDMYtoISO(self.sem["date_debut"])
fin_sem = notesdb.DateDMYtoISO(self.sem["date_fin"])
self._CountAbs = self.context.Absences.CountAbs(
etudid=self.etudid, debut=debut_sem, fin=fin_sem
)
self._CountAbsJust = self.context.Absences.CountAbsJust(
etudid=self.etudid, debut=debut_sem, fin=fin_sem
)
self._loaded = True
def invalidate(self, args=None):
"Notify me that DB has been modified"
# log('invalidate CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id']))
self._loaded = False
# Accès au cache des absences
ABS_CACHE_INST = {} # { DeptId : { formsemestre_id : { etudid : CAbsEtudSem } } }
def getAbsSemEtud(context, sem, etudid):
AbsSemEtuds = getAbsSemEtuds(context, sem)
if not etudid in AbsSemEtuds:
AbsSemEtuds[etudid] = CAbsSemEtud(context, sem, etudid)
return AbsSemEtuds[etudid]
def getAbsSemEtuds(context, sem):
u = context.GetDBConnexionString() # identifie le dept de facon fiable
if not u in ABS_CACHE_INST:
ABS_CACHE_INST[u] = {}
C = ABS_CACHE_INST[u]
if sem["formsemestre_id"] not in C:
C[sem["formsemestre_id"]] = {}
return C[sem["formsemestre_id"]]
def invalidateAbsEtudDate(context, etudid, date):
"""Doit etre appelé à chaque modification des absences pour cet étudiant et cette date.
Invalide cache absence et PDF bulletins si nécessaire.
date: date au format ISO
"""
# Semestres a cette date:
etud = context.getEtudInfo(etudid=etudid, filled=True)[0]
sems = [
sem
for sem in etud["sems"]
if sem["date_debut_iso"] <= date and sem["date_fin_iso"] >= date
]
# Invalide les PDF et les abscences:
for sem in sems:
# Inval cache bulletin et/ou note_table
if sco_compute_moy.formsemestre_expressions_use_abscounts(
context, sem["formsemestre_id"]
):
pdfonly = False # seules certaines formules utilisent les absences
else:
pdfonly = (
True # efface toujours le PDF car il affiche en général les absences
)
context.Notes._inval_cache(
pdfonly=pdfonly, formsemestre_id=sem["formsemestre_id"]
)
# Inval cache compteurs absences:
AbsSemEtuds = getAbsSemEtuds(context, sem)
if etudid in AbsSemEtuds:
AbsSemEtuds[etudid].invalidate()

View File

@ -107,7 +107,7 @@ class EntreprisesEditor(EditableTable):
"select E.*, I.nom as etud_nom, I.prenom as etud_prenom, C.date from entreprises E, entreprise_contact C, identite I where C.entreprise_id = E.entreprise_id and C.etudid = I.etudid and I.nom ~* %(etud_nom)s ORDER BY E.nom", "select E.*, I.nom as etud_nom, I.prenom as etud_prenom, C.date from entreprises E, entreprise_contact C, identite I where C.entreprise_id = E.entreprise_id and C.etudid = I.etudid and I.nom ~* %(etud_nom)s ORDER BY E.nom",
args, args,
) )
titles, res = [x[0] for x in cursor.description], cursor.dictfetchall() _, res = [x[0] for x in cursor.description], cursor.dictfetchall()
R = [] R = []
for r in res: for r in res:
r["etud_prenom"] = r["etud_prenom"] or "" r["etud_prenom"] = r["etud_prenom"] or ""
@ -450,7 +450,6 @@ class ZEntreprises(
def do_entreprise_correspondant_listnames(self, args={}): def do_entreprise_correspondant_listnames(self, args={}):
"-> liste des noms des correspondants (pour affichage menu)" "-> liste des noms des correspondants (pour affichage menu)"
cnx = self.GetDBConnexion()
C = self.do_entreprise_correspondant_list(args=args) C = self.do_entreprise_correspondant_list(args=args)
return [ return [
(x["prenom"] + " " + x["nom"], str(x["entreprise_corresp_id"])) for x in C (x["prenom"] + " " + x["nom"], str(x["entreprise_corresp_id"])) for x in C
@ -538,43 +537,48 @@ class ZEntreprises(
# (fonction ad-hoc car requete sur plusieurs tables) # (fonction ad-hoc car requete sur plusieurs tables)
raise NotImplementedError raise NotImplementedError
# XXXXX fonction non achevee , non testee... # XXXXX fonction non achevee , non testee...
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) # cnx = self.GetDBConnexion()
vals = dictfilter(args, self.dbfields) # cursor = cnx.cursor(cursor_factory=ScoDocCursor)
# DBSelect # if sortkey:
what = ["*"] # orderby = " order by " + sortkey
operator = " " + operator + " " # else:
cond = " E.entreprise_id = C.entreprise_id " # orderby = ""
if vals: # vals = dictfilter(args, self.dbfields)
cond += " where " + operator.join( # # DBSelect
["%s%s%%(%s)s" % (x, test, x) for x in vals.keys() if vals[x] != None] # what = ["*"]
) # operator = " " + operator + " "
cnuls = " and ".join( # cond = " E.entreprise_id = C.entreprise_id "
["%s is NULL" % x for x in vals.keys() if vals[x] is None] # if vals:
) # cond += " where " + operator.join(
if cnuls: # ["%s%s%%(%s)s" % (x, test, x) for x in vals.keys() if vals[x] != None]
cond = cond + " and " + cnuls # )
else: # cnuls = " and ".join(
cond += "" # ["%s is NULL" % x for x in vals.keys() if vals[x] is None]
cursor.execute( # )
"select distinct" # if cnuls:
+ ", ".join(what) # cond = cond + " and " + cnuls
+ " from entreprises E, entreprise_contact C " # else:
+ cond # cond += ""
+ orderby, # cursor.execute(
vals, # "select distinct"
) # + ", ".join(what)
titles, res = [x[0] for x in cursor.description], cursor.fetchall() # + " from entreprises E, entreprise_contact C "
# # + cond
R = [] # + orderby,
for r in res: # vals,
d = {} # )
for i in range(len(titles)): # titles, res = [x[0] for x in cursor.description], cursor.fetchall()
v = r[i] # #
# value not formatted ! (see EditableTable.list()) # R = []
d[titles[i]] = v # for r in res:
R.append(d) # d = {}
return R # for i in range(len(titles)):
# v = r[i]
# # value not formatted ! (see EditableTable.list())
# d[titles[i]] = v
# R.append(d)
# return R
# -------- Formulaires: traductions du DTML # -------- Formulaires: traductions du DTML
security.declareProtected(ScoEntrepriseChange, "entreprise_create") security.declareProtected(ScoEntrepriseChange, "entreprise_create")
@ -892,7 +896,3 @@ def manage_addZEntreprises(
if REQUEST is not None: if REQUEST is not None:
return self.manage_main(self, REQUEST) return self.manage_main(self, REQUEST)
# return self.manage_editForm(self, REQUEST) # return self.manage_editForm(self, REQUEST)
# The form used to get the instance id from the user.
# manage_addZAbsencesForm = DTMLFile('dtml/manage_addZAbsencesForm', globals())

View File

@ -89,6 +89,7 @@ import ZEntreprises
import ZScoUsers import ZScoUsers
import sco_modalites import sco_modalites
import ImportScolars import ImportScolars
import sco_abs
import sco_portal_apogee import sco_portal_apogee
import sco_synchro_etuds import sco_synchro_etuds
import sco_page_etud import sco_page_etud
@ -274,13 +275,13 @@ UE11 Découverte métiers <span class="ue_code">(code UCOD46, 16 ECTS, Apo <span
</p> </p>
</body> </body>
""" """
return ( # return (
self.sco_header(REQUEST) # self.sco_header(REQUEST)
+ """<div class="xp">%s</div>""" % x # + """<div class="xp">%s</div>""" % x
+ self.sco_footer(REQUEST) # + self.sco_footer(REQUEST)
) # )
b = "<p>Hello, World !</p><br/>" # b = "<p>Hello, World !</p><br/>"
raise ValueError("essai exception") # raise ValueError("essai exception")
# raise ScoValueError('essai exception !', dest_url='totoro', REQUEST=REQUEST) # raise ScoValueError('essai exception !', dest_url='totoro', REQUEST=REQUEST)
# cursor = cnx.cursor(cursor_factory=ScoDocCursor) # cursor = cnx.cursor(cursor_factory=ScoDocCursor)
@ -592,7 +593,7 @@ UE11 Découverte métiers <span class="ue_code">(code UCOD46, 16 ECTS, Apo <span
# calcule dates 1er jour semaine pour absences # calcule dates 1er jour semaine pour absences
try: try:
if with_absences: if with_absences:
first_monday = ZAbsences.ddmmyyyy(sem["date_debut"]).prev_monday() first_monday = sco_abs.ddmmyyyy(sem["date_debut"]).prev_monday()
FA = [] # formulaire avec menu saisi absences FA = [] # formulaire avec menu saisi absences
FA.append( FA.append(
'<td><form action="Absences/SignaleAbsenceGrSemestre" method="get">' '<td><form action="Absences/SignaleAbsenceGrSemestre" method="get">'

View File

@ -26,7 +26,7 @@
############################################################################## ##############################################################################
from sco_utils import * from sco_utils import *
from ZAbsences import getAbsSemEtud from sco_abs import getAbsSemEtud
""" """
Génération de la "sidebar" (marge gauche des pages HTML) Génération de la "sidebar" (marge gauche des pages HTML)

595
sco_abs.py Normal file
View File

@ -0,0 +1,595 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Fonctions sur les absences
"""
# Anciennement dans ZAbscences.py, séparé pour migration
import string
import datetime
import re
import time
import calendar
import cgi
import notesdb
from sco_exceptions import ScoValueError, ScoInvalidDateError
import sco_formsemestre
import sco_compute_moy
def MonthNbDays(month, year):
"returns nb of days in month"
if month > 7:
month = month + 1
if month % 2:
return 31
elif month == 2:
if calendar.isleap(year):
return 29
else:
return 28
else:
return 30
class ddmmyyyy:
"""immutable dates"""
def __init__(self, date=None, fmt="ddmmyyyy", work_saturday=False):
self.work_saturday = work_saturday
if date is None:
return
try:
if fmt == "ddmmyyyy":
self.day, self.month, self.year = string.split(date, "/")
elif fmt == "iso":
self.year, self.month, self.day = string.split(date, "-")
else:
raise ValueError("invalid format spec. (%s)" % fmt)
self.year = string.atoi(self.year)
self.month = string.atoi(self.month)
self.day = string.atoi(self.day)
except:
raise ScoValueError("date invalide: %s" % date)
# accept years YYYY or YY, uses 1970 as pivot
if self.year < 1970:
if self.year > 100:
raise ScoInvalidDateError("Année invalide: %s" % self.year)
if self.year < 70:
self.year = self.year + 2000
else:
self.year = self.year + 1900
if self.month < 1 or self.month > 12:
raise ScoInvalidDateError("Mois invalide: %s" % self.month)
if self.day < 1 or self.day > MonthNbDays(self.month, self.year):
raise ScoInvalidDateError("Jour invalide: %s" % self.day)
# weekday in 0-6, where 0 is monday
self.weekday = calendar.weekday(self.year, self.month, self.day)
self.time = time.mktime((self.year, self.month, self.day, 0, 0, 0, 0, 0, 0))
def iswork(self):
"returns true if workable day"
if self.work_saturday:
nbdays = 6
else:
nbdays = 5
if (
self.weekday >= 0 and self.weekday < nbdays
): # monday-friday or monday-saturday
return 1
else:
return 0
def __repr__(self):
return "'%02d/%02d/%04d'" % (self.day, self.month, self.year)
def __str__(self):
return "%02d/%02d/%04d" % (self.day, self.month, self.year)
def ISO(self):
"iso8601 representation of the date"
return "%04d-%02d-%02d" % (self.year, self.month, self.day)
def next(self, days=1):
"date for the next day (nota: may be a non workable day)"
day = self.day + days
month = self.month
year = self.year
while day > MonthNbDays(month, year):
day = day - MonthNbDays(month, year)
month = month + 1
if month > 12:
month = 1
year = year + 1
return self.__class__(
"%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday
)
def prev(self, days=1):
"date for previous day"
day = self.day - days
month = self.month
year = self.year
while day <= 0:
month = month - 1
if month == 0:
month = 12
year = year - 1
day = day + MonthNbDays(month, year)
return self.__class__(
"%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday
)
def next_monday(self):
"date of next monday"
return self.next((7 - self.weekday) % 7)
def prev_monday(self):
"date of last monday, but on sunday, pick next monday"
if self.weekday == 6:
return self.next_monday()
else:
return self.prev(self.weekday)
def __cmp__(self, other):
"""return a negative integer if self < other,
zero if self == other, a positive integer if self > other"""
return int(self.time - other.time)
def __hash__(self):
"we are immutable !"
return hash(self.time) ^ hash(str(self))
# d = ddmmyyyy( '21/12/99' )
def YearTable(
context,
year,
events=[],
firstmonth=9,
lastmonth=7,
halfday=0,
dayattributes="",
pad_width=8,
):
"""Generate a calendar table
events = list of tuples (date, text, color, href [,halfday])
where date is a string in ISO format (yyyy-mm-dd)
halfday is boolean (true: morning, false: afternoon)
text = text to put in calendar (must be short, 1-5 cars) (optional)
if halfday, generate 2 cells per day (morning, afternoon)
"""
T = [
'<table id="maincalendar" class="maincalendar" border="3" cellpadding="1" cellspacing="1" frame="box">'
]
T.append("<tr>")
month = firstmonth
while 1:
T.append('<td valign="top">')
T.append(MonthTableHead(month))
T.append(
MonthTableBody(
month,
year,
events,
halfday,
dayattributes,
context.is_work_saturday(),
pad_width=pad_width,
)
)
T.append(MonthTableTail())
T.append("</td>")
if month == lastmonth:
break
month = month + 1
if month > 12:
month = 1
year = year + 1
T.append("</table>")
return string.join(T, "\n")
# ---- BILLETS
_billet_absenceEditor = notesdb.EditableTable(
"billet_absence",
"billet_id",
(
"billet_id",
"etudid",
"abs_begin",
"abs_end",
"description",
"etat",
"entry_date",
"justified",
),
sortkey="entry_date desc",
)
billet_absence_create = _billet_absenceEditor.create
billet_absence_delete = _billet_absenceEditor.delete
billet_absence_list = _billet_absenceEditor.list
billet_absence_edit = _billet_absenceEditor.edit
# ------ HTML Calendar functions (see YearTable function)
# MONTH/DAY NAMES:
MONTHNAMES = (
"Janvier",
"F&eacute;vrier",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"Aout",
"Septembre",
"Octobre",
"Novembre",
"D&eacute;cembre",
)
MONTHNAMES_ABREV = (
"Jan.",
"F&eacute;v.",
"Mars",
"Avr.",
"Mai&nbsp;",
"Juin",
"Juil",
"Aout",
"Sept",
"Oct.",
"Nov.",
"D&eacute;c.",
)
DAYNAMES = ("Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche")
DAYNAMES_ABREV = ("L", "M", "M", "J", "V", "S", "D")
# COLORS:
WHITE = "#FFFFFF"
GRAY1 = "#EEEEEE"
GREEN3 = "#99CC99"
WEEKDAYCOLOR = GRAY1
WEEKENDCOLOR = GREEN3
def MonthTableHead(month):
color = WHITE
return """<table class="monthcalendar" border="0" cellpadding="0" cellspacing="0" frame="box">
<tr bgcolor="%s"><td class="calcol" colspan="2" align="center">%s</td></tr>\n""" % (
color,
MONTHNAMES_ABREV[month - 1],
)
def MonthTableTail():
return "</table>\n"
def MonthTableBody(
month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8
):
firstday, nbdays = calendar.monthrange(year, month)
localtime = time.localtime()
current_weeknum = time.strftime("%U", localtime)
current_year = localtime[0]
T = []
# cherche date du lundi de la 1ere semaine de ce mois
monday = ddmmyyyy("1/%d/%d" % (month, year))
while monday.weekday != 0:
monday = monday.prev()
if work_saturday:
weekend = ("D",)
else:
weekend = ("S", "D")
if not halfday:
for d in range(1, nbdays + 1):
weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
)
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
if day in weekend:
bgcolor = WEEKENDCOLOR
weekclass = "wkend"
attrs = ""
else:
bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes
color = None
legend = ""
href = ""
descr = ""
# event this day ?
# each event is a tuple (date, text, color, href)
# where date is a string in ISO format (yyyy-mm-dd)
for ev in events:
ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10])
if year == ev_year and month == ev_month and ev_day == d:
if ev[1]:
legend = ev[1]
if ev[2]:
color = ev[2]
if ev[3]:
href = ev[3]
if len(ev) > 4 and ev[4]:
descr = ev[4]
#
cc = []
if color != None:
cc.append('<td bgcolor="%s" class="calcell">' % color)
else:
cc.append('<td class="calcell">')
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
if pad_width != None:
n = pad_width - len(legend) # pad to 8 cars
if n > 0:
legend = "&nbsp;" * (n / 2) + legend + "&nbsp;" * ((n + 1) / 2)
else:
legend = "&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>")
cell = string.join(cc, "")
if day == "D":
monday = monday.next(7)
if (
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weekclass += " currentweek"
T.append(
'<tr bgcolor="%s" class="%s" %s><td class="calday">%d%s</td>%s</tr>'
% (bgcolor, weekclass, attrs, d, day, cell)
)
else:
# Calendar with 2 cells / day
for d in range(1, nbdays + 1):
weeknum = time.strftime(
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
)
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
if day in weekend:
bgcolor = WEEKENDCOLOR
weekclass = "wkend"
attrs = ""
else:
bgcolor = WEEKDAYCOLOR
weekclass = "wk" + str(monday).replace("/", "_")
attrs = trattributes
if (
weeknum == current_weeknum
and current_year == year
and weekclass != "wkend"
):
weeknum += " currentweek"
if day == "D":
monday = monday.next(7)
T.append(
'<tr bgcolor="%s" class="wk%s" %s><td class="calday">%d%s</td>'
% (bgcolor, weekclass, attrs, d, day)
)
cc = []
for morning in (1, 0):
color = None
legend = ""
href = ""
descr = ""
for ev in events:
ev_year = int(ev[0][:4])
ev_month = int(ev[0][5:7])
ev_day = int(ev[0][8:10])
if ev[4] != None:
ev_half = int(ev[4])
else:
ev_half = 0
if (
year == ev_year
and month == ev_month
and ev_day == d
and morning == ev_half
):
if ev[1]:
legend = ev[1]
if ev[2]:
color = ev[2]
if ev[3]:
href = ev[3]
if len(ev) > 5 and ev[5]:
descr = ev[5]
#
if color != None:
cc.append('<td bgcolor="%s" class="calcell">' % (color))
else:
cc.append('<td class="calcell">')
if href:
href = 'href="%s"' % href
if descr:
descr = 'title="%s"' % cgi.escape(descr, quote=True)
if href or descr:
cc.append("<a %s %s>" % (href, descr))
if legend or d == 1:
n = 3 - len(legend) # pad to 3 cars
if n > 0:
legend = "&nbsp;" * (n / 2) + legend + "&nbsp;" * ((n + 1) / 2)
else:
legend = "&nbsp;&nbsp;&nbsp;" # empty cell
cc.append(legend)
if href or descr:
cc.append("</a>")
cc.append("</td>\n")
T.append(string.join(cc, "") + "</tr>")
return string.join(T, "\n")
# --------------------------------------------------------------------
#
# Cache absences
#
# On cache simplement (à la demande) le nombre d'absences de chaque etudiant
# dans un semestre donné.
# Toute modification du semestre (invalidation) invalide le cache
# (simple mécanisme de "listener" sur le cache de semestres)
# Toute modification des absences d'un étudiant invalide les caches des semestres
# concernés à cette date (en général un seul semestre)
#
# On ne cache pas la liste des absences car elle est rarement utilisée (calendrier,
# absences à une date donnée).
#
# --------------------------------------------------------------------
class CAbsSemEtud:
"""Comptes d'absences d'un etudiant dans un semestre"""
def __init__(self, context, sem, etudid):
self.context = context
self.sem = sem
self.etudid = etudid
self._loaded = False
formsemestre_id = sem["formsemestre_id"]
context.Notes._getNotesCache().add_listener(
self.invalidate, formsemestre_id, (etudid, formsemestre_id)
)
def CountAbs(self):
if not self._loaded:
self.load()
return self._CountAbs
def CountAbsJust(self):
if not self._loaded:
self.load()
return self._CountAbsJust
def load(self):
"Load state from DB"
# log('loading CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id']))
# Reload sem, it may have changed
self.sem = sco_formsemestre.get_formsemestre(
self.context, self.sem["formsemestre_id"]
)
debut_sem = notesdb.DateDMYtoISO(self.sem["date_debut"])
fin_sem = notesdb.DateDMYtoISO(self.sem["date_fin"])
self._CountAbs = self.context.Absences.CountAbs(
etudid=self.etudid, debut=debut_sem, fin=fin_sem
)
self._CountAbsJust = self.context.Absences.CountAbsJust(
etudid=self.etudid, debut=debut_sem, fin=fin_sem
)
self._loaded = True
def invalidate(self, args=None):
"Notify me that DB has been modified"
# log('invalidate CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id']))
self._loaded = False
# Accès au cache des absences
ABS_CACHE_INST = {} # { DeptId : { formsemestre_id : { etudid : CAbsEtudSem } } }
def getAbsSemEtud(context, sem, etudid):
AbsSemEtuds = getAbsSemEtuds(context, sem)
if not etudid in AbsSemEtuds:
AbsSemEtuds[etudid] = CAbsSemEtud(context, sem, etudid)
return AbsSemEtuds[etudid]
def getAbsSemEtuds(context, sem):
u = context.GetDBConnexionString() # identifie le dept de facon fiable
if not u in ABS_CACHE_INST:
ABS_CACHE_INST[u] = {}
C = ABS_CACHE_INST[u]
if sem["formsemestre_id"] not in C:
C[sem["formsemestre_id"]] = {}
return C[sem["formsemestre_id"]]
def invalidateAbsEtudDate(context, etudid, date):
"""Doit etre appelé à chaque modification des absences pour cet étudiant et cette date.
Invalide cache absence et PDF bulletins si nécessaire.
date: date au format ISO
"""
# Semestres a cette date:
etud = context.getEtudInfo(etudid=etudid, filled=True)[0]
sems = [
sem
for sem in etud["sems"]
if sem["date_debut_iso"] <= date and sem["date_fin_iso"] >= date
]
# Invalide les PDF et les abscences:
for sem in sems:
# Inval cache bulletin et/ou note_table
if sco_compute_moy.formsemestre_expressions_use_abscounts(
context, sem["formsemestre_id"]
):
pdfonly = False # seules certaines formules utilisent les absences
else:
pdfonly = (
True # efface toujours le PDF car il affiche en général les absences
)
context.Notes._inval_cache(
pdfonly=pdfonly, formsemestre_id=sem["formsemestre_id"]
)
# Inval cache compteurs absences:
AbsSemEtuds = getAbsSemEtuds(context, sem)
if etudid in AbsSemEtuds:
AbsSemEtuds[etudid].invalidate()

View File

@ -40,7 +40,7 @@ import sco_find_etud
import sco_formsemestre import sco_formsemestre
import sco_photos import sco_photos
import ZAbsences import sco_abs
def doSignaleAbsence( def doSignaleAbsence(
@ -631,7 +631,7 @@ def CalAbs(context, REQUEST=None): # etud implied
events.append( events.append(
(str(a["jour"]), "X", "#8EA2C6", "", a["matin"], a["description"]) (str(a["jour"]), "X", "#8EA2C6", "", a["matin"], a["description"])
) )
CalHTML = ZAbsences.YearTable(context, anneescolaire, events=events, halfday=1) CalHTML = sco_abs.YearTable(context, anneescolaire, events=events, halfday=1)
# #
H = [ H = [

View File

@ -44,7 +44,7 @@ import sco_groups
import sco_pvjury import sco_pvjury
import sco_formsemestre_status import sco_formsemestre_status
import sco_photos import sco_photos
import ZAbsences import sco_abs
import sco_abs_views import sco_abs_views
import sco_preferences import sco_preferences
import sco_codes_parcours import sco_codes_parcours
@ -150,7 +150,7 @@ def formsemestre_bulletinetud_dict(
context, pid context, pid
) )
# --- Absences # --- Absences
AbsSemEtud = ZAbsences.getAbsSemEtud(context, nt.sem, etudid) AbsSemEtud = sco_abs.getAbsSemEtud(context, nt.sem, etudid)
I["nbabs"] = AbsSemEtud.CountAbs() I["nbabs"] = AbsSemEtud.CountAbs()
I["nbabsjust"] = AbsSemEtud.CountAbsJust() I["nbabsjust"] = AbsSemEtud.CountAbsJust()

View File

@ -34,7 +34,7 @@ from notes_table import *
import sco_formsemestre import sco_formsemestre
import sco_groups import sco_groups
import sco_photos import sco_photos
import ZAbsences import sco_abs
import sco_bulletins import sco_bulletins
# -------- Bulletin en JSON # -------- Bulletin en JSON
@ -321,7 +321,7 @@ def formsemestre_bulletinetud_published_dict(
if context.get_preference("bul_show_abs", formsemestre_id): if context.get_preference("bul_show_abs", formsemestre_id):
debut_sem = DateDMYtoISO(sem["date_debut"]) debut_sem = DateDMYtoISO(sem["date_debut"])
fin_sem = DateDMYtoISO(sem["date_fin"]) fin_sem = DateDMYtoISO(sem["date_fin"])
AbsEtudSem = ZAbsences.getAbsSemEtud(context, sem, etudid) AbsEtudSem = sco_abs.getAbsSemEtud(context, sem, etudid)
nbabs = AbsEtudSem.CountAbs() nbabs = AbsEtudSem.CountAbs()
nbabsjust = AbsEtudSem.CountAbsJust() nbabsjust = AbsEtudSem.CountAbsJust()

View File

@ -41,7 +41,7 @@ from notes_table import *
import sco_formsemestre import sco_formsemestre
import sco_groups import sco_groups
import sco_photos import sco_photos
import ZAbsences import sco_abs
import sco_bulletins import sco_bulletins
# -------- Bulletin en XML # -------- Bulletin en XML
@ -323,7 +323,7 @@ def make_xml_formsemestre_bulletinetud(
if context.get_preference("bul_show_abs", formsemestre_id): if context.get_preference("bul_show_abs", formsemestre_id):
debut_sem = DateDMYtoISO(sem["date_debut"]) debut_sem = DateDMYtoISO(sem["date_debut"])
fin_sem = DateDMYtoISO(sem["date_fin"]) fin_sem = DateDMYtoISO(sem["date_fin"])
AbsEtudSem = ZAbsences.getAbsSemEtud(context, sem, etudid) AbsEtudSem = sco_abs.getAbsSemEtud(context, sem, etudid)
nbabs = AbsEtudSem.CountAbs() nbabs = AbsEtudSem.CountAbs()
nbabsjust = AbsEtudSem.CountAbsJust() nbabsjust = AbsEtudSem.CountAbsJust()
doc._push() doc._push()

View File

@ -37,7 +37,7 @@ import sco_formsemestre
import sco_groups import sco_groups
import sco_evaluations import sco_evaluations
from sco_formulas import * from sco_formulas import *
import ZAbsences import sco_abs
def moduleimpl_has_expression(context, mod): def moduleimpl_has_expression(context, mod):
@ -124,7 +124,7 @@ def compute_user_formula(
Retourne moy, et en cas d'erreur met à jour diag_info (msg) Retourne moy, et en cas d'erreur met à jour diag_info (msg)
""" """
if use_abs: if use_abs:
AbsSemEtud = ZAbsences.getAbsSemEtud(context, sem, etudid) AbsSemEtud = sco_abs.getAbsSemEtud(context, sem, etudid)
nbabs = AbsSemEtud.CountAbs() nbabs = AbsSemEtud.CountAbs()
nbabs_just = AbsSemEtud.CountAbsJust() nbabs_just = AbsSemEtud.CountAbsJust()
else: else:

View File

@ -42,7 +42,7 @@ 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 sco_abs
import sco_evaluations import sco_evaluations
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -487,7 +487,7 @@ def formsemestre_evaluations_cal(context, formsemestre_id, REQUEST=None):
if day > today: if day > today:
e[2] = color_futur e[2] = color_futur
CalHTML = ZAbsences.YearTable( CalHTML = sco_abs.YearTable(
context.Absences, year, events=events.values(), halfday=False, pad_width=None context.Absences, year, events=events.values(), halfday=False, pad_width=None
) )

View File

@ -35,7 +35,7 @@ from notes_log import log
from scolog import logdb from scolog import logdb
from notes_table import * from notes_table import *
import notes_table import notes_table
from ZAbsences import getAbsSemEtud from sco_abs import getAbsSemEtud
import sco_formsemestre import sco_formsemestre
import sco_formsemestre_edit import sco_formsemestre_edit

View File

@ -35,7 +35,7 @@ from sco_utils import *
import html_sco_header import html_sco_header
from gen_tables import GenTable from gen_tables import GenTable
import scolars import scolars
import ZAbsences import sco_abs
import sco_excel import sco_excel
import sco_formsemestre import sco_formsemestre
import sco_groups import sco_groups
@ -870,7 +870,7 @@ def form_choix_jour_saisie_hebdo(
if not authuser.has_permission(ScoAbsChange, context): if not authuser.has_permission(ScoAbsChange, context):
return "" return ""
sem = groups_infos.formsemestre sem = groups_infos.formsemestre
first_monday = ZAbsences.ddmmyyyy(sem["date_debut"]).prev_monday() first_monday = sco_abs.ddmmyyyy(sem["date_debut"]).prev_monday()
today_idx = datetime.date.today().weekday() today_idx = datetime.date.today().weekday()
FA = [] # formulaire avec menu saisi absences FA = [] # formulaire avec menu saisi absences
@ -923,7 +923,7 @@ def form_choix_saisie_semaine(context, groups_infos, REQUEST=None):
) # car ici utilisee dans un format string ! ) # car ici utilisee dans un format string !
DateJour = time.strftime("%d/%m/%Y") DateJour = time.strftime("%d/%m/%Y")
datelundi = ZAbsences.ddmmyyyy(DateJour).prev_monday() datelundi = sco_abs.ddmmyyyy(DateJour).prev_monday()
FA = [] # formulaire avec menu saisi hebdo des absences FA = [] # formulaire avec menu saisi hebdo des absences
FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">') FA.append('<form action="Absences/SignaleAbsenceGrHebdo" method="get">')
FA.append('<input type="hidden" name="datelundi" value="%s"/>' % datelundi) FA.append('<input type="hidden" name="datelundi" value="%s"/>' % datelundi)

View File

@ -43,7 +43,7 @@ import sco_formsemestre
import sco_formsemestre_status import sco_formsemestre_status
from sco_formsemestre_status import makeMenu from sco_formsemestre_status import makeMenu
import sco_compute_moy import sco_compute_moy
import ZAbsences import sco_abs
# ported from old DTML code in oct 2009 # ported from old DTML code in oct 2009
@ -241,7 +241,7 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
if authuser.has_permission( if authuser.has_permission(
ScoAbsChange, context ScoAbsChange, context
) and sco_formsemestre.sem_est_courant(context, sem): ) and sco_formsemestre.sem_est_courant(context, sem):
datelundi = ZAbsences.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday() datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday()
H.append( H.append(
'<span class="moduleimpl_abs_link"><a class="stdlink" href="Absences/SignaleAbsenceGrHebdo?formsemestre_id=%s&moduleimpl_id=%s&datelundi=%s">Saisie Absences hebdo.</a></span>' '<span class="moduleimpl_abs_link"><a class="stdlink" href="Absences/SignaleAbsenceGrHebdo?formsemestre_id=%s&moduleimpl_id=%s&datelundi=%s">Saisie Absences hebdo.</a></span>'
% (formsemestre_id, moduleimpl_id, datelundi) % (formsemestre_id, moduleimpl_id, datelundi)

View File

@ -36,7 +36,7 @@ from notes_log import log
from gen_tables import GenTable from gen_tables import GenTable
import sco_formsemestre import sco_formsemestre
import sco_groups import sco_groups
import ZAbsences import sco_abs
from sco_codes_parcours import code_semestre_validant, code_semestre_attente from sco_codes_parcours import code_semestre_validant, code_semestre_attente
@ -90,7 +90,7 @@ def etud_get_poursuite_info(context, sem, etud):
rangs.append(["rang_" + codeModule, rangModule]) rangs.append(["rang_" + codeModule, rangModule])
# Absences # Absences
AbsSemEtud = ZAbsences.getAbsSemEtud(context, nt.sem, etudid) AbsSemEtud = sco_abs.getAbsSemEtud(context, nt.sem, etudid)
NbAbs = AbsSemEtud.CountAbs() NbAbs = AbsSemEtud.CountAbs()
NbAbsJust = AbsSemEtud.CountAbsJust() NbAbsJust = AbsSemEtud.CountAbsJust()
if ( if (

View File

@ -38,7 +38,7 @@ import sco_formsemestre
import sco_parcours_dut import sco_parcours_dut
import sco_codes_parcours import sco_codes_parcours
from scolars import format_nom, format_prenom, format_sexe, format_lycee from scolars import format_nom, format_prenom, format_sexe, format_lycee
from ZAbsences import getAbsSemEtud from sco_abs import getAbsSemEtud
def feuille_preparation_jury(context, formsemestre_id, REQUEST): def feuille_preparation_jury(context, formsemestre_id, REQUEST):

View File

@ -40,7 +40,7 @@ import tempfile
from notes_log import log from notes_log import log
from sco_utils import * from sco_utils import *
import ZAbsences import sco_abs
import scolars import scolars
import sco_photos import sco_photos
import sco_formsemestre import sco_formsemestre
@ -301,9 +301,9 @@ def pdf_feuille_releve_absences(
NB_CELL_PM = context.get_preference("feuille_releve_abs_PM") NB_CELL_PM = context.get_preference("feuille_releve_abs_PM")
COLWIDTH = 0.85 * cm COLWIDTH = 0.85 * cm
if context.get_preference("feuille_releve_abs_samedi"): if context.get_preference("feuille_releve_abs_samedi"):
days = ZAbsences.DAYNAMES[:6] # Lundi, ..., Samedi days = sco_abs.DAYNAMES[:6] # Lundi, ..., Samedi
else: else:
days = ZAbsences.DAYNAMES[:5] # Lundi, ..., Vendredi days = sco_abs.DAYNAMES[:5] # Lundi, ..., Vendredi
nb_days = len(days) nb_days = len(days)
# Informations sur les groupes à afficher: # Informations sur les groupes à afficher: