예제 #1
0
 def occurrences(self):
     if not self.data.is_event: return None
     rec = IRecurrenceSupport(self.context, None)
     if rec:
         return rec.occurrences()
     else:
         return None
예제 #2
0
 def publishTraverse(self, request, name):
     context = self.context
     dateobj = guess_date_from(name, context)
     if dateobj:
         occs = IRecurrenceSupport(context).occurrences(range_start=dateobj)
         occurrence = occs.next()
         occ_acc = IEventAccessor(occurrence)
         if is_same_day(dateobj, occ_acc.start):
             return occurrence
     return self.fallbackTraverse(request, name)
예제 #3
0
    def next_occurrences(self):
        """Returns occurrences for this context, except the start
        occurrence, limited to self.max_occurrence occurrences.

        :returns: List with next occurrences.
        :rtype: list
        """
        occurrences = []
        adapter = IRecurrenceSupport(self.event_context, None)
        if adapter:
            for cnt, occ in enumerate(adapter.occurrences()):
                if cnt == self.max_occurrences:
                    break
                occurrences.append(occ)
        return occurrences
예제 #4
0
    def next_occurrences(self):
        """Returns occurrences for this context, except the start
        occurrence, limited to self.max_occurrence occurrences.

        :returns: List with next occurrences.
        :rtype: list
        """
        occurrences = []
        adapter = IRecurrenceSupport(self.event_context, None)
        if adapter:
            for cnt, occ in enumerate(adapter.occurrences(range_start=self.data.start)):
                if cnt == self.max_occurrences:
                    break
                occurrences.append(occ)
        return occurrences
예제 #5
0
    def dictFromObject(self, item, kwargs={}):
        eventPhysicalPath = '/'.join(item.getPhysicalPath())
        wft = getToolByName(self.context, 'portal_workflow')
        state = wft.getInfoFor(self.context, 'review_state')
        member = self.context.portal_membership.getAuthenticatedMember()
        if member.has_permission('Modify portal content', item):
            editable = True

        copycut = ''
        if self.copyDict and eventPhysicalPath == self.copyDict['url']:
            copycut = self.copyDict['op'] == 1 and ' event_cutted' \
                                             or ' event_copied'

        typeClass = ' type-' + item.portal_type
        colorDict = getColorIndex(self.context, self.request, eventPhysicalPath)
        colorIndex = colorDict.get('class', '')
        color = colorDict.get('color', '')
        extraClass = self.getObjectExtraClass(item)

        events = []
        if handle_recurrence(self.request) and IRecurrenceSupport(item, None):
            events = get_recurring_events(self.request, item)
        else:
            events = item
        return (dict_from_events(
            events,
            editable=editable,
            state=state,
            color=color,
            css=copycut + typeClass + colorIndex + extraClass
            ))
예제 #6
0
    def dictFromBrain(self, brain, editableEvents=[]):

        if brain.UID in editableEvents:
            editable = True
        else:
            editable = False

        copycut = ''
        if self.copyDict and brain.getPath() == self.copyDict['url']:
            copycut = self.copyDict['op'] == 1 and ' event_cutted' \
                                             or ' event_copied'
        typeClass = ' type-' + brain.portal_type
        colorDict = getColorIndex(self.context, self.request, brain=brain)
        colorIndex = colorDict.get('class', '')
        color = colorDict.get('color', '')
        extraClass = self.getBrainExtraClass(brain)

        events = []
        if handle_recurrence(self.request):
            event = brain.getObject()
            if IRecurrenceSupport(event, None):
                events = get_recurring_events(self.request, event)
            else:
                events = brain
        else:
            events = brain
        return (dict_from_events(
            events,
            editable=editable,
            state=brain.review_state,
            color=color,
            css=copycut + typeClass + colorIndex + extraClass
            ))
예제 #7
0
    def next_occurrences(self):
        """Returns occurrences for this context, except the start
        occurrence, limited to self.num_occurrences occurrences.

        :returns: List with next occurrences.
        :rtype: list
        """
        occurrences = []
        adapter = IRecurrenceSupport(self.context, None)
        if adapter:
            for cnt, occ in enumerate(adapter.occurrences()):
                if cnt == self.max_occurrences + 1:
                    break
                elif cnt == 0:
                    continue
                occurrences.append(occ)
        return occurrences
