Skip to content

Commit

Permalink
* get_relatives method - look up the objects pointed to by the RELATE…
Browse files Browse the repository at this point in the history
…D-TO-attribute (needed by plann)

* Piggy-backing in one minor style fix and one minor error message fix
  • Loading branch information
tobixen committed Jul 18, 2023
1 parent 006a573 commit 63603d9
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
5 changes: 4 additions & 1 deletion caldav/lib/vcal.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ def fix(event):
else:
log = logging.debug
log(
"Ical data was modified to avoid compatibility issues. (error count: %i - this error is ratelimited)"
"""Ical data was modified to avoid compatibility issues
(Your calendar server breaks the icalendar standard)
This is probably harmless, particularly if not editing events or tasks
(error count: %i - this error is ratelimited)"""
% fixup_error_loggings,
exc_info=True,
)
Expand Down
57 changes: 55 additions & 2 deletions caldav/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class hierarchy into a separate file)
"""
import re
import uuid
from collections import defaultdict
from datetime import date
from datetime import datetime
from datetime import timedelta
Expand Down Expand Up @@ -1077,8 +1078,13 @@ def search(
end = kwargs["end"]

for o in objects:
## This should not be needed
## This would not be needed if the servers would follow the standard ...
o.load(only_if_unloaded=True)

## Google sometimes returns empty objects
objects = [o for o in objects if o.icalendar_component]

for o in objects:
component = o.icalendar_component
if component is None:
continue
Expand Down Expand Up @@ -1827,6 +1833,49 @@ def set_relation(

self.save()

## TODO: this method is undertested in the caldav library.
## However, as this consolidated and eliminated quite some duplicated code in the
## plann project, it is extensively tested in plann.
def get_relatives(
self, reltypes=None, relfilter=None, fetch_objects=True, ignore_missing=True
):
"""
By default, loads all objects pointed to by the RELATED-TO
property and loads the related objects.
It's possible to filter, either by passing a set or a list of
acceptable relation types in reltypes, or by passing a lambda
function in relfilter.
TODO: Make it possible to also check up reverse relationships
TODO: this is partially overlapped by plann.lib._relships_by_type
in the plann tool. Should consolidate the code.
"""
ret = defaultdict(set)
relations = self.icalendar_component.get("RELATED-TO", [])
if not isinstance(relations, list):
relations = [relations]
for rel in relations:
if relfilter and not relfilter(rel):
continue
reltype = rel.params.get("RELTYPE", "PARENT")
if reltypes and not reltype in reltypes:
continue
ret[reltype].add(str(rel))

if fetch_objects:
for reltype in ret:
uids = ret[reltype]
ret[reltype] = []
for obj in uids:
try:
ret[reltype].append(self.parent.object_by_uid(obj))
except error.NotFoundError:
if not ignore_missing:
raise
return ret

def _get_icalendar_component(self, assert_one=False):
"""Returns the icalendar subcomponent - which should be an
Event, Journal, Todo or FreeBusy from the icalendar class
Expand Down Expand Up @@ -2763,9 +2812,13 @@ def set_due(self, due, move_dtstart=False, check_dependent=False):
rels = []
if not isinstance(rels, list):
rels = [rels]
## TODO: refactor, use https://www.theguardian.com/environment/2023/jul/17/boat-wrecking-orcas-are-sadly-no-socialists
for rel in rels:
if rel.params.get("RELTYPE") == "PARENT":
parent = self.parent.object_by_uid(rel)
try:
parent = self.parent.object_by_uid(rel)
except error.NotFoundError:
continue
pend = parent.icalendar_component.get("DTEND")
if pend:
pend = pend.dt
Expand Down
5 changes: 0 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,8 @@
test_packages = [
"pytest",
"pytest-coverage",
"icalendar",
"coverage",
"tzlocal",
"pytz",
"xandikos",
"radicale",
"sphinx",
]

Expand Down Expand Up @@ -88,7 +84,6 @@
"recurring-ical-events>=2.0.0",
]
+ extra_packages,
tests_require=test_packages + extra_test_packages,
extras_require={
"test": test_packages,
},
Expand Down
14 changes: 14 additions & 0 deletions tests/test_caldav.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,20 @@ def testCreateChildParent(self):
assert rt == parent.id
assert rt.params["RELTYPE"] == "PARENT"

foo = parent_.get_relatives(reltypes={"PARENT"})
assert len(foo) == 1
assert len(foo["PARENT"]) == 1
assert [foo["PARENT"][0].icalendar_component["UID"] == grandparent.id]
foo = parent_.get_relatives(reltypes={"CHILD"})
assert len(foo) == 1
assert len(foo["CHILD"]) == 1
assert [foo["CHILD"][0].icalendar_component["UID"] == child.id]
foo = parent_.get_relatives(reltypes={"CHILD", "PARENT"})
assert len(foo) == 2
assert len(foo["CHILD"]) == 1
assert len(foo["PARENT"]) == 1
foo = parent_.get_relatives(relfilter=lambda x: x.params.get("GAP"))

def testSetDue(self):
self.skip_on_compatibility_flag("read_only")

Expand Down

0 comments on commit 63603d9

Please sign in to comment.