Example #1
0
    def todos(self, sort_key='due', include_completed=False):
        """
        fetches a list of todo events.

        Parameters:
         * sort_key: use this field in the VTODO for sorting (lower case string, i.e. 'priority').
         * include_completed: boolean - by default, only pending tasks are listed
        """
        ## ref https://www.ietf.org/rfc/rfc4791.txt, section 7.8.9
        matches = []

        # build the request
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        if not include_completed:
            vnotcompleted = cdav.TextMatch('COMPLETED', negate=True)
            vnotcancelled = cdav.TextMatch('CANCELLED', negate=True)
            vstatus = cdav.PropFilter('STATUS') + vnotcancelled + vnotcompleted
            vnocompletedate = cdav.PropFilter('COMPLETED') + cdav.NotDefined()
            vtodo = cdav.CompFilter("VTODO") + vnocompletedate + vstatus
        else:
            vtodo = cdav.CompFilter("VTODO")
        vcalendar = cdav.CompFilter("VCALENDAR") + vtodo
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')
        results = self._handle_prop_response(response=response,
                                             props=[cdav.CalendarData()])
        for r in results:
            matches.append(
                Todo(self.client,
                     url=self.url.join(r),
                     data=results[r][cdav.CalendarData.tag],
                     parent=self))

        def sort_key_func(x):
            val = getattr(x.instance.vtodo, sort_key, None)
            if not val:
                return None
            val = val.value
            if hasattr(val, 'strftime'):
                return val.strftime('%F%H%M%S')
            return val

        if sort_key:
            matches.sort(key=sort_key_func)
        return matches
Example #2
0
    def todos(self,
              sort_keys=('due', 'priority'),
              include_completed=False,
              sort_key=None):
        """
        fetches a list of todo events.

        Parameters:
         * sort_keys: use this field in the VTODO for sorting (iterable of
           lower case string, i.e. ('priority','due')).
         * include_completed: boolean -
           by default, only pending tasks are listed
         * sort_key: DEPRECATED, for backwards compatibility with version 0.4.
        """
        if sort_key:
            sort_keys = (sort_key, )

        if not include_completed:
            vnotcompleted = cdav.TextMatch('COMPLETED', negate=True)
            vnotcancelled = cdav.TextMatch('CANCELLED', negate=True)
            vstatusNotCompleted = cdav.PropFilter('STATUS') + vnotcompleted
            vstatusNotCancelled = cdav.PropFilter('STATUS') + vnotcancelled
            vstatusNotDefined = cdav.PropFilter('STATUS') + cdav.NotDefined()
            vnocompletedate = cdav.PropFilter('COMPLETED') + cdav.NotDefined()
            filters1 = (cdav.CompFilter("VTODO") + vnocompletedate +
                        vstatusNotCompleted + vstatusNotCancelled)
            ## This query is quite much in line with https://tools.ietf.org/html/rfc4791#section-7.8.9
            matches1 = self._fetch_todos(filters1)
            ## However ... some server implementations (i.e. NextCloud
            ## and Baikal) will yield "false" on a negated TextMatch
            ## if the field is not defined.  Hence, for those
            ## implementations we need to turn back and ask again
            ## ... do you have any VTODOs for us where the STATUS
            ## field is not defined? (ref
            ## https://github.com/python-caldav/caldav/issues/14)
            filters2 = (cdav.CompFilter("VTODO") + vnocompletedate +
                        vstatusNotDefined)
            matches2 = self._fetch_todos(filters2)

            ## For most caldav servers, everything in matches2 already exists
            ## in matches1.  We need to make a union ...
            match_set = set()
            matches = []
            for todo in matches1 + matches2:
                if not str(todo.url) in match_set:
                    match_set.add(str(todo.url))
                    ## and still, Zimbra seems to deliver too many TODOs on the
                    ## filter2 ... let's do some post-filtering in case the
                    ## server fails in filtering things the right way
                    if (not '\nCOMPLETED:' in todo.data
                            and not '\nSTATUS:COMPLETED' in todo.data
                            and not '\nSTATUS:CANCELLED' in todo.data):
                        matches.append(todo)

        else:
            filters = cdav.CompFilter("VTODO")
            matches = self._fetch_todos(filters)

        def sort_key_func(x):
            ret = []
            vtodo = x.instance.vtodo
            defaults = {
                'due':
                '2050-01-01',
                'dtstart':
                '1970-01-01',
                'priority':
                '0',
                # JA: why compare datetime.strftime('%F%H%M%S')
                # JA: and not simply datetime?

                # tobixen: probably it was made like this because we can get
                # both dates and timestamps from the objects.
                # Python will yield an exception if trying to compare
                # a timestamp with a date.
                'isnt_overdue':
                not (hasattr(vtodo, 'due')
                     and vtodo.due.value.strftime('%F%H%M%S') <
                     datetime.now().strftime('%F%H%M%S')),
                'hasnt_started': (hasattr(vtodo, 'dtstart')
                                  and vtodo.dtstart.value.strftime('%F%H%M%S')
                                  > datetime.now().strftime('%F%H%M%S'))
            }
            for sort_key in sort_keys:
                val = getattr(vtodo, sort_key, None)
                if val is None:
                    ret.append(defaults.get(sort_key, '0'))
                    continue
                val = val.value
                if hasattr(val, 'strftime'):
                    ret.append(val.strftime('%F%H%M%S'))
                else:
                    ret.append(val)
            return ret

        if sort_keys:
            matches.sort(key=sort_key_func)
        return matches
