nc-py-api 0.11.0__py3-none-any.whl → 0.18.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nc_py_api/__init__.py +1 -1
- nc_py_api/_session.py +27 -14
- nc_py_api/_version.py +1 -1
- nc_py_api/apps.py +0 -13
- nc_py_api/ex_app/__init__.py +13 -3
- nc_py_api/ex_app/defs.py +17 -29
- nc_py_api/ex_app/events_listener.py +137 -0
- nc_py_api/ex_app/integration_fastapi.py +25 -14
- nc_py_api/ex_app/logging.py +46 -0
- nc_py_api/ex_app/misc.py +6 -1
- nc_py_api/ex_app/occ_commands.py +153 -0
- nc_py_api/ex_app/providers/providers.py +7 -21
- nc_py_api/ex_app/providers/task_processing.py +261 -0
- nc_py_api/ex_app/ui/files_actions.py +45 -61
- nc_py_api/files/__init__.py +76 -6
- nc_py_api/files/_files.py +12 -0
- nc_py_api/files/files.py +26 -488
- nc_py_api/files/files_async.py +528 -0
- nc_py_api/loginflow_v2.py +161 -0
- nc_py_api/nextcloud.py +77 -21
- nc_py_api/talk_bot.py +5 -0
- nc_py_api/users.py +3 -3
- nc_py_api/webhooks.py +224 -0
- {nc_py_api-0.11.0.dist-info → nc_py_api-0.18.1.dist-info}/METADATA +35 -23
- nc_py_api-0.18.1.dist-info/RECORD +53 -0
- {nc_py_api-0.11.0.dist-info → nc_py_api-0.18.1.dist-info}/WHEEL +1 -1
- {nc_py_api-0.11.0.dist-info → nc_py_api-0.18.1.dist-info}/licenses/AUTHORS +1 -0
- nc_py_api/ex_app/providers/speech_to_text.py +0 -128
- nc_py_api/ex_app/providers/text_processing.py +0 -135
- nc_py_api/ex_app/providers/translations.py +0 -165
- nc_py_api-0.11.0.dist-info/RECORD +0 -49
- {nc_py_api-0.11.0.dist-info → nc_py_api-0.18.1.dist-info}/licenses/LICENSE.txt +0 -0
nc_py_api/files/__init__.py
CHANGED
|
@@ -4,10 +4,19 @@ import dataclasses
|
|
|
4
4
|
import datetime
|
|
5
5
|
import email.utils
|
|
6
6
|
import enum
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
7
9
|
import warnings
|
|
8
10
|
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
9
13
|
from .. import _misc
|
|
10
14
|
|
|
15
|
+
user_regex = re.compile(r"(?:files|trashbin|versions)/([^/]+)/")
|
|
16
|
+
"""Regex for evaluating user from full path string; instantiated once on import."""
|
|
17
|
+
user_path_regex = re.compile(r".*?(files|trashbin|versions)/([^/]+)/")
|
|
18
|
+
"""Regex for evaluating user path from full path string; instantiated once on import."""
|
|
19
|
+
|
|
11
20
|
|
|
12
21
|
class LockType(enum.IntEnum):
|
|
13
22
|
"""Nextcloud File Locks types."""
|
|
@@ -200,9 +209,7 @@ class FsNode:
|
|
|
200
209
|
)
|
|
201
210
|
|
|
202
211
|
def __eq__(self, other):
|
|
203
|
-
|
|
204
|
-
return True
|
|
205
|
-
return False
|
|
212
|
+
return bool(self.file_id and self.file_id == other.file_id)
|
|
206
213
|
|
|
207
214
|
@property
|
|
208
215
|
def has_extra(self) -> bool:
|
|
@@ -217,12 +224,12 @@ class FsNode:
|
|
|
217
224
|
@property
|
|
218
225
|
def user(self) -> str:
|
|
219
226
|
"""Returns user ID extracted from the `full_path`."""
|
|
220
|
-
return self.full_path
|
|
227
|
+
return user_regex.findall(self.full_path)[0]
|
|
221
228
|
|
|
222
229
|
@property
|
|
223
230
|
def user_path(self) -> str:
|
|
224
231
|
"""Returns path relative to the user's root directory."""
|
|
225
|
-
return
|
|
232
|
+
return user_path_regex.sub("", self.full_path, count=1)
|
|
226
233
|
|
|
227
234
|
@property
|
|
228
235
|
def is_shared(self) -> bool:
|
|
@@ -279,12 +286,13 @@ class FilePermissions(enum.IntFlag):
|
|
|
279
286
|
"""Access to re-share object(s)"""
|
|
280
287
|
|
|
281
288
|
|
|
282
|
-
def permissions_to_str(permissions: int, is_dir: bool = False) -> str:
|
|
289
|
+
def permissions_to_str(permissions: int | str, is_dir: bool = False) -> str:
|
|
283
290
|
"""Converts integer permissions to string permissions.
|
|
284
291
|
|
|
285
292
|
:param permissions: concatenation of ``FilePermissions`` integer flags.
|
|
286
293
|
:param is_dir: Flag indicating is permissions related to the directory object or not.
|
|
287
294
|
"""
|
|
295
|
+
permissions = int(permissions) if not isinstance(permissions, int) else permissions
|
|
288
296
|
r = ""
|
|
289
297
|
if permissions & FilePermissions.PERMISSION_SHARE:
|
|
290
298
|
r += "R"
|
|
@@ -461,3 +469,65 @@ class Share:
|
|
|
461
469
|
f"{self.share_type.name}: `{self.path}` with id={self.share_id}"
|
|
462
470
|
f" from {self.share_owner} to {self.share_with}"
|
|
463
471
|
)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
class ActionFileInfo(BaseModel):
|
|
475
|
+
"""Information Nextcloud sends to the External Application about File Nodes affected in action."""
|
|
476
|
+
|
|
477
|
+
fileId: int
|
|
478
|
+
"""FileID without Nextcloud instance ID"""
|
|
479
|
+
name: str
|
|
480
|
+
"""Name of the file/directory"""
|
|
481
|
+
directory: str
|
|
482
|
+
"""Directory relative to the user's home directory"""
|
|
483
|
+
etag: str
|
|
484
|
+
mime: str
|
|
485
|
+
fileType: str
|
|
486
|
+
"""**file** or **dir**"""
|
|
487
|
+
size: int
|
|
488
|
+
"""size of file/directory"""
|
|
489
|
+
favorite: str
|
|
490
|
+
"""**true** or **false**"""
|
|
491
|
+
permissions: int
|
|
492
|
+
"""Combination of :py:class:`~nc_py_api.files.FilePermissions` values"""
|
|
493
|
+
mtime: int
|
|
494
|
+
"""Last modified time"""
|
|
495
|
+
userId: str
|
|
496
|
+
"""The ID of the user performing the action."""
|
|
497
|
+
shareOwner: str | None = None
|
|
498
|
+
"""If the object is shared, this is a display name of the share owner."""
|
|
499
|
+
shareOwnerId: str | None = None
|
|
500
|
+
"""If the object is shared, this is the owner ID of the share."""
|
|
501
|
+
instanceId: str | None = None
|
|
502
|
+
"""Nextcloud instance ID."""
|
|
503
|
+
|
|
504
|
+
def to_fs_node(self) -> FsNode:
|
|
505
|
+
"""Returns usual :py:class:`~nc_py_api.files.FsNode` created from this class."""
|
|
506
|
+
user_path = os.path.join(self.directory, self.name).rstrip("/")
|
|
507
|
+
is_dir = bool(self.fileType.lower() == "dir")
|
|
508
|
+
if is_dir:
|
|
509
|
+
user_path += "/"
|
|
510
|
+
full_path = os.path.join(f"files/{self.userId}", user_path.lstrip("/"))
|
|
511
|
+
file_id = str(self.fileId).rjust(8, "0")
|
|
512
|
+
|
|
513
|
+
permissions = "S" if self.shareOwnerId else ""
|
|
514
|
+
permissions += permissions_to_str(self.permissions, is_dir)
|
|
515
|
+
return FsNode(
|
|
516
|
+
full_path,
|
|
517
|
+
etag=self.etag,
|
|
518
|
+
size=self.size,
|
|
519
|
+
content_length=0 if is_dir else self.size,
|
|
520
|
+
permissions=permissions,
|
|
521
|
+
favorite=bool(self.favorite.lower() == "true"),
|
|
522
|
+
file_id=file_id + self.instanceId if self.instanceId else file_id,
|
|
523
|
+
fileid=self.fileId,
|
|
524
|
+
last_modified=datetime.datetime.utcfromtimestamp(self.mtime).replace(tzinfo=datetime.timezone.utc),
|
|
525
|
+
mimetype=self.mime,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
class ActionFileInfoEx(BaseModel):
|
|
530
|
+
"""New ``register_ex`` uses new data format which allowing receiving multiple NC Nodes in one request."""
|
|
531
|
+
|
|
532
|
+
files: list[ActionFileInfo]
|
|
533
|
+
"""Always list of ``ActionFileInfo`` with one element minimum."""
|
nc_py_api/files/_files.py
CHANGED
|
@@ -168,6 +168,18 @@ def build_list_tags_response(response: Response) -> list[SystemTag]:
|
|
|
168
168
|
return result
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
def build_tags_ids_for_object(url_to_fetch: str, response: Response) -> list[int]:
|
|
172
|
+
result = []
|
|
173
|
+
records = _webdav_response_to_records(response, "list_tags_ids")
|
|
174
|
+
for record in records:
|
|
175
|
+
prop_stat = record["d:propstat"]
|
|
176
|
+
if str(prop_stat.get("d:status", "")).find("200 OK") != -1:
|
|
177
|
+
href_suffix = str(record["d:href"]).removeprefix(url_to_fetch).strip("/")
|
|
178
|
+
if href_suffix:
|
|
179
|
+
result.append(int(href_suffix))
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
|
|
171
183
|
def build_update_tag_req(
|
|
172
184
|
name: str | None, user_visible: bool | None, user_assignable: bool | None
|
|
173
185
|
) -> ElementTree.Element:
|