Skip to content

Commit d910be9

Browse files
committed
added CalendarAPI with the help of caldav
Signed-off-by: Alexander Piskun <[email protected]>
1 parent c12bd13 commit d910be9

File tree

11 files changed

+120
-6
lines changed

11 files changed

+120
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.3.1 - 2023-09-30]
6+
7+
### Added
8+
9+
- CalendarAPI with the help of [caldav](https://pypi.org/project/caldav/) package.
10+
511
## [0.3.0 - 2023-09-28]
612

713
### Added

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Python library that provides a robust and well-documented API that allows develo
2525
### Capabilities
2626
| **_Capability_** | Nextcloud 26 | Nextcloud 27 | Nextcloud 28 |
2727
|-----------------------|:------------:|:------------:|:------------:|
28+
| Calendar ||||
2829
| File System & Tags ||||
2930
| Nextcloud Talk** ||||
3031
| Notifications ||||
@@ -37,7 +38,7 @@ Python library that provides a robust and well-documented API that allows develo
3738
| SpeechToText* | N/A |||
3839

3940
&ast;_available only for NextcloudApp_<br>
40-
&ast;&ast; _work is in progress, not all API's is described, yet._
41+
&ast;&ast; _work is in progress, not all APIs are described yet._
4142

4243
### Differences between the Nextcloud and NextcloudApp classes
4344

docs/Installation.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ To use in the Nextcloud Application mode install it with additional ``app`` depe
1313

1414
python -m pip install --upgrade "nc_py_api[app]"
1515

16+
To use **Calendar API** just add **calendar** dependency, and command will look like this :command:`pip`::
17+
18+
python -m pip install --upgrade "nc_py_api[app,calendar]"
19+
1620
To join the development of **nc_py_api** api install development dependencies with :command:`pip`::
1721

1822
python -m pip install --upgrade "nc_py_api[dev]"

docs/reference/Calendar.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.. py:currentmodule:: nc_py_api.calendar
2+
3+
Calendar API
4+
============
5+
6+
.. note:: To make this API work you should install **nc_py_api** with **calendar** extra dependency.
7+
8+
.. code-block:: python
9+
10+
principal = nc.cal.principal()
11+
calendars = principal.calendars() # get list of calendars
12+
13+
``nc.cal`` is usual ``caldav.DAVClient`` object with the same API.
14+
15+
Documentation for ``caldav`` can be found here: `CalDAV <"https://caldav.readthedocs.io/en/latest">`_
16+
17+
.. class:: _CalendarAPI
18+
19+
Class that encapsulates ``caldav.DAVClient``. Avalaible as **cal** in the Nextcloud class.
20+
21+
.. note:: You should not call ``close`` or ``request`` methods of CalendarAPI, they will be removed somewhere
22+
in the future when ``caldav.DAVClient`` will be rewritten(API compatability will remains).

docs/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ Reference
1313
Exceptions
1414
Talk
1515
TalkBot
16+
Calendar
1617
Session

nc_py_api/_session.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ def dav(
240240
elif json is not None:
241241
headers.update({"Content-Type": "application/json"})
242242
data_bytes = dumps(json).encode("utf-8")
243-
return self._dav(method, quote(self.cfg.dav_url_suffix + path), headers, data_bytes, **kwargs)
243+
return self._dav(
244+
method,
245+
quote(self.cfg.dav_url_suffix + path) if isinstance(path, str) else path,
246+
headers,
247+
data_bytes,
248+
**kwargs,
249+
)
244250

245251
def dav_stream(
246252
self, method: str, path: str, data: Optional[Union[str, bytes]] = None, **kwargs
@@ -255,7 +261,12 @@ def _dav(self, method: str, path: str, headers: dict, data: Optional[bytes], **k
255261
self.init_adapter()
256262
timeout = kwargs.pop("timeout", self.cfg.options.timeout_dav)
257263
result = self.adapter.request(
258-
method, self.cfg.endpoint + path, headers=headers, content=data, timeout=timeout, **kwargs
264+
method,
265+
self.cfg.endpoint + path if isinstance(path, str) else str(path),
266+
headers=headers,
267+
content=data,
268+
timeout=timeout,
269+
**kwargs,
259270
)
260271
self.response_headers = result.headers
261272
return result

nc_py_api/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Version of nc_py_api."""
22

3-
__version__ = "0.3.0"
3+
__version__ = "0.3.1.dev0"

nc_py_api/calendar.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Nextcloud Calendar DAV API."""
2+
3+
from ._session import NcSessionBasic
4+
5+
try:
6+
from caldav.davclient import DAVClient, DAVResponse
7+
8+
class _CalendarAPI(DAVClient):
9+
"""Class that encapsulates ``caldav.DAVClient`` to work with the Nextcloud calendar."""
10+
11+
def __init__(self, session: NcSessionBasic):
12+
self._session = session
13+
super().__init__(session.cfg.dav_endpoint)
14+
15+
@property
16+
def available(self) -> bool:
17+
"""Returns True if ``caldav`` package is avalaible, False otherwise."""
18+
return True
19+
20+
def request(self, url, method="GET", body="", headers={}): # noqa pylint: disable=dangerous-default-value
21+
if isinstance(body, str):
22+
body = body.encode("UTF-8")
23+
if body:
24+
body = body.replace(b"\n", b"\r\n").replace(b"\r\r\n", b"\r\n")
25+
r = self._session.dav(method, url, data=body, headers=headers)
26+
return DAVResponse(r)
27+
28+
except ImportError:
29+
30+
class _CalendarAPI: # type: ignore
31+
"""A stub class in case **caldav** is missing."""
32+
33+
def __init__(self, session: NcSessionBasic):
34+
self._session = session
35+
36+
@property
37+
def available(self) -> bool:
38+
"""Returns True if ``caldav`` package is avalaible, False otherwise."""
39+
return False

nc_py_api/nextcloud.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ._theming import ThemingInfo, get_parsed_theme
1515
from .activity import _ActivityAPI
1616
from .apps import _AppsAPI
17+
from .calendar import _CalendarAPI
1718
from .ex_app.defs import ApiScope, LogLvl
1819
from .ex_app.ui.ui import UiApi
1920
from .files.files import FilesAPI
@@ -29,6 +30,8 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
2930
"""Nextcloud API for App management"""
3031
activity: _ActivityAPI
3132
"""Activity Application API"""
33+
cal: _CalendarAPI
34+
"""Nextcloud Calendar API"""
3235
files: FilesAPI
3336
"""Nextcloud API for File System and Files Sharing"""
3437
preferences: PreferencesAPI
@@ -50,6 +53,7 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
5053
def _init_api(self, session: NcSessionBasic):
5154
self.apps = _AppsAPI(session)
5255
self.activity = _ActivityAPI(session)
56+
self.cal = _CalendarAPI(session)
5357
self.files = FilesAPI(session)
5458
self.preferences = PreferencesAPI(session)
5559
self.notifications = _NotificationsAPI(session)

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ bench = [
6060
"numpy",
6161
"py-cpuinfo",
6262
]
63+
calendar = [
64+
"caldav==1.3.6",
65+
]
6366
dev = [
6467
"coverage",
65-
"nc_py_api[bench]",
68+
"nc_py_api[bench,calendar]",
6669
"pillow",
6770
"pre-commit",
6871
"pylint",
@@ -71,7 +74,7 @@ dev = [
7174
]
7275
docs = [
7376
"autodoc_pydantic>=2.0.1",
74-
"nc_py_api[app]",
77+
"nc_py_api[app,calendar]",
7578
"sphinx>=6.2",
7679
"sphinx-copybutton",
7780
"sphinx-inline-tabs",

tests/actual_tests/calendar_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import datetime
2+
3+
import pytest
4+
5+
6+
def test_create_delete(nc_client):
7+
if nc_client.cal.available is False:
8+
pytest.skip("``caldav`` package is not installed")
9+
10+
principal = nc_client.cal.principal()
11+
calendars = principal.calendars()
12+
assert calendars
13+
calendar = calendars[0]
14+
all_events_before = calendar.events()
15+
event = calendar.save_event(
16+
dtstart=datetime.datetime.now(),
17+
dtend=datetime.datetime.now() + datetime.timedelta(hours=1),
18+
summary="NcPyApi + CalDAV test",
19+
)
20+
all_events_after = calendar.events()
21+
assert len(all_events_after) == len(all_events_before) + 1
22+
event.delete()
23+
assert len(calendar.events()) == len(all_events_before)

0 commit comments

Comments
 (0)