async def availabilities(self): """ List all availabilities from the calendar. Returns: * [Availability(), ...] """ # TODO: this is basically a copy of events() - can we do more # refactoring and consolidation here? Maybe it's wrong to do # separate methods for journals, todos and events? all = [] data = cdav.CalendarData() prop = dav.Prop() + data vevent = cdav.CompFilter("VAVAILABILITY") vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = await self._query(root, 1, query_method='report') results = self._handle_prop_response(response, props=[cdav.CalendarData()]) for r in results: all.append( Availability(self.client, url=self.url.join(r), data=results[r][cdav.CalendarData.tag], parent=self)) return all
async def events(self): """ List all events from the calendar. Returns: * [Event(), ...] """ all = [] data = cdav.CalendarData() prop = dav.Prop() + data vevent = cdav.CompFilter("VEVENT") vcalendar = cdav.CompFilter("VCALENDAR") + vevent filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = await self._query(root, 1, query_method='report') results = self._handle_prop_response(response, props=[cdav.CalendarData()]) for r in results: all.append( Event(self.client, url=self.url.join(r), data=results[r][cdav.CalendarData.tag], parent=self)) return all
def test_filter_1(): """Should not raise an Error.""" cdav.Filter().append( cdav.CompFilter("VCALENDAR").append( cdav.CompFilter("VEVENT").append( cdav.PropFilter("UID").append( [cdav.TextMatch("pouet", negate=True)])))) assert True
async def date_search(self, start, end=None, compfilter="VEVENT"): """ Search events by date in the calendar. Recurring events are expanded if they are occuring during the specified time frame and if an end timestamp is given. Parameters: * start = datetime.today(). * end = same as above. * compfilter = defaults to events only. Set to None to fetch all calendar components. Returns: * [CalendarObjectResource(), ...] """ matches = [] # build the request # Some servers will raise an error if we send the expand flag # but don't set any end-date - expand doesn't make much sense # if we have one recurring event describing an indefinite # series of events. Hence, if the end date is not set, we # skip asking for expanded events. start = date_to_utc(start) if end: end = date_to_utc(end) data = cdav.CalendarData() + cdav.Expand(start, end) else: data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TimeRange(start, end) if compfilter: query = cdav.CompFilter(compfilter) + query vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = await self._query(root, 1, 'report') results = self._handle_prop_response(response=response, props=[cdav.CalendarData()]) for r in results: matches.append( Event(self.client, url=self.url.join(r), data=results[r][cdav.CalendarData.tag], parent=self)) return matches
async def object_by_uid(self, uid, comp_filter=None): """ Get one event from the calendar. Parameters: * uid: the event uid Returns: * Event() or None """ data = cdav.CalendarData() prop = dav.Prop() + data query = cdav.TextMatch(uid) query = cdav.PropFilter("UID") + query if comp_filter: query = comp_filter + query vcalendar = cdav.CompFilter("VCALENDAR") + query filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = await self._query(root, 1, 'report') if response.status == 404: raise error.NotFoundError(errmsg(response)) elif response.status == 400: raise error.ReportError(errmsg(response)) items_found = response.tree.findall(".//" + dav.Response.tag) for r in items_found: href = unquote(r.find(".//" + dav.Href.tag).text) data = unquote(r.find(".//" + cdav.CalendarData.tag).text) # Ref Lucas Verney, we've actually done a substring search, if the # uid given in the query is short (i.e. just "0") we're likely to # get false positives back from the server. # # Long uids are folded, so splice the lines together here before # attempting a match. item_uid = re.search(r'\nUID:((.|\n[ \t])*)\n', data) if (not item_uid or re.sub(r'\n[ \t]', '', item_uid.group(1)) != uid): continue return self._calendar_comp_class_by_data(data)( self.client, url=URL.objectify(href), data=data, parent=self) raise error.NotFoundError(errmsg(response))
async 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: # == QUERY 1 # vnotcompleted = cdav.TextMatch('COMPLETED', negate=True) vnotcancelled = cdav.TextMatch('CANCELLED', negate=True) # vstatusNotCompleted = cdav.PropFilter( # 'STATUS') + vnotcompleted vstatusNotCancelled = cdav.PropFilter('STATUS') + vnotcancelled vnocompletedate = cdav.PropFilter('COMPLETED') + cdav.NotDefined() # vnocanceldate = cdav.PropFilter('CANCELLED') + cdav.NotDefined() vtodo = (cdav.CompFilter("VTODO") + vnocompletedate + vstatusNotCancelled) # vtodo2 = (cdav.CompFilter("VTODO") + vnostatus) vcalendar = cdav.CompFilter("VCALENDAR") + vtodo # + vtodo2 filter1 = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter1] response = await 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)) # == QUERY 2 == Add all TODO without status vnostatus = cdav.PropFilter('STATUS') + cdav.NotDefined() vtodo2 = (cdav.CompFilter("VTODO") + vnocompletedate + vnostatus) vcalendar2 = cdav.CompFilter("VCALENDAR") + vtodo2 filter2 = cdav.Filter() + vcalendar2 root2 = cdav.CalendarQuery() + [prop, filter2] response2 = await self._query(root2, 1, 'report') results2 = self._handle_prop_response(response=response2, props=[cdav.CalendarData()]) for r in results2: matches.append( Todo(self.client, url=self.url.join(r), data=results2[r][cdav.CalendarData.tag], parent=self)) else: vtodo = cdav.CompFilter("VTODO") vcalendar = cdav.CompFilter("VCALENDAR") + vtodo filter = cdav.Filter() + vcalendar root = cdav.CalendarQuery() + [prop, filter] response = await 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