rcer-iot-client-pkg 0.5.2__py3-none-any.whl → 0.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.
@@ -4,6 +4,6 @@ from importlib.metadata import version
4
4
  __version__ = version("rcer_iot_client_pkg")
5
5
 
6
6
  from .services.epii.api import EpiiAPI
7
- from .general_types.api.update_thies_data_types import EpiiAPIConfig
7
+ from .general_types.api.update_thies_data_types import EpiiUpdateThiesConfig
8
8
 
9
- __all__ = ["EpiiAPI", "EpiiAPIConfig"]
9
+ __all__ = ["EpiiAPI", "EpiiUpdateThiesConfig"]
@@ -1,3 +1,3 @@
1
- from .update_thies_data_types import EpiiAPIConfig
1
+ from .update_thies_data_types import EpiiUpdateThiesConfig
2
2
 
3
- __all__ = ["EpiiAPIConfig"]
3
+ __all__ = ["EpiiUpdateThiesConfig"]
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
 
3
3
 
4
4
  @dataclass
5
- class EpiiAPIConfig:
5
+ class EpiiUpdateThiesConfig:
6
6
  """
7
7
  Configuration for Epii API.
8
8
 
@@ -16,6 +16,7 @@ class EpiiAPIConfig:
16
16
  sharepoint_tenant_id (str): Tenant ID for SharePoint authentication.
17
17
  sharepoint_tenant_name (str): Tenant name for SharePoint.
18
18
  sharepoint_site_name (str): Site name in SharePoint.
19
+ logger (Logger): Logger object for logging during synchronisation of files from THIES Data Logger
19
20
  """
20
21
 
21
22
  ftp_port: int
@@ -41,3 +41,17 @@ class SharePointFetchingError(Exception):
41
41
 
42
42
  except json.decoder.JSONDecodeError:
43
43
  return self.reason.__str__()
44
+
45
+
46
+ class SharePointUploadError(Exception):
47
+ """Raised when there is an error uploading files to the Microsoft SharePoint folder."""
48
+
49
+ def __init__(self, *args, reason):
50
+ super().__init__(*args, reason)
51
+ self.reason = reason
52
+
53
+ def __str__(self):
54
+ return (
55
+ "An error occurred while uploading files to the Microsoft SharePoint folder. "
56
+ + self.reason.__str__()
57
+ )
@@ -1,4 +1,3 @@
1
- import os
2
1
  from typing import Any
3
2
 
4
3
  from aiohttp import ClientError, ClientSession
@@ -125,12 +124,9 @@ class SharepointRestAPI(SharepointClientContract):
125
124
  folder_relative_url = (
126
125
  f"GetFolderByServerRelativeUrl('{args.folder_relative_url}')"
127
126
  )
128
- # Read the file
129
- source_file_path = os.path.basename(args.file_path)
130
- with open(source_file_path, "rb") as file:
131
- data = file.read()
127
+ data = args.file_content
132
128
 
133
- endpoint = f"web/{folder_relative_url}/Files/add(url='{source_file_path}',overwrite=false)"
129
+ endpoint = f"web/{folder_relative_url}/Files/add(url='{args.file_name}',overwrite=true)"
134
130
  response = await self.session.post(endpoint, data=data, headers=headers)
135
131
 
136
132
  response.raise_for_status()
@@ -28,7 +28,7 @@ class SharepointClient(SharepointClientContract):
28
28
  return await self.client_obj.list_files(args)
29
29
 
30
30
  async def list_folders(self, args: SpListFoldersArgs) -> list:
31
- return self.client_obj.list_files(args)
31
+ return await self.client_obj.list_files(args)
32
32
 
33
33
  async def upload_file(self, args: SpUploadFileArgs) -> dict:
34
- return self.client_obj.upload_file(args)
34
+ return await self.client_obj.upload_file(args)
@@ -20,6 +20,6 @@ class SpListFoldersArgs:
20
20
 
21
21
  @dataclass
22
22
  class SpUploadFileArgs:
23
- file_path: str
24
23
  folder_relative_url: str
24
+ file_name: str
25
25
  file_content: bytes = bytes()
@@ -2,7 +2,9 @@ from typing import Any, Dict
2
2
 
3
3
  from .controllers.types.update_thies_data_types import UpdateThiesDataControllerInput
4
4
  from .controllers.update_thies_data import UpdateThiesDataController
5
- from rcer_iot_client_pkg.general_types.api.update_thies_data_types import EpiiAPIConfig
5
+ from rcer_iot_client_pkg.general_types.api.update_thies_data_types import (
6
+ EpiiUpdateThiesConfig,
7
+ )
6
8
 
