33from typing import Any , List , Mapping , Optional , Tuple
44
55from google .protobuf .struct_pb2 import Struct
6- from google .protobuf .timestamp_pb2 import Timestamp
76from grpclib .client import Channel
87
98from viam import logging
109from viam .proto .app .data import (
1110 AddTagsToBinaryDataByFilterRequest ,
12- AddTagsToBinaryDataByFilterResponse ,
1311 AddTagsToBinaryDataByIDsRequest ,
14- AddTagsToBinaryDataByIDsResponse ,
1512 BinaryDataByFilterRequest ,
1613 BinaryDataByFilterResponse ,
1714 BinaryDataByIDsRequest ,
5148 SensorMetadata ,
5249 UploadMetadata ,
5350)
54- from viam .utils import struct_to_dict
51+ from viam .utils import datetime_to_timestamp , struct_to_dict
5552
5653LOGGER = logging .getLogger (__name__ )
5754
5855
5956class DataClient :
6057 """gRPC client for uploading and retrieving data from app.
6158
62- Constructor is used by `AppClient ` to instantiate relevant service stubs. Calls to `DataClient` methods should be made through
63- `AppClient `.
59+ Constructor is used by `ViamClient ` to instantiate relevant service stubs. Calls to `DataClient` methods should be made through
60+ `ViamClient `.
6461 """
6562
6663 def __init__ (self , channel : Channel , metadata : Mapping [str , str ]):
6764 """Create a `DataClient` that maintains a connection to app.
6865
6966 Args:
70- channel (Channel): Connection to app.
67+ channel (grpclib.client. Channel): Connection to app.
7168 metadata (Mapping[str, str]): Required authorization token to send requests to app.
7269 """
7370 self ._metadata = metadata
@@ -232,7 +229,7 @@ async def add_tags_to_binary_data_by_ids(self, tags: List[str], binary_ids: List
232229 GRPCError: If no `BinaryID` objects or tags are provided.
233230 """
234231 request = AddTagsToBinaryDataByIDsRequest (binary_ids = binary_ids , tags = tags )
235- _ : AddTagsToBinaryDataByIDsResponse = await self ._data_client .AddTagsToBinaryDataByIDs (request , metadata = self ._metadata )
232+ await self ._data_client .AddTagsToBinaryDataByIDs (request , metadata = self ._metadata )
236233
237234 async def add_tags_to_binary_data_by_filter (self , tags : List [str ], filter : Optional [Filter ] = None ) -> None :
238235 """Add tags to binary data.
@@ -247,7 +244,7 @@ async def add_tags_to_binary_data_by_filter(self, tags: List[str], filter: Optio
247244 """
248245 filter = filter if filter else Filter ()
249246 request = AddTagsToBinaryDataByFilterRequest (filter = filter , tags = tags )
250- _ : AddTagsToBinaryDataByFilterResponse = await self ._data_client .AddTagsToBinaryDataByFilter (request , metadata = self ._metadata )
247+ await self ._data_client .AddTagsToBinaryDataByFilter (request , metadata = self ._metadata )
251248
252249 async def remove_tags_from_binary_data_by_ids (self , tags : List [str ], binary_ids : List [BinaryID ]) -> int :
253250 """Remove tags from binary.
@@ -302,7 +299,7 @@ async def tags_by_filter(self, filter: Optional[Filter] = None) -> List[str]:
302299 filter = filter if filter else Filter ()
303300 request = TagsByFilterRequest (filter = filter )
304301 response : TagsByFilterResponse = await self ._data_client .TagsByFilter (request , metadata = self ._metadata )
305- return response .tags
302+ return list ( response .tags )
306303
307304 # TODO: implement
308305 async def add_bounding_box_to_image_by_id (self ):
@@ -325,7 +322,7 @@ async def bounding_box_labels_by_filter(self, filter: Optional[Filter] = None) -
325322 filter = filter if filter else Filter ()
326323 request = BoundingBoxLabelsByFilterRequest (filter = filter )
327324 response : BoundingBoxLabelsByFilterResponse = await self ._data_client .BoundingBoxLabelsByFilter (request , metadata = self ._metadata )
328- return response .labels
325+ return list ( response .labels )
329326
330327 async def binary_data_capture_upload (
331328 self ,
@@ -360,8 +357,8 @@ async def binary_data_capture_upload(
360357 sensor_contents = SensorData (
361358 metadata = (
362359 SensorMetadata (
363- time_requested = self . datetime_to_timestamp (data_request_times [0 ]) if data_request_times [0 ] else None ,
364- time_received = self . datetime_to_timestamp (data_request_times [1 ]) if data_request_times [1 ] else None ,
360+ time_requested = datetime_to_timestamp (data_request_times [0 ]) if data_request_times [0 ] else None ,
361+ time_received = datetime_to_timestamp (data_request_times [1 ]) if data_request_times [1 ] else None ,
365362 )
366363 if data_request_times
367364 else None
@@ -375,12 +372,10 @@ async def binary_data_capture_upload(
375372 component_name = component_name ,
376373 method_name = method_name ,
377374 type = DataType .DATA_TYPE_BINARY_SENSOR ,
378- file_name = None , # Not used in app.
379375 method_parameters = method_parameters ,
380- file_extension = None , # Will be stored as empty string "".
381376 tags = tags ,
382377 )
383- _ : DataCaptureUploadResponse = await self ._data_capture_upload (metadata = metadata , sensor_contents = [sensor_contents ])
378+ await self ._data_capture_upload (metadata = metadata , sensor_contents = [sensor_contents ])
384379
385380 async def tabular_data_capture_upload (
386381 self ,
@@ -418,7 +413,7 @@ async def tabular_data_capture_upload(
418413 AssertionError: If a list of `Timestamp` objects is provided and its length does not match the length of the list of tabular
419414 data.
420415 """
421- sensor_contents = [None ] * len (tabular_data )
416+ sensor_contents = [SensorData () ] * len (tabular_data )
422417 if data_request_times :
423418 assert len (data_request_times ) == len (tabular_data )
424419
@@ -428,8 +423,8 @@ async def tabular_data_capture_upload(
428423 sensor_contents [i ] = SensorData (
429424 metadata = (
430425 SensorMetadata (
431- time_requested = self . datetime_to_timestamp (data_request_times [i ][0 ]) if data_request_times [i ][0 ] else None ,
432- time_received = self . datetime_to_timestamp (data_request_times [i ][1 ]) if data_request_times [i ][1 ] else None ,
426+ time_requested = datetime_to_timestamp (data_request_times [i ][0 ]) if data_request_times [i ][0 ] else None ,
427+ time_received = datetime_to_timestamp (data_request_times [i ][1 ]) if data_request_times [i ][1 ] else None ,
433428 )
434429 if data_request_times [i ]
435430 else None
@@ -445,12 +440,10 @@ async def tabular_data_capture_upload(
445440 component_name = component_name ,
446441 method_name = method_name ,
447442 type = DataType .DATA_TYPE_TABULAR_SENSOR ,
448- file_name = None , # Not used in app.
449443 method_parameters = method_parameters ,
450- file_extension = None , # Will be stored as empty string "".
451444 tags = tags ,
452445 )
453- _ : DataCaptureUploadResponse = await self ._data_capture_upload (metadata = metadata , sensor_contents = sensor_contents )
446+ await self ._data_capture_upload (metadata = metadata , sensor_contents = sensor_contents )
454447
455448 async def _data_capture_upload (self , metadata : UploadMetadata , sensor_contents : List [SensorData ]) -> DataCaptureUploadResponse :
456449 request = DataCaptureUploadRequest (metadata = metadata , sensor_contents = sensor_contents )
@@ -491,16 +484,16 @@ async def file_upload(
491484 """
492485 metadata = UploadMetadata (
493486 part_id = part_id ,
494- component_type = component_type ,
495- component_name = component_name ,
496- method_name = method_name ,
487+ component_type = component_type if component_type else "" ,
488+ component_name = component_name if component_name else "" ,
489+ method_name = method_name if method_name else "" ,
497490 type = DataType .DATA_TYPE_FILE ,
498- file_name = file_name ,
491+ file_name = file_name if file_name else "" ,
499492 method_parameters = method_parameters ,
500- file_extension = file_extension ,
493+ file_extension = file_extension if file_extension else "" ,
501494 tags = tags ,
502495 )
503- _ : FileUploadResponse = await self ._file_upload (metadata = metadata , file_contents = FileData (data = data ))
496+ await self ._file_upload (metadata = metadata , file_contents = FileData (data = data if data else bytes () ))
504497
505498 async def file_upload_from_path (
506499 self ,
@@ -539,40 +532,27 @@ async def file_upload_from_path(
539532
540533 metadata = UploadMetadata (
541534 part_id = part_id ,
542- component_type = component_type ,
543- component_name = component_name ,
544- method_name = method_name ,
535+ component_type = component_type if component_type else "" ,
536+ component_name = component_name if component_name else "" ,
537+ method_name = method_name if method_name else "" ,
545538 type = DataType .DATA_TYPE_FILE ,
546539 file_name = file_name ,
547540 method_parameters = method_parameters ,
548- file_extension = file_extension ,
541+ file_extension = file_extension if file_extension else "" ,
549542 tags = tags ,
550543 )
551- _ : FileUploadResponse = await self ._file_upload (metadata = metadata , file_contents = FileData (data = data ))
544+ await self ._file_upload (metadata = metadata , file_contents = FileData (data = data ))
552545
553546 async def _file_upload (self , metadata : UploadMetadata , file_contents : FileData ) -> FileUploadResponse :
554547 request_metadata = FileUploadRequest (metadata = metadata )
555548 request_file_contents = FileUploadRequest (file_contents = file_contents )
556549 async with self ._data_sync_client .FileUpload .open (metadata = self ._metadata ) as stream :
557550 await stream .send_message (request_metadata )
558551 await stream .send_message (request_file_contents , end = True )
559- response : FileUploadResponse = await stream .recv_message ()
552+ response = await stream .recv_message ()
553+ assert response is not None
560554 return response
561555
562- @staticmethod
563- def datetime_to_timestamp (dt : datetime ) -> Timestamp :
564- """Convert a Python native `datetime` into a `Timestamp`.
565-
566- Args:
567- dt (datetime.datetime): A `datetime` object. UTC is assumed in the conversion if the object is naive to timezone information.
568-
569- Returns:
570- google.protobuf.timestamp_pb2.Timestamp: The `Timestamp` object.
571- """
572- timestamp = Timestamp ()
573- timestamp .FromDatetime (dt )
574- return timestamp
575-
576556 @staticmethod
577557 def create_filter (
578558 component_name : Optional [str ] = None ,
@@ -613,20 +593,20 @@ def create_filter(
613593 viam.proto.app.data.Filter: The `Filter` object.
614594 """
615595 return Filter (
616- component_name = component_name ,
617- component_type = component_type ,
618- method = method ,
619- robot_name = robot_name ,
620- robot_id = robot_id ,
621- part_name = part_name ,
622- part_id = part_id ,
596+ component_name = component_name if component_name else "" ,
597+ component_type = component_type if component_type else "" ,
598+ method = method if method else "" ,
599+ robot_name = robot_name if robot_name else "" ,
600+ robot_id = robot_id if robot_id else "" ,
601+ part_name = part_name if part_name else "" ,
602+ part_id = part_id if part_id else "" ,
623603 location_ids = location_ids ,
624604 organization_ids = organization_ids ,
625605 mime_type = mime_type ,
626606 interval = (
627607 CaptureInterval (
628- start = DataClient . datetime_to_timestamp (start_time ) if start_time else None ,
629- end = DataClient . datetime_to_timestamp (end_time ) if end_time else None ,
608+ start = datetime_to_timestamp (start_time ) if start_time else None ,
609+ end = datetime_to_timestamp (end_time ) if end_time else None ,
630610 )
631611 )
632612 if start_time and end_time
0 commit comments