Skip to content

Commit 8059b53

Browse files
committed
bugfixings
1 parent 949514f commit 8059b53

4 files changed

Lines changed: 78 additions & 36 deletions

File tree

.pre-commit-config.yaml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,3 @@ repos:
99
# Run the formatter.
1010
- id: ruff-format
1111

12-
- repo: https://github.com/pre-commit/pre-commit-hooks
13-
rev: v6.0.0
14-
hooks:
15-
- id: trailing-whitespace
16-
- id: end-of-file-fixer
17-
- id: check-yaml
18-
- id: check-toml
19-
- id: check-ast
20-
- id: mixed-line-ending
21-
args: [--fix=lf]

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ In the early versions, the filter method will take the calendar contents one by
2222

2323
## License
2424

25-
As for now I'm releasing this under the GNU Affero General Public License v.3.0. If you find this too restrictive or if this causes license compatibility issues for you, please get in touch with me and I will consider to fix some dual licensing, like it's done with the python CalDAV library.
25+
As for now I'm releasing this under the GNU Affero General Public License v.3.0. If you find this too restrictive or if this causes license compatibility issues for you, I will consider to fix some dual licensing, like it's done with the python CalDAV library.
2626

27-
Any contributor has to accept that the code is released under AGPL v3.0 and at some point in the future may be dual-licensed under some more permissive license, like the EUPL v1.1.
27+
This also menas that any contributor has to accept that the code is released under AGPL v3.0 and at some point in the future may be dual-licensed under some more permissive license, like the EUPL v1.1.
28+
29+
I don't have very strong opinions on licenses. If you have any issues in one way or another, please reach out.

pyproject.toml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,11 @@ requires-python = ">=3.10,<4.0"
1919
dependencies = [
2020
"icalendar>=6.0.0",
2121
]
22-
23-
[tool.poetry]
2422
packages = [{include = "icalendar_searcher", from = "src"}]
2523

2624
[tool.poetry-dynamic-versioning]
2725
enable = true
2826

29-
[tool.poetry.dependencies]
30-
poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
31-
icalendar = {}
32-
3327
[build-system]
3428
requires = ["poetry-core>=2.0.0,<3.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
3529
build-backend = "poetry_dynamic_versioning.backend"

src/icalendar_searcher/__init__.py

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
from dataclasses import dataclass
2-
from dataclasses import field
1+
from dataclasses import dataclass, field
32
from datetime import datetime
4-
from typing import Any
5-
from typing import List
6-
from typing import TYPE_CHECKING
7-
from typing import Union
3+
from typing import TYPE_CHECKING, Any, Union
84

5+
from icalendar import Timezone
96
from icalendar.prop import TypesFactory
107

118
if TYPE_CHECKING:
12-
from icalendar import Calendar
13-
from icalendar import Component
149
from caldav.calendarobjectresource import CalendarObjectResource
10+
from icalendar import Calendar, Component
1511

1612
types_factory = TypesFactory()
1713

@@ -40,9 +36,9 @@ class Searcher:
4036
of them has to match)
4137
4238
Properties (like SUMMARY, CATEGORIES, etc) are not meant to be
43-
sent through the constructor, use the ``add_property_filter``
39+
sent through the constructor, use the :func:`icalendar_searcher.Searcher.add_property_filter`
4440
method. Same goes with sort keys, they can be added through the
45-
``add_sort_key`` method.
41+
`func:icalendar_searcher.Searcher.add_sort_key` method.
4642
4743
The ``todo``, ``event`` and ``journal`` parameters are booleans
4844
for filtering the component type. If i.e. both todo and
@@ -118,9 +114,7 @@ class Searcher:
118114
_property_filters: dict = field(default_factory=dict)
119115
_property_operator: dict = field(default_factory=dict)
120116

