def expand_events(events, ret_mode, start=None, end=None, sort=None, sort_reverse=None): """Expand to the recurrence occurrences of a given set of events. :param events: IEvent based objects or IEventAccessor object wrapper. :param ret_mode: Return type of search results. These options are available: * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. Option "1" (brains) is not supported. :type ret_mode: integer [2|3] :param start: Date, from which on events should be expanded. :type start: Python datetime. :param end: Date, until which events should be expanded. :type end: Python datetime :param sort: Object or IEventAccessor Attribute to sort on. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean """ assert (ret_mode is not RET_MODE_BRAINS) exp_result = [] for it in events: obj = it.getObject() if getattr(it, 'getObject', False) else it if IEventRecurrence.providedBy(obj): occurrences = [ _obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end) ] elif IEvent.providedBy(obj): occurrences = [_obj_or_acc(obj, ret_mode)] else: # No IEvent based object. Could come from a collection. continue exp_result += occurrences if sort: exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() return exp_result
def test_implementsInterfaces(self): """Test if an ATEvent object implements all relevant interfaces. """ self.assertTrue(IEvent.providedBy(self.obj)) self.assertTrue(IEventRecurrence.providedBy(self.obj)) self.assertTrue(IATEvent.providedBy(self.obj)) self.assertTrue(IATEventRecurrence.providedBy(self.obj)) self.assertTrue(IATEvent_ATCT.providedBy(self.obj)) self.assertTrue(verifyObject(IATEvent_ATCT, self.obj))
def expand_events(events, ret_mode, start=None, end=None, sort=None, sort_reverse=None): """Expand to the recurrence occurrences of a given set of events. :param events: IEvent based objects or IEventAccessor object wrapper. :param ret_mode: Return type of search results. These options are available: * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. Option "1" (brains) is not supported. :type ret_mode: integer [2|3] :param start: Date, from which on events should be expanded. :type start: Python datetime. :param end: Date, until which events should be expanded. :type end: Python datetime :param sort: Object or IEventAccessor Attribute to sort on. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean """ assert(ret_mode is not RET_MODE_BRAINS) exp_result = [] for it in events: obj = it.getObject() if getattr(it, 'getObject', False) else it if IEventRecurrence.providedBy(obj): occurrences = [_obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end)] elif IEvent.providedBy(obj): occurrences = [_obj_or_acc(obj, ret_mode)] else: # No IEvent based object. Could come from a collection. continue exp_result += occurrences if sort: exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() return exp_result
def get_occurrences_by_date(context, range_start=None, range_end=None, **kw): """Return a dictionary with dates in a given timeframe as keys and the actual occurrences for that date for building calendars. :param context: [required] A context object. :type context: Content object :param range_start: Date, from which on events should be searched. :type range_start: Python datetime. :param range_end: Date, until which events should be searched. :type range_end: Python datetime :returns: Dictionary with dates keys and occurrences as values. :rtype: dict """ range_start, range_end = _prepare_range(context, range_start, range_end) events = get_portal_events(context, range_start, range_end, **kw) events_by_date = {} for event in events: obj = event.getObject() if IEventRecurrence.providedBy(obj): occurrences = IRecurrenceSupport(obj).occurrences( range_start, range_end) else: occurrences = [obj] for occ in occurrences: accessor = IEventAccessor(occ) start_str = datetime.strftime(accessor.start, '%Y-%m-%d') # TODO: add span_events parameter to include dates btw. start # and end also. for events lasting longer than a day... if start_str not in events_by_date: events_by_date[start_str] = [occ] else: events_by_date[start_str].append(occ) return events_by_date
def get_events(context, start=None, end=None, limit=None, ret_mode=1, expand=False, sort='start', sort_reverse=False, **kw): """Return all events as catalog brains, possibly within a given timeframe. :param context: [required] A context object. :type context: Content object :param start: Date, from which on events should be searched. :type start: Python datetime. :param end: Date, until which events should be searched. :type end: Python datetime :param limit: Number of items to be returned. :type limit: integer :param ret_mode: Return type of search results. These options are available: * 1 (brains): Return results as catalog brains. * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. :type ret_mode: integer [1|2|3] :param expand: Expand the results to all occurrences (within a timeframe, if given). With this option set to True, the resultset also includes the event's recurrence occurrences and is sorted by the start date. Only available in ret_mode 2 (objects) and 3 (accessors). :type expand: boolean :param sort: Catalog index id to sort on. Not available with expand=True. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean :returns: Portal events, matching the search criteria. :rtype: catalog brains """ start, end = _prepare_range(context, start, end) query = {} query['object_provides'] = IEvent.__identifier__ if 'path' not in kw: # limit to the current navigation root, usually (not always) site portal = getSite() navroot = getNavigationRootObject(context, portal) query['path'] = '/'.join(navroot.getPhysicalPath()) else: query['path'] = kw['path'] if start: # All events from start date ongoing: # The minimum end date of events is the date from which we search. query['end'] = {'query': start, 'range': 'min'} if end: # All events until end date: # The maximum start date must be the date until we search. query['start'] = {'query': end, 'range': 'max'} # Sorting # In expand mode we sort after calculation of recurrences again. But we # need to leave this sorting here in place, since no sort definition could # lead to arbitrary results when limiting with sort_limit. query['sort_on'] = sort if sort_reverse: query['sort_order'] = 'reverse' # XXX Passing the limit to the catalog can cause events that should be in # the results to be missing from the results. #if limit: # query['sort_limit'] = limit query.update(kw) cat = getToolByName(context, 'portal_catalog') result = cat(**query) # Helper functions def _obj_or_acc(obj, ret_mode): if ret_mode == 2: return obj elif ret_mode == 3: return IEventAccessor(obj) def _get_compare_attr(obj, attr): val = getattr(obj, attr, None) if safe_callable(val): val = val() if isinstance(val, DateTime): val = pydt(val) return val if ret_mode in (2, 3) and expand == False: result = [_obj_or_acc(it.getObject(), ret_mode) for it in result] elif ret_mode in (2, 3) and expand == True: exp_result = [] for it in result: obj = it.getObject() if IEventRecurrence.providedBy(obj): occurrences = [_obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end)] else: occurrences = [_obj_or_acc(obj, ret_mode)] exp_result += occurrences if sort: # support AT and DX without wrapped by IEventAccessor (mainly for # sorting after "start" or "end"). exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() result = exp_result if limit: # Expanded events as well as catalog search results (which might not # exactly be limited by the query) must be limited again. result = result[:limit] return result
def get_events(context, start=None, end=None, limit=None, ret_mode=1, expand=False, sort='start', sort_reverse=False, **kw): """Return all events as catalog brains, possibly within a given timeframe. :param context: [required] A context object. :type context: Content object :param start: Date, from which on events should be searched. :type start: Python datetime. :param end: Date, until which events should be searched. :type end: Python datetime :param limit: Number of items to be returned. :type limit: integer :param ret_mode: Return type of search results. These options are available: * 1 (brains): Return results as catalog brains. * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. :type ret_mode: integer [1|2|3] :param expand: Expand the results to all occurrences (within a timeframe, if given). With this option set to True, the resultset also includes the event's recurrence occurrences and is sorted by the start date. Only available in ret_mode 2 (objects) and 3 (accessors). :type expand: boolean :param sort: Catalog index id to sort on. Not available with expand=True. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean :returns: Portal events, matching the search criteria. :rtype: catalog brains """ start, end = _prepare_range(context, start, end) query = {} query['object_provides'] = IEvent.__identifier__ if 'path' not in kw: # limit to the current navigation root, usually (not always) site portal = getSite() navroot = getNavigationRootObject(context, portal) query['path'] = '/'.join(navroot.getPhysicalPath()) else: query['path'] = kw['path'] if start: # All events from start date ongoing: # The minimum end date of events is the date from which we search. query['end'] = {'query': start, 'range': 'min'} if end: # All events until end date: # The maximum start date must be the date until we search. query['start'] = {'query': end, 'range': 'max'} # Sorting # In expand mode we sort after calculation of recurrences again. But we # need to leave this sorting here in place, since no sort definition could # lead to arbitrary results when limiting with sort_limit. query['sort_on'] = sort if sort_reverse: query['sort_order'] = 'reverse' if limit: query['sort_limit'] = limit query.update(kw) cat = getToolByName(context, 'portal_catalog') result = cat(**query) # Helper functions def _obj_or_acc(obj, ret_mode): if ret_mode == 2: return obj elif ret_mode == 3: return IEventAccessor(obj) def _get_compare_attr(obj, attr): val = getattr(obj, attr, None) if safe_callable(val): val = val() if isinstance(val, DateTime): val = pydt(val) return val if ret_mode in (2, 3) and expand == False: result = [_obj_or_acc(it.getObject(), ret_mode) for it in result] elif ret_mode in (2, 3) and expand == True: exp_result = [] for it in result: obj = it.getObject() if IEventRecurrence.providedBy(obj): occurrences = [ _obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end) ] else: occurrences = [_obj_or_acc(obj, ret_mode)] exp_result += occurrences if sort: # support AT and DX without wrapped by IEventAccessor (mainly for # sorting after "start" or "end"). exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() result = exp_result if limit: # Expanded events as well as catalog search results (which might not # exactly be limited by the query) must be limited again. result = result[:limit] return result