Implement time-range filters for events
This commit is contained in:
parent
656680d998
commit
248fc7e9e3
@ -29,6 +29,7 @@ import posixpath
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from urllib.parse import unquote, urlparse
|
||||
|
||||
import vobject
|
||||
@ -146,9 +147,9 @@ def _comp_match(item, filter_, scope="collection"):
|
||||
return filter_.get("name") != tag
|
||||
if filter_[0].tag == _tag("C", "time-range"):
|
||||
# Point #3 of rfc4791-9.7.1
|
||||
if not _time_range_match(item, filter_):
|
||||
if not _time_range_match(item.item, filter_[0], tag):
|
||||
return False
|
||||
filter_.remove(filter_[0])
|
||||
filter_ = filter_[1:]
|
||||
# Point #4 of rfc4791-9.7.1
|
||||
return all(
|
||||
_prop_match(item, child) if child.tag == _tag("C", "prop-filter")
|
||||
@ -180,26 +181,81 @@ def _prop_match(item, filter_):
|
||||
return name not in vobject_item.contents
|
||||
if filter_[0].tag == _tag("C", "time-range"):
|
||||
# Point #3 of rfc4791-9.7.2
|
||||
if not _time_range_match(item, filter_[0]):
|
||||
if not _time_range_match(vobject_item, filter_[0], name):
|
||||
return False
|
||||
filter_.remove(filter_[0])
|
||||
filter_ = filter_[1:]
|
||||
elif filter_[0].tag == _tag("C", "text-match"):
|
||||
# Point #4 of rfc4791-9.7.2
|
||||
if not _text_match(vobject_item, filter_[0], name):
|
||||
return False
|
||||
filter_.remove(filter_[0])
|
||||
filter_ = filter_[1:]
|
||||
return all(
|
||||
_param_filter_match(vobject_item, param_filter, name)
|
||||
for param_filter in filter_)
|
||||
|
||||
|
||||
def _time_range_match(item, filter_):
|
||||
def _time_range_match(vobject_item, filter_, child_name):
|
||||
"""Check whether the ``item`` matches the time-range ``filter_``.
|
||||
|
||||
See rfc4791-9.9.
|
||||
|
||||
"""
|
||||
# TODO: implement this
|
||||
start = filter_.get("start")
|
||||
end = filter_.get("end")
|
||||
if not start and not end:
|
||||
return False
|
||||
if start:
|
||||
start = datetime.strptime(start, "%Y%m%dT%H%M%SZ")
|
||||
else:
|
||||
start = datetime.datetime.min
|
||||
if end:
|
||||
end = datetime.strptime(end, "%Y%m%dT%H%M%SZ")
|
||||
else:
|
||||
end = datetime.datetime.max
|
||||
start = start.replace(tzinfo=timezone.utc)
|
||||
end = end.replace(tzinfo=timezone.utc)
|
||||
child = getattr(vobject_item, child_name.lower())
|
||||
|
||||
# Comments give the lines in the tables of the specification
|
||||
if child_name == "VEVENT":
|
||||
# TODO: check if there's a timezone
|
||||
dtstart = child.dtstart.value
|
||||
if not isinstance(dtstart, datetime):
|
||||
dtstart_is_datetime = False
|
||||
# TODO: changing dates to datetimes may be wrong because of tz
|
||||
dtstart = datetime.combine(dtstart, datetime.min.time()).replace(
|
||||
tzinfo=timezone.utc)
|
||||
else:
|
||||
dtstart_is_datetime = True
|
||||
dtend = getattr(child, "dtend", None)
|
||||
duration = getattr(child, "duration", None)
|
||||
if dtend is not None:
|
||||
# Line 1
|
||||
dtend = dtend.value
|
||||
if not isinstance(dtend, datetime):
|
||||
dtend = datetime.combine(dtend, datetime.min.time()).replace(
|
||||
tzinfo=timezone.utc)
|
||||
return start < dtend and end > dtstart
|
||||
elif duration is not None:
|
||||
duration = duration.value
|
||||
if duration.seconds > 0:
|
||||
# Line 2
|
||||
return start < dtstart + duration and end > dtstart
|
||||
else:
|
||||
# Line 3
|
||||
return start <= dtstart and end > dtstart
|
||||
elif dtstart_is_datetime:
|
||||
# Line 4
|
||||
return start <= dtstart and end > dtstart
|
||||
else:
|
||||
# Line 5
|
||||
return start < dtstart + timedelta(days=1) and end > dtstart
|
||||
elif child_name == "VTODO":
|
||||
# TODO: implement this
|
||||
pass
|
||||
elif child_name == "VJOURNAL":
|
||||
# TODO: implement this
|
||||
pass
|
||||
return True
|
||||
|
||||
|
||||
@ -491,11 +547,11 @@ def _propfind_response(path, item, props, user, write=False):
|
||||
for href, _ in item.list():
|
||||
event = item.get(href)
|
||||
if "vtimezone" in event.contents:
|
||||
for timezone in event.vtimezone_list:
|
||||
timezones.add(timezone)
|
||||
for timezone_ in event.vtimezone_list:
|
||||
timezones.add(timezone_)
|
||||
collection = vobject.iCalendar()
|
||||
for timezone in timezones:
|
||||
collection.add(timezone)
|
||||
for timezone_ in timezones:
|
||||
collection.add(timezone_)
|
||||
element.text = collection.serialize()
|
||||
elif tag == _tag("D", "displayname"):
|
||||
element.text = item.get_meta("D:displayname") or item.path
|
||||
|
@ -23,12 +23,12 @@ BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event
|
||||
UID:event1
|
||||
SUMMARY:Event
|
||||
ORGANIZER:mailto:unclesam@example.com
|
||||
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Jane Doe:MAILTO:janedoe@example.com
|
||||
ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="MAILTO:bob@host.com";PARTSTAT=ACCEPTED;CN=John Doe:MAILTO:johndoe@example.com
|
||||
DTSTART;TZID=Europe/Paris:20130902T180000
|
||||
DTEND;TZID=Europe/Paris:20130902T190000
|
||||
DTSTART;TZID=Europe/Paris:20130901T180000
|
||||
DTEND;TZID=Europe/Paris:20130901T190000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
31
tests/static/event2.ics
Normal file
31
tests/static/event2.ics
Normal file
@ -0,0 +1,31 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event2
|
||||
SUMMARY:Event2
|
||||
DTSTART;TZID=Europe/Paris:20130902T180000
|
||||
DTEND;TZID=Europe/Paris:20130902T190000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
31
tests/static/event3.ics
Normal file
31
tests/static/event3.ics
Normal file
@ -0,0 +1,31 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event3
|
||||
SUMMARY:Event3
|
||||
DTSTART;TZID=Europe/Paris:20130903
|
||||
DURATION:PT1H
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
30
tests/static/event4.ics
Normal file
30
tests/static/event4.ics
Normal file
@ -0,0 +1,30 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event4
|
||||
SUMMARY:Event4
|
||||
DTSTART;TZID=Europe/Paris:20130904T180000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
30
tests/static/event5.ics
Normal file
30
tests/static/event5.ics
Normal file
@ -0,0 +1,30 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event5
|
||||
SUMMARY:Event5
|
||||
DTSTART;TZID=Europe/Paris:20130905
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -54,8 +54,8 @@ class BaseRequests:
|
||||
"""Add an event."""
|
||||
self.request(
|
||||
"PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
|
||||
event = get_file_content("event.ics")
|
||||
path = "/calendar.ics/event.ics"
|
||||
event = get_file_content("event1.ics")
|
||||
path = "/calendar.ics/event1.ics"
|
||||
status, headers, answer = self.request("PUT", path, event)
|
||||
assert status == 201
|
||||
status, headers, answer = self.request("GET", path)
|
||||
@ -83,8 +83,8 @@ class BaseRequests:
|
||||
"""Delete an event."""
|
||||
self.request(
|
||||
"PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
|
||||
event = get_file_content("event.ics")
|
||||
path = "/calendar.ics/event.ics"
|
||||
event = get_file_content("event1.ics")
|
||||
path = "/calendar.ics/event1.ics"
|
||||
status, headers, answer = self.request("PUT", path, event)
|
||||
# Then we send a DELETE request
|
||||
status, headers, answer = self.request("DELETE", path)
|
||||
@ -93,13 +93,14 @@ class BaseRequests:
|
||||
status, headers, answer = self.request("GET", "/calendar.ics/")
|
||||
assert "VEVENT" not in answer
|
||||
|
||||
def _test_filter(self, filters):
|
||||
def _test_filter(self, filters, events=1):
|
||||
filters_text = "".join(
|
||||
"<C:filter>%s</C:filter>" % filter_ for filter_ in filters)
|
||||
self.request(
|
||||
"PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
|
||||
event = get_file_content("event.ics")
|
||||
self.request("PUT", "/calendar.ics/event.ics", event)
|
||||
for i in range(events):
|
||||
event = get_file_content("event%i.ics" % (i + 1))
|
||||
self.request("PUT", "/calendar.ics/event%i.ics" % (i + 1), event)
|
||||
status, headers, answer = self.request(
|
||||
"REPORT", "/calendar.ics",
|
||||
"""<?xml version="1.0" encoding="utf-8" ?>
|
||||
@ -113,29 +114,29 @@ class BaseRequests:
|
||||
|
||||
def test_calendar_tag_filter(self):
|
||||
"""Report request with tag-based filter on calendar."""
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR"></C:comp-filter>"""])
|
||||
|
||||
def test_item_tag_filter(self):
|
||||
"""Report request with tag-based filter on an item."""
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT"></C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VTODO"></C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
|
||||
def test_item_not_tag_filter(self):
|
||||
"""Report request with tag-based is-not filter on an item."""
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:is-not-defined />
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VTODO">
|
||||
<C:is-not-defined />
|
||||
@ -144,13 +145,13 @@ class BaseRequests:
|
||||
|
||||
def test_item_prop_filter(self):
|
||||
"""Report request with prop-based filter on an item."""
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="UNKNOWN"></C:prop-filter>
|
||||
@ -159,7 +160,7 @@ class BaseRequests:
|
||||
|
||||
def test_item_not_prop_filter(self):
|
||||
"""Report request with prop-based is-not filter on an item."""
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY">
|
||||
@ -167,7 +168,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="UNKNOWN">
|
||||
@ -178,7 +179,7 @@ class BaseRequests:
|
||||
|
||||
def test_mutiple_filters(self):
|
||||
"""Report request with multiple filters on an item."""
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY">
|
||||
@ -193,7 +194,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||
@ -206,7 +207,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY"></C:prop-filter>
|
||||
@ -218,7 +219,7 @@ class BaseRequests:
|
||||
|
||||
def test_text_match_filter(self):
|
||||
"""Report request with text-match filter on calendar."""
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY">
|
||||
@ -226,7 +227,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="UNKNOWN">
|
||||
@ -234,7 +235,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY">
|
||||
@ -242,7 +243,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="SUMMARY">
|
||||
@ -253,7 +254,7 @@ class BaseRequests:
|
||||
|
||||
def test_param_filter(self):
|
||||
"""Report request with param-filter on calendar."""
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="ATTENDEE">
|
||||
@ -264,7 +265,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="ATTENDEE">
|
||||
@ -275,7 +276,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" not in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="ATTENDEE">
|
||||
@ -285,7 +286,7 @@ class BaseRequests:
|
||||
</C:prop-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
assert "href>/calendar.ics/event.ics</" in self._test_filter(["""
|
||||
assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="ATTENDEE">
|
||||
@ -296,6 +297,80 @@ class BaseRequests:
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""])
|
||||
|
||||
def test_time_range_filter(self):
|
||||
"""Report request with time-range filter on calendar."""
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" in answer
|
||||
assert "href>/calendar.ics/event2.ics</" in answer
|
||||
assert "href>/calendar.ics/event3.ics</" in answer
|
||||
assert "href>/calendar.ics/event4.ics</" in answer
|
||||
assert "href>/calendar.ics/event5.ics</" in answer
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:prop-filter name="ATTENDEE">
|
||||
<C:param-filter name="PARTSTAT">
|
||||
<C:is-not-defined />
|
||||
</C:param-filter>
|
||||
</C:prop-filter>
|
||||
<C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" not in answer
|
||||
assert "href>/calendar.ics/event2.ics</" not in answer
|
||||
assert "href>/calendar.ics/event3.ics</" not in answer
|
||||
assert "href>/calendar.ics/event4.ics</" not in answer
|
||||
assert "href>/calendar.ics/event5.ics</" not in answer
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20130902T000000Z" end="20131001T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" not in answer
|
||||
assert "href>/calendar.ics/event2.ics</" in answer
|
||||
assert "href>/calendar.ics/event3.ics</" in answer
|
||||
assert "href>/calendar.ics/event4.ics</" in answer
|
||||
assert "href>/calendar.ics/event5.ics</" in answer
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20130903T000000Z" end="20131001T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" not in answer
|
||||
assert "href>/calendar.ics/event2.ics</" not in answer
|
||||
assert "href>/calendar.ics/event3.ics</" in answer
|
||||
assert "href>/calendar.ics/event4.ics</" in answer
|
||||
assert "href>/calendar.ics/event5.ics</" in answer
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20130903T000000Z" end="20130904T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" not in answer
|
||||
assert "href>/calendar.ics/event2.ics</" not in answer
|
||||
assert "href>/calendar.ics/event3.ics</" in answer
|
||||
assert "href>/calendar.ics/event4.ics</" not in answer
|
||||
assert "href>/calendar.ics/event5.ics</" not in answer
|
||||
answer = self._test_filter(["""
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20130805T000000Z" end="20130810T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>"""], events=5)
|
||||
assert "href>/calendar.ics/event1.ics</" not in answer
|
||||
assert "href>/calendar.ics/event2.ics</" not in answer
|
||||
assert "href>/calendar.ics/event3.ics</" not in answer
|
||||
assert "href>/calendar.ics/event4.ics</" not in answer
|
||||
assert "href>/calendar.ics/event5.ics</" not in answer
|
||||
|
||||
|
||||
class TestMultiFileSystem(BaseRequests, BaseTest):
|
||||
"""Base class for filesystem tests."""
|
||||
|
Loading…
Reference in New Issue
Block a user