diff --git a/caldav/objects.py b/caldav/objects.py index 13cc740..d8c7cbf 100644 --- a/caldav/objects.py +++ b/caldav/objects.py @@ -241,6 +241,7 @@ def _query( expected_return_value is not None and ret.status != expected_return_value ) or ret.status >= 400: ## COMPATIBILITY HACK - see https://github.com/python-caldav/caldav/issues/309 + ## TODO: server quirks! body = to_wire(body) if ( ret.status == 500 @@ -946,7 +947,11 @@ def save(self): self._create(id=self.id, name=self.name, **self.extra_init_options) return self - def calendar_multiget(self, event_urls: Iterable[URL]) -> List["Event"]: + ## TODO: this is missing test code. + ## TODO: needs refactoring: + ## Objects found may be Todo and Journal, not only Event. + ## Replace the last lines with _request_report_build_resultlist method + def calendar_multiget(self, event_urls: Iterable[URL]) -> List[_CC]: """ get multiple events' data @author mtorange@gmail.com @@ -1078,15 +1083,15 @@ def date_search( return objects + ## TODO: this logic has been partly duplicated in calendar_multiget, but + ## the code there is much more readable and condensed than this. + ## Can code below be refactored? def _request_report_build_resultlist( self, xml, comp_class=None, props=None, no_calendardata=False ): """ Takes some input XML, does a report query on a calendar object and returns the resource objects found. - - TODO: similar code is duplicated many places, we ought to do even more code - refactoring """ matches = [] if props is None: @@ -1122,7 +1127,6 @@ def _request_report_build_resultlist( props=pdata, ) ) - return (response, matches) def search( @@ -1163,7 +1167,7 @@ def search( unless the next parameter is set ... * include_completed - include completed tasks * event - sets comp_class to event - * text attribute search parameters: category, uid, summary, omment, + * text attribute search parameters: category, uid, summary, comment, description, location, status * no-category, no-summary, etc ... search for objects that does not have those attributes. TODO: WRITE TEST CODE! @@ -2338,6 +2342,8 @@ def copy(self, keep_uid: bool = False, new_parent: Optional[Any] = None) -> Self obj.url = self.url return obj + ## TODO: move get-logics to a load_by_get method. + ## The load method should deal with "server quirks". def load(self, only_if_unloaded: bool = False) -> Self: """ (Re)load the object from the caldav server. @@ -2351,7 +2357,10 @@ def load(self, only_if_unloaded: bool = False) -> Self: if self.client is None: raise ValueError("Unexpected value None for self.client") - r = self.client.request(str(self.url)) + try: + r = self.client.request(str(self.url)) + except: + return self.load_by_multiget() if r.status == 404: raise error.NotFoundError(errmsg(r)) self.data = vcal.fix(r.raw) @@ -2361,6 +2370,23 @@ def load(self, only_if_unloaded: bool = False) -> Self: self.props[cdav.ScheduleTag.tag] = r.headers["Schedule-Tag"] return self + def load_by_multiget(self) -> Self: + """ + Some servers do not accept a GET, but we can still do a REPORT + with a multiget query + """ + error.assert_(self.url) + href = self.url.path + prop = dav.Prop() + cdav.CalendarData() + root = cdav.CalendarMultiGet() + prop + dav.Href(value=href) + response = self.parent._query(root, 1, "report") + results = response.expand_simple_props([cdav.CalendarData()]) + error.assert_(len(results) == 1) + data = results[href][cdav.CalendarData.tag] + error.assert_(data) + self.data = data + return self + ## TODO: self.id should either always be available or never def _find_id_path(self, id=None, path=None) -> None: """