rcer-iot-client-pkg 0.3.1__tar.gz → 0.5.0__tar.gz

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.
Files changed (57) hide show
  1. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/PKG-INFO +34 -1
  2. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/README.md +33 -0
  3. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/pyproject.toml +1 -1
  4. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/general_types/api/update_thies_data_types.py +29 -0
  5. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py +30 -0
  6. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/general_types/error_types/common/__init__.py +7 -0
  7. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/general_types/error_types/common/common_types.py +17 -0
  8. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/ftp_client/__init__.py +4 -0
  9. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/ftp_client/clients/aioftp_client.py +20 -12
  10. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/ftp_client/ftp_client.py +3 -3
  11. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/ftp_client/ftp_client_contract.py +13 -0
  12. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/ftp_client/types/__init__.py +3 -0
  13. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/ftp_client/types/ftp_client_types.py +4 -6
  14. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/sharepoint_client/__init__.py +15 -0
  15. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py +134 -0
  16. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client.py +34 -0
  17. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client_contract.py +21 -0
  18. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/libs/sharepoint_client/types/sharepoint_client_types.py +25 -0
  19. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/services/epii/api.py +24 -0
  20. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/services/epii/constants/update_thies_data_constants.py +5 -0
  21. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/controllers/types/update_thies_data_types.py +2 -4
  22. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py +40 -9
  23. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/services/epii/use_cases/types/__init__.py +7 -0
  24. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/services/epii/use_cases/types/update_thies_data_types.py +32 -0
  25. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/use_cases/update_thies_data.py +60 -62
  26. rcer_iot_client_pkg-0.5.0/src/rcer_iot_client_pkg/services/epii/utils/__init__.py +3 -0
  27. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/utils/update_thies_data_utils.py +1 -1
  28. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/general_types/api/update_thies_data_types.py +0 -0
  29. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py +0 -19
  30. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/general_types/error_types/common/__init__.py +0 -7
  31. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/general_types/error_types/common/common_types.py +0 -13
  32. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/libs/ftp_client/__init__.py +0 -4
  33. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/libs/ftp_client/ftp_client_contract.py +0 -13
  34. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/libs/ftp_client/types/__init__.py +0 -3
  35. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/services/epii/api.py +0 -24
  36. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/services/epii/use_cases/constants.py +0 -4
  37. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/services/epii/use_cases/types/__init__.py +0 -3
  38. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/services/epii/use_cases/types/update_thies_data_types.py +0 -17
  39. rcer_iot_client_pkg-0.3.1/src/rcer_iot_client_pkg/services/epii/utils/__init__.py +0 -3
  40. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/LICENSE +0 -0
  41. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/__init__.py +0 -0
  42. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/general_types/__init__.py +0 -0
  43. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/general_types/api/__init__.py +0 -0
  44. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/general_types/error_types/__init__.py +0 -0
  45. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/general_types/error_types/api/__init__.py +0 -0
  46. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/__init__.py +0 -0
  47. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/async_http_client.py +0 -0
  48. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/async_http_client_contract.py +0 -0
  49. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/clients/__init__.py +0 -0
  50. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/clients/aiohttp_client.py +0 -0
  51. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/types/__init__.py +0 -0
  52. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/async_http_client/types/async_http_client_types.py +0 -0
  53. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/ftp_client/clients/__init__.py +0 -0
  54. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/libs/zero_dependency/utils/datetime_utils.py +0 -0
  55. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/__init__.py +0 -0
  56. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/controllers/__init__.py +0 -0
  57. {rcer_iot_client_pkg-0.3.1 → rcer_iot_client_pkg-0.5.0}/src/rcer_iot_client_pkg/services/epii/controllers/types/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rcer_iot_client_pkg
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: A client library for IoT projects in the RCER initiative
5
5
  License: MIT
6
6
  Author: pedropablozavalat
@@ -30,6 +30,39 @@ This library provides a robust and efficient client for interacting with IoT dev
30
30
  pip install rcer_iot_client_pkg
31
31
  ```
32
32
 
33
+ ## Usage
34
+
35
+ ### Initialize the EPii API Client
36
+ To start using the library, you need to create an `EpiiAPI` client instance:
37
+
38
+ ```python
39
+ from rcer_iot_client_pkg import EpiiAPI
40
+
41
+ api_client = EpiiAPI()
42
+ ```
43
+
44
+ ### Update THIES Data Logger Files
45
+ The library provides a method to synchronize THIES Data Logger files with the RCER SharePoint client. This method updates the folder containing binary files with meteorological data:
46
+
47
+ ```python
48
+ import asyncio
49
+
50
+ async def update_thies_data():
51
+ response = await api_client.update_thies_data(
52
+ ftp_port=PORT,
53
+ ftp_host=LOCAL_HOST,
54
+ ftp_password=PASSWORD,
55
+ ftp_user=USER
56
+ )
57
+ return response
58
+
59
+ asyncio.run(update_thies_data())
60
+ ```
61
+
62
+ **Notes:**
63
+ - Store sensitive data like `PASSWORD` and `USER` securely, e.g., in environment variables or a secrets file.
64
+ - Ensure `asyncio` is installed to run concurrent code with `EpiiAPI` methods.
65
+
33
66
  ## Development
34
67
 
35
68
  This project includes a `Makefile` to simplify common tasks. Below are the available commands:
@@ -9,6 +9,39 @@ This library provides a robust and efficient client for interacting with IoT dev
9
9
  pip install rcer_iot_client_pkg
10
10
  ```
11
11
 
