This file is indexed.

/usr/share/pyshared/schooltool/calendar/README.txt is in python-schooltool 1:2.1.0-0ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
SchoolTool calendaring library
==============================

schooltool.calendar is a calendaring library for Zope 3.


Features
--------

- It can parse and generate iCalendar files.  Only a subset of the iCalendar
  spec is supported, however it is a sensible subset that should be enough for
  interoperation with desktop calendaring applications like Apple's iCal,
  Mozilla Calendar, Evolution, and KOrganizer.

- It is storage independent -- your application could store the calendar in
  ZODB, in a relational database, or elsewhere, as long as the storage
  component provides the necessary interface.

- You can display several calendars in a single view by using calendar
  composition.

- It supports recurring events (daily, weekly, monthly and yearly).

Things that are not currently supported:

- Timezone handling (UTC times are converted into server's local time in the
  iCalendar parser, but that's all).

- All-day events (that is, events that only specify the date but not the time).

- Informing the user when uploaded iCalendar files use features that are not
  supported by SchoolTool.


Modules
-------

At the moment ``schooltool.calendar`` contains building blocks for calendaring
in your own application.

``schooltool.calendar.interfaces``
    defines interfaces for calendars and calendar events.

``schooltool.calendar.simple``
    defines simple calendar and calendar event classes.

    They are not tied into any particular storage system, so if you want to
    store your calendars in the ZODB or in a relational database, you will
    want to write your own.

    TODO: there should be a standard Persistent calendar class in this package.

``schooltool.calendar.mixins``
    defines mixins that make implementation of your own calendar and event classes easier.

``schooltool.calendar.icalendar``
    lets you parse and generate iCalendar (`RFC 2445`_) files.

``schooltool.calendar.browser``
    defines some browser views for calendars.

``schooltool.calendar.recurrent``
    defines some recurrence rules that let you describe events recurring daily,
    weekly, monthly or yearly.

``schooltool.calendar.utils``
    contains a number of small standalone utility functions for manipulating dates and times.


Calendars
---------

A calendar is a set of events.  A calendar event has a date and time, a
duration, a title, and a bunch of other (optional) attributes like location
or description.  Here's a sample calendar event:

    >>> from pytz import utc
    >>> from datetime import datetime, timedelta
    >>> from schooltool.calendar.simple import SimpleCalendarEvent
    >>> appointment = SimpleCalendarEvent(datetime(2004, 12, 28, 13, 40,
    ...                                            tzinfo=utc),
    ...                                   timedelta(hours=1),
    ...                                   'Dentist')

Calendar events are described by the ICalendarEvent interface.

    >>> from schooltool.calendar.interfaces import ICalendarEvent
    >>> ICalendarEvent.providedBy(appointment)
    True

Here's another calendar event.  It repeats every week:

    >>> from schooltool.calendar.recurrent import WeeklyRecurrenceRule
    >>> meeting = SimpleCalendarEvent(datetime(2005, 2, 7, 18, 0, tzinfo=utc),
    ...                               timedelta(hours=1),
    ...                               'IRC meeting',
    ...                               location='#schooltool',
    ...                               recurrence=WeeklyRecurrenceRule())

A calendar is a set of events.  Some calendars are read-only, while others
are editable.  Here's a simple read-only calendar that contains two events:

    >>> from schooltool.calendar.simple import ImmutableCalendar
    >>> calendar = ImmutableCalendar([meeting, appointment])
    >>> len(calendar)
    2

You can iterate over calendars to get all events in unspecified order.  You
can then sort the events by date.  Let us define a simple function for
listing calendar events sorted by date::

    >>> def print_cal(calendar):
    ...     events = list(calendar)
    ...     events.sort()
    ...     for event in events:
    ...         print event.dtstart.strftime('%Y-%m-%d'), event.title

    >>> print_cal(calendar)
    2004-12-28 Dentist
    2005-02-07 IRC meeting

Note that, although IRC meeting repeats weekly, it was printed only once.
If you want to see all occurrences of repeating calendar events, you can
call calendar.expand.  Since some events may repeat indefinitely, expand
takes two datetime arguments and limits returned events to the specified
datetime range.

    >>> print_cal(calendar.expand(datetime(2005, 2, 1, tzinfo=utc),
    ...                           datetime(2005, 3, 1, tzinfo=utc)))
    2005-02-07 IRC meeting
    2005-02-14 IRC meeting
    2005-02-21 IRC meeting
    2005-02-28 IRC meeting


Storage of calendars
--------------------

SchoolTool was designed to allow flexibility in calendar storage: calendars
may be stored in the ZODB, in a relational database, as iCalendar files on
disk, or computed on the fly from some other data source.

To achieve this, ``schooltool.calendar`` defines interfaces for calendars
(``ICalendar`` and ``IEditCalendar``) and calendar events (``ICalendarEvent``) and relies
on objects implementing those interfaces.

You can define your own calendar and calendar event classes.  There are
mixins (``CalendarMixin``, ``EditableCalendarMixin``, ``CalendarEventMixin``) defined
in ``schooltool.calendar.mixins`` that you can use (if you want to) to implement
some of calendar/calendar event operations.

There are some simple implementations of calendars and calendar events in
``schooltool.calendar.simple``: ``SimpleCalendarEvent`` and
``ImmutableCalendar``. They are particularly useful for calendars that are
generated on the fly. For example, suppose we have a list of deadlines for a
project:;

    >>> deadlines = [('2005-02-28', 'Feature freeze'),
    ...              ('2005-03-05', 'Release candidate 1'),
    ...              ('2005-03-15', 'Release')]

We can generate a calendar like this::

    >>> from schooltool.calendar.simple import ImmutableCalendar
    >>> from schooltool.calendar.simple import SimpleCalendarEvent
    >>> from schooltool.calendar.utils import parse_datetimetz
    >>> from datetime import timedelta

    >>> deadline_calendar = ImmutableCalendar([
    ...         SimpleCalendarEvent(parse_datetimetz(date + ' 00:00:00Z'),
    ...                             timedelta(hours=1),
    ...                             deadline)
    ...         for date, deadline in deadlines])

    >>> print_cal(deadline_calendar)
    2005-02-28 Feature freeze
    2005-03-05 Release candidate 1
    2005-03-15 Release

Note that every event will get a new randomly generated `unique_id` attribute.
If you want to publish a computed calendar as an iCalendar file, you might
want to generate deterministic unique IDs and explicitly pass them to
``SimpleCalendarEvent``'s constructor.


iCalendar
---------

iCalendar (defined in `RFC 2445`_) is a popular calendar representation
format.

.. _`RFC 2445`: http://www.ietf.org/rfc/rfc2445.txt

There is a sample iCalendar file (created with Evolution) in
``schooltool.calendar.tests``

    >>> import os, schooltool.calendar
    >>> filename = os.path.join(os.path.dirname(schooltool.calendar.__file__),
    ...                         'tests', 'sample.ics')

You can read an iCalendar file event by event:

    >>> from schooltool.calendar.icalendar import read_icalendar
    >>> print_cal(read_icalendar(open(filename)))
    2005-02-09 SchoollTool 0.9 release
    2005-02-09 SchoolTool release party!
    2005-02-14 #schooltool meeting

Note that read_icalendar returns an iterator, and not a calendar.  If you want
a calendar object, you can create one as follows:

    >>> from schooltool.calendar.simple import ImmutableCalendar
    >>> sample_calendar = ImmutableCalendar(read_icalendar(open(filename)))
    >>> len(sample_calendar)
    3

You can create an iCalendar file from a SchoolTool calendar.

    >>> from schooltool.calendar.icalendar import convert_calendar_to_ical
    >>> lines = convert_calendar_to_ical(sample_calendar)
    >>> print "\n".join(lines)                          # doctest: +ELLIPSIS
    BEGIN:VCALENDAR
    VERSION:2.0
    PRODID:-//SchoolTool.org/NONSGML SchoolTool//EN
    BEGIN:VEVENT
    UID:20050211T140836Z-19135-1013-8968-3@muskatas
    ...
    DTSTART:20050209T183000Z
    DURATION:PT3H
    DTSTAMP:...
    END:VEVENT
    END:VCALENDAR

iCalendar spec mandates lines (including the last one) are terminated by CR LF
characters, so you should use something like

    >>> output_as_string = "\r\n".join(lines + [''])

TODO: this is inconvenient.  ``"".join(lines)`` should return a valid iCalendar
stream.  ``fileobject.writelines(lines)`` should just work.  Although there
are other complications with automatic LF -> CRLF transformations when
file objects are opened in text mode.

iCalendar is a large specification, and ``schooltool.calendar`` supports only a
subset of it.  This subset should be enough to interoperate with most open
source calendaring software, but you should keep in mind that reading an
iCalendar file into SchoolTool objects and writing it back is a lossy
transformation.


Calendar composition
--------------------

It is often useful to display several calendars at the same time.  Rather
than iterating over a number of calendars, you may want to construct a single
calendar that contains all the events from those other calendars:

    >>> from schooltool.calendar.simple import combine_calendars
    >>> cal = combine_calendars(deadline_calendar, sample_calendar)
    >>> len(cal) == len(deadline_calendar) + len(sample_calendar)
    True
    >>> print_cal(cal)
    2005-02-09 SchoollTool 0.9 release
    2005-02-09 SchoolTool release party!
    2005-02-14 #schooltool meeting
    2005-02-28 Feature freeze
    2005-03-05 Release candidate 1
    2005-03-15 Release

TODO: when we display a combined calendar, we often want to know where each
event came from.


Recurring events
----------------

Recurring events are calendar events with a defined recurrence rule.  An
example of a recurrence rule that says "this event repeats 5 times on every
third day" is

    >>> from schooltool.calendar.recurrent import DailyRecurrenceRule
    >>> rule = DailyRecurrenceRule(count=5, interval=3)

Currently there are four kinds of recurrence rules defined in
``schooltool.calendar.recurrent``:

- daily recurrences (e.g. "every second day")
- weekly recurrences (e.g. "every week on Monday through Friday")
- monthly recurrences (e.g. "every second Tuesday of a month" or
  "15th of every second month")
- yearly (e.g. "January 29th every year")

All recurrence rules share some common attributes:

`interval`
  to express "every third week" you would create a ``WeeklyRecurrenceRule``
  with an interval of 3.  An interval of 1 means simply "every".
  Intervals lower than 1 are not allowed.

`count` and `until`
  you can limit the number of occurrences by specifying
  an explicit count (e.g. "every second year during the next 10 years" would
  be expressed as ``YearlyRecurrenceRule(interval=2, count=5)``), or by
  specifying the date of the last occurrence (e.g. "every third day until
  August 15th" would be expressed as ``DailyRecurrenceRule(interval=3,
  until=datetime.date(2005, 8, 15))``).  If neither `count` not `until` are
  specified, the rule repeats forever.

`exceptions`
  you can say "repeat every Monday except on February 21" as
  ``WeeklyRecurrenceRule(weekdays=calendar.MONDAY, exceptions=[date(2005, 2, 21)])``.

Weekly recurrence rules let you specify a set of weekdays that the event
occurs on.  E.g. "every second weekend" can be expressed as
``WeeklyRecurrenceRule(interval=2, weekdays=[calendar.SATURDAY, calendar.SUNDAY])``.

Monthly recurrence rules also let you choose one of three variants:

- same day of month (e.g. an event that occurrs on January 29 and has
  a ``MonthlyRecurrenceRule(monthly='monthday')`` will recur on the 29th of
  every month (except Februrary on non-leap years).

- same day of week (e.g. "2nd Tuesday of a month" can be expressed as
  a ``MonthlyRecurrenceRule(monthly='weekday')``, if the recurrence rule is
  assigned to an event that happens on the 2nd Tuesday of some month.

- same day of week, but counting from the end of the month, e.g. "2nd last
  Wednesday of a month" -- ``MonthlyRecurrenceRule(monthly='lastweekday')``.

Here's how you create recurring events:

    >>> from schooltool.calendar.recurrent import YearlyRecurrenceRule
    >>> event = SimpleCalendarEvent(datetime(2005, 1, 29, 12, tzinfo=utc),
    ...                             timedelta(hours=1),
    ...                             'My birthday',
    ...                             recurrence=YearlyRecurrenceRule())

Here's how you can get all recurrence dates of an event:

    >>> iterator = event.recurrence.apply(event)
    >>> iterator.next()
    datetime.date(2005, 1, 29)
    >>> iterator.next()
    datetime.date(2006, 1, 29)
    >>> iterator.next()
    datetime.date(2007, 1, 29)

Usually you will use ``ICalendar.expand``.

    >>> cal = ImmutableCalendar([event])
    >>> print_cal(cal.expand(datetime(2003, 1, 1, tzinfo=utc),
    ...                      datetime(2009, 1, 1, tzinfo=utc)))
    2005-01-29 My birthday
    2006-01-29 My birthday
    2007-01-29 My birthday
    2008-01-29 My birthday

Sometimes a recurrence rule may exclude even the original occurrence:

    >>> from datetime import date
    >>> empty_event = SimpleCalendarEvent(datetime(2005, 2, 3, 12, tzinfo=utc),
    ...                     timedelta(hours=1), 'Copious free time',
    ...                     recurrence=YearlyRecurrenceRule(
    ...                                     until=date(2005, 1, 1)))
    >>> list(empty_event.recurrence.apply(event))
    []

You can check if an even is "empty" by calling ``event.hasOccurrences()``:

    >>> event.hasOccurrences()
    True
    >>> empty_event.hasOccurrences()
    False


Utilities
---------

``schooltool.calendar.utils`` contains a number of small standalone functions
for parsing and manipulating dates::

    >>> from schooltool.calendar.utils import parse_date, parse_datetime
    >>> from schooltool.calendar.utils import parse_datetimetz
    >>> parse_date('2004-02-11')
    datetime.date(2004, 2, 11)
    >>> parse_datetime('2004-02-11 15:35:44')
    datetime.datetime(2004, 2, 11, 15, 35, 44)
    >>> parse_datetimetz('2004-02-11 15:35:44Z')
    datetime.datetime(2004, 2, 11, 15, 35, 44, tzinfo=<UTC>)

    >>> from datetime import date
    >>> from schooltool.calendar.utils import prev_month, next_month
    >>> prev_month(date(2004, 2, 11))
    datetime.date(2004, 1, 1)
    >>> next_month(date(2004, 2, 11))
    datetime.date(2004, 3, 1)

    >>> from calendar import SUNDAY
    >>> from schooltool.calendar.utils import week_start
    >>> week_start(date(2004, 2, 11)).strftime('%a, %b %d %Y')
    'Mon, Feb 09 2004'
    >>> week_start(date(2004, 2, 11), SUNDAY).strftime('%a, %b %d %Y')
    'Sun, Feb 08 2004'

    >>> from schooltool.calendar.utils import weeknum_bounds
    >>> s, e = [d.strftime('%a, %b %d') for d in weeknum_bounds(2005, 1)]
    >>> print 'Week 1 of 2005 started on %s and ended on %s.' % (s, e)
    Week 1 of 2005 started on Mon, Jan 03 and ended on Sun, Jan 09.

    >>> from schooltool.calendar.utils import check_weeknum
    >>> if check_weeknum(2004, 53):
    ...     print "There was a 53-th week in 2004"
    There was a 53-th week in 2004
    >>> if not check_weeknum(2005, 53):
    ...     print "There is no week 53 in 2005"
    There is no week 53 in 2005