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.
@@ -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
- if self.file_id and self.file_id == other.file_id:
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.lstrip("/").split("/", maxsplit=2)[1]
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 self.full_path.lstrip("/").split("/", maxsplit=2)[-1]
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: