Skip to content

Commit

Permalink
added CalendarAPI with the help of caldav
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Piskun <[email protected]>
  • Loading branch information
bigcat88 committed Sep 29, 2023
1 parent c12bd13 commit d910be9
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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

## [0.3.1 - 2023-09-30]

### Added

- CalendarAPI with the help of [caldav](https://pypi.org/project/caldav/) package.

## [0.3.0 - 2023-09-28]

### Added
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Python library that provides a robust and well-documented API that allows develo
### Capabilities
| **_Capability_** | Nextcloud 26 | Nextcloud 27 | Nextcloud 28 |
|-----------------------|:------------:|:------------:|:------------:|
| Calendar ||||
| File System & Tags ||||
| Nextcloud Talk** ||||
| Notifications ||||
Expand All @@ -37,7 +38,7 @@ Python library that provides a robust and well-documented API that allows develo
| SpeechToText* | N/A |||

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

### Differences between the Nextcloud and NextcloudApp classes

Expand Down
4 changes: 4 additions & 0 deletions docs/Installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ To use in the Nextcloud Application mode install it with additional ``app`` depe

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

To use **Calendar API** just add **calendar** dependency, and command will look like this :command:`pip`::

python -m pip install --upgrade "nc_py_api[app,calendar]"

To join the development of **nc_py_api** api install development dependencies with :command:`pip`::

python -m pip install --upgrade "nc_py_api[dev]"
Expand Down
22 changes: 22 additions & 0 deletions docs/reference/Calendar.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.. py:currentmodule:: nc_py_api.calendar
Calendar API
============

.. note:: To make this API work you should install **nc_py_api** with **calendar** extra dependency.

.. code-block:: python
principal = nc.cal.principal()
calendars = principal.calendars() # get list of calendars
``nc.cal`` is usual ``caldav.DAVClient`` object with the same API.

Documentation for ``caldav`` can be found here: `CalDAV <"https://caldav.readthedocs.io/en/latest">`_

.. class:: _CalendarAPI

Class that encapsulates ``caldav.DAVClient``. Avalaible as **cal** in the Nextcloud class.

.. note:: You should not call ``close`` or ``request`` methods of CalendarAPI, they will be removed somewhere
in the future when ``caldav.DAVClient`` will be rewritten(API compatability will remains).
1 change: 1 addition & 0 deletions docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Reference
Exceptions
Talk
TalkBot
Calendar
Session
15 changes: 13 additions & 2 deletions nc_py_api/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,13 @@ def dav(
elif json is not None:
headers.update({"Content-Type": "application/json"})
data_bytes = dumps(json).encode("utf-8")
return self._dav(method, quote(self.cfg.dav_url_suffix + path), headers, data_bytes, **kwargs)
return self._dav(
method,
quote(self.cfg.dav_url_suffix + path) if isinstance(path, str) else path,
headers,
data_bytes,
**kwargs,
)

def dav_stream(
self, method: str, path: str, data: Optional[Union[str, bytes]] = None, **kwargs
Expand All @@ -255,7 +261,12 @@ def _dav(self, method: str, path: str, headers: dict, data: Optional[bytes], **k
self.init_adapter()
timeout = kwargs.pop("timeout", self.cfg.options.timeout_dav)
result = self.adapter.request(
method, self.cfg.endpoint + path, headers=headers, content=data, timeout=timeout, **kwargs
method,
self.cfg.endpoint + path if isinstance(path, str) else str(path),
headers=headers,
content=data,
timeout=timeout,
**kwargs,
)
self.response_headers = result.headers
return result
Expand Down
2 changes: 1 addition & 1 deletion nc_py_api/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version of nc_py_api."""

__version__ = "0.3.0"
__version__ = "0.3.1.dev0"
39 changes: 39 additions & 0 deletions nc_py_api/calendar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Nextcloud Calendar DAV API."""

from ._session import NcSessionBasic

try:
from caldav.davclient import DAVClient, DAVResponse

class _CalendarAPI(DAVClient):
"""Class that encapsulates ``caldav.DAVClient`` to work with the Nextcloud calendar."""

def __init__(self, session: NcSessionBasic):
self._session = session
super().__init__(session.cfg.dav_endpoint)

@property
def available(self) -> bool:
"""Returns True if ``caldav`` package is avalaible, False otherwise."""
return True

def request(self, url, method="GET", body="", headers={}): # noqa pylint: disable=dangerous-default-value
if isinstance(body, str):
body = body.encode("UTF-8")
if body:
body = body.replace(b"\n", b"\r\n").replace(b"\r\r\n", b"\r\n")
r = self._session.dav(method, url, data=body, headers=headers)
return DAVResponse(r)

except ImportError:

class _CalendarAPI: # type: ignore
"""A stub class in case **caldav** is missing."""

def __init__(self, session: NcSessionBasic):
self._session = session

@property
def available(self) -> bool:
"""Returns True if ``caldav`` package is avalaible, False otherwise."""
return False
4 changes: 4 additions & 0 deletions nc_py_api/nextcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ._theming import ThemingInfo, get_parsed_theme
from .activity import _ActivityAPI
from .apps import _AppsAPI
from .calendar import _CalendarAPI
from .ex_app.defs import ApiScope, LogLvl
from .ex_app.ui.ui import UiApi
from .files.files import FilesAPI
Expand All @@ -29,6 +30,8 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
"""Nextcloud API for App management"""
activity: _ActivityAPI
"""Activity Application API"""
cal: _CalendarAPI
"""Nextcloud Calendar API"""
files: FilesAPI
"""Nextcloud API for File System and Files Sharing"""
preferences: PreferencesAPI
Expand All @@ -50,6 +53,7 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
def _init_api(self, session: NcSessionBasic):
self.apps = _AppsAPI(session)
self.activity = _ActivityAPI(session)
self.cal = _CalendarAPI(session)
self.files = FilesAPI(session)
self.preferences = PreferencesAPI(session)
self.notifications = _NotificationsAPI(session)
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ bench = [
"numpy",
"py-cpuinfo",
]
calendar = [
"caldav==1.3.6",
]
dev = [
"coverage",
"nc_py_api[bench]",
"nc_py_api[bench,calendar]",
"pillow",
"pre-commit",
"pylint",
Expand All @@ -71,7 +74,7 @@ dev = [
]
docs = [
"autodoc_pydantic>=2.0.1",
"nc_py_api[app]",
"nc_py_api[app,calendar]",
"sphinx>=6.2",
"sphinx-copybutton",
"sphinx-inline-tabs",
Expand Down
23 changes: 23 additions & 0 deletions tests/actual_tests/calendar_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import datetime

import pytest


def test_create_delete(nc_client):
if nc_client.cal.available is False:
pytest.skip("``caldav`` package is not installed")

principal = nc_client.cal.principal()
calendars = principal.calendars()
assert calendars
calendar = calendars[0]
all_events_before = calendar.events()
event = calendar.save_event(
dtstart=datetime.datetime.now(),
dtend=datetime.datetime.now() + datetime.timedelta(hours=1),
summary="NcPyApi + CalDAV test",
)
all_events_after = calendar.events()
assert len(all_events_after) == len(all_events_before) + 1
event.delete()
assert len(calendar.events()) == len(all_events_before)

0 comments on commit d910be9

Please sign in to comment.