mercuto-client 0.2.7__py3-none-any.whl → 0.3.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mercuto-client might be problematic. Click here for more details.

Files changed (38) hide show
  1. mercuto_client/__init__.py +2 -24
  2. mercuto_client/_authentication.py +72 -0
  3. mercuto_client/_tests/test_ingester/test_parsers.py +67 -67
  4. mercuto_client/_tests/test_mocking/__init__.py +0 -0
  5. mercuto_client/_tests/test_mocking/conftest.py +13 -0
  6. mercuto_client/_tests/test_mocking/test_mock_identity.py +8 -0
  7. mercuto_client/acl.py +16 -10
  8. mercuto_client/client.py +53 -779
  9. mercuto_client/exceptions.py +5 -1
  10. mercuto_client/ingester/__main__.py +1 -1
  11. mercuto_client/ingester/mercuto.py +15 -16
  12. mercuto_client/ingester/parsers/__init__.py +3 -3
  13. mercuto_client/ingester/parsers/campbell.py +2 -2
  14. mercuto_client/ingester/parsers/generic_csv.py +5 -5
  15. mercuto_client/ingester/parsers/worldsensing.py +4 -3
  16. mercuto_client/mocks/__init__.py +92 -0
  17. mercuto_client/mocks/_utility.py +69 -0
  18. mercuto_client/mocks/mock_data.py +402 -0
  19. mercuto_client/mocks/mock_fatigue.py +30 -0
  20. mercuto_client/mocks/mock_identity.py +188 -0
  21. mercuto_client/modules/__init__.py +19 -0
  22. mercuto_client/modules/_util.py +18 -0
  23. mercuto_client/modules/core.py +674 -0
  24. mercuto_client/modules/data.py +623 -0
  25. mercuto_client/modules/fatigue.py +189 -0
  26. mercuto_client/modules/identity.py +254 -0
  27. mercuto_client/{ingester/util.py → util.py} +27 -11
  28. mercuto_client-0.3.0a0.dist-info/METADATA +72 -0
  29. mercuto_client-0.3.0a0.dist-info/RECORD +41 -0
  30. mercuto_client/_tests/test_mocking.py +0 -93
  31. mercuto_client/_util.py +0 -13
  32. mercuto_client/mocks.py +0 -203
  33. mercuto_client/types.py +0 -409
  34. mercuto_client-0.2.7.dist-info/METADATA +0 -20
  35. mercuto_client-0.2.7.dist-info/RECORD +0 -30
  36. {mercuto_client-0.2.7.dist-info → mercuto_client-0.3.0a0.dist-info}/WHEEL +0 -0
  37. {mercuto_client-0.2.7.dist-info → mercuto_client-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
  38. {mercuto_client-0.2.7.dist-info → mercuto_client-0.3.0a0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,189 @@
1
+ from datetime import datetime
2
+ from typing import TYPE_CHECKING, Literal, Optional
3
+
4
+ from pydantic import Field, TypeAdapter
5
+
6
+ if TYPE_CHECKING:
7
+ from ..client import MercutoClient
8
+
9
+ from . import _PayloadType
10
+ from ._util import BaseModel
11
+
12
+
13
+ class RainflowConfiguration(BaseModel):
14
+ project: str
15
+ max_bins: int
16
+ bin_size: float
17
+ multiplier: float
18
+ reservoir_adjustment: bool
19
+ sources: list[str]
20
+
21
+
22
+ class FatigueConnection(BaseModel):
23
+ project: str
24
+ code: str
25
+ label: str
26
+ multiplier: float
27
+ c_d: float
28
+ m: float
29
+ s_0: float
30
+ bs7608_failure_probability: Optional[float]
31
+ bs7608_detail_category: Optional[str]
32
+ initial_date: datetime
33
+ initial_damage: float
34
+ sources: list[str]
35
+
36
+
37
+ class ConnectionRemnantCapacity(BaseModel):
38
+ connection: FatigueConnection
39
+ remaining_life_years: float = Field(description="Remaining life of the connection in years")
40
+ total_damage: float = Field(description="Total damage accumulated in the connection up to the 'end_time' specified")
41
+
42
+
43
+ class Healthcheck(BaseModel):
44
+ status: str
45
+
46
+
47
+ _RainflowConfigurationlistAdapter = TypeAdapter(list[RainflowConfiguration])
48
+ _FatigueConnectionlistAdapter = TypeAdapter(list[FatigueConnection])
49
+ _ConnectionRemnantCapacitylistAdapter = TypeAdapter(list[ConnectionRemnantCapacity])
50
+
51
+
52
+ class MercutoFatigueService:
53
+ def __init__(self, client: 'MercutoClient', path: str = '/fatigue') -> None:
54
+ self._client = client
55
+ self._path = path
56
+
57
+ def healthcheck(self) -> Healthcheck:
58
+ r = self._client._http_request(f"{self._path}/healthcheck", "GET")
59
+ return Healthcheck.model_validate_json(r.text)
60
+
61
+ # --- Rainflow routes ---
62
+
63
+ def list_rainflow_config(self, project: str) -> list[RainflowConfiguration]:
64
+ params: _PayloadType = {"project": project}
65
+ r = self._client._http_request(f"{self._path}/rainflow/setup", "GET", params=params)
66
+ return _RainflowConfigurationlistAdapter.validate_json(r.text)
67
+
68
+ def setup_rainflow(
69
+ self,
70
+ project: str,
71
+ max_bins: int,
72
+ bin_size: float,
73
+ multiplier: float,
74
+ reservoir_adjustment: bool,
75
+ sources: list[str]
76
+ ) -> RainflowConfiguration:
77
+ payload: _PayloadType = {
78
+ "project": project,
79
+ "max_bins": max_bins,
80
+ "bin_size": bin_size,
81
+ "multiplier": multiplier,
82
+ "reservoir_adjustment": reservoir_adjustment,
83
+ "sources": sources,
84
+ }
85
+ r = self._client._http_request(f"{self._path}/rainflow/setup", "PUT", json=payload)
86
+ return RainflowConfiguration.model_validate_json(r.text)
87
+
88
+ def get_cycle_counts(
89
+ self, project: str, start_time: datetime, end_time: datetime
90
+ ) -> bytes:
91
+ params: _PayloadType = {
92
+ "project": project,
93
+ "start_time": start_time.isoformat(),
94
+ "end_time": end_time.isoformat(),
95
+ }
96
+ r = self._client._http_request(
97
+ f"{self._path}/rainflow/cycle_counts", "GET", params=params, stream=True
98
+ )
99
+ return r.content
100
+
101
+ def delete_cycle_counts(
102
+ self, project: str, start_time: datetime, end_time: datetime, ignore_if_not_configured: bool = False
103
+ ) -> None:
104
+ params: _PayloadType = {
105
+ "project": project,
106
+ "start_time": start_time.isoformat(),
107
+ "end_time": end_time.isoformat(),
108
+ "ignore_if_not_configured": ignore_if_not_configured,
109
+ }
110
+ self._client._http_request(
111
+ f"{self._path}/rainflow/cycle_counts", "DELETE", params=params
112
+ )
113
+
114
+ def calculate_cycle_counts(
115
+ self,
116
+ project: str,
117
+ event: str,
118
+ presigned_url: str,
119
+ mime_type: Literal['application/feather'],
120
+ url_expiry: Optional[datetime] = None,
121
+ ignore_if_not_configured: bool = False
122
+ ) -> None:
123
+ payload: _PayloadType = {
124
+ "project": project,
125
+ "event": event,
126
+ "presigned_url": presigned_url,
127
+ "mime_type": mime_type,
128
+ }
129
+ if url_expiry is not None:
130
+ payload["url_expiry"] = url_expiry.isoformat()
131
+ params = {"ignore_if_not_configured": ignore_if_not_configured}
132
+ self._client._http_request(
133
+ f"{self._path}/rainflow/cycle_counts/calculate", "PUT", json=payload, params=params
134
+ )
135
+
136
+ # --- Fatigue Connections routes ---
137
+
138
+ def get_connections(self, project: str) -> list[FatigueConnection]:
139
+ params: _PayloadType = {"project": project}
140
+ r = self._client._http_request(f"{self._path}/connections", "GET", params=params)
141
+ return _FatigueConnectionlistAdapter.validate_json(r.text)
142
+
143
+ def add_connection(
144
+ self,
145
+ project: str,
146
+ label: str,
147
+ multiplier: float,
148
+ c_d: float,
149
+ m: float,
150
+ s_0: float,
151
+ bs7608_failure_probability: float,
152
+ bs7608_detail_category: str,
153
+ initial_date: datetime,
154
+ initial_damage: float,
155
+ sources: list[str]
156
+ ) -> FatigueConnection:
157
+ payload: _PayloadType = {
158
+ "project": project,
159
+ "label": label,
160
+ "multiplier": multiplier,
161
+ "c_d": c_d,
162
+ "m": m,
163
+ "s_0": s_0,
164
+ "bs7608_failure_probability": bs7608_failure_probability,
165
+ "bs7608_detail_category": bs7608_detail_category,
166
+ "initial_date": initial_date.isoformat(),
167
+ "initial_damage": initial_damage,
168
+ "sources": sources,
169
+ }
170
+ r = self._client._http_request(f"{self._path}/connections", "PUT", json=payload)
171
+ return FatigueConnection.model_validate_json(r.text)
172
+
173
+ def delete_connection(self, connection_code: str) -> None:
174
+ self._client._http_request(f"{self._path}/connections/{connection_code}", "DELETE")
175
+
176
+ # --- Connection Data routes ---
177
+
178
+ def get_connection_remnant_capacity(
179
+ self, project: str, start_time: datetime, end_time: datetime
180
+ ) -> list[ConnectionRemnantCapacity]:
181
+ params: _PayloadType = {
182
+ "project": project,
183
+ "start_time": start_time.isoformat(),
184
+ "end_time": end_time.isoformat(),
185
+ }
186
+ r = self._client._http_request(
187
+ f"{self._path}/connection_data/remnant-capacity", "GET", params=params
188
+ )
189
+ return _ConnectionRemnantCapacitylistAdapter.validate_json(r.text)
@@ -0,0 +1,254 @@
1
+ from typing import TYPE_CHECKING, Optional
2
+
3
+ from pydantic import TypeAdapter
4
+
5
+ if TYPE_CHECKING:
6
+ from ..client import MercutoClient
7
+
8
+ from . import _PayloadType
9
+ from ._util import BaseModel
10
+
11
+
12
+ class PermissionGroup(BaseModel):
13
+ tenant: str
14
+ code: str
15
+ label: str
16
+ acl_policy: str
17
+
18
+
19
+ class Tenant(BaseModel):
20
+ code: str
21
+ name: str
22
+ description: str
23
+ logo_url: Optional[str] = None
24
+
25
+
26
+ class HiddenUserAPIKey(BaseModel):
27
+ code: str
28
+ description: str
29
+ last_used: Optional[str]
30
+ custom_policy: Optional[str] = None
31
+
32
+
33
+ class UserDetails(BaseModel):
34
+ code: str
35
+ username: Optional[str] = None
36
+ email_address: Optional[str] = None
37
+ mobile_number: Optional[str] = None
38
+ first_name: Optional[str] = None
39
+ last_name: Optional[str] = None
40
+ api_keys: list[HiddenUserAPIKey] = []
41
+
42
+
43
+ class User(BaseModel):
44
+ code: str
45
+ username: Optional[str] = None
46
+ description: str
47
+ tenant: str
48
+ permission_group: str
49
+
50
+
51
+ class CurrentUser(BaseModel):
52
+ code: str
53
+ username: Optional[str] = None
54
+ description: str
55
+ tenant: Tenant
56
+ permission_group: PermissionGroup
57
+ current_permission_policy: str
58
+
59
+
60
+ class VisibleUserAPIKey(BaseModel):
61
+ code: str
62
+ new_api_key: str
63
+ description: str
64
+ custom_policy: Optional[str]
65
+
66
+
67
+ class VerifyMyPermissions(BaseModel):
68
+ user: Optional[str]
69
+ acl_policy: str
70
+
71
+
72
+ class Healthcheck(BaseModel):
73
+ status: str
74
+
75
+
76
+ # --- TypeAdapters for lists ---
77
+ _PermissionGrouplistAdapter = TypeAdapter(list[PermissionGroup])
78
+ _TenantlistAdapter = TypeAdapter(list[Tenant])
79
+ _UserlistAdapter = TypeAdapter(list[User])
80
+ _HiddenUserAPIKeylistAdapter = TypeAdapter(list[HiddenUserAPIKey])
81
+
82
+
83
+ class MercutoIdentityService:
84
+ def __init__(self, client: 'MercutoClient', path: str = '/identity') -> None:
85
+ self._client = client
86
+ self._path = path
87
+
88
+ def healthcheck(self) -> Healthcheck:
89
+ r = self._client._http_request(f"{self._path}/healthcheck", "GET")
90
+ return Healthcheck.model_validate_json(r.text)
91
+
92
+ # --- Verify routes ---
93
+
94
+ def get_my_permissions(self) -> VerifyMyPermissions:
95
+ r = self._client._http_request(f"{self._path}/verify/me", "GET")
96
+ return VerifyMyPermissions.model_validate_json(r.text)
97
+
98
+ # --- User routes ---
99
+
100
+ def list_users(self, tenant: Optional[str] = None) -> list[User]:
101
+ params: _PayloadType = {}
102
+ if tenant is not None:
103
+ params["tenant"] = tenant
104
+ r = self._client._http_request(f"{self._path}/users", "GET", params=params)
105
+ return _UserlistAdapter.validate_json(r.text)
106
+
107
+ def create_user(
108
+ self,
109
+ username: str,
110
+ tenant: str,
111
+ description: str,
112
+ group: str,
113
+ default_password: Optional[str] = None
114
+ ) -> User:
115
+ payload: _PayloadType = {
116
+ "username": username,
117
+ "tenant_code": tenant,
118
+ "description": description,
119
+ "group_code": group,
120
+ "default_password": default_password,
121
+ }
122
+ r = self._client._http_request(f"{self._path}/users", "PUT", json=payload)
123
+ return User.model_validate_json(r.text)
124
+
125
+ def get_current_user(self) -> CurrentUser:
126
+ r = self._client._http_request(f"{self._path}/users/me", "GET")
127
+ return CurrentUser.model_validate_json(r.text)
128
+
129
+ def get_user(self, code: str) -> User:
130
+ r = self._client._http_request(f"{self._path}/users/{code}", "GET")
131
+ return User.model_validate_json(r.text)
132
+
133
+ def delete_user(self, code: str) -> None:
134
+ self._client._http_request(f"{self._path}/users/{code}", "DELETE")
135
+
136
+ def edit_user(
137
+ self,
138
+ code: str,
139
+ description: str,
140
+ group: str
141
+ ) -> User:
142
+ payload: _PayloadType = {
143
+ "description": description,
144
+ "group_code": group,
145
+ }
146
+ r = self._client._http_request(f"{self._path}/users/{code}", "PATCH", json=payload)
147
+ return User.model_validate_json(r.text)
148
+
149
+ def get_user_details(self, code: str) -> UserDetails:
150
+ r = self._client._http_request(f"{self._path}/users/{code}/details", "GET")
151
+ return UserDetails.model_validate_json(r.text)
152
+
153
+ def set_user_details(
154
+ self,
155
+ code: str,
156
+ email_address: Optional[str] = None,
157
+ mobile_number: Optional[str] = None,
158
+ first_name: Optional[str] = None,
159
+ last_name: Optional[str] = None
160
+ ) -> UserDetails:
161
+ payload: _PayloadType = {
162
+ "email_address": email_address,
163
+ "mobile_number": mobile_number,
164
+ "first_name": first_name,
165
+ "last_name": last_name,
166
+ }
167
+ r = self._client._http_request(f"{self._path}/users/{code}/details", "PATCH", json=payload)
168
+ return UserDetails.model_validate_json(r.text)
169
+
170
+ def get_user_api_keys(self, user: str) -> list[HiddenUserAPIKey]:
171
+ r = self._client._http_request(f"{self._path}/users/{user}/api_keys", "GET")
172
+ return _HiddenUserAPIKeylistAdapter.validate_json(r.text)
173
+
174
+ def generate_api_key_for_user(
175
+ self,
176
+ user: str,
177
+ description: str,
178
+ custom_policy: Optional[str] = None
179
+ ) -> VisibleUserAPIKey:
180
+ payload: _PayloadType = {
181
+ "description": description,
182
+ "custom_policy": custom_policy,
183
+ }
184
+ r = self._client._http_request(f"{self._path}/users/{user}/api_keys", "POST", json=payload)
185
+ return VisibleUserAPIKey.model_validate_json(r.text)
186
+
187
+ def delete_api_key(self, user: str, key_code: str) -> None:
188
+ self._client._http_request(f"{self._path}/users/{user}/api_keys/{key_code}", "DELETE")
189
+
190
+ # --- Tenants routes ---
191
+
192
+ def list_tenants(self) -> list[Tenant]:
193
+ r = self._client._http_request(f"{self._path}/tenants", "GET")
194
+ return _TenantlistAdapter.validate_json(r.text)
195
+
196
+ def get_tenant(self, code: str) -> Tenant:
197
+ r = self._client._http_request(f"{self._path}/tenants/{code}", "GET")
198
+ return Tenant.model_validate_json(r.text)
199
+
200
+ def create_tenant(
201
+ self,
202
+ name: str,
203
+ description: str,
204
+ logo_url: Optional[str] = None
205
+ ) -> Tenant:
206
+ payload: _PayloadType = {
207
+ "name": name,
208
+ "description": description,
209
+ "logo_url": logo_url,
210
+ }
211
+ r = self._client._http_request(f"{self._path}/tenants", "PUT", json=payload)
212
+ return Tenant.model_validate_json(r.text)
213
+
214
+ # --- Permission Groups routes ---
215
+
216
+ def get_permission_groups(self, tenant: Optional[str] = None) -> list[PermissionGroup]:
217
+ params = {}
218
+ if tenant is not None:
219
+ params["tenant"] = tenant
220
+ r = self._client._http_request(f"{self._path}/permissions", "GET", params=params)
221
+ return _PermissionGrouplistAdapter.validate_json(r.text)
222
+
223
+ def create_permission_group(
224
+ self,
225
+ tenant: str,
226
+ label: str,
227
+ acl_policy: str
228
+ ) -> PermissionGroup:
229
+ payload: _PayloadType = {
230
+ "tenant": tenant,
231
+ "label": label,
232
+ "acl_policy": acl_policy,
233
+ }
234
+ r = self._client._http_request(f"{self._path}/permissions", "PUT", json=payload)
235
+ return PermissionGroup.model_validate_json(r.text)
236
+
237
+ def get_permission_group(self, group: str) -> PermissionGroup:
238
+ r = self._client._http_request(f"{self._path}/permissions/{group}", "GET")
239
+ return PermissionGroup.model_validate_json(r.text)
240
+
241
+ def delete_permission_group(self, group: str) -> None:
242
+ self._client._http_request(f"{self._path}/permissions/{group}", "DELETE")
243
+
244
+ def modify_permission_group(
245
+ self,
246
+ group: str,
247
+ label: str,
248
+ acl_policy: str
249
+ ) -> None:
250
+ payload: _PayloadType = {
251
+ "label": label,
252
+ "acl_policy": acl_policy,
253
+ }
254
+ self._client._http_request(f"{self._path}/permissions/{group}", "PATCH", json=payload)
@@ -1,11 +1,24 @@
1
1
  import itertools
2
2
  import shutil
3
+ from datetime import timedelta
3
4
  from pathlib import Path
4
5
  from typing import Iterable, Iterator, TypeVar
5
6
 
6
7
  import requests
7
8
 
8
9
 
10
+ def timedelta_isoformat(td: timedelta) -> str:
11
+ """
12
+ ISO 8601 encoding for Python timedelta object.
13
+ Taken from pydantic source:
14
+ https://github.com/pydantic/pydantic/blob/3704eccce4661455acdda1cdcf716bd4b3382e08/pydantic/deprecated/json.py#L135-L140
15
+
16
+ """
17
+ minutes, seconds = divmod(td.seconds, 60)
18
+ hours, minutes = divmod(minutes, 60)
19
+ return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
20
+
21
+
9
22
  def get_my_public_ip() -> str:
10
23
  """
11
24
  Fetches the public IP address of the machine making the request.
@@ -51,14 +64,17 @@ def get_free_space_excluding_files(directory: str) -> int:
51
64
 
52
65
  T = TypeVar('T')
53
66
 
54
-
55
- def batched(iterable: Iterable[T], n: int) -> Iterator[tuple[T, ...]]:
56
- """
57
- Implementation of itertools.batched for < Python 3.12
58
- """
59
- it = iter(iterable)
60
- while True:
61
- chunk = tuple(itertools.islice(it, n))
62
- if not chunk:
63
- break
64
- yield chunk
67
+ try:
68
+ from itertools import batched # type: ignore
69
+ except ImportError:
70
+ # Python < 3.12
71
+ def batched(iterable: Iterable[T], n: int) -> Iterator[tuple[T, ...]]: # type: ignore[no-redef]
72
+ """
73
+ Implementation of itertools.batched for < Python 3.12
74
+ """
75
+ it = iter(iterable)
76
+ while True:
77
+ chunk = tuple(itertools.islice(it, n))
78
+ if not chunk:
79
+ break
80
+ yield chunk
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: mercuto-client
3
+ Version: 0.3.0a0
4
+ Summary: Library for interfacing with Rockfield's Mercuto API
5
+ Author-email: Daniel Whipp <daniel.whipp@rocktech.com.au>
6
+ License-Expression: AGPL-3.0-only
7
+ Project-URL: Homepage, https://mercuto.rockfieldcloud.com.au
8
+ Project-URL: Repository, https://github.com/RockfieldTechnologiesAustralia/mercuto-client
9
+ Project-URL: Documentation, https://github.com/RockfieldTechnologiesAustralia/mercuto-client/blob/main/README.md
10
+ Keywords: mercuto,rockfield,infratech
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Build Tools
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: requests>=2.32
21
+ Requires-Dist: pyftpdlib>=2.0.1
22
+ Requires-Dist: python-dateutil>=2.9.0.post0
23
+ Requires-Dist: pytz>=2025.2
24
+ Requires-Dist: schedule>=1.2.2
25
+ Requires-Dist: pydantic>=2.0
26
+ Dynamic: license-file
27
+
28
+ # Mercuto Client Python Library
29
+
30
+ Library for interfacing with Rockfield's Mercuto public API.
31
+ This library is in an early development state and is subject to major structural changes at any time.
32
+
33
+ (Visit our Github Repository)[https://github.com/RockfieldTechnologiesAustralia/mercuto-client]
34
+
35
+ ## Installation
36
+ Install from PyPi: `pip install mercuto-client` or adding the same line into your `requirements.txt`.
37
+
38
+ ## Basic Usage
39
+
40
+ Use the `connect()` function exposed within the main package and provide your API key.
41
+
42
+ ```python
43
+ from mercuto_client import connect
44
+
45
+ client = connect(api_key="<YOUR API KEY>")
46
+ print(client.core().list_projects())
47
+
48
+ # Logout after finished.
49
+ client.logout()
50
+
51
+ ```
52
+
53
+ You can also use the client as a context manager. It will logout automatically.
54
+
55
+ ```python
56
+ from mercuto_client import MercutoClient
57
+
58
+ with MercutoClient.as_credentials(api_key='<YOUR API KEY>') as client:
59
+ print(client.core().list_projects())
60
+ ```
61
+
62
+ ## Current Status
63
+ This library is incomplete and may not be fully compliant with the latest Mercuto version. It is only updated periodically and provided for use without any warranty or guarantees.
64
+
65
+ - [x] API Based login (Completed)
66
+ - [ ] Username/password login
67
+
68
+ ## Running tests
69
+ Install test packages:
70
+ `python -m uv sync --group tests`
71
+ Run tests:
72
+ `uv run pytest`
@@ -0,0 +1,41 @@
1
+ mercuto_client/__init__.py,sha256=wsBZ73gKjbKLHfJD0nSSl1xOY69HxwALGX73vm7m6iA,283
2
+ mercuto_client/_authentication.py,sha256=M213OBPYs7x5anK9PL92K_8w-orh9QNYm1-Rsdze6gQ,2700
3
+ mercuto_client/acl.py,sha256=_1ogLx62JrYe3igqZwgop-m5NbLoUZPXCo0pEpSR6pc,2927
4
+ mercuto_client/client.py,sha256=GnKB-q0IWPcMkLYr4WL_Mu8p8IzRRYY2FCNO1pi3JvM,6931
5
+ mercuto_client/exceptions.py,sha256=xyUXVSUOXKoMV23hh0qHrRtQuojDBv5e-I0dU7x6a0c,507
6
+ mercuto_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ mercuto_client/util.py,sha256=PWNp8BU9wa_sKMPxW0R1yveEF1XslZXG8sw6BZvUoO8,2603
8
+ mercuto_client/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ mercuto_client/_tests/conftest.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ mercuto_client/_tests/test_ingester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ mercuto_client/_tests/test_ingester/test_file_processor.py,sha256=kC1DC0phmjl7jBMMBJYrs9Tx4NL9xKJNmqVX5FNH59s,7399
12
+ mercuto_client/_tests/test_ingester/test_ftp.py,sha256=w1CHAGcZy88D2-nY61Gj16l1nHcer9LIKaMc_DXk23o,1318
13
+ mercuto_client/_tests/test_ingester/test_parsers.py,sha256=R9GnzAaGu_tva0s23VpwEVfEsEUcto3kN3EloIxZvVY,5917
14
+ mercuto_client/_tests/test_mocking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ mercuto_client/_tests/test_mocking/conftest.py,sha256=M-HikiSj_uuYAEYQnFA0uDtSBpZjO2YXjzxNbluQdMQ,266
16
+ mercuto_client/_tests/test_mocking/test_mock_identity.py,sha256=394r6A_xK78tse4CZA345JpIlIKF_GnPz2xXEjc_Nho,313
17
+ mercuto_client/ingester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ mercuto_client/ingester/__main__.py,sha256=HZisjXuj5wzdUJ9vLqjhCDqLobflLLVLuI-7PclvVgw,6762
19
+ mercuto_client/ingester/ftp.py,sha256=3-gMzoRCWjLZWeynjkwOXV59B4f0F2VnWp97fuUFTX4,4441
20
+ mercuto_client/ingester/mercuto.py,sha256=djlP9HVoBoB2R-pQVogyMvHpPpbWEjCX5vbMhlEhc8U,6077
21
+ mercuto_client/ingester/processor.py,sha256=XlMMM0taSHZzth39qVMsUkPO0g_ahC7Xcb01rOjQp3I,11906
22
+ mercuto_client/ingester/parsers/__init__.py,sha256=lOt4TyP08hK56wl7XHMlqmj62mDE0Idx8UJL0wljtwM,1398
23
+ mercuto_client/ingester/parsers/campbell.py,sha256=S5enYbajVTm3zSQYkEP6JRVUw94Z7ky100j8p5qLCls,441
24
+ mercuto_client/ingester/parsers/generic_csv.py,sha256=v4rwO4oJb1Ue6zirO0TGtvewOSp9f6ZUT_sWvozvQHo,4051
25
+ mercuto_client/ingester/parsers/worldsensing.py,sha256=rY3Io4mh8htfV4TghnCDkehPZIVoMqGWxEWnDVmZM6I,1028
26
+ mercuto_client/mocks/__init__.py,sha256=Y01V4eyzC5hGX3A9XwqPC82glTSIaWLAPB3lOyBymqg,2988
27
+ mercuto_client/mocks/_utility.py,sha256=YiUm_LzDOdHEhgkve_A5AEUAr9FmcPdVJi3R_izO8yw,2174
28
+ mercuto_client/mocks/mock_data.py,sha256=PqVtKe6eShkfAvMUkZyf_w4-yed2TEx3CncwGIRmxWA,17964
29
+ mercuto_client/mocks/mock_fatigue.py,sha256=GYcud-Rmpyz9wTYY9rsePvCUviIvu6c8wGXqe3L0MDk,940
30
+ mercuto_client/mocks/mock_identity.py,sha256=2Qc9hze9lpPnfa8aj25NDYdDii15hgqej0ymBc3DWQQ,7318
31
+ mercuto_client/modules/__init__.py,sha256=oPbPfkgL0JfCYRITS8O7uL8Zh8pjJTTPoEpK-nHmRuU,747
32
+ mercuto_client/modules/_util.py,sha256=edPLMJsdxd3oIqMDYNtUIAbg_wKypmr-C8ilBzmafDY,403
33
+ mercuto_client/modules/core.py,sha256=KzSmKH71qK9wDM6I-LvXY9oA2nBJFLoQXawK2ei3uCY,22828
34
+ mercuto_client/modules/data.py,sha256=hMhzkkr0f7N9kVKvUQE65uXDpcCxGILcNUhZLSD3nD4,20742
35
+ mercuto_client/modules/fatigue.py,sha256=SPftwW5rMk6LcsIGWO8OHc9r6DA99qt0AyAVOaNRt_8,6397
36
+ mercuto_client/modules/identity.py,sha256=VeMGsq-s5B9xorP9G2TxwIDYUSyyDGuFAJ8Ilg7_mn4,7898
37
+ mercuto_client-0.3.0a0.dist-info/licenses/LICENSE,sha256=0R2QbX4pr5XSiwUc2JoGS7Ja4npcQHyZlGJsR-E73I8,32386
38
+ mercuto_client-0.3.0a0.dist-info/METADATA,sha256=u2Yn0V3LiTyZaMtKoJenocAtWXKsqvHN-7BrVBa7YGc,2423
39
+ mercuto_client-0.3.0a0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ mercuto_client-0.3.0a0.dist-info/top_level.txt,sha256=ecV4spooVaOU8AlclvojxY1LzLW1byDywh-ayLHvKCs,15
41
+ mercuto_client-0.3.0a0.dist-info/RECORD,,