Bläddra i källkod

(#1812) Work on expand events time-range filter processing

Georgiy 8 månader sedan
förälder
incheckning
9d591bd514
1 ändrade filer med 78 tillägg och 3 borttagningar
  1. 78 3
      radicale/app/report.py

+ 78 - 3
radicale/app/report.py

@@ -224,9 +224,17 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
         root.findall(xmlutils.make_clark("C:filter")) +
         root.findall(xmlutils.make_clark("CR:filter")))
 
+    # extract time-range filter for processing after main filters
+    time_range_element = None
+    non_time_range_filters = []
+    for filter_ in filters:
+        time_range_element = filter_.find(".//" + xmlutils.make_clark("C:time-range"))
+        if time_range_element is None:
+            non_time_range_filters.append(filter_)
+
     # Retrieve everything required for finishing the request.
     retrieved_items = list(retrieve_items(
-        base_prefix, path, collection, hreferences, filters, multistatus))
+        base_prefix, path, collection, hreferences, non_time_range_filters, multistatus))
     collection_tag = collection.tag
     # !!! Don't access storage after this !!!
     unlock_storage_fn()
@@ -239,7 +247,7 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
         if filters and not filters_matched:
             try:
                 if not all(test_filter(collection_tag, item, filter_)
-                           for filter_ in filters):
+                           for filter_ in non_time_range_filters):
                     continue
             except ValueError as e:
                 raise ValueError("Failed to filter item %r from %r: %s" %
@@ -248,6 +256,13 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
                 raise RuntimeError("Failed to filter item %r from %r: %s" %
                                    (item.href, collection.path, e)) from e
 
+        # Filtering non-recurring events by time-range
+        if (time_range_element is not None) and not hasattr(item, 'rrule'):
+            start, end = radicale_filter.time_range_timestamps(time_range_element)
+            istart, iend = item.time_range
+            if istart >= end or iend <= start:
+                continue
+
         found_props = []
         not_found_props = []
 
@@ -280,8 +295,18 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
                         end, '%Y%m%dT%H%M%SZ'
                     ).replace(tzinfo=datetime.timezone.utc)
 
+                    time_range_start = None
+                    time_range_end = None
+
+                    if time_range_element is not None:
+                        time_range_start, time_range_end = radicale_filter.parse_time_range(time_range_element)
+
                     expanded_element = _expand(
-                        element, copy.copy(item), start, end)
+                        element=element, item=copy.copy(item),
+                        start=start, end=end,
+                        time_range_start=time_range_start, time_range_end=time_range_end,
+                    )
+
                     found_props.append(expanded_element)
                 else:
                     found_props.append(element)
@@ -303,6 +328,8 @@ def _expand(
         item: radicale_item.Item,
         start: datetime.datetime,
         end: datetime.datetime,
+        time_range_start: Optional[datetime.datetime] = None,
+        time_range_end: Optional[datetime.datetime] = None,
 ) -> ET.Element:
     vevent_component: vobject.base.Component = copy.copy(item.vobject_item)
 
@@ -347,6 +374,13 @@ def _expand(
 
         for recurrence_dt in recurrences:
             recurrence_utc = recurrence_dt.astimezone(datetime.timezone.utc)
+
+            if time_range_start is not None and time_range_end is not None:
+                dtstart = recurrence_dt if all_day_event else recurrence_utc
+                dtend = dtstart + duration if duration else dtstart
+                if not (dtstart < time_range_end and dtend > time_range_start):
+                    continue
+
             i_overridden, vevent = _find_overridden(i_overridden, vevents_overridden, recurrence_utc, dt_format)
 
             if not vevent:
@@ -377,6 +411,47 @@ def _expand(
             else:
                 vevent_component.add(vevent)
 
+        # Filter overridden events and vevent_recurrence if recurrences is empty
+        # Todo: optimize that code
+        if time_range_start is not None and time_range_end is not None:
+            filtered_vevents = []
+            for vevent in vevents_overridden:
+                dtstart = vevent.dtstart.value
+                dtend = vevent.dtend.value if hasattr(vevent, 'dtend') else dtstart
+
+                dtstart = datetime.datetime.strptime(
+                    dtstart, "%Y%m%dT%H%M%SZ").replace(
+                        tzinfo=datetime.timezone.utc)
+                dtend = datetime.datetime.strptime(
+                    dtend, "%Y%m%dT%H%M%SZ").replace(
+                        tzinfo=datetime.timezone.utc)
+
+                if dtstart < time_range_end and dtend > time_range_start:
+                    filtered_vevents.append(vevent)
+
+            dtstart = vevent_recurrence.dtstart.value
+            dtend = vevent_recurrence.dtend.value if hasattr(vevent_recurrence, 'dtend') else dtstart
+            dtstart = datetime.datetime.strptime(
+                dtstart, "%Y%m%dT%H%M%SZ").replace(
+                    tzinfo=datetime.timezone.utc)
+            dtend = datetime.datetime.strptime(
+                dtend, "%Y%m%dT%H%M%SZ").replace(
+                    tzinfo=datetime.timezone.utc)
+
+            if filtered_vevents or (dtstart < time_range_end and dtend > time_range_start):
+                if filtered_vevents:
+                    vevent_component.vevent = filtered_vevents[0]
+                    for vevent in filtered_vevents[1:]:
+                        vevent_component.add(vevent)
+                if dtstart < time_range_end and dtend > time_range_start:
+                    if not filtered_vevents:
+                        vevent_component.vevent = vevent_recurrence
+                    else:
+                        vevent_component.add(vevent_recurrence)
+            else:
+                element.text = ""
+                return element
+
     element.text = vevent_component.serialize()
 
     return element