7
9
 
8
10
  class EpiiAPI:
@@ -10,12 +12,12 @@ class EpiiAPI:
10
12
  EpiiAPI is a service class that provides methods to interact with Patagonia Center system.
11
13
  """
12
14
 
13
- async def update_thies_data(self, config: EpiiAPIConfig) -> Dict[str, Any]:
15
+ async def update_thies_data(self, config: EpiiUpdateThiesConfig) -> Dict[str, Any]:
14
16
  """
15
17
  This method establishes a connection to an FTP server using the provided
16
18
  credentials and updates data related to THIES Data Logger.
17
19
  Args:
18
- config (EpiiAPIConfig): configuration class for FTP Server and Microsoft SharePoint credentials.
20
+ config (EpiiUpdateThiesConfig): configuration class for FTP Server and Microsoft SharePoint credentials.
19
21
  Returns:
20
22
  response (dict): A dictionary representation of the API response.
21
23
  """
@@ -1,4 +1,4 @@
1
- SHAREPOINT_BASE_URL = "/sites/uc365_CentrosyEstacionesRegionalesUC/Shared Documents/General/Test_Raspberry/THIES"
1
+ SHAREPOINT_BASE_URL = "/sites/uc365_CentrosyEstacionesRegionalesUC/Shared%20Documents/General/Test_Raspberry/THIES"
2
2
  SHAREPOINT_THIES_FOLDERS = ["AVG", "EXT"]
3
3
 
4
4
  FTP_SERVER_PATH_AVG_FILES = "ftp/thies/BINFILES/ARCH_AV1"
@@ -1,11 +1,13 @@
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
+ from rcer_iot_client_pkg.general_types.api.update_thies_data_types import (
4
+ EpiiUpdateThiesConfig,
5
+ )
4
6
 
5
7
 
6
8
  @dataclass
7
9
  class UpdateThiesDataControllerInput:
8
- config: EpiiAPIConfig
10
+ config: EpiiUpdateThiesConfig
9
11
 
10
12
 
11
13
  @dataclass
@@ -4,6 +4,7 @@ from rcer_iot_client_pkg.general_types.error_types.api.update_thies_data_error_t
4
4
  SharePointFetchingError,
5
5
  ThiesConnectionError,
6
6
  ThiesFetchingError,
7
+ SharePointUploadError,
7
8
  )
8
9
  from rcer_iot_client_pkg.general_types.error_types.common.common_types import (
9
10
  EmptyDataError,
@@ -54,7 +55,7 @@ class UpdateThiesDataController:
54
55
  )
55
56
  except EmptyDataError:
56
57
  return UpdateThiesDataControllerOutput(
57
- message="No files to upload", status=HTTPStatus.NO_CONTENT
58
+ message="No files to upload", status=HTTPStatus.NO_CONTENT.value
58
59
  )
59
60
 
60
61
  except (AttributeError, NameError, ValueError) as error:
@@ -84,6 +85,13 @@ class UpdateThiesDataController:
84
85
  metadata={"error": error.__str__()},
85
86
  )
86
87
 
88
+ except SharePointUploadError as error:
89
+ return UpdateThiesDataControllerOutput(
90
+ message="An error oucrred while uploading files to RCER Cloud",
91
+ status=HTTPStatus.BAD_REQUEST.value,
92
+ metadata={"error": error.__str__()},
93
+ )
94
+
87
95
  except ThiesFetchingError as error:
88
96
  return UpdateThiesDataControllerOutput(
89
97
  message="An error ocurred while retrieving file names from THIES FTP Server.",
@@ -3,6 +3,7 @@ from dotenv import load_dotenv
3
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
5
  SharePointFetchingError,
6
+ SharePointUploadError,
6
7
  ThiesConnectionError,
7
8
  ThiesFetchingError,
8
9
  )
@@ -21,11 +22,12 @@ from rcer_iot_client_pkg.libs.sharepoint_client import (
21
22
  SharepointClient,
22
23
  SharepointClientInitArgs,
23
24
  SpListFilesArgs,
25
+ SpUploadFileArgs,
24
26
  )
25
27
  from rcer_iot_client_pkg.services.epii.use_cases.types import (
26
- UpdateThiesDataUseCaseInput,
27
28
  FtpClientConfig,
28
29
  SharepointConfig,
30
+ UpdateThiesDataUseCaseInput,
29
31
  )
30
32
  from rcer_iot_client_pkg.services.epii.utils import (
31
33
  parse_execute_response,
@@ -109,13 +111,48 @@ class UpdateThiesDataUseCase:
109
111
  content = await self.thies_ftp_client.read_file(
110
112
  FtpReadFileArgs(file_path)
111
113
  )
112
- content_files[filename] = content
114
+ content_files[file] = content # Save the file with its prefix
113
115
  return content_files
114
116
  except ConnectionRefusedError as error:
115
117
  raise ThiesConnectionError(reason=error)
116
118
  except ConnectionAbortedError as error:
117
119
  raise ThiesFetchingError(reason=error)
118
120
 
121
+ async def upload_thies_files_to_sharepoint(
122
+ self, files: dict
123
+ ) -> dict[str, list[str]]:
124
+ """Upload files to SharePoint and categorize the results."""
125
+ upload_results = {"failed_files": [], "overwritten_files": [], "new_files": []}
126
+
127
+ async with self.sharepoint_client:
128
+ for file, file_content in files.items():
129
+ try:
130
+ folder, file_name = file.split("_", 1)
131
+ args = SpUploadFileArgs(
132
+ folder_relative_url=f"{c.SHAREPOINT_BASE_URL}/{folder}",
133
+ file_content=file_content,
134
+ file_name=file_name,
135
+ )
136
+ response = await self.sharepoint_client.upload_file(args)
137
+
138
+ if response.get("Exists", False):
139
+ upload_results["overwritten_files"].append(file)
140
+ else:
141
+ upload_results["new_files"].append(file)
142
+
143
+ except ConnectionError as error:
144
+ upload_results["failed_files"].append(
145
+ f"{file} (Error: {str(error)})"
146
+ )
147
+
148
+ if upload_results["failed_files"]:
149
+ raise SharePointUploadError(
150
+ reason="Files failed to upload: "
151
+ + ", ".join(upload_results["failed_files"])
152
+ )
153
+
154
+ return upload_results
155
+
119
156
  async def execute(self) -> dict:
120
157
  """Synchronize data from the THIES Center to the cloud."""
121
158
  try:
@@ -126,10 +163,17 @@ class UpdateThiesDataUseCase:
126
163
  cloud_files = await self.fetch_cloud_file_names()
127
164
  except RuntimeError as error:
128
165
  raise SharepointClient(error)
166
+
129
167
  self.uploading = thies_files - cloud_files
130
168
  if not self.uploading:
131
169
  raise EmptyDataError(reason="No files to upload.")
132
170
 
133
- thies_file_contents = await self.fetch_thies_file_content()
134
- data = parse_execute_response(thies_file_contents)
135
- return data
171
+ # Fetch the content of the files to be uploaded from THIES FTP Server
172
+ thies_fetched_files = await self.fetch_thies_file_content()
173
+
174
+ # Upload the fetched files to SharePoint and gather statistics
175
+ upload_statistics = await self.upload_thies_files_to_sharepoint(
176
+ thies_fetched_files
177
+ )
178
+
179
+ return parse_execute_response(thies_fetched_files, upload_statistics)
@@ -7,12 +7,15 @@ from rcer_iot_client_pkg.libs.zero_dependency.utils.datetime_utils import (
7
7
 
8
8
 
9
9
  def parse_execute_response(
10
- file_contents: dict[str, Any],
10
+ thies_fetched_files: dict[str, Any], upload_statistics: dict[str, Any]
11
11
  ) -> dict[str, dict[str, int | str]]:
12
12
  return {
13
- filename: {
14
- "size": len(data),
15
- "date": datetime_to_str(today()),
16
- }
17
- for filename, data in file_contents.items()
13
+ **upload_statistics,
14
+ "processed_files": {
15
+ filename: {
16
+ "file_size": len(data),
17
+ "processed_date": datetime_to_str(today()),
18
+ }
19
+ for filename, data in thies_fetched_files.items()
20
+ },
18
21
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rcer_iot_client_pkg
3
- Version: 0.5.2
3
+ Version: 0.6.1
4
4
  Summary: A client library for IoT projects in the RCER initiative
5
5
  License: MIT
6
6
  Author: pedropablozavalat
@@ -19,8 +19,7 @@ Requires-Dist: pydantic (==2.11.3)
19
19
  Requires-Dist: pytest-cov (==6.1.1)
20
20
  Description-Content-Type: text/markdown
21
21
 
22
- # RCER IoT Client Library | `rcer_iot_client_pkg`
23
-
22
+ # RCER IoT Client Package
24
23
 
25
24
  ## Installation
26
25
  You can find the package on [PyPI](https://pypi.org/project/rcer-iot-client-pkg/).
@@ -45,11 +44,11 @@ api_client = EpiiAPI()
45
44
  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
45
 
47
46
  ```python
48
- from rcer_iot_client_pkg import EpiiAPIConfig
47
+ from rcer_iot_client_pkg import EpiiUpdateThiesConfig
49
48
  import asyncio
50
49
 
51
50
  async def update_thies_data():
52
- config = EpiiAPIConfig(
51
+ config = EpiiUpdateThiesConfig(
53
52
  ftp_port=FTP_PORT,
54
53
  ftp_host=FTP_HOST,
55
54
  ftp_user=FTP_USER,
@@ -1,10 +1,10 @@
1
- rcer_iot_client_pkg/__init__.py,sha256=ynL8ADM5u2SQTdGfqVLDWEu9bYQnp6znMCEXnhu5zRA,272
1
+ rcer_iot_client_pkg/__init__.py,sha256=oQxns1FtAq7ubv9zqRt9yFWk-ksAQ5qp6DxEiGyWA7U,288
2
2
  rcer_iot_client_pkg/general_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- rcer_iot_client_pkg/general_types/api/__init__.py,sha256=1afRFS8AkxkxOWmcNLFI3H1lb-w_5dVJMMDKobBYlOY,80
4
- rcer_iot_client_pkg/general_types/api/update_thies_data_types.py,sha256=NqViiGKm1FdhgxJrNkLBLzqI-8SnPieAYv21kEN752U,963
3
+ rcer_iot_client_pkg/general_types/api/__init__.py,sha256=Dmy5H7Mm4n-ewj3dC1wzM5o-379Ebeb9hnBOqq8M4IE,96
4
+ rcer_iot_client_pkg/general_types/api/update_thies_data_types.py,sha256=1ZTjHSSfmhOp9wAHOMHfQxoibiNKN05PdpRrXUcd2JQ,1077
5
5
  rcer_iot_client_pkg/general_types/error_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  rcer_iot_client_pkg/general_types/error_types/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py,sha256=Wl9zd7qM2KeZF-h28dqSd6L9N8azOEagD837zoBgpAc,1269
7
+ rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py,sha256=YkbMXzg3B2fD-L-sh6f__6uZQ9nkvN6rUacchcPpJn8,1690
8
8
  rcer_iot_client_pkg/general_types/error_types/common/__init__.py,sha256=yOBLZbt64Ki9Q0IJ0tMAubgq7PtrQ7XQ3RgtAzyOjiE,170
9
9
  rcer_iot_client_pkg/general_types/error_types/common/common_types.py,sha256=n5yuw-gVtkrtNfmaZ83ZkYxYHGl4jynOLUB9C8Tr32w,474
10
10
  rcer_iot_client_pkg/libs/ftp_client/__init__.py,sha256=dW2Yutgc7mJJJzgKLhWKXMgQ6KIWJYfFa1sGpjHH5xU,191
@@ -15,24 +15,24 @@ rcer_iot_client_pkg/libs/ftp_client/ftp_client_contract.py,sha256=2x1MPZNFVw3l-s
15
15
  rcer_iot_client_pkg/libs/ftp_client/types/__init__.py,sha256=syfwf9feP4QK7fkCTfl4j8l11ic-jHtfi1DE2chaWbs,155
16
16
  rcer_iot_client_pkg/libs/ftp_client/types/ftp_client_types.py,sha256=e4SmYkewldulaD8ms2q75zVgLFXyBxBqoa_L-IQOmso,256
17
17
  rcer_iot_client_pkg/libs/sharepoint_client/__init__.py,sha256=v7h-cNsK-BaPp-hTU3NWRNXYRD9ztU-hsCk0eNRPIKA,334
18
- rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=Jxa8ZODVHVMdIwTCcE1tNS7z2JJDPMbgSshLvQ7H0Uk,5430
19
- rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client.py,sha256=UYfAhckzmal5-Jjvbz0P4gt64w5gOYJYksVSHXdJPiQ,1234
18
+ rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=4QvTvB6FDp9SdNmXLPvNAU4YkSaqU5TiSnK9LMUwk_c,5272
19
+ rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client.py,sha256=lzzRk-5YzScNL15TzHp8dAcdhiAtRVCfY7L2GEqoccM,1246
20
20
  rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client_contract.py,sha256=xqNHzCjp7GvUGGUox9YTJj2QJgTc5819t2etOk8X26o,485
21
- rcer_iot_client_pkg/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=gfXhvKuqZjxSdbgbEgGF1EKYDDq7CjK9Tdth-Waq5xo,414
21
+ rcer_iot_client_pkg/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=OmPlCJ9rLrAFBeG6aDp5cxMiQ5BZlDyGVx5S4GN4aqg,414
22
22
  rcer_iot_client_pkg/libs/zero_dependency/utils/datetime_utils.py,sha256=kD38wC087H3jwTIgrntBajE55cR2ioo_ftPUHiyGs_M,751
23
23
  rcer_iot_client_pkg/services/epii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- rcer_iot_client_pkg/services/epii/api.py,sha256=imw7crOMCuGLztEf5MpfpdzWC9IBnvDZJ4N957wRcIE,1038
25
- rcer_iot_client_pkg/services/epii/constants/update_thies_data_constants.py,sha256=XeBNsQaTsc0mylQegB4Wf9ECIo0i1Fk-BurWm84WDqE,273
24
+ rcer_iot_client_pkg/services/epii/api.py,sha256=bSZmn-jgAfPoE7dSy6Upp9-EKNndhGVSx_aSNEDSVJs,1071
25
+ rcer_iot_client_pkg/services/epii/constants/update_thies_data_constants.py,sha256=38f2WKV-zBO9O-CV8mh3tW3MFMiaJrrv4_FfQiyHApc,275
26
26
  rcer_iot_client_pkg/services/epii/controllers/__init__.py,sha256=mCdGgKGDgGxCtRoiZN9Rki-fTOyOuJWw9e7festpQYA,98
27
27
  rcer_iot_client_pkg/services/epii/controllers/types/__init__.py,sha256=xzky-oTSojLNkWETp_k8a4dcXYvYSQY0VhWo23Yhb8U,195
28
- rcer_iot_client_pkg/services/epii/controllers/types/update_thies_data_types.py,sha256=M5rYsjgqTndhPIRi77McxLyTrjmGsoVUIAGPETNXz9U,374
29
- rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py,sha256=YWoXXNVaddCt8D_rTb8cU7gYR6l1VNwrjtldbqLIQ3A,4060
28
+ rcer_iot_client_pkg/services/epii/controllers/types/update_thies_data_types.py,sha256=A9w_R4sQKiJOycFl15uucZsHoDjBNtegIuwZSbX775U,399
29
+ rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py,sha256=NOaVcB-GynY1bkz2o04iDtSK7kKX-hxvZSM4mfYocDs,4393
30
30
  rcer_iot_client_pkg/services/epii/use_cases/types/__init__.py,sha256=u6fyodOEJE2j6FMqJux40Xf9ccYAi-UUYxqT-Kzc0kE,199
31
31
  rcer_iot_client_pkg/services/epii/use_cases/types/update_thies_data_types.py,sha256=C0TU50KKYodpaX87OnG0MnHyGY4gRzmluUHk-esCEVU,635
32
- rcer_iot_client_pkg/services/epii/use_cases/update_thies_data.py,sha256=Tc12NUnZ0or2Tz5BA9jE62I5XxmKvZ__v4w5dTBk_5A,5052
32
+ rcer_iot_client_pkg/services/epii/use_cases/update_thies_data.py,sha256=su_aprLs7i4nvU_ItkDbn9Jl_VnfmwRDYws2kreG3No,6804
33
33
  rcer_iot_client_pkg/services/epii/utils/__init__.py,sha256=cYt2tvq65_OMjFaqb8-CCC7IGCQgFd4ziEUWJV7s1iY,98
34
- rcer_iot_client_pkg/services/epii/utils/update_thies_data_utils.py,sha256=-q8t-xZmpwFDADGlDu0S7EKvZK7R0GUR6JqOxQShWgE,415
35
- rcer_iot_client_pkg-0.5.2.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
36
- rcer_iot_client_pkg-0.5.2.dist-info/METADATA,sha256=BWgFty5jiYYErS5W7KgoFumdMKUy6ZfciJ3r309PkzA,3860
37
- rcer_iot_client_pkg-0.5.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
38
- rcer_iot_client_pkg-0.5.2.dist-info/RECORD,,
34
+ rcer_iot_client_pkg/services/epii/utils/update_thies_data_utils.py,sha256=_OWoSBmuKJTlUbGxrOkjVK3AKxh90OaTprzywj3sPhI,565
35
+ rcer_iot_client_pkg-0.6.1.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
36
+ rcer_iot_client_pkg-0.6.1.dist-info/METADATA,sha256=Y7jdN73UjXR2g5yN9w_C8JA1tFHG6r0on4omXL5pG1o,3852
37
+ rcer_iot_client_pkg-0.6.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
38
+ rcer_iot_client_pkg-0.6.1.dist-info/RECORD,,