Пример #1
0
    def test_scroll(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule(interval=3)
        ev = SimpleCalendarEvent(datetime(2004, 10, 13, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # start before event
        self.assertEqual(rule._scroll(ev, date(2004, 1, 1)),
                         (0, date(2004, 10, 13)))

        # start on event date
        self.assertEqual(rule._scroll(ev, date(2004, 10, 13)),
                         (0, date(2004, 10, 13)))

        # start after date
        self.assertEqual(rule._scroll(ev, date(2005, 3, 14)),
                         (1, date(2005, 1, 13)))

        # If we ask for an illegal dates, we get less events
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(2004, 1, 30, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')
        self.assertEqual(rule._scroll(ev, date(2004, 1, 29)),
                         (0, date(2004, 1, 30)))
        self.assertEqual(rule._scroll(ev, date(2004, 2, 28)),
                         (0, date(2004, 1, 30)))
        # sequence nr 1 is taken by illegal date 2004-02-30
        self.assertEqual(rule._scroll(ev, date(2004, 3, 1)),
                         (2, date(2004, 3, 30)))
Пример #2
0
 def __init__(self, *args, **kwargs):
     resources = kwargs.pop("resources", ())
     SimpleCalendarEvent.__init__(self, *args, **kwargs)
     self.__name__ = base64.encodestring(self.unique_id.encode("utf-8")).replace("\n", "")
     self._resources = ()
     for resource in resources:
         self.bookResource(resource)
Пример #3
0
 def __init__(self, *args, **kwargs):
     resources = kwargs.pop('resources', ())
     SimpleCalendarEvent.__init__(self, *args, **kwargs)
     self.__name__ = base64.encodestring(self.unique_id.encode('utf-8')).replace('\n', '')
     self._resources = ()
     for resource in resources:
         self.bookResource(resource)
Пример #4
0
 def test_scroll(self):
     from schooltool.calendar.simple import SimpleCalendarEvent
     rule = self.createRule()
     ev = SimpleCalendarEvent(datetime(1978, 5, 17, 12, 0),
                              timedelta(minutes=10),
                              "reality check",
                              unique_id='uid')
     self.assertEqual(rule._scroll(ev, date(1900, 3, 1)),
                      (0, date(1978, 5, 17)))
     self.assertEqual(rule._scroll(ev, date(2000, 3, 1)),
                      (22, date(2000, 5, 17)))
Пример #5
0
def read_icalendar(icalendar_text, charset='UTF-8', fallback_tz=pytz.utc):
    """Read an iCalendar file and return calendar events.

    Returns an iterator over calendar events.

    `icalendar_text` can be a file object or a string.  It is assumed that
    the iCalendar file contains UTF-8 text.

    `fallback_tz` is used for timestamps when the timezone is not specified
    or not recognized.

    Unsupported features of the iCalendar file (e.g. VTODO components, complex
    recurrence rules, unknown properties) are silently ignored.
    """

    if not hasattr(icalendar_text, 'read'):
        # It is not a file-like object -- let's assume it is a string
        icalendar_text = StringIO(icalendar_text)

    rows = RowParser.parse(icalendar_text, charset)
    vccol = VCalendarCollection.parse(rows)

    for calendar in vccol.vcalendars:
        for vevent in calendar.events:
            if vevent.uid == EMPTY_CALENDAR_PLACEHOLDER:
                continue # Ignore empty calendar placeholder "event"

            dtstart = vevent.dtstart
            if not isinstance(dtstart, datetime.datetime):
                dtstart = datetime.datetime.combine(dtstart,
                                                    datetime.time(0))
            # XXX regression test for events with a date as dtend
            dtend = vevent.dtend
            if not isinstance(dtend, datetime.datetime):
                dtend = datetime.datetime.combine(dtend,
                                                  datetime.time(0))

            if dtstart.tzinfo is None:
                dtstart_tz = calendar.timezones.get(vevent.dtstart_tzid, fallback_tz)
                dtstart = dtstart_tz.localize(dtstart).astimezone(pytz.utc)
            if dtend.tzinfo is None:
                dtend_tz = calendar.timezones.get(vevent.dtend_tzid, fallback_tz)
                dtend = dtend_tz.localize(dtend).astimezone(pytz.utc)
            duration = dtend - dtstart

            yield SimpleCalendarEvent(dtstart, duration,
                                      vevent.summary or '',
                                      location=vevent.location,
                                      description=vevent.description,
                                      unique_id=vevent.uid,
                                      recurrence=vevent.rrule,
                                      allday=vevent.all_day_event)
Пример #6
0
 def test_scroll_feb29(self):
     from schooltool.calendar.simple import SimpleCalendarEvent
     rule = self.createRule()
     ev = SimpleCalendarEvent(datetime(1996, 2, 29, 12, 0),
                              timedelta(minutes=10),
                              "once in 4 years",
                              unique_id='uid')
     self.assertEqual(rule._scroll(ev, date(1900, 3, 1)),
                      (0, date(1996, 2, 29)))
     self.assertEqual(rule._scroll(ev, date(1999, 3, 1)),
                      (0, date(1996, 2, 29)))
     self.assertEqual(rule._scroll(ev, date(2000, 3, 1)),
                      (4, date(2000, 2, 29)))
     self.assertEqual(rule._scroll(ev, date(2004, 3, 1)),
                      (8, date(2004, 2, 29)))
Пример #7
0
    def test_apply_weekday_edge_case(self):
        # A test for Issue381 (monthly repeat causes bad date)
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule(monthly="weekday")
        # 1st Tuesday and 7th day of the week
        ev = SimpleCalendarEvent(datetime(2006, 2, 7, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        result = list(rule.apply(ev, enddate=date(2006, 5, 10)))
        expected = [
            date(2006, 2, 7),
            date(2006, 3, 7),
            date(2006, 4, 4),
            date(2006, 5, 2)
        ]
        self.assertEqual(result, expected)
Пример #8
0
    def test_scroll(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(2004, 10, 13, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # if the start date is before the event,
        # return the original event date.
        self.assertEqual(rule._scroll(ev, date(2004, 1, 1)),
                         (0, date(2004, 10, 13)))

        # if the start date coincides with the event, date
        self.assertEqual(rule._scroll(ev, date(2004, 10, 13)),
                         (0, date(2004, 10, 13)))

        # if the start date is later than the event date
        self.assertEqual(rule._scroll(ev, date(2004, 10, 23)),
                         (10, date(2004, 10, 23)))

        rule = self.createRule(interval=3)

        # if the start date is before the event,
        # return the original event date.
        self.assertEqual(rule._scroll(ev, date(2004, 1, 1)),
                         (0, date(2004, 10, 13)))

        # if the start date coincides with the event, date
        self.assertEqual(rule._scroll(ev, date(2004, 10, 13)),
                         (0, date(2004, 10, 13)))

        # if the start date is later than the event date
        self.assertEqual(rule._scroll(ev, date(2004, 10, 23)),
                         (3, date(2004, 10, 22)))

        self.assertEqual(rule._scroll(ev, date(2004, 10, 22)),
                         (3, date(2004, 10, 22)))

        self.assertEqual(rule._scroll(ev, date(2004, 10, 21)),
                         (2, date(2004, 10, 19)))
Пример #9
0
    def test_scroll(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(1978, 5, 17, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        self.assertEqual(rule._scroll(ev, date(1978, 5, 25)),
                         (1, date(1978, 5, 24)))
        self.assertEqual(rule._scroll(ev, date(1978, 6, 2)),
                         (1, date(1978, 5, 24)))

        # tricky case!
        # --  Tu We
        # -- -- --
        # Mo Tu We
        # -- -- --
        # Mo Tu We
        # ...

        rule = self.createRule(weekdays=(
            0,
            3,
        ), interval=2)

        # We don't care that we get the closest event.  What we care
        # about is that the sequence of events is correct:
        goodresults = [(0, date(1978, 5, 17)), (1, date(1978, 5, 18)),
                       (2, date(1978, 5, 29)), (3, date(1978, 5, 31)),
                       (4, date(1978, 6, 1)), (5, date(1978, 6, 5)),
                       (6, date(1978, 6, 7)), (7, date(1978, 6, 8))]

        for delta in range(-10, 40):
            d = date(1978, 5, 17) + date.resolution * delta
            self.assert_(rule._scroll(ev, d) in goodresults, d)

        # Still, it does not always return the zeroth result:
        self.assertEqual(rule._scroll(ev, date(1978, 6, 29)),
                         (6, date(1978, 6, 14)))
Пример #10
0
    def test_apply_endofmonth(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule(monthly="monthday")
        ev = SimpleCalendarEvent(datetime(2001, 1, 31, 0, 0),
                                 timedelta(minutes=10),
                                 "End of month",
                                 unique_id="uid")

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(2001, 12, 31)))
        self.assertEqual(len(result), 7)

        rule = self.createRule(monthly="monthday", count=7)
        result = list(rule.apply(ev, enddate=date(2001, 12, 31)))
        self.assertEqual(len(result), 7)
        self.assertEqual(result[-1], date(2001, 12, 31))

        rule = self.createRule(monthly="monthday", interval=2)
        result = list(rule.apply(ev, enddate=date(2002, 1, 31)))
        self.assertEqual(result, [
            date(2001, 1, 31),
            date(2001, 3, 31),
            date(2001, 5, 31),
            date(2001, 7, 31),
            date(2002, 1, 31),
        ])

        result = list(
            rule.apply(ev,
                       startdate=date(2001, 4, 1),
                       enddate=date(2002, 1, 31)))
        self.assertEqual(result, [
            date(2001, 5, 31),
            date(2001, 7, 31),
            date(2002, 1, 31),
        ])
Пример #11
0
    def test_apply_lastweekday(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule(monthly="lastweekday")
        ev = SimpleCalendarEvent(
            datetime(1978, 5, 17, 12, 0),  # 3rd last Wednesday
            timedelta(minutes=10),
            "reality check",
            unique_id='uid')

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(1970, 1, 1)))
        self.assertEqual(result, [])

        # Simplest case
        result = list(rule.apply(ev, enddate=date(1978, 8, 17)))
        expected = [
            date(1978, 5, 17),
            date(1978, 6, 14),
            date(1978, 7, 12),
            date(1978, 8, 16)
        ]
        self.assertEqual(result, expected)

        # With a start date
        result = list(
            rule.apply(ev,
                       startdate=date(1978, 8, 1),
                       enddate=date(1979, 2, 21)))
        expected = [
            date(1978, 8, 16),
            date(1978, 9, 13),
            date(1978, 10, 11),
            date(1978, 11, 15),
            date(1978, 12, 13),
            date(1979, 1, 17),
            date(1979, 2, 14)
        ]
        self.assertEqual(result, expected)

        # With a start date later than the recurrence
        result = list(
            rule.apply(ev,
                       startdate=date(1978, 8, 17),
                       enddate=date(1979, 2, 21)))
        expected = [
            date(1978, 9, 13),
            date(1978, 10, 11),
            date(1978, 11, 15),
            date(1978, 12, 13),
            date(1979, 1, 17),
            date(1979, 2, 14)
        ]
        self.assertEqual(result, expected)

        # Over the end of the year
        result = list(rule.apply(ev, enddate=date(1979, 2, 21)))
        expected = [
            date(1978, 5, 17),
            date(1978, 6, 14),
            date(1978, 7, 12),
            date(1978, 8, 16),
            date(1978, 9, 13),
            date(1978, 10, 11),
            date(1978, 11, 15),
            date(1978, 12, 13),
            date(1979, 1, 17),
            date(1979, 2, 14)
        ]
        self.assertEqual(result, expected)

        # With an end date
        rule = self.createRule(monthly="lastweekday", until=date(1979, 2, 21))
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With a count
        rule = self.createRule(monthly="lastweekday", count=10)
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With an interval
        rule = self.createRule(monthly="lastweekday", interval=2)
        result = list(rule.apply(ev, enddate=date(1979, 2, 21)))
        expected = [
            date(1978, 5, 17),
            date(1978, 7, 12),
            date(1978, 9, 13),
            date(1978, 11, 15),
            date(1979, 1, 17)
        ]
        self.assertEqual(result, expected)

        # With exceptions
        rule = self.createRule(monthly="lastweekday",
                               interval=2,
                               exceptions=[date(1978, 7, 12)])
        result = list(rule.apply(ev, enddate=date(1978, 9, 30)))
        expected = [date(1978, 5, 17), date(1978, 9, 13)]
        self.assertEqual(result, expected)
Пример #12
0
    def test_apply_monthday(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule(monthly="monthday")
        ev = SimpleCalendarEvent(datetime(1978, 5, 17, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(1970, 1, 1)))
        self.assertEqual(result, [])

        # Simplest case
        result = list(rule.apply(ev, enddate=date(1978, 8, 17)))
        expected = [date(1978, m, 17) for m in range(5, 9)]
        self.assertEqual(result, expected)

        # Over the end of the year
        result = list(rule.apply(ev, enddate=date(1979, 2, 17)))
        expected = ([date(1978, m, 17) for m in range(5, 13)] +
                    [date(1979, m, 17) for m in range(1, 3)])
        self.assertEqual(result, expected)

        # With an end date
        rule = self.createRule(monthly="monthday", until=date(1979, 2, 17))
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With an end datetime
        rule = self.createRule(monthly="monthday", until=datetime(1979, 2, 17))
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With a count
        rule = self.createRule(count=10)
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With an interval
        rule = self.createRule(monthly="monthday", interval=2)
        result = list(rule.apply(ev, enddate=date(1979, 2, 17)))
        expected = [
            date(1978, 5, 17),
            date(1978, 7, 17),
            date(1978, 9, 17),
            date(1978, 11, 17),
            date(1979, 1, 17)
        ]
        self.assertEqual(result, expected)

        # With exceptions
        rule = self.createRule(monthly="monthday",
                               interval=2,
                               exceptions=[date(1978, 7, 17)])
        result = list(rule.apply(ev, enddate=date(1978, 9, 17)))
        expected = [date(1978, 5, 17), date(1978, 9, 17)]
        self.assertEqual(result, expected)

        # With a start date
        rule = self.createRule(monthly="monthday",
                               interval=2,
                               exceptions=[date(1978, 7, 17)])
        result = list(
            rule.apply(ev,
                       startdate=date(2000, 1, 1),
                       enddate=date(2000, 6, 17)))
        expected = [date(2000, 1, 17), date(2000, 3, 17), date(2000, 5, 17)]
        self.assertEqual(result, expected)
Пример #13
0
    def test_apply(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(1978, 5, 17, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(1970, 1, 1)))
        self.assertEqual(result, [])

        # Simplest case
        result = list(rule.apply(ev, enddate=date(1978, 7, 17)))  # Wednesday
        expected = [date(1978, 5, 17) + timedelta(w * 7) for w in range(9)]
        self.assertEqual(result, expected)

        # With an end date
        rule = self.createRule(until=date(1978, 7, 12))
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With an end date as datetime
        rule = self.createRule(until=datetime(1978, 7, 12))
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With a count
        rule = self.createRule(count=9)
        result = list(rule.apply(ev))
        self.assertEqual(result, expected)

        # With an interval
        rule = self.createRule(interval=2, weekdays=(3, ))
        result = list(rule.apply(ev, enddate=date(1978, 7, 12)))
        expected = [
            date(1978, 5, 17),
            date(1978, 5, 18),
            date(1978, 5, 31),
            date(1978, 6, 1),
            date(1978, 6, 14),
            date(1978, 6, 15),
            date(1978, 6, 28),
            date(1978, 6, 29),
            date(1978, 7, 12)
        ]
        self.assertEqual(result, expected)

        # With exceptions
        rule = self.createRule(interval=2,
                               weekdays=(3, ),
                               exceptions=[date(1978, 6, 29)])
        result = list(rule.apply(ev, enddate=date(1978, 7, 12)))
        expected = [
            date(1978, 5, 17),
            date(1978, 5, 18),
            date(1978, 5, 31),
            date(1978, 6, 1),
            date(1978, 6, 14),
            date(1978, 6, 15),
            date(1978, 6, 28),
            date(1978, 7, 12)
        ]
        self.assertEqual(result, expected)

        # With a start date
        rule = self.createRule(interval=2, weekdays=(3, ), count=9)
        result = list(rule.apply(ev, startdate=date(1978, 6, 15)))
        expected = [
            date(1978, 6, 15),
            date(1978, 6, 28),
            date(1978, 6, 29),
            date(1978, 7, 12)
        ]

        self.assertEqual(result, expected)
Пример #14
0
    def test_apply(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(1978, 5, 17, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(1970, 1, 1)))
        self.assertEqual(result, [])

        # Simplest case
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(result, [date(y, 5, 17) for y in range(1978, 2005)])

        # With an end date
        rule = self.createRule(until=date(2004, 10, 20))
        result = list(rule.apply(ev))
        self.assertEqual(result, [date(y, 5, 17) for y in range(1978, 2005)])

        # With a count
        rule = self.createRule(count=8)
        result = list(rule.apply(ev))
        self.assertEqual(result, [date(y, 5, 17) for y in range(1978, 1986)])

        # With an interval
        rule = self.createRule(interval=4)
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(result, [
            date(y, 5, 17) for y in [1978, 1982, 1986, 1990, 1994, 1998, 2002]
        ])

        # With exceptions
        rule = self.createRule(exceptions=[date(1980, 5, 17)])
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(
            result, [date(y, 5, 17) for y in [1978, 1979] + range(1981, 2005)])

        # With exceptions and count -- the total nr. of events is less
        # that count.
        rule = self.createRule(exceptions=[date(1980, 5, 17)], count=4)
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(
            result, [date(1978, 5, 17),
                     date(1979, 5, 17),
                     date(1981, 5, 17)])

        # Event somewhere in the future
        rule = self.createRule(interval=2)
        result = list(rule.apply(ev, date(2000, 1, 1), date(2006, 1, 1)))
        self.assertEqual(
            result, [date(2000, 5, 17),
                     date(2002, 5, 17),
                     date(2004, 5, 17)])

        # Frebruary 29
        ev = SimpleCalendarEvent(datetime(1996, 2, 29, 12, 0),
                                 timedelta(minutes=10),
                                 "once in 4 years",
                                 unique_id='uid')
        rule = self.createRule()
        result = list(rule.apply(ev, date(1995, 1, 1), date(2006, 1, 1)))
        self.assertEqual(
            result, [date(1996, 2, 29),
                     date(2000, 2, 29),
                     date(2004, 2, 29)])
Пример #15
0
    def test_apply(self):
        from schooltool.calendar.simple import SimpleCalendarEvent
        rule = self.createRule()
        ev = SimpleCalendarEvent(datetime(2004, 10, 13, 12, 0),
                                 timedelta(minutes=10),
                                 "reality check",
                                 unique_id='uid')

        # The event happened after the range -- empty result
        result = list(rule.apply(ev, enddate=date(2003, 10, 1)))
        self.assertEqual(result, [])

        # Simplest case
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 21)])

        # With an end date
        rule = self.createRule(until=date(2004, 10, 20))
        result = list(rule.apply(ev))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 21)])

        # With an end date as datetime, see issue318
        rule = self.createRule(until=datetime(2004, 10, 20))
        result = list(rule.apply(ev))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 21)])

        # With a count
        rule = self.createRule(count=8)
        result = list(rule.apply(ev))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 21)])

        # With an interval
        rule = self.createRule(interval=2)
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 21, 2)])

        # With exceptions
        rule = self.createRule(
            exceptions=[date(2004, 10, d) for d in range(16, 21)])
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(result, [date(2004, 10, d) for d in range(13, 16)])

        # With exceptions and count -- exceptions are excluded after
        # counting
        rule = self.createRule(
            exceptions=[date(2004, 10, d) for d in range(16, 21)], count=6)
        result = list(rule.apply(ev, enddate=date(2004, 10, 20)))
        self.assertEqual(
            result,
            [date(2004, 10, 13),
             date(2004, 10, 14),
             date(2004, 10, 15)])

        # Far in future
        tick = time.clock()
        rule = self.createRule(interval=3)
        result = list(
            rule.apply(ev,
                       startdate=date(3000, 01, 01),
                       enddate=date(3000, 01, 20)))
        self.assertEquals(int(time.clock() - tick), 0)
        self.assertEqual(result, [
            date(3000, 1, 3),
            date(3000, 1, 6),
            date(3000, 1, 9),
            date(3000, 1, 12),
            date(3000, 1, 15),
            date(3000, 1, 18)
        ])
