saviialib 0.8.0__py3-none-any.whl → 0.9.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.

@@ -79,3 +79,4 @@ class EpiiSharepointBackupConfig:
79
79
  sharepoint_tenant_name: str
80
80
  sharepoint_site_name: str
81
81
  local_backup_source_path: str
82
+ destination_folders: str
@@ -53,7 +53,7 @@ class EpiiAPI:
53
53
  return response.__dict__
54
54
 
55
55
  async def upload_backup_to_sharepoint(
56
- self, local_backup_source_path: str
56
+ self, local_backup_source_path: str, destination_folders: dict[str, str]
57
57
  ) -> Dict[str, Any]:
58
58
  """Migrate a backup folder from Home assistant to Sharepoint directory.
59
59
  Args:
@@ -70,6 +70,7 @@ class EpiiAPI:
70
70
  sharepoint_tenant_id=self.sharepoint_tenant_id,
71
71
  sharepoint_tenant_name=self.sharepoint_tenant_name,
72
72
  local_backup_source_path=local_backup_source_path,
73
+ destination_folders=destination_folders
73
74
  )
74
75
 
75
76
  controller = UploadBackupToSharepointController(
@@ -34,6 +34,7 @@ class UploadBackupToSharepointController:
34
34
  sharepoint_tenant_id=input.config.sharepoint_tenant_id,
35
35
  ),
36
36
  local_backup_source_path=input.config.local_backup_source_path,
37
+ destination_folders=input.config.destination_folders
37
38
  )
38
39
  )
39
40
 
@@ -1,5 +1,5 @@
1
+ import logging
2
+
1
3
  SHAREPOINT_BASE_URL = "/sites/uc365_CentrosyEstacionesRegionalesUC/Shared%20Documents/General/Test_Raspberry"
2
- DESTINATION_FOLDERS = {
3
- "CAM1": "Camaras_Trampa",
4
- "RECORDS1": "Grabaciones",
5
- }
4
+
5
+ LOGGER = logging.getLogger(__package__)
@@ -6,3 +6,4 @@ from saviialib.general_types.api.epii_api_types import SharepointConfig
6
6
  class UploadBackupToSharepointUseCaseInput:
7
7
  sharepoint_config: SharepointConfig
8
8
  local_backup_source_path: str
9
+ destination_folders: dict
@@ -28,34 +28,40 @@ from saviialib.services.epii.utils.upload_backup_to_sharepoint_utils import (
28
28
  from .types.upload_backup_to_sharepoint_types import (
29
29
  UploadBackupToSharepointUseCaseInput,
30
30
  )
31
+ from .constants.upload_backup_to_sharepoint_constants import LOGGER
31
32
 
32
33
 
33
34
  class UploadBackupToSharepointUsecase:
34
35
  def __init__(self, input: UploadBackupToSharepointUseCaseInput):
35
36
  self.sharepoint_config = input.sharepoint_config
36
37
  self.local_backup_source_path = input.local_backup_source_path
37
- self.grouped_files_by_folder = self._extract_filesnames_by_folder()
38
+ self.destination_folders = input.destination_folders
38
39
  self.files_client = self._initialize_files_client()
39
- self.total_files = sum(
40
- len(files) for files in self.grouped_files_by_folder.values()
41
- )
42
40
  self.log_history = []
41
+ self.grouped_files_by_folder = None
42
+ self.total_files = None
43
43
 
44
44
  def _initialize_files_client(self):
45
45
  return FilesClient(FilesClientInitArgs(client_name="aiofiles_client"))
46
46
 
47
- def _extract_filesnames_by_folder(self) -> dict[str, list[str]]:
47
+ async def _extract_filesnames_by_folder(self) -> dict[str, list[str]]:
48
48
  """Groups files by their parent folder."""
49
- if not os.path.exists(self.local_backup_source_path):
49
+ backup_folder_exists = await asyncio.to_thread(
50
+ os.path.exists, self.local_backup_source_path
51
+ )
52
+ if not backup_folder_exists:
50
53
  return {}
54
+ folder_names = await asyncio.to_thread(
55
+ os.listdir, self.local_backup_source_path
56
+ )
51
57
  return {
52
58
  folder_name: [
53
59
  file_name
54
- for file_name in os.listdir(
55
- os.path.join(self.local_backup_source_path, folder_name)
60
+ for file_name in await asyncio.to_thread(
61
+ os.listdir, os.path.join(self.local_backup_source_path, folder_name)
56
62
  )
57
63
  ]
58
- for folder_name in os.listdir(self.local_backup_source_path)
64
+ for folder_name in folder_names
59
65
  }
60
66
 
61
67
  def _save_log_history(self) -> None:
@@ -82,7 +88,9 @@ class UploadBackupToSharepointUsecase:
82
88
 
83
89
  async with sharepoint_client:
84
90
  try:
85
- destination_folder = c.DESTINATION_FOLDERS.get(folder_name, folder_name)
91
+ destination_folder = self.destination_folders.get(
92
+ folder_name, folder_name
93
+ )
86
94
  folder_url = f"{c.SHAREPOINT_BASE_URL}/{destination_folder}"
87
95
  args = SpUploadFileArgs(
88
96
  folder_relative_url=folder_url,
@@ -103,14 +111,14 @@ class UploadBackupToSharepointUsecase:
103
111
  f"[BACKUP] Uploading file '{file_name}' from '{folder_name}' "
104
112
  )
105
113
  self.log_history.append(uploading_message)
106
- print(uploading_message)
114
+ LOGGER.debug(uploading_message)
107
115
  file_path = os.path.join(self.local_backup_source_path, folder_name, file_name)
108
116
  file_content = await self.files_client.read(ReadArgs(file_path, mode="rb"))
109
117
  uploaded, error_message = await self.export_file_to_sharepoint(
110
118
  folder_name, file_name, file_content
111
119
  )
112
120
  result_message = show_upload_result(uploaded, file_name)
113
- print(result_message)
121
+ LOGGER.debug(result_message)
114
122
  self.log_history.append(result_message)
115
123
  return {
116
124
  "parent_folder": folder_name,
@@ -126,7 +134,7 @@ class UploadBackupToSharepointUsecase:
126
134
  f"[BACKUP] Retrying upload for {len(failed_files)} failed files... 🚨"
127
135
  )
128
136
  self.log_history.append(retry_message)
129
- print(retry_message)
137
+ LOGGER.debug(retry_message)
130
138
  for file in failed_files:
131
139
  tasks.append(
132
140
  self.upload_and_log_progress_task(
@@ -142,40 +150,72 @@ class UploadBackupToSharepointUsecase:
142
150
  "[BACKUP] All files uploaded successfully after retry."
143
151
  )
144
152
  self.log_history.append(successful_upload_retry)
145
- print(successful_upload_retry)
153
+ LOGGER.debug(successful_upload_retry)
146
154
  self._save_log_history()
147
155
  return parse_execute_response(results)
148
156
 
149
157
  async def execute(self):
150
158
  """Exports all files from the local backup folder to SharePoint cloud."""
159
+ self.grouped_files_by_folder = await self._extract_filesnames_by_folder()
160
+ self.total_files = sum(
161
+ len(files) for files in self.grouped_files_by_folder.values()
162
+ )
151
163
  tasks = []
152
164
  start_time = time()
153
- if not directory_exists(self.local_backup_source_path):
165
+
166
+ # Check if the local path exists in the main directory
167
+ if not await directory_exists(self.local_backup_source_path):
154
168
  raise BackupSourcePathError(
155
169
  reason=f"'{self.local_backup_source_path}' doesn't exist."
156
170
  )
171
+
172
+ # Check if the current folder only have files.
173
+ items = [
174
+ item
175
+ for item in await asyncio.to_thread(
176
+ os.listdir, self.local_backup_source_path
177
+ )
178
+ ]
179
+ for item in items:
180
+ folder_included = item in self.destination_folders.keys()
181
+ is_file = not await asyncio.to_thread(
182
+ os.path.isdir, os.path.join(self.local_backup_source_path, item)
183
+ )
184
+
185
+ if not folder_included and not is_file:
186
+ raise BackupSourcePathError(
187
+ reason=(
188
+ f"'{item}' must be included in the destination folders dictionary",
189
+ )
190
+ )
191
+ elif folder_included and is_file:
192
+ print(folder_included, is_file)
193
+ raise BackupSourcePathError(reason=(f"'{item}' must be a directory.",))
194
+
157
195
  if self.total_files == 0:
158
196
  no_files_message = (
159
197
  f"[BACKUP] {self.local_backup_source_path} has no files ⚠️"
160
198
  )
161
199
  self.log_history.append(no_files_message)
162
- print(no_files_message)
200
+ LOGGER.debug(no_files_message)
163
201
  raise BackupEmptyError
164
202
  # Create task for each file stored in the the local backup folder.
165
203
  for folder_name in self.grouped_files_by_folder:
166
204
  if (
167
- count_files_in_directory(self.local_backup_source_path, folder_name)
205
+ await count_files_in_directory(
206
+ self.local_backup_source_path, folder_name
207
+ )
168
208
  == 0
169
209
  ):
170
210
  empty_folder_message = f"[BACKUP] The folder '{folder_name}' is empty ⚠️"
171
- print(empty_folder_message)
211
+ LOGGER.debug(empty_folder_message)
172
212
  self.log_history.append(empty_folder_message)
173
213
  continue
174
214
  extracting_files_message = (
175
215
  "[BACKUP]" + f" Extracting files from '{folder_name} ".center(15, "*")
176
216
  )
177
217
  self.log_history.append(extracting_files_message)
178
- print(extracting_files_message)
218
+ LOGGER.debug(extracting_files_message)
179
219
  for file_name in self.grouped_files_by_folder[folder_name]:
180
220
  tasks.append(self.upload_and_log_progress_task(folder_name, file_name))
181
221
 
@@ -1,6 +1,13 @@
1
1
  import re
2
2
  from typing import List, Dict, Optional
3
+ import asyncio
3
4
  import os
5
+ from saviialib.general_types.error_types.api.epii_api_error_types import (
6
+ BackupSourcePathError,
7
+ )
8
+ from saviialib.services.epii.use_cases.constants.upload_backup_to_sharepoint_constants import (
9
+ LOGGER,
10
+ )
4
11
 
5
12
 
6
13
  def extract_error_information(error: str) -> Optional[Dict[str, str]]:
@@ -24,7 +31,7 @@ def explain_status_code(status_code: int) -> str:
24
31
 
25
32
 
26
33
  def extract_error_message(results: List[Dict], success: float) -> str:
27
- print(
34
+ LOGGER.info(
28
35
  "[BACKUP] Not all files uploaded ⚠️\n"
29
36
  f"[BACKUP] Files failed to upload: {(1 - success):.2%}"
30
37
  )
@@ -52,9 +59,9 @@ def extract_error_message(results: List[Dict], success: float) -> str:
52
59
 
53
60
  # Summary
54
61
  for code, items in grouped_errors.items():
55
- print(f"[BACKUP] Status code {code} - {explain_status_code(int(code))}")
62
+ LOGGER.info(f"[BACKUP] Status code {code} - {explain_status_code(int(code))}")
56
63
  for item in items:
57
- print(
64
+ LOGGER.info(
58
65
  f"[BACKUP] File {item['file_name']}, url: {item['url']}, message: {item['message']}"
59
66
  )
60
67
 
@@ -63,11 +70,14 @@ def extract_error_message(results: List[Dict], success: float) -> str:
63
70
 
64
71
 
65
72
  def parse_execute_response(results: List[Dict]) -> Dict[str, List[str]]:
66
- return {
67
- "new_files": len(
68
- [item["file_name"] for item in results if item.get("uploaded")]
69
- ),
70
- }
73
+ try:
74
+ return {
75
+ "new_files": len(
76
+ [item["file_name"] for item in results if item.get("uploaded")]
77
+ ),
78
+ }
79
+ except (IsADirectoryError, AttributeError, ConnectionError) as error:
80
+ raise BackupSourcePathError(reason=error)
71
81
 
72
82
 
73
83
  def show_upload_result(uploaded: bool, file_name: str) -> str:
@@ -84,9 +94,9 @@ def calculate_percentage_uploaded(results: List[Dict], total_files: int) -> floa
84
94
  return (uploaded_count / total_files) * 100 if total_files > 0 else 0
85
95
 
86
96
 
87
- def directory_exists(path: str) -> bool:
88
- return os.path.exists(path)
97
+ async def directory_exists(path: str) -> bool:
98
+ return await asyncio.to_thread(os.path.exists, path)
89
99
 
90
100
 
91
- def count_files_in_directory(path: str, folder_name: str) -> int:
92
- return len(os.listdir(os.path.join(path, folder_name)))
101
+ async def count_files_in_directory(path: str, folder_name: str) -> int:
102
+ return len(await asyncio.to_thread(os.listdir, os.path.join(path, folder_name)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: saviialib
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: A client library for IoT projects in the RCER initiative
5
5
  License: MIT
6
6
  Author: pedropablozavalat
@@ -1,7 +1,7 @@
1
1
  saviialib/__init__.py,sha256=TMsEY8OOjo9KOz-0jf3QBh6WFOhCY07Rd7W2PK7C-1A,253
2
2
  saviialib/general_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  saviialib/general_types/api/__init__.py,sha256=jXhoMlbSv7UCizW_Fm2mqR_3n99qkocVwg6CF7s3w7w,145
4
- saviialib/general_types/api/epii_api_types.py,sha256=Yqn99Ku0Ni2BXopCLikApWFc43sbqlpvk6jH9EIKWps,2295
4
+ saviialib/general_types/api/epii_api_types.py,sha256=_3Q20yOTHn8Vmsx-fn_FR9XF2P719KobFrklxsdDr1U,2324
5
5
  saviialib/general_types/error_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  saviialib/general_types/error_types/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  saviialib/general_types/error_types/api/epii_api_error_types.py,sha256=j1b2gE8zRTlh4gCB4kf9-JNlBzcZ9AmMKxRiFVbSqQ0,2722
@@ -26,24 +26,24 @@ saviialib/libs/sharepoint_client/sharepoint_client_contract.py,sha256=xqNHzCjp7G
26
26
  saviialib/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=OmPlCJ9rLrAFBeG6aDp5cxMiQ5BZlDyGVx5S4GN4aqg,414
27
27
  saviialib/libs/zero_dependency/utils/datetime_utils.py,sha256=NFPHxOTuCtfkYjUnsbRqw9ZK87UGAK-Eira2CwG8rJ8,754
28
28
  saviialib/services/epii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- saviialib/services/epii/api.py,sha256=ihdxM137s6Vjptm_u0VbWY2IPA2rxj7Jn9L2_KhXDjU,3399
29
+ saviialib/services/epii/api.py,sha256=rAiyf6sumaW9DIdqT5KKGMk0iIP6_tnN2__oLIuDuiI,3488
30
30
  saviialib/services/epii/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  saviialib/services/epii/controllers/types/__init__.py,sha256=xzky-oTSojLNkWETp_k8a4dcXYvYSQY0VhWo23Yhb8U,195
32
32
  saviialib/services/epii/controllers/types/update_thies_data_types.py,sha256=Id86QaFt6A2EJgHJvmm3rvWGUkT2SM8D5OTxZGS-aTs,380
33
33
  saviialib/services/epii/controllers/types/upload_backup_to_sharepoint_types.py,sha256=k_vfqI4UfdSS62HY5pi8eq2PxYXRp4LexDgRB3V2lrc,400
34
34
  saviialib/services/epii/controllers/update_thies_data.py,sha256=5vi-CEXV9maCTHgyZRnGs8zlc2xkEQ3TJMVza4xr-Qw,4334
35
- saviialib/services/epii/controllers/upload_backup_to_sharepoint.py,sha256=vFGs6OB_kNx7dJrneZxcXP-aB60Z2Vi7YLNFFkVRlw8,3685
35
+ saviialib/services/epii/controllers/upload_backup_to_sharepoint.py,sha256=l3oBiPU1GHYuVI8LCnDQhY_2xH7bqiQzJ4lB_is4VAM,3754
36
36
  saviialib/services/epii/use_cases/constants/update_thies_data_constants.py,sha256=38f2WKV-zBO9O-CV8mh3tW3MFMiaJrrv4_FfQiyHApc,275
37
- saviialib/services/epii/use_cases/constants/upload_backup_to_sharepoint_constants.py,sha256=Hia8MgpfKuGuKiyAXIxd7tKTFWkKDO_SKo0iuO5BwOo,197
37
+ saviialib/services/epii/use_cases/constants/upload_backup_to_sharepoint_constants.py,sha256=zkQLcI2ZIhGVGOcb-i-XCTdIcX78U4nAhpXPNenBjow,167
38
38
  saviialib/services/epii/use_cases/types/__init__.py,sha256=u6fyodOEJE2j6FMqJux40Xf9ccYAi-UUYxqT-Kzc0kE,199
39
39
  saviialib/services/epii/use_cases/types/update_thies_data_types.py,sha256=BoT6N9YoVw3f8Cyb88T7fJTCCnkVybOh1K1u2fJ3X7k,419
40
- saviialib/services/epii/use_cases/types/upload_backup_to_sharepoint_types.py,sha256=sELiRxsFcEa_qsWUDY25QwE-Xb-hzSz_WnHlPQw7yq8,237
40
+ saviialib/services/epii/use_cases/types/upload_backup_to_sharepoint_types.py,sha256=rvuhJnjsCQpuNz5UO_0lGMR_KJRybNdq_11Be9ZaY1U,267
41
41
  saviialib/services/epii/use_cases/update_thies_data.py,sha256=WLBpdAUkxAL_XdDnc49scmst1xMjrdScjr6Cmgg3UEE,6494
42
- saviialib/services/epii/use_cases/upload_backup_to_sharepoint.py,sha256=izYUpNNoMEO_S3ZB1Upzd-QobU-bpXtlDpKpmL-UdPM,8073
42
+ saviialib/services/epii/use_cases/upload_backup_to_sharepoint.py,sha256=rurCi-01Rm1SKa6o_SPcbsnOBfHlUD1ZMmIn6k7JbCQ,9608
43
43
  saviialib/services/epii/utils/__init__.py,sha256=cYt2tvq65_OMjFaqb8-CCC7IGCQgFd4ziEUWJV7s1iY,98
44
44
  saviialib/services/epii/utils/update_thies_data_utils.py,sha256=EpjYWXqyHxJ-dO3MHhdXp-rGV7WyUckeFko-nnfnNac,555
45
- saviialib/services/epii/utils/upload_backup_to_sharepoint_utils.py,sha256=jRnPkjsAi0SdbZFeYMFoXoyiJef0_Kq8U6SGhV4fZVs,3037
46
- saviialib-0.8.0.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
47
- saviialib-0.8.0.dist-info/METADATA,sha256=t9-PFit1kCqYyOLqpriLs1mX0lF-Tb0HGfhxb6iL25o,4082
48
- saviialib-0.8.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
49
- saviialib-0.8.0.dist-info/RECORD,,
45
+ saviialib/services/epii/utils/upload_backup_to_sharepoint_utils.py,sha256=DnV4ddqZ0eeD50Wh66J9__m3Sqk5-1p_93e9YuQitpA,3499
46
+ saviialib-0.9.1.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
47
+ saviialib-0.9.1.dist-info/METADATA,sha256=8xbbpq_CG90pYR9SaNjkuVcGPM2vZ0-9qPrjd8aHazo,4082
48
+ saviialib-0.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
49
+ saviialib-0.9.1.dist-info/RECORD,,