Attachment 'Calendar-20060216.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 This is improved the old Calendar processor.
4
5 @copyright: 2005 by Karel Zak <kzak@redhat.com>
6 @license: GNU GPL, see COPYING for details.
7
8 @version: 20060216
9 """
10
11 import calendar
12 import time
13 import locale
14
15 Dependencies = []
16 EnglishDayText = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
17
18 class CalendarOptions:
19 def __init__(self, items):
20 self.options = {}
21 self.defaults = {}
22
23 # name of first week day is independent on locales
24 self.defaults['firstWeekDay'] = {
25 'VALUE': 'Sunday'
26 }
27 self.defaults['headerMonth'] = {
28 'VALUE': 'enable',
29 'STYLE': 'text-align: center; font-weight: bold; background-color: #fcfdc1; font-size: 150%'
30 }
31 self.defaults['headerDayName'] = {
32 'VALUE': 'enable',
33 'STYLE': 'text-align: center; font-weight: bold; background-color: #fff0ac; width: 10em'
34 }
35 self.defaults['headerWeekendName'] = {
36 'VALUE': 'enable',
37 'STYLE': 'text-align: center; font-weight: bold; background-color: #d6e0b7; width: 10em'
38 }
39 self.defaults['headerDay'] = {
40 'STYLE': 'text-align: center; font-weight: bold; background-color: #fff7d2; border-bottom: white 0px none'
41 }
42 self.defaults['eventDay'] = {
43 'STYLE': 'border-top: white 0px none;'
44 }
45 self.defaults['headerWeekend'] = {
46 'VALUE': 'enable',
47 'STYLE': 'text-align: center; font-weight: bold; background-color: #e5ead6; border-bottom: white 0px none'
48 }
49 self.defaults['headerToday'] = {
50 'VALUE': 'enable',
51 'STYLE': 'border: #a6a49a 1px solid; background-color: #d3f6d1'
52 }
53 self.defaults['calendarsOrder'] = {
54 'VALUE': 'normal'
55 }
56 self.defaults['calendarCollapse'] = {
57 'VALUE': 'disable'
58 }
59
60 self.mergeOptionsAndDefaults(items)
61
62 def mergeOptionsAndDefaults(self, items):
63 if len(items) <= 0:
64 self.options = self.defaults
65 return
66 for k, defaults in self.defaults.iteritems():
67 if not items.has_key(k):
68 self.options[k] = defaults
69 else:
70 options = items[k]
71 for key, val in defaults.iteritems():
72 if not options.has_key(key):
73 options[key] = val
74 self.options[k] = options
75
76 def get(self, name):
77 if len(self.options) > 0:
78 if self.options.has_key(name):
79 return self.options[name]
80 return None
81
82 def getBool(self, name):
83 val = self.getValue(name)
84 if val and len(val):
85 if val.lower() == 'true' or val.lower() == 'enable':
86 return 1
87 return 0
88
89 def getItem(self, name, key):
90 items = self.get(name)
91 if items and items.has_key(key):
92 return items[key]
93 return None
94
95 def getValue(self, name):
96 return self.getItem(name, 'VALUE')
97
98 def getStyle(self, name):
99 return self.getItem(name, 'STYLE')
100
101 class EventCalendar:
102 def __init__(self, y, m, options, y_today, m_today, d_today):
103 self.y = y
104 self.m = m
105 self.days = {}
106 self.options = options
107 self.debug = []
108 self.y_today = y_today
109 self.m_today = m_today
110 self.d_today = d_today
111 self.footnotes = []
112
113 val = self.getValue('firstWeekDay')
114 start = self.dayToNumber(val)
115 if start <= 5:
116 self.weekend = [5-start, 6-start]
117 else:
118 self.weekend = [0,6]
119
120 calendar.setfirstweekday(start)
121 lastDay = calendar.monthrange(y, m)[1]
122 for i in xrange(1,lastDay+1):
123 self.days[i] = []
124
125 def isToday(self, d):
126 if self.y == self.y_today and self.m == self.m_today and d == self.d_today:
127 return 1
128 return 0
129
130 def getValue(self, name):
131 return self.options.getValue(name)
132
133 def getStyle(self, name):
134 return self.options.getStyle(name)
135
136 def addEvent(self,d,event):
137 self.days[d].append(event)
138
139 def dayToNumber(self, day):
140 for i in xrange(0, 7):
141 if EnglishDayText[i].lower()==day.lower():
142 return i
143 return None
144
145 def getDayName(self, idx):
146 lc = [locale.DAY_2, locale.DAY_3, locale.DAY_4, locale.DAY_5, locale.DAY_6, locale.DAY_7, locale.DAY_1]
147 return locale.nl_langinfo(lc[idx])
148
149 def getMonthName(self, idx):
150 lc = [locale.MON_1, locale.MON_2, locale.MON_3, locale.MON_4, locale.MON_5, locale.MON_6,
151 locale.MON_7, locale.MON_8, locale.MON_9, locale.MON_10,locale.MON_11,locale.MON_12]
152 return locale.nl_langinfo(lc[idx])
153
154 def format(self, req, fmt):
155
156 id = '%s_%s' % (self.y, self.m)
157 if self.y==self.y_today and self.m==self.m_today:
158 display = 'block'
159 else:
160 display = 'none'
161
162 if self.options.getBool('calendarCollapse'):
163 req.write('''<p onclick="showCalendar('%s')" style="color: blue; cursor: pointer; cursor: hand; margin-bottom: 0px">''' % id)
164 req.write(fmt.text("%d / %s" % (self.y, self.getMonthName(self.m-1))))
165 req.write('</p>\n')
166 req.write('<div id="%s" style="display: %s">\n' % (id, display))
167
168 req.write('<table style="background-color: transparent">\n')
169
170 # Year / Month
171 if self.options.getBool('headerMonth')==1:
172 req.write(fmt.table_row(1))
173 req.write(fmt.table_cell(1, {'colspan': '7', 'style': self.getStyle('headerMonth') }))
174 req.write(fmt.text("%d / %s" % (self.y, self.getMonthName(self.m-1))))
175 req.write(fmt.table_cell(0))
176 req.write(fmt.table_row(0))
177
178 # Names of Days
179 if self.options.getBool('headerDayName')==1:
180 req.write(fmt.table_row(1))
181 for i in range(calendar.firstweekday(), calendar.firstweekday()+7):
182 d = i%7
183 if self.options.getBool('headerWeekendName') and d in [5,6]:
184 req.write(fmt.table_cell(1, {'style': self.getStyle('headerWeekendName') }))
185 else:
186 req.write(fmt.table_cell(1, {'style': self.getStyle('headerDayName') }))
187 req.write(fmt.text(self.getDayName(d)))
188 req.write(fmt.table_cell(0))
189 req.write(fmt.table_row(0))
190
191 calList = calendar.monthcalendar(self.y, self.m)
192 weekRows = len(calList)
193
194 for week in calList:
195 # date
196 req.write(fmt.table_row(1))
197 xd = 0
198 for day in week:
199 if self.options.getBool('headerWeekend') and xd in self.weekend:
200 req.write(fmt.table_cell(1, {'style': self.getStyle('headerWeekend') }))
201 else:
202 req.write(fmt.table_cell(1, {'style': self.getStyle('headerDay') }))
203 if day > 0:
204 req.write('<div id="%d_%d_%d">' % (self.y,self.m,day))
205 req.write(fmt.text("-%d-" % day))
206 req.write('</div>')
207 req.write(fmt.table_cell(0))
208 xd += 1
209 req.write(fmt.table_row(0))
210 # event
211 req.write(fmt.table_row(1))
212 for day in week:
213 req.write(fmt.table_cell(1, {'style': self.getStyle('eventDay'), 'valign': 'top' }))
214 if day > 0:
215 for event in self.days[day]:
216 if event.has_key('TEXT'):
217 if event.has_key('STYLE'):
218 req.write('<div style="%s">' % event['STYLE'])
219 else:
220 req.write('<div>')
221 req.write(fmt.text(event['TEXT']))
222 if event.has_key('FOOTNOTE'):
223 dt = '%d-%d-%d' % (self.y,self.m,day)
224 self.footnotes.append((dt, event['FOOTNOTE']))
225 req.write(fmt.anchorlink(1, name=dt))
226 req.write(fmt.text(' (%d)' % len(self.footnotes)))
227 req.write(fmt.anchorlink(0))
228 req.write('</div>')
229 req.write(fmt.table_cell(0))
230 req.write(fmt.table_row(0))
231
232 req.write('</table>\n');
233
234 if len(self.footnotes):
235 req.write(fmt.strong(1))
236 req.write(fmt.text('Footnotes:'))
237 req.write(fmt.strong(0))
238 req.write(fmt.number_list(1))
239 for d,f in self.footnotes:
240 req.write(fmt.listitem(1))
241 req.write(fmt.anchordef(d))
242 req.write(fmt.strong(1))
243 req.write(fmt.text(d+': '))
244 req.write(fmt.strong(0))
245 req.write(fmt.text(f))
246 req.write(fmt.listitem(0))
247 req.write(fmt.number_list(0))
248
249 if len(self.debug) > 0:
250 req.write('DEBUG: %s' % str(self.debug))
251
252 if self.options.getBool('calendarCollapse'):
253 req.write('</div>\n');
254
255 class Parser:
256 """ Forma calendar as a table
257 """
258 caching = 0
259 Dependencies = []
260
261 def __init__(self, raw, request, **kw):
262 """ Store the source text.
263 """
264 self.raw = raw
265 self.request = request
266 self.form = request.form
267 self._ = request.getText
268 self.events = {}
269 self.optionsItems = {}
270 self.options = None
271 self.calendars = []
272 self.debug = []
273
274 def stringNormalization(self, text):
275 text = text.strip()
276 data = ''
277 blnk = 0
278 for p in text:
279 if p==' ' or p=='\t':
280 if blnk==1:
281 continue
282 blnk=1
283 blnk = 0
284 data += p
285 text = data.replace(' = "', '="').replace('=" ', '="').replace(' ="', '="')
286 #self.debug.append('TEXT: %s<br>' % text)
287 return text
288
289 def stringToItems(self, text):
290 items = {}
291 type = None
292 text = self.stringNormalization(text)
293 if len(text)==0:
294 return None, None
295 chunks = text.split('" ')
296 for chunk in chunks:
297 chunk = chunk.strip()
298 x = chunk.split('=')
299 if len(x)!=2:
300 continue
301 name, value = x
302 value = value.strip()
303 name = name.strip()
304 if value and len(value)>0:
305 if value[0] == '"':
306 value = value[1:]
307 if value[-1] == '"':
308 value = value[:-1]
309 items[name] = value
310 if type==None:
311 type = name
312 return type, items
313
314 def parseDate(self, text):
315 if text==None or len(text) < 3:
316 return None, None, None
317 x = text.strip().split('-')
318 if len(x) < 3:
319 return None, None, None
320 return int(x[0]), int(x[1]), int(x[2])
321
322 def appendEvent(self, y, m, d, items):
323 if items and len(items) > 0:
324 if not self.events.has_key(y):
325 self.events[y] = { }
326 if not self.events[y].has_key(m):
327 self.events[y][m] = { }
328 if not self.events[y][m].has_key(d):
329 self.events[y][m][d] = [ ]
330 self.events[y][m][d].append(items)
331
332 def parseEvent(self, linetype, items):
333 if linetype==None or len(linetype) == 0:
334 return
335 from_y, from_m, from_d = self.parseDate(linetype)
336 if from_y==None:
337 return
338
339 # store the fisrt variable as two separate variables
340 items['TEXT'] = items[linetype]
341 items['FROM'] = linetype
342 del items[linetype]
343
344 if items.has_key('TO'):
345 # range
346 to_y, to_m, to_d = self.parseDate(items['TO'])
347 if to_y==None:
348 return
349
350 for y in xrange(from_y, to_y+1):
351 max_m = to_m
352 min_m = from_m
353 if y < to_y:
354 max_m = 12
355 if y > from_y:
356 min_m = 1
357 for m in xrange(min_m, max_m+1):
358 max_d = to_d
359 min_d = from_d
360 if y < to_y or m < max_m:
361 max_d = calendar.monthrange(y, m)[1]
362 if y > from_y or m > from_m:
363 min_d = 1
364 for d in xrange(min_d, max_d+1):
365 self.appendEvent(y, m, d, items)
366 else:
367 # simple event
368 self.appendEvent(from_y, from_m, from_d, items)
369
370 def parse(self, raw):
371 """ Parse to events or options
372 """
373 if raw==None or len(raw)==0:
374 return
375
376 lines = raw.split('\n')
377
378 for line in lines:
379 text = line.strip()
380 if len(text) == 0:
381 continue
382 linetype, items = self.stringToItems(text)
383 if items==None:
384 continue
385 if linetype == 'OPTION':
386 name = items['OPTION']
387 self.optionsItems[name] = items
388 else:
389 self.parseEvent(linetype, items)
390
391 def calendarCreate(self):
392 y_today = int( time.strftime("%Y"))
393 m_today = int( time.strftime("%m"))
394 d_today = int( time.strftime("%d"))
395
396 self.options = CalendarOptions(self.optionsItems);
397
398 for y in self.events.keys():
399 for m in self.events[y].keys():
400 cal = EventCalendar(y, m, self.options, y_today, m_today, d_today)
401 for d in self.events[y][m].keys( ):
402 for event in self.events[y][m][d]:
403 cal.addEvent(d,event)
404 self.calendars.append((y,m,cal))
405
406 self.calendars.sort()
407
408 if self.options.getValue('calendarsOrder')=='reverse':
409 self.calendars.reverse()
410
411 def format(self, formatter):
412
413 self.parse(self.raw)
414 self.calendarCreate()
415
416 if len(self.calendars)==0:
417 return
418
419 self.request.write('''
420 <script language="javascript" type="text/javascript">
421 function showCalendar(id)
422 {
423 el=document.getElementById(id).style;
424 el.display=(el.display == 'block')?'none':'block';
425 }
426 </script>\n''')
427
428 for y,m,cal in self.calendars:
429 cal.format(self.request, formatter)
430
431 if len(self.debug) > 0:
432 self.request.write('DEBUG: %s' % self.debug)
433
434 # vim: set tabstop=4:
435 # vim: set shiftwidth=4:
436 # vim: set expandtab:
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.