Skip to content

Commit

Permalink
New download_log method (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
bigcat88 authored Jan 2, 2024
1 parent b9d6050 commit 0d73cdb
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 31 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ All notable changes to this project will be documented in this file.

### Added

- `download_log` method to download `nextcloud.log`. #199
- NextcloudApp: API for registering Speech to Text providers(*avalaible from Nextcloud 29*). #196
- NextcloudApp: API for registering Text Processing providers(*avalaible from Nextcloud 29*). #197
- NextcloudApp: API for registering Text Processing providers(*avalaible from Nextcloud 29*). #198

### Fixed

Expand Down
34 changes: 34 additions & 0 deletions nc_py_api/_session.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Session represents one connection to Nextcloud. All related stuff for these live here."""

import builtins
import pathlib
import re
import typing
from abc import ABC, abstractmethod
Expand Down Expand Up @@ -250,6 +252,15 @@ def user(self) -> str:
def set_user(self, user_id: str) -> None:
self._user = user_id

def download2stream(self, url_path: str, fp, dav: bool = False, **kwargs):
if isinstance(fp, str | pathlib.Path):
with builtins.open(fp, "wb") as f:
self._download2fp(url_path, f, dav, **kwargs)
elif hasattr(fp, "write"):
self._download2fp(url_path, fp, dav, **kwargs)
else:
raise TypeError("`fp` must be a path to file or an object with `write` method.")

def _get_adapter_kwargs(self, dav: bool) -> dict[str, typing.Any]:
if dav:
return {
Expand All @@ -276,6 +287,13 @@ def _response_event(self, response: Response) -> None:
return
self.response_headers = response.headers

def _download2fp(self, url_path: str, fp, dav: bool, **kwargs):
adapter = self.adapter_dav if dav else self.adapter
with adapter.stream("GET", url_path) as response:
check_error(response)
for data_chunk in response.iter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
fp.write(data_chunk)


class AsyncNcSessionBasic(NcSessionBase, ABC):
adapter: AsyncClient
Expand Down Expand Up @@ -354,6 +372,15 @@ async def user(self) -> str:
def set_user(self, user: str) -> None:
self._user = user

async def download2stream(self, url_path: str, fp, dav: bool = False, **kwargs):
if isinstance(fp, str | pathlib.Path):
with builtins.open(fp, "wb") as f:
await self._download2fp(url_path, f, dav, **kwargs)
elif hasattr(fp, "write"):
await self._download2fp(url_path, fp, dav, **kwargs)
else:
raise TypeError("`fp` must be a path to file or an object with `write` method.")

def _get_adapter_kwargs(self, dav: bool) -> dict[str, typing.Any]:
if dav:
return {
Expand All @@ -380,6 +407,13 @@ async def _response_event(self, response: Response) -> None:
return
self.response_headers = response.headers

async def _download2fp(self, url_path: str, fp, dav: bool, **kwargs):
adapter = self.adapter_dav if dav else self.adapter
async with adapter.stream("GET", url_path) as response:
check_error(response)
async for data_chunk in response.aiter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
fp.write(data_chunk)


class NcSession(NcSessionBasic):
cfg: Config
Expand Down
34 changes: 4 additions & 30 deletions nc_py_api/files/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,8 @@ def download2stream(self, path: str | FsNode, fp, **kwargs) -> None:
The object must implement the ``file.write`` method and be able to write binary data.
:param kwargs: **chunk_size** an int value specifying chunk size to write. Default = **5Mb**
"""
path = path.user_path if isinstance(path, FsNode) else path
if isinstance(fp, str | Path):
with builtins.open(fp, "wb") as f:
self.__download2stream(path, f, **kwargs)
elif hasattr(fp, "write"):
self.__download2stream(path, fp, **kwargs)
else:
raise TypeError("`fp` must be a path to file or an object with `write` method.")
path = quote(dav_get_obj_path(self._session.user, path.user_path if isinstance(path, FsNode) else path))
self._session.download2stream(path, fp, dav=True, **kwargs)

def download_directory_as_zip(self, path: str | FsNode, local_path: str | Path | None = None, **kwargs) -> Path:
"""Downloads a remote directory as zip archive.
Expand Down Expand Up @@ -439,12 +433,6 @@ def _listdir(
self._session.cfg.dav_url_suffix, webdav_response, user, path, properties, exclude_self, prop_type
)

def __download2stream(self, path: str, fp, **kwargs) -> None:
with self._session.adapter_dav.stream("GET", quote(dav_get_obj_path(self._session.user, path))) as response:
check_error(response, f"download_stream: user={self._session.user}, path={path}")
for data_chunk in response.iter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
fp.write(data_chunk)

def __upload_stream(self, path: str, fp, chunk_size: int) -> FsNode:
_tmp_path = "nc-py-api-" + random_string(56)
_dav_path = quote(dav_get_obj_path(self._session.user, _tmp_path, root_path="/uploads"))
Expand Down Expand Up @@ -561,14 +549,8 @@ async def download2stream(self, path: str | FsNode, fp, **kwargs) -> None:
The object must implement the ``file.write`` method and be able to write binary data.
:param kwargs: **chunk_size** an int value specifying chunk size to write. Default = **5Mb**
"""
path = path.user_path if isinstance(path, FsNode) else path
if isinstance(fp, str | Path):
with builtins.open(fp, "wb") as f:
await self.__download2stream(path, f, **kwargs)
elif hasattr(fp, "write"):
await self.__download2stream(path, fp, **kwargs)
else:
raise TypeError("`fp` must be a path to file or an object with `write` method.")
path = quote(dav_get_obj_path(await self._session.user, path.user_path if isinstance(path, FsNode) else path))
await self._session.download2stream(path, fp, dav=True, **kwargs)

async def download_directory_as_zip(
self, path: str | FsNode, local_path: str | Path | None = None, **kwargs
Expand Down Expand Up @@ -909,14 +891,6 @@ async def _listdir(
self._session.cfg.dav_url_suffix, webdav_response, user, path, properties, exclude_self, prop_type
)

async def __download2stream(self, path: str, fp, **kwargs) -> None:
async with self._session.adapter_dav.stream(
"GET", quote(dav_get_obj_path(await self._session.user, path))
) as response:
check_error(response, f"download_stream: user={await self._session.user}, path={path}")
async for data_chunk in response.aiter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
fp.write(data_chunk)

async def __upload_stream(self, path: str, fp, chunk_size: int) -> FsNode:
_tmp_path = "nc-py-api-" + random_string(56)
_dav_path = quote(dav_get_obj_path(await self._session.user, _tmp_path, root_path="/uploads"))
Expand Down
8 changes: 8 additions & 0 deletions nc_py_api/nextcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ def ocs(
"""Performs OCS call and returns OCS response payload data."""
return self._session.ocs(method, path, content=content, json=json, params=params, **kwargs)

def download_log(self, fp) -> None:
"""Downloads Nextcloud log file. Requires Admin privileges."""
self._session.download2stream("/index.php/settings/admin/log/download", fp)


class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
apps: _AsyncAppsAPI
Expand Down Expand Up @@ -229,6 +233,10 @@ async def ocs(
"""Performs OCS call and returns OCS response payload data."""
return await self._session.ocs(method, path, content=content, json=json, params=params, **kwargs)

async def download_log(self, fp) -> None:
"""Downloads Nextcloud log file. Requires Admin privileges."""
await self._session.download2stream("/index.php/settings/admin/log/download", fp)


class Nextcloud(_NextcloudBasic):
"""Nextcloud client class.
Expand Down
14 changes: 14 additions & 0 deletions tests/actual_tests/misc_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import io
import os

import pytest
Expand Down Expand Up @@ -206,3 +207,16 @@ async def test_perform_login_async(anc_any):
assert not new_nc._session._capabilities
await new_nc.perform_login()
assert new_nc._session._capabilities


def test_download_log(nc_any):
buf = io.BytesIO()
nc_any.download_log(buf)
assert buf.tell() > 0


@pytest.mark.asyncio(scope="session")
async def test_download_log_async(anc_any):
buf = io.BytesIO()
await anc_any.download_log(buf)
assert buf.tell() > 0

0 comments on commit 0d73cdb

Please sign in to comment.