Make CaldavStorage.list faster

This commit is contained in:
Markus Unterwaditzer 2014-04-12 14:42:08 +02:00
parent b5f42457bd
commit fadff19752
3 changed files with 48 additions and 34 deletions

View file

@ -15,18 +15,25 @@ take a look at `vfix <https://github.com/geier/vfix>`_.
Radicale Radicale
======== ========
vdirsyncer is tested against the git version and the latest PyPI release of Vdirsyncer is tested against the git version and the latest PyPI release of
Radicale. Radicale.
Radicale doesn't `support time ranges in the calendar-query of CalDAV - Radicale doesn't `support time ranges in the calendar-query of CalDAV
<https://github.com/Kozea/Radicale/issues/146>`_, so setting ``start_date`` and <https://github.com/Kozea/Radicale/issues/146>`_, so setting ``start_date``
``end_date`` for :py:class:`vdirsyncer.storage.CaldavStorage` will have no or and ``end_date`` for :py:class:`vdirsyncer.storage.CaldavStorage` will have
unpredicted consequences. no or unpredicted consequences.
- Versions of Radicale older than 0.9b1 don't support the necessary
functionality for efficient querying for all items of a collection.
Vdirsyncer's defaults are supposed to deal with this situation, but if you're
using :py:class:`vdirsyncer.storage.CaldavStorage` and set ``item_types`` to
an empty value (``item_types =``), these versions of Radicale will not work
properly.
ownCloud ownCloud
======== ========
vdirsyncer is tested against the latest version of ownCloud. Vdirsyncer is tested against the latest version of ownCloud.
ownCloud uses SabreDAV, which had problems detecting collisions and ownCloud uses SabreDAV, which had problems detecting collisions and
race-conditions. The problems were reported and are fixed in SabreDAV's repo. race-conditions. The problems were reported and are fixed in SabreDAV's repo.

View file

@ -111,11 +111,14 @@ class TestCaldavStorage(DavStorageTests):
('VTODO',), ('VTODO',),
('VEVENT',), ('VEVENT',),
('VTODO', 'VEVENT'), ('VTODO', 'VEVENT'),
('VTODO', 'VEVENT', 'VJOURNAL') ('VTODO', 'VEVENT', 'VJOURNAL'),
()
]) ])
def test_item_types_performance(self, item_types, monkeypatch): def test_item_types_performance(self, item_types, monkeypatch):
kw = self.get_storage_args() kw = self.get_storage_args()
s = self.storage_class(item_types=item_types, **kw) s = self.storage_class(item_types=item_types, **kw)
item = self._create_bogus_item()
href, etag = s.upload(item)
old_list = s._list old_list = s._list
calls = [] calls = []
@ -126,8 +129,10 @@ class TestCaldavStorage(DavStorageTests):
monkeypatch.setattr(s, '_list', _list) monkeypatch.setattr(s, '_list', _list)
list(s.list()) rv = list(s.list())
assert len(calls) == len(item_types) if dav_server != 'radicale' or item.parsed.name in s.item_types:
assert rv == [(href, etag)]
assert len(calls) == (len(item_types) or 1)
@pytest.mark.xfail(dav_server == 'radicale', @pytest.mark.xfail(dav_server == 'radicale',
reason='Radicale doesn\'t support timeranges.') reason='Radicale doesn\'t support timeranges.')

View file

@ -440,11 +440,8 @@ class CaldavStorage(DavStorage):
:param start_date: Start date of timerange to show, default -inf. :param start_date: Start date of timerange to show, default -inf.
:param end_date: End date of timerange to show, default +inf. :param end_date: End date of timerange to show, default +inf.
:param item_types: A tuple of collection types to show from the server. :param item_types: A tuple of collection types to show from the server.
For example, if you want to only get VEVENTs, pass ``('VEVENT',)``. For example, if you want to only get VEVENTs, pass ``VEVENT``.
Falsy values mean "get all types". Dependent on server Dependent on server functionality, no clientside validation of results.
functionality, no clientside validation of results. This currently
only affects the `list` method, but this shouldn't cause problems
in the normal usecase.
''' '''
storage_name = 'caldav' storage_name = 'caldav'
@ -469,7 +466,7 @@ class CaldavStorage(DavStorage):
get_multi_data_query = '{urn:ietf:params:xml:ns:caldav}calendar-data' get_multi_data_query = '{urn:ietf:params:xml:ns:caldav}calendar-data'
def __init__(self, start_date=None, end_date=None, def __init__(self, start_date=None, end_date=None,
item_types=(), **kwargs): item_types='VTODO, VEVENT', **kwargs):
super(CaldavStorage, self).__init__(**kwargs) super(CaldavStorage, self).__init__(**kwargs)
if isinstance(item_types, str): if isinstance(item_types, str):
item_types = [x.strip() for x in item_types.split(',')] item_types = [x.strip() for x in item_types.split(',')]
@ -489,29 +486,34 @@ class CaldavStorage(DavStorage):
@staticmethod @staticmethod
def _get_list_filters(components, start, end): def _get_list_filters(components, start, end):
if not components: if components:
components = ('VTODO', 'VEVENT') caldavfilter = '''
<C:comp-filter name="VCALENDAR">
caldavfilter = ''' <C:comp-filter name="{component}">
<C:comp-filter name="VCALENDAR"> {timefilter}
<C:comp-filter name="{component}"> </C:comp-filter>
{timefilter}
</C:comp-filter> </C:comp-filter>
</C:comp-filter> '''
'''
if start is not None and end is not None: if start is not None and end is not None:
start = start.strftime(CALDAV_DT_FORMAT) start = start.strftime(CALDAV_DT_FORMAT)
end = end.strftime(CALDAV_DT_FORMAT) end = end.strftime(CALDAV_DT_FORMAT)
timefilter = ('<C:time-range start="{start}" end="{end}"/>' timefilter = ('<C:time-range start="{start}" end="{end}"/>'
.format(start=start, end=end)) .format(start=start, end=end))
else:
timefilter = ''
for component in components:
yield caldavfilter.format(component=component,
timefilter=timefilter)
else: else:
timefilter = '' if start is not None and end is not None:
for x in CaldavStorage._get_list_filters(('VTODO', 'VEVENT'),
for component in components: start, end):
yield caldavfilter.format(component=component, yield x
timefilter=timefilter) else:
yield '<C:comp-filter name="VCALENDAR"/>'
def list(self): def list(self):
data = '''<?xml version="1.0" encoding="utf-8" ?> data = '''<?xml version="1.0" encoding="utf-8" ?>