mercuto-client 0.2.8__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 (37) 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.2.8.dist-info → mercuto_client-0.3.0a0.dist-info}/METADATA +10 -3
  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.8.dist-info/RECORD +0 -30
  35. {mercuto_client-0.2.8.dist-info → mercuto_client-0.3.0a0.dist-info}/WHEEL +0 -0
  36. {mercuto_client-0.2.8.dist-info → mercuto_client-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
  37. {mercuto_client-0.2.8.dist-info → mercuto_client-0.3.0a0.dist-info}/top_level.txt +0 -0
mercuto_client/mocks.py DELETED
@@ -1,203 +0,0 @@
1
- import contextlib
2
- import dataclasses
3
- import re
4
- import uuid
5
- from typing import Any, Optional, Protocol
6
-
7
- from .exceptions import MercutoClientException
8
- from .types import Tenant, User, UserDetails, VerifyMeResult
9
-
10
- """
11
- This module provides a context manager for mocking the Mercuto Client.
12
-
13
- Within the mock_client() context, any calls to the Mercuto Client will be intercepted and handled by the MercutoMocker.
14
-
15
-
16
- Usage:
17
-
18
- client = MercutoClient()
19
-
20
- with mock_client() as mock:
21
- # Create a user in the system
22
- api_key = mock.add_user()
23
-
24
- # Login using the generated user key
25
- client.connect(api_key = api_key)
26
-
27
- # Call Mercuto Client endpoints as normal
28
- client.identity().verify_me()
29
-
30
- """
31
-
32
-
33
- class MakeRequestHookType(Protocol):
34
- def __call__(self, method: str, url: str, *args, **kwargs) -> Any:
35
- """
36
- *args and **kwargs are arguments passed to requests.request(method, url, *args, **kwargs)
37
- """
38
- pass
39
-
40
-
41
- @contextlib.contextmanager
42
- def mock_client():
43
- from .client import MercutoClient
44
- original = MercutoClient._make_request
45
- mocker = MercutoMocker()
46
- try:
47
- setattr(MercutoClient, '_make_request', mocker._on_make_request)
48
- yield mocker
49
- finally:
50
- setattr(MercutoClient, '_make_request', original)
51
-
52
-
53
- class MercutoMocker:
54
- @dataclasses.dataclass
55
- class MockedUser:
56
- code: str
57
- username: str
58
- description: str
59
- tenant: str
60
- permission_group: str
61
- mobile_number: Optional[str] = None
62
-
63
- def __init__(self) -> None:
64
- self._hooks: dict[tuple[str, str], MakeRequestHookType] = {}
65
-
66
- # Users based on Api-Key
67
- self._known_users: dict[str, MercutoMocker.MockedUser] = {}
68
- self._known_tenants: dict[str, Tenant] = {}
69
-
70
- self._setup_default_hooks()
71
-
72
- def on(self, method: str, path: str, callback: MakeRequestHookType):
73
- self._hooks[(method, path)] = callback
74
-
75
- def add_tenant(self, code: Optional[str] = None) -> None:
76
- if code is None:
77
- code = str(uuid.uuid4())
78
- self._known_tenants[code] = Tenant(
79
- code=code,
80
- name=f"Tenant {code}",
81
- description=f"Tenant {code}",
82
- logo_url=None,
83
- )
84
-
85
- def add_user(self,
86
- user: Optional[str] = None,
87
- tenant: Optional[str] = None,
88
- permission_group: Optional[str] = None,
89
- key: Optional[str] = None,
90
- username: Optional[str] = None,
91
- description: Optional[str] = None,
92
- mobile_number: Optional[str] = None,
93
- ):
94
- if user is None:
95
- # User code
96
- user = str(uuid.uuid4())
97
- if tenant is None:
98
- tenant = str(uuid.uuid4())
99
- if permission_group is None:
100
- permission_group = str(uuid.uuid4())
101
- if key is None:
102
- key = str(uuid.uuid4())
103
- if username is None:
104
- username = f"{user.replace(' ', '')}@example.com"
105
- if description is None:
106
- description = f"User {user}"
107
-
108
- mocked = self.MockedUser(user, username, description, tenant, permission_group,
109
- mobile_number=mobile_number)
110
- self._known_users[key] = mocked
111
- return key
112
-
113
- def delete_user(self, key: Optional[str] = None, code: Optional[str] = None):
114
- if key is not None and key in self._known_users:
115
- del self._known_users[key]
116
-
117
- if code is not None:
118
- key = next((k for k, u in self._known_users.items()
119
- if u.code == code), None)
120
- if key is not None:
121
- del self._known_users[key]
122
-
123
- def _mocked_get_tenant(self, method: str, url: str, *args, **kwargs) -> Tenant:
124
- key = kwargs.get('headers', {}).get('X-Api-Key', None)
125
- if key is None:
126
- raise MercutoClientException("No X-Api-Key header provided")
127
- tenant = self._known_tenants.get(key, None)
128
- if tenant is None:
129
- raise MercutoClientException("Tenant not found")
130
- return tenant
131
-
132
- def _mocked_verify_me(self, method: str, url: str, *args, **kwargs) -> VerifyMeResult:
133
- key = kwargs.get('headers', {}).get('X-Api-Key', None)
134
- if key is None:
135
- raise MercutoClientException("No X-Api-Key header provided")
136
- user = self._known_users.get(key, None)
137
- if user is None:
138
- raise MercutoClientException(f"User {user} not found")
139
- return VerifyMeResult(
140
- user=user.code,
141
- tenant=user.tenant,
142
- permission_group=user.permission_group,
143
- acl_policy='{"version": 1, "permissions": []}'
144
- )
145
-
146
- def _mocked_get_user(self, method: str, url: str, *args, **kwargs) -> User:
147
- apikey = kwargs.get('headers', {}).get('X-Api-Key', None)
148
- servicekey = kwargs.get('headers', {}).get('X-Service-Token', None)
149
- if apikey is None and servicekey is None:
150
- raise MercutoClientException(
151
- "No X-Api-Key or X-Service-Token header provided")
152
- user_code = url.split('/')[-1]
153
- user = next((u for u in self._known_users.values()
154
- if u.code == user_code), None)
155
- if user is None:
156
- raise MercutoClientException("User not found")
157
- return User(
158
- code=user.code,
159
- username=user.username,
160
- description=user.description,
161
- tenant=user.tenant,
162
- permission_group=user.permission_group,
163
- )
164
-
165
- def _mocked_get_user_details(self, method: str, url: str, *args, **kwargs) -> UserDetails:
166
- apikey = kwargs.get('headers', {}).get('X-Api-Key', None)
167
- servicekey = kwargs.get('headers', {}).get('X-Service-Token', None)
168
- if apikey is None and servicekey is None:
169
- raise MercutoClientException(
170
- "No X-Api-Key or X-Service-Token header provided")
171
- url_parts = url.split('/')
172
- assert url_parts[-1] == 'details'
173
- user_code = url_parts[-2]
174
- user = next((u for u in self._known_users.values()
175
- if u.code == user_code), None)
176
- if user is None:
177
- raise MercutoClientException("User not found")
178
- return UserDetails(
179
- code=user.code,
180
- username=user.username,
181
- mobile_number=user.mobile_number,
182
- email_address=None,
183
- first_name=None,
184
- last_name=None,
185
- api_keys=[]
186
- )
187
-
188
- def _setup_default_hooks(self) -> None:
189
- self.on('GET', r'\/identity\/verify\/me', self._mocked_verify_me)
190
- self.on('GET', r'\/identity\/users\/[^\/]+', self._mocked_get_user)
191
- self.on('GET', r'\/identity\/tenant\/[^\/]+', self._mocked_get_tenant)
192
- self.on(
193
- 'GET', r'\/identity\/users\/[^\/]+\/details', self._mocked_get_user_details)
194
-
195
- def _on_make_request(self, method: str, url: str, *args, **kwargs) -> Any:
196
-
197
- # First check any custom hooks
198
- for (hook_method, pattern), callback in self._hooks.items():
199
- if method == hook_method and re.fullmatch(pattern, url) is not None:
200
- return callback(method, url, *args, **kwargs)
201
-
202
- raise NotImplementedError(
203
- "Mocking is not supported for this endpoint: %s %s" % (method, url))
mercuto_client/types.py DELETED
@@ -1,409 +0,0 @@
1
- from typing import Any, List, Literal, Optional, TypedDict
2
-
3
-
4
- class User(TypedDict):
5
- code: str
6
- username: Optional[str]
7
- description: str
8
- tenant: str
9
- permission_group: str
10
-
11
-
12
- class HiddenUserAPIKey(TypedDict):
13
- code: str
14
- description: str
15
- last_used: Optional[str]
16
- custom_policy: Optional[str]
17
-
18
-
19
- class UserDetails(TypedDict):
20
- code: str
21
- username: Optional[str]
22
- first_name: Optional[str]
23
- last_name: Optional[str]
24
- email_address: Optional[str]
25
- mobile_number: Optional[str]
26
-
27
- api_keys: list[HiddenUserAPIKey]
28
-
29
-
30
- class NewUserApiKey(TypedDict):
31
- code: str
32
- new_api_key: str
33
- description: str
34
- custom_policy: Optional[str]
35
-
36
-
37
- class Tenant(TypedDict):
38
- code: str
39
- name: str
40
- description: str
41
- logo_url: Optional[str]
42
-
43
-
44
- class AccessControlListJsonEntry(TypedDict):
45
- action: str
46
- resource: str
47
-
48
-
49
- class AccessControlListJson(TypedDict):
50
- version: Literal[1]
51
- permissions: list[AccessControlListJsonEntry]
52
-
53
-
54
- class PermissionGroup(TypedDict):
55
- tenant: str
56
- code: str
57
- label: str
58
- acl_policy: str
59
-
60
-
61
- class VerifyMeResult(TypedDict):
62
- user: Optional[str]
63
- tenant: Optional[str]
64
- permission_group: Optional[str]
65
- acl_policy: str
66
-
67
-
68
- HealthCheckMessageResult = Literal['OK', 'ERROR']
69
-
70
-
71
- class SystemHealthcheckResult(TypedDict):
72
- database: HealthCheckMessageResult
73
- cache: HealthCheckMessageResult
74
- ephemeral_document_store: HealthCheckMessageResult
75
- ephemeral_warehouse: HealthCheckMessageResult
76
-
77
-
78
- class AuthHealthcheckResult(TypedDict):
79
- status: HealthCheckMessageResult
80
-
81
-
82
- class ProjectStatus(TypedDict):
83
- last_ping: Optional[str]
84
- last_sample: Optional[str]
85
- ip_address: Optional[str]
86
-
87
-
88
- class Project(TypedDict):
89
- code: str
90
- name: str
91
- project_number: str
92
- active: bool
93
- description: str
94
- latitude: Optional[float]
95
- longitude: Optional[float]
96
- timezone: str
97
- display_timezone: Optional[str]
98
- tenant: str
99
- status: ProjectStatus
100
-
101
-
102
- class ProjectCode(TypedDict):
103
- code: str
104
-
105
-
106
- class ItemCode(TypedDict):
107
- code: str
108
-
109
-
110
- class Units(TypedDict):
111
- code: str
112
- name: str
113
- unit: Optional[str]
114
-
115
-
116
- CHANNEL_CLASSIFICATION = Literal['PRIMARY', 'SECONDARY',
117
- 'EVENT_METRIC', 'PRIMARY_EVENT_AGGREGATE']
118
-
119
-
120
- class Channel(TypedDict):
121
- code: str
122
- project: ItemCode
123
- units: Optional[Units]
124
- sampling_period: Optional[str]
125
- classification: CHANNEL_CLASSIFICATION
126
- label: str
127
- dtype: str
128
- device: Optional[ItemCode]
129
- metric: Optional[str]
130
- source: Optional[ItemCode]
131
- aggregate: Optional[str]
132
- value_range_min: Optional[float]
133
- value_range_max: Optional[float]
134
- channel_multiplier: float
135
- channel_offset: float
136
- last_valid_timestamp: Optional[str]
137
-
138
-
139
- class DeviceType(TypedDict):
140
- code: str
141
- description: str
142
- manufacturer: str
143
- model_number: str
144
-
145
-
146
- class Device(TypedDict):
147
- code: str
148
- project: ProjectCode
149
- label: str
150
- location_description: Optional[str]
151
- device_type: DeviceType
152
- groups: List[str]
153
-
154
-
155
- class DataSample(TypedDict):
156
- timestamp: str
157
- channel_code: str
158
- value: float
159
-
160
-
161
- class WidgetConfig(TypedDict):
162
- type: str
163
- config: dict
164
-
165
-
166
- class WidgetColumn(TypedDict):
167
- size: Optional[str | int]
168
- widget: WidgetConfig
169
-
170
-
171
- class WidgetRow(TypedDict):
172
- columns: List[WidgetColumn]
173
- height: int
174
- title: str
175
- breakpoint: Optional[str]
176
-
177
-
178
- class Dashboard(TypedDict):
179
- icon: Optional[str]
180
- name: Optional[str]
181
- banner_image: Optional[str]
182
- widgets: Optional[List[WidgetRow]]
183
- fullscreen: Optional[bool]
184
-
185
-
186
- class Dashboards(TypedDict):
187
- dashboards: List[Dashboard]
188
-
189
-
190
- class DataRequestMeta(TypedDict):
191
- first_timestamp: str
192
- last_timestamp: str
193
-
194
-
195
- class DataRequest(TypedDict):
196
- code: ItemCode
197
- requested_at: str
198
- completed_at: str | None
199
- in_progress: bool
200
- presigned_url: str | None
201
- message: str | None
202
- mime_type: str
203
- metadata: DataRequestMeta | None
204
-
205
-
206
- class Object(TypedDict):
207
- code: str
208
- mime_type: str
209
- size_bytes: int
210
- name: str
211
- event: ItemCode | None
212
- project: ItemCode
213
- access_url: str | None
214
- access_expires: str
215
-
216
-
217
- class EventMetric(TypedDict):
218
- channel: Channel
219
- value: float | str | dict | list | None
220
-
221
-
222
- class EventTag(TypedDict):
223
- tag: str
224
- value: Any | None
225
-
226
-
227
- class Event(TypedDict):
228
- code: str
229
- project: Project
230
- start_time: str
231
- end_time: str
232
- objects: list[Object]
233
- metrics: list[EventMetric]
234
- tags: list[EventTag]
235
-
236
-
237
- UserContactMethod = Literal['EMAIL', 'SMS']
238
-
239
-
240
- class ContactGroup(TypedDict):
241
- project: str
242
- code: str
243
- label: str
244
- users: dict[str, list[UserContactMethod]]
245
-
246
-
247
- class ScheduledReport(TypedDict):
248
- code: str
249
- project: str
250
- label: str
251
- revision: str
252
- schedule: Optional[str]
253
- contact_group: Optional[str]
254
- last_scheduled: Optional[str]
255
-
256
-
257
- class ScheduledReportLog(TypedDict):
258
- code: str
259
- report: str
260
- scheduled_start: Optional[str]
261
- actual_start: str
262
- actual_finish: Optional[str]
263
- status: Literal['IN_PROGRESS', 'COMPLETED', 'FAILED']
264
- message: Optional[str]
265
- access_url: Optional[str]
266
- mime_type: Optional[str]
267
- filename: Optional[str]
268
-
269
-
270
- class DatatableColumn(TypedDict):
271
- code: str
272
- column_label: str
273
-
274
-
275
- class DatatableOut(TypedDict):
276
- code: str
277
- name: str
278
- src: Optional[str]
279
- enabled: bool
280
- sampling_period: Optional[str]
281
- columns: list[DatatableColumn]
282
-
283
-
284
- class Datalogger(TypedDict):
285
- code: str
286
- project: ItemCode
287
- name: str
288
- type: str
289
- uri: str
290
- datatables: list[DatatableOut]
291
-
292
-
293
- class RainflowSource(TypedDict):
294
- linked_primary_channel: Channel
295
- sub_channels: list[Channel]
296
-
297
-
298
- class RainflowConfiguration(TypedDict):
299
- project: str
300
- max_bins: int
301
- bin_size: float
302
- multiplier: float
303
- sources: list[RainflowSource]
304
-
305
-
306
- class FatigueConnection(TypedDict):
307
- code: str
308
- label: str
309
- multiplier: float
310
- c_d: float
311
- m: float
312
- s_0: float
313
-
314
- bs7608_failure_probability: Optional[float]
315
- bs7608_detail_category: Optional[str]
316
-
317
- initial_date: str
318
- initial_damage: float
319
- sources: list[Channel]
320
-
321
- sub_channels: list[Channel]
322
-
323
-
324
- class Condition(TypedDict):
325
- code: str
326
- source: Channel
327
- description: str
328
- upper_exclusive_bound: Optional[float]
329
- lower_inclusive_bound: Optional[float]
330
- neutral_position: float
331
-
332
-
333
- class AlertConfiguration(TypedDict):
334
- code: str
335
- project: str
336
- label: str
337
- conditions: list[Condition]
338
- contact_group: Optional[ContactGroup]
339
- retrigger_interval: str
340
-
341
-
342
- class AlertLogConditionEntry(TypedDict):
343
- condition: Condition
344
- start_value: float
345
- start_time: str
346
- start_percentile: float
347
-
348
- peak_value: float
349
- peak_time: str
350
- peak_percentile: float
351
-
352
- end_value: float
353
- end_time: str
354
- end_percentile: float
355
-
356
-
357
- class AlertLogComment(TypedDict):
358
- user_code: str
359
- comment: str
360
- created_at: str
361
-
362
-
363
- class AlertLog(TypedDict):
364
- code: str
365
- project: str
366
- event: Optional[str]
367
- acknowledged: bool
368
- fired_at: str
369
- configuration: str
370
- conditions: list[AlertLogConditionEntry]
371
- comments: list[AlertLogComment]
372
-
373
-
374
- class ListAlertsResponseType(TypedDict):
375
- alerts: list[AlertLog]
376
- total: int
377
-
378
-
379
- class Camera(TypedDict):
380
- code: str
381
- project: str
382
- label: str
383
-
384
-
385
- class Video(TypedDict):
386
- code: str
387
- project: str
388
- camera: str | None
389
- start_time: str
390
- end_time: str
391
- mime_type: str
392
- size_bytes: int
393
- name: str
394
- event: str | None
395
- access_url: str | None
396
- access_expires: str
397
-
398
-
399
- class Image(TypedDict):
400
- code: str
401
- project: str
402
- camera: str | None
403
- timestamp: str | None
404
- mime_type: str
405
- size_bytes: int
406
- name: str
407
- event: str | None
408
- access_url: str | None
409
- access_expires: str
@@ -1,30 +0,0 @@
1
- mercuto_client/__init__.py,sha256=lgSN48xiIZAbJFSt1hw1-6nOPhBUji_fqgOOZjim6To,1260
2
- mercuto_client/_util.py,sha256=jWTriLwlDvC12fBpnkwrErRIcAmDtP2peXH-bLAAF14,512
3
- mercuto_client/acl.py,sha256=Ui7xfLxaqibNHyAIsC1t2koX-u4g3IVS6Tk-TgJ_1R4,2781
4
- mercuto_client/client.py,sha256=pkiek-Z9c9J7MHFjCscv-KX5UQfni2M9fkmphkWThXY,36849
5
- mercuto_client/exceptions.py,sha256=IWwywHlH5vNwKIaPsIxMYTRtQVLMUfy4NoyOeayrJx0,355
6
- mercuto_client/mocks.py,sha256=2sigKUdo8skg1VGnh0rL_7sjpv1Ril32AnfZCwsdfHk,7330
7
- mercuto_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- mercuto_client/types.py,sha256=9Cx5vBafhFbPKtLCNW-2CkgIyTHfR038fvBNfwtMmrs,7799
9
- mercuto_client/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- mercuto_client/_tests/conftest.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- mercuto_client/_tests/test_mocking.py,sha256=LnDFKQE8dwS4wyL2tPFEf7sUEqxX02Y52EsmHAwlKiQ,2873
12
- mercuto_client/_tests/test_ingester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- mercuto_client/_tests/test_ingester/test_file_processor.py,sha256=kC1DC0phmjl7jBMMBJYrs9Tx4NL9xKJNmqVX5FNH59s,7399
14
- mercuto_client/_tests/test_ingester/test_ftp.py,sha256=w1CHAGcZy88D2-nY61Gj16l1nHcer9LIKaMc_DXk23o,1318
15
- mercuto_client/_tests/test_ingester/test_parsers.py,sha256=SJIdPi_k0rZl2Ee3UFkUo4utJz2aq9Yv5PuEfpy_gog,5961
16
- mercuto_client/ingester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- mercuto_client/ingester/__main__.py,sha256=yF47P7WmCsRRtc8NjIeK1U7P7KJ8pQbQBUCpa1BDohs,6761
18
- mercuto_client/ingester/ftp.py,sha256=3-gMzoRCWjLZWeynjkwOXV59B4f0F2VnWp97fuUFTX4,4441
19
- mercuto_client/ingester/mercuto.py,sha256=9guacyCKsRLi39tp312kSGx8KFnSu8PevP0HoQHNBPo,6125
20
- mercuto_client/ingester/processor.py,sha256=XlMMM0taSHZzth39qVMsUkPO0g_ahC7Xcb01rOjQp3I,11906
21
- mercuto_client/ingester/util.py,sha256=yq8jgVIDeH4N1TglzaT8uf7z9m0yW7d5NPdwKFVsJKU,1931
22
- mercuto_client/ingester/parsers/__init__.py,sha256=2RXriMSH9-ld0W6nHZH7dDZUs8HBbAIM3B7FZwlY5b4,1364
23
- mercuto_client/ingester/parsers/campbell.py,sha256=jnuoQug5Rv239ANGp_1BDTM9oTH8nD8k-EJV7N82E38,416
24
- mercuto_client/ingester/parsers/generic_csv.py,sha256=UbujpRzOYFIGHY6sbhoVjQX-2bO1arlaFMmfurmiZVI,4025
25
- mercuto_client/ingester/parsers/worldsensing.py,sha256=QoSm1cH9A5zkQKaUnLDR0jWzv16RFIimc1zWgjV66PM,974
26
- mercuto_client-0.2.8.dist-info/licenses/LICENSE,sha256=0R2QbX4pr5XSiwUc2JoGS7Ja4npcQHyZlGJsR-E73I8,32386
27
- mercuto_client-0.2.8.dist-info/METADATA,sha256=J9EYId6CVKgJh773sirqtkY9gyEagG7atVjuJG_YscY,2296
28
- mercuto_client-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- mercuto_client-0.2.8.dist-info/top_level.txt,sha256=ecV4spooVaOU8AlclvojxY1LzLW1byDywh-ayLHvKCs,15
30
- mercuto_client-0.2.8.dist-info/RECORD,,