From d4bcd67d94393c7dc80f6f5285310de69b6fa65c Mon Sep 17 00:00:00 2001 From: twissell- Date: Tue, 13 Aug 2024 19:38:14 +0200 Subject: [PATCH 1/2] Added sort_reverse option on Calendar.search to reverse the sorting order of the found objects --- caldav/objects.py | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/caldav/objects.py b/caldav/objects.py index 40ab040..d2facdd 100644 --- a/caldav/objects.py +++ b/caldav/objects.py @@ -11,22 +11,9 @@ class hierarchy into a separate file) import sys import uuid from collections import defaultdict -from datetime import date -from datetime import datetime -from datetime import timedelta -from datetime import timezone -from typing import Any -from typing import List -from typing import Optional -from typing import Set -from typing import Tuple -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union -from urllib.parse import ParseResult -from urllib.parse import quote -from urllib.parse import SplitResult -from urllib.parse import unquote +from datetime import date, datetime, timedelta, timezone +from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple, TypeVar, Union +from urllib.parse import ParseResult, SplitResult, quote, unquote import icalendar import vobject @@ -35,12 +22,10 @@ class hierarchy into a separate file) from lxml.etree import _Element from vobject.base import VBase +from caldav.lib.python_utilities import to_normal_str, to_unicode, to_wire + from .elements.base import BaseElement -from .elements.cdav import CalendarData -from .elements.cdav import CompFilter -from caldav.lib.python_utilities import to_normal_str -from caldav.lib.python_utilities import to_unicode -from caldav.lib.python_utilities import to_wire +from .elements.cdav import CalendarData, CompFilter try: from typing import ClassVar, Optional, Union @@ -1132,6 +1117,7 @@ def search( todo: Optional[bool] = None, include_completed: bool = False, sort_keys: Sequence[str] = (), + sort_reverse: bool = False, split_expanded: bool = True, props: Optional[List[CalendarData]] = None, **kwargs, @@ -1163,6 +1149,7 @@ def search( * start, end: do a time range search * filters - other kind of filters (in lxml tree format) * sort_keys - list of attributes to use when sorting + * sort_reverse - reverse the sorting order not supported yet: * negated text match @@ -1290,7 +1277,7 @@ def sort_key_func(x): return ret if sort_keys: - objects.sort(key=sort_key_func) + objects.sort(key=sort_key_func, reverse=sort_reverse) ## partial workaround for https://github.com/python-caldav/caldav/issues/201 for obj in objects: From b95a61a3a267f7701e7d7348a8f0945ade5e18fa Mon Sep 17 00:00:00 2001 From: twissell- Date: Tue, 13 Aug 2024 20:19:33 +0200 Subject: [PATCH 2/2] Fixed style, added tests and changelog entry --- CHANGELOG.md | 1 + caldav/objects.py | 27 +++++++++++++++++++++------ tests/test_caldav.py | 10 ++++++++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d9cf98..5f705b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ This project should more or less adhere to [Semantic Versioning](https://semver. * Use setuptools-scm / pyproject.toml (modern packaging). Details in https://github.com/python-caldav/caldav/pull/364 and https://github.com/python-caldav/caldav/pull/367 * Debugging tool - an environment variable can be set, causing the library to spew out server communications into files under /tmp. Details in https://github.com/python-caldav/caldav/pull/249 and https://github.com/python-caldav/caldav/issues/248 * Comaptibility matrix for posteo.de servers in `tests/compatibility_issues.py` +* Added sort_reverse option to the search function to reverse the sorting order of the found objects. ### Security diff --git a/caldav/objects.py b/caldav/objects.py index d2facdd..5cf7314 100644 --- a/caldav/objects.py +++ b/caldav/objects.py @@ -11,9 +11,22 @@ class hierarchy into a separate file) import sys import uuid from collections import defaultdict -from datetime import date, datetime, timedelta, timezone -from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple, TypeVar, Union -from urllib.parse import ParseResult, SplitResult, quote, unquote +from datetime import date +from datetime import datetime +from datetime import timedelta +from datetime import timezone +from typing import Any +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from urllib.parse import ParseResult +from urllib.parse import quote +from urllib.parse import SplitResult +from urllib.parse import unquote import icalendar import vobject @@ -22,10 +35,12 @@ class hierarchy into a separate file) from lxml.etree import _Element from vobject.base import VBase -from caldav.lib.python_utilities import to_normal_str, to_unicode, to_wire - from .elements.base import BaseElement -from .elements.cdav import CalendarData, CompFilter +from .elements.cdav import CalendarData +from .elements.cdav import CompFilter +from caldav.lib.python_utilities import to_normal_str +from caldav.lib.python_utilities import to_unicode +from caldav.lib.python_utilities import to_wire try: from typing import ClassVar, Optional, Union diff --git a/tests/test_caldav.py b/tests/test_caldav.py index 2c003df..d7e5e0e 100644 --- a/tests/test_caldav.py +++ b/tests/test_caldav.py @@ -59,10 +59,11 @@ from caldav.objects import Todo if test_xandikos: - from xandikos.web import XandikosBackend, XandikosApp + import asyncio + import aiohttp import aiohttp.web - import asyncio + from xandikos.web import XandikosApp, XandikosBackend if test_radicale: import radicale.config @@ -1332,6 +1333,11 @@ def testSearchEvent(self): assert len(all_events) == 3 assert all_events[0].instance.vevent.summary.value == "Bastille Day Jitsi Party" + ## Sorting in reverse order should work also + all_events = c.search(sort_keys=("SUMMARY", "DTSTAMP"), sort_reverse=True) + assert len(all_events) == 3 + assert all_events[0].instance.vevent.summary.value == "Our Blissful Anniversary" + def testSearchSortTodo(self): self.skip_on_compatibility_flag("read_only") self.skip_on_compatibility_flag("no_todo")