23
23
BinaryID ,
24
24
BoundingBoxLabelsByFilterRequest ,
25
25
BoundingBoxLabelsByFilterResponse ,
26
+ CaptureInterval ,
26
27
CaptureMetadata ,
27
28
ConfigureDatabaseUserRequest ,
28
29
DataRequest ,
33
34
DeleteBinaryDataByIDsResponse ,
34
35
DeleteTabularDataRequest ,
35
36
DeleteTabularDataResponse ,
37
+ ExportTabularDataRequest ,
38
+ ExportTabularDataResponse ,
36
39
Filter ,
37
40
GetDatabaseConnectionRequest ,
38
41
GetDatabaseConnectionResponse ,
@@ -145,6 +148,69 @@ def __eq__(self, other: object) -> bool:
145
148
return str (self ) == str (other )
146
149
return False
147
150
151
+ @dataclass
152
+ class TabularDataPoint :
153
+ """Represents a tabular data point and its associated metadata."""
154
+
155
+ part_id : str
156
+ """The robot part ID"""
157
+
158
+ resource_name : str
159
+ """The resource name"""
160
+
161
+ resource_subtype : str
162
+ """The resource subtype. Ex: `rdk:component:sensor`"""
163
+
164
+ method_name : str
165
+ """The method used for data capture. Ex" `Readings`"""
166
+
167
+ time_captured : datetime
168
+ """The time at which the data point was captured"""
169
+
170
+ organization_id : str
171
+ """The organization ID"""
172
+
173
+ location_id : str
174
+ """The location ID"""
175
+
176
+ robot_name : str
177
+ """The robot name"""
178
+
179
+ robot_id : str
180
+ """The robot ID"""
181
+
182
+ part_name : str
183
+ """The robot part name"""
184
+
185
+ method_parameters : Mapping [str , ValueTypes ]
186
+ """Additional parameters associated with the data capture method"""
187
+
188
+ tags : List [str ]
189
+ """A list of tags associated with the data point"""
190
+
191
+ payload : Mapping [str , ValueTypes ]
192
+ """The captured data"""
193
+
194
+ def __str__ (self ) -> str :
195
+ return (
196
+ f"TabularDataPoint("
197
+ f"robot='{ self .robot_name } ' (id={ self .robot_id } ), "
198
+ f"part='{ self .part_name } ' (id={ self .part_id } ), "
199
+ f"resource='{ self .resource_name } ' ({ self .resource_subtype } ), "
200
+ f"method='{ self .method_name } ', "
201
+ f"org={ self .organization_id } , "
202
+ f"location={ self .location_id } , "
203
+ f"time='{ self .time_captured .isoformat ()} ', "
204
+ f"params={ self .method_parameters } , "
205
+ f"tags={ self .tags } , "
206
+ f"payload={ self .payload } )"
207
+ )
208
+
209
+ def __eq__ (self , other : object ) -> bool :
210
+ if isinstance (other , DataClient .TabularDataPoint ):
211
+ return str (self ) == str (other )
212
+ return False
213
+
148
214
def __init__ (self , channel : Channel , metadata : Mapping [str , str ]):
149
215
"""Create a `DataClient` that maintains a connection to app.
150
216
@@ -254,7 +320,6 @@ async def tabular_data_by_sql(self, organization_id: str, sql_query: str) -> Lis
254
320
sql_query="SELECT * FROM readings LIMIT 5"
255
321
)
256
322
257
-
258
323
Args:
259
324
organization_id (str): The ID of the organization that owns the data.
260
325
You can obtain your organization ID from the Viam app's organization settings page.
@@ -284,7 +349,6 @@ async def tabular_data_by_mql(self, organization_id: str, mql_binary: List[bytes
284
349
285
350
print(f"Tabular Data: {tabular_data}")
286
351
287
-
288
352
Args:
289
353
organization_id (str): The ID of the organization that owns the data.
290
354
You can obtain your organization ID from the Viam app's organization settings page.
@@ -307,13 +371,12 @@ async def get_latest_tabular_data(
307
371
308
372
::
309
373
310
- time_captured, time_synced, payload = await data_client.get_latest_tabular_data(
311
- part_id="<PART-ID>",
312
- resource_name="<RESOURCE-NAME>",
313
- resource_subtype="<RESOURCE-SUBTYPE>",
314
- method_name="<METHOD-NAME>"
315
- )
316
-
374
+ time_captured, time_synced, payload = await data_client.get_latest_tabular_data(
375
+ part_id="<PART-ID>",
376
+ resource_name="<RESOURCE-NAME>",
377
+ resource_subtype="<RESOURCE-SUBTYPE>",
378
+ method_name="<METHOD-NAME>"
379
+ )
317
380
318
381
Args:
319
382
part_id (str): The ID of the part that owns the data.
@@ -327,6 +390,7 @@ async def get_latest_tabular_data(
327
390
datetime: The time captured,
328
391
datetime: The time synced,
329
392
Dict[str, ValueTypes]: The latest tabular data captured from the specified data source.
393
+
330
394
For more information, see `Data Client API <https://docs.viam.com/appendix/apis/data-client/>`_.
331
395
"""
332
396
@@ -338,6 +402,64 @@ async def get_latest_tabular_data(
338
402
return None
339
403
return response .time_captured .ToDatetime (), response .time_synced .ToDatetime (), struct_to_dict (response .payload )
340
404
405
+ async def export_tabular_data (
406
+ self , part_id : str , resource_name : str , resource_subtype : str , method_name : str , start_time : Optional [datetime ] = None , end_time : Optional [datetime ] = None
407
+ ) -> List [TabularDataPoint ]:
408
+ """Obtain unified tabular data and metadata from the specified data source.
409
+
410
+ ::
411
+
412
+ tabular_data = await data_client.export_tabular_data(
413
+ part_id="<PART-ID>",
414
+ resource_name="<RESOURCE-NAME>",
415
+ resource_subtype="<RESOURCE-SUBTYPE>",
416
+ method_name="<METHOD-NAME>",
417
+ start_time="<START_TIME>"
418
+ end_time="<END_TIME>"
419
+ )
420
+
421
+ print(f"My data: {tabular_data}")
422
+
423
+ Args:
424
+ part_id (str): The ID of the part that owns the data.
425
+ resource_name (str): The name of the requested resource that captured the data.
426
+ resource_subtype (str): The subtype of the requested resource that captured the data.
427
+ method_name (str): The data capture method name.
428
+ start_time (datetime): Optional start time for requesting a specific range of data.
429
+ end_time (datetime): Optional end time for requesting a specific range of data.
430
+
431
+ Returns:
432
+ List[TabularDataPoint]: The unified tabular data and metadata.
433
+
434
+ For more information, see `Data Client API <https://docs.viam.com/appendix/apis/data-client/>`_.
435
+ """
436
+
437
+ interval = CaptureInterval (start = datetime_to_timestamp (start_time ), end = datetime_to_timestamp (end_time ))
438
+ request = ExportTabularDataRequest (
439
+ part_id = part_id , resource_name = resource_name , resource_subtype = resource_subtype , method_name = method_name , interval = interval
440
+ )
441
+ response : List [ExportTabularDataResponse ] = await self ._data_client .ExportTabularData (request , metadata = self ._metadata )
442
+
443
+ return [
444
+ DataClient .TabularDataPoint (
445
+ part_id = resp .part_id ,
446
+ resource_name = resp .resource_name ,
447
+ resource_subtype = resp .resource_subtype ,
448
+ method_name = resp .method_name ,
449
+ time_captured = resp .time_captured .ToDatetime (),
450
+ organization_id = resp .organization_id ,
451
+ location_id = resp .location_id ,
452
+ robot_name = resp .robot_name ,
453
+ robot_id = resp .robot_id ,
454
+ part_name = resp .part_name ,
455
+ method_parameters = struct_to_dict (resp .method_parameters ),
456
+ tags = list (resp .tags ),
457
+ payload = struct_to_dict (resp .payload )
458
+ )
459
+ for resp in response
460
+ ]
461
+
462
+
341
463
async def binary_data_by_filter (
342
464
self ,
343
465
filter : Optional [Filter ] = None ,
0 commit comments