121-
def add_property_filter(
122-
self, key: str, value: Any, operator: str = "contains"
123-
) -> None:
117+
def add_property_filter(self, key: str, value: Any, operator: str = "contains") -> None:
124118
"""Adds a filter for some specific iCalendar property.
125119
126120
Examples of valid iCalendar properties: SUMMARY,
@@ -182,8 +176,8 @@ def check_component(
182176
raise NotImplementedError()
183177

184178
def filter(
185-
self, components: List[Union["Calendar", "CalendarObjectResource"]]
186-
) -> List[Union["Calendar", "CalendarObjectResource"]]:
179+
self, components: list[Union["Calendar", "CalendarObjectResource"]]
180+
) -> list[Union["Calendar", "CalendarObjectResource"]]:
187181
"""
188182
Filters the components given according to the search
189183
criterias, and possibly expanding recurrences.
@@ -194,8 +188,8 @@ def filter(
194188
raise NotImplementedError()
195189

196190
def sort(
197-
self, components: List[Union["Calendar", "CalendarObjectResource"]]
198-
) -> List[Union["Calendar", "CalendarObjectResource"]]:
191+
self, components: list[Union["Calendar", "CalendarObjectResource"]]
192+
) -> list[Union["Calendar", "CalendarObjectResource"]]:
199193
"""
200194
Sorts the components given according to the sort
201195
keys.
@@ -209,4 +203,66 @@ def sort_key(self, component: Union["Calendar", "CalendarObjectResource"]) -> tu
209203
"""
210204
Returns a sortable value from the component, based on the sort keys
211205
"""
212-
raise NotImplementedError()
206+
ret = []
207+
## TODO: we disregard any complexity wrg of recurring events
208+
209+
icalendar_instance = component
210+
211+
not_tz_components = (
212+
x for x in icalendar_instance.subcomponents if not isinstance(x, Timezone)
213+
)
214+
comp = next(not_tz_components)
215+
216+
defaults = {
217+
## TODO: all possible non-string sort attributes needs to be listed here, otherwise we will get type errors when comparing objects with the property defined vs undefined (or maybe we should make an "undefined" object that always will compare below any other type? Perhaps there exists such an object already?)
218+
"due": "2050-01-01",
219+
"dtstart": "1970-01-01",
220+
"priority": 0,
221+
"status": {
222+
"VTODO": "NEEDS-ACTION",
223+
"VJOURNAL": "FINAL",
224+
"VEVENT": "TENTATIVE",
225+
}[comp.name],
226+
"category": "",
227+
## Usage of strftime is a simple way to ensure there won't be
228+
## problems if comparing dates with timestamps
229+
"isnt_overdue": not (
230+
"due" in comp
231+
and comp["due"].dt.strftime("%F%H%M%S") < datetime.now().strftime("%F%H%M%S")
232+
),
233+
"hasnt_started": (
234+
"dtstart" in comp
235+
and comp["dtstart"].dt.strftime("%F%H%M%S") > datetime.now().strftime("%F%H%M%S")
236+
),
237+
}
238+
for sort_key, reverse in self._sort_keys:
239+
val = comp.get(sort_key, None)
240+
if val is None:
241+
ret.append(defaults.get(sort_key.lower(), ""))
242+
continue
243+
if hasattr(val, "dt"):
244+
val = val.dt
245+
elif hasattr(val, "cats"):
246+
val = ",".join(val.cats)
247+
if hasattr(val, "strftime"):
248+
val = val.strftime("%F%H%M%S")
249+
if reverse:
250+
if isinstance(val, str):
251+
val = val.encode()
252+
val = bytes(b ^ 0xFF for b in val)
253+
else:
254+
val = -val
255+
ret.append(val)
256+
257+
return ret
258+
259+
def _unrwap(self, component: Union["Calendar", "CalendarObjectResource"]) -> "Calendar":
260+
"""
261+
To support the caldav library (and possibly other libraries where the
262+
icalendar component is wrapped)
263+
"""
264+
try:
265+
component = component.icalendar_instance
266+
except AttributeError:
267+
pass
268+
return component

0 commit comments

Comments
 (0)