Skip to content

Commit

Permalink
feat: Add task processing API (#254)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexander Piskun <[email protected]>
  • Loading branch information
provokateurin and bigcat88 authored Jul 9, 2024
1 parent 818dc2e commit 619ad3f
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ All notable changes to this project will be documented in this file.
- `LoginFlowV2` implementation by @blvdek #255
- `files.get_tags` function to get all tags assigned to the file or directory. #260
- NextcloudApp: `nc.ui.files_dropdown_menu.register_ex` to register new version of FileActions(AppAPI 2.6.0+) #252
- NextcloudApp: `enabled_state` property to check if current ExApp is disabled or enabled.
- NextcloudApp: `enabled_state` property to check if the current ExApp is disabled or enabled. #268
- NextcloudApp: support for the new AI API for the Nextcloud 30. #254

## [0.13.0 - 2024-04-28]

Expand Down
3 changes: 3 additions & 0 deletions docs/reference/ExApp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ UI methods should be accessed with the help of :class:`~nc_py_api.nextcloud.Next
.. autoclass:: nc_py_api.ex_app.providers.translations._TranslationsProviderAPI
:members:

.. autoclass:: nc_py_api.ex_app.providers.task_processing._TaskProcessingProviderAPI
:members:

.. autoclass:: nc_py_api.ex_app.events_listener.EventsListener
:members:

Expand Down
10 changes: 8 additions & 2 deletions nc_py_api/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,16 @@ def ocs(
content: bytes | str | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None,
json: dict | list | None = None,
params: dict | None = None,
files: dict | None = None,
**kwargs,
):
self.init_adapter()
info = f"request: {method} {path}"
nested_req = kwargs.pop("nested_req", False)
try:
response = self.adapter.request(method, path, content=content, json=json, params=params, **kwargs)
response = self.adapter.request(
method, path, content=content, json=json, params=params, files=files, **kwargs
)
except ReadTimeout:
raise NextcloudException(408, info=info) from None

Expand Down Expand Up @@ -315,13 +318,16 @@ async def ocs(
content: bytes | str | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None = None,
json: dict | list | None = None,
params: dict | None = None,
files: dict | None = None,
**kwargs,
):
self.init_adapter()
info = f"request: {method} {path}"
nested_req = kwargs.pop("nested_req", False)
try:
response = await self.adapter.request(method, path, content=content, json=json, params=params, **kwargs)
response = await self.adapter.request(
method, path, content=content, json=json, params=params, files=files, **kwargs
)
except ReadTimeout:
raise NextcloudException(408, info=info) from None

Expand Down
7 changes: 7 additions & 0 deletions nc_py_api/ex_app/providers/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ..._session import AsyncNcSessionApp, NcSessionApp
from .speech_to_text import _AsyncSpeechToTextProviderAPI, _SpeechToTextProviderAPI
from .task_processing import _AsyncTaskProcessingProviderAPI, _TaskProcessingProviderAPI
from .text_processing import _AsyncTextProcessingProviderAPI, _TextProcessingProviderAPI
from .translations import _AsyncTranslationsProviderAPI, _TranslationsProviderAPI

Expand All @@ -15,11 +16,14 @@ class ProvidersApi:
"""TextProcessing Provider API."""
translations: _TranslationsProviderAPI
"""Translations Provider API."""
task_processing: _TaskProcessingProviderAPI
"""TaskProcessing Provider API."""

def __init__(self, session: NcSessionApp):
self.speech_to_text = _SpeechToTextProviderAPI(session)
self.text_processing = _TextProcessingProviderAPI(session)
self.translations = _TranslationsProviderAPI(session)
self.task_processing = _TaskProcessingProviderAPI(session)


class AsyncProvidersApi:
Expand All @@ -31,8 +35,11 @@ class AsyncProvidersApi:
"""TextProcessing Provider API."""
translations: _AsyncTranslationsProviderAPI
"""Translations Provider API."""
task_processing: _AsyncTaskProcessingProviderAPI
"""TaskProcessing Provider API."""

def __init__(self, session: AsyncNcSessionApp):
self.speech_to_text = _AsyncSpeechToTextProviderAPI(session)
self.text_processing = _AsyncTextProcessingProviderAPI(session)
self.translations = _AsyncTranslationsProviderAPI(session)
self.task_processing = _AsyncTaskProcessingProviderAPI(session)
189 changes: 189 additions & 0 deletions nc_py_api/ex_app/providers/task_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
"""Nextcloud API for declaring TaskProcessing provider."""

import contextlib
import dataclasses
import typing

from ..._exceptions import NextcloudException, NextcloudExceptionNotFound
from ..._misc import require_capabilities
from ..._session import AsyncNcSessionApp, NcSessionApp

_EP_SUFFIX: str = "ai_provider/task_processing"


@dataclasses.dataclass
class TaskProcessingProvider:
"""TaskProcessing provider description."""

def __init__(self, raw_data: dict):
self._raw_data = raw_data

@property
def name(self) -> str:
"""Unique ID for the provider."""
return self._raw_data["name"]

@property
def display_name(self) -> str:
"""Providers display name."""
return self._raw_data["display_name"]

@property
def task_type(self) -> str:
"""The TaskType provided by this provider."""
return self._raw_data["task_type"]

def __repr__(self):
return f"<{self.__class__.__name__} name={self.name}, type={self.task_type}>"


class _TaskProcessingProviderAPI:
"""API for TaskProcessing providers, available as **nc.providers.task_processing.<method>**."""

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

def register(self, name: str, display_name: str, task_type: str) -> None:
"""Registers or edit the TaskProcessing provider."""
require_capabilities("app_api", self._session.capabilities)
params = {
"name": name,
"displayName": display_name,
"taskType": task_type,
}
self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)

def unregister(self, name: str, not_fail=True) -> None:
"""Removes TaskProcessing provider."""
require_capabilities("app_api", self._session.capabilities)
try:
self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"name": name})
except NextcloudExceptionNotFound as e:
if not not_fail:
raise e from None

def next_task(self, provider_ids: list[str], task_types: list[str]) -> dict[str, typing.Any]:
"""Get the next task processing task from Nextcloud."""
with contextlib.suppress(NextcloudException):
if r := self._session.ocs(
"GET",
"/ocs/v2.php/taskprocessing/tasks_provider/next",
json={"providerIds": provider_ids, "taskTypeIds": task_types},
):
return r
return {}

def set_progress(self, task_id: int, progress: float) -> dict[str, typing.Any]:
"""Report new progress value of the task to Nextcloud. Progress should be in range from 0.0 to 100.0."""
with contextlib.suppress(NextcloudException):
if r := self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/progress",
json={"taskId": task_id, "progress": progress / 100.0},
):
return r
return {}

def upload_result_file(self, task_id: int, file: bytes | str | typing.Any) -> int:
"""Uploads file and returns fileID that should be used in the ``report_result`` function.
.. note:: ``file`` can be any file-like object.
"""
return self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/file",
files={"file": file},
)["fileId"]

def report_result(
self,
task_id: int,
output: dict[str, typing.Any] | None = None,
error_message: str | None = None,
) -> dict[str, typing.Any]:
"""Report result of the task processing to Nextcloud."""
with contextlib.suppress(NextcloudException):
if r := self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/result",
json={"taskId": task_id, "output": output, "errorMessage": error_message},
):
return r
return {}


class _AsyncTaskProcessingProviderAPI:
"""Async API for TaskProcessing providers."""

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

async def register(self, name: str, display_name: str, task_type: str) -> None:
"""Registers or edit the TaskProcessing provider."""
require_capabilities("app_api", await self._session.capabilities)
params = {
"name": name,
"displayName": display_name,
"taskType": task_type,
}
await self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)

async def unregister(self, name: str, not_fail=True) -> None:
"""Removes TaskProcessing provider."""
require_capabilities("app_api", await self._session.capabilities)
try:
await self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"name": name})
except NextcloudExceptionNotFound as e:
if not not_fail:
raise e from None

async def next_task(self, provider_ids: list[str], task_types: list[str]) -> dict[str, typing.Any]:
"""Get the next task processing task from Nextcloud."""
with contextlib.suppress(NextcloudException):
if r := await self._session.ocs(
"GET",
"/ocs/v2.php/taskprocessing/tasks_provider/next",
json={"providerIds": provider_ids, "taskTypeIds": task_types},
):
return r
return {}

async def set_progress(self, task_id: int, progress: float) -> dict[str, typing.Any]:
"""Report new progress value of the task to Nextcloud. Progress should be in range from 0.0 to 100.0."""
with contextlib.suppress(NextcloudException):
if r := await self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/progress",
json={"taskId": task_id, "progress": progress / 100.0},
):
return r
return {}

async def upload_result_file(self, task_id: int, file: bytes | str | typing.Any) -> int:
"""Uploads file and returns fileID that should be used in the ``report_result`` function.
.. note:: ``file`` can be any file-like object.
"""
return (
await self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/file",
files={"file": file},
)
)["fileId"]

async def report_result(
self,
task_id: int,
output: dict[str, typing.Any] | None = None,
error_message: str | None = None,
) -> dict[str, typing.Any]:
"""Report result of the task processing to Nextcloud."""
with contextlib.suppress(NextcloudException):
if r := await self._session.ocs(
"POST",
f"/ocs/v2.php/taskprocessing/tasks_provider/{task_id}/result",
json={"taskId": task_id, "output": output, "errorMessage": error_message},
):
return r
return {}

0 comments on commit 619ad3f

Please sign in to comment.