saviialib 1.3.0__py3-none-any.whl → 1.5.0__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.

Files changed (64) hide show
  1. saviialib/__init__.py +73 -3
  2. saviialib/general_types/api/__init__.py +0 -3
  3. saviialib/general_types/api/{epii_api_types.py → saviia_api_types.py} +2 -38
  4. saviialib/general_types/api/saviia_backup_api_types.py +24 -0
  5. saviialib/general_types/api/saviia_netcamera_api_types.py +11 -0
  6. saviialib/general_types/api/saviia_shakes_api_types.py +21 -0
  7. saviialib/general_types/api/saviia_thies_api_types.py +31 -0
  8. saviialib/general_types/error_types/api/{epii_api_error_types.py → saviia_api_error_types.py} +8 -0
  9. saviialib/general_types/error_types/api/saviia_netcamera_error_types.py +7 -0
  10. saviialib/general_types/error_types/common/common_types.py +9 -0
  11. saviialib/libs/directory_client/client/os_client.py +1 -1
  12. saviialib/libs/ffmpeg_client/__init__.py +8 -0
  13. saviialib/libs/ffmpeg_client/clients/ffmpeg_asyncio_client.py +101 -0
  14. saviialib/libs/ffmpeg_client/ffmpeg_client.py +25 -0
  15. saviialib/libs/ffmpeg_client/ffmpeg_client_contract.py +12 -0
  16. saviialib/libs/ffmpeg_client/types/ffmpeg_client_types.py +28 -0
  17. saviialib/libs/sftp_client/__init__.py +8 -0
  18. saviialib/libs/sftp_client/clients/asyncssh_sftp_client.py +83 -0
  19. saviialib/libs/sftp_client/sftp_client.py +26 -0
  20. saviialib/libs/sftp_client/sftp_client_contract.py +13 -0
  21. saviialib/libs/sftp_client/types/sftp_client_types.py +24 -0
  22. saviialib/libs/sharepoint_client/clients/sharepoint_rest_api.py +7 -3
  23. saviialib/libs/zero_dependency/utils/strings_utils.py +5 -0
  24. saviialib/services/backup/api.py +36 -0
  25. saviialib/services/{epii → backup}/controllers/types/__init__.py +1 -1
  26. saviialib/services/{epii → backup}/controllers/types/upload_backup_to_sharepoint_types.py +2 -2
  27. saviialib/services/{epii → backup}/controllers/upload_backup_to_sharepoint.py +5 -5
  28. saviialib/services/{epii → backup}/use_cases/types/__init__.py +1 -1
  29. saviialib/services/{epii → backup}/use_cases/types/upload_backup_to_sharepoint_types.py +1 -1
  30. saviialib/services/{epii → backup}/use_cases/upload_backup_to_sharepoint.py +2 -2
  31. saviialib/services/backup/utils/__init__.py +3 -0
  32. saviialib/services/{epii → backup}/utils/upload_backup_to_sharepoint_utils.py +1 -1
  33. saviialib/services/netcamera/api.py +30 -0
  34. saviialib/services/netcamera/controllers/get_media_files.py +40 -0
  35. saviialib/services/netcamera/controllers/types/get_media_files_types.py +16 -0
  36. saviialib/services/netcamera/use_cases/get_media_files.py +76 -0
  37. saviialib/services/netcamera/use_cases/types/get_media_files_types.py +18 -0
  38. saviialib/services/shakes/__init__.py +0 -0
  39. saviialib/services/shakes/api.py +31 -0
  40. saviialib/services/shakes/controllers/get_miniseed_files.py +48 -0
  41. saviialib/services/shakes/controllers/types/get_miniseed_files_types.py +16 -0
  42. saviialib/services/shakes/use_cases/get_miniseed_files.py +79 -0
  43. saviialib/services/shakes/use_cases/types/get_miniseed_files_types.py +18 -0
  44. saviialib/services/shakes/use_cases/utils/get_miniseed_files_utils.py +11 -0
  45. saviialib/services/thies/__init__.py +0 -0
  46. saviialib/services/thies/api.py +35 -0
  47. saviialib/services/{epii → thies}/controllers/types/update_thies_data_types.py +2 -4
  48. saviialib/services/{epii → thies}/controllers/update_thies_data.py +4 -4
  49. saviialib/services/{epii → thies}/use_cases/components/create_thies_statistics_file.py +11 -2
  50. saviialib/services/{epii → thies}/use_cases/types/update_thies_data_types.py +4 -1
  51. saviialib/services/{epii → thies}/use_cases/update_thies_data.py +19 -12
  52. saviialib-1.5.0.dist-info/METADATA +126 -0
  53. saviialib-1.5.0.dist-info/RECORD +87 -0
  54. {saviialib-1.3.0.dist-info → saviialib-1.5.0.dist-info}/WHEEL +1 -1
  55. saviialib/services/epii/api.py +0 -94
  56. saviialib/services/epii/utils/__init__.py +0 -3
  57. saviialib-1.3.0.dist-info/METADATA +0 -122
  58. saviialib-1.3.0.dist-info/RECORD +0 -57
  59. /saviialib/services/{epii → backup}/__init__.py +0 -0
  60. /saviialib/services/{epii → backup}/controllers/__init__.py +0 -0
  61. /saviialib/services/{epii → backup}/use_cases/constants/upload_backup_to_sharepoint_constants.py +0 -0
  62. /saviialib/services/{epii → thies}/use_cases/components/thies_bp.py +0 -0
  63. /saviialib/services/{epii → thies}/utils/update_thies_data_utils.py +0 -0
  64. {saviialib-1.3.0.dist-info → saviialib-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,36 @@
1
+ from typing import Any, Dict
2
+
3
+ from .controllers.upload_backup_to_sharepoint import (
4
+ UploadBackupToSharepointControllerInput,
5
+ )
6
+ from .controllers.upload_backup_to_sharepoint import UploadBackupToSharepointController
7
+ from saviialib.general_types.api.saviia_backup_api_types import SaviiaBackupConfig
8
+
9
+
10
+ class SaviiaBackupAPI:
11
+ """
12
+ EpiiAPI is a service class that provides methods to interact with Patagonia Center system.
13
+ """
14
+
15
+ def __init__(self, config: SaviiaBackupConfig):
16
+ self.config = config
17
+
18
+ async def upload_backup_to_sharepoint(
19
+ self, local_backup_source_path: str, sharepoint_destination_path: str
20
+ ) -> Dict[str, Any]:
21
+ """Migrate a backup folder from Home assistant to Sharepoint directory.
22
+ Args:
23
+ local_backup_source_path (str): Local path to backup.
24
+ Returns:
25
+ response (dict): A dictionary containing the response from the upload operation.
26
+ This dictionary will typically include information about the success or
27
+ failure of the upload, as well as any relevant metadata.
28
+ """
29
+
30
+ controller = UploadBackupToSharepointController(
31
+ UploadBackupToSharepointControllerInput(
32
+ self.config, local_backup_source_path, sharepoint_destination_path
33
+ )
34
+ )
35
+ response = await controller.execute()
36
+ return response.__dict__
@@ -1,4 +1,4 @@
1
- from .update_thies_data_types import (
1
+ from ....thies.controllers.types.update_thies_data_types import (
2
2
  UpdateThiesDataControllerInput,
3
3
  UpdateThiesDataControllerOutput,
4
4
  )
@@ -1,12 +1,12 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import Dict
3
3
 
4
- from saviialib.general_types.api.epii_api_types import EpiiSharepointBackupConfig
4
+ from saviialib.general_types.api.saviia_backup_api_types import SaviiaBackupConfig
5
5
 
6
6
 
7
7
  @dataclass
8
8
  class UploadBackupToSharepointControllerInput:
9
- config: EpiiSharepointBackupConfig
9
+ config: SaviiaBackupConfig
10
10
  local_backup_source_path: str
11
11
  sharepoint_destination_path: str
12
12
 
@@ -1,7 +1,7 @@
1
1
  from http import HTTPStatus
2
- from saviialib.general_types.api.epii_api_types import SharepointConfig
2
+ from saviialib.general_types.api.saviia_api_types import SharepointConfig
3
3
 
4
- from saviialib.general_types.error_types.api.epii_api_error_types import (
4
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
5
5
  BackupUploadError,
6
6
  BackupSourcePathError,
7
7
  BackupEmptyError,
@@ -10,14 +10,14 @@ from saviialib.general_types.error_types.common.common_types import (
10
10
  EmptyDataError,
11
11
  SharepointClientError,
12
12
  )
13
- from saviialib.services.epii.controllers.types.upload_backup_to_sharepoint_types import (
13
+ from saviialib.services.backup.controllers.types.upload_backup_to_sharepoint_types import (
14
14
  UploadBackupToSharepointControllerInput,
15
15
  UploadBackupToSharepointControllerOutput,
16
16
  )
17
- from saviialib.services.epii.use_cases.types.upload_backup_to_sharepoint_types import (
17
+ from saviialib.services.backup.use_cases.types.upload_backup_to_sharepoint_types import (
18
18
  UploadBackupToSharepointUseCaseInput,
19
19
  )
20
- from saviialib.services.epii.use_cases.upload_backup_to_sharepoint import (
20
+ from saviialib.services.backup.use_cases.upload_backup_to_sharepoint import (
21
21
  UploadBackupToSharepointUsecase,
22
22
  )
23
23
 
@@ -1,4 +1,4 @@
1
- from .update_thies_data_types import (
1
+ from ....thies.use_cases.types.update_thies_data_types import (
2
2
  FtpClientConfig,
3
3
  SharepointConfig,
4
4
  UpdateThiesDataUseCaseInput,
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from saviialib.general_types.api.epii_api_types import SharepointConfig
2
+ from saviialib.general_types.api.saviia_api_types import SharepointConfig
3
3
  from logging import Logger
4
4
 
5
5
 
@@ -2,7 +2,7 @@ import asyncio
2
2
  from time import time
3
3
  from saviialib.libs.zero_dependency.utils.datetime_utils import today, datetime_to_str
4
4
  from logging import Logger
5
- from saviialib.general_types.error_types.api.epii_api_error_types import (
5
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
6
6
  BackupEmptyError,
7
7
  BackupSourcePathError,
8
8
  BackupUploadError,
@@ -23,7 +23,7 @@ from saviialib.libs.sharepoint_client import (
23
23
  SpUploadFileArgs,
24
24
  SpCreateFolderArgs,
25
25
  )
26
- from saviialib.services.epii.utils.upload_backup_to_sharepoint_utils import (
26
+ from saviialib.services.backup.utils.upload_backup_to_sharepoint_utils import (
27
27
  calculate_percentage_uploaded,
28
28
  count_files_in_directory,
29
29
  extract_error_message,
@@ -0,0 +1,3 @@
1
+ from ...thies.utils.update_thies_data_utils import parse_execute_response
2
+
3
+ __all__ = ["parse_execute_response"]
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  from logging import Logger
3
3
  from typing import List, Dict, Optional
4
- from saviialib.general_types.error_types.api.epii_api_error_types import (
4
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
5
5
  BackupSourcePathError,
6
6
  )
7
7
  from saviialib.libs.directory_client import DirectoryClient, DirectoryClientArgs
@@ -0,0 +1,30 @@
1
+ from .controllers.get_media_files import (
2
+ GetMediaFilesController,
3
+ GetMediaFilesControllerInput,
4
+ )
5
+ from typing import Dict, Tuple, Any
6
+ from saviialib.general_types.api.saviia_netcamera_api_types import SaviiaNetcameraConfig
7
+
8
+
9
+ class NetcameraAPI:
10
+ """This class provides methods for interacting with network cameras and retrieving
11
+ files using streaming services."""
12
+
13
+ def __init__(self, config: SaviiaNetcameraConfig):
14
+ self.config = config
15
+
16
+ async def get_media_files(
17
+ self, cameras: Dict[str, Tuple[str, int]]
18
+ ) -> Dict[str, Any]:
19
+ """Retrieve media files from Network cameras.
20
+
21
+ :param cameras: Dictionary where the key is the identifier of the camera, and the
22
+ value is a tuple wich contains the service IP address and port of connection.
23
+ Example: {'cam_01': ('192.168.1.10', 8080), ...}
24
+ :type cameras: dict
25
+ :return response: A dictionary containg information of the extraction operation.
26
+ :rtype: dict
27
+ """
28
+ controller = GetMediaFilesController(GetMediaFilesControllerInput(cameras))
29
+ response = await controller.execute()
30
+ return response.__dict__
@@ -0,0 +1,40 @@
1
+ from .types.get_media_files_types import (
2
+ GetMediaFilesControllerInput,
3
+ GetMediaFilesControllerOutput,
4
+ )
5
+ from saviialib.services.netcamera.use_cases.get_media_files import (
6
+ GetMediaFilesUseCase,
7
+ GetMediaFilesUseCaseInput,
8
+ )
9
+ from http import HTTPStatus
10
+ from saviialib.general_types.error_types.api.saviia_netcamera_error_types import (
11
+ NetcameraConnectionError,
12
+ )
13
+
14
+
15
+ class GetMediaFilesController:
16
+ def __init__(self, input: GetMediaFilesControllerInput) -> None:
17
+ self.use_case = GetMediaFilesUseCase(
18
+ GetMediaFilesUseCaseInput(
19
+ cameras=input.cameras,
20
+ username=input.config.username,
21
+ password=input.config.password,
22
+ protocol=input.config.protocol,
23
+ logger=input.config.logger,
24
+ destination_path=input.config.destination_path,
25
+ )
26
+ )
27
+
28
+ async def execute(self) -> GetMediaFilesControllerOutput:
29
+ try:
30
+ _ = await self.use_case.execute()
31
+ return GetMediaFilesControllerOutput(
32
+ message="The extraction of media files was successfully!",
33
+ status=HTTPStatus.OK.value,
34
+ )
35
+ except NetcameraConnectionError as error:
36
+ return GetMediaFilesControllerOutput(
37
+ message="An unnexpected error ocurred while extractingphotos and videos.",
38
+ status=HTTPStatus.GATEWAY_TIMEOUT.value,
39
+ metadata={"error": error.__str__()},
40
+ )
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Dict, Tuple
3
+ from saviialib.general_types.api.saviia_netcamera_api_types import SaviiaNetcameraConfig
4
+
5
+
6
+ @dataclass
7
+ class GetMediaFilesControllerInput:
8
+ config = SaviiaNetcameraConfig
9
+ cameras: Dict[str, Tuple[str, int]]
10
+
11
+
12
+ @dataclass
13
+ class GetMediaFilesControllerOutput:
14
+ status: int
15
+ message: str
16
+ metadata: Dict = field(default_factory=dict)
@@ -0,0 +1,76 @@
1
+ from .types.get_media_files_types import (
2
+ GetMediaFilesUseCaseInput,
3
+ GetMediaFilesUseCaseOutput,
4
+ )
5
+ from saviialib.libs.ffmpeg_client import (
6
+ FfmpegClient,
7
+ FfmpegClientInitArgs,
8
+ RecordVideoArgs,
9
+ RecordPhotoArgs,
10
+ )
11
+ from saviialib.libs.zero_dependency.utils.strings_utils import are_equal
12
+ from typing import Tuple, Dict
13
+ from saviialib.libs.directory_client.directory_client import (
14
+ DirectoryClient,
15
+ DirectoryClientArgs,
16
+ )
17
+ from saviialib.general_types.error_types.api.saviia_netcamera_error_types import (
18
+ NetcameraConnectionError,
19
+ )
20
+
21
+
22
+ class GetMediaFilesUseCase:
23
+ def __init__(self, input: GetMediaFilesUseCaseInput) -> None:
24
+ self.ffmpeg_client = FfmpegClient(
25
+ FfmpegClientInitArgs(
26
+ client_name="ffmpeg_asyncio",
27
+ )
28
+ )
29
+ self.dir_client = DirectoryClient(DirectoryClientArgs("os_client"))
30
+ self.user = input.username
31
+ self.pwd = input.password
32
+ self.cameras: Dict[str, Tuple[str, int]] = input.cameras
33
+ self.protocol = input.protocol
34
+ self.logger = input.logger
35
+ self.dest_path = input.destination_path
36
+
37
+ async def _retieve_with_rtsp(self):
38
+ for name, conn in self.cameras.items():
39
+ ip, port = conn
40
+ dest_path = self.dir_client.join_paths(self.dest_path, name)
41
+ try:
42
+ # Extraction of photo files into dest_path dir.
43
+ await self.ffmpeg_client.record_photo(
44
+ RecordPhotoArgs(
45
+ ip_address=ip,
46
+ port=str(port),
47
+ destination_path=dest_path,
48
+ rtsp_user=self.user,
49
+ rtsp_password=self.pwd,
50
+ extension="jpg",
51
+ frames=1,
52
+ )
53
+ )
54
+ # Extraction of video files into dest_path dir.
55
+ await self.ffmpeg_client.record_video(
56
+ RecordVideoArgs(
57
+ destination_path=dest_path,
58
+ ip_address=ip,
59
+ port=str(port),
60
+ rtsp_user=self.user,
61
+ rtsp_password=self.pwd,
62
+ extension="mp3",
63
+ duration=10,
64
+ )
65
+ )
66
+ except ConnectionError as error:
67
+ raise NetcameraConnectionError(reason=error)
68
+
69
+ async def execute(self) -> GetMediaFilesUseCaseOutput:
70
+ if are_equal(self.protocol, "rtsp"):
71
+ await self._retieve_with_rtsp()
72
+ else:
73
+ raise NotImplementedError(
74
+ f"The media files extraction with {self.protocol} is not implemented yet."
75
+ )
76
+ return GetMediaFilesUseCaseOutput()
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from logging import Logger
3
+ from typing import Dict, Tuple
4
+
5
+
6
+ @dataclass
7
+ class GetMediaFilesUseCaseInput:
8
+ cameras: Dict[str, Tuple[str, int]]
9
+ username: str
10
+ password: str
11
+ protocol: str
12
+ logger: Logger
13
+ destination_path: str
14
+
15
+
16
+ @dataclass
17
+ class GetMediaFilesUseCaseOutput:
18
+ pass
File without changes
@@ -0,0 +1,31 @@
1
+ from .controllers.get_miniseed_files import (
2
+ GetMiniseedFilesController,
3
+ GetMiniseedFilesControllerInput,
4
+ )
5
+ from saviialib.general_types.api.saviia_shakes_api_types import SaviiaShakesConfig
6
+
7
+ from typing import Dict
8
+
9
+
10
+ class ShakesAPI:
11
+ """This class provides methods for interacting with Raspberry Shakes"""
12
+
13
+ def __init__(self, config: SaviiaShakesConfig) -> None:
14
+ self.config = config
15
+
16
+ async def get_miniseed_files(self, raspberry_shakes: Dict[str, str]):
17
+ """Download the MiniSEED files from the SFTP Server provided by each Raspberry Shake.
18
+ Args:
19
+ raspberry_shakes (dict): Dictionary where the key is the name of the Raspberry Shake,
20
+ and the value is the IP Address.
21
+ Returns:
22
+ response (dict): A dictionary containg the response from the download operation.
23
+ This response will tipically include the message, the response status, and metadata.
24
+ """
25
+ controller = GetMiniseedFilesController(
26
+ GetMiniseedFilesControllerInput(
27
+ config=self.config, raspberry_shakes=raspberry_shakes
28
+ )
29
+ )
30
+ response = await controller.execute()
31
+ return response.__dict__
@@ -0,0 +1,48 @@
1
+ from .types.get_miniseed_files_types import (
2
+ GetMiniseedFilesControllerInput,
3
+ GetMiniseedFilesControllerOutput,
4
+ )
5
+ from saviialib.services.shakes.use_cases.get_miniseed_files import (
6
+ GetMiniseedFilesUseCase,
7
+ GetMiniseedFilesUseCaseInput,
8
+ )
9
+ from http import HTTPStatus
10
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
11
+ ShakesNoContentError,
12
+ )
13
+ from saviialib.general_types.error_types.common.common_types import SftpClientError
14
+
15
+
16
+ class GetMiniseedFilesController:
17
+ def __init__(self, input: GetMiniseedFilesControllerInput) -> None:
18
+ self.use_case = GetMiniseedFilesUseCase(
19
+ GetMiniseedFilesUseCaseInput(
20
+ raspberry_shakes=input.raspberry_shakes,
21
+ username=input.config.sftp_user,
22
+ password=input.config.sftp_password,
23
+ ssh_key_path=input.config.ssh_key_path,
24
+ port=input.config.sftp_port,
25
+ logger=input.config.logger,
26
+ )
27
+ )
28
+
29
+ async def execute(self) -> GetMiniseedFilesControllerOutput:
30
+ try:
31
+ res = await self.use_case.execute()
32
+ return GetMiniseedFilesControllerOutput(
33
+ message="The MiniSEED files have been downloaded succesfully!",
34
+ status=HTTPStatus.OK.value,
35
+ metadata=res.download_status,
36
+ )
37
+ except ShakesNoContentError as error:
38
+ return GetMiniseedFilesControllerOutput(
39
+ message="No files to upload.",
40
+ status=HTTPStatus.NO_CONTENT.value,
41
+ metadata={"error": error.__str__()},
42
+ )
43
+ except SftpClientError as error:
44
+ return GetMiniseedFilesControllerOutput(
45
+ message="An unexpected error ocurred during SFTP Client connection.",
46
+ status=HTTPStatus.REQUEST_TIMEOUT.value,
47
+ metadata={"error": error.__str__()},
48
+ )
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict
3
+ from saviialib.general_types.api.saviia_shakes_api_types import SaviiaShakesConfig
4
+
5
+
6
+ @dataclass
7
+ class GetMiniseedFilesControllerInput:
8
+ raspberry_shakes: Dict[str, str]
9
+ config: SaviiaShakesConfig
10
+
11
+
12
+ @dataclass
13
+ class GetMiniseedFilesControllerOutput:
14
+ status: int
15
+ metadata: Dict
16
+ message: str
@@ -0,0 +1,79 @@
1
+ import asyncio
2
+ from .types.get_miniseed_files_types import (
3
+ GetMiniseedFilesUseCaseInput,
4
+ GetMiniseedFilesUseCaseOutput,
5
+ )
6
+ from typing import Dict, Any
7
+ from saviialib.libs.sftp_client import (
8
+ SFTPClient,
9
+ SFTPClientInitArgs,
10
+ ListfilesArgs,
11
+ DownloadfilesArgs,
12
+ )
13
+ from saviialib.libs.directory_client import DirectoryClient, DirectoryClientArgs
14
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
15
+ ShakesNoContentError,
16
+ )
17
+ from saviialib.general_types.error_types.common.common_types import SftpClientError
18
+ from .utils.get_miniseed_files_utils import parse_downloaded_metadata
19
+
20
+
21
+ class GetMiniseedFilesUseCase:
22
+ def __init__(self, input: GetMiniseedFilesUseCaseInput) -> None:
23
+ self.password = input.password
24
+ self.username = input.username
25
+ self.ssh_key_path = input.ssh_key_path
26
+ self.port = input.port
27
+ self.raspberry_shakes: Dict[str, str] = input.raspberry_shakes
28
+ self.dir_client = DirectoryClient(DirectoryClientArgs("os_client"))
29
+
30
+ def _initialize_sftp_client(self, ip_address: str):
31
+ return SFTPClient(
32
+ SFTPClientInitArgs(
33
+ "asyncssh_sftp",
34
+ password=self.password,
35
+ username=self.username,
36
+ ssh_key_path=self.ssh_key_path,
37
+ host=ip_address,
38
+ port=self.port,
39
+ )
40
+ )
41
+
42
+ async def _download_mseed_file(self, rs_name: str, rs_ip: str) -> Dict[str, Any]:
43
+ DEST_BASE_DIR = "./rshakes-mseed-files"
44
+ SOURCE_PATH = self.dir_client.join_paths("opt", "data", "archive")
45
+ local_path = self.dir_client.join_paths(DEST_BASE_DIR, rs_name)
46
+ if not await self.dir_client.isdir(local_path):
47
+ await self.dir_client.makedirs(local_path)
48
+ sftp_client = self._initialize_sftp_client(rs_ip)
49
+
50
+ local_files = await self.dir_client.listdir(local_path)
51
+ try:
52
+ sftp_files = await sftp_client.list_files(ListfilesArgs(path=SOURCE_PATH))
53
+ pending_files = set(sftp_files) - set(local_files)
54
+ if not pending_files:
55
+ raise ShakesNoContentError
56
+
57
+ await sftp_client.download_files(
58
+ DownloadfilesArgs(
59
+ source_path=SOURCE_PATH,
60
+ destination_path=local_path,
61
+ files_to_download=list(pending_files),
62
+ )
63
+ )
64
+ except ConnectionError as error:
65
+ raise SftpClientError(reason=error)
66
+ return {
67
+ "rs_name": rs_name,
68
+ "destination_path": local_path,
69
+ "total_files": len(pending_files),
70
+ }
71
+
72
+ async def execute(self):
73
+ requests = []
74
+ for rs_name, rs_ip in self.raspberry_shakes:
75
+ requests.append(self._download_mseed_file(rs_name, rs_ip))
76
+ responses = await asyncio.gather(*requests, return_exceptions=True)
77
+ return GetMiniseedFilesUseCaseOutput(
78
+ download_status=parse_downloaded_metadata(responses),
79
+ )
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Dict
3
+ from logging import Logger
4
+
5
+
6
+ @dataclass
7
+ class GetMiniseedFilesUseCaseInput:
8
+ raspberry_shakes: Dict[str, str]
9
+ username: str
10
+ password: str
11
+ ssh_key_path: str
12
+ port: int
13
+ logger: Logger
14
+
15
+
16
+ @dataclass
17
+ class GetMiniseedFilesUseCaseOutput:
18
+ download_status: Dict[str, str] = field(default_factory=dict)
@@ -0,0 +1,11 @@
1
+ from typing import Dict, List, Any
2
+
3
+
4
+ def parse_downloaded_metadata(responses: List[Any]) -> Dict[str, Any]:
5
+ return {
6
+ x["rs_name"]: {
7
+ "destination_path": x["destination_path"],
8
+ "total_files": x["total_files"],
9
+ }
10
+ for x in responses
11
+ }
File without changes
@@ -0,0 +1,35 @@
1
+ from typing import Any, Dict, List
2
+
3
+ from .controllers.types.update_thies_data_types import UpdateThiesDataControllerInput
4
+ from .controllers.update_thies_data import UpdateThiesDataController
5
+ from saviialib.general_types.api.saviia_thies_api_types import (
6
+ SaviiaThiesConfig,
7
+ )
8
+
9
+
10
+ class SaviiaThiesAPI:
11
+ def __init__(self, config: SaviiaThiesConfig) -> None:
12
+ self.config = config
13
+
14
+ async def update_thies_data(
15
+ self, sharepoint_folders_path: List[str], ftp_server_folders_path: List[str]
16
+ ) -> Dict[str, Any]:
17
+ """Updates data from a THIES Data Logger by connecting to an FTP server
18
+ and transferring data to specified Sharepoint folders.
19
+
20
+ Args:
21
+ sharepoint_folders_path (list): List of Sharepoint folder paths for AVG and EXT data.
22
+ The AVG path must be the first element.
23
+ ftp_server_folders_path (list): List of FTP server folder paths for AVG and EXT data.
24
+ The AVG path must be the first element.
25
+
26
+ Returns:
27
+ dict: A dictionary representation of the API response.
28
+ """
29
+ controller = UpdateThiesDataController(
30
+ UpdateThiesDataControllerInput(
31
+ self.config, sharepoint_folders_path, ftp_server_folders_path
32
+ )
33
+ )
34
+ response = await controller.execute()
35
+ return response.__dict__
@@ -1,13 +1,11 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import Dict
3
- from saviialib.general_types.api.epii_api_types import (
4
- EpiiUpdateThiesConfig,
5
- )
3
+ from saviialib.general_types.api.saviia_thies_api_types import SaviiaThiesConfig
6
4
 
7
5
 
8
6
  @dataclass
9
7
  class UpdateThiesDataControllerInput:
10
- config: EpiiUpdateThiesConfig
8
+ config: SaviiaThiesConfig
11
9
  sharepoint_folders_path: list
12
10
  ftp_server_folders_path: list
13
11
 
@@ -1,6 +1,6 @@
1
1
  from http import HTTPStatus
2
2
 
3
- from saviialib.general_types.error_types.api.epii_api_error_types import (
3
+ from saviialib.general_types.error_types.api.saviia_api_error_types import (
4
4
  SharePointFetchingError,
5
5
  ThiesConnectionError,
6
6
  ThiesFetchingError,
@@ -12,16 +12,16 @@ from saviialib.general_types.error_types.common.common_types import (
12
12
  FtpClientError,
13
13
  SharepointClientError,
14
14
  )
15
- from saviialib.services.epii.controllers.types.update_thies_data_types import (
15
+ from saviialib.services.thies.controllers.types.update_thies_data_types import (
16
16
  UpdateThiesDataControllerInput,
17
17
  UpdateThiesDataControllerOutput,
18
18
  )
19
- from saviialib.services.epii.use_cases.types import (
19
+ from saviialib.services.backup.use_cases.types import (
20
20
  UpdateThiesDataUseCaseInput,
21
21
  SharepointConfig,
22
22
  FtpClientConfig,
23
23
  )
24
- from saviialib.services.epii.use_cases.update_thies_data import (
24
+ from saviialib.services.thies.use_cases.update_thies_data import (
25
25
  UpdateThiesDataUseCase,
26
26
  )
27
27
 
@@ -1,5 +1,4 @@
1
1
  from .thies_bp import THIESDayData
2
- from typing import List
3
2
  from logging import Logger
4
3
  from asyncio import to_thread
5
4
  from saviialib.libs.directory_client import DirectoryClient
@@ -70,7 +69,7 @@ UNITS = {
70
69
 
71
70
 
72
71
  async def create_thies_daily_statistics_file(
73
- os_client: DirectoryClient, logger: Logger, daily_files: List[str]
72
+ os_client: DirectoryClient, logger: Logger
74
73
  ) -> None:
75
74
  logger.debug("[thies_synchronization_lib] Creating Daily Statistics ...")
76
75
  csv_client = FilesClient(FilesClientInitArgs(client_name="csv_client"))
@@ -127,6 +126,16 @@ async def create_thies_daily_statistics_file(
127
126
  if max_col not in row:
128
127
  max_val = 0
129
128
 
129
+ if (mean < min_val or mean > max_val) and col not in ["WD"]:
130
+ logger.warning(
131
+ f"[thies_synchronization_lib] Inconsistent data for {col}: "
132
+ f"min {min_val}, max {max_val}, mean {mean}. "
133
+ )
134
+ mean = (min_val + max_val) / 2
135
+ logger.warning(
136
+ f"[thies_synchronization_lib] Mean value corrected to {mean}."
137
+ )
138
+
130
139
  if col in ["WD"]: # Avoid error
131
140
  rows.append(
132
141
  {
@@ -1,6 +1,9 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import Dict, List
3
- from saviialib.general_types.api.epii_api_types import FtpClientConfig, SharepointConfig
3
+ from saviialib.general_types.api.saviia_api_types import (
4
+ FtpClientConfig,
5
+ SharepointConfig,
6
+ )
4
7
  from logging import Logger
5
8
 
6
9