예제 #8
0
 def _recurrence_upcoming_event(self):
     """Return the next upcoming event"""
     adapter = IRecurrenceSupport(self.context)
     occs = adapter.occurrences(range_start=localized_now())
     try:
         return next(occs)
     except StopIteration:
         # No more future occurrences: passed event
         return IEventBasic(self.context)
예제 #9
0
    def next_occurrences(self):
        """Returns occurrences for this context, except the start
        occurrence, limited to self.max_occurrence occurrences.

        :returns: List with next occurrences.
        :rtype: list
        """
        occurrences = []
        adapter = IRecurrenceSupport(self.event_context, None)
        if adapter:
            cnt_future_occ = 0
            for occ in adapter.occurrences():
                if cnt_future_occ == self.max_occurrences:
                    break
                if occ.end >= datetime.utcnow().replace(tzinfo=pytz.utc):
                    cnt_future_occ += 1
                    occurrences.append(occ)
        return occurrences
예제 #10
0
 def create_ticket_occurrences(self):
     tickets = self.tickets
     recurrence = IRecurrenceSupport(self.context)
     for occurrence in recurrence.occurrences():
         if not isinstance(occurrence, Occurrence):
             continue
         for ticket in tickets:
             if occurrence.id in ticket.objectIds():
                 continue
             ticket.invokeFactory('Ticket Occurrence',
                                  occurrence.id,
                                  title=ticket.Title())
             ticket_occurrence = ticket[occurrence.id]
             self._copy_field_value(ticket, ticket_occurrence,
                                    'item_available')
             self._copy_field_value(ticket, ticket_occurrence,
                                    'item_overbook')
             ticket_occurrence.reindexObject()
예제 #11
0
    def next_occurrences(self):
        """Returns occurrences for this context, except the start
        occurrence, limited to self.max_occurrence occurrences.

        :returns: List with next occurrences.
        :rtype: list
        """
        occurrences = []
        adapter = IRecurrenceSupport(self.event_context, None)
        if adapter:
            cnt_future_occ = 0
            for occ in adapter.occurrences():
                if cnt_future_occ == self.max_occurrences:
                    break
                if occ.end >= datetime.utcnow().replace(tzinfo=pytz.utc):
                    cnt_future_occ += 1
                    occurrences.append(occ)
        return occurrences
예제 #12
0
    def test_recurrence_occurrences_with_range_start_2(self):
        # Test with range
        rs = datetime.datetime(2011, 11, 16, 11, 0, tzinfo=self.tz)
        result = IRecurrenceSupport(self.data).occurrences(range_start=rs)

        self.assertEqual(3, len(result))

        # Only IOccurrence objects in the result set
        self.assertTrue(IOccurrence.providedBy(result[0]))
예제 #13
0
    def occurrences(self):
        """Returns all occurrences for this context, except the start
        occurrence.
        The maximum defaults to 7 occurrences. If there are more occurrences
        defined for this context, the result will contain the last item
        of the occurrence list.

        :rtype: dict - with ``events`` and ``tail`` as keys.

        """
        eventsinfo = dict(events=[], tail=None)
        context = self.context
        adapter = IRecurrenceSupport(context, None)
        if adapter is not None:
            occurrences = adapter.occurrences()[1:] # don't include first
            eventsinfo['events'], eventsinfo['tail'] = (
                self._get_occurrences_helper(occurrences)
            )
        return eventsinfo
예제 #14
0
    def next_occurrences(self):
        """Returns all occurrences for this context, except the start
        occurrence.
        The maximum defaults to 7 occurrences. If there are more occurrences
        defined for this context, the result will contain the last item
        of the occurrence list.

        :returns: Dictionary with ``events`` and ``tail`` as keys.
        :rtype: dict

        """
        occ_dict = dict(events=[], tail=None)
        context = self.context
        adapter = IRecurrenceSupport(context, None)
        if adapter is not None:
            occurrences = adapter.occurrences()[1:]  # don't include first
            occ_dict['events'], occ_dict['tail'] = (
                self._get_occurrences_helper(occurrences))
        return occ_dict
