Przeglądaj źródła

fix assertion error on "RECURRENCE-ID;VALUE=DATE:…"

Fabian Peter Hammerle 1 dzień temu
rodzic
commit
e274496212

+ 3 - 0
CHANGELOG.md

@@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Removed
 ### Removed
 - compatibility with `python3.7`, `python3.8` & `python3.9`
 - compatibility with `python3.7`, `python3.8` & `python3.9`
 
 
+### Fixed
+- assertion error on "RECURRENCE-ID;VALUE=DATE:…"
+
 ## [1.0.0] - 2022-08-01
 ## [1.0.0] - 2022-08-01
 ### Added
 ### Added
 - `pyproject.toml` to specify build dependencies & build backend
 - `pyproject.toml` to specify build dependencies & build backend

+ 6 - 4
ical2vdir/__init__.py

@@ -19,8 +19,8 @@ import argparse
 import datetime
 import datetime
 import logging
 import logging
 import os
 import os
-import shutil
 import pathlib
 import pathlib
+import shutil
 import sys
 import sys
 import tempfile
 import tempfile
 import typing
 import typing
@@ -86,9 +86,11 @@ def _event_vdir_filename(event: icalendar.cal.Event) -> str:
     output_filename = str(event["UID"])
     output_filename = str(event["UID"])
     if "RECURRENCE-ID" in event:
     if "RECURRENCE-ID" in event:
         recurrence_id = event["RECURRENCE-ID"]
         recurrence_id = event["RECURRENCE-ID"]
-        assert isinstance(recurrence_id.dt, datetime.datetime), recurrence_id.dt
-
-        output_filename += "." + _datetime_basic_isoformat(recurrence_id.dt)
+        if isinstance(recurrence_id.dt, datetime.datetime):
+            output_filename += "." + _datetime_basic_isoformat(recurrence_id.dt)
+        else:
+            assert isinstance(recurrence_id.dt, datetime.date), vars(recurrence_id)
+            output_filename += "." + recurrence_id.dt.strftime("%Y%m%d")
     return output_filename + _VDIR_EVENT_FILE_EXTENSION
     return output_filename + _VDIR_EVENT_FILE_EXTENSION
 
 
 
 

+ 25 - 0
tests/cli_test.py

@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 
+import datetime
 import io
 import io
 import logging
 import logging
 import pathlib
 import pathlib
@@ -60,6 +61,30 @@ def test__main_create_all(
     assert event["SUMMARY"] == "recurring"
     assert event["SUMMARY"] == "recurring"
 
 
 
 
+def test__main_create_all_recurrence_id_date(
+    caplog: _pytest.logging.LogCaptureFixture, tmp_path: pathlib.Path
+) -> None:
+    with pathlib.Path(__file__).parent.joinpath(
+        "resources", "nextcloud-recurring.ics"
+    ).open("rb") as calendar_file:
+        with unittest.mock.patch("sys.stdin", calendar_file), unittest.mock.patch(
+            "sys.argv", ["", "--output-dir", str(tmp_path)]
+        ), caplog.at_level(logging.WARNING):
+            ical2vdir._main()
+    created_item_paths = sorted(tmp_path.iterdir())
+    assert [p.name for p in created_item_paths] == [
+        "b0fea373-389b-48d5-b739-9de3e298f555.20260101.ics",
+        "b0fea373-389b-48d5-b739-9de3e298f555.20260201.ics",
+        "b0fea373-389b-48d5-b739-9de3e298f555.20260301.ics",
+    ]
+    assert not caplog.records
+    event = icalendar.cal.Event.from_ical(created_item_paths[1].read_bytes())
+    assert isinstance(event, icalendar.cal.Event)
+    assert event["UID"] == "b0fea373-389b-48d5-b739-9de3e298f555"
+    assert event["SUMMARY"] == "second"
+    assert event["RECURRENCE-ID"].dt == datetime.date(2026, 2, 1)
+
+
 def test__main_create_some(
 def test__main_create_some(
     caplog: _pytest.logging.LogCaptureFixture,
     caplog: _pytest.logging.LogCaptureFixture,
     tmp_path: pathlib.Path,
     tmp_path: pathlib.Path,

+ 56 - 0
tests/resources/nextcloud-recurring.ics

@@ -0,0 +1,56 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//SabreDAV//SabreDAV//EN
+X-WR-CALNAME:whatever
+X-APPLE-CALENDAR-COLOR:#00FF00
+REFRESH-INTERVAL;VALUE=DURATION:PT4H
+X-PUBLISHED-TTL:PT4H
+BEGIN:VTIMEZONE
+TZID:Europe/Vienna
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20260101T123456Z
+UID:b0fea373-389b-48d5-b739-9de3e298f555
+RECURRENCE-ID;VALUE=DATE:20260101
+SUMMARY:first
+DTSTART;VALUE=DATE:20260101
+DTEND;VALUE=DATE:20260102
+TRANSP:TRANSPARENT
+SEQUENCE:1
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20260101T123456Z
+UID:b0fea373-389b-48d5-b739-9de3e298f555
+RECURRENCE-ID;VALUE=DATE:20260201
+SUMMARY:second
+DTSTART;VALUE=DATE:20260201
+DTEND;VALUE=DATE:20260202
+TRANSP:TRANSPARENT
+SEQUENCE:1
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20260101T123456Z
+UID:b0fea373-389b-48d5-b739-9de3e298f555
+RECURRENCE-ID;VALUE=DATE:20260301
+SUMMARY:third
+DTSTART;VALUE=DATE:20260301
+DTEND;VALUE=DATE:20260302
+TRANSP:TRANSPARENT
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR