From 2627126fde1f6d1ca64442de58253640c59b972c Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Tue, 26 Mar 2024 18:00:50 +0100 Subject: New upstream version 2.1.3 --- .github/FUNDING.yml | 3 ++- .github/workflows/tests.yml | 3 ++- README.rst | 6 ++++++ recurring_ical_events.py | 8 ++++++++ setup.py | 4 ++-- test/calendars/issue-128-only-first-event.ics | 17 ++++++++++++++++ test/test_issue_128_only_first_event.py | 28 +++++++++++++++++++++++++++ tox.ini | 2 +- 8 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 test/calendars/issue-128-only-first-event.ics create mode 100644 test/test_issue_128_only_first_event.py diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 2644416..558ddcc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -3,7 +3,7 @@ github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] - niccokunzmann patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username +open_collective: open-web-calendar # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry @@ -12,3 +12,4 @@ issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] + - https://polar.sh/niccokunzmann/python-recurring-ical-events diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7f4395b..6ab699f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,8 @@ jobs: - ["3.8", "py38", "Australia/Sydney"] - ["3.9", "py39", "UTC"] - ["3.10", "py310", "Asia/Singapore"] - - ["3.11", "py311", "Asia/Singapore"] + - ["3.11", "py311", "Pacific/Bougainville"] + - ["3.12", "py312", "Europe/London"] runs-on: ubuntu-latest name: ${{ matrix.config[1] }} diff --git a/README.rst b/README.rst index f41fc16..b595121 100644 --- a/README.rst +++ b/README.rst @@ -278,6 +278,12 @@ To release new versions, Changelog --------- +- v2.1.3 (unreleased) + + - Test and support Python 3.12. + - Change SPDX license header. + - Fix RRULE with negative COUNT, see `Issue 128 `_ + - v2.1.2 - Fix RRULE with EXDATE as DATE, see `PR 121 `__ by Jan Grasnick and `PR 122 `__. diff --git a/recurring_ical_events.py b/recurring_ical_events.py index 8ecf4a9..574da9d 100644 --- a/recurring_ical_events.py +++ b/recurring_ical_events.py @@ -20,6 +20,7 @@ import x_wr_timezone from collections import defaultdict from icalendar.prop import vDDDTypes from typing import Optional +import re if sys.version_info[0] == 2: # Python2 has no ZoneInfo. We can assume that pytz is used. @@ -36,10 +37,15 @@ else: """Return the time stamp of a datetime""" return dt.timestamp() + +NEGATIVE_RRULE_COUNT_REGEX = re.compile(r"COUNT=-\d+;?") + + def convert_to_date(date): """converts a date or datetime to a date""" return datetime.date(date.year, date.month, date.day) + def convert_to_datetime(date, tzinfo): """converts a date to a datetime""" if isinstance(date, datetime.datetime): @@ -55,6 +61,7 @@ def convert_to_datetime(date, tzinfo): return datetime.datetime(date.year, date.month, date.day, tzinfo=tzinfo) return date + def time_span_contains_event(span_start, span_stop, event_start, event_stop, comparable=False): """Return whether an event should is included within a time span. @@ -246,6 +253,7 @@ class RepeatedComponent: def rrulestr(self, rule_string): """Return an rrulestr with a start. This might fail.""" + rule_string = NEGATIVE_RRULE_COUNT_REGEX.sub("", rule_string) # Issue 128 rule = rrulestr(rule_string, dtstart=self.start, cache=True) rule.string = rule_string rule.until = until = self._get_rrule_until(rule) diff --git a/setup.py b/setup.py index c68996f..307f328 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ PACKAGE_NAME = "recurring_ical_events" HERE = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, HERE) # for package import -__version__ = "2.1.2" +__version__ = "2.1.3" __author__ = 'Nicco Kunzmann' @@ -41,7 +41,7 @@ METADATA = dict( author=__author__, author_email='niccokunzmann@rambler.ru', description='A Python module which repeats ICalendar events by RRULE, RDATE and EXDATE.', - license='LGPLv3+', + license='LGPL-3.0-or-later', url='https://github.com/niccokunzmann/python-recurring-ical-events', keywords='icalendar', ) diff --git a/test/calendars/issue-128-only-first-event.ics b/test/calendars/issue-128-only-first-event.ics new file mode 100644 index 0000000..8bc5000 --- /dev/null +++ b/test/calendars/issue-128-only-first-event.ics @@ -0,0 +1,17 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTAMP:20231102T221721Z +DTSTART;VALUE=DATE:20231002 +DTEND;VALUE=DATE:20231009 +SUMMARY:test123 +CATEGORIES:other +SUBCALENDAR-NAME:test +EVENT-ID:538924 +EVENT-ALLDAY:true +RRULE:FREQ=WEEKLY;UNTIL=20240331;COUNT=-1;INTERVAL=4;BYDAY=MO +CREATED:20231102T221633Z +LAST-MODIFIED:20231102T221716Z +TRANSP:TRANSPARENT +STATUS:CONFIRMED +END:VEVENT +END:VCALENDAR diff --git a/test/test_issue_128_only_first_event.py b/test/test_issue_128_only_first_event.py new file mode 100644 index 0000000..896671b --- /dev/null +++ b/test/test_issue_128_only_first_event.py @@ -0,0 +1,28 @@ +"""The atlassian confluence calendar sets the count value to -1 when future events are deleted. + +See https://github.com/niccokunzmann/python-recurring-ical-events/issues/128 +""" +import recurring_ical_events +import pytest + + +def test_all_events_are_present(calendars): + """All events are shown and not just the first one.""" + assert len(calendars.issue_128_only_first_event.all()) == 7 + + +@pytest.mark.parametrize( + "string,matches", + [ + ("COUNT=1", False), + ("COUNT=1;", False), + ("COUNT=-1", True), + ("COUNT=-1;", True), + ("COUNT=-100", True), + ("COUNT=-100;", True), + ] +) +def test_matching_negative_count(string, matches): + """Make sure the general replacement pattern works.""" + actually_matches = recurring_ical_events.NEGATIVE_RRULE_COUNT_REGEX.match(string) is not None + assert actually_matches == matches diff --git a/tox.ini b/tox.ini index f45aee8..d588e6b 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ [tox] skipsdist = True -envlist = py37, py38, py39, py310, py311 +envlist = py37, py38, py39, py310, py311, py312 [testenv] setenv = TMPDIR={envtmpdir} -- cgit v1.2.3