예제 #15
0
 def create_ticket_occurrences(self):
     tickets = self.tickets
     recurrence = IRecurrenceSupport(self.context)
     for occurrence in recurrence.occurrences():
         if not isinstance(occurrence, Occurrence):
             continue
         for ticket in tickets:
             if occurrence.id in ticket.objectIds():
                 continue
             ticket.invokeFactory(
                 'Ticket Occurrence',
                 occurrence.id,
                 title=ticket.Title())
             ticket_occurrence = ticket[occurrence.id]
             self._copy_field_value(
                 ticket, ticket_occurrence, 'item_available')
             self._copy_field_value(
                 ticket, ticket_occurrence, 'item_overbook')
             ticket_occurrence.reindexObject()
예제 #16
0
    def test_recurrence_occurrences(self):
        result = IRecurrenceSupport(self.data).occurrences()

        self.assertEqual(4, len(result))

        # First occurrence is an IEvent object
        self.assertTrue(IEvent.providedBy(result[0]))

        # Subsequent ones are IOccurrence objects
        self.assertTrue(IOccurrence.providedBy(result[1]))
예제 #17
0
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
예제 #18
0
def get_recurring_events(request, event):
    if isinstance(event.start, datetime):
        tz = event.start.tzinfo
        start = datetime.fromtimestamp(request.get('start')).replace(tzinfo=tz)
        end = datetime.fromtimestamp(request.get('end')).replace(tzinfo=tz)
    else:
        start = pydt(DateTime(request.get('start')))
        end = pydt(DateTime(request.get('end')))
    events = IRecurrenceSupport(event).occurrences(range_start=start,
                                                   range_end=end)
    return events
예제 #19
0
    def test_recurrence_occurrences_with_range_start_1(self):
        # Test with range
        rs = datetime.datetime(2011, 11, 15, 11, 0, tzinfo=self.tz)
        result = IRecurrenceSupport(self.data).occurrences(range_start=rs)

        self.assertEqual(4, len(result))

        # First occurrence is an IEvent object
        self.assertTrue(IEvent.providedBy(result[0]))

        # Subsequent ones are IOccurrence objects
        self.assertTrue(IOccurrence.providedBy(result[1]))
예제 #20
0
    def test_recurrence_occurrences_with_range_start_and_end(self):
        # Test with range
        rs = datetime.datetime(2011, 11, 11, 11, 0, tzinfo=self.tz)
        re = datetime.datetime(2011, 11, 12, 11, 0, tzinfo=self.tz)
        result = IRecurrenceSupport(self.data).occurrences(range_start=rs,
                                                           range_end=re)
        result = list(result)  # cast generator to list

        self.assertEqual(2, len(result))

        # First occurrence is an IEvent object
        self.assertTrue(IEvent.providedBy(result[0]))

        # Subsequent ones are IOccurrence objects
        self.assertTrue(IOccurrence.providedBy(result[1]))
예제 #21
0
    def test_recurrence(self):
        tz = pytz.timezone('Europe/Vienna')
        duration = datetime.timedelta(days=4)
        data = MockEvent()
        data.start = datetime.datetime(2011, 11, 11, 11, 00, tzinfo=tz)
        data.end = data.start + duration
        data.recurrence = 'RRULE:FREQ=DAILY;COUNT=4'
        zope.interface.alsoProvides(data, IEvent, IEventRecurrence)
        result = IRecurrenceSupport(data).occurrences()

        self.assertEqual(4, len(result))

        # First occurrence is an IEvent object
        self.assertTrue(IEvent.providedBy(result[0]))

        # Subsequent ones are IOccurrence objects
        self.assertTrue(IOccurrence.providedBy(result[1]))
예제 #22
0
    def dictFromObject(self, item):
        eventPhysicalPath = '/'.join(item.getPhysicalPath())
        wft = getToolByName(self.context, 'portal_workflow')
        state = wft.getInfoFor(self.context, 'review_state')
        member = self.context.portal_membership.getAuthenticatedMember()
        if member.has_permission('Modify portal content', item):
            editable = True

        if item.end() - item.start() > 1.0:
            allday = True
        else:
            allday = False

        adapted = interfaces.ISFBaseEventFields(item, None)
        if adapted:
            allday = adapted.allDay

        copycut = ''
        if self.copyDict and eventPhysicalPath == self.copyDict['url']:
            copycut = self.copyDict['op'] == 1 and ' event_cutted' or ' event_copied'

        typeClass = ' type-' + item.portal_type
        colorIndex = getColorIndex(self.context, self.request, eventPhysicalPath)
        extraClass = self.getObjectExtraClass(item)
        if HAS_RECCURENCE_SUPPORT:
            occurences = IRecurrenceSupport(item).occurences()
        else:
            occurences = [(item.start().rfc822(), item.end().rfc822())]

        events = []
        for occurence_start, occurence_end in occurences:
            events.append({
                "status": "ok",
                "id": "UID_%s" % (item.UID()),
                "title": item.Title(),
                "description": item.Description(),
                "start": HAS_RECCURENCE_SUPPORT and occurence_start.isoformat() or occurence_start,
                "end": HAS_RECCURENCE_SUPPORT and occurence_end.isoformat() or occurence_end,
                "url": item.absolute_url(),
                "editable": editable,
                "allDay": allday,
                "className": "contextualContentMenuEnabled state-" + str(state) + (editable and " editable" or "")+copycut+typeClass+colorIndex+extraClass})

        return events
예제 #23
0
    def getEvents(self):
        context = self.context
        wft = getToolByName(context, 'portal_workflow')
        state = wft.getInfoFor(context, 'review_state')
        member = context.portal_membership.getAuthenticatedMember()
        editable = bool(member.has_permission('Modify portal content', context))
        extraClass = self.getObjectExtraClass()
        typeClass = ' type-' + context.portal_type

        events = []
        if handle_recurrence(self.request) and IRecurrenceSupport(context, None):
            events = get_recurring_events(self.request, context)
        else:
            events = context
        return (dict_from_events(
            events,
            editable=editable,
            state=state,
            css=typeClass + extraClass
            ))
예제 #24
0
    def test_recurrence(self):
        tz = pytz.timezone('Europe/Vienna')
        duration = timedelta(days=4)
        mock = MockEvent()
        mock.start = tz.localize(datetime(2011, 11, 11, 11, 0))
        mock.end = mock.start + duration
        mock.recurrence = 'RRULE:FREQ=DAILY;COUNT=4'
        zope.interface.alsoProvides(mock, IEvent, IEventBasic,
                                    IEventRecurrence, IDXEvent,
                                    IDXEventRecurrence)
        result = IRecurrenceSupport(mock).occurrences()
        result = list(result)  # cast generator to list

        self.assertEqual(4, len(result))

        # First occurrence is an IEvent object
        self.assertTrue(IEvent.providedBy(result[0]))

        # Subsequent ones are IOccurrence objects
        self.assertTrue(IOccurrence.providedBy(result[1]))
예제 #25
0
    def dictFromBrain(self, brain, editableEvents=[]):

        if brain.UID in editableEvents:
            editable = True
        else:
            editable = False

        if brain.end - brain.start > 1.0:
            allday = True
        else:
            allday = False

        if getattr(brain, 'SFAllDay', None) in [False,True]:
            allday = brain.SFAllDay

        copycut = ''
        if self.copyDict and brain.getPath() == self.copyDict['url']:
            copycut = self.copyDict['op'] == 1 and ' event_cutted' or ' event_copied'
        typeClass = ' type-'+brain.portal_type
        colorIndex = getColorIndex(self.context, self.request, brain=brain)
        extraClass = self.getBrainExtraClass(brain)
        if HAS_RECCURENCE_SUPPORT:
            occurences = IRecurrenceSupport(brain.getObject()).occurences()
        else:
            occurences = [(brain.start.rfc822(), brain.end.rfc822())]
        events = []
        for occurence_start, occurence_end in occurences:
            events.append({
                "id": "UID_%s" % (brain.UID),
                "title": brain.Title,
                "description": brain.Description,
                "start": HAS_RECCURENCE_SUPPORT and occurence_start.isoformat() or occurence_start,
                "end": HAS_RECCURENCE_SUPPORT and occurence_end.isoformat() or occurence_end,
                "url": brain.getURL(),
                "editable": editable,
                "allDay": allday,
                "className": "contextualContentMenuEnabled state-" + str(brain.review_state) + (editable and " editable" or "")+copycut+typeClass+colorIndex+extraClass})
        return events
