saviialib 1.6.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.
- saviialib/__init__.py +79 -0
- saviialib/general_types/__init__.py +0 -0
- saviialib/general_types/api/__init__.py +0 -0
- saviialib/general_types/api/saviia_api_types.py +48 -0
- saviialib/general_types/api/saviia_backup_api_types.py +24 -0
- saviialib/general_types/api/saviia_netcamera_api_types.py +11 -0
- saviialib/general_types/api/saviia_shakes_api_types.py +21 -0
- saviialib/general_types/api/saviia_thies_api_types.py +31 -0
- saviialib/general_types/error_types/__init__.py +0 -0
- saviialib/general_types/error_types/api/__init__.py +0 -0
- saviialib/general_types/error_types/api/saviia_api_error_types.py +113 -0
- saviialib/general_types/error_types/api/saviia_netcamera_error_types.py +7 -0
- saviialib/general_types/error_types/common/__init__.py +7 -0
- saviialib/general_types/error_types/common/common_types.py +26 -0
- saviialib/libs/directory_client/__init__.py +4 -0
- saviialib/libs/directory_client/client/os_client.py +55 -0
- saviialib/libs/directory_client/directory_client.py +44 -0
- saviialib/libs/directory_client/directory_client_contract.py +40 -0
- saviialib/libs/directory_client/types/directory_client_types.py +6 -0
- saviialib/libs/files_client/__init__.py +4 -0
- saviialib/libs/files_client/clients/aiofiles_client.py +44 -0
- saviialib/libs/files_client/clients/csv_client.py +42 -0
- saviialib/libs/files_client/files_client.py +26 -0
- saviialib/libs/files_client/files_client_contract.py +13 -0
- saviialib/libs/files_client/types/files_client_types.py +32 -0
- saviialib/libs/ftp_client/__init__.py +4 -0
- saviialib/libs/ftp_client/clients/__init__.py +0 -0
- saviialib/libs/ftp_client/clients/aioftp_client.py +52 -0
- saviialib/libs/ftp_client/clients/ftplib_client.py +58 -0
- saviialib/libs/ftp_client/ftp_client.py +25 -0
- saviialib/libs/ftp_client/ftp_client_contract.py +13 -0
- saviialib/libs/ftp_client/types/__init__.py +3 -0
- saviialib/libs/ftp_client/types/ftp_client_types.py +18 -0
- saviialib/libs/log_client/__init__.py +19 -0
- saviialib/libs/log_client/log_client.py +46 -0
- saviialib/libs/log_client/log_client_contract.py +28 -0
- saviialib/libs/log_client/logging_client/logging_client.py +58 -0
- saviialib/libs/log_client/types/__init__.py +0 -0
- saviialib/libs/log_client/types/log_client_types.py +47 -0
- saviialib/libs/log_client/utils/log_client_utils.py +6 -0
- saviialib/libs/sftp_client/__init__.py +8 -0
- saviialib/libs/sftp_client/clients/asyncssh_sftp_client.py +83 -0
- saviialib/libs/sftp_client/sftp_client.py +26 -0
- saviialib/libs/sftp_client/sftp_client_contract.py +13 -0
- saviialib/libs/sftp_client/types/sftp_client_types.py +24 -0
- saviialib/libs/sharepoint_client/__init__.py +17 -0
- saviialib/libs/sharepoint_client/clients/sharepoint_rest_api.py +160 -0
- saviialib/libs/sharepoint_client/sharepoint_client.py +58 -0
- saviialib/libs/sharepoint_client/sharepoint_client_contract.py +26 -0
- saviialib/libs/sharepoint_client/types/sharepoint_client_types.py +30 -0
- saviialib/libs/zero_dependency/utils/booleans_utils.py +2 -0
- saviialib/libs/zero_dependency/utils/datetime_utils.py +25 -0
- saviialib/libs/zero_dependency/utils/strings_utils.py +5 -0
- saviialib/services/backup/__init__.py +0 -0
- saviialib/services/backup/api.py +36 -0
- saviialib/services/backup/controllers/__init__.py +0 -0
- saviialib/services/backup/controllers/types/__init__.py +6 -0
- saviialib/services/backup/controllers/types/upload_backup_to_sharepoint_types.py +18 -0
- saviialib/services/backup/controllers/upload_backup_to_sharepoint.py +87 -0
- saviialib/services/backup/use_cases/constants/upload_backup_to_sharepoint_constants.py +5 -0
- saviialib/services/backup/use_cases/types/__init__.py +7 -0
- saviialib/services/backup/use_cases/types/upload_backup_to_sharepoint_types.py +11 -0
- saviialib/services/backup/use_cases/upload_backup_to_sharepoint.py +474 -0
- saviialib/services/backup/utils/__init__.py +3 -0
- saviialib/services/backup/utils/upload_backup_to_sharepoint_utils.py +100 -0
- saviialib/services/netcamera/api.py +30 -0
- saviialib/services/netcamera/controllers/get_media_files.py +40 -0
- saviialib/services/netcamera/controllers/types/get_media_files_types.py +16 -0
- saviialib/services/netcamera/use_cases/get_media_files.py +76 -0
- saviialib/services/netcamera/use_cases/types/get_media_files_types.py +18 -0
- saviialib/services/shakes/__init__.py +0 -0
- saviialib/services/shakes/api.py +31 -0
- saviialib/services/shakes/controllers/get_miniseed_files.py +48 -0
- saviialib/services/shakes/controllers/types/get_miniseed_files_types.py +16 -0
- saviialib/services/shakes/use_cases/get_miniseed_files.py +79 -0
- saviialib/services/shakes/use_cases/types/get_miniseed_files_types.py +18 -0
- saviialib/services/shakes/use_cases/utils/get_miniseed_files_utils.py +11 -0
- saviialib/services/thies/__init__.py +0 -0
- saviialib/services/thies/api.py +42 -0
- saviialib/services/thies/constants/update_thies_data_constants.py +67 -0
- saviialib/services/thies/controllers/types/update_thies_data_types.py +18 -0
- saviialib/services/thies/controllers/update_thies_data.py +119 -0
- saviialib/services/thies/use_cases/components/create_thies_statistics_file.py +115 -0
- saviialib/services/thies/use_cases/components/thies_bp.py +442 -0
- saviialib/services/thies/use_cases/types/update_thies_data_types.py +24 -0
- saviialib/services/thies/use_cases/update_thies_data.py +391 -0
- saviialib/services/thies/utils/update_thies_data_utils.py +21 -0
- saviialib-1.6.1.dist-info/METADATA +126 -0
- saviialib-1.6.1.dist-info/RECORD +91 -0
- saviialib-1.6.1.dist-info/WHEEL +4 -0
- saviialib-1.6.1.dist-info/licenses/LICENSE +22 -0
saviialib/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# read version from installed package
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
|
|
4
|
+
__version__ = version("saviialib")
|
|
5
|
+
|
|
6
|
+
from .general_types.api.saviia_api_types import SaviiaAPIConfig
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Type, Any, overload, Literal, List
|
|
9
|
+
|
|
10
|
+
from saviialib.services.backup.api import SaviiaBackupAPI
|
|
11
|
+
from saviialib.services.thies.api import SaviiaThiesAPI
|
|
12
|
+
from saviialib.general_types.api.saviia_thies_api_types import SaviiaThiesConfig
|
|
13
|
+
from saviialib.general_types.api.saviia_backup_api_types import SaviiaBackupConfig
|
|
14
|
+
|
|
15
|
+
__all__ = ["SaviiaAPI", "SaviiaAPIConfig"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SaviiaAPI:
|
|
19
|
+
API_REGISTRY: Dict[str, Type] = {
|
|
20
|
+
"thies": SaviiaThiesAPI,
|
|
21
|
+
"backup": SaviiaBackupAPI,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@overload
|
|
25
|
+
def get(self, name: Literal["thies"]) -> SaviiaThiesAPI: ...
|
|
26
|
+
@overload
|
|
27
|
+
def get(self, name: Literal["backup"]) -> SaviiaBackupAPI: ...
|
|
28
|
+
|
|
29
|
+
def __init__(self, config: SaviiaAPIConfig):
|
|
30
|
+
"""
|
|
31
|
+
Receive a dictionary of configurations, with the same key
|
|
32
|
+
as those registered in API_REGISTRY.
|
|
33
|
+
|
|
34
|
+
:params configs: Dictionary of configurations for each API.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
configs = {
|
|
38
|
+
"thies": SaviiaThiesConfig(...),
|
|
39
|
+
"backup": SaviiaBackupConfig(...)
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
self._instances: Dict[str, Any] = {}
|
|
43
|
+
|
|
44
|
+
for name, api_class in SaviiaAPI.API_REGISTRY.items():
|
|
45
|
+
if name == "thies":
|
|
46
|
+
service_config = SaviiaThiesConfig(
|
|
47
|
+
ftp_host=config.ftp_host,
|
|
48
|
+
ftp_port=config.ftp_port,
|
|
49
|
+
ftp_user=config.ftp_user,
|
|
50
|
+
ftp_password=config.ftp_password,
|
|
51
|
+
sharepoint_client_id=config.sharepoint_client_id,
|
|
52
|
+
sharepoint_client_secret=config.sharepoint_client_secret,
|
|
53
|
+
sharepoint_tenant_id=config.sharepoint_tenant_id,
|
|
54
|
+
sharepoint_tenant_name=config.sharepoint_tenant_name,
|
|
55
|
+
sharepoint_site_name=config.sharepoint_site_name,
|
|
56
|
+
logger=config.logger,
|
|
57
|
+
)
|
|
58
|
+
elif name == "backup":
|
|
59
|
+
service_config = SaviiaBackupConfig(
|
|
60
|
+
sharepoint_client_id=config.sharepoint_client_id,
|
|
61
|
+
sharepoint_client_secret=config.sharepoint_client_secret,
|
|
62
|
+
sharepoint_tenant_id=config.sharepoint_tenant_id,
|
|
63
|
+
sharepoint_tenant_name=config.sharepoint_tenant_name,
|
|
64
|
+
sharepoint_site_name=config.sharepoint_site_name,
|
|
65
|
+
logger=config.logger,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
self._instances[name] = api_class(service_config)
|
|
69
|
+
|
|
70
|
+
def get(self, name: Literal["thies", "backup"]) -> Any:
|
|
71
|
+
"""Returns the API instance associated with the given name."""
|
|
72
|
+
try:
|
|
73
|
+
return self._instances[name]
|
|
74
|
+
except KeyError:
|
|
75
|
+
raise ValueError(f"API '{name}' is not registered or not configured.")
|
|
76
|
+
|
|
77
|
+
def list_available(self) -> List[str]:
|
|
78
|
+
"""List of available registered APIs."""
|
|
79
|
+
return list(self._instances.keys())
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class SaviiaAPIConfig:
|
|
7
|
+
"""
|
|
8
|
+
Configuration for SAVIIA API.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
ftp_port (int): Port number of the FTP server.
|
|
12
|
+
ftp_host (str): Hostname or IP address of the FTP server.
|
|
13
|
+
ftp_user (str): Username for the FTP server.
|
|
14
|
+
ftp_password (str): Password for the FTP server.
|
|
15
|
+
sharepoint_client_id (str): Client ID for SharePoint authentication.
|
|
16
|
+
sharepoint_client_secret (str): Client secret for SharePoint authentication.
|
|
17
|
+
sharepoint_tenant_id (str): Tenant ID for SharePoint authentication.
|
|
18
|
+
sharepoint_tenant_name (str): Tenant name for SharePoint.
|
|
19
|
+
sharepoint_site_name (str): Site name in SharePoint.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
ftp_host: str
|
|
23
|
+
ftp_port: int
|
|
24
|
+
ftp_user: str
|
|
25
|
+
ftp_password: str
|
|
26
|
+
sharepoint_client_id: str
|
|
27
|
+
sharepoint_client_secret: str
|
|
28
|
+
sharepoint_tenant_id: str
|
|
29
|
+
sharepoint_tenant_name: str
|
|
30
|
+
sharepoint_site_name: str
|
|
31
|
+
logger: Logger
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class FtpClientConfig:
|
|
36
|
+
ftp_host: str
|
|
37
|
+
ftp_port: int
|
|
38
|
+
ftp_user: str
|
|
39
|
+
ftp_password: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SharepointConfig:
|
|
44
|
+
sharepoint_client_id: str
|
|
45
|
+
sharepoint_client_secret: str
|
|
46
|
+
sharepoint_tenant_id: str
|
|
47
|
+
sharepoint_tenant_name: str
|
|
48
|
+
sharepoint_site_name: str
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class SaviiaBackupConfig:
|
|
7
|
+
"""
|
|
8
|
+
Configuration for backing up files to SharePoint.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
sharepoint_client_id (str): Client ID for SharePoint authentication.
|
|
12
|
+
sharepoint_client_secret (str): Client secret for SharePoint authentication.
|
|
13
|
+
sharepoint_tenant_id (str): Tenant ID for SharePoint authentication.
|
|
14
|
+
sharepoint_tenant_name (str): Tenant name for SharePoint.
|
|
15
|
+
sharepoint_site_name (str): Site name in SharePoint.
|
|
16
|
+
local_backup_source_path (str): Local path to backup.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
sharepoint_client_id: str
|
|
20
|
+
sharepoint_client_secret: str
|
|
21
|
+
sharepoint_tenant_id: str
|
|
22
|
+
sharepoint_tenant_name: str
|
|
23
|
+
sharepoint_site_name: str
|
|
24
|
+
logger: Logger
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class SaviiaShakesConfig:
|
|
7
|
+
"""
|
|
8
|
+
Configuration for Raspberry shakes activities as Miniseed extraction, photo record and video record.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
sftp_user (str): Username for SFTP Client connection
|
|
12
|
+
sftp_password (str): Password for SFTP Client connection
|
|
13
|
+
sftp_port (str): SFTP Server Port. Default port is 22.
|
|
14
|
+
ssh_key_path (str): Path to the SSH Private key for client-side authentication.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
sftp_user: str
|
|
18
|
+
sftp_password: str
|
|
19
|
+
ssh_key_path: str
|
|
20
|
+
logger: Logger
|
|
21
|
+
sftp_port: int = 22
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class SaviiaThiesConfig:
|
|
7
|
+
"""
|
|
8
|
+
Configuration for Saviia Thies.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
ftp_port (int): Port number of the FTP server.
|
|
12
|
+
ftp_host (str): Hostname or IP address of the FTP server.
|
|
13
|
+
ftp_user (str): Username for the FTP server.
|
|
14
|
+
ftp_password (str): Password for the FTP server.
|
|
15
|
+
sharepoint_client_id (str): Client ID for SharePoint authentication.
|
|
16
|
+
sharepoint_client_secret (str): Client secret for SharePoint authentication.
|
|
17
|
+
sharepoint_tenant_id (str): Tenant ID for SharePoint authentication.
|
|
18
|
+
sharepoint_tenant_name (str): Tenant name for SharePoint.
|
|
19
|
+
sharepoint_site_name (str): Site name in SharePoint.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
ftp_host: str
|
|
23
|
+
ftp_port: int
|
|
24
|
+
ftp_user: str
|
|
25
|
+
ftp_password: str
|
|
26
|
+
sharepoint_client_id: str
|
|
27
|
+
sharepoint_client_secret: str
|
|
28
|
+
sharepoint_tenant_id: str
|
|
29
|
+
sharepoint_tenant_name: str
|
|
30
|
+
sharepoint_site_name: str
|
|
31
|
+
logger: Logger
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ThiesConnectionError(Exception):
|
|
5
|
+
"""Raised when unable to connect to the THIES FTP Server"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, *args, reason):
|
|
8
|
+
super().__init__(*args, reason)
|
|
9
|
+
self.reason = reason
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return "Unable to connect to THIES FTP Server. " + self.reason.__str__()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ThiesFetchingError(Exception):
|
|
16
|
+
"""Raised when no files are found to upload to the server."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args, reason):
|
|
19
|
+
super().__init__(*args, reason)
|
|
20
|
+
self.reason = reason
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return (
|
|
24
|
+
"An error ocurred while retrieving files from THIES FTP Server. "
|
|
25
|
+
+ self.reason.__str__()
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SharePointFetchingError(Exception):
|
|
30
|
+
"""Raised when there is an error fetching file names from the RCER cloud."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, *args, reason):
|
|
33
|
+
super().__init__(*args, reason)
|
|
34
|
+
self.reason = reason
|
|
35
|
+
|
|
36
|
+
def __str__(self):
|
|
37
|
+
try:
|
|
38
|
+
_, internal_metadata = self.reason.__str__().split(",", 1)
|
|
39
|
+
internal_metadata_dict = json.loads(internal_metadata)
|
|
40
|
+
return internal_metadata_dict["error_description"]
|
|
41
|
+
|
|
42
|
+
except json.decoder.JSONDecodeError:
|
|
43
|
+
return self.reason.__str__()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SharePointDirectoryError(Exception):
|
|
47
|
+
def __init__(self, *args, reason):
|
|
48
|
+
super().__init__(*args, reason)
|
|
49
|
+
self.reason = reason
|
|
50
|
+
|
|
51
|
+
def __str__(self):
|
|
52
|
+
return (
|
|
53
|
+
"An error occurred while fetching the folders from Microsoft SharePoint. "
|
|
54
|
+
+ self.reason.__str__()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SharePointUploadError(Exception):
|
|
59
|
+
"""Raised when there is an error uploading files to the Microsoft SharePoint folder."""
|
|
60
|
+
|
|
61
|
+
def __init__(self, *args, reason):
|
|
62
|
+
super().__init__(*args, reason)
|
|
63
|
+
self.reason = reason
|
|
64
|
+
|
|
65
|
+
def __str__(self):
|
|
66
|
+
return (
|
|
67
|
+
"An error occurred while uploading files to the Microsoft SharePoint folder. "
|
|
68
|
+
+ self.reason.__str__()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class BackupUploadError(Exception):
|
|
73
|
+
"""Raised when there is an error when occurs the migration from the local backup to
|
|
74
|
+
sharepoint cloud."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, *args, reason):
|
|
77
|
+
super().__init__(*args, reason)
|
|
78
|
+
self.reason = reason
|
|
79
|
+
|
|
80
|
+
def __str__(self):
|
|
81
|
+
return (
|
|
82
|
+
"An error occurred during the migration from the local backup to SharePoint cloud. "
|
|
83
|
+
"Search the logs for more information. " + self.reason.__str__()
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class BackupSourcePathError(Exception):
|
|
88
|
+
"""Raised when the local backup source path is invalid."""
|
|
89
|
+
|
|
90
|
+
def __init__(self, *args, reason):
|
|
91
|
+
super().__init__(*args, reason)
|
|
92
|
+
self.reason = reason
|
|
93
|
+
|
|
94
|
+
def __str__(self):
|
|
95
|
+
return "Invalid local backup source path. " + self.reason.__str__()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class BackupEmptyError(Exception):
|
|
99
|
+
"""Raised when the local backup folder is empty."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, *args):
|
|
102
|
+
super().__init__(*args)
|
|
103
|
+
|
|
104
|
+
def __str__(self):
|
|
105
|
+
return "The local backup folder is empty. "
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ShakesNoContentError(Exception):
|
|
109
|
+
def __init__(self, *args):
|
|
110
|
+
super().__init__(*args)
|
|
111
|
+
|
|
112
|
+
def __str__(self):
|
|
113
|
+
return "All the miniSEED files have been downloaded and are in the local directory."
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class EmptyDataError(Exception):
|
|
2
|
+
def __init__(self, *args, reason):
|
|
3
|
+
super().__init__(*args, reason)
|
|
4
|
+
self.reason = reason
|
|
5
|
+
|
|
6
|
+
def __str__(self):
|
|
7
|
+
return "The data provided is empty. " + self.reason.__str__()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SharepointClientError(Exception):
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return "SharePoint API REST Client initialization fails."
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FtpClientError(Exception):
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return "Ftp Client initialization fails."
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SftpClientError(Exception):
|
|
21
|
+
def __init__(self, *args, reason):
|
|
22
|
+
super().__init__(*args, reason)
|
|
23
|
+
self.reason = reason
|
|
24
|
+
|
|
25
|
+
def __str__(self):
|
|
26
|
+
return "SFTP Client initialization fails." + self.reason.__str__()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from saviialib.libs.directory_client.directory_client_contract import (
|
|
2
|
+
DirectoryClientContract,
|
|
3
|
+
)
|
|
4
|
+
import os
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OsClient(DirectoryClientContract):
|
|
9
|
+
@staticmethod
|
|
10
|
+
def join_paths(*paths: str) -> str:
|
|
11
|
+
return os.path.join(*paths)
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
async def path_exists(path: str) -> bool:
|
|
15
|
+
return await asyncio.to_thread(os.path.exists, path)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
async def listdir(path: str, more_info: bool = False) -> list:
|
|
19
|
+
def _listdir_with_size(path):
|
|
20
|
+
items = []
|
|
21
|
+
for name in os.listdir(path):
|
|
22
|
+
full_path = os.path.join(path, name)
|
|
23
|
+
is_dir = os.path.isdir(full_path)
|
|
24
|
+
size = os.stat(full_path).st_size if not is_dir else 0
|
|
25
|
+
items.append((name, size))
|
|
26
|
+
return items
|
|
27
|
+
|
|
28
|
+
if more_info:
|
|
29
|
+
return await asyncio.to_thread(_listdir_with_size, path)
|
|
30
|
+
return await asyncio.to_thread(os.listdir, path)
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
async def isdir(path: str) -> bool:
|
|
34
|
+
return await asyncio.to_thread(os.path.isdir, path)
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
async def makedirs(path: str) -> None:
|
|
38
|
+
return await asyncio.to_thread(os.makedirs, path, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
async def remove_file(path: str) -> None:
|
|
42
|
+
if await asyncio.to_thread(os.path.exists, path):
|
|
43
|
+
await asyncio.to_thread(os.remove, path)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
async def walk(path: str):
|
|
47
|
+
return await asyncio.to_thread(os.walk, path)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def relative_path(full_path: str, base_folder: str):
|
|
51
|
+
return os.path.relpath(full_path, base_folder)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def get_basename(path: str):
|
|
55
|
+
return os.path.basename(path)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .client.os_client import OsClient
|
|
2
|
+
from .directory_client_contract import DirectoryClientContract
|
|
3
|
+
from .types.directory_client_types import DirectoryClientArgs
|
|
4
|
+
from typing import Iterator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DirectoryClient(DirectoryClientContract):
|
|
8
|
+
CLIENTS = {"os_client"}
|
|
9
|
+
|
|
10
|
+
def __init__(self, args: DirectoryClientArgs) -> None:
|
|
11
|
+
if args.client_name not in DirectoryClient.CLIENTS:
|
|
12
|
+
msg = f"Unsupported client {args.client_name}"
|
|
13
|
+
raise KeyError(msg)
|
|
14
|
+
|
|
15
|
+
if args.client_name == "os_client":
|
|
16
|
+
self.client_obj = OsClient()
|
|
17
|
+
self.client_name = args.client_name
|
|
18
|
+
|
|
19
|
+
def join_paths(self, *paths: str) -> str:
|
|
20
|
+
return self.client_obj.join_paths(*paths)
|
|
21
|
+
|
|
22
|
+
async def path_exists(self, path: str) -> bool:
|
|
23
|
+
return await self.client_obj.path_exists(path)
|
|
24
|
+
|
|
25
|
+
async def listdir(self, path: str, more_info: bool = False) -> list:
|
|
26
|
+
return await self.client_obj.listdir(path, more_info)
|
|
27
|
+
|
|
28
|
+
async def isdir(self, path: str) -> bool:
|
|
29
|
+
return await self.client_obj.isdir(path)
|
|
30
|
+
|
|
31
|
+
async def makedirs(self, path: str) -> None:
|
|
32
|
+
return await self.client_obj.makedirs(path)
|
|
33
|
+
|
|
34
|
+
async def remove_file(self, path: str) -> None:
|
|
35
|
+
return await self.client_obj.remove_file(path)
|
|
36
|
+
|
|
37
|
+
async def walk(self, path: str) -> Iterator:
|
|
38
|
+
return await self.client_obj.walk(path)
|
|
39
|
+
|
|
40
|
+
def relative_path(self, full_path: str, base_folder: str):
|
|
41
|
+
return self.client_obj.relative_path(full_path, base_folder)
|
|
42
|
+
|
|
43
|
+
def get_basename(self, path: str):
|
|
44
|
+
return self.client_obj.get_basename(path)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Iterator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DirectoryClientContract(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def join_paths(self, *paths: str) -> str:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
async def path_exists(self, path: str) -> bool:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
async def listdir(self, path: str, more_info: bool = False) -> list:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
async def isdir(self, path) -> bool:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
async def makedirs(self, path: str) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def remove_file(self, path: str) -> None:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
async def walk(self, path: str) -> Iterator:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def relative_path(self, full_path: str, base_folder: str):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def get_basename(self, path: str):
|
|
40
|
+
pass
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import aiofiles
|
|
2
|
+
from saviialib.libs.directory_client.directory_client import (
|
|
3
|
+
DirectoryClient,
|
|
4
|
+
DirectoryClientArgs,
|
|
5
|
+
)
|
|
6
|
+
from saviialib.libs.files_client.files_client_contract import FilesClientContract
|
|
7
|
+
from saviialib.libs.files_client.types.files_client_types import (
|
|
8
|
+
FilesClientInitArgs,
|
|
9
|
+
ReadArgs,
|
|
10
|
+
WriteArgs,
|
|
11
|
+
)
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AioFilesClient(FilesClientContract):
|
|
16
|
+
def __init__(self, args: FilesClientInitArgs):
|
|
17
|
+
self.dir_client = DirectoryClient(DirectoryClientArgs(client_name="os_client"))
|
|
18
|
+
|
|
19
|
+
async def read(self, args: ReadArgs) -> str | bytes:
|
|
20
|
+
if args.mode == "json":
|
|
21
|
+
async with aiofiles.open(
|
|
22
|
+
args.file_path, "r", encoding=args.encoding or "utf-8"
|
|
23
|
+
) as file:
|
|
24
|
+
content = await file.read()
|
|
25
|
+
return json.loads(content)
|
|
26
|
+
|
|
27
|
+
encoding = None if args.mode == "rb" else args.encoding
|
|
28
|
+
async with aiofiles.open(args.file_path, args.mode, encoding=encoding) as file:
|
|
29
|
+
return await file.read()
|
|
30
|
+
|
|
31
|
+
async def write(self, args: WriteArgs) -> None:
|
|
32
|
+
file_path = (
|
|
33
|
+
self.dir_client.join_paths(args.destination_path, args.file_name)
|
|
34
|
+
if args.destination_path
|
|
35
|
+
else args.file_name
|
|
36
|
+
)
|
|
37
|
+
if args.mode == "json":
|
|
38
|
+
async with aiofiles.open(file_path, "w", encoding="utf-8") as file:
|
|
39
|
+
json_str = json.dumps(args.file_content, ensure_ascii=False, indent=2)
|
|
40
|
+
await file.write(json_str)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
async with aiofiles.open(file_path, args.mode) as file:
|
|
44
|
+
await file.write(args.file_content)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
from asyncio import to_thread
|
|
3
|
+
|
|
4
|
+
from saviialib.libs.directory_client.directory_client import (
|
|
5
|
+
DirectoryClient,
|
|
6
|
+
DirectoryClientArgs,
|
|
7
|
+
)
|
|
8
|
+
from saviialib.libs.files_client.files_client_contract import FilesClientContract
|
|
9
|
+
from saviialib.libs.files_client.types.files_client_types import (
|
|
10
|
+
FilesClientInitArgs,
|
|
11
|
+
ReadArgs,
|
|
12
|
+
WriteArgs,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CsvClient(FilesClientContract):
|
|
17
|
+
def __init__(self, args: FilesClientInitArgs):
|
|
18
|
+
self.dir_client = DirectoryClient(DirectoryClientArgs(client_name="os_client"))
|
|
19
|
+
|
|
20
|
+
async def read(self, args: ReadArgs) -> str | bytes:
|
|
21
|
+
raise OSError("This method is not implemented yet.")
|
|
22
|
+
|
|
23
|
+
async def write(self, args: WriteArgs) -> None:
|
|
24
|
+
file_type = args.file_name.split(".")[-1]
|
|
25
|
+
file_content = args.file_content
|
|
26
|
+
header = file_content[0].keys()
|
|
27
|
+
if file_type == "tsv":
|
|
28
|
+
delimiter = "\t"
|
|
29
|
+
else: # Default CSV.
|
|
30
|
+
delimiter = ","
|
|
31
|
+
|
|
32
|
+
if args.destination_path == "":
|
|
33
|
+
dest_path = self.dir_client.join_paths(args.file_name)
|
|
34
|
+
else:
|
|
35
|
+
dest_path = self.dir_client.join_paths(
|
|
36
|
+
args.destination_path, args.file_name
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
with open(dest_path, "w", newline="") as file:
|
|
40
|
+
writer = csv.DictWriter(file, fieldnames=header, delimiter=delimiter) # type: ignore
|
|
41
|
+
await to_thread(writer.writeheader)
|
|
42
|
+
await to_thread(writer.writerows, file_content) # type: ignore
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .clients.aiofiles_client import AioFilesClient
|
|
2
|
+
from .clients.csv_client import CsvClient
|
|
3
|
+
from .files_client_contract import FilesClientContract
|
|
4
|
+
from .types.files_client_types import FilesClientInitArgs, ReadArgs, WriteArgs
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FilesClient(FilesClientContract):
|
|
8
|
+
CLIENTS = {"aiofiles_client", "csv_client"}
|
|
9
|
+
|
|
10
|
+
def __init__(self, args: FilesClientInitArgs) -> None:
|
|
11
|
+
if args.client_name not in FilesClient.CLIENTS:
|
|
12
|
+
msg = f"Unsupported client {args.client_name}"
|
|
13
|
+
raise KeyError(msg)
|
|
14
|
+
|
|
15
|
+
if args.client_name == "aiofiles_client":
|
|
16
|
+
self.client_obj = AioFilesClient(args)
|
|
17
|
+
elif args.client_name == "csv_client":
|
|
18
|
+
self.client_obj = CsvClient(args)
|
|
19
|
+
|
|
20
|
+
self.client_name = args.client_name
|
|
21
|
+
|
|
22
|
+
async def read(self, args: ReadArgs):
|
|
23
|
+
return await self.client_obj.read(args)
|
|
24
|
+
|
|
25
|
+
async def write(self, args: WriteArgs):
|
|
26
|
+
return await self.client_obj.write(args)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from .types.files_client_types import ReadArgs, WriteArgs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FilesClientContract(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def read(self, args: ReadArgs) -> str | bytes:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def write(self, arg: WriteArgs) -> None:
|
|
13
|
+
pass
|