12
+ ## Usage
13
+
14
+ ### Initialize the EPii API Client
15
+ To start using the library, you need to create an `EpiiAPI` client instance:
16
+
17
+ ```python
18
+ from rcer_iot_client_pkg import EpiiAPI
19
+
20
+ api_client = EpiiAPI()
21
+ ```
22
+
23
+ ### Update THIES Data Logger Files
24
+ The library provides a method to synchronize THIES Data Logger files with the RCER SharePoint client. This method updates the folder containing binary files with meteorological data:
25
+
26
+ ```python
27
+ import asyncio
28
+
29
+ async def update_thies_data():
30
+ response = await api_client.update_thies_data(
31
+ ftp_port=PORT,
32
+ ftp_host=LOCAL_HOST,
33
+ ftp_password=PASSWORD,
34
+ ftp_user=USER
35
+ )
36
+ return response
37
+
38
+ asyncio.run(update_thies_data())
39
+ ```
40
+
41
+ **Notes:**
42
+ - Store sensitive data like `PASSWORD` and `USER` securely, e.g., in environment variables or a secrets file.
43
+ - Ensure `asyncio` is installed to run concurrent code with `EpiiAPI` methods.
44
+
12
45
  ## Development
13
46
 
14
47
  This project includes a `Makefile` to simplify common tasks. Below are the available commands:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rcer_iot_client_pkg"
3
- version = "0.3.1"
3
+ version = "0.5.0"
4
4
  description = "A client library for IoT projects in the RCER initiative"
5
5
  authors = ["pedropablozavalat"]
6
6
  license = "MIT"
@@ -0,0 +1,29 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class EpiiAPIConfig:
6
+ """
7
+ Configuration for Epii API.
8
+
9
+ Attributes:
10
+ ftp_port (int): Port number of the FTP server.
11
+ ftp_host (str): Hostname or IP address of the FTP server.
12
+ ftp_user (str): Username for the FTP server.
13
+ ftp_password (str): Password for the FTP server.
14
+ sharepoint_client_id (str): Client ID for SharePoint authentication.
15
+ sharepoint_client_secret (str): Client secret for SharePoint authentication.
16
+ sharepoint_tenant_id (str): Tenant ID for SharePoint authentication.
17
+ sharepoint_tenant_name (str): Tenant name for SharePoint.
18
+ sharepoint_site_name (str): Site name in SharePoint.
19
+ """
20
+
21
+ ftp_port: int
22
+ ftp_host: str
23
+ ftp_user: str
24
+ ftp_password: str
25
+ sharepoint_client_id: str
26
+ sharepoint_client_secret: str
27
+ sharepoint_tenant_id: str
28
+ sharepoint_tenant_name: str
29
+ sharepoint_site_name: str
@@ -0,0 +1,30 @@
1
+ class ThiesConnectionError(Exception):
2
+ """Raised when unable to connect to the THIES FTP Server"""
3
+
4
+ def __init__(self, *args, reason):
5
+ super().__init__(*args, reason)
6
+ self.reason = reason
7
+
8
+ def __str__(self):
9
+ return "Unable to connect to THIES FTP Server. " + self.reason.__str__()
10
+
11
+
12
+ class ThiesFetchingError(Exception):
13
+ """Raised when no files are found to upload to the server."""
14
+
15
+ def __init__(self, *args, reason):
16
+ super().__init__(*args, reason)
17
+ self.reason = reason
18
+
19
+ def __str__(self):
20
+ return (
21
+ "An error ocurred while retrieving files from THIES FTP Server. "
22
+ + self.reason.__str__()
23
+ )
24
+
25
+
26
+ class SharePointFetchingError(Exception):
27
+ """Raised when there is an error fetching file names from the RCER cloud."""
28
+
29
+ def __str__(self):
30
+ return "An error occurred while retrieving file names from the RCER cloud"
@@ -0,0 +1,7 @@
1
+ from .common_types import (
2
+ EmptyDataError,
3
+ FtpClientError,
4
+ SharepointClientError,
5
+ )
6
+
7
+ __all__ = ["EmptyDataError", "SharepointClientError", "FtpClientError"]
@@ -0,0 +1,17 @@
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."
@@ -0,0 +1,4 @@
1
+ from .ftp_client import FTPClient
2
+ from .types import FtpClientInitArgs, FtpListFilesArgs, FtpReadFileArgs
3
+
4
+ __all__ = ["FTPClient", "FtpClientInitArgs", "FtpListFilesArgs", "FtpReadFileArgs"]
@@ -1,37 +1,45 @@
1
1
  from aioftp import Client
2
-
2
+ from aioftp.errors import StatusCodeError
3
3
  from rcer_iot_client_pkg.libs.ftp_client.ftp_client_contract import (
4
4
  FTPClientContract,
5
5
  )
6
6
  from rcer_iot_client_pkg.libs.ftp_client.types.ftp_client_types import (
7
7
  FtpClientInitArgs,
8
- ListFilesArgs,
9
- ReadFileArgs,
8
+ FtpListFilesArgs,
9
+ FtpReadFileArgs,
10
10
  )
11
11
 
12
12
 
13
13
  class AioFTPClient(FTPClientContract):
14
14
  def __init__(self, args: FtpClientInitArgs) -> None:
15
- self.host = args.host
16
- self.port = args.port
17
- self.password = args.password
18
- self.user = args.user
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
19
  self.client = Client()
20
20
 
21
21
  async def _async_start(self) -> None:
22
- try:
22
+ try:
23
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:
24
30
  await self.client.login(user=self.user, password=self.password)
25
- except Exception:
26
- raise RuntimeError("Unexpected error occurred while trying to connect to the FTP server")
31
+ except StatusCodeError:
32
+ raise ConnectionAbortedError(
33
+ "Authentication failed. Please verify your credentials and try again."
34
+ )
27
35
 
28
- async def list_files(self, args: ListFilesArgs) -> list[str]:
36
+ async def list_files(self, args: FtpListFilesArgs) -> list[str]:
29
37
  await self._async_start()
