mirror of
https://github.com/Kozea/Radicale.git
synced 2025-08-04 18:22:26 +00:00
Merge pull request #1822 from metallerok/empty_calendars_after_filters
Fix: Exclude empty calendars from the result if no matching events were found after applying filters
This commit is contained in:
commit
c208ad4374
2 changed files with 142 additions and 6 deletions
|
@ -233,10 +233,17 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
|
|||
for filter_ in filters:
|
||||
# extract time-range filter for processing after main filters
|
||||
# for expand request
|
||||
time_range_element = filter_.find(".//" + xmlutils.make_clark("C:time-range"))
|
||||
filter_copy = copy.deepcopy(filter_)
|
||||
|
||||
if expand is None or time_range_element is None:
|
||||
main_filters.append(filter_)
|
||||
if expand is not None:
|
||||
for comp_filter in filter_copy.findall(".//" + xmlutils.make_clark("C:comp-filter")):
|
||||
if comp_filter.get("name", "").upper() == "VCALENDAR":
|
||||
continue
|
||||
time_range_element = comp_filter.find(xmlutils.make_clark("C:time-range"))
|
||||
if time_range_element is not None:
|
||||
comp_filter.remove(time_range_element)
|
||||
|
||||
main_filters.append(filter_copy)
|
||||
|
||||
# Retrieve everything required for finishing the request.
|
||||
retrieved_items = list(retrieve_items(
|
||||
|
@ -306,6 +313,11 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
|
|||
time_range_start=time_range_start, time_range_end=time_range_end,
|
||||
max_occurrence=max_occurrence,
|
||||
)
|
||||
|
||||
if n_vev == 0:
|
||||
logger.debug("No VEVENTs found after expansion for %r, skipping", item.href)
|
||||
continue
|
||||
|
||||
n_vevents += n_vev
|
||||
found_props.append(expanded_element)
|
||||
else:
|
||||
|
@ -322,9 +334,11 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
|
|||
assert item.href
|
||||
uri = pathutils.unstrip_path(
|
||||
posixpath.join(collection.path, item.href))
|
||||
multistatus.append(xml_item_response(
|
||||
base_prefix, uri, found_props=found_props,
|
||||
not_found_props=not_found_props, found_item=True))
|
||||
|
||||
if found_props or not_found_props:
|
||||
multistatus.append(xml_item_response(
|
||||
base_prefix, uri, found_props=found_props,
|
||||
not_found_props=not_found_props, found_item=True))
|
||||
|
||||
return client.MULTI_STATUS, multistatus
|
||||
|
||||
|
@ -340,6 +354,8 @@ def _expand(
|
|||
) -> Tuple[ET.Element, int]:
|
||||
vevent_component: vobject.base.Component = copy.copy(item.vobject_item)
|
||||
logger.info("Expanding event %s", item.href)
|
||||
logger.debug(f"Expand range: {start} to {end}")
|
||||
logger.debug(f"Time range: {time_range_start} to {time_range_end}")
|
||||
|
||||
# Split the vevents included in the component into one that contains the
|
||||
# recurrence information and others that contain a recurrence id to
|
||||
|
|
|
@ -347,3 +347,123 @@ permissions: RrWw""")
|
|||
check=check)
|
||||
assert len(responses) == 0
|
||||
assert status == check
|
||||
|
||||
def test_report_vcalendar_all_components(self) -> None:
|
||||
"""Test calendar-query with comp-filter VCALENDAR, returns all components."""
|
||||
self.mkcalendar("/test/")
|
||||
self.put("/test/calendar.ics", get_file_content("event_daily_rrule.ics"))
|
||||
self.put("/test/todo.ics", get_file_content("todo1.ics"))
|
||||
|
||||
request = """
|
||||
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:prop>
|
||||
<C:calendar-data/>
|
||||
</D:prop>
|
||||
<C:filter>
|
||||
<C:comp-filter name="VCALENDAR"/>
|
||||
</C:filter>
|
||||
</C:calendar-query>
|
||||
"""
|
||||
status, responses = self.report("/test", request)
|
||||
assert status == 207
|
||||
assert len(responses) == 2
|
||||
assert "/test/calendar.ics" in responses
|
||||
assert "/test/todo.ics" in responses
|
||||
|
||||
def test_report_vevent_only(self) -> None:
|
||||
"""Test calendar-query with comp-filter VEVENT, returns only VEVENT."""
|
||||
self.mkcalendar("/test/")
|
||||
self.put("/test/calendar.ics", get_file_content("event_daily_rrule.ics"))
|
||||
self.put("/test/todo.ics", get_file_content("todo1.ics"))
|
||||
|
||||
start = "20060101T000000Z"
|
||||
end = "20060104T000000Z"
|
||||
|
||||
request = f"""
|
||||
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:prop>
|
||||
<C:calendar-data>
|
||||
<C:expand start="{start}" end="{end}"/>
|
||||
</C:calendar-data>
|
||||
</D:prop>
|
||||
<C:filter>
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="{start}" end="{end}"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>
|
||||
</C:filter>
|
||||
</C:calendar-query>
|
||||
"""
|
||||
status, responses = self.report("/test", request)
|
||||
assert status == 207
|
||||
assert len(responses) == 1
|
||||
assert "/test/calendar.ics" in responses
|
||||
vevent_response = responses["/test/calendar.ics"]
|
||||
assert type(vevent_response) is dict
|
||||
status, element = vevent_response["C:calendar-data"]
|
||||
assert status == 200 and element.text
|
||||
assert "BEGIN:VEVENT" in element.text
|
||||
assert "UID:" in element.text
|
||||
assert "BEGIN:VTODO" not in element.text
|
||||
|
||||
def test_report_time_range_no_vevent(self) -> None:
|
||||
"""Test calendar-query with time-range filter, no matching VEVENT."""
|
||||
self.mkcalendar("/test/")
|
||||
self.put("/test/calendar.ics/", get_file_content("event_daily_rrule.ics"))
|
||||
|
||||
request = """
|
||||
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:prop>
|
||||
<C:calendar-data>
|
||||
<C:expand start="20000101T000000Z" end="20000105T000000Z"/>
|
||||
</C:calendar-data>
|
||||
</D:prop>
|
||||
<C:filter>
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="20000101T000000Z" end="20000105T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>
|
||||
</C:filter>
|
||||
</C:calendar-query>
|
||||
"""
|
||||
status, responses = self.report("/test", request)
|
||||
assert status == 207
|
||||
assert len(responses) == 0
|
||||
|
||||
def test_report_time_range_one_vevent(self) -> None:
|
||||
"""Test calendar-query with time-range filter, matches one VEVENT."""
|
||||
self.mkcalendar("/test/")
|
||||
self.put("/test/calendar1.ics/", get_file_content("event_daily_rrule.ics"))
|
||||
self.put("/test/calendar2.ics/", get_file_content("event1.ics"))
|
||||
|
||||
start = "20060101T000000Z"
|
||||
end = "20060104T000000Z"
|
||||
|
||||
request = f"""
|
||||
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:prop>
|
||||
<C:calendar-data>
|
||||
<C:expand start="{start}" end="{end}"/>
|
||||
</C:calendar-data>
|
||||
</D:prop>
|
||||
<C:filter>
|
||||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VEVENT">
|
||||
<C:time-range start="{start}" end="{end}"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>
|
||||
</C:filter>
|
||||
</C:calendar-query>
|
||||
"""
|
||||
status, responses = self.report("/test", request)
|
||||
assert status == 207
|
||||
assert len(responses) == 1
|
||||
response = responses["/test/calendar1.ics"]
|
||||
assert type(response) is dict
|
||||
status, element = response["C:calendar-data"]
|
||||
assert status == 200 and element.text
|
||||
assert "BEGIN:VEVENT" in element.text
|
||||
assert "RECURRENCE-ID:20060103T170000Z" in element.text
|
||||
assert "DTSTART:20060103T170000Z" in element.text
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue