summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJochen Sprickerhof <jspricke@debian.org>2024-03-26 18:00:50 +0100
committerJochen Sprickerhof <jspricke@debian.org>2024-03-26 18:00:50 +0100
commit2627126fde1f6d1ca64442de58253640c59b972c (patch)
tree2c95d51ffef559e6e42091e9764d787a228ec049
parent91a7f5f399b3444d80129c4796015229182df45f (diff)
New upstream version 2.1.3
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/tests.yml3
-rw-r--r--README.rst6
-rw-r--r--recurring_ical_events.py8
-rw-r--r--setup.py4
-rw-r--r--test/calendars/issue-128-only-first-event.ics17
-rw-r--r--test/test_issue_128_only_first_event.py28
-rw-r--r--tox.ini2
8 files changed, 66 insertions, 5 deletions
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 <https://github.com/niccokunzmann/python-recurring-ical-events/issues/128>`_
+
- v2.1.2
- Fix RRULE with EXDATE as DATE, see `PR 121 <https://github.com/niccokunzmann/python-recurring-ical-events/pull/121>`__ by Jan Grasnick and `PR 122 <https://github.com/niccokunzmann/python-recurring-ical-events/pull/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}