30
38
  return [
31
39
  path.name async for path, _ in self.client.list(args.path, recursive=False)
32
40
  ]
33
41
 
34
- async def read_file(self, args: ReadFileArgs) -> bytes:
42
+ async def read_file(self, args: FtpReadFileArgs) -> bytes:
35
43
  await self._async_start()
36
44
  async with self.client.download_stream(args.file_path) as stream:
37
45
  return await stream.read()
@@ -1,6 +1,6 @@
1
1
  from .clients.aioftp_client import AioFTPClient
2
2
  from .ftp_client_contract import FTPClientContract
3
- from .types.ftp_client_types import FtpClientInitArgs, ListFilesArgs, ReadFileArgs
3
+ from .types.ftp_client_types import FtpClientInitArgs, FtpListFilesArgs, FtpReadFileArgs
4
4
 
5
5
 
6
6
  class FTPClient(FTPClientContract):
@@ -15,8 +15,8 @@ class FTPClient(FTPClientContract):
15
15
  self.client_obj = AioFTPClient(args)
16
16
  self.client_name = args.client_name
17
17
 
18
- def list_files(self, args: ListFilesArgs) -> list[str]:
18
+ def list_files(self, args: FtpListFilesArgs) -> list[str]:
19
19
  return self.client_obj.list_files(args)
20
20
 
21
- def read_file(self, args: ReadFileArgs) -> bytes:
21
+ def read_file(self, args: FtpReadFileArgs) -> bytes:
22
22
  return 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
+ def list_files(self, args: FtpListFilesArgs) -> list[str]:
9
+ pass
10
+
11
+ @abstractmethod
12
+ def read_file(self, args: FtpReadFileArgs) -> bytes:
13
+ pass
@@ -0,0 +1,3 @@
1
+ from .ftp_client_types import FtpClientInitArgs, FtpListFilesArgs, FtpReadFileArgs
2
+
3
+ __all__ = ["FtpClientInitArgs", "FtpListFilesArgs", "FtpReadFileArgs"]
@@ -1,20 +1,18 @@
1
1
  from dataclasses import dataclass
2
+ from typing import Any
2
3
 
3
4
 
4
5
  @dataclass
5
6
  class FtpClientInitArgs:
6
- host: str
7
- user: str
8
- password: str
7
+ config: Any
9
8
  client_name: str = "aioftp_client"
10
- port: int = 21
11
9
 
12
10
 
13
11
  @dataclass
14
- class ListFilesArgs:
12
+ class FtpListFilesArgs:
15
13
  path: str
16
14
 
17
15
 
18
16
  @dataclass
19
- class ReadFileArgs:
17
+ class FtpReadFileArgs:
20
18
  file_path: str