Example #3
0
    def todos(self,
              sort_keys=('due', 'priority'),
              include_completed=False,
              sort_key=None):
        """
        fetches a list of todo events.

        Parameters:
         * sort_keys: use this field in the VTODO for sorting (iterable of
           lower case string, i.e. ('priority','due')).
         * include_completed: boolean -
           by default, only pending tasks are listed
         * sort_key: DEPRECATED, for backwards compatibility with version 0.4.
        """
        # ref https://www.ietf.org/rfc/rfc4791.txt, section 7.8.9
        matches = []

        # build the request
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        if sort_key:
            sort_keys = (sort_key, )

        if not include_completed:
            vnotcompleted = cdav.TextMatch('COMPLETED', negate=True)
            vnotcancelled = cdav.TextMatch('CANCELLED', negate=True)
            vstatusNotCompleted = cdav.PropFilter(
                'STATUS') + vnotcompleted + cdav.NotDefined()
            vstatusNotCancelled = cdav.PropFilter(
                'STATUS') + vnotcancelled + cdav.NotDefined()
            vnocompletedate = cdav.PropFilter('COMPLETED') + cdav.NotDefined()
            vtodo = (cdav.CompFilter("VTODO") + vnocompletedate +
                     vstatusNotCompleted + vstatusNotCancelled)
        else:
            vtodo = cdav.CompFilter("VTODO")
        vcalendar = cdav.CompFilter("VCALENDAR") + vtodo
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')
        results = self._handle_prop_response(response=response,
                                             props=[cdav.CalendarData()])
        for r in results:
            matches.append(
                Todo(self.client,
                     url=self.url.join(r),
                     data=results[r][cdav.CalendarData.tag],
                     parent=self))

        def sort_key_func(x):
            ret = []
            vtodo = x.instance.vtodo
            defaults = {
                'due':
                '2050-01-01',
                'dtstart':
                '1970-01-01',
                'priority':
                '0',
                # JA: why compare datetime.strftime('%F%H%M%S')
                # JA: and not simply datetime?
                'isnt_overdue':
                not (hasattr(vtodo, 'due')
                     and vtodo.due.value.strftime('%F%H%M%S') <
                     datetime.datetime.now().strftime('%F%H%M%S')),
                'hasnt_started':
                (hasattr(vtodo, 'dtstart')
                 and vtodo.dtstart.value.strftime('%F%H%M%S') >
                 datetime.datetime.now().strftime('%F%H%M%S'))
            }
            for sort_key in sort_keys:
                val = getattr(vtodo, sort_key, None)
                if val is None:
                    ret.append(defaults.get(sort_key, '0'))
                    continue
                val = val.value
                if hasattr(val, 'strftime'):
                    ret.append(val.strftime('%F%H%M%S'))
                else:
                    ret.append(val)
            return ret

        if sort_keys:
            matches.sort(key=sort_key_func)
        return matches