Пример #16
0
def convert_calendar_to_ical(calendar):
    r"""Convert an ICalendar to iCalendar VCALENDAR component.

    Returns a list of strings (without newlines) in UTF-8.  They should be
    joined with '\r\n' to get a valid iCalendar file.

        >>> from schooltool.calendar.simple import ImmutableCalendar
        >>> from schooltool.calendar.simple import SimpleCalendarEvent
        >>> from datetime import datetime, timedelta
        >>> event = SimpleCalendarEvent(datetime(2004, 12, 16, 10, 7, 29),
        ...                             timedelta(hours=1), "iCal rendering",
        ...                             location="Big room",
        ...                             unique_id="*****@*****.**")
        >>> calendar = ImmutableCalendar([event])
        >>> lines = convert_calendar_to_ical(calendar)
        >>> print "\n".join(lines)
        BEGIN:VCALENDAR
        VERSION:2.0
        PRODID:-//SchoolTool.org/NONSGML SchoolTool//EN
        BEGIN:VEVENT
        UID:[email protected]
        SUMMARY:iCal rendering
        LOCATION:Big room
        DTSTART:20041216T100729Z
        DURATION:PT1H
        DTSTAMP:...
        END:VEVENT
        END:VCALENDAR

    Empty calendars are not allowed by RFC 2445, so we have to invent a dummy
    event:

        >>> lines = convert_calendar_to_ical(ImmutableCalendar())
        >>> print "\n".join(lines)
        BEGIN:VCALENDAR
        VERSION:2.0
        PRODID:-//SchoolTool.org/NONSGML SchoolTool//EN
        BEGIN:VEVENT
        UID:...
        SUMMARY:Empty calendar
        DTSTART:19700101T000000Z
        DURATION:P0D
        DTSTAMP:...
        END:VEVENT
        END:VCALENDAR

    """
    header = ["BEGIN:VCALENDAR",
              "VERSION:2.0",
              "PRODID:-//SchoolTool.org/NONSGML SchoolTool//EN"]
    footer = ["END:VCALENDAR"]
    events = []
    for event in calendar:
        events += convert_event_to_ical(event)
    if not events:
        placeholder = SimpleCalendarEvent(datetime.datetime(1970, 1, 1),
                                          datetime.timedelta(0),
                                          "Empty calendar",
                                          unique_id=EMPTY_CALENDAR_PLACEHOLDER)
        events += convert_event_to_ical(placeholder)
    return header + events + footer