@@ -0,0 +1,15 @@
1
+ from .sharepoint_client import SharepointClient
2
+ from .types.sharepoint_client_types import (
3
+ SharepointClientInitArgs,
4
+ SpListFilesArgs,
5
+ SpListFoldersArgs,
6
+ SpUploadFileArgs,
7
+ )
8
+
9
+ __all__ = [
10
+ "SharepointClientInitArgs",
11
+ "SharepointClient",
12
+ "SpListFilesArgs",
13
+ "SpListFoldersArgs",
14
+ "SpUploadFileArgs",
15
+ ]
@@ -0,0 +1,134 @@
1
+ import os
2
+ from typing import Any
3
+
4
+ from aiohttp import ClientError, ClientSession
5
+ from dotenv import load_dotenv
6
+
7
+ from rcer_iot_client_pkg.libs.sharepoint_client.sharepoint_client_contract import (
8
+ SharepointClientContract,
9
+ )
10
+ from rcer_iot_client_pkg.libs.sharepoint_client.types.sharepoint_client_types import (
11
+ SpListFilesArgs,
12
+ SpListFoldersArgs,
13
+ SpUploadFileArgs,
14
+ SharepointClientInitArgs,
15
+ )
16
+
17
+ load_dotenv()
18
+
19
+
20
+ class SharepointRestAPI(SharepointClientContract):
21
+ def __init__(self, args: SharepointClientInitArgs):
22
+ self.session: ClientSession | None = None
23
+ self.base_headers = {}
24
+ self.credentials = {}
25
+ self.base_url = ""
26
+ self.tenant_id = args.config.sharepoint_tenant_id
27
+ self.tenant_name = args.config.sharepoint_tenant_name
28
+ self.client_secret = args.config.sharepoint_client_secret
29
+ self.client_id = args.config.sharepoint_client_id
30
+ self.site_name = args.config.sharepoint_site_name
31
+
32
+ async def _load_form_digest_value(self) -> str:
33
+ try:
34
+ response = await self.session.post("contextinfo")
35
+ response_json = await response.json()
36
+ return response_json["FormDigestValue"]
37
+ except ClientError as error:
38
+ raise ConnectionError(error) from error
39
+
40
+ async def _load_credentials(self) -> dict:
41
+ resource_base = "00000003-0000-0ff1-ce00-000000000000"
42
+ resource = f"{resource_base}/{self.tenant_name}.sharepoint.com@{self.tenant_id}"
43
+ url = f"https://accounts.accesscontrol.windows.net/{self.tenant_id}/tokens/OAuth/2"
44
+ payload = {
45
+ "grant_type": "client_credentials",
46
+ "client_id": f"{self.client_id}@{self.tenant_id}",
47
+ "client_secret": self.client_secret,
48
+ "resource": resource,
49
+ }
50
+ headers = {
51
+ "Content-Type": "application/x-www-form-urlencoded",
52
+ }
53
+
54
+ async with ClientSession() as session:
55
+ # Load access token
56
+ response = await session.post(url, data=payload, headers=headers)
57
+ if response.status != 200:
58
+ raise ClientError(
59
+ f"Failed to fetch credentials: {response.status}, {await response.text()}"
60
+ )
61
+ response_json = await response.json()
62
+
63
+ return {
64
+ "access_token": response_json["access_token"],
65
+ }
66
+
67
+ async def __aenter__(self) -> "SharepointRestAPI":
68
+ self.credentials = await self._load_credentials()
69
+ site_url = f"https://{self.tenant_name}.sharepoint.com"
70
+
71
+ self.base_headers = {
72
+ "Authorization": f"Bearer {self.credentials['access_token']}",
73
+ "Accept": "application/json",
74
+ "Content-Type": "application/json",
75
+ }
76
+ self.base_url = f"{site_url}/sites/{self.site_name}/_api/"
77
+ self.session = ClientSession(headers=self.base_headers, base_url=self.base_url)
78
+ return self
79
+
80
+ async def __aexit__(
81
+ self, _exc_type: type[BaseException], _exc_val: BaseException, _exc_tb: Any
82
+ ) -> None:
83
+ await self.session.close()
84
+
85
+ async def list_files(self, args: SpListFilesArgs) -> list:
86
+ try:
87
+ folder_relative_url = (
88
+ f"GetFolderByServerRelativeUrl('{args.folder_relative_url}')"
89
+ )
90
+ endpoint = f"web/{folder_relative_url}/Files"
91
+ response = await self.session.get(endpoint.lstrip("/"))
92
+ response.raise_for_status()
93
+ response_json = await response.json()
94
+ return response_json
95
+ except ClientError as error:
96
+ raise ConnectionError(error) from error
97
+
98
+ async def list_folders(self, args: SpListFoldersArgs) -> list:
99
+ try:
100
+ folder_relative_url = (
101
+ f"GetFolderByServerRelativeUrl('{args.folder_relative_url}')"
102
+ )
103
+ endpoint = f"web/{folder_relative_url}/Folder"
104
+ response = await self.session.get(endpoint.lstrip("/"))
105
+ response.raise_for_status()
106
+ return await response.json()
107
+ except ClientError as error:
108
+ raise ConnectionError(error) from error
109
+
110
+ async def upload_file(self, args: SpUploadFileArgs) -> dict:
111
+ try:
112
+ # Load form digest value
113
+ form_digest_value = await self._load_form_digest_value()
114
+ headers = {
115
+ **self.base_headers,
116
+ "X-RequestDigest": form_digest_value,
117
+ "Content-Type": "application/octet-stream",
118
+ }
119
+ # Upload the file in the requested folder
120
+ folder_relative_url = (
121
+ f"GetFolderByServerRelativeUrl('{args.folder_relative_url}')"
122
+ )
123
+ # Read the file
124
+ source_file_path = os.path.basename(args.file_path)
125
+ with open(source_file_path, "rb") as file:
126
+ data = file.read()
127
+
128
+ endpoint = f"web/{folder_relative_url}/Files/add(url='{source_file_path}',overwrite=false)"
129
+ response = await self.session.post(endpoint, data=data, headers=headers)
130
+
131
+ response.raise_for_status()
132
+ return await response.json()
133
+ except ClientError as error:
134
+ raise ConnectionError(error) from error
@@ -0,0 +1,34 @@
1
+ from .clients.sharepoint_rest_api import SharepointRestAPI
2
+ from .sharepoint_client_contract import SharepointClientContract
3
+ from .types.sharepoint_client_types import (
4
+ SharepointClientInitArgs,
5
+ SpListFilesArgs,
6
+ SpListFoldersArgs,
7
+ SpUploadFileArgs,
8
+ )
9
+
10
+
11
+ class SharepointClient(SharepointClientContract):
12
+ CLIENTS = {"sharepoint_rest_api"}
13
+
14
+ def __init__(self, args: SharepointClientInitArgs):
15
+ if args.client_name not in SharepointClient.CLIENTS:
16
+ msg = f"Unsupported client {args.client_name}"
17
+ raise KeyError(msg)
18
+ elif args.client_name == "sharepoint_rest_api":
19
+ self.client_obj = SharepointRestAPI(args)
20
+
21
+ async def __aenter__(self):
22
+ return await self.client_obj.__aenter__()
23
+
24
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
25
+ await self.client_obj.__aexit__(exc_type, exc_val, exc_tb)
26
+
27
+ async def list_files(self, args: SpListFilesArgs) -> list:
28
+ return await self.client_obj.list_files(args)
29
+
30
+ async def list_folders(self, args: SpListFoldersArgs) -> list:
31
+ return self.client_obj.list_files(args)
32
+
33
+ async def upload_file(self, args: SpUploadFileArgs) -> dict:
34
+ return self.client_obj.upload_file(args)
@@ -0,0 +1,21 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from .types.sharepoint_client_types import (
4
+ SpListFilesArgs,
5
+ SpListFoldersArgs,
6
+ SpUploadFileArgs,
7
+ )
8
+
9
+
10
+ class SharepointClientContract(ABC):
11
+ @abstractmethod
12
+ async def list_files(self, args: SpListFilesArgs) -> list:
13
+ pass
14
+
15
+ @abstractmethod
16
+ async def list_folders(self, args: SpListFoldersArgs) -> list:
17
+ pass
18
+
19
+ @abstractmethod
20
+ async def upload_file(self, args: SpUploadFileArgs) -> dict:
21
+ pass
@@ -0,0 +1,25 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any
3
+
4
+
5
+ @dataclass
6
+ class SharepointClientInitArgs:
7
+ config: Any
8
+ client_name: str = "sharepoint_rest_api"
9
+
10
+
11
+ @dataclass
12
+ class SpListFilesArgs:
13
+ folder_relative_url: str
14
+
15
+
16
+ @dataclass
17
+ class SpListFoldersArgs:
18
+ folder_relative_url: str
19
+
20
+
21
+ @dataclass
22
+ class SpUploadFileArgs:
23
+ file_path: str
24
+ folder_relative_url: str
25
+ file_content: bytes = bytes()
@@ -0,0 +1,24 @@
1
+ from typing import Any, Dict
2
+
3
+ from .controllers.types.update_thies_data_types import UpdateThiesDataControllerInput
4
+ from .controllers.update_thies_data import UpdateThiesDataController
5
+ from rcer_iot_client_pkg.general_types.api.update_thies_data_types import EpiiAPIConfig
6
+
7
+
8
+ class EpiiAPI:
9
+ """
10
+ EpiiAPI is a service class that provides methods to interact with Patagonia Center system.
11
+ """
12
+
13
+ async def update_thies_data(self, config: EpiiAPIConfig) -> Dict[str, Any]:
14
+ """
15
+ This method establishes a connection to an FTP server using the provided
16
+ credentials and updates data related to THIES Data Logger.
17
+ Args:
18
+ config (EpiiAPIConfig): configuration class for FTP Server and Microsoft SharePoint credentials.
19
+ Returns:
20
+ response (dict): A dictionary representation of the API response.
21
+ """
22
+ controller = UpdateThiesDataController(UpdateThiesDataControllerInput(config))
23
+ response = await controller.execute()
24
+ return response.__dict__
@@ -0,0 +1,5 @@
1
+ SHAREPOINT_BASE_URL = "/sites/uc365_CentrosyEstacionesRegionalesUC/Shared Documents/General/Test_Raspberry/THIES"
2
+ SHAREPOINT_THIES_FOLDERS = ["AVG", "EXT"]
3
+
4
+ FTP_SERVER_PATH_AVG_FILES = "ftp/thies/BINFILES/ARCH_AV1"
5
+ FTP_SERVER_PATH_EXT_FILES = "ftp/thies/BINFILES/ARCH_EX1"
@@ -1,13 +1,11 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import Dict
3
+ from rcer_iot_client_pkg.general_types.api.update_thies_data_types import EpiiAPIConfig
3
4
 