예제 #26
0
    def __call__(self):
        event = self.context
        context = self.context
        referer = self.request.get('HTTP_REFERER')
        if referer:
            portal = getToolByName(self.context,
                                   'portal_url').getPortalObject()
            url = "/".join(portal.getPhysicalPath()) + referer.replace(
                portal.absolute_url(), '')
            context = portal.restrictedTraverse(url)
        eventPhysicalPath = '/'.join(event.getPhysicalPath())
        wft = getToolByName(context, 'portal_workflow')
        state = wft.getInfoFor(event, 'review_state')
        member = context.portal_membership.getAuthenticatedMember()
        editable = bool(member.has_permission('Modify portal content', event))

        copycut = ''
        if self.copyDict and eventPhysicalPath == self.copyDict['url']:
            copycut = self.copyDict['op'] == 1 and ' event_cutted' \
                                             or ' event_copied'

        typeClass = ' type-' + event.portal_type
        colorDict = getColorIndex(context, self.request, eventPhysicalPath)
        colorIndex = colorDict.get('class', '')
        color = colorDict.get('color', '')
        extraClass = self.getExtraClass()

        events = []
        if handle_recurrence(self.request) and IRecurrenceSupport(event, None):
            events = get_recurring_events(self.request, event)
        else:
            events = event
        return (dict_from_events(events,
                                 editable=editable,
                                 state=state,
                                 color=color,
                                 css=copycut + typeClass + colorIndex +
                                 extraClass))
예제 #27
0
파일: views.py 프로젝트: eea/eionet.theme
    def generate_source_dict_from_event(self, event):
        """generate_source_dict_from_event.

        :param event:
        """
        view = self.request.get('view')
        ret = []
        title = event.Title()
        description = event.Description()

        if event.text:
            description = event.text.output
        editable = api.user.has_permission('Modify portal content', obj=event)
        deletable = api.user.has_permission('Delete objects', obj=event)
        color = 'grey'
        if event.tag:
            for _view_type, group_color, categories in CATEGORIES:
                for cat_id, _cat_title in categories:
                    if cat_id in event.tag:
                        color = group_color
                        break
        else:
            # for events imported from ICS
            for _view_type, group_color, categories in CATEGORIES:
                for cat_id, _cat_tile in categories:
                    for tag in event.subject:
                        if cat_id == tag.lower():
                            color = group_color
                            break

        adapter = IRecurrenceSupport(event)
        # get all occurrences of the current event (if not recurrent,
        # the generator will only produce the event itself) and create a
        # results entry for each one
        for occurrence in adapter.occurrences(
                range_start=DateTime(self.request.get('start')),
                range_end=DateTime(self.request.get('end'))):
            # The default source marks an event as all day if it is longer than
            # one day. Marking an event as all day in contentpage will set
            # the times to 00:00 and 23:59. If those times are on the same
            # date they will not be recognised as all day because that's only a
            # 0.999.. day. This check will mark those events as all day.
            start = occurrence.start
            end = occurrence.end
            duration = occurrence.end - occurrence.start
            if isinstance(duration, timedelta):
                duration = duration.total_seconds() / 60. / 60. / 24.
            # compute real all day for the tooltip information
            real_allday = (event.whole_day or
                           duration > 0.99 or
                           start == end or
                           occurrence.start.date() != occurrence.end.date())
            # For the main calendar_view we set all events to allday because we
            # don't show start and end times anyway and we need the background
            # color that only appears on full day events.
            if view == 'calendar_view':
                allday = True
                end += timedelta(days=1)
            else:
                # on all other views we need the allday to be correct
                allday = real_allday
            iso = 'isoformat' if hasattr(start, 'isoformat') else 'ISO8601'
            start = getattr(start, iso)()
            end = getattr(end, iso)()

            ret.append({
                "id": "UID_%s" % (event.UID()),
                "title": title,
                "start": start,
                "end": end,
                "url": event.absolute_url(),
                "can_edit": editable,
                "can_delete": deletable,
                "backgroundColor": color,
                "allDay": allday,
                "realAllDay": real_allday,
                "className": "state-" + str(get_state(event)) +
                (editable and " editable" or ""),
                "description": description,
                "location": event.location,
                "realStartTime": occurrence.start.strftime('%H:%M'),
                "realEndTime": occurrence.end.strftime('%H:%M'),
                "realStartDate": occurrence.start.strftime('%B %d'),
                "realEndDate": occurrence.end.strftime('%B %d'),
                "oneday": occurrence.start.date() == occurrence.end.date()
            })
        return ret
예제 #28
0
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