From 513415d2015d28754bcea880bb841d7777013c4e Mon Sep 17 00:00:00 2001 From: Georgiy Date: Mon, 1 Apr 2024 19:27:59 +0300 Subject: [PATCH] start creating test for rrule expand property, fix expand processing --- radicale/app/report.py | 54 +++++++++++++-------- radicale/tests/static/event_daily_rrule.ics | 28 +++++++++++ radicale/tests/test_base.py | 43 ++++++++++++++++ 3 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 radicale/tests/static/event_daily_rrule.ics diff --git a/radicale/app/report.py b/radicale/app/report.py index 4420f0e0..08e1fed3 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -172,9 +172,9 @@ 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) - expanded_elements = _expand( + expanded_element = _expand( element, copy.copy(item), start, end) - found_props.extend(expanded_elements) + found_props.append(expanded_element) else: found_props.append(element) else: @@ -195,34 +195,46 @@ def _expand( item: radicale_item.Item, start: datetime.datetime, end: datetime.datetime, -) -> List[ET.Element]: +) -> ET.Element: + rruleset = None + if hasattr(item.vobject_item.vevent, 'rrule'): + rruleset = item.vobject_item.vevent.getrruleset() + expanded_item = _make_vobject_expanded_item(item) - element.text = expanded_item.vobject_item.serialize() - expanded = [element] - if hasattr(item.vobject_item.vevent, "rrule"): - rulleset = item.vobject_item.vevent.getrruleset() - recurrences = rulleset.between(start, end) + if rruleset: + recurrences = rruleset.between(start, end) - expanded = [] + expanded = None for recurrence_dt in recurrences: + expanded_item_ = copy.copy(expanded_item) + try: - delattr(expanded_item.vobject_item.vevent, 'recurrence-id') + delattr(expanded_item_.vobject_item.vevent, 'recurrence-id') except AttributeError: pass recurrence_utc = recurrence_dt.astimezone(datetime.timezone.utc) - expanded_item.vobject_item.vevent.recurrence_id = ContentLine( + vevent = copy.deepcopy(expanded_item_.vobject_item.vevent) + recurrence_id = ContentLine( name='RECURRENCE-ID', value=recurrence_utc.strftime('%Y%m%dT%H%M%SZ'), params={} ) + vevent.add(recurrence_id) - element = copy.copy(element) - element.text = expanded_item.vobject_item.serialize() - expanded.append(element) + if expanded is None: + expanded_item_.vobject_item.vevent.add(recurrence_id) + expanded_item_.vobject_item.remove(expanded_item_.vobject_item.vevent) + expanded = expanded_item_ + else: + expanded.vobject_item.add(vevent) - return expanded + element.text = expanded.vobject_item.serialize() + else: + element.text = expanded_item.vobject_item.serialize() + + return element def _make_vobject_expanded_item( @@ -239,14 +251,16 @@ def _make_vobject_expanded_item( vevent = item.vobject_item.vevent start_utc = vevent.dtstart.value.astimezone(datetime.timezone.utc) - end_utc = vevent.dtend.value.astimezone(datetime.timezone.utc) - vevent.dtstart = ContentLine( name='DTSTART', value=start_utc.strftime('%Y%m%dT%H%M%SZ'), params={}) - vevent.dtend = ContentLine( - name='DTEND', - value=end_utc.strftime('%Y%m%dT%H%M%SZ'), params={}) + + dt_end = getattr(vevent, 'dtend', None) + if dt_end is not None: + end_utc = dt_end.value.astimezone(datetime.timezone.utc) + vevent.dtend = ContentLine( + name='DTEND', + value=end_utc.strftime('%Y%m%dT%H%M%SZ'), params={}) timezones_to_remove = [] for component in item.vobject_item.components(): diff --git a/radicale/tests/static/event_daily_rrule.ics b/radicale/tests/static/event_daily_rrule.ics new file mode 100644 index 00000000..362a18e4 --- /dev/null +++ b/radicale/tests/static/event_daily_rrule.ics @@ -0,0 +1,28 @@ +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VTIMEZONE +LAST-MODIFIED:20040110T032845Z +TZID:US/Eastern +BEGIN:DAYLIGHT +DTSTART:20000404T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20001026T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=US/Eastern:20060102T120000 +DURATION:PT1H +RRULE:FREQ=DAILY;COUNT=5 +SUMMARY:Recurring event +UID:event_daily_rrule +END:VEVENT +END:VCALENDAR diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index a0d3d534..6c1ac1b3 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -1525,6 +1525,49 @@ permissions: RrWw""") calendar_path, "http://radicale.org/ns/sync/INVALID") assert not sync_token + def test_report_with_expand_property(self) -> None: + self.put("/calendar.ics/", get_file_content("event_daily_rrule.ics")) + + req_body = \ + """ + + + + + + + + + + + + + + + """ + + # status, _, answer = self.request("REPORT", "/calendar.ics/", req_body, check=207) + # print(status, answer) + + _, responses = self.report("/calendar.ics/", req_body) + assert len(responses) == 1 + response = responses['/calendar.ics/event_daily_rrule.ics'] + status, element = list(response.values())[0] + assert status == 200 + + print("resp", status, element, flush=True) + + uids = [] + for line in element.text.split("\n"): + print("line", line, line.startswith("UID:")) + if line.startswith("UID:"): + uid = line[len("UID:"):] + assert uid == "event_daily_rrule" + uids.append(uids) + + assert len(uids) == 3 + assert False + def test_propfind_sync_token(self) -> None: """Retrieve the sync-token with a propfind request""" calendar_path = "/calendar.ics/"