forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -10,7 +10,7 @@
|
||||
|
||||
import datetime
|
||||
|
||||
from flask import flash, g, request, url_for
|
||||
from flask import g, request, url_for
|
||||
from flask_json import as_json
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
|
511
app/scodoc/sco_cal.py
Normal file
511
app/scodoc/sco_cal.py
Normal file
@ -0,0 +1,511 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2023 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
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Génération calendrier (ancienne présentation)
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
import html
|
||||
import time
|
||||
|
||||
from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidDateError
|
||||
from app.scodoc import sco_preferences
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
|
||||
def is_work_saturday():
|
||||
"Vrai si le samedi est travaillé"
|
||||
return int(sco_preferences.get_preference("work_saturday"))
|
||||
|
||||
|
||||
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(object):
|
||||
"""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 = date.split("/")
|
||||
elif fmt == "iso":
|
||||
self.year, self.month, self.day = date.split("-")
|
||||
else:
|
||||
raise ValueError("invalid format spec. (%s)" % fmt)
|
||||
self.year = int(self.year)
|
||||
self.month = int(self.month)
|
||||
self.day = int(self.day)
|
||||
except ValueError:
|
||||
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_day(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_day((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): # #py3 TODO à supprimer
|
||||
"""return a negative integer if self < other,
|
||||
zero if self == other, a positive integer if self > other"""
|
||||
return int(self.time - other.time)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.time == other.time
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.time != other.time
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.time < other.time
|
||||
|
||||
def __le__(self, other):
|
||||
return self.time <= other.time
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.time > other.time
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.time >= other.time
|
||||
|
||||
def __hash__(self):
|
||||
"we are immutable !"
|
||||
return hash(self.time) ^ hash(str(self))
|
||||
|
||||
|
||||
# d = ddmmyyyy( '21/12/99' )
|
||||
def DateRangeISO(date_beg, date_end, workable=1):
|
||||
"""returns list of dates in [date_beg,date_end]
|
||||
workable = 1 => keeps only workable days"""
|
||||
if not date_beg:
|
||||
raise ScoValueError("pas de date spécifiée !")
|
||||
if not date_end:
|
||||
date_end = date_beg
|
||||
r = []
|
||||
work_saturday = is_work_saturday()
|
||||
try:
|
||||
cur = ddmmyyyy(date_beg, work_saturday=work_saturday)
|
||||
end = ddmmyyyy(date_end, work_saturday=work_saturday)
|
||||
except (AttributeError, ValueError) as e:
|
||||
raise ScoValueError("date invalide !") from e
|
||||
while cur <= end:
|
||||
if (not workable) or cur.iswork():
|
||||
r.append(cur)
|
||||
cur = cur.next_day()
|
||||
|
||||
return [x.ISO() for x in r]
|
||||
|
||||
|
||||
def day_names():
|
||||
"""Returns week day names.
|
||||
If work_saturday property is set, include saturday
|
||||
"""
|
||||
if is_work_saturday():
|
||||
return scu.DAY_NAMES[:-1]
|
||||
else:
|
||||
return scu.DAY_NAMES[:-2]
|
||||
|
||||
|
||||
def next_iso_day(date):
|
||||
"return date after date"
|
||||
d = ddmmyyyy(date, fmt="iso", work_saturday=is_work_saturday())
|
||||
return d.next_day().ISO()
|
||||
|
||||
|
||||
def YearTable(
|
||||
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,
|
||||
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 "\n".join(T)
|
||||
|
||||
|
||||
# ------ HTML Calendar functions (see YearTable function)
|
||||
|
||||
# MONTH/DAY NAMES:
|
||||
|
||||
MONTHNAMES = (
|
||||
"Janvier",
|
||||
"Février",
|
||||
"Mars",
|
||||
"Avril",
|
||||
"Mai",
|
||||
"Juin",
|
||||
"Juillet",
|
||||
"Aout",
|
||||
"Septembre",
|
||||
"Octobre",
|
||||
"Novembre",
|
||||
"Décembre",
|
||||
)
|
||||
|
||||
MONTHNAMES_ABREV = (
|
||||
"Jan.",
|
||||
"Fév.",
|
||||
"Mars",
|
||||
"Avr.",
|
||||
"Mai ",
|
||||
"Juin",
|
||||
"Juil",
|
||||
"Aout",
|
||||
"Sept",
|
||||
"Oct.",
|
||||
"Nov.",
|
||||
"Dé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 is not 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"' % html.escape(descr, quote=True)
|
||||
if href or descr:
|
||||
cc.append("<a %s %s>" % (href, descr))
|
||||
|
||||
if legend or d == 1:
|
||||
if pad_width is not None:
|
||||
n = pad_width - len(legend) # pad to 8 cars
|
||||
if n > 0:
|
||||
legend = (
|
||||
" " * (n // 2) + legend + " " * ((n + 1) // 2)
|
||||
)
|
||||
else:
|
||||
legend = " " # empty cell
|
||||
cc.append(legend)
|
||||
if href or descr:
|
||||
cc.append("</a>")
|
||||
cc.append("</td>")
|
||||
cell = "".join(cc)
|
||||
if day == "D":
|
||||
monday = monday.next_day(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_day(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 (True, False):
|
||||
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] is not 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 is not 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"' % html.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 = (
|
||||
" " * (n // 2) + legend + " " * ((n + 1) // 2)
|
||||
)
|
||||
else:
|
||||
legend = " " # empty cell
|
||||
cc.append(legend)
|
||||
if href or descr:
|
||||
cc.append("</a>")
|
||||
cc.append("</td>\n")
|
||||
T.append("".join(cc) + "</tr>")
|
||||
return "\n".join(T)
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.11"
|
||||
SCOVERSION = "9.6.12"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user