def generate(self, app, seed=None): self.random = PortableRandom(seed) event_titles = self._readLines('event_titles.txt') person_ids = [person for person in app['persons'].keys() if person.startswith('student') or person.startswith('teacher')] dates = [] for term in ITermContainer(app).values(): dates.append(term.first) dates.append(term.last) first = min(dates) last = max(dates) days = DateRange(first, last) for person_id in person_ids: person = app['persons'][person_id] calendar = ISchoolToolCalendar(person) for day in days: if self.random.randrange(0, 100) < self.probability: event_title = self.random.choice(event_titles) time_hour = self.random.randint(6, 23) time_min = self.random.choice((0, 30)) event_time = datetime.datetime(day.year, day.month, day.day, time_hour, time_min, tzinfo=utc) event_duration = datetime.timedelta( minutes=self.random.randint(1, 12)*30) event = CalendarEvent(event_time, event_duration, event_title) calendar.addEvent(event)
def bookResource(self, resource): calendar = ISchoolToolCalendar(resource) if resource in self.resources: raise ValueError("resource already booked") if calendar is self.__parent__: raise ValueError("cannot book itself") self._resources += (resource,) if self.__parent__ is not None: calendar.addEvent(self)
def bookResource(self, resource): calendar = ISchoolToolCalendar(resource) if resource in self.resources: raise ValueError('resource already booked') if calendar is self.__parent__: raise ValueError('cannot book itself') self._resources += (resource, ) if self.__parent__ is not None: calendar.addEvent(self)
def addEvent(self, event): assert ISchoolToolCalendarEvent.providedBy(event) if event.unique_id in self.events: raise ValueError('an event with this unique_id already exists') if event.__parent__ is None: for resource in event.resources: if ISchoolToolCalendar(resource) is self: raise ValueError('cannot book itself') event.__parent__ = self for resource in event.resources: ISchoolToolCalendar(resource).addEvent(event) elif self.__parent__ not in event.resources: raise ValueError("Event already belongs to a calendar") self.events[event.unique_id] = event
def test_calendarRows(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView from schooltool.app.security import Principal request = TestRequest() principal = Principal('person', 'Some person', person=self.person) request.setPrincipal(principal) view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) result = list(view.calendarRows(date(2004, 11, 5), 8, 19, events=[])) expected = [("1", dt('08:00'), timedelta(hours=1)), ("9:00", dt('09:00'), timedelta(hours=1)), ("10:00", dt('10:00'), timedelta(minutes=15)), ("2", dt('10:15'), timedelta(hours=1)), ("11:15", dt('11:15'), timedelta(minutes=15)), ("3", dt('11:30'), timedelta(hours=1)), ("4", dt('12:30'), timedelta(hours=2)), ("14:30", dt('14:30'), timedelta(minutes=30)), ("15:00", dt('15:00'), timedelta(hours=1)), ("16:00", dt('16:00'), timedelta(hours=1)), ("17:00", dt('17:00'), timedelta(hours=1)), ("18:00", dt('18:00'), timedelta(hours=1))] self.assertEquals(result, expected)
def create_random_events(app, count=5000, seed=42): """Create a user with many nonrecurrent events in his calendar. The user's username will be 'manager', and his calendar will have a given number of random nonrecurring events in the year 2005. """ rng = random.Random(seed) person = Person('manager', 'Manager') app['persons']['manager'] = person year = 2005 months = range(1, 13) days = range(1, 29) hours = range(24) minutes = range(60) durations = range(15, 180) for i in range(count): dtstart = datetime(2005, rng.choice(months), rng.choice(days), rng.choice(hours), rng.choice(minutes)) duration = timedelta(minutes=rng.choice(durations)) event = CalendarEvent(dtstart, duration, 'Lorem ipsum %d' % i, recurrence=None, location='Booha', allday=False, description='Some words.') ISchoolToolCalendar(person).addEvent(event)
def getApplicationCalendar(self): if self.user is None: return None app = ISchoolToolApplication(None) calendar = ISchoolToolCalendar(app) if not canAccess(calendar, '__iter__'): return None return calendar
def test_getPersonTimezone(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView request = TestRequest() view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) # when there is no principal - the default timezone should be # returned self.assertEquals(view.getPersonTimezone(), timezone('UTC'))
def getResourceCalendars(self): if self.user is None: return [] app = ISchoolToolApplication(None) result = [] for obj in app['resources'].values(): calendar = ISchoolToolCalendar(obj) if canAccess(calendar, '__iter__'): result.append(calendar) return result
def getCalendars(self): """Get a list of calendars to display. Yields tuples (calendar, color1, color2). """ person_calendar = ISchoolToolCalendar(IPerson(self.request.principal)) resource_calendars = getSelectedResourceCalendars(self.request) booking_calendar = createBookingCalendar(person_calendar, resource_calendars) yield (booking_calendar, '#9db8d2', '#7590ae') yield (self.context, '#bfdaf4', '#99b2ce')
def unrelateCalendarOnDeletion(event): """When you delete an object, relationships of it's calendar should be removed >>> from schooltool.relationship.tests import setUp, tearDown >>> from schooltool.testing.setup import setUpCalendaring >>> setUp() >>> setUpCalendaring() >>> import zope.event >>> old_subscribers = zope.event.subscribers[:] >>> from schooltool.app.overlay import unrelateCalendarOnDeletion >>> zope.event.subscribers.append(unrelateCalendarOnDeletion) We will need some object that implements IHaveCalendar for that: >>> from zope.container.btree import BTreeContainer >>> container = BTreeContainer() >>> from schooltool.person.person import Person >>> container = BTreeContainer() >>> container['jonas'] = jonas = Person(username="******") >>> container['petras'] = petras = Person(username="******") Let's add calendar of Petras to the list of overlaid calendars: >>> jonas.overlaid_calendars.add(ISchoolToolCalendar(petras)) <...CalendarOverlayInfo object at ...> >>> list(jonas.overlaid_calendars) [<schooltool.app.overlay.CalendarOverlayInfo object at ...>] If we delete Petras - Jonas should have no calendars in his overlay list: >>> del container['petras'] >>> list(jonas.overlaid_calendars) [] Restore old subscribers: >>> zope.event.subscribers[:] = old_subscribers >>> tearDown() """ if not IObjectRemovedEvent.providedBy(event): return # event.object may be a ContainedProxy obj = getProxiedObject(event.object) if not IHaveCalendar.providedBy(obj): return calendar = ISchoolToolCalendar(obj) linkset = IRelationshipLinks(calendar, None) if linkset is not None: unrelateAll(calendar)
def test_calendarRows_default(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView request = TestRequest() # do not set the principal view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) result = list(view.calendarRows(date(2004, 11, 5), 8, 19, events=[])) # the default is not to show periods expected = [("%d:00" % i, dt('%d:00' % i), timedelta(hours=1)) for i in range(8, 19)] self.assertEquals(result, expected)
def personAppCalendarOverlaySubscriber(person, event): """Add application calendar to overlays of all new persons. """ app = ISchoolToolApplication(None, None) if app is None: # If we get this we are probably in the initial new-site setup # or creating a new manager during startup. This should be # safe to ignore since it will happen very infrequently # (perhaps only once) and the manager can easily add the site # calendar to his/her overlay in the overlay selection view. return person.overlaid_calendars.add(ISchoolToolCalendar(app))
def __call__(self): app = ISchoolToolApplication(None) person = IPerson(self.request.principal, None) if not person: raise Unauthorized("Only logged in users can book resources.") cal = ISchoolToolCalendar(person) if self.request.has_key('event_id'): event = cal.find(self.request['event_id']) else: start_date = self.request.get('start_date') start_time = self.request.get('start_time') title = self.request.get('title') start_datetime = "%s %s" % (start_date, start_time) start_datetime = datetime(*strptime(start_datetime, "%Y-%m-%d %H:%M")[0:6]) start_datetime = self.timezone.localize(start_datetime) start_datetime = start_datetime.astimezone(pytz.UTC) duration = timedelta(seconds=int(self.request.get('duration'))) event = CalendarEvent(dtstart = start_datetime, duration = duration, title = title) cal.addEvent(event) if event: resource = app["resources"].get(self.request['resource_id']) if resource is not None: resource_calendar = ISchoolToolCalendar(resource) if not canAccess(resource_calendar, "addEvent"): raise Unauthorized("You don't have the right to" " book this resource!") event.bookResource(resource) self.request.response.redirect(self.nextURL(event))
def getCalendars(self): """Get a list of calendars to display. Yields tuples (calendar, color1, color2). """ owner = self.context.__parent__ user = IPerson(self.request.principal, None) if (user is not None and sameProxiedObjects(user, owner)): return instructs = list( getRelatedObjects(owner, URISection, rel_type=URIInstruction)) member_of = list( getRelatedObjects(owner, URIGroup, rel_type=URIMembership)) for obj in instructs + member_of: if IHaveSchedule.providedBy(obj): cal = ISchoolToolCalendar(obj, None) if cal is not None: yield (ISchoolToolCalendar(obj), '#9db8d2', '#7590ae')
def create_user_and_recurrent_events(app): """Create a user with some recurrent events in his calendar. The user's username will be 'manager', and his calendar will have exactly four events, recurring daily, weekly, monthly, and yearly respectively. All these events start on the same day. """ person = Person('manager', 'Manager') app['persons']['manager'] = person calendar = ISchoolToolCalendar(person) recurrence = DailyRecurrenceRule() daily_event = CalendarEvent(datetime(2005, 1, 1, 10, 0), timedelta(60), 'Daily Event', recurrence=recurrence) calendar.addEvent(daily_event) recurrence = WeeklyRecurrenceRule(weekdays=(0, 1, 2, 3, 4, 5, 6)) weekly_event = CalendarEvent(datetime(2005, 1, 1, 11, 0), timedelta(60), 'Weekly event', recurrence=recurrence) calendar.addEvent(weekly_event) recurrence = MonthlyRecurrenceRule() monthly_event = CalendarEvent(datetime(2005, 1, 1, 12, 0), timedelta(60), 'Monthly event', recurrence=recurrence) calendar.addEvent(monthly_event) recurrence = YearlyRecurrenceRule() yearly_event = CalendarEvent(datetime(2005, 1, 1, 13, 0), timedelta(60), 'Yearly event', recurrence=recurrence) calendar.addEvent(yearly_event)
def updateInstructorCalendars(event): """Add section's calendar to instructors overlaid calendars.""" if event.rel_type != URIInstruction: return person = event[URIInstructor] section = event[URISection] calendar = ISchoolToolCalendar(section) if IRelationshipAddedEvent.providedBy(event): if calendar not in person.overlaid_calendars: person.overlaid_calendars.add(calendar) elif IRelationshipRemovedEvent.providedBy(event): if calendar in person.overlaid_calendars: person.overlaid_calendars.remove(calendar)
def getApplicationCalendar(self): """Return the application calendar. Returns None if the user lacks sufficient permissions. """ user = IPerson(self.request.principal, None) if user: app = ISchoolToolApplication(None) calendar = ISchoolToolCalendar(app) if canAccess(calendar, '__iter__'): return { 'title': app.title, 'selected': calendar in user.overlaid_calendars, 'calendar': calendar } return {}
def nextURL(self, event): """Return the URL to be displayed after the add operation.""" app = ISchoolToolApplication(None) resource = app["resources"].get(self.request['resource_id']) back_url = self.request.get('next_url', '') if resource is not None and not back_url: back_url = urllib.quote(absoluteURL( ISchoolToolCalendar(resource), self.request)) cancel_url = self.cancelURL(event, back_url) url = "%s/edit.html?back_url=%s&cancel_url=%s" % ( absoluteURL(event, self.request), back_url, urllib.quote(cancel_url)) return url
def getCalendars(self, container): """List all calendars from a given container.""" user = removeSecurityProxy(IPerson(self.request.principal, None)) if user is None: return [] app = ISchoolToolApplication(None) result = [] for obj in app[container].values(): calendar = ISchoolToolCalendar(obj) if obj is not user and canAccess(calendar, '__iter__'): result.append({ 'id': obj.__name__, 'title': obj.title, 'selected': calendar in user.overlaid_calendars, 'calendar': calendar }) return sorted(result, key=lambda item: (item['title'], item['id']))
def test_calendarRows_no_periods(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView from schooltool.person.preference import getPersonPreferences from schooltool.app.security import Principal prefs = getPersonPreferences(self.person) prefs.cal_periods = False # do not show periods request = TestRequest() principal = Principal('person', 'Some person', person=self.person) request.setPrincipal(principal) view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) result = list(view.calendarRows(date(2004, 11, 5), 8, 19, events=[])) expected = [("%d:00" % i, dt('%d:00' % i), timedelta(hours=1)) for i in range(8, 19)] self.assertEquals(result, expected)
def updateStudentCalendars(event): """Add section's calendar to students overlaid calendars.""" if event.rel_type != URIMembership: return section = event[URIGroup] # Only continue if we're working with Sections rather than generic groups if not ISection.providedBy(section): return member = event[URIMember] calendar = ISchoolToolCalendar(section) if IRelationshipAddedEvent.providedBy(event): if IPerson.providedBy(member) and \ calendar not in member.overlaid_calendars: member.overlaid_calendars.add(calendar) elif IGroup.providedBy(member): for person in member.members: # we don't handle nested groups any more so there # shouldn't be more than one layer of groups if IPerson.providedBy(person) and \ calendar not in person.overlaid_calendars: person.overlaid_calendars.add(calendar) elif IRelationshipRemovedEvent.providedBy(event): if IPerson.providedBy(member): if calendar in member.overlaid_calendars: for group in member.groups: if group in section.members: return member.overlaid_calendars.remove(calendar) elif IGroup.providedBy(member): for person in member.members: if IPerson.providedBy(person): if calendar in person.overlaid_calendars: if person not in section.members: person.overlaid_calendars.remove(calendar)
def test_getPeriods(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView request = TestRequest() view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) # if no user has logged we should get an empty list self.assertEquals(view.getPeriods(date(2005, 1, 1)), []) # same if our user doesn't want to see periods in his calendar request.setPrincipal(self.person) IPersonPreferences(self.person).cal_periods = False self.assertEquals(view.getPeriods(date(2005, 1, 1)), []) # if currently logged in user wants to see periods, the # parameter is passed to getPeriodsForDay method. view.getPeriodsForDay = lambda cursor: ("Yep", cursor) IPersonPreferences(self.person).cal_periods = True self.assertEquals(view.getPeriods(date(2005, 1, 1)), ("Yep", date(2005, 1, 1)))
def clearCalendarOnDeletion(event): """When you delete an object, it's calendar should be cleared >>> from schooltool.relationship.tests import setUp, tearDown >>> from schooltool.testing.setup import setUpCalendaring >>> setUp() >>> setUpCalendaring() >>> import zope.event >>> old_subscribers = zope.event.subscribers[:] >>> from schooltool.app.cal import clearCalendarOnDeletion >>> zope.event.subscribers.append(clearCalendarOnDeletion) We will need some object that implements IHaveCalendar for that: >>> from zope.container.btree import BTreeContainer >>> container = BTreeContainer() >>> from schooltool.person.person import Person >>> container = BTreeContainer() >>> container['petras'] = petras = Person(username="******") >>> def clearCalendar(): ... print "Clearing calendar" >>> ISchoolToolCalendar(petras).clear = clearCalendar If we delete Petras his calendar should be cleared: >>> del container['petras'] Clearing calendar Restore old subscribers: >>> zope.event.subscribers[:] = old_subscribers >>> tearDown() """ if IHaveCalendar.providedBy(event.object): ISchoolToolCalendar(event.object).clear()
def test_calendarRows_otherTZ(self): from schooltool.timetable.browser.cal import DailyTimetableCalendarRowsView from schooltool.app.security import Principal request = TestRequest() principal = Principal('person', 'Some person', person=self.person) request.setPrincipal(principal) view = DailyTimetableCalendarRowsView(ISchoolToolCalendar(self.person), request) km = timezone('Asia/Kamchatka') view.getPersonTimezone = lambda: km result = list(view.calendarRows(date(2004, 11, 5), 8, 19, events=[])) kmdt = lambda arg: km.localize(parse_datetime('2004-11-05 %s:00' % arg) ) expected = [('8:00', kmdt('8:00'), timedelta(0, 3600)), ('9:00', kmdt('9:00'), timedelta(0, 3600)), ('10:00', kmdt('10:00'), timedelta(0, 3600)), ('11:00', kmdt('11:00'), timedelta(0, 3600)), ('12:00', kmdt('12:00'), timedelta(0, 3600)), ('13:00', kmdt('13:00'), timedelta(0, 3600)), ('14:00', kmdt('14:00'), timedelta(0, 3600)), ('15:00', kmdt('15:00'), timedelta(0, 3600)), ('16:00', kmdt('16:00'), timedelta(0, 3600)), ('17:00', kmdt('17:00'), timedelta(0, 3600)), ('18:00', kmdt('18:00'), timedelta(0, 3600)), ('19:00', kmdt('19:00'), timedelta(0, 3600)), ('1', kmdt("20:00"), timedelta(0, 3600)), ('21:00', kmdt("21:00"), timedelta(0, 4500)), ('2', kmdt("22:15"), timedelta(0, 3600)), ('23:15', kmdt("23:15"), timedelta(0, 900)), ('3', kmdt("23:30"), timedelta(0, 1800))] self.assertEquals(result, expected)
def update(self): if ("field.title" not in self.request): calendar = ISchoolToolCalendar(self.context) owner = IHaveSchedule(calendar.__parent__) self.request.form["field.title"] = owner.title super(FlourishScheduleEventAddView, self).update()
def getSelectedResourceCalendars(request): rc = ISchoolToolApplication(None)['resources'] session = ISession(request)['schooltool.resource'] resource_calendars = [ISchoolToolCalendar(rc[resource_id]) for resource_id in session['bookingSelection']] return resource_calendars
def update(self): self.template = self.date_template if 'CANCEL' in self.request: self.request.response.redirect( absoluteURL(self.context, self.request)) return if 'date' in self.request: try: self.date = parse_date(self.request['date']) except ValueError: self.error = _("The date you entered is invalid." " Please use the YYYY-MM-DD format.") return if not self.date in self.context: self.error = _("The date you entered does not belong to" " this term.") return if not self.context.isSchoolday(self.date): self.error = _("The date you entered is not a schoolday.") return self.template = self.replacement_template if 'replacement' in self.request: try: self.replacement = parse_date(self.request['replacement']) except ValueError: self.error = _("The replacement date you entered is invalid.") self.template = self.replacement_template return if self.date and self.replacement: if self.context.last < self.replacement: # XXX: I wonder if all places that are dependent on # term start/end dates are updated properly self.context.last = self.replacement # XXX: assert?! assert not self.context.isSchoolday(self.replacement) assert self.context.isSchoolday(self.date) self.context.add(self.replacement) notify(EmergencyDayEvent(self.date, self.replacement)) # XXX: Following code should be move to the event subscriber, but! # It wants to store translated messages, and in current default # case the event description will be stored in the language # from somebodys browser settings. Now that's naughty! # If only we had 'apllication language' separate form 'user # presentation language' this problem would go away. # # Post calendar events to schoolwide calendar calendar = ISchoolToolCalendar(ISchoolToolApplication(None)) dtstart = datetime.datetime.combine(self.date, datetime.time()) msg = _('School cancelled due to emergency.' ' Replacement day $replacement.', mapping={'replacement': str(self.replacement)}) msg = translate(msg, context=self.request) calendar.addEvent( CalendarEvent(dtstart, datetime.timedelta(), msg, allday=True)) dtstart = datetime.datetime.combine(self.replacement, datetime.time()) msg = _('Replacement day for emergency day $emergency.', mapping={'emergency': str(self.date)}) msg = translate(msg, context=self.request) calendar.addEvent( CalendarEvent(dtstart, datetime.timedelta(), msg, allday=True)) self.request.response.redirect( absoluteURL(self.context, self.request))
def nextURL(self): url = self.request.get('nexturl') if url is None: cal = ISchoolToolCalendar(self.context) url = absoluteURL(cal, self.request) return url
def unbookResource(self, resource): if resource not in self.resources: raise ValueError('resource not booked') self._resources = tuple([r for r in self.resources if r is not resource]) ISchoolToolCalendar(resource).removeEvent(self)