saviialib 0.11.2__py3-none-any.whl → 1.0.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.

Potentially problematic release.


This version of saviialib might be problematic. Click here for more details.

@@ -0,0 +1,58 @@
1
+ import ftplib
2
+ import asyncio
3
+ from io import BytesIO
4
+ from saviialib.libs.ftp_client.ftp_client_contract import (
5
+ FTPClientContract,
6
+ )
7
+ from saviialib.libs.ftp_client.types.ftp_client_types import (
8
+ FtpClientInitArgs,
9
+ FtpListFilesArgs,
10
+ FtpReadFileArgs,
11
+ )
12
+
13
+
14
+ class FtplibClient(FTPClientContract):
15
+ def __init__(self, args: FtpClientInitArgs) -> None:
16
+ self.host = args.config.ftp_host
17
+ self.port = args.config.ftp_port
18
+ self.password = args.config.ftp_password
19
+ self.user = args.config.ftp_user
20
+ self.client = ftplib.FTP(host=self.host, user=self.user, passwd=self.password)
21
+
22
+ async def _async_start(self) -> None:
23
+ try:
24
+ await asyncio.to_thread(self.client.login, self.user, self.password)
25
+ except OSError:
26
+ raise ConnectionRefusedError(
27
+ f"{self.host}:{self.port} isn't active. "
28
+ "Please ensure the server is running and accessible."
29
+ )
30
+ except Exception as error:
31
+ raise ConnectionError(
32
+ f"General connection for {self.host}:{self.port}.", error.__str__()
33
+ )
34
+
35
+ async def list_files(self, args: FtpListFilesArgs) -> list[str]:
36
+ try:
37
+ EXCLUDED_NAMES = [".", ".."]
38
+ await self._async_start()
39
+ await asyncio.to_thread(self.client.cwd, args.path)
40
+ filenames = await asyncio.to_thread(self.client.nlst, args.path)
41
+ return [
42
+ filename for filename in filenames if filename not in EXCLUDED_NAMES
43
+ ]
44
+ except Exception as error:
45
+ raise ConnectionAbortedError(error)
46
+
47
+ async def read_file(self, args: FtpReadFileArgs) -> bytes:
48
+ await self._async_start()
49
+ try:
50
+ file_content = BytesIO()
51
+ await asyncio.to_thread(
52
+ self.client.retrbinary, "RETR " + args.file_path, file_content.write
53
+ )
54
+ await asyncio.to_thread(file_content.seek, 0)
55
+ file_bytes = await asyncio.to_thread(file_content.read)
56
+ return file_bytes
57
+ except Exception as error:
58
+ raise FileNotFoundError(f"File not found: {args.file_path}") from error
@@ -1,10 +1,11 @@
1
1
  from .clients.aioftp_client import AioFTPClient
2
+ from .clients.ftplib_client import FtplibClient
2
3
  from .ftp_client_contract import FTPClientContract
3
4
  from .types.ftp_client_types import FtpClientInitArgs, FtpListFilesArgs, FtpReadFileArgs
4
5
 
5
6
 
6
7
  class FTPClient(FTPClientContract):
7
- CLIENTS = {"aioftp_client"}
8
+ CLIENTS = {"aioftp_client", "ftplib_client"}
8
9
 
9
10
  def __init__(self, args: FtpClientInitArgs) -> None:
10
11
  if args.client_name not in FTPClient.CLIENTS:
@@ -13,6 +14,8 @@ class FTPClient(FTPClientContract):
13
14
 
14
15
  if args.client_name == "aioftp_client":
15
16
  self.client_obj = AioFTPClient(args)
17
+ elif args.client_name == "ftplib_client":
18
+ self.client_obj = FtplibClient(args)
16
19
  self.client_name = args.client_name
17
20
 
18
21
  async def list_files(self, args: FtpListFilesArgs) -> list[str]:
@@ -4,6 +4,7 @@ from .types.sharepoint_client_types import (
4
4
  SpListFilesArgs,
5
5
  SpListFoldersArgs,
6
6
  SpUploadFileArgs,
7
+ SpCreateFolderArgs,
7
8
  )
8
9
 
9
10
  __all__ = [
@@ -12,4 +13,5 @@ __all__ = [
12
13
  "SpListFilesArgs",
13
14
  "SpListFoldersArgs",
14
15
  "SpUploadFileArgs",
16
+ "SpCreateFolderArgs",
15
17
  ]
@@ -1,5 +1,5 @@
1
1
  from typing import Any
2
-
2
+ import json
3
3
  from aiohttp import ClientError, ClientSession
4
4
  from dotenv import load_dotenv
5
5
 
@@ -10,6 +10,7 @@ from saviialib.libs.sharepoint_client.types.sharepoint_client_types import (
10
10
  SpListFilesArgs,
11
11
  SpListFoldersArgs,
12
12
  SpUploadFileArgs,
13
+ SpCreateFolderArgs,
13
14
  SharepointClientInitArgs,
14
15
  )
15
16
 
@@ -134,3 +135,22 @@ class SharepointRestAPI(SharepointClientContract):
134
135
  return await response.json()
135
136
  except ClientError as error:
136
137
  raise ConnectionError(error) from error
138
+
139
+ async def create_folder(self, args: SpCreateFolderArgs):
140
+ try:
141
+ # Load form digest value
142
+ form_digest_value = await self._load_form_digest_value()
143
+ headers = {
144
+ **self.base_headers,
145
+ "X-RequestDigest": form_digest_value,
146
+ }
147
+ body = {"ServerRelativeUrl": f"{args.folder_relative_url}"}
148
+ endpoint = "web/folders"
149
+ response = await self.session.post(
150
+ endpoint.lstrip("/"), data=json.dumps(body), headers=headers
151
+ )
152
+ response.raise_for_status()
153
+ response_json = await response.json()
154
+ return response_json
155
+ except ClientError as error:
156
+ raise ConnectionError(error) from error
@@ -5,6 +5,7 @@ from .types.sharepoint_client_types import (
5
5
  SpListFilesArgs,
6
6
  SpListFoldersArgs,
7
7
  SpUploadFileArgs,
8
+ SpCreateFolderArgs,
8
9
  )
9
10
 
10
11
 
@@ -52,3 +53,6 @@ class SharepointClient(SharepointClientContract):
52
53
 
53
54
  async def upload_file(self, args: SpUploadFileArgs) -> dict:
54
55
  return await self.client_obj.upload_file(args)
56
+
57
+ async def create_folder(self, args: SpCreateFolderArgs) -> dict:
58
+ return await self.client_obj.create_folder(args)
@@ -4,6 +4,7 @@ from .types.sharepoint_client_types import (
4
4
  SpListFilesArgs,
5
5
  SpListFoldersArgs,
6
6
  SpUploadFileArgs,
7
+ SpCreateFolderArgs,
7
8
  )
8
9
 
9
10
 
@@ -19,3 +20,7 @@ class SharepointClientContract(ABC):
19
20
  @abstractmethod
20
21
  async def upload_file(self, args: SpUploadFileArgs) -> dict:
21
22
  pass
23
+
24
+ @abstractmethod
25
+ async def create_folder(self, args: SpCreateFolderArgs) -> dict:
26
+ pass
@@ -23,3 +23,8 @@ class SpUploadFileArgs:
23
23
  folder_relative_url: str
24
24
  file_name: str
25
25
  file_content: bytes = bytes()
26
+
27
+
28
+ @dataclass
29
+ class SpCreateFolderArgs:
30
+ folder_relative_url: str
@@ -2,7 +2,7 @@ from datetime import datetime
2
2
  from zoneinfo import ZoneInfo
3
3
 
4
4
 
5
- def today(timezone: str = "America/Santiago") -> str:
5
+ def today(timezone: str = "America/Santiago") -> datetime:
6
6
  """
7
7
  Return the current date.
8
8
 
@@ -59,7 +59,7 @@ class UpdateThiesDataUseCase:
59
59
  def _initialize_thies_ftp_client(self, config: FtpClientConfig) -> FTPClient:
60
60
  """Initialize the FTP client."""
61
61
  try:
62
- return FTPClient(FtpClientInitArgs(config, client_name="aioftp_client"))
62
+ return FTPClient(FtpClientInitArgs(config, client_name="ftplib_client"))
63
63
  except RuntimeError as error:
64
64
  raise FtpClientError(error)
65
65
 
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  from time import time
3
+ from saviialib.libs.zero_dependency.utils.datetime_utils import today, datetime_to_str
3
4
  from logging import Logger
4
5
  from saviialib.general_types.error_types.api.epii_api_error_types import (
5
6
  BackupEmptyError,
@@ -20,7 +21,7 @@ from saviialib.libs.sharepoint_client import (
20
21
  SharepointClient,
21
22
  SharepointClientInitArgs,
22
23
  SpUploadFileArgs,
23
- SpListFoldersArgs,
24
+ SpCreateFolderArgs,
24
25
  )
25
26
  from saviialib.services.epii.utils.upload_backup_to_sharepoint_utils import (
26
27
  calculate_percentage_uploaded,
@@ -40,6 +41,7 @@ class UploadBackupToSharepointUsecase:
40
41
  self.sharepoint_config = input.sharepoint_config
41
42
  self.local_backup_source_path = input.local_backup_source_path
42
43
  self.sharepoint_destination_path = input.sharepoint_destination_path
44
+
43
45
  self.files_client = self._initialize_files_client()
44
46
  self.dir_client = self._initialize_directory_client()
45
47
  self.log_history = []
@@ -61,7 +63,28 @@ class UploadBackupToSharepointUsecase:
61
63
  def _initialize_files_client(self):
62
64
  return FilesClient(FilesClientInitArgs(client_name="aiofiles_client"))
63
65
 
66
+ async def _initialize_backup_base_folder(self):
67
+ local_backup_name = (
68
+ f"/local-backup-{datetime_to_str(today(), date_format='%m-%d-%Y')}"
69
+ )
70
+ local_backup_destination_path = (
71
+ self.sharepoint_destination_path + local_backup_name
72
+ )
73
+ async with self.sharepoint_client:
74
+ await self.sharepoint_client.create_folder(
75
+ SpCreateFolderArgs(folder_relative_url=local_backup_destination_path)
76
+ )
77
+ self.sharepoint_destination_path = local_backup_destination_path
78
+ base_folder_message = (
79
+ "[local_backup_lib] Creating base folder" + local_backup_name
80
+ )
81
+ self.logger.info(base_folder_message)
82
+ self.log_history.append(base_folder_message)
83
+
64
84
  async def _validate_backup_structure(self):
85
+ # Initialize the backup folder
86
+ await self._initialize_backup_base_folder()
87
+
65
88
  # Check if the local path exists in the main directory
66
89
  if not await self.dir_client.path_exists(self.local_backup_source_path):
67
90
  raise BackupSourcePathError(
@@ -74,20 +97,21 @@ class UploadBackupToSharepointUsecase:
74
97
  else []
75
98
  )
76
99
  async with self.sharepoint_client: # type: ignore
77
- response = await self.sharepoint_client.list_folders(
78
- SpListFoldersArgs(folder_relative_url=self.sharepoint_destination_path)
79
- )
80
- sharepoint_directories = [x["Name"] for x in response["value"]] # type: ignore
100
+ for local_dir in local_directories:
101
+ create_message = (
102
+ f"[local_backup_lib] Creating a new directory '{local_dir}'."
103
+ )
81
104
 
82
- # Verify that all local directories exist in SharePoint
83
- for local_dir in local_directories:
84
- if local_dir not in sharepoint_directories:
85
- error_message = f"Folder '{local_dir}' does not exist in SharePoint destination path '{self.sharepoint_destination_path}'."
86
- self.log_history.append(error_message)
87
- self.logger.error("[local_backup_lib] %s", error_message)
88
- raise BackupSourcePathError(
89
- reason=f"Folder '{local_dir}' does not exist in SharePoint destination path '{self.sharepoint_destination_path}'."
105
+ self.log_history.append(create_message)
106
+ await self.sharepoint_client.create_folder(
107
+ SpCreateFolderArgs(
108
+ folder_relative_url=self.sharepoint_destination_path
109
+ + "/"
110
+ + local_dir
111
+ )
90
112
  )
113
+ self.logger.info("[local_backup_lib] %s", create_message)
114
+ self.log_history.append(f"[local_backup_lib] {create_message}")
91
115
 
92
116
  # Check if the current folder only have files and each folder exist in Microsoft Sharepoint.
93
117
  if self.total_files == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: saviialib
3
- Version: 0.11.2
3
+ Version: 1.0.1
4
4
  Summary: A client library for IoT projects in the RCER initiative
5
5
  License: MIT
6
6
  Author: pedropablozavalat
@@ -11,12 +11,12 @@ Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
- Requires-Dist: aiofiles (==24.1.0)
15
- Requires-Dist: aioftp (==0.25.1)
16
- Requires-Dist: aiohttp (==3.11.16)
14
+ Requires-Dist: aiofiles
15
+ Requires-Dist: aioftp
16
+ Requires-Dist: aiohttp
17
17
  Requires-Dist: build
18
- Requires-Dist: dotenv (==0.9.9)
19
- Requires-Dist: pytest-cov (==6.1.1)
18
+ Requires-Dist: dotenv (>=0.9.9,<0.10.0)
19
+ Requires-Dist: pytest-cov (>=6.1.1,<7.0.0)
20
20
  Description-Content-Type: text/markdown
21
21
 
22
22
  # SAVIIA Library
@@ -20,16 +20,17 @@ saviialib/libs/files_client/types/files_client_types.py,sha256=8OHm4nvZTW9K3ryeI
20
20
  saviialib/libs/ftp_client/__init__.py,sha256=dW2Yutgc7mJJJzgKLhWKXMgQ6KIWJYfFa1sGpjHH5xU,191
21
21
  saviialib/libs/ftp_client/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  saviialib/libs/ftp_client/clients/aioftp_client.py,sha256=5sgr3PMETgaBRlKeu_avxHIh6tr1gb7t2mkxmpRas_k,1925
23
- saviialib/libs/ftp_client/ftp_client.py,sha256=UWpuIrfO27A1M40YaBvAUdkdwPYJ4Ty4q55x7H4y1F8,844
23
+ saviialib/libs/ftp_client/clients/ftplib_client.py,sha256=VcpAn23r03iyCUAj35xHU_AVNrll_45uHFN8Z9OnBkE,2203
24
+ saviialib/libs/ftp_client/ftp_client.py,sha256=Naj9p0yYWlXt9um0zKfvuHSoycM8JZg2SPBm8110pYk,1008
24
25
  saviialib/libs/ftp_client/ftp_client_contract.py,sha256=tymkugDzsJ5PzUXIaSkwX1h7T0naR15qAkjrqswqDyM,338
25
26
  saviialib/libs/ftp_client/types/__init__.py,sha256=syfwf9feP4QK7fkCTfl4j8l11ic-jHtfi1DE2chaWbs,155
26
27
  saviialib/libs/ftp_client/types/ftp_client_types.py,sha256=e4SmYkewldulaD8ms2q75zVgLFXyBxBqoa_L-IQOmso,256
27
- saviialib/libs/sharepoint_client/__init__.py,sha256=v7h-cNsK-BaPp-hTU3NWRNXYRD9ztU-hsCk0eNRPIKA,334
28
- saviialib/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=oGqNQcPL0HX-WYqO26JuIi9PDYPIZAoyDYMl965L_0c,5295
29
- saviialib/libs/sharepoint_client/sharepoint_client.py,sha256=LMQFti0Ulc4FCPQy17mrTLNJkO2Qp3i-KR0VbjPyPG4,1665
30
- saviialib/libs/sharepoint_client/sharepoint_client_contract.py,sha256=xqNHzCjp7GvUGGUox9YTJj2QJgTc5819t2etOk8X26o,485
31
- saviialib/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=OmPlCJ9rLrAFBeG6aDp5cxMiQ5BZlDyGVx5S4GN4aqg,414
32
- saviialib/libs/zero_dependency/utils/datetime_utils.py,sha256=NFPHxOTuCtfkYjUnsbRqw9ZK87UGAK-Eira2CwG8rJ8,754
28
+ saviialib/libs/sharepoint_client/__init__.py,sha256=6RbEzFtCfMrrY8F0JOCVcK6Gqt1FBJ09m0NuWHMCmCI,384
29
+ saviialib/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=Gq__zrYtSyiwi5NguMYCqJ5Bbs84UqzC9L5qvOMhfVI,6100
30
+ saviialib/libs/sharepoint_client/sharepoint_client.py,sha256=gXFisWWCk6pKhoEeaNN8dcHA5ywrGDLqfiKjchrHRy0,1816
31
+ saviialib/libs/sharepoint_client/sharepoint_client_contract.py,sha256=H-WsXR5f50jy5RRYNlgV61y_YGjOUq4U_E1DWUUrDYw,612
32
+ saviialib/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=JruUCn6o16w00t4zxDrxmv_Bxa52UyShGYmwUFfBCCQ,482
33
+ saviialib/libs/zero_dependency/utils/datetime_utils.py,sha256=c2H_JpUKpunCzDw6I4alDWNftEqaciWGtEUklPepm_s,759
33
34
  saviialib/services/epii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
35
  saviialib/services/epii/api.py,sha256=99G-iC8Nz8vv9TuNPpdFsHxDAH8DXVCHnUQzwAUO9Ao,4036
35
36
  saviialib/services/epii/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -42,12 +43,12 @@ saviialib/services/epii/use_cases/constants/upload_backup_to_sharepoint_constant
42
43
  saviialib/services/epii/use_cases/types/__init__.py,sha256=u6fyodOEJE2j6FMqJux40Xf9ccYAi-UUYxqT-Kzc0kE,199
43
44
  saviialib/services/epii/use_cases/types/update_thies_data_types.py,sha256=3lJzG1nuZoP1mqFlvQ0-aFJp80SECaeiROlvucVhaSY,539
44
45
  saviialib/services/epii/use_cases/types/upload_backup_to_sharepoint_types.py,sha256=J_sGhqSaPoMZA0GIrzCx6pmgSKgIkGi0uyrsltU_H8w,320
45
- saviialib/services/epii/use_cases/update_thies_data.py,sha256=SKW9_Z2pbvE0rgyFHmpBynGVqfQ30OMqK3zofW_tY8Y,9866
46
- saviialib/services/epii/use_cases/upload_backup_to_sharepoint.py,sha256=UZ2Pe_mavdquYG00_GYtANY4nn0wHjT_Z0HLUvtfW2c,11143
46
+ saviialib/services/epii/use_cases/update_thies_data.py,sha256=0dkk45qPkvPpxS8uERPDrPN4woWz4vze3FR0gA-P6wI,9866
47
+ saviialib/services/epii/use_cases/upload_backup_to_sharepoint.py,sha256=-0MvCzkFM7T2y7XLgFevWAXmqAsthzuY68ReiiL2zDU,11886
47
48
  saviialib/services/epii/utils/__init__.py,sha256=cYt2tvq65_OMjFaqb8-CCC7IGCQgFd4ziEUWJV7s1iY,98
48
49
  saviialib/services/epii/utils/update_thies_data_utils.py,sha256=EpjYWXqyHxJ-dO3MHhdXp-rGV7WyUckeFko-nnfnNac,555
49
50
  saviialib/services/epii/utils/upload_backup_to_sharepoint_utils.py,sha256=hEeV4_kcG8YL6t_3V8AlhgDHtHiUNsdYpilfgTLaQMc,3528
50
- saviialib-0.11.2.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
51
- saviialib-0.11.2.dist-info/METADATA,sha256=Af3aG28BKcG0Fsfds49sWCWJTBuWs4xo--RhfBsAjSE,4083
52
- saviialib-0.11.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
53
- saviialib-0.11.2.dist-info/RECORD,,
51
+ saviialib-1.0.1.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
52
+ saviialib-1.0.1.dist-info/METADATA,sha256=qxj1sUazmjA38ue9RFtljIuW4xt9IJGyPSkBpS_LXqQ,4063
53
+ saviialib-1.0.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
54
+ saviialib-1.0.1.dist-info/RECORD,,