mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
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:
parent
b93bcf0ba8
commit
7ce0fb958f
3 changed files with 75 additions and 8 deletions
|
|
@ -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
|
||||
=============
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue