Split recurring events properly

Previously we moved each VEVENT into its own Item, now we group by UID,
if one exists.
This commit is contained in:
Markus Unterwaditzer 2016-03-18 20:29:03 +01:00
parent b93bcf0ba8
commit 7ce0fb958f
3 changed files with 75 additions and 8 deletions

View file

@ -9,6 +9,12 @@ Package maintainers and users who have to manually update their installation
may want to subscribe to `GitHub's tag feed
<https://github.com/pimutils/vdirsyncer/tags.atom>`_.
Version 0.9.3
=============
- :storage:`singlefile` and :storage:`http` now handle recurring events
properly.
Version 0.9.2
=============

View file

@ -2,6 +2,8 @@
import random
import textwrap
from hypothesis import given
import hypothesis.strategies as st
@ -12,7 +14,7 @@ from vdirsyncer.storage.base import Item, normalize_meta_value
from vdirsyncer.utils.compat import iteritems, text_type, urlquote, urlunquote
from .. import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE, \
assert_item_equals, printable_characters_strategy
assert_item_equals, normalize_item, printable_characters_strategy
def get_server_mixin(server_name):
@ -285,3 +287,49 @@ class StorageTests(object):
# ownCloud replaces "" with "unnamed"
s.set_meta('displayname', value)
assert s.get_meta('displayname') == normalize_meta_value(value)
def test_recurring_events(self, s, item_type):
if item_type != 'VEVENT':
pytest.skip('This storage instance doesn\'t support iCalendar.')
uid = u'abc123'
item = Item(textwrap.dedent(u'''
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART;TZID=Australia/Sydney:20140325T084000
DTEND;TZID=Australia/Sydney:20140325T101000
DTSTAMP:20140327T060506Z
UID:{uid}
RECURRENCE-ID;TZID=Australia/Sydney:20140325T083000
CREATED:20131216T033331Z
DESCRIPTION:
LAST-MODIFIED:20140327T060215Z
LOCATION:
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:test Event
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Australia/Sydney:20140128T083000
DTEND;TZID=Australia/Sydney:20140128T100000
RRULE:FREQ=WEEKLY;UNTIL=20141208T213000Z;BYDAY=TU
DTSTAMP:20140327T060506Z
UID:{uid}
CREATED:20131216T033331Z
DESCRIPTION:
LAST-MODIFIED:20140222T101012Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Test event
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
'''.format(uid=uid)).strip())
href, etag = s.upload(item)
item2, etag2 = s.get(href)
assert normalize_item(item) == normalize_item(item2)

View file

@ -4,7 +4,7 @@ import hashlib
from itertools import chain, tee
from . import cached_property, uniq
from .compat import text_type
from .compat import itervalues, text_type
def _process_properties(*s):
@ -114,17 +114,24 @@ def hash_item(text):
def split_collection(text):
assert isinstance(text, text_type)
inline = []
items = []
items = {} # uid => item
ungrouped_items = []
def inner(item, main):
if item.name == u'VTIMEZONE':
inline.append(item)
elif item.name == u'VCARD':
items.append(item)
ungrouped_items.append(item)
elif item.name in (u'VTODO', u'VEVENT', u'VJOURNAL'):
items.append(_Component(main.name,
main.props[:],
[item]))
uid = item.get(u'UID', u'')
wrapper = _Component(main.name, main.props[:], [])
if uid.strip():
wrapper = items.setdefault(uid, wrapper)
else:
ungrouped_items.append(wrapper)
wrapper.subcomponents.append(item)
elif item.name in (u'VCALENDAR', u'VADDRESSBOOK'):
for subitem in item.subcomponents:
inner(subitem, item)
@ -135,7 +142,7 @@ def split_collection(text):
for main in _Component.parse(text, multiple=True):
inner(main, main)
for item in items:
for item in chain(itervalues(items), ungrouped_items):
item.subcomponents.extend(inline)
yield u'\r\n'.join(item.dump_lines())
@ -316,3 +323,9 @@ class _Component(object):
break
return rv
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default