/usr/share/pyshared/schooltool/timetable/calendar.py 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 | #
# SchoolTool - common information systems platform for school administration
# Copyright (c) 2010 Shuttleworth Foundation
#
# 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
#
"""
Synchronisation between timetables and calendars.
"""
import pytz
import zope.lifecycleevent.interfaces
from zope.annotation.interfaces import IAnnotations
from zope.component import adapts, adapter, getUtility
from zope.interface import implements
from zope.intid.interfaces import IIntIds
from zope.security.proxy import removeSecurityProxy
from schooltool.timetable import interfaces
from schooltool.app.cal import CalendarEvent, Calendar
from schooltool.calendar.simple import ImmutableCalendar
from schooltool.schoolyear.subscriber import ObjectEventAdapterSubscriber
SCHEDULE_CALENDAR_KEY = 'schooltool.timetable.app.ScheduleCalendar'
class ScheduleCalendarEvent(CalendarEvent):
"""Period scheduled in a calendar."""
implements(interfaces.IScheduleCalendarEvent)
schedule = None # schedule responsible for this event
period = None
meeting_id = None
def __init__(self, *args, **kw):
self.schedule = kw.pop('schedule', None)
# XXX: int id would be more appropriate here maybe
self.period = kw.pop('period', None)
self.meeting_id = kw.pop('meeting_id', None)
super(ScheduleCalendarEvent, self).__init__(*args, **kw)
if self.meeting_id is None:
self.meeting_id = self.unique_id
class ImmutableScheduleCalendar(ImmutableCalendar):
adapts(interfaces.ISchedule)
implements(interfaces.IImmutableScheduleCalendar)
schedule = None
def __init__(self, schedule):
self.schedule = schedule
events = tuple(self.createEvents())
super(ImmutableScheduleCalendar, self).__init__(events=events)
def makeGUID(self, date, period, int_ids=None):
if int_ids is None:
int_ids = getUtility(IIntIds)
return u'%s.%s.%s' % (
date.isoformat(),
int_ids.getId(self.schedule),
int_ids.getId(period),
)
def createEvents(self):
int_ids = getUtility(IIntIds)
owner = interfaces.IHaveSchedule(self.schedule)
title = getattr(owner, 'title', u'')
schedule = self.schedule
if (schedule.first is None or
schedule.last is None):
return # Empty schedule
meetings = schedule.iterMeetings(schedule.first, schedule.last)
for meeting in meetings:
# We need to convert dtstart to UTC, because calendar
# events insist on storing UTC time.
dtstart = meeting.dtstart.astimezone(pytz.UTC)
guid = self.makeGUID(dtstart.date(), meeting.period,
int_ids=int_ids)
event = ScheduleCalendarEvent(
dtstart, meeting.duration, title,
schedule=schedule,
period=meeting.period,
meeting_id=meeting.meeting_id,
unique_id=guid)
yield event
class ScheduleCalendar(Calendar):
implements(interfaces.IScheduleCalendar)
_synchronised_attrs = ('dtstart', 'duration',
'period', 'meeting_id',
'title')
def updateEvent(self, event, other_event):
_unspecified = object()
changed = False
for attr in self._synchronised_attrs:
new_val = getattr(other_event, attr, _unspecified)
if (new_val is not _unspecified and
new_val != getattr(event, attr)):
setattr(event, attr, new_val)
changed = True
return changed
def updateSchedule(self, schedule):
schedule = removeSecurityProxy(schedule)
schedule_cal = interfaces.IImmutableScheduleCalendar(schedule)
if schedule_cal is None:
self.removeSchedule(schedule)
return
old_events = dict(
[(e.unique_id, e) for e in removeSecurityProxy(self)
if e.schedule is schedule])
new_events = dict([(e.unique_id, e) for e in schedule_cal])
old_set = set(old_events)
new_set = set(new_events)
for uid in sorted(old_set - new_set):
self.removeEvent(old_events[uid])
for uid in sorted(new_set - old_set):
self.addEvent(new_events[uid])
for uid in sorted(new_set & old_set):
self.updateEvent(old_events[uid], new_events[uid])
def removeSchedule(self, schedule):
schedule = removeSecurityProxy(schedule)
old_events = sorted([e for e in removeSecurityProxy(self)])
for event in old_events:
self.removeEvent(event)
@adapter(interfaces.IHaveSchedule)
def getScheduleCalendar(owner):
annotations = IAnnotations(owner)
try:
return annotations[SCHEDULE_CALENDAR_KEY]
except KeyError:
calendar = ScheduleCalendar(owner)
annotations[SCHEDULE_CALENDAR_KEY] = calendar
return calendar
getScheduleCalendar.factory = ScheduleCalendar
class UpdateScheduleCalendar(ObjectEventAdapterSubscriber):
def __call__(self):
owner = interfaces.IHaveSchedule(self.object, None)
if owner is None:
return
calendar = interfaces.IScheduleCalendar(owner, None)
if calendar is None:
return
container = interfaces.IScheduleContainer(owner, None)
if container is None:
return
calendar.updateSchedule(container)
class RemoveScheduleCalendar(ObjectEventAdapterSubscriber):
def __call__(self):
owner = interfaces.IHaveSchedule(self.object, None)
if owner is None:
return
calendar = interfaces.IScheduleCalendar(owner, None)
if calendar is None:
return
container = interfaces.IScheduleContainer(owner, None)
if container is None:
return
calendar.removeSchedule(self.object)
|