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
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Literal, Union, List, Dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class FilesClientInitArgs:
|
|
7
|
+
client_name: str = "aiofiles_client"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ReadArgs:
|
|
12
|
+
"""
|
|
13
|
+
Represents the arguments required to read a file.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
file_path (str): The path to the file to be read.
|
|
17
|
+
mode (Literal['r', 'rb']): The mode in which the file should be opened.
|
|
18
|
+
- 'r': Open for reading (text mode).
|
|
19
|
+
- 'rb': Open for reading (binary mode).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
file_path: str
|
|
23
|
+
mode: Literal["r", "rb", "json"]
|
|
24
|
+
encoding: str = "utf-8"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class WriteArgs:
|
|
29
|
+
file_name: str
|
|
30
|
+
file_content: Union[str, bytes, List[Dict]]
|
|
31
|
+
mode: Literal["w", "wb", "a", "json"]
|
|
32
|
+
destination_path: str = ""
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from aioftp import Client
|
|
2
|
+
from aioftp.errors import StatusCodeError
|
|
3
|
+
from saviialib.libs.ftp_client.ftp_client_contract import (
|
|
4
|
+
FTPClientContract,
|
|
5
|
+
)
|
|
6
|
+
from saviialib.libs.ftp_client.types.ftp_client_types import (
|
|
7
|
+
FtpClientInitArgs,
|
|
8
|
+
FtpListFilesArgs,
|
|
9
|
+
FtpReadFileArgs,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AioFTPClient(FTPClientContract):
|
|
14
|
+
def __init__(self, args: FtpClientInitArgs) -> None:
|
|
15
|
+
self.host = args.config.ftp_host
|
|
16
|
+
self.port = args.config.ftp_port
|
|
17
|
+
self.password = args.config.ftp_password
|
|
18
|
+
self.user = args.config.ftp_user
|
|
19
|
+
self.client = Client()
|
|
20
|
+
|
|
21
|
+
async def _async_start(self) -> None:
|
|
22
|
+
try:
|
|
23
|
+
await self.client.connect(host=self.host, port=self.port)
|
|
24
|
+
except OSError:
|
|
25
|
+
raise ConnectionRefusedError(
|
|
26
|
+
f"{self.host}:{self.port} isn't active. "
|
|
27
|
+
"Please ensure the server is running and accessible."
|
|
28
|
+
)
|
|
29
|
+
try:
|
|
30
|
+
await self.client.login(user=self.user, password=self.password)
|
|
31
|
+
except StatusCodeError:
|
|
32
|
+
raise ConnectionAbortedError(
|
|
33
|
+
"Authentication failed. Please verify your credentials and try again."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
async def list_files(self, args: FtpListFilesArgs) -> list[str]:
|
|
37
|
+
try:
|
|
38
|
+
await self._async_start()
|
|
39
|
+
files = []
|
|
40
|
+
async for path, info in self.client.list(args.path, recursive=False):
|
|
41
|
+
files.append((path.name, int((info.get("size", 0)))))
|
|
42
|
+
return files
|
|
43
|
+
except StatusCodeError as error:
|
|
44
|
+
raise ConnectionAbortedError(error)
|
|
45
|
+
|
|
46
|
+
async def read_file(self, args: FtpReadFileArgs) -> bytes:
|
|
47
|
+
await self._async_start()
|
|
48
|
+
try:
|
|
49
|
+
async with self.client.download_stream(args.file_path) as stream: # type: ignore
|
|
50
|
+
return await stream.read()
|
|
51
|
+
except StatusCodeError as error:
|
|
52
|
+
raise FileNotFoundError(f"File not found: {args.file_path}") from error
|
|
@@ -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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .clients.aioftp_client import AioFTPClient
|
|
2
|
+
from .clients.ftplib_client import FtplibClient
|
|
3
|
+
from .ftp_client_contract import FTPClientContract
|
|
4
|
+
from .types.ftp_client_types import FtpClientInitArgs, FtpListFilesArgs, FtpReadFileArgs
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FTPClient(FTPClientContract):
|
|
8
|
+
CLIENTS = {"aioftp_client", "ftplib_client"}
|
|
9
|
+
|
|
10
|
+
def __init__(self, args: FtpClientInitArgs) -> None:
|
|
11
|
+
if args.client_name not in FTPClient.CLIENTS:
|
|
12
|
+
msg = f"Unsupported client {args.client_name}"
|
|
13
|
+
raise KeyError(msg)
|
|
14
|
+
|
|
15
|
+
if args.client_name == "aioftp_client":
|
|
16
|
+
self.client_obj = AioFTPClient(args)
|
|
17
|
+
elif args.client_name == "ftplib_client":
|
|
18
|
+
self.client_obj = FtplibClient(args)
|
|
19
|
+
self.client_name = args.client_name
|
|
20
|
+
|
|
21
|
+
async def list_files(self, args: FtpListFilesArgs) -> list[str]:
|
|
22
|
+
return await self.client_obj.list_files(args)
|
|
23
|
+
|
|
24
|
+
async def read_file(self, args: FtpReadFileArgs) -> bytes:
|
|
25
|
+
return await self.client_obj.read_file(args)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from .types.ftp_client_types import FtpListFilesArgs, FtpReadFileArgs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FTPClientContract(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def list_files(self, args: FtpListFilesArgs) -> list[str]:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def read_file(self, args: FtpReadFileArgs) -> bytes:
|
|
13
|
+
pass
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class FtpClientInitArgs:
|
|
7
|
+
config: Any
|
|
8
|
+
client_name: str = "aioftp_client"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class FtpListFilesArgs:
|
|
13
|
+
path: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class FtpReadFileArgs:
|
|
18
|
+
file_path: str
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from saviialib.libs.log_client.log_client import LogClient
|
|
2
|
+
from .types.log_client_types import (
|
|
3
|
+
InfoArgs,
|
|
4
|
+
DebugArgs,
|
|
5
|
+
ErrorArgs,
|
|
6
|
+
WarningArgs,
|
|
7
|
+
LogClientArgs,
|
|
8
|
+
LogStatus,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"LogClient",
|
|
13
|
+
"InfoArgs",
|
|
14
|
+
"DebugArgs",
|
|
15
|
+
"ErrorArgs",
|
|
16
|
+
"WarningArgs",
|
|
17
|
+
"LogClientArgs",
|
|
18
|
+
"LogStatus",
|
|
19
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Internal modules
|
|
2
|
+
from saviialib.libs.log_client.log_client_contract import LogClientContract
|
|
3
|
+
from saviialib.libs.log_client.logging_client.logging_client import LoggingClient
|
|
4
|
+
from saviialib.libs.log_client.types.log_client_types import (
|
|
5
|
+
ErrorArgs,
|
|
6
|
+
InfoArgs,
|
|
7
|
+
LogClientArgs,
|
|
8
|
+
DebugArgs,
|
|
9
|
+
WarningArgs,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogClient(LogClientContract):
|
|
14
|
+
def __init__(self, args: LogClientArgs):
|
|
15
|
+
clients = {
|
|
16
|
+
"logging": LoggingClient(args),
|
|
17
|
+
}
|
|
18
|
+
if clients.get(args.client_name):
|
|
19
|
+
self.client_name = args.client_name
|
|
20
|
+
self.client_obj = clients[args.client_name]
|
|
21
|
+
else:
|
|
22
|
+
raise KeyError(f"Unsupported client '{args.client_name}'.")
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def method_name(self):
|
|
26
|
+
return self.client_obj.method_name
|
|
27
|
+
|
|
28
|
+
@method_name.setter
|
|
29
|
+
def method_name(self, method_name: str):
|
|
30
|
+
self.client_obj.method_name = method_name
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def log_history(self):
|
|
34
|
+
return self.client_obj.log_history
|
|
35
|
+
|
|
36
|
+
def info(self, args: InfoArgs):
|
|
37
|
+
return self.client_obj.info(args)
|
|
38
|
+
|
|
39
|
+
def error(self, args: ErrorArgs):
|
|
40
|
+
return self.client_obj.error(args)
|
|
41
|
+
|
|
42
|
+
def debug(self, args: DebugArgs):
|
|
43
|
+
return self.client_obj.debug(args)
|
|
44
|
+
|
|
45
|
+
def warning(self, args: WarningArgs):
|
|
46
|
+
return self.client_obj.warning(args)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# External libraries
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
# Internal modules
|
|
5
|
+
from saviialib.libs.log_client.types.log_client_types import (
|
|
6
|
+
ErrorArgs,
|
|
7
|
+
InfoArgs,
|
|
8
|
+
DebugArgs,
|
|
9
|
+
WarningArgs,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogClientContract(ABC):
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def info(self, args: InfoArgs):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def error(self, args: ErrorArgs):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def debug(self, args: DebugArgs):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def warning(self, args: WarningArgs):
|
|
28
|
+
pass
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# External libraries
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
# Internal modules
|
|
5
|
+
from saviialib.libs.log_client.utils.log_client_utils import format_message
|
|
6
|
+
from saviialib.libs.log_client.log_client_contract import LogClientContract
|
|
7
|
+
from saviialib.libs.log_client.types.log_client_types import (
|
|
8
|
+
ErrorArgs,
|
|
9
|
+
InfoArgs,
|
|
10
|
+
LogClientArgs,
|
|
11
|
+
DebugArgs,
|
|
12
|
+
WarningArgs,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from saviialib.libs.zero_dependency.utils.datetime_utils import today, datetime_to_str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LoggingClient(LogClientContract):
|
|
19
|
+
def __init__(self, args: LogClientArgs):
|
|
20
|
+
log_format = "{message}"
|
|
21
|
+
logging.basicConfig(format=log_format, level=logging.INFO, style="{")
|
|
22
|
+
self.class_name = args.class_name
|
|
23
|
+
self.method_name = args.method_name
|
|
24
|
+
self.active_record = args.active_record
|
|
25
|
+
self.log_history = []
|
|
26
|
+
|
|
27
|
+
def _save_to_history(self, meta: dict):
|
|
28
|
+
self.log_history.append(
|
|
29
|
+
f"[{datetime_to_str(today(), date_format='%m-%d-%Y %H:%M:%S')}][{self.class_name}] {meta.get('msg', '')}"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def info(self, args: InfoArgs) -> None:
|
|
33
|
+
if self.active_record:
|
|
34
|
+
self._save_to_history(args.metadata)
|
|
35
|
+
return logging.info(
|
|
36
|
+
format_message(self.class_name, self.method_name, args.status)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def error(self, args: ErrorArgs) -> None:
|
|
40
|
+
if self.active_record:
|
|
41
|
+
self._save_to_history(args.metadata)
|
|
42
|
+
return logging.error(
|
|
43
|
+
format_message(self.class_name, self.method_name, args.status)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def debug(self, args: DebugArgs) -> None:
|
|
47
|
+
if self.active_record:
|
|
48
|
+
self._save_to_history(args.metadata)
|
|
49
|
+
return logging.debug(
|
|
50
|
+
format_message(self.class_name, self.method_name, args.status)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def warning(self, args: WarningArgs) -> None:
|
|
54
|
+
if self.active_record:
|
|
55
|
+
self._save_to_history(args.metadata)
|
|
56
|
+
return logging.warning(
|
|
57
|
+
format_message(self.class_name, self.method_name, args.status)
|
|
58
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Literal, Dict
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LogStatus(Enum):
|
|
7
|
+
STARTED = "started"
|
|
8
|
+
SUCCESSFUL = "successful"
|
|
9
|
+
FAILED = "failed"
|
|
10
|
+
ERROR = "error"
|
|
11
|
+
EARLY_RETURN = "early_return"
|
|
12
|
+
ALERT = "alert"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class LogClientArgs:
|
|
17
|
+
client_name: str = field(default="logging")
|
|
18
|
+
service_name: str = field(default="")
|
|
19
|
+
class_name: str = field(default="")
|
|
20
|
+
method_name: str = field(default="")
|
|
21
|
+
active_record: bool = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class InfoArgs:
|
|
26
|
+
status: Literal[LogStatus.STARTED, LogStatus.SUCCESSFUL, LogStatus.EARLY_RETURN]
|
|
27
|
+
metadata: Dict = field(default_factory=dict)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class DebugArgs:
|
|
32
|
+
status: Literal[
|
|
33
|
+
LogStatus.STARTED, LogStatus.SUCCESSFUL, LogStatus.EARLY_RETURN, LogStatus.ALERT
|
|
34
|
+
]
|
|
35
|
+
metadata: Dict = field(default_factory=dict)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ErrorArgs:
|
|
40
|
+
status: Literal[LogStatus.ERROR]
|
|
41
|
+
metadata: Dict = field(default_factory=dict)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class WarningArgs:
|
|
46
|
+
status: Literal[LogStatus.FAILED, LogStatus.ALERT]
|
|
47
|
+
metadata: Dict = field(default_factory=dict)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from saviialib.libs.sftp_client.sftp_client_contract import SFTPClientContract
|
|
2
|
+
from saviialib.libs.sftp_client.types.sftp_client_types import (
|
|
3
|
+
SFTPClientInitArgs,
|
|
4
|
+
ListfilesArgs,
|
|
5
|
+
DownloadfilesArgs,
|
|
6
|
+
)
|
|
7
|
+
from typing import Optional, List, Tuple
|
|
8
|
+
from saviialib.general_types.error_types.common.common_types import EmptyDataError
|
|
9
|
+
import asyncssh # type: ignore
|
|
10
|
+
from saviialib.libs.directory_client import DirectoryClient, DirectoryClientArgs
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AsyncsshSFTPClient(SFTPClientContract):
|
|
14
|
+
def __init__(self, args: SFTPClientInitArgs) -> None:
|
|
15
|
+
self.host: str = args.host
|
|
16
|
+
self.port: int = args.port
|
|
17
|
+
self.username: str = args.username
|
|
18
|
+
self.password: Optional[str] = args.password
|
|
19
|
+
self.ssh_key_path: Optional[str] = args.ssh_key_path
|
|
20
|
+
self.dir_client = DirectoryClient(DirectoryClientArgs("os_client"))
|
|
21
|
+
self._validate_credentials()
|
|
22
|
+
|
|
23
|
+
def _validate_credentials(self):
|
|
24
|
+
if not self.password and not self.ssh_key_path:
|
|
25
|
+
raise EmptyDataError(
|
|
26
|
+
reason="At least one attribute (ssh key or password) must be provided"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def _start_connection(
|
|
30
|
+
self,
|
|
31
|
+
) -> Tuple[asyncssh.SSHClientConnection, asyncssh.SFTPClient]:
|
|
32
|
+
try:
|
|
33
|
+
ssh_connection = await asyncssh.connect(
|
|
34
|
+
self.host,
|
|
35
|
+
username=self.username,
|
|
36
|
+
port=self.port,
|
|
37
|
+
client_keys=[self.ssh_key_path],
|
|
38
|
+
password=self.password,
|
|
39
|
+
)
|
|
40
|
+
sftp_client = await ssh_connection.start_sftp_client()
|
|
41
|
+
return ssh_connection, sftp_client
|
|
42
|
+
except (OSError, asyncssh.Error) as exc:
|
|
43
|
+
raise ConnectionError("SFTP Operation failed: " + str(exc))
|
|
44
|
+
|
|
45
|
+
async def list_files(self, args: ListfilesArgs) -> List[str]:
|
|
46
|
+
ssh_conn, sftp_client = await self._start_connection()
|
|
47
|
+
async with ssh_conn:
|
|
48
|
+
async with sftp_client:
|
|
49
|
+
files = await sftp_client.listdir(args.path)
|
|
50
|
+
files = [f for f in files if f not in (".", "..")]
|
|
51
|
+
return files
|
|
52
|
+
await self._end_connection(ssh_conn)
|
|
53
|
+
|
|
54
|
+
async def download_files(self, args: DownloadfilesArgs) -> None:
|
|
55
|
+
ssh_conn, sftp_client = await self._start_connection()
|
|
56
|
+
if not args.destination_path or not args.source_path:
|
|
57
|
+
conflict_path = (
|
|
58
|
+
"destination path" if not args.destination_path else "source path"
|
|
59
|
+
)
|
|
60
|
+
raise ConnectionError(f"The {conflict_path} must be provided.")
|
|
61
|
+
|
|
62
|
+
download_all = len(args.files_to_download) == 0
|
|
63
|
+
async with ssh_conn:
|
|
64
|
+
async with sftp_client:
|
|
65
|
+
if download_all:
|
|
66
|
+
await sftp_client.get(
|
|
67
|
+
args.source_path,
|
|
68
|
+
args.destination_path,
|
|
69
|
+
recurse=True,
|
|
70
|
+
preserve=True,
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
for filename in args.files_to_download:
|
|
74
|
+
source_path = self.dir_client.join_paths(
|
|
75
|
+
args.source_path, filename
|
|
76
|
+
)
|
|
77
|
+
dest_path = self.dir_client.join_paths(
|
|
78
|
+
args.destination_path, filename
|
|
79
|
+
)
|
|
80
|
+
await sftp_client.get(source_path, dest_path)
|
|
81
|
+
|
|
82
|
+
async def _end_connection(self, connection) -> None:
|
|
83
|
+
await connection.wait_closed()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .sftp_client_contract import SFTPClientContract
|
|
2
|
+
from .types.sftp_client_types import (
|
|
3
|
+
SFTPClientInitArgs,
|
|
4
|
+
ListfilesArgs,
|
|
5
|
+
DownloadfilesArgs,
|
|
6
|
+
)
|
|
7
|
+
from .clients.asyncssh_sftp_client import AsyncsshSFTPClient
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SFTPClient(SFTPClientContract):
|
|
12
|
+
CLIENTS = {"asyncssh_sftp"}
|
|
13
|
+
|
|
14
|
+
def __init__(self, args: SFTPClientInitArgs) -> None:
|
|
15
|
+
if args.client_name not in SFTPClient.CLIENTS:
|
|
16
|
+
msg = f"Unsupported client {args.client_name}"
|
|
17
|
+
raise KeyError(msg)
|
|
18
|
+
if args.client_name == "asyncssh_sftp":
|
|
19
|
+
self.client_obj = AsyncsshSFTPClient(args)
|
|
20
|
+
self.client_name = args.client_name
|
|
21
|
+
|
|
22
|
+
async def list_files(self, args: ListfilesArgs) -> List[str]:
|
|
23
|
+
return await self.client_obj.list_files(args)
|
|
24
|
+
|
|
25
|
+
async def download_files(self, args: DownloadfilesArgs) -> None:
|
|
26
|
+
return await self.client_obj.download_files(args)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .types.sftp_client_types import ListfilesArgs, DownloadfilesArgs
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SFTPClientContract(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def list_files(self, args: ListfilesArgs) -> List[str]:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def download_files(self, args: DownloadfilesArgs) -> None:
|
|
13
|
+
pass
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class SFTPClientInitArgs:
|
|
7
|
+
client_name: str
|
|
8
|
+
password: Optional[str]
|
|
9
|
+
username: str
|
|
10
|
+
ssh_key_path: str
|
|
11
|
+
host: str = "localhost"
|
|
12
|
+
port: int = 22
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ListfilesArgs:
|
|
17
|
+
path: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class DownloadfilesArgs:
|
|
22
|
+
source_path: str
|
|
23
|
+
destination_path: str
|
|
24
|
+
files_to_download: List[str] = field(default_factory=list)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .sharepoint_client import SharepointClient
|
|
2
|
+
from .types.sharepoint_client_types import (
|
|
3
|
+
SharepointClientInitArgs,
|
|
4
|
+
SpListFilesArgs,
|
|
5
|
+
SpListFoldersArgs,
|
|
6
|
+
SpUploadFileArgs,
|
|
7
|
+
SpCreateFolderArgs,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"SharepointClientInitArgs",
|
|
12
|
+
"SharepointClient",
|
|
13
|
+
"SpListFilesArgs",
|
|
14
|
+
"SpListFoldersArgs",
|
|
15
|
+
"SpUploadFileArgs",
|
|
16
|
+
"SpCreateFolderArgs",
|
|
17
|
+
]
|