Skip to content

Commit 619ad3f

Browse files
feat: Add task processing API (#254)
Co-authored-by: Alexander Piskun <[email protected]>
1 parent 818dc2e commit 619ad3f

File tree

5 files changed

+209
-3
lines changed

5 files changed

+209
-3
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ All notable changes to this project will be documented in this file.
99
- `LoginFlowV2` implementation by @blvdek #255
1010
- `files.get_tags` function to get all tags assigned to the file or directory. #260
1111
- NextcloudApp: `nc.ui.files_dropdown_menu.register_ex` to register new version of FileActions(AppAPI 2.6.0+) #252
12-
- NextcloudApp: `enabled_state` property to check if current ExApp is disabled or enabled.
12+
- NextcloudApp: `enabled_state` property to check if the current ExApp is disabled or enabled. #268
13+
- NextcloudApp: support for the new AI API for the Nextcloud 30. #254
1314

1415
## [0.13.0 - 2024-04-28]
1516

docs/reference/ExApp.rst

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ UI methods should be accessed with the help of :class:`~nc_py_api.nextcloud.Next
8787
.. autoclass:: nc_py_api.ex_app.providers.translations._TranslationsProviderAPI
8888
:members:
8989

90+
.. autoclass:: nc_py_api.ex_app.providers.task_processing._TaskProcessingProviderAPI
91+
:members:
92+
9093
.. autoclass:: nc_py_api.ex_app.events_listener.EventsListener
9194
:members:
9295

nc_py_api/_session.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,16 @@ def ocs(
197197
content: bytes | str | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None,
198198
json: dict | list | None = None,
199199
params: dict | None = None,
200+
files: dict | None = None,
200201
**kwargs,
201202
):
202203
self.init_adapter()
203204
info = f"request: {method} {path}"
204205
nested_req = kwargs.pop("nested_req", False)
205206
try:
206-
response = self.adapter.request(method, path, content=content, json=json, params=params, **kwargs)
207+
response = self.adapter.request(
208+
method, path, content=content, json=json, params=params, files=files, **kwargs
209+
)
207210
except ReadTimeout:
208211
raise NextcloudException(408, info=info) from None
209212

@@ -315,13 +318,16 @@ async def ocs(
315318
content: bytes | str | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None,
316319
json: dict | list | None = None,
317320
params: dict | None = None,
321+
files: dict | None = None,
318322
**kwargs,
319323
):
320324
self.init_adapter()
321325
info = f"request: {method} {path}"
322326
nested_req = kwargs.pop("nested_req", False)
323327
try:
324-
response = await self.adapter.request(method, path, content=content, json=json, params=params, **kwargs)
328+
response = await self.adapter.request(
329+
method, path, content=content, json=json, params=params, files=files, **kwargs
330+
)
325331
except ReadTimeout:
326332
raise NextcloudException(408, info=info) from None
327333

nc_py_api/ex_app/providers/providers.py

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from ..._session import AsyncNcSessionApp, NcSessionApp
44
from .speech_to_text import _AsyncSpeechToTextProviderAPI, _SpeechToTextProviderAPI
5+
from .task_processing import _AsyncTaskProcessingProviderAPI, _TaskProcessingProviderAPI
56
from .text_processing import _AsyncTextProcessingProviderAPI, _TextProcessingProviderAPI
67
from .translations import _AsyncTranslationsProviderAPI, _TranslationsProviderAPI
78

@@ -15,11 +16,14 @@ class ProvidersApi:
1516
"""TextProcessing Provider API."""
1617
translations: _TranslationsProviderAPI
1718
"""Translations Provider API."""
19+
task_processing: _TaskProcessingProviderAPI
20+
"""TaskProcessing Provider API."""
1821

1922
def __init__(self, session: NcSessionApp):
2023
self.speech_to_text = _SpeechToTextProviderAPI(session)
2124
self.text_processing = _TextProcessingProviderAPI(session)
2225
self.translations = _TranslationsProviderAPI(session)
26+
self.task_processing = _TaskProcessingProviderAPI(session)
2327

2428

2529
class AsyncProvidersApi:
@@ -31,8 +35,11 @@ class AsyncProvidersApi:
3135
"""TextProcessing Provider API."""
3236
translations: _AsyncTranslationsProviderAPI
3337
"""Translations Provider API."""
38+
task_processing: _AsyncTaskProcessingProviderAPI
39+
"""TaskProcessing Provider API."""
3440

3541
def __init__(self, session: AsyncNcSessionApp):
3642
self.speech_to_text = _AsyncSpeechToTextProviderAPI(session)
3743
self.text_processing = _AsyncTextProcessingProviderAPI(session)
3844
self.translations = _AsyncTranslationsProviderAPI(session)
45+
self.task_processing = _AsyncTaskProcessingProviderAPI(session)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"""Nextcloud API for declaring TaskProcessing provider."""
2+
3+
import contextlib
4+
import dataclasses
5+
import typing
6+
7+
from ..._exceptions import NextcloudException, NextcloudExceptionNotFound
8+
from ..._misc import require_capabilities
9+
from ..._session import AsyncNcSessionApp, NcSessionApp
10+
11+
_EP_SUFFIX: str = "ai_provider/task_processing"
12+
13+
14+
@dataclasses.dataclass
15+
class TaskProcessingProvider:
16+
"""TaskProcessing provider description."""
17+
18+
def __init__(self, raw_data: dict):
19+
self._raw_data = raw_data
20+
21+
@property
22+
def name(self) -> str:
23+
"""Unique ID for the provider."""
24+
return self._raw_data["name"]
25+
26+
@property
27+
def display_name(self) -> str:
28+
"""Providers display name."""
29+
return self._raw_data["display_name"]
30+
31+
@property
32+
def task_type(self) -> str:
33+
"""The TaskType provided by this provider."""
34+
return self._raw_data["task_type"]
35+
36+
def __repr__(self):
37+
return f"<{self.__class__.__name__} name={self.name}, type={self.task_type}>"
38+
39+
40+
class _TaskProcessingProviderAPI:
41+
"""API for TaskProcessing providers, available as **nc.providers.task_processing.<method>**."""
42+
43+
def __init__(self, session: NcSessionApp):
44+
self._session = session
45+
46+
def register(self, name: str, display_name: str, task_type: str) -> None:
47+
"""Registers or edit the TaskProcessing provider."""
48+
require_capabilities("app_api", self._session.capabilities)
49+
params = {
50+
"name": name,
51+
"displayName": display_name,
52+
"taskType": task_type,
53+
}
54+
self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
55+
56+
def unregister(self, name: str, not_fail=True) -> None:
57+
"""Removes TaskProcessing provider."""
58+
require_capabilities("app_api", self._session.capabilities)
59+
try:
60+
self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"name": name})
61+
except NextcloudExceptionNotFound as e:
62+
if not not_fail:
63+
raise e from None
64+
65+
def next_task(self, provider_ids: list[str], task_types: list[str]) -> dict[str, typing.Any]:
66+
"""Get the next task processing task from Nextcloud."""
67+
with contextlib.suppress(NextcloudException):
68+
if r := self._session.ocs(
69+
"GET",
70+
"/ocs/v2.php/taskprocessing/tasks_provider/next",
71+
json={"providerIds": provider_ids, "taskTypeIds": task_types},
72+
):
73+
return r
74+
return {}
75+
76+
def set_progress(self, task_id: int, progress: float) -> dict[str, typing.Any]:
77+
"""Report new progress value of the task to Nextcloud. Progress should be in range from 0.0 to 100.0."""
78+
with contextlib.suppress(NextcloudException):
79+
if r := self._session.ocs(
80+
"POST",
81+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/progress",
82+
json={"taskId": task_id, "progress": progress / 100.0},
83+
):
84+
return r
85+
return {}
86+
87+
def upload_result_file(self, task_id: int, file: bytes | str | typing.Any) -> int:
88+
"""Uploads file and returns fileID that should be used in the ``report_result`` function.
89+
90+
.. note:: ``file`` can be any file-like object.
91+
"""
92+
return self._session.ocs(
93+
"POST",
94+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/file",
95+
files={"file": file},
96+
)["fileId"]
97+
98+
def report_result(
99+
self,
100+
task_id: int,
101+
output: dict[str, typing.Any] | None = None,
102+
error_message: str | None = None,
103+
) -> dict[str, typing.Any]:
104+
"""Report result of the task processing to Nextcloud."""
105+
with contextlib.suppress(NextcloudException):
106+
if r := self._session.ocs(
107+
"POST",
108+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/result",
109+
json={"taskId": task_id, "output": output, "errorMessage": error_message},
110+
):
111+
return r
112+
return {}
113+
114+
115+
class _AsyncTaskProcessingProviderAPI:
116+
"""Async API for TaskProcessing providers."""
117+
118+
def __init__(self, session: AsyncNcSessionApp):
119+
self._session = session
120+
121+
async def register(self, name: str, display_name: str, task_type: str) -> None:
122+
"""Registers or edit the TaskProcessing provider."""
123+
require_capabilities("app_api", await self._session.capabilities)
124+
params = {
125+
"name": name,
126+
"displayName": display_name,
127+
"taskType": task_type,
128+
}
129+
await self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
130+
131+
async def unregister(self, name: str, not_fail=True) -> None:
132+
"""Removes TaskProcessing provider."""
133+
require_capabilities("app_api", await self._session.capabilities)
134+
try:
135+
await self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"name": name})
136+
except NextcloudExceptionNotFound as e:
137+
if not not_fail:
138+
raise e from None
139+
140+
async def next_task(self, provider_ids: list[str], task_types: list[str]) -> dict[str, typing.Any]:
141+
"""Get the next task processing task from Nextcloud."""
142+
with contextlib.suppress(NextcloudException):
143+
if r := await self._session.ocs(
144+
"GET",
145+
"/ocs/v2.php/taskprocessing/tasks_provider/next",
146+
json={"providerIds": provider_ids, "taskTypeIds": task_types},
147+
):
148+
return r
149+
return {}
150+
151+
async def set_progress(self, task_id: int, progress: float) -> dict[str, typing.Any]:
152+
"""Report new progress value of the task to Nextcloud. Progress should be in range from 0.0 to 100.0."""
153+
with contextlib.suppress(NextcloudException):
154+
if r := await self._session.ocs(
155+
"POST",
156+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/progress",
157+
json={"taskId": task_id, "progress": progress / 100.0},
158+
):
159+
return r
160+
return {}
161+
162+
async def upload_result_file(self, task_id: int, file: bytes | str | typing.Any) -> int:
163+
"""Uploads file and returns fileID that should be used in the ``report_result`` function.
164+
165+
.. note:: ``file`` can be any file-like object.
166+
"""
167+
return (
168+
await self._session.ocs(
169+
"POST",
170+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/file",
171+
files={"file": file},
172+
)
173+
)["fileId"]
174+
175+
async def report_result(
176+
self,
177+
task_id: int,
178+
output: dict[str, typing.Any] | None = None,
179+
error_message: str | None = None,
180+
) -> dict[str, typing.Any]:
181+
"""Report result of the task processing to Nextcloud."""
182+
with contextlib.suppress(NextcloudException):
183+
if r := await self._session.ocs(
184+
"POST",
185+
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/result",
186+
json={"taskId": task_id, "output": output, "errorMessage": error_message},
187+
):
188+
return r
189+
return {}

0 commit comments

Comments
 (0)