4
5
 
5
6
  @dataclass
6
7
  class UpdateThiesDataControllerInput:
7
- ftp_host: str
8
- ftp_port: str
9
- ftp_user: str
10
- ftp_password: str
8
+ config: EpiiAPIConfig
11
9
 
12
10
 
13
11
  @dataclass
@@ -1,12 +1,14 @@
1
1
  from http import HTTPStatus
2
2
 
3
3
  from rcer_iot_client_pkg.general_types.error_types.api.update_thies_data_error_types import (
4
- FetchCloudFileNamesError,
5
- ThiesUploadEmptyError,
4
+ SharePointFetchingError,
5
+ ThiesConnectionError,
6
+ ThiesFetchingError,
6
7
  )
7
8
  from rcer_iot_client_pkg.general_types.error_types.common.common_types import (
9
+ EmptyDataError,
8
10
  FtpClientError,
9
- HttpClientError,
11
+ SharepointClientError,
10
12
  )
11
13
  from rcer_iot_client_pkg.services.epii.controllers.types.update_thies_data_types import (
12
14
  UpdateThiesDataControllerInput,
@@ -14,6 +16,8 @@ from rcer_iot_client_pkg.services.epii.controllers.types.update_thies_data_types
14
16
  )
15
17
  from rcer_iot_client_pkg.services.epii.use_cases.types import (
16
18
  UpdateThiesDataUseCaseInput,
19
+ SharepointConfig,
20
+ FtpClientConfig,
17
21
  )
18
22
  from rcer_iot_client_pkg.services.epii.use_cases.update_thies_data import (
19
23
  UpdateThiesDataUseCase,
@@ -23,7 +27,21 @@ from rcer_iot_client_pkg.services.epii.use_cases.update_thies_data import (
23
27
  class UpdateThiesDataController:
24
28
  def __init__(self, input: UpdateThiesDataControllerInput):
25
29
  self.use_case = UpdateThiesDataUseCase(
26
- UpdateThiesDataUseCaseInput(**input.__dict__)
30
+ UpdateThiesDataUseCaseInput(
31
+ ftp_config=FtpClientConfig(
32
+ ftp_host=input.config.ftp_host,
33
+ ftp_password=input.config.ftp_password,
34
+ ftp_port=input.config.ftp_port,
35
+ ftp_user=input.config.ftp_user,
36
+ ),
37
+ sharepoint_config=SharepointConfig(
38
+ sharepoint_client_id=input.config.sharepoint_client_id,
39
+ sharepoint_client_secret=input.config.sharepoint_client_secret,
40
+ sharepoint_site_name=input.config.sharepoint_site_name,
41
+ sharepoint_tenant_name=input.config.sharepoint_tenant_name,
42
+ sharepoint_tenant_id=input.config.sharepoint_tenant_id,
43
+ ),
44
+ )
27
45
  )
28
46
 
29
47
  async def execute(self) -> UpdateThiesDataControllerOutput:
@@ -34,6 +52,11 @@ class UpdateThiesDataController:
34
52
  status=HTTPStatus.OK.value,
35
53
  metadata={"data": data},
36
54
  )
55
+ except EmptyDataError:
56
+ return UpdateThiesDataControllerOutput(
57
+ message="No files to upload", status=HTTPStatus.NO_CONTENT
58
+ )
59
+
37
60
  except (AttributeError, NameError, ValueError) as error:
38
61
  return UpdateThiesDataControllerOutput(
39
62
  message="An unexpected error occurred during use case initialization.",
@@ -47,22 +70,30 @@ class UpdateThiesDataController:
47
70
  metadata={"error": error.__str__()},
48
71
  )
49
72
 
50
- except HttpClientError as error:
73
+ except SharepointClientError as error:
51
74
  return UpdateThiesDataControllerOutput(
52
- message="Http Client initialization fails.",
75
+ message="Sharepoint Client initialization fails.",
53
76
  status=HTTPStatus.BAD_REQUEST.value,
54
77
  metadata={"error": error.__str__()},
55
78
  )
56
79
 
57
- except FetchCloudFileNamesError as error:
80
+ except SharePointFetchingError as error:
58
81
  return UpdateThiesDataControllerOutput(
59
82
  message="An error occurred while retrieving file names from the RCER cloud",
60
83
  status=HTTPStatus.INTERNAL_SERVER_ERROR.value,
61
84
  metadata={"error": error.__str__()},
62
85
  )
63
- except ThiesUploadEmptyError as error:
86
+
87
+ except ThiesFetchingError as error:
64
88
  return UpdateThiesDataControllerOutput(
65
- message="No files were found to upload.",
89
+ message="An error ocurred while retrieving file names from THIES FTP Server.",
66
90
  status=HTTPStatus.NO_CONTENT.value,
67
91
  metadata={"error": error.__str__()},
68
92
  )
93
+
94
+ except ThiesConnectionError as error:
95
+ return UpdateThiesDataControllerOutput(
96
+ message="Unable to connect to THIES Data Logger FTP Server.",
97
+ status=HTTPStatus.INTERNAL_SERVER_ERROR.value,
98
+ metadata={"error": error.__str__()},
99
+ )
@@ -0,0 +1,7 @@
1
+ from .update_thies_data_types import (
2
+ FtpClientConfig,
3
+ SharepointConfig,
4
+ UpdateThiesDataUseCaseInput,
5
+ )
6
+
7
+ __all__ = ["UpdateThiesDataUseCaseInput", "FtpClientConfig", "SharepointConfig"]
@@ -0,0 +1,32 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Dict
3
+
4
+
5
+ @dataclass
6
+ class FtpClientConfig:
7
+ ftp_host: str
8
+ ftp_port: int
9
+ ftp_user: str
10
+ ftp_password: str
11
+
12
+
13
+ @dataclass
14
+ class SharepointConfig:
15
+ sharepoint_client_id: str
16
+ sharepoint_client_secret: str
17
+ sharepoint_tenant_id: str
18
+ sharepoint_tenant_name: str
19
+ sharepoint_site_name: str
20
+
21
+
22
+ @dataclass
23
+ class UpdateThiesDataUseCaseInput:
24
+ ftp_config: FtpClientConfig
25
+ sharepoint_config: SharepointConfig
26
+
27
+
28
+ @dataclass
29
+ class UpdateThiesDataUseCaseOutput:
30
+ message: str
31
+ status: int = 0
32
+ metadata: Dict[str, str] = field(default_factory=dict)
@@ -1,32 +1,34 @@
1
1
  from dotenv import load_dotenv
2
2
 
3
- import rcer_iot_client_pkg.services.epii.use_cases.constants as c
3
+ import rcer_iot_client_pkg.services.epii.constants.update_thies_data_constants as c
4
4
  from rcer_iot_client_pkg.general_types.error_types.api.update_thies_data_error_types import (
5
- FetchCloudFileNamesError,
6
- FetchThiesFileContentError,
7
- ThiesUploadEmptyError,
5
+ SharePointFetchingError,
6
+ ThiesConnectionError,
7
+ ThiesFetchingError,
8
8
  )
9
9
  from rcer_iot_client_pkg.general_types.error_types.common import (
10
10
  EmptyDataError,
11
11
  FtpClientError,
12
- HttpClientError,
13
- )
14
- from rcer_iot_client_pkg.libs.async_http_client import (
15
- AsyncHTTPClient,
16
- AsyncHttpClientInitArgs,
17
- GetArgs,
12
+ SharepointClientError,
18
13
  )
19
14
  from rcer_iot_client_pkg.libs.ftp_client import (
20
15
  FTPClient,
21
16
  FtpClientInitArgs,
22
- ListFilesArgs,
23
- ReadFileArgs,
17
+ FtpListFilesArgs,
18
+ FtpReadFileArgs,
19
+ )
20
+ from rcer_iot_client_pkg.libs.sharepoint_client import (
21
+ SharepointClient,
22
+ SharepointClientInitArgs,
23
+ SpListFilesArgs,
24
24
  )
25
25
  from rcer_iot_client_pkg.services.epii.use_cases.types import (
26
26
  UpdateThiesDataUseCaseInput,
27
+ FtpClientConfig,
28
+ SharepointConfig,
27
29
  )
28
30
  from rcer_iot_client_pkg.services.epii.utils import (
29
- generate_file_content,
31
+ parse_execute_response,
30
32
  )
31
33
 
32
34
  load_dotenv()
@@ -34,91 +36,85 @@ load_dotenv()
34
36
 
35
37
  class UpdateThiesDataUseCase:
36
38
  def __init__(self, input: UpdateThiesDataUseCaseInput):
37
- self.ftp_port = input.ftp_port
38
- self.ftp_host = input.ftp_host
39
- self.ftp_password = input.ftp_password
40
- self.ftp_user = input.ftp_user
41
- self.sharepoint_client = self._initialize_sharepoint_client()
42
- self.thies_ftp_client = self._initialize_thies_ftp_client()
39
+ self.sharepoint_client = self._initialize_sharepoint_client(
40
+ input.sharepoint_config
41
+ )
42
+ self.thies_ftp_client = self._initialize_thies_ftp_client(input.ftp_config)
43
43
  self.uploading = set()
44
44
 
45
- def _initialize_sharepoint_client(self) -> AsyncHTTPClient:
45
+ def _initialize_sharepoint_client(
46
+ self, config: SharepointConfig
47
+ ) -> SharepointClient:
46
48
  """Initialize the HTTP client."""
47
49
  try:
48
- return AsyncHTTPClient(
49
- AsyncHttpClientInitArgs(
50
- client_name="aiohttp_client",
51
- access_token="temporal-token",
52
- base_url="https://graph.microsoft.com/v1.0/",
53
- )
50
+ return SharepointClient(
51
+ SharepointClientInitArgs(config, client_name="sharepoint_rest_api")
54
52
  )
55
53
  except ConnectionError as error:
56
- raise HttpClientError(error)
54
+ raise SharepointClientError(error)
57
55
 
58
- def _initialize_thies_ftp_client(self) -> FTPClient:
56
+ def _initialize_thies_ftp_client(self, config: FtpClientConfig) -> FTPClient:
59
57
  """Initialize the FTP client."""
60
58
  try:
61
- return FTPClient(
62
- FtpClientInitArgs(
63
- client_name="aioftp_client",
64
- host=self.ftp_host,
65
- user=self.ftp_user,
66
- password=self.ftp_password,
67
- port=self.ftp_port,
68
- )
69
- )
59
+ return FTPClient(FtpClientInitArgs(config, client_name="aioftp_client"))
70
60
  except RuntimeError as error:
71
61
  raise FtpClientError(error)
72
62
 
73
- async def fetch_cloud_file_names(self, folder_name: str) -> set[str]:
63
+ async def fetch_cloud_file_names(self) -> set[str]:
74
64
  """Fetch file names from the RCER cloud."""
65
+
75
66
  try:
76
67
  cloud_files = set()
77
68
  async with self.sharepoint_client:
78
- for file_type in c.FILE_TYPES:
79
- destination_path = f"Onedrive_UC/noveno-semestre/IPRE-RCER/{folder_name}/{file_type}"
80
- endpoint = f"drives/{c.DRIVE_ID}/root:/{destination_path}:/children"
81
- response = await self.sharepoint_client.get(
82
- GetArgs(endpoint=endpoint)
69
+ for folder in c.SHAREPOINT_THIES_FOLDERS:
70
+ args = SpListFilesArgs(
71
+ folder_relative_url=f"{c.SHAREPOINT_BASE_URL}/{folder}"
83
72
  )
73
+ response = await self.sharepoint_client.list_files(args)
84
74
  cloud_files.update(
85
- {f"{file_type}_{item['name']}" for item in response["value"]}
75
+ {f"{folder}_{item['Name']}" for item in response["value"]}
86
76
  )
87
77
  return cloud_files
88
78
  except ConnectionError as error:
89
- raise FetchCloudFileNamesError(error)
79
+ raise SharePointFetchingError(reason=error)
90
80
 
91
81
  async def fetch_thies_file_names(self) -> set[str]:
92
82
  """Fetch file names from the THIES FTP server."""
93
83
  try:
94
84
  avg_files = await self.thies_ftp_client.list_files(
95
- ListFilesArgs(path=c.PATH_AVG_FILES)
85
+ FtpListFilesArgs(path=c.FTP_SERVER_PATH_AVG_FILES)
96
86
  )
97
87
  ext_files = await self.thies_ftp_client.list_files(
98
- ListFilesArgs(path=c.PATH_EXT_FILES)
88
+ FtpListFilesArgs(path=c.FTP_SERVER_PATH_EXT_FILES)
99
89
  )
100
90
  return {f"AVG_{name}" for name in avg_files} | {
101
91
  f"EXT_{name}" for name in ext_files
102
92
  }
103
- except ConnectionError:
104
- raise ThiesUploadEmptyError
93
+ except ConnectionRefusedError as error:
94
+ raise ThiesConnectionError(reason=error)
95
+ except ConnectionAbortedError as error:
96
+ raise ThiesFetchingError(reason=error)
105
97
 
106
98
  async def fetch_thies_file_content(self) -> dict[str, bytes]:
107
99
  """Fetch the content of files from the THIES FTP server."""
108
- content_files = {}
109
- for file in self.uploading:
110
- try:
100
+ try:
101
+ content_files = {}
102
+ for file in self.uploading:
111
103
  origin, filename = file.split("_", 1)
112
104
  file_path = (
113
- f"{c.PATH_AVG_FILES}/{filename}"
105
+ f"{c.FTP_SERVER_PATH_AVG_FILES}/{filename}"
114
106
  if origin == "AVG"
115
- else f"{c.PATH_EXT_FILES}/{filename}"
107
+ else f"{c.FTP_SERVER_PATH_EXT_FILES}/{filename}"
108
+ )
109
+ content = await self.thies_ftp_client.read_file(
110
+ FtpReadFileArgs(file_path)
116
111
  )
117
- content = await self.thies_ftp_client.read_file(ReadFileArgs(file_path))
118
112
  content_files[filename] = content
119
- except ConnectionError as error:
120
- raise FetchThiesFileContentError(error)
121
- return content_files
113
+ return content_files
114
+ except ConnectionRefusedError as error:
115
+ raise ThiesConnectionError(reason=error)
116
+ except ConnectionAbortedError as error:
117
+ raise ThiesFetchingError(reason=error)
122
118
 
123
119
  async def execute(self) -> dict:
124
120
  """Synchronize data from the THIES Center to the cloud."""
@@ -126,12 +122,14 @@ class UpdateThiesDataUseCase:
126
122
  thies_files = await self.fetch_thies_file_names()
127
123
  except RuntimeError as error:
128
124
  raise FtpClientError(error)
129
-
130
- cloud_files = await self.fetch_cloud_file_names(folder_name="thies")
125
+ try:
126
+ cloud_files = await self.fetch_cloud_file_names()
127
+ except RuntimeError as error:
128
+ raise SharepointClient(error)
131
129
  self.uploading = thies_files - cloud_files
132
130
  if not self.uploading:
133
- raise EmptyDataError
131
+ raise EmptyDataError(reason="No files to upload.")
134
132
 
135
133
  thies_file_contents = await self.fetch_thies_file_content()
136
- data = generate_file_content(thies_file_contents)
134
+ data = parse_execute_response(thies_file_contents)
137
135
  return data
@@ -0,0 +1,3 @@
1
+ from .update_thies_data_utils import parse_execute_response
2
+
3
+ __all__ = ["parse_execute_response"]
@@ -6,7 +6,7 @@ from rcer_iot_client_pkg.libs.zero_dependency.utils.datetime_utils import (
6
6
  )
7
7
 
8
8
 
9
- def generate_file_content(
9
+ def parse_execute_response(
10
10
  file_contents: dict[str, Any],
11
11
  ) -> dict[str, dict[str, int | str]]:
12
12
  return {
@@ -1,19 +0,0 @@
1
- class ThiesUploadEmptyError(Exception):
2
- """Raised when no files are found to upload to the server."""
3
-
4
- def __str__(self):
5
- return "No files were found to upload."
6
-
7
-
8
- class FetchCloudFileNamesError(Exception):
9
- """Raised when there is an error fetching file names from the RCER cloud."""
10
-
11
- def __str__(self):
12
- return "An error occurred while retrieving file names from the RCER cloud"
13
-
14
-
15
- class FetchThiesFileContentError(Exception):
16
- """Raised when there is an error fetching the content of a Thies file."""
17
-
18
- def __str__(self):
19
- return "An error occurred while retrieving the content of a Thies file"
@@ -1,7 +0,0 @@
1
- from .common_types import (
2
- EmptyDataError,
3
- FtpClientError,
4
- HttpClientError,
5
- )
6
-
7
- __all__ = ["EmptyDataError", "HttpClientError", "FtpClientError"]
@@ -1,13 +0,0 @@
1
- class EmptyDataError(Exception):
2
- def __str__(self):
3
- return "The data provided is empty."
4
-
5
-
6
- class HttpClientError(Exception):
7
- def __str__(self):
8
- return "Http Client initialization fails."
9
-
10
-
11
- class FtpClientError(Exception):
12
- def __str__(self):
13
- return "Ftp Client initialization fails."
@@ -1,4 +0,0 @@
1
- from .ftp_client import FTPClient
2
- from .types import FtpClientInitArgs, ListFilesArgs, ReadFileArgs
3
-
4
- __all__ = ["FTPClient", "FtpClientInitArgs", "ListFilesArgs", "ReadFileArgs"]
@@ -1,13 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from .types.ftp_client_types import ListFilesArgs, ReadFileArgs
4
-
5
-
6
- class FTPClientContract(ABC):
7
- @abstractmethod
8
- def list_files(self, args: ListFilesArgs) -> list[str]:
9
- pass
10
-
11
- @abstractmethod
12
- def read_file(self, args: ReadFileArgs) -> bytes:
13
- pass
@@ -1,3 +0,0 @@
1
- from .ftp_client_types import FtpClientInitArgs, ListFilesArgs, ReadFileArgs
2
-
3
- __all__ = ["FtpClientInitArgs", "ListFilesArgs", "ReadFileArgs"]
@@ -1,24 +0,0 @@
1
- from typing import Dict
2
-
3
- from .controllers.types.update_thies_data_types import UpdateThiesDataControllerInput
4
- from .controllers.update_thies_data import UpdateThiesDataController
5
-
6
-
7
- class EpiiAPI:
8
- async def update_thies_data(
9
- self,
10
- ftp_port: int,
11
- ftp_host: str,
12
- ftp_password: str,
13
- ftp_user: str,
14
- ) -> Dict[str, any]:
15
- controller = UpdateThiesDataController(
16
- UpdateThiesDataControllerInput(
17
- ftp_port=ftp_port,
18
- ftp_host=ftp_host,
19
- ftp_password=ftp_password,
20
- ftp_user=ftp_user,
21
- )
22
- )
23
- response = await controller.execute()
24
- return response.__dict__
@@ -1,4 +0,0 @@
1
- PATH_AVG_FILES = "ftp/thies/BINFILES/ARCH_AV1"
2
- PATH_EXT_FILES = "ftp/thies/BINFILES/ARCH_EX1"
3
- DRIVE_ID = "b!Row14jaFrU-1q8qzrvj3OmPPTYWXizFEpJmI-wsfH5pXxA0qQwgQS50m2xvPCZem"
4
- FILE_TYPES = ["AVG", "EXT"]
@@ -1,3 +0,0 @@
1
- from .update_thies_data_types import UpdateThiesDataUseCaseInput
2
-
3
- __all__ = ["UpdateThiesDataUseCaseInput"]
@@ -1,17 +0,0 @@
1
- from dataclasses import dataclass, field
2
- from typing import Dict
3
-
4
-
5
- @dataclass
6
- class UpdateThiesDataUseCaseInput:
7
- ftp_host: str
8
- ftp_port: str
9
- ftp_user: str
10
- ftp_password: str
11
-
12
-
13
- @dataclass
14
- class UpdateThiesDataUseCaseOutput:
15
- message: str
16
- status: int = 0
17
- metadata: Dict[str, str] = field(default_factory=dict)
@@ -1,3 +0,0 @@
1
- from .update_thies_data_utils import generate_file_content
2
-
3
- __all__ = ["generate_file_content"]