From 52c0104cbf1d3f9cc6de2f2fedc04d486fed83ec Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 28 Jul 2023 16:28:21 -0700 Subject: [PATCH 01/77] adding basic handling for inspection_id --- src/groundlight/client.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index e0250bcf..8da8fe82 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -170,6 +170,7 @@ def submit_image_query( detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, + inspection_id: Optional[str] = None, ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` @@ -189,26 +190,30 @@ def submit_image_query( image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) - raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) + raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio, inspection_id=inspection_id) image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) if wait: threshold = self.get_detector(detector).confidence_threshold - image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) + image_query = self.wait_for_confident_result( + image_query, confidence_threshold=threshold, timeout_sec=wait + ) return self._fixup_image_query(image_query) def wait_for_confident_result( self, - image_query: ImageQuery, + image_query: Union[ImageQuery, str], confidence_threshold: float, timeout_sec: float = 30.0, ) -> ImageQuery: """Waits for an image query result's confidence level to reach the specified value. Currently this is done by polling with an exponential back-off. - :param image_query: An ImageQuery object to poll + :param image_query: An ImageQuery object or and image_query id (str). :param confidence_threshold: The minimum confidence level required to return before the timeout. :param timeout_sec: The maximum number of seconds to wait. """ - # TODO: Add support for ImageQuery id instead of object. + # Convert from image_query id to ImageQuery if needed. + image_query = self.get_image_query(image_query) if isinstance(image_query, str) else image_query + start_time = time.time() next_delay = self.POLLING_INITIAL_DELAY target_delay = 0.0 From 219ce7af9ad2ef0b0c576db9f52c6449d6e56525 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 28 Jul 2023 23:29:20 +0000 Subject: [PATCH 02/77] Automatically reformatting code --- src/groundlight/client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 8da8fe82..2f2488db 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -190,13 +190,13 @@ def submit_image_query( image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) - raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio, inspection_id=inspection_id) + raw_image_query = self.image_queries_api.submit_image_query( + detector_id=detector_id, body=image_bytesio, inspection_id=inspection_id + ) image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) if wait: threshold = self.get_detector(detector).confidence_threshold - image_query = self.wait_for_confident_result( - image_query, confidence_threshold=threshold, timeout_sec=wait - ) + image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) return self._fixup_image_query(image_query) def wait_for_confident_result( From 31ad0fbe1ff883c7302dc9c0d41fc1468769dd39 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Mon, 31 Jul 2023 17:47:01 -0700 Subject: [PATCH 03/77] added inspection_id support --- src/groundlight/client.py | 46 ++++++++++++++++++++++++++++------ src/groundlight/internalapi.py | 25 ++++++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 8da8fe82..201ecd4f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -21,6 +21,8 @@ ) from groundlight.optional_imports import Image, np +import base64 + logger = logging.getLogger("groundlight.sdk") @@ -190,13 +192,21 @@ def submit_image_query( image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) - raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio, inspection_id=inspection_id) - image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) + if inspection_id is None: + raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) + image_query_dict = raw_image_query.to_dict() + else: + image_query_dict = self.api_client._submit_image_query_with_inspection( + detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id + ) + + print("image_query_dict", type(image_query_dict), image_query_dict) + + # image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) + image_query = ImageQuery.parse_obj(image_query_dict) if wait: threshold = self.get_detector(detector).confidence_threshold - image_query = self.wait_for_confident_result( - image_query, confidence_threshold=threshold, timeout_sec=wait - ) + image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) return self._fixup_image_query(image_query) def wait_for_confident_result( @@ -207,11 +217,11 @@ def wait_for_confident_result( ) -> ImageQuery: """Waits for an image query result's confidence level to reach the specified value. Currently this is done by polling with an exponential back-off. - :param image_query: An ImageQuery object or and image_query id (str). + :param image_query: An ImageQuery object or an image_query id (str). :param confidence_threshold: The minimum confidence level required to return before the timeout. :param timeout_sec: The maximum number of seconds to wait. """ - # Convert from image_query id to ImageQuery if needed. + # Convert from image_query_id to ImageQuery if needed. image_query = self.get_image_query(image_query) if isinstance(image_query, str) else image_query start_time = time.time() @@ -254,3 +264,25 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str api_label = convert_display_label_to_internal(image_query_id, label) return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access + + def start_inspection( + self, + ) -> str: + """Starts an inspection report and returns the id of the inspection.""" + pass + + def add_or_update_inspection_metadata( + self, + inspection_id: str, + user_provided_key: str, + user_provided_value: str, + ) -> bool: + """Starts a new inspection and returns the inspection id.""" + pass + + def stop_inspection( + self, + inspection_id: str, + ) -> None: + """Takes an inspection id and stops the inspection.""" + pass diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 968baebf..f668d3c0 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -7,6 +7,8 @@ from typing import Callable, Optional from urllib.parse import urlsplit, urlunsplit +from io import BytesIO + import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException @@ -203,6 +205,29 @@ def _add_label(self, image_query_id: str, label: str) -> dict: return response.json() + @RequestsRetryDecorator() + def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> dict: + start_time = time.time() + url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}&send_notification=False" + + headers = self._headers() + headers["Content-Type"] = "image/jpeg" + + response = requests.request("POST", url, headers=headers, data=image.read()) + + elapsed = 1000 * (time.time() - start_time) + logger.debug(f"Call to ImageQuery._submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}") + + if not is_ok(response.status_code): + logger.info(response) + raise InternalApiError( + status=response.status_code, + reason=f"Error submitting image query with inspection ID on detector {detector_id}", + http_resp=response, + ) + + return response.json() + @RequestsRetryDecorator() def _get_detector_by_name(self, name: str) -> Detector: """Get a detector by name. For now, we use the list detectors API directly. From 383a588dc3d309910c1e253e94889c2f19f5414a Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 1 Aug 2023 00:54:46 +0000 Subject: [PATCH 04/77] Automatically reformatting code --- src/groundlight/client.py | 2 -- src/groundlight/internalapi.py | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 015e0f1e..3b96bad0 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -21,8 +21,6 @@ ) from groundlight.optional_imports import Image, np -import base64 - logger = logging.getLogger("groundlight.sdk") diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index f668d3c0..980f965c 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -7,8 +7,6 @@ from typing import Callable, Optional from urllib.parse import urlsplit, urlunsplit -from io import BytesIO - import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException @@ -216,7 +214,9 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio response = requests.request("POST", url, headers=headers, data=image.read()) elapsed = 1000 * (time.time() - start_time) - logger.debug(f"Call to ImageQuery._submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}") + logger.debug( + f"Call to ImageQuery._submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}" + ) if not is_ok(response.status_code): logger.info(response) From 4da4bb60056a62e75901d3578ecbe399f3b26ee9 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 14:45:58 -0700 Subject: [PATCH 05/77] adding more methods for inspections --- src/groundlight/client.py | 36 +++++------- src/groundlight/internalapi.py | 102 ++++++++++++++++++++++++++++----- 2 files changed, 102 insertions(+), 36 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 3b96bad0..b5e63e38 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -183,6 +183,7 @@ def submit_image_query( Any binary format must be JPEG-encoded already. Any pixel format will get converted to JPEG at high quality before sending to service. :param wait: How long to wait (in seconds) for a confident answer. + :param inspection_id: The ID of the inspection (str) to associate with the image query. """ if wait is None: wait = self.DEFAULT_WAIT @@ -190,16 +191,19 @@ def submit_image_query( image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) + # Submit Image Query + # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) + # However, our autogenerated code does not support inspection_id, so we use the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) image_query_dict = raw_image_query.to_dict() + image_query = ImageQuery.parse_obj(image_query_dict) else: - image_query_dict = self.api_client._submit_image_query_with_inspection( + json_response = self.api_client._submit_image_query_with_inspection( detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id ) + image_query = self.get_image_query(json_response["id"]) - # image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) - image_query = ImageQuery.parse_obj(image_query_dict) if wait: threshold = self.get_detector(detector).confidence_threshold image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) @@ -261,24 +265,14 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access - def start_inspection( - self, - ) -> str: + def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" - pass + return self.api_client._start_inspection() - def add_or_update_inspection_metadata( - self, - inspection_id: str, - user_provided_key: str, - user_provided_value: str, - ) -> bool: - """Starts a new inspection and returns the inspection id.""" - pass - - def stop_inspection( - self, - inspection_id: str, - ) -> None: + def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> bool: + """Add/update inspection metadata with the user_provided_key and user_provided_value.""" + return self.api_client._add_or_update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + + def stop_inspection(self, inspection_id: str) -> bool: """Takes an inspection id and stops the inspection.""" - pass + return self.api_client._stop_inspection(inspection_id) \ No newline at end of file diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 980f965c..f5ac4517 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -7,6 +7,9 @@ from typing import Callable, Optional from urllib.parse import urlsplit, urlunsplit +from io import BytesIO +import json + import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException @@ -203,6 +206,29 @@ def _add_label(self, image_query_id: str, label: str) -> dict: return response.json() + @RequestsRetryDecorator() + def _get_detector_by_name(self, name: str) -> Detector: + """Get a detector by name. For now, we use the list detectors API directly. + + TODO: Properly model this in the API, and generate SDK code for it. + """ + url = f"{self.configuration.host}/v1/detectors?name={name}" + headers = self._headers() + response = requests.request("GET", url, headers=headers) + + if not is_ok(response.status_code): + raise InternalApiError(status=response.status_code, http_resp=response) + + parsed = response.json() + + if parsed["count"] == 0: + raise NotFoundError(f"Detector with name={name} not found.") + if parsed["count"] > 1: + raise RuntimeError( + f"We found multiple ({parsed['count']}) detectors with the same name. This shouldn't happen.", + ) + return Detector.parse_obj(parsed["results"][0]) + @RequestsRetryDecorator() def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> dict: start_time = time.time() @@ -222,31 +248,77 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio logger.info(response) raise InternalApiError( status=response.status_code, - reason=f"Error submitting image query with inspection ID on detector {detector_id}", + reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", http_resp=response, ) return response.json() @RequestsRetryDecorator() - def _get_detector_by_name(self, name: str) -> Detector: - """Get a detector by name. For now, we use the list detectors API directly. + def _start_inspection(self) -> str: + """Start an inspection, return the ID.""" + url = f"{self.configuration.host}/inspections" + + headers = self._headers() + + response = requests.request("POST", url, headers=headers, json={}) + + return response.json()["id"] + + @RequestsRetryDecorator() + def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> bool: + """Add/update inspection metadata with the user_provided_key and user_provided_value. + + Inspections store metadata in two ways: + 1) At the top level of the inspection with user_provided_id_key and user_provided_id_value. This is a + kind of "primary" piece of metadata for the inspection. Only one key/value pair is allowed at this level. + 2) In the user_metadata field as a dictionary. Multiple key/value pairs are allowed at this level. + + The first piece of metadata presented to an inspection will be assumed to be the user_provided_id_key and + user_provided_id_value. All subsequent pieces metadata will be stored in the user_metadata field. - TODO: Properly model this in the API, and generate SDK code for it. """ - url = f"{self.configuration.host}/v1/detectors?name={name}" + url = f"{self.configuration.host}/inspections/{inspection_id}" + headers = self._headers() + + # Get inspection to see if user_provided_id_key is set response = requests.request("GET", url, headers=headers) - if not is_ok(response.status_code): - raise InternalApiError(status=response.status_code, http_resp=response) + # Return false if we can't get the inspection. An invalid inspection_id might have + # been passed in. + if response.status_code != 200: + return False - parsed = response.json() + payload = {} - if parsed["count"] == 0: - raise NotFoundError(f"Detector with name={name} not found.") - if parsed["count"] > 1: - raise RuntimeError( - f"We found multiple ({parsed['count']}) detectors with the same name. This shouldn't happen.", - ) - return Detector.parse_obj(parsed["results"][0]) + # Set the user_provided_id_key and user_provided_id_value if they are not set. + response_json = response.json() + if not response_json["user_provided_id_key"]: + payload["user_provided_id_key"] = user_provided_key + payload["user_provided_id_value"] = user_provided_value + + # Get the existing keys and values in user_metadata (if any) so that we don't overwrite them + metadata = response_json["user_metadata"] + if not metadata: + metadata = {} + + # Submit the new metadata + metadata[user_provided_key] = user_provided_value + payload["user_metadata_json"] = json.dumps(metadata) + response = requests.request("PATCH", url, headers=headers, json=payload) + + return response.status_code == 200 + + @RequestsRetryDecorator() + def _stop_inspection(self, inspection_id: str) -> bool: + """Stop an inspection and returns a response code.""" + url = f"{self.configuration.host}/inspections/{inspection_id}" + + headers = self._headers() + + payload = {"status": "COMPLETE"} + + response = requests.request("PATCH", url, headers=headers, json=payload) + + return response.status_code == 200 \ No newline at end of file From c1ef1ee201a5575db729175bdc449ddda1d36916 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 1 Aug 2023 21:47:02 +0000 Subject: [PATCH 06/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index b5e63e38..34b90558 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -275,4 +275,4 @@ def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_k def stop_inspection(self, inspection_id: str) -> bool: """Takes an inspection id and stops the inspection.""" - return self.api_client._stop_inspection(inspection_id) \ No newline at end of file + return self.api_client._stop_inspection(inspection_id) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index f5ac4517..0024ee8f 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -1,3 +1,4 @@ +import json import logging import os import random @@ -7,9 +8,6 @@ from typing import Callable, Optional from urllib.parse import urlsplit, urlunsplit -from io import BytesIO -import json - import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException @@ -321,4 +319,4 @@ def _stop_inspection(self, inspection_id: str) -> bool: response = requests.request("PATCH", url, headers=headers, json=payload) - return response.status_code == 200 \ No newline at end of file + return response.status_code == 200 From c3ee6bac30f19c29d14e697dd4c0660017bb3fb4 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 14:49:02 -0700 Subject: [PATCH 07/77] trivial change to comment --- src/groundlight/internalapi.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index f5ac4517..76412a29 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -285,8 +285,7 @@ def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_k # Get inspection to see if user_provided_id_key is set response = requests.request("GET", url, headers=headers) - # Return false if we can't get the inspection. An invalid inspection_id might have - # been passed in. + # Return false if we can't get the inspection. An invalid inspection_id might have been passed in. if response.status_code != 200: return False From 4299baf23f2b90fd5bce6993234a2b37223566e9 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 16:47:19 -0700 Subject: [PATCH 08/77] adding method to update a detector's confidence threshold --- src/groundlight/client.py | 23 ++++++++---- src/groundlight/internalapi.py | 67 ++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 34b90558..8a0def07 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -199,10 +199,10 @@ def submit_image_query( image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: - json_response = self.api_client._submit_image_query_with_inspection( + iq_id = self.api_client._submit_image_query_with_inspection( detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id ) - image_query = self.get_image_query(json_response["id"]) + image_query = self.get_image_query(iq_id) if wait: threshold = self.get_detector(detector).confidence_threshold @@ -269,10 +269,17 @@ def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" return self.api_client._start_inspection() - def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> bool: + def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - return self.api_client._add_or_update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - - def stop_inspection(self, inspection_id: str) -> bool: - """Takes an inspection id and stops the inspection.""" - return self.api_client._stop_inspection(inspection_id) + self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + + def stop_inspection(self, inspection_id: str) -> None: + """Stops an inspection and raises an exception if the response from the server does not indicate success.""" + self.api_client._stop_inspection(inspection_id) + + def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: + """Updates the confidence threshold of a detector. + :param detector: The Detector object or string id of a detector like `det_2TB2BnYKcPAXkJGcr30Q3gZT7uc` + :param confidence_threshold: The new confidence threshold. + """ + self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 92568f67..d75036b3 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -20,6 +20,11 @@ class NotFoundError(Exception): pass +class InspectionError(Exception): + pass + +class UpdateDetectorError(Exception): + pass def sanitize_endpoint_url(endpoint: Optional[str] = None) -> str: """Takes a URL for an endpoint, and returns a "sanitized" version of it. @@ -141,7 +146,6 @@ def decorated(*args, **kwargs): return decorated - class GroundlightApiClient(ApiClient): """Subclassing the OpenAPI-generated ApiClient to add a bit of custom functionality. Not crazy about using polymorphism, but this is simpler than modifying the moustache @@ -228,7 +232,10 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> dict: + def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: + """Submits an image query to the API, and returns the ID of the image query. + The image query will be associated to the inspection_id provided. + """ start_time = time.time() url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}&send_notification=False" @@ -250,21 +257,26 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio http_resp=response, ) - return response.json() + return response.json()['id'] @RequestsRetryDecorator() def _start_inspection(self) -> str: - """Start an inspection, return the ID.""" + """Starts an inspection, returns the ID.""" url = f"{self.configuration.host}/inspections" headers = self._headers() response = requests.request("POST", url, headers=headers, json={}) + if not is_ok(response.status_code): + raise InspectionError( + f'Error starting inspection. Status code: {response.status_code}' + ) + return response.json()["id"] @RequestsRetryDecorator() - def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> bool: + def _update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value. Inspections store metadata in two ways: @@ -283,9 +295,10 @@ def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_k # Get inspection to see if user_provided_id_key is set response = requests.request("GET", url, headers=headers) - # Return false if we can't get the inspection. An invalid inspection_id might have been passed in. - if response.status_code != 200: - return False + if not is_ok(response.status_code): + raise InspectionError( + f'Error getting inspection details for inspection {inspection_id}. Status code: {response.status_code}' + ) payload = {} @@ -305,11 +318,14 @@ def _add_or_update_inspection_metadata(self, inspection_id: str, user_provided_k payload["user_metadata_json"] = json.dumps(metadata) response = requests.request("PATCH", url, headers=headers, json=payload) - return response.status_code == 200 + if not is_ok(response.status_code): + raise InspectionError( + f'Error updating inspection metadata on inspection {inspection_id}. Status code: {response.status_code}' + ) @RequestsRetryDecorator() - def _stop_inspection(self, inspection_id: str) -> bool: - """Stop an inspection and returns a response code.""" + def _stop_inspection(self, inspection_id: str) -> None: + """Stops an inspection and raises an exception if the response from the server does not indicate success.""" url = f"{self.configuration.host}/inspections/{inspection_id}" headers = self._headers() @@ -317,5 +333,32 @@ def _stop_inspection(self, inspection_id: str) -> bool: payload = {"status": "COMPLETE"} response = requests.request("PATCH", url, headers=headers, json=payload) + + if not is_ok(response.status_code): + raise InspectionError( + f'Error stopping inspection {inspection_id}. Status code: {response.status_code}' + ) + + @RequestsRetryDecorator() + def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: + """Updates the confidence threshold of a detector. + """ + + # The API does not validate the confidence threshold, so we will validate it here and raise an exception if necessary + if confidence_threshold < 0 or confidence_threshold > 1: + raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") + + url = f"{self.configuration.host}/predictors/{detector_id}" - return response.status_code == 200 + headers = self._headers() + + payload = { + "confidence_threshold": confidence_threshold + } + + response = requests.request("PATCH", url, headers=headers, json=payload) + + if not is_ok(response.status_code): + raise UpdateDetectorError( + f'Error updating detector {detector_id}. Status code: {response.status_code}' + ) \ No newline at end of file From 3668235923afb88b98c91612adb3d746d10fed77 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 1 Aug 2023 23:48:30 +0000 Subject: [PATCH 09/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 39 +++++++++++++++------------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 8a0def07..79524348 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -276,7 +276,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" self.api_client._stop_inspection(inspection_id) - + def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector. :param detector: The Detector object or string id of a detector like `det_2TB2BnYKcPAXkJGcr30Q3gZT7uc` diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index d75036b3..6045febb 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -20,12 +20,15 @@ class NotFoundError(Exception): pass + class InspectionError(Exception): pass + class UpdateDetectorError(Exception): pass + def sanitize_endpoint_url(endpoint: Optional[str] = None) -> str: """Takes a URL for an endpoint, and returns a "sanitized" version of it. Currently the production API path must be exactly "/device-api". @@ -146,6 +149,7 @@ def decorated(*args, **kwargs): return decorated + class GroundlightApiClient(ApiClient): """Subclassing the OpenAPI-generated ApiClient to add a bit of custom functionality. Not crazy about using polymorphism, but this is simpler than modifying the moustache @@ -233,7 +237,7 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: - """Submits an image query to the API, and returns the ID of the image query. + """Submits an image query to the API, and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ start_time = time.time() @@ -257,7 +261,7 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio http_resp=response, ) - return response.json()['id'] + return response.json()["id"] @RequestsRetryDecorator() def _start_inspection(self) -> str: @@ -269,9 +273,7 @@ def _start_inspection(self) -> str: response = requests.request("POST", url, headers=headers, json={}) if not is_ok(response.status_code): - raise InspectionError( - f'Error starting inspection. Status code: {response.status_code}' - ) + raise InspectionError(f"Error starting inspection. Status code: {response.status_code}") return response.json()["id"] @@ -297,7 +299,7 @@ def _update_inspection_metadata(self, inspection_id: str, user_provided_key, use if not is_ok(response.status_code): raise InspectionError( - f'Error getting inspection details for inspection {inspection_id}. Status code: {response.status_code}' + f"Error getting inspection details for inspection {inspection_id}. Status code: {response.status_code}" ) payload = {} @@ -320,7 +322,7 @@ def _update_inspection_metadata(self, inspection_id: str, user_provided_key, use if not is_ok(response.status_code): raise InspectionError( - f'Error updating inspection metadata on inspection {inspection_id}. Status code: {response.status_code}' + f"Error updating inspection metadata on inspection {inspection_id}. Status code: {response.status_code}" ) @RequestsRetryDecorator() @@ -333,32 +335,25 @@ def _stop_inspection(self, inspection_id: str) -> None: payload = {"status": "COMPLETE"} response = requests.request("PATCH", url, headers=headers, json=payload) - + if not is_ok(response.status_code): - raise InspectionError( - f'Error stopping inspection {inspection_id}. Status code: {response.status_code}' - ) - + raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") + @RequestsRetryDecorator() def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: - """Updates the confidence threshold of a detector. - """ + """Updates the confidence threshold of a detector.""" # The API does not validate the confidence threshold, so we will validate it here and raise an exception if necessary if confidence_threshold < 0 or confidence_threshold > 1: raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") - + url = f"{self.configuration.host}/predictors/{detector_id}" headers = self._headers() - payload = { - "confidence_threshold": confidence_threshold - } + payload = {"confidence_threshold": confidence_threshold} response = requests.request("PATCH", url, headers=headers, json=payload) - + if not is_ok(response.status_code): - raise UpdateDetectorError( - f'Error updating detector {detector_id}. Status code: {response.status_code}' - ) \ No newline at end of file + raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") From 5deacc33d05916880d224f3a649cf39b70241457 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 16:49:02 -0700 Subject: [PATCH 10/77] fixing docstring --- src/groundlight/client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 8a0def07..e9ecd594 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -279,7 +279,5 @@ def stop_inspection(self, inspection_id: str) -> None: def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector. - :param detector: The Detector object or string id of a detector like `det_2TB2BnYKcPAXkJGcr30Q3gZT7uc` - :param confidence_threshold: The new confidence threshold. """ self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) From 5dd62c0261fd6a41d6656280555527c46d4814e6 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 1 Aug 2023 23:50:19 +0000 Subject: [PATCH 11/77] Automatically reformatting code --- src/groundlight/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 135338f5..8aee504f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -278,6 +278,5 @@ def stop_inspection(self, inspection_id: str) -> None: self.api_client._stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: - """Updates the confidence threshold of a detector. - """ + """Updates the confidence threshold of a detector.""" self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) From 48f96ef8e18556c344fc97c00969686f24b1c1a9 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 16:51:35 -0700 Subject: [PATCH 12/77] fixing a comment --- src/groundlight/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 135338f5..4356982d 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -193,7 +193,7 @@ def submit_image_query( # Submit Image Query # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not support inspection_id, so we use the private API client instead. + # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) image_query_dict = raw_image_query.to_dict() From bfe562077e9cd00e36d8be0fbcc02212afc74210 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 17:27:03 -0700 Subject: [PATCH 13/77] fixing some linter issues --- src/groundlight/client.py | 9 +++++---- src/groundlight/internalapi.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 735d4af4..d133f2c8 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -193,7 +193,8 @@ def submit_image_query( # Submit Image Query # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use the private API client instead. + # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use + # the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) image_query_dict = raw_image_query.to_dict() @@ -271,12 +272,12 @@ def start_inspection(self) -> str: def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client._stop_inspection(inspection_id) + self.api_client._stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) + self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 6045febb..98c00f2a 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -236,12 +236,15 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: + def _submit_image_query_with_inspection(self, + detector_id: str, + image, inspection_id: str) -> str: """Submits an image query to the API, and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ start_time = time.time() - url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}&send_notification=False" + url = (f"{self.configuration.host}/posichecks?inspection_id={inspection_id}" + f"&predictor_id={detector_id}") headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -340,7 +343,9 @@ def _stop_inspection(self, inspection_id: str) -> None: raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") @RequestsRetryDecorator() - def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: + def _update_detector_confidence_threshold(self, + detector_id: str, + confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" # The API does not validate the confidence threshold, so we will validate it here and raise an exception if necessary From c13c816e5694ad1a76f652842b8b7dba660fbf67 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 00:27:56 +0000 Subject: [PATCH 14/77] Automatically reformatting code --- src/groundlight/client.py | 8 ++++---- src/groundlight/internalapi.py | 11 +++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index d133f2c8..df829be9 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -193,7 +193,7 @@ def submit_image_query( # Submit Image Query # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use + # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use # the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) @@ -272,12 +272,12 @@ def start_inspection(self) -> str: def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client._stop_inspection(inspection_id) + self.api_client._stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) + self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 98c00f2a..c9bb0277 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -236,15 +236,12 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def _submit_image_query_with_inspection(self, - detector_id: str, - image, inspection_id: str) -> str: + def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: """Submits an image query to the API, and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ start_time = time.time() - url = (f"{self.configuration.host}/posichecks?inspection_id={inspection_id}" - f"&predictor_id={detector_id}") + url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -343,9 +340,7 @@ def _stop_inspection(self, inspection_id: str) -> None: raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") @RequestsRetryDecorator() - def _update_detector_confidence_threshold(self, - detector_id: str, - confidence_threshold: float) -> None: + def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" # The API does not validate the confidence threshold, so we will validate it here and raise an exception if necessary From 2ab16d48e49cf909e371f3800db49200bf444273 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 17:33:37 -0700 Subject: [PATCH 15/77] fixing another linter issue --- src/groundlight/internalapi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 98c00f2a..9f5301e6 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -348,7 +348,8 @@ def _update_detector_confidence_threshold(self, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - # The API does not validate the confidence threshold, so we will validate it here and raise an exception if necessary + # The API does not validate the confidence threshold, + # so we will validate it here and raise an exception if necessary if confidence_threshold < 0 or confidence_threshold > 1: raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") From 9901478efef9551700257bcccd7adca771c3f988 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 00:34:37 +0000 Subject: [PATCH 16/77] Automatically reformatting code --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 5d3131aa..a2686d5f 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -343,7 +343,7 @@ def _stop_inspection(self, inspection_id: str) -> None: def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - # The API does not validate the confidence threshold, + # The API does not validate the confidence threshold, # so we will validate it here and raise an exception if necessary if confidence_threshold < 0 or confidence_threshold > 1: raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") From 64dc8e86fbb144b9b501a2871f4401da3b01f40a Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 17:36:39 -0700 Subject: [PATCH 17/77] another linter issue --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 5d3131aa..a2686d5f 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -343,7 +343,7 @@ def _stop_inspection(self, inspection_id: str) -> None: def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - # The API does not validate the confidence threshold, + # The API does not validate the confidence threshold, # so we will validate it here and raise an exception if necessary if confidence_threshold < 0 or confidence_threshold > 1: raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") From 0d0e049084919a55de379d876bfd064f6fd5d94c Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 17:52:59 -0700 Subject: [PATCH 18/77] linter issue --- src/groundlight/client.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index df829be9..cc98210f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -268,16 +268,19 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" - return self.api_client._start_inspection() + return self.api_client._start_inspection() # pylint: disable=protected-access def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + self.api_client._update_inspection_metadata(inspection_id, + user_provided_key, + user_provided_value) # pylint: disable=protected-access def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client._stop_inspection(inspection_id) + self.api_client._stop_inspection(inspection_id) # pylint: disable=protected-access def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) + self.api_client._update_detector_confidence_threshold(detector_id, + confidence_threshold) # pylint: disable=protected-access From 431a0fb02e2ea1ae820df8588ba8560e61e9a1f6 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 00:53:54 +0000 Subject: [PATCH 19/77] Automatically reformatting code --- src/groundlight/client.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index cc98210f..67c5171e 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -268,19 +268,20 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" - return self.api_client._start_inspection() # pylint: disable=protected-access + return self.api_client._start_inspection() # pylint: disable=protected-access def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client._update_inspection_metadata(inspection_id, - user_provided_key, - user_provided_value) # pylint: disable=protected-access + self.api_client._update_inspection_metadata( + inspection_id, user_provided_key, user_provided_value + ) # pylint: disable=protected-access def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client._stop_inspection(inspection_id) # pylint: disable=protected-access + self.api_client._stop_inspection(inspection_id) # pylint: disable=protected-access def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - self.api_client._update_detector_confidence_threshold(detector_id, - confidence_threshold) # pylint: disable=protected-access + self.api_client._update_detector_confidence_threshold( + detector_id, confidence_threshold + ) # pylint: disable=protected-access From 33f0b1b58d1f7686edb7f8107579c7a8bf3aaea0 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 18:02:07 -0700 Subject: [PATCH 20/77] linter issues --- src/groundlight/client.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 67c5171e..74535627 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -268,20 +268,20 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" - return self.api_client._start_inspection() # pylint: disable=protected-access + # pylint: disable=protected-access + return self.api_client._start_inspection() def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client._update_inspection_metadata( - inspection_id, user_provided_key, user_provided_value - ) # pylint: disable=protected-access + # pylint: disable=protected-access + self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client._stop_inspection(inspection_id) # pylint: disable=protected-access + # pylint: disable=protected-access + self.api_client._stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" - self.api_client._update_detector_confidence_threshold( - detector_id, confidence_threshold - ) # pylint: disable=protected-access + # pylint: disable=protected-access + self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) \ No newline at end of file From 5d727b32ba0020762189357a6e0400d30bf60626 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 01:03:08 +0000 Subject: [PATCH 21/77] Automatically reformatting code --- src/groundlight/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 74535627..c5e0af22 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -269,12 +269,12 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" # pylint: disable=protected-access - return self.api_client._start_inspection() + return self.api_client._start_inspection() def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" # pylint: disable=protected-access - self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" @@ -284,4 +284,4 @@ def stop_inspection(self, inspection_id: str) -> None: def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" # pylint: disable=protected-access - self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) \ No newline at end of file + self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) From 7b60a0593d2f2530ccc2d55a4b377fdfe0e8a8bb Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 1 Aug 2023 18:14:08 -0700 Subject: [PATCH 22/77] linter issues --- src/groundlight/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index c5e0af22..6a3af60c 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -282,6 +282,6 @@ def stop_inspection(self, inspection_id: str) -> None: self.api_client._stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: - """Updates the confidence threshold of a detector.""" + """Updates the confidence threshold of a detector given a detector_id.""" # pylint: disable=protected-access self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) From c2b6a5fa65f50ac77414d8e772efc5d5369aab50 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 09:37:32 -0700 Subject: [PATCH 23/77] changes for linter --- src/groundlight/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 6a3af60c..6a609b73 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -200,6 +200,7 @@ def submit_image_query( image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: + # pylint: disable=protected-access iq_id = self.api_client._submit_image_query_with_inspection( detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id ) From 358b8b2761fc153d6b7ca6efe18346a0004783e6 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 09:57:50 -0700 Subject: [PATCH 24/77] more linter changes --- src/groundlight/client.py | 15 +++++---------- src/groundlight/internalapi.py | 12 ++++++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 6a609b73..f0dec53e 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -200,8 +200,7 @@ def submit_image_query( image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: - # pylint: disable=protected-access - iq_id = self.api_client._submit_image_query_with_inspection( + iq_id = self.api_client.submit_image_query_with_inspection( detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id ) image_query = self.get_image_query(iq_id) @@ -269,20 +268,16 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str def start_inspection(self) -> str: """Starts an inspection report and returns the id of the inspection.""" - # pylint: disable=protected-access - return self.api_client._start_inspection() + return self.api_client.start_inspection() def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - # pylint: disable=protected-access - self.api_client._update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + self.api_client.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - # pylint: disable=protected-access - self.api_client._stop_inspection(inspection_id) + self.api_client.stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector given a detector_id.""" - # pylint: disable=protected-access - self.api_client._update_detector_confidence_threshold(detector_id, confidence_threshold) + self.api_client.update_detector_confidence_threshold(detector_id, confidence_threshold) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index a2686d5f..a18a6c91 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -236,7 +236,7 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def _submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: + def submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: """Submits an image query to the API, and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ @@ -250,7 +250,7 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio elapsed = 1000 * (time.time() - start_time) logger.debug( - f"Call to ImageQuery._submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}" + f"Call to ImageQuery.submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}" ) if not is_ok(response.status_code): @@ -264,7 +264,7 @@ def _submit_image_query_with_inspection(self, detector_id: str, image, inspectio return response.json()["id"] @RequestsRetryDecorator() - def _start_inspection(self) -> str: + def start_inspection(self) -> str: """Starts an inspection, returns the ID.""" url = f"{self.configuration.host}/inspections" @@ -278,7 +278,7 @@ def _start_inspection(self) -> str: return response.json()["id"] @RequestsRetryDecorator() - def _update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: + def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value. Inspections store metadata in two ways: @@ -326,7 +326,7 @@ def _update_inspection_metadata(self, inspection_id: str, user_provided_key, use ) @RequestsRetryDecorator() - def _stop_inspection(self, inspection_id: str) -> None: + def stop_inspection(self, inspection_id: str) -> None: """Stops an inspection and raises an exception if the response from the server does not indicate success.""" url = f"{self.configuration.host}/inspections/{inspection_id}" @@ -340,7 +340,7 @@ def _stop_inspection(self, inspection_id: str) -> None: raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") @RequestsRetryDecorator() - def _update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: + def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector.""" # The API does not validate the confidence threshold, From 7de90a619d9174de225ed30e860dd6753445e18e Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 10:07:29 -0700 Subject: [PATCH 25/77] trivial changes to comments --- src/groundlight/internalapi.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index a18a6c91..a8d22803 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -237,10 +237,9 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: - """Submits an image query to the API, and returns the ID of the image query. + """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ - start_time = time.time() url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" headers = self._headers() @@ -248,11 +247,6 @@ def submit_image_query_with_inspection(self, detector_id: str, image, inspection response = requests.request("POST", url, headers=headers, data=image.read()) - elapsed = 1000 * (time.time() - start_time) - logger.debug( - f"Call to ImageQuery.submit_image_query_with_inspection took {elapsed:.1f}ms response={response.text}" - ) - if not is_ok(response.status_code): logger.info(response) raise InternalApiError( @@ -281,7 +275,7 @@ def start_inspection(self) -> str: def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value. - Inspections store metadata in two ways: + The API stores inspections metadata in two ways: 1) At the top level of the inspection with user_provided_id_key and user_provided_id_value. This is a kind of "primary" piece of metadata for the inspection. Only one key/value pair is allowed at this level. 2) In the user_metadata field as a dictionary. Multiple key/value pairs are allowed at this level. @@ -304,13 +298,13 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user payload = {} - # Set the user_provided_id_key and user_provided_id_value if they are not set. + # Set the user_provided_id_key and user_provided_id_value if they were not previously set. response_json = response.json() if not response_json["user_provided_id_key"]: payload["user_provided_id_key"] = user_provided_key payload["user_provided_id_value"] = user_provided_value - # Get the existing keys and values in user_metadata (if any) so that we don't overwrite them + # Get the existing keys and values in user_metadata (if any) so that we don't overwrite them. metadata = response_json["user_metadata"] if not metadata: metadata = {} @@ -344,7 +338,7 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre """Updates the confidence threshold of a detector.""" # The API does not validate the confidence threshold, - # so we will validate it here and raise an exception if necessary + # so we will validate it here and raise an exception if necessary. if confidence_threshold < 0 or confidence_threshold > 1: raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") @@ -357,4 +351,4 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre response = requests.request("PATCH", url, headers=headers, json=payload) if not is_ok(response.status_code): - raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") + raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") \ No newline at end of file From 14762193ea9f743d07c64cb52bc5d0ad339f34d8 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 17:08:17 +0000 Subject: [PATCH 26/77] Automatically reformatting code --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index a8d22803..46bbe49e 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -351,4 +351,4 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre response = requests.request("PATCH", url, headers=headers, json=payload) if not is_ok(response.status_code): - raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") \ No newline at end of file + raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") From 7d886597f82be5ebe514a17b69a92822e6832f6f Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 10:13:15 -0700 Subject: [PATCH 27/77] trivial change for linter --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 46bbe49e..363a7a13 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -340,7 +340,7 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre # The API does not validate the confidence threshold, # so we will validate it here and raise an exception if necessary. if confidence_threshold < 0 or confidence_threshold > 1: - raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}") + raise ValueError(f"Confidence threshold must be between 0 and 1. Got {confidence_threshold}.") url = f"{self.configuration.host}/predictors/{detector_id}" From 0d973f6c1e02c13f1b84dab5c01ef402ff342899 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 11:02:07 -0700 Subject: [PATCH 28/77] adding type hint --- src/groundlight/internalapi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 363a7a13..7a149e85 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -11,6 +11,7 @@ import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException +from groundlight.images import ByteStreamWrapper from groundlight.status_codes import is_ok @@ -236,7 +237,7 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection(self, detector_id: str, image, inspection_id: str) -> str: + def submit_image_query_with_inspection(self, detector_id: str, image: ByteStreamWrapper, inspection_id: str) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ From ec6cebb6d99f192e801e617b877eaf8510811b01 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 18:03:38 +0000 Subject: [PATCH 29/77] Automatically reformatting code --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 7a149e85..515768fa 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -11,8 +11,8 @@ import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException -from groundlight.images import ByteStreamWrapper +from groundlight.images import ByteStreamWrapper from groundlight.status_codes import is_ok logger = logging.getLogger("groundlight.sdk") From be55d2d675ee0c19ffc4e958c1688b661c042941 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 11:34:20 -0700 Subject: [PATCH 30/77] trivial changes to spacing --- src/groundlight/internalapi.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 515768fa..6a44cb13 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -21,11 +21,9 @@ class NotFoundError(Exception): pass - class InspectionError(Exception): pass - class UpdateDetectorError(Exception): pass From dea58962cf113c3fd0112c8239927e873364df74 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 18:35:11 +0000 Subject: [PATCH 31/77] Automatically reformatting code --- src/groundlight/internalapi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 6a44cb13..515768fa 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -21,9 +21,11 @@ class NotFoundError(Exception): pass + class InspectionError(Exception): pass + class UpdateDetectorError(Exception): pass From ab2692665f4c788c67511a31683ffdce8de67498 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 15:51:29 -0700 Subject: [PATCH 32/77] changing stop_inspection to return a string of the result --- src/groundlight/client.py | 9 ++++++--- src/groundlight/internalapi.py | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index f0dec53e..a9607853 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -274,9 +274,12 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user """Add/update inspection metadata with the user_provided_key and user_provided_value.""" self.api_client.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - def stop_inspection(self, inspection_id: str) -> None: - """Stops an inspection and raises an exception if the response from the server does not indicate success.""" - self.api_client.stop_inspection(inspection_id) + def stop_inspection(self, inspection_id: str) -> str: + """Stops an inspection and raises an exception if the response from the server + does not indicate success communication. + Returns a str with result of the inspection (either PASS or FAIL) + """ + return self.api_client.stop_inspection(inspection_id) def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector given a detector_id.""" diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 6a44cb13..06c274b9 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -319,8 +319,10 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user ) @RequestsRetryDecorator() - def stop_inspection(self, inspection_id: str) -> None: - """Stops an inspection and raises an exception if the response from the server does not indicate success.""" + def stop_inspection(self, inspection_id: str) -> str: + """Stops an inspection and raises an exception if the response from the server does not indicate success. + Returns a string that indicates the result (either PASS or FAIL). The URCap requires this. + """ url = f"{self.configuration.host}/inspections/{inspection_id}" headers = self._headers() @@ -331,6 +333,8 @@ def stop_inspection(self, inspection_id: str) -> None: if not is_ok(response.status_code): raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") + + return response.json()['result'] @RequestsRetryDecorator() def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: From de5000081c0e8ff1e033f41d627f589251c55d82 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Wed, 2 Aug 2023 22:53:13 +0000 Subject: [PATCH 33/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index a9607853..f4873801 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -275,7 +275,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user self.api_client.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def stop_inspection(self, inspection_id: str) -> str: - """Stops an inspection and raises an exception if the response from the server + """Stops an inspection and raises an exception if the response from the server does not indicate success communication. Returns a str with result of the inspection (either PASS or FAIL) """ diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index d1e5820a..74de8a0d 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -335,8 +335,8 @@ def stop_inspection(self, inspection_id: str) -> str: if not is_ok(response.status_code): raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") - - return response.json()['result'] + + return response.json()["result"] @RequestsRetryDecorator() def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: From 73c9a15972774dc56685201152a872489b14c721 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Wed, 2 Aug 2023 15:56:22 -0700 Subject: [PATCH 34/77] linter changes --- src/groundlight/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index f4873801..5b5afd6a 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -276,7 +276,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user def stop_inspection(self, inspection_id: str) -> str: """Stops an inspection and raises an exception if the response from the server - does not indicate success communication. + indicates that the inspection was not successfully stopped. Returns a str with result of the inspection (either PASS or FAIL) """ return self.api_client.stop_inspection(inspection_id) From a410e7ab58915c27c91a656c4996e2350872f9fd Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 14:10:30 -0700 Subject: [PATCH 35/77] resolving merge conflicts --- src/groundlight/client.py | 51 ++++++++------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 5b5afd6a..9cac13fd 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -170,7 +170,7 @@ def submit_image_query( detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, - inspection_id: Optional[str] = None, + human_review: Optional[bool] = True, ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` @@ -183,7 +183,7 @@ def submit_image_query( Any binary format must be JPEG-encoded already. Any pixel format will get converted to JPEG at high quality before sending to service. :param wait: How long to wait (in seconds) for a confident answer. - :param inspection_id: The ID of the inspection (str) to associate with the image query. + :param human_review: If set to False, do not escalate for human review """ if wait is None: wait = self.DEFAULT_WAIT @@ -191,20 +191,10 @@ def submit_image_query( image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) - # Submit Image Query - # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use - # the private API client instead. - if inspection_id is None: - raw_image_query = self.image_queries_api.submit_image_query(detector_id=detector_id, body=image_bytesio) - image_query_dict = raw_image_query.to_dict() - image_query = ImageQuery.parse_obj(image_query_dict) - else: - iq_id = self.api_client.submit_image_query_with_inspection( - detector_id=detector_id, image=image_bytesio, inspection_id=inspection_id - ) - image_query = self.get_image_query(iq_id) - + raw_image_query = self.image_queries_api.submit_image_query( + detector_id=detector_id, patience_time=wait, human_review=human_review, body=image_bytesio + ) + image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) if wait: threshold = self.get_detector(detector).confidence_threshold image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) @@ -212,19 +202,17 @@ def submit_image_query( def wait_for_confident_result( self, - image_query: Union[ImageQuery, str], + image_query: ImageQuery, confidence_threshold: float, timeout_sec: float = 30.0, ) -> ImageQuery: """Waits for an image query result's confidence level to reach the specified value. Currently this is done by polling with an exponential back-off. - :param image_query: An ImageQuery object or an image_query id (str). + :param image_query: An ImageQuery object to poll :param confidence_threshold: The minimum confidence level required to return before the timeout. :param timeout_sec: The maximum number of seconds to wait. """ - # Convert from image_query_id to ImageQuery if needed. - image_query = self.get_image_query(image_query) if isinstance(image_query, str) else image_query - + # TODO: Add support for ImageQuery id instead of object. start_time = time.time() next_delay = self.POLLING_INITIAL_DELAY target_delay = 0.0 @@ -264,23 +252,4 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str raise ValueError(f"Invalid image query id {image_query_id}") api_label = convert_display_label_to_internal(image_query_id, label) - return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access - - def start_inspection(self) -> str: - """Starts an inspection report and returns the id of the inspection.""" - return self.api_client.start_inspection() - - def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: - """Add/update inspection metadata with the user_provided_key and user_provided_value.""" - self.api_client.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - - def stop_inspection(self, inspection_id: str) -> str: - """Stops an inspection and raises an exception if the response from the server - indicates that the inspection was not successfully stopped. - Returns a str with result of the inspection (either PASS or FAIL) - """ - return self.api_client.stop_inspection(inspection_id) - - def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: - """Updates the confidence threshold of a detector given a detector_id.""" - self.api_client.update_detector_confidence_threshold(detector_id, confidence_threshold) + return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access \ No newline at end of file From 52d5e716a7588fa347c254264a54a644560c1e95 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 4 Aug 2023 21:11:28 +0000 Subject: [PATCH 36/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 9cac13fd..60d84eaa 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -252,4 +252,4 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str raise ValueError(f"Invalid image query id {image_query_id}") api_label = convert_display_label_to_internal(image_query_id, label) - return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access \ No newline at end of file + return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access From 351c7c15b00070f7acbabda10b6835756ee4745b Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 17:13:04 -0700 Subject: [PATCH 37/77] responding to PR comments --- pyproject.toml | 2 +- src/groundlight/client.py | 69 ++++++++++++++++++++++++++++++---- src/groundlight/internalapi.py | 62 +++++++++++++++++++----------- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5e7c1ffd..dbff7c63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ packages = [ {include = "**/*.py", from = "src"}, ] readme = "README.md" -version = "0.10.0" +version = "0.10.1" [tool.poetry.dependencies] certifi = "^2021.10.8" diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 60d84eaa..463783e9 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -171,6 +171,7 @@ def submit_image_query( image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, human_review: Optional[bool] = True, + inspection_id: Optional[str] = None, ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` @@ -184,22 +185,49 @@ def submit_image_query( converted to JPEG at high quality before sending to service. :param wait: How long to wait (in seconds) for a confident answer. :param human_review: If set to False, do not escalate for human review + :param inspection_id: Most users will omit this. For accounts with Inspection Reports enabled, + this is the ID of the inspection to associate with the image query. """ if wait is None: wait = self.DEFAULT_WAIT - detector_id = detector.id if isinstance(detector, Detector) else detector + + # Convert from Detector to detector_id if necessary + if isinstance(detector, Detector): + detector_id = self.get_detector(detector) + else: + detector_id = detector image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) - raw_image_query = self.image_queries_api.submit_image_query( - detector_id=detector_id, patience_time=wait, human_review=human_review, body=image_bytesio - ) - image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) + # Submit Image Query + # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) + # However, our autogenerated code does not support inspection_id, so if an inspection_id was provided, we use + # the private API client instead. + if inspection_id is None: + raw_image_query = self.image_queries_api.submit_image_query( + detector_id=detector_id, + patience_time=wait, + human_review=human_review, + body=image_bytesio + ) + image_query_dict = raw_image_query.to_dict() + image_query = ImageQuery.parse_obj(image_query_dict) + else: + print(f"Using private API client to submit image query with human_review: {human_review}") + iq_id = self.api_client.submit_image_query_with_inspection( + detector_id=detector_id, + patience_time=wait, + human_review=human_review, + image=image_bytesio, + inspection_id=inspection_id + ) + image_query = self.get_image_query(iq_id) + if wait: threshold = self.get_detector(detector).confidence_threshold image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) return self._fixup_image_query(image_query) - + def wait_for_confident_result( self, image_query: ImageQuery, @@ -212,7 +240,10 @@ def wait_for_confident_result( :param confidence_threshold: The minimum confidence level required to return before the timeout. :param timeout_sec: The maximum number of seconds to wait. """ - # TODO: Add support for ImageQuery id instead of object. + # Convert from image_query_id to ImageQuery if needed. + if isinstance(image_query, str): + image_query = self.get_image_query(image_query) + start_time = time.time() next_delay = self.POLLING_INITIAL_DELAY target_delay = 0.0 @@ -253,3 +284,27 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str api_label = convert_display_label_to_internal(image_query_id, label) return self.api_client._add_label(image_query_id, api_label) # pylint: disable=protected-access + + def start_inspection(self) -> str: + """For users with Inspection Reports enabled only. + Starts an inspection report and returns the id of the inspection. + """ + return self.api_client.start_inspection() + + def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, user_provided_value: str) -> None: + """For users with Inspection Reports enabled only. + Add/update inspection metadata with the user_provided_key and user_provided_value. + """ + self.api_client.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + + def stop_inspection(self, inspection_id: str) -> str: + """For users with Inspection Reports enabled only. + Stops an inspection and raises an exception if the response from the server + indicates that the inspection was not successfully stopped. + Returns a str with result of the inspection (either PASS or FAIL) + """ + return self.api_client.stop_inspection(inspection_id) + + def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: + """Updates the confidence threshold of a detector given a detector_id.""" + self.api_client.update_detector_confidence_threshold(detector_id, confidence_threshold) \ No newline at end of file diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 74de8a0d..dff46b6b 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -17,19 +17,9 @@ logger = logging.getLogger("groundlight.sdk") - class NotFoundError(Exception): pass - -class InspectionError(Exception): - pass - - -class UpdateDetectorError(Exception): - pass - - def sanitize_endpoint_url(endpoint: Optional[str] = None) -> str: """Takes a URL for an endpoint, and returns a "sanitized" version of it. Currently the production API path must be exactly "/device-api". @@ -237,16 +227,28 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection(self, detector_id: str, image: ByteStreamWrapper, inspection_id: str) -> str: + def submit_image_query_with_inspection(self, + detector_id: str, + patience_time: float, + human_review: bool, + image: ByteStreamWrapper, + inspection_id: str) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ - url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" + + # In the API, "send_notification" was used to escalate image queries to cloud labelers. + send_notification = human_review + + url = (f"{self.configuration.host}/posichecks" + f"?inspection_id={inspection_id}" + f"&predictor_id={detector_id}" + f"&send_notification={send_notification}") headers = self._headers() headers["Content-Type"] = "image/jpeg" - response = requests.request("POST", url, headers=headers, data=image.read()) + response = requests.request("POST", url, headers=headers, timeout=patience_time, data=image.read()) if not is_ok(response.status_code): logger.info(response) @@ -268,12 +270,16 @@ def start_inspection(self) -> str: response = requests.request("POST", url, headers=headers, json={}) if not is_ok(response.status_code): - raise InspectionError(f"Error starting inspection. Status code: {response.status_code}") + raise InternalApiError( + status=response.status_code, + reason="Error starting inspection.", + http_resp=response, + ) return response.json()["id"] @RequestsRetryDecorator() - def update_inspection_metadata(self, inspection_id: str, user_provided_key, user_provided_value) -> None: + def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, user_provided_value: str) -> None: """Add/update inspection metadata with the user_provided_key and user_provided_value. The API stores inspections metadata in two ways: @@ -293,15 +299,17 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user response = requests.request("GET", url, headers=headers) if not is_ok(response.status_code): - raise InspectionError( - f"Error getting inspection details for inspection {inspection_id}. Status code: {response.status_code}" + raise InternalApiError( + status=response.status_code, + reason=f"Error getting inspection details for inspection {inspection_id}.", + http_resp=response, ) payload = {} # Set the user_provided_id_key and user_provided_id_value if they were not previously set. response_json = response.json() - if not response_json["user_provided_id_key"]: + if not response_json.get("user_provided_id_key"): payload["user_provided_id_key"] = user_provided_key payload["user_provided_id_value"] = user_provided_value @@ -316,8 +324,10 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key, user response = requests.request("PATCH", url, headers=headers, json=payload) if not is_ok(response.status_code): - raise InspectionError( - f"Error updating inspection metadata on inspection {inspection_id}. Status code: {response.status_code}" + raise InternalApiError( + status=response.status_code, + reason=f"Error updating inspection metadata on inspection {inspection_id}.", + http_resp=response, ) @RequestsRetryDecorator() @@ -334,7 +344,11 @@ def stop_inspection(self, inspection_id: str) -> str: response = requests.request("PATCH", url, headers=headers, json=payload) if not is_ok(response.status_code): - raise InspectionError(f"Error stopping inspection {inspection_id}. Status code: {response.status_code}") + raise InternalApiError( + status=response.status_code, + reason=f"Error stopping inspection {inspection_id}.", + http_resp=response, + ) return response.json()["result"] @@ -356,4 +370,8 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre response = requests.request("PATCH", url, headers=headers, json=payload) if not is_ok(response.status_code): - raise UpdateDetectorError(f"Error updating detector {detector_id}. Status code: {response.status_code}") + raise InternalApiError( + status=response.status_code, + reason=f"Error updating detector {detector_id}.", + http_resp=response, + ) From eb047f5d3df3628cfeb66493b3e09f8ffb048e06 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Sat, 5 Aug 2023 00:14:11 +0000 Subject: [PATCH 38/77] Automatically reformatting code --- src/groundlight/client.py | 23 +++++++++------------ src/groundlight/internalapi.py | 37 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 463783e9..cebc9d47 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -185,7 +185,7 @@ def submit_image_query( converted to JPEG at high quality before sending to service. :param wait: How long to wait (in seconds) for a confident answer. :param human_review: If set to False, do not escalate for human review - :param inspection_id: Most users will omit this. For accounts with Inspection Reports enabled, + :param inspection_id: Most users will omit this. For accounts with Inspection Reports enabled, this is the ID of the inspection to associate with the image query. """ if wait is None: @@ -205,21 +205,18 @@ def submit_image_query( # the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query( - detector_id=detector_id, - patience_time=wait, - human_review=human_review, - body=image_bytesio - ) + detector_id=detector_id, patience_time=wait, human_review=human_review, body=image_bytesio + ) image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: print(f"Using private API client to submit image query with human_review: {human_review}") iq_id = self.api_client.submit_image_query_with_inspection( - detector_id=detector_id, - patience_time=wait, - human_review=human_review, - image=image_bytesio, - inspection_id=inspection_id + detector_id=detector_id, + patience_time=wait, + human_review=human_review, + image=image_bytesio, + inspection_id=inspection_id, ) image_query = self.get_image_query(iq_id) @@ -227,7 +224,7 @@ def submit_image_query( threshold = self.get_detector(detector).confidence_threshold image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) return self._fixup_image_query(image_query) - + def wait_for_confident_result( self, image_query: ImageQuery, @@ -307,4 +304,4 @@ def stop_inspection(self, inspection_id: str) -> str: def update_detector_confidence_threshold(self, detector_id: str, confidence_threshold: float) -> None: """Updates the confidence threshold of a detector given a detector_id.""" - self.api_client.update_detector_confidence_threshold(detector_id, confidence_threshold) \ No newline at end of file + self.api_client.update_detector_confidence_threshold(detector_id, confidence_threshold) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index dff46b6b..e585df12 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -17,9 +17,11 @@ logger = logging.getLogger("groundlight.sdk") + class NotFoundError(Exception): pass + def sanitize_endpoint_url(endpoint: Optional[str] = None) -> str: """Takes a URL for an endpoint, and returns a "sanitized" version of it. Currently the production API path must be exactly "/device-api". @@ -227,12 +229,9 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection(self, - detector_id: str, - patience_time: float, - human_review: bool, - image: ByteStreamWrapper, - inspection_id: str) -> str: + def submit_image_query_with_inspection( + self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str + ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ @@ -240,10 +239,12 @@ def submit_image_query_with_inspection(self, # In the API, "send_notification" was used to escalate image queries to cloud labelers. send_notification = human_review - url = (f"{self.configuration.host}/posichecks" - f"?inspection_id={inspection_id}" - f"&predictor_id={detector_id}" - f"&send_notification={send_notification}") + url = ( + f"{self.configuration.host}/posichecks" + f"?inspection_id={inspection_id}" + f"&predictor_id={detector_id}" + f"&send_notification={send_notification}" + ) headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -345,10 +346,10 @@ def stop_inspection(self, inspection_id: str) -> str: if not is_ok(response.status_code): raise InternalApiError( - status=response.status_code, - reason=f"Error stopping inspection {inspection_id}.", - http_resp=response, - ) + status=response.status_code, + reason=f"Error stopping inspection {inspection_id}.", + http_resp=response, + ) return response.json()["result"] @@ -371,7 +372,7 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre if not is_ok(response.status_code): raise InternalApiError( - status=response.status_code, - reason=f"Error updating detector {detector_id}.", - http_resp=response, - ) + status=response.status_code, + reason=f"Error updating detector {detector_id}.", + http_resp=response, + ) From f433c48cf0c7e1f3f6735620179f45bef1f0e8ac Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 17:27:34 -0700 Subject: [PATCH 39/77] linter issues --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index e585df12..0b44b7b2 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -373,6 +373,6 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre if not is_ok(response.status_code): raise InternalApiError( status=response.status_code, - reason=f"Error updating detector {detector_id}.", + reason=f"Error updating detector: {detector_id}.", http_resp=response, ) From 949a7e22b05e53555825bd0d3b3049ca2bed9c3a Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 17:38:31 -0700 Subject: [PATCH 40/77] fixing detector_id conversion issue --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index cebc9d47..f536ecb6 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -193,7 +193,7 @@ def submit_image_query( # Convert from Detector to detector_id if necessary if isinstance(detector, Detector): - detector_id = self.get_detector(detector) + detector_id = detector.id else: detector_id = detector diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 0b44b7b2..0eb61c8d 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -230,7 +230,12 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( - self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str + self, + detector_id: str, + patience_time: float, + human_review: bool, + image: ByteStreamWrapper, + inspection_id: str ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. From bcc467bda57e207a581468f15179fc643eb00163 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 17:39:24 -0700 Subject: [PATCH 41/77] removing debugging line --- src/groundlight/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index f536ecb6..66f9c77f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -210,7 +210,6 @@ def submit_image_query( image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: - print(f"Using private API client to submit image query with human_review: {human_review}") iq_id = self.api_client.submit_image_query_with_inspection( detector_id=detector_id, patience_time=wait, From 20e5d3bc9870f650922f8c0eb1f4f58bf387bbc4 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Sat, 5 Aug 2023 00:40:16 +0000 Subject: [PATCH 42/77] Automatically reformatting code --- src/groundlight/internalapi.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 0eb61c8d..0b44b7b2 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -230,12 +230,7 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( - self, - detector_id: str, - patience_time: float, - human_review: bool, - image: ByteStreamWrapper, - inspection_id: str + self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. From ecd424aa50fe6b4b1d69b5a5cff417e78396d715 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 4 Aug 2023 17:42:25 -0700 Subject: [PATCH 43/77] linter issue --- src/groundlight/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 66f9c77f..c38b03bb 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -297,7 +297,7 @@ def stop_inspection(self, inspection_id: str) -> str: """For users with Inspection Reports enabled only. Stops an inspection and raises an exception if the response from the server indicates that the inspection was not successfully stopped. - Returns a str with result of the inspection (either PASS or FAIL) + Returns a str with result of the inspection (either PASS or FAIL). """ return self.api_client.stop_inspection(inspection_id) From c111a7be5189cf2a015e52c03a9adcdaa451a580 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Mon, 7 Aug 2023 16:00:35 -0700 Subject: [PATCH 44/77] adding some tests --- test/integration/test_groundlight.py | 67 +++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 27b7505a..647022cc 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -13,6 +13,8 @@ from groundlight.status_codes import is_user_error from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList +from internalapi import InternalApiError + DEFAULT_CONFIDENCE_THRESHOLD = 0.9 @@ -59,7 +61,6 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: iq = gl.submit_image_query(detector=detector.id, image="test/assets/cat.jpeg") return iq - def test_create_detector(gl: Groundlight): name = f"Test {datetime.utcnow()}" # Need a unique name query = "Is there a dog?" @@ -410,3 +411,67 @@ def submit_noisy_image(image, label=None): return assert False, "The detector performance has not improved after two minutes" + +def test_start_inspection(gl: Groundlight): + inspection_id = gl.start_inspection() + + assert isinstance(inspection_id, str) + assert 'inspect_' in inspection_id + +def test_update_inspection_metadata_successful(gl: Groundlight): + """Starts an inspection and adds a couple pieces of metadata to it. + This should succeed. If there are any errors, an exception will be raised. + """ + inspection_id = gl.start_inspection() + + user_provided_key = 'Inspector' + user_provided_value = 'Bob' + gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + + user_provided_key = 'Engine ID' + user_provided_value = '1234' + gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + +def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): + """Attempt to update metadata for an inspection that doesn't exist. + Should raise an InternalApiError. + """ + + # The URCap might submit an empty string if an inspection hasn't started. + # An InternalApiError should be raised in this case. + inspection_id = '' + user_provided_key = 'Operator' + user_provided_value = 'Bob' + + with pytest.raises(InternalApiError): + gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + +def test_stop_inspection_pass(gl: Groundlight, detector: Detector): + """Starts an inspection, submits a query that should pass, stops + the inspection, checks the result. + """ + inspection_id = gl.start_inspection() + + _ = gl.submit_image_query(detector=detector, + image="test/assets/dog.jpeg", + inspection_id=inspection_id) + + assert gl.stop_inspection(inspection_id) == "PASS" + +def test_stop_inspection_fail(gl: Groundlight, detector: Detector): + """Starts an inspection, submits a query that should fail, stops + the inspection, checks the result. + """ + inspection_id = gl.start_inspection() + + _ = gl.submit_image_query(detector=detector, + image="test/assets/cat.jpeg", + inspection_id=inspection_id) + + assert gl.stop_inspection(inspection_id) == "FAIL" + +def test_stop_inspection_with_invalid_id(gl: Groundlight): + inspection_id = 'some_invalid_inspection_id' + + with pytest.raises(InternalApiError): + gl.stop_inspection(inspection_id) From ca0a2a5500e77bf019be8a7385afa5a819745e82 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Mon, 7 Aug 2023 23:01:34 +0000 Subject: [PATCH 45/77] Automatically reformatting code --- test/integration/test_groundlight.py | 38 +++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 647022cc..a0250e55 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -11,9 +11,8 @@ from groundlight.internalapi import NotFoundError from groundlight.optional_imports import * from groundlight.status_codes import is_user_error -from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList - from internalapi import InternalApiError +from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList DEFAULT_CONFIDENCE_THRESHOLD = 0.9 @@ -61,6 +60,7 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: iq = gl.submit_image_query(detector=detector.id, image="test/assets/cat.jpeg") return iq + def test_create_detector(gl: Groundlight): name = f"Test {datetime.utcnow()}" # Need a unique name query = "Is there a dog?" @@ -412,11 +412,13 @@ def submit_noisy_image(image, label=None): assert False, "The detector performance has not improved after two minutes" + def test_start_inspection(gl: Groundlight): inspection_id = gl.start_inspection() assert isinstance(inspection_id, str) - assert 'inspect_' in inspection_id + assert "inspect_" in inspection_id + def test_update_inspection_metadata_successful(gl: Groundlight): """Starts an inspection and adds a couple pieces of metadata to it. @@ -424,54 +426,54 @@ def test_update_inspection_metadata_successful(gl: Groundlight): """ inspection_id = gl.start_inspection() - user_provided_key = 'Inspector' - user_provided_value = 'Bob' + user_provided_key = "Inspector" + user_provided_value = "Bob" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - user_provided_key = 'Engine ID' - user_provided_value = '1234' + user_provided_key = "Engine ID" + user_provided_value = "1234" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): """Attempt to update metadata for an inspection that doesn't exist. Should raise an InternalApiError. """ # The URCap might submit an empty string if an inspection hasn't started. - # An InternalApiError should be raised in this case. - inspection_id = '' - user_provided_key = 'Operator' - user_provided_value = 'Bob' + # An InternalApiError should be raised in this case. + inspection_id = "" + user_provided_key = "Operator" + user_provided_value = "Bob" with pytest.raises(InternalApiError): gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + def test_stop_inspection_pass(gl: Groundlight, detector: Detector): """Starts an inspection, submits a query that should pass, stops the inspection, checks the result. """ inspection_id = gl.start_inspection() - _ = gl.submit_image_query(detector=detector, - image="test/assets/dog.jpeg", - inspection_id=inspection_id) + _ = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) assert gl.stop_inspection(inspection_id) == "PASS" + def test_stop_inspection_fail(gl: Groundlight, detector: Detector): """Starts an inspection, submits a query that should fail, stops the inspection, checks the result. """ inspection_id = gl.start_inspection() - _ = gl.submit_image_query(detector=detector, - image="test/assets/cat.jpeg", - inspection_id=inspection_id) + _ = gl.submit_image_query(detector=detector, image="test/assets/cat.jpeg", inspection_id=inspection_id) assert gl.stop_inspection(inspection_id) == "FAIL" + def test_stop_inspection_with_invalid_id(gl: Groundlight): - inspection_id = 'some_invalid_inspection_id' + inspection_id = "some_invalid_inspection_id" with pytest.raises(InternalApiError): gl.stop_inspection(inspection_id) From d80287c7dc9fc64d151f7e145498de8e55ddb7e1 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Mon, 7 Aug 2023 16:09:39 -0700 Subject: [PATCH 46/77] fixing a test --- test/integration/test_groundlight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index a0250e55..1cfb70e2 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -11,7 +11,7 @@ from groundlight.internalapi import NotFoundError from groundlight.optional_imports import * from groundlight.status_codes import is_user_error -from internalapi import InternalApiError +from groundlight.internalapi import InternalApiError from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList DEFAULT_CONFIDENCE_THRESHOLD = 0.9 From 0d744f9fe11f9ea58aeb86034651ee6a5e9af386 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Mon, 7 Aug 2023 23:10:31 +0000 Subject: [PATCH 47/77] Automatically reformatting code --- test/integration/test_groundlight.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 1cfb70e2..acebba59 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -8,10 +8,9 @@ import pytest from groundlight import Groundlight from groundlight.binary_labels import VALID_DISPLAY_LABELS, DeprecatedLabel, Label, convert_internal_label_to_display -from groundlight.internalapi import NotFoundError +from groundlight.internalapi import InternalApiError, NotFoundError from groundlight.optional_imports import * from groundlight.status_codes import is_user_error -from groundlight.internalapi import InternalApiError from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList DEFAULT_CONFIDENCE_THRESHOLD = 0.9 From 291695a38e90a84168a9009b5214c06b5eeb15ed Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 11:23:14 -0700 Subject: [PATCH 48/77] adding more tests --- src/groundlight/internalapi.py | 32 +++++++++++++++++----------- test/integration/test_groundlight.py | 26 ++++++++++++++++------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 0b44b7b2..f4c178c9 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -235,17 +235,18 @@ def submit_image_query_with_inspection( """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ - - # In the API, "send_notification" was used to escalate image queries to cloud labelers. - send_notification = human_review - url = ( f"{self.configuration.host}/posichecks" f"?inspection_id={inspection_id}" f"&predictor_id={detector_id}" - f"&send_notification={send_notification}" ) + # # TODO: make sure this handles all cases: True, False, None + # if human_review: + # url += f"&send_notification=True" # TODO should this be True or always? + # elif not human_review: + # url += f"&send_notification=False" # TODO should this be True or always? + headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -256,7 +257,6 @@ def submit_image_query_with_inspection( raise InternalApiError( status=response.status_code, reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", - http_resp=response, ) return response.json()["id"] @@ -274,7 +274,6 @@ def start_inspection(self) -> str: raise InternalApiError( status=response.status_code, reason="Error starting inspection.", - http_resp=response, ) return response.json()["id"] @@ -296,14 +295,20 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, headers = self._headers() - # Get inspection to see if user_provided_id_key is set + # Get inspection in order to find out: + # 1) if user_provided_id_key has been set + # 2) if the inspection is closed response = requests.request("GET", url, headers=headers) if not is_ok(response.status_code): raise InternalApiError( status=response.status_code, reason=f"Error getting inspection details for inspection {inspection_id}.", - http_resp=response, + ) + elif response.json()["status"] == 'COMPLETE': + raise InternalApiError( + status=response.status_code, + reason=f"Inspection {inspection_id} is closed. Metadata cannot be added.", ) payload = {} @@ -328,7 +333,6 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, raise InternalApiError( status=response.status_code, reason=f"Error updating inspection metadata on inspection {inspection_id}.", - http_resp=response, ) @RequestsRetryDecorator() @@ -340,6 +344,12 @@ def stop_inspection(self, inspection_id: str) -> str: headers = self._headers() + # Closing an inspection generates a new inspection PDF. Therefore, if the inspection + # is already closed, just return "COMPLETE" to avoid unnecessarily generating a new PDF. + response = requests.request("GET", url, headers=headers) + if response.json()["status"] == "COMPLETE": + return "COMPLETE" + payload = {"status": "COMPLETE"} response = requests.request("PATCH", url, headers=headers, json=payload) @@ -348,7 +358,6 @@ def stop_inspection(self, inspection_id: str) -> str: raise InternalApiError( status=response.status_code, reason=f"Error stopping inspection {inspection_id}.", - http_resp=response, ) return response.json()["result"] @@ -374,5 +383,4 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre raise InternalApiError( status=response.status_code, reason=f"Error updating detector: {detector_id}.", - http_resp=response, ) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 1cfb70e2..337f81cb 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -420,7 +420,7 @@ def test_start_inspection(gl: Groundlight): assert "inspect_" in inspection_id -def test_update_inspection_metadata_successful(gl: Groundlight): +def test_update_inspection_metadata_success(gl: Groundlight): """Starts an inspection and adds a couple pieces of metadata to it. This should succeed. If there are any errors, an exception will be raised. """ @@ -434,29 +434,30 @@ def test_update_inspection_metadata_successful(gl: Groundlight): user_provided_value = "1234" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + + def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): """Attempt to update metadata for an inspection that doesn't exist. Should raise an InternalApiError. """ - # The URCap might submit an empty string if an inspection hasn't started. - # An InternalApiError should be raised in this case. - inspection_id = "" + inspection_id = "some_invalid_inspection_id" user_provided_key = "Operator" user_provided_value = "Bob" with pytest.raises(InternalApiError): gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - def test_stop_inspection_pass(gl: Groundlight, detector: Detector): """Starts an inspection, submits a query that should pass, stops the inspection, checks the result. """ inspection_id = gl.start_inspection() - _ = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) + print('DEBUGGING test_stop_inspection_pass') + iq = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) + print(iq) assert gl.stop_inspection(inspection_id) == "PASS" @@ -477,3 +478,16 @@ def test_stop_inspection_with_invalid_id(gl: Groundlight): with pytest.raises(InternalApiError): gl.stop_inspection(inspection_id) + + +def test_update_detector_confidence_threshold_success(gl: Groundlight, detector: Detector): + """Updates the confidence threshold for a detector. This should succeed. + """ + gl.update_detector_confidence_threshold(detector, 0.77) + +def test_update_detector_confidence_threshold_failure(gl: Groundlight, detector: Detector): + """Attemps to update the confidence threshold for a detector to an invalid value. + Should raise an ValueError. + """ + with pytest.raises(ValueError): + gl.update_detector_confidence_threshold(detector, 77) From f0e649598f6c557723b888b72f862db2bd0d2395 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 11:32:41 -0700 Subject: [PATCH 49/77] adding some more tests and fixing linter issues --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index c38b03bb..520714f9 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -172,7 +172,7 @@ def submit_image_query( wait: Optional[float] = None, human_review: Optional[bool] = True, inspection_id: Optional[str] = None, - ) -> ImageQuery: + ) -> ImageQuery: # pylint: disable=R0913 """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` :param image: The image, in several possible formats: diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index f4c178c9..15bf3f1b 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -231,7 +231,7 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str - ) -> str: + ) -> str: # pylint: disable=R0913 """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ From 48e1a40065ef2cdc72dd9988775b0bcaf377bf4c Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 8 Aug 2023 18:34:41 +0000 Subject: [PATCH 50/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 14 +++++--------- test/integration/test_groundlight.py | 9 ++++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 520714f9..feaf91cb 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -172,7 +172,7 @@ def submit_image_query( wait: Optional[float] = None, human_review: Optional[bool] = True, inspection_id: Optional[str] = None, - ) -> ImageQuery: # pylint: disable=R0913 + ) -> ImageQuery: # pylint: disable=R0913 """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` :param image: The image, in several possible formats: diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 15bf3f1b..4ee7ae0e 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -231,15 +231,11 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str - ) -> str: # pylint: disable=R0913 + ) -> str: # pylint: disable=R0913 """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ - url = ( - f"{self.configuration.host}/posichecks" - f"?inspection_id={inspection_id}" - f"&predictor_id={detector_id}" - ) + url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" # # TODO: make sure this handles all cases: True, False, None # if human_review: @@ -295,7 +291,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, headers = self._headers() - # Get inspection in order to find out: + # Get inspection in order to find out: # 1) if user_provided_id_key has been set # 2) if the inspection is closed response = requests.request("GET", url, headers=headers) @@ -305,7 +301,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, status=response.status_code, reason=f"Error getting inspection details for inspection {inspection_id}.", ) - elif response.json()["status"] == 'COMPLETE': + elif response.json()["status"] == "COMPLETE": raise InternalApiError( status=response.status_code, reason=f"Inspection {inspection_id} is closed. Metadata cannot be added.", @@ -344,7 +340,7 @@ def stop_inspection(self, inspection_id: str) -> str: headers = self._headers() - # Closing an inspection generates a new inspection PDF. Therefore, if the inspection + # Closing an inspection generates a new inspection PDF. Therefore, if the inspection # is already closed, just return "COMPLETE" to avoid unnecessarily generating a new PDF. response = requests.request("GET", url, headers=headers) if response.json()["status"] == "COMPLETE": diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 4f79b342..857840c4 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -433,8 +433,6 @@ def test_update_inspection_metadata_success(gl: Groundlight): user_provided_value = "1234" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) - - def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): """Attempt to update metadata for an inspection that doesn't exist. @@ -448,13 +446,14 @@ def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): with pytest.raises(InternalApiError): gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + def test_stop_inspection_pass(gl: Groundlight, detector: Detector): """Starts an inspection, submits a query that should pass, stops the inspection, checks the result. """ inspection_id = gl.start_inspection() - print('DEBUGGING test_stop_inspection_pass') + print("DEBUGGING test_stop_inspection_pass") iq = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) print(iq) @@ -480,10 +479,10 @@ def test_stop_inspection_with_invalid_id(gl: Groundlight): def test_update_detector_confidence_threshold_success(gl: Groundlight, detector: Detector): - """Updates the confidence threshold for a detector. This should succeed. - """ + """Updates the confidence threshold for a detector. This should succeed.""" gl.update_detector_confidence_threshold(detector, 0.77) + def test_update_detector_confidence_threshold_failure(gl: Groundlight, detector: Detector): """Attemps to update the confidence threshold for a detector to an invalid value. Should raise an ValueError. From c1beda4f5b2f3528cbf1af3b0f4a814b9b365288 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 11:52:12 -0700 Subject: [PATCH 51/77] fixing linter issues --- src/groundlight/client.py | 4 ++-- src/groundlight/internalapi.py | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index feaf91cb..96125cae 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -171,8 +171,8 @@ def submit_image_query( image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, human_review: Optional[bool] = True, - inspection_id: Optional[str] = None, - ) -> ImageQuery: # pylint: disable=R0913 + inspection_id: Optional[str] = None, # pylint: disable=R0913 + ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` :param image: The image, in several possible formats: diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 4ee7ae0e..68733575 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -230,11 +230,21 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( - self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, inspection_id: str - ) -> str: # pylint: disable=R0913 + self, + detector_id: str, + patience_time: float, + human_review: bool, + image: ByteStreamWrapper, + inspection_id: str # pylint: disable=R0913 + ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ + + # TODO properly implement human_review based on recent changes + if human_review: + pass + url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" # # TODO: make sure this handles all cases: True, False, None @@ -301,7 +311,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, status=response.status_code, reason=f"Error getting inspection details for inspection {inspection_id}.", ) - elif response.json()["status"] == "COMPLETE": + if response.json()["status"] == "COMPLETE": raise InternalApiError( status=response.status_code, reason=f"Inspection {inspection_id} is closed. Metadata cannot be added.", From 83bb798422358583f3832554c95287fe8dcfcc5a Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 8 Aug 2023 18:53:02 +0000 Subject: [PATCH 52/77] Automatically reformatting code --- src/groundlight/client.py | 4 ++-- src/groundlight/internalapi.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 96125cae..6a5da9d8 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -171,8 +171,8 @@ def submit_image_query( image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, human_review: Optional[bool] = True, - inspection_id: Optional[str] = None, # pylint: disable=R0913 - ) -> ImageQuery: + inspection_id: Optional[str] = None, # pylint: disable=R0913 + ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` :param image: The image, in several possible formats: diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 68733575..bd2b0e74 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -230,13 +230,13 @@ def _get_detector_by_name(self, name: str) -> Detector: @RequestsRetryDecorator() def submit_image_query_with_inspection( - self, - detector_id: str, - patience_time: float, - human_review: bool, - image: ByteStreamWrapper, - inspection_id: str # pylint: disable=R0913 - ) -> str: + self, + detector_id: str, + patience_time: float, + human_review: bool, + image: ByteStreamWrapper, + inspection_id: str, # pylint: disable=R0913 + ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. """ From 78207756603304138eb202324ec05be619106b1f Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 11:54:58 -0700 Subject: [PATCH 53/77] fixing linter issues --- src/groundlight/internalapi.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index bd2b0e74..d11b0096 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -241,16 +241,14 @@ def submit_image_query_with_inspection( The image query will be associated to the inspection_id provided. """ - # TODO properly implement human_review based on recent changes - if human_review: - pass - url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" # # TODO: make sure this handles all cases: True, False, None - # if human_review: + if human_review: + pass # url += f"&send_notification=True" # TODO should this be True or always? - # elif not human_review: + else: + pass # url += f"&send_notification=False" # TODO should this be True or always? headers = self._headers() From a74b26b3d1b5a7e0e6da7a871d4b5d990782650b Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 11:58:46 -0700 Subject: [PATCH 54/77] fixing linter issues --- src/groundlight/client.py | 4 ++-- src/groundlight/internalapi.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 6a5da9d8..627a9da6 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,13 +165,13 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - def submit_image_query( + def submit_image_query( # pylint: disable=R0913 self, detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, human_review: Optional[bool] = True, - inspection_id: Optional[str] = None, # pylint: disable=R0913 + inspection_id: Optional[str] = None, ) -> ImageQuery: """Evaluates an image with Groundlight. :param detector: the Detector object, or string id of a detector like `det_12345` diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index d11b0096..206dde72 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,13 +229,13 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection( + def submit_image_query_with_inspection( # pylint: disable=R0913 self, detector_id: str, patience_time: float, human_review: bool, image: ByteStreamWrapper, - inspection_id: str, # pylint: disable=R0913 + inspection_id: str, ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. From d513b2db2547a089f28702373125896466af0e17 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Tue, 8 Aug 2023 18:59:30 +0000 Subject: [PATCH 55/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 627a9da6..14ffd4d3 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,7 +165,7 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - def submit_image_query( # pylint: disable=R0913 + def submit_image_query( # pylint: disable=R0913 self, detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 206dde72..6b8abb7d 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,7 +229,7 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection( # pylint: disable=R0913 + def submit_image_query_with_inspection( # pylint: disable=R0913 self, detector_id: str, patience_time: float, From fcb0769805980faeb7b76b8fd0d79e978cd374f7 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 12:06:24 -0700 Subject: [PATCH 56/77] fixing linter issues --- src/groundlight/client.py | 3 ++- src/groundlight/internalapi.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 14ffd4d3..d56d286f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,7 +165,8 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - def submit_image_query( # pylint: disable=R0913 + # pylint: disable=too-many-arguments + def submit_image_query( self, detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 6b8abb7d..756e88d3 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,7 +229,8 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - def submit_image_query_with_inspection( # pylint: disable=R0913 + # pylint: disable=too-many-arguments + def submit_image_query_with_inspection( self, detector_id: str, patience_time: float, From a88e1abbafeb2f692b0e0f14c66dc59c58424094 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 12:10:41 -0700 Subject: [PATCH 57/77] fixing linter issues --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index d56d286f..2c76b0f1 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,7 +165,7 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - # pylint: disable=too-many-arguments + # pylint: disable=PLR0913 def submit_image_query( self, detector: Union[Detector, str], diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 756e88d3..b8fdc9c2 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,7 +229,7 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - # pylint: disable=too-many-arguments + # pylint: disable=PLR0913 def submit_image_query_with_inspection( self, detector_id: str, From 88f2e47113a74a418bad4f845517f694ba0bac83 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 12:51:57 -0700 Subject: [PATCH 58/77] fixing linter issues --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 2c76b0f1..d56d286f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,7 +165,7 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - # pylint: disable=PLR0913 + # pylint: disable=too-many-arguments def submit_image_query( self, detector: Union[Detector, str], diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index b8fdc9c2..756e88d3 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,7 +229,7 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - # pylint: disable=PLR0913 + # pylint: disable=too-many-arguments def submit_image_query_with_inspection( self, detector_id: str, From 593bb3256b56688381ddb8c39a28416015fbd117 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 12:59:24 -0700 Subject: [PATCH 59/77] fixing linter issues --- src/groundlight/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index d56d286f..97ed4993 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -211,6 +211,7 @@ def submit_image_query( image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: + # pylint: disable=too-many-arguments iq_id = self.api_client.submit_image_query_with_inspection( detector_id=detector_id, patience_time=wait, From 1fef157ccc80d0aa76dc8c7a193bd652222dc1bd Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Tue, 8 Aug 2023 15:47:46 -0700 Subject: [PATCH 60/77] linter changes --- src/groundlight/client.py | 11 ++++++----- src/groundlight/internalapi.py | 26 ++++++++++++++++---------- test/integration/test_groundlight.py | 12 ++++++++---- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 97ed4993..95fe177c 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -165,8 +165,7 @@ def list_image_queries(self, page: int = 1, page_size: int = 10) -> PaginatedIma image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries - # pylint: disable=too-many-arguments - def submit_image_query( + def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments self, detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], @@ -206,15 +205,17 @@ def submit_image_query( # the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query( - detector_id=detector_id, patience_time=wait, human_review=human_review, body=image_bytesio + detector_id=detector_id, + patience_time=wait, + human_review=human_review, + body=image_bytesio, ) image_query_dict = raw_image_query.to_dict() image_query = ImageQuery.parse_obj(image_query_dict) else: - # pylint: disable=too-many-arguments iq_id = self.api_client.submit_image_query_with_inspection( detector_id=detector_id, - patience_time=wait, + patience_time=wait, # TODO is this relevant to submit_image_query_with_inspection? human_review=human_review, image=image_bytesio, inspection_id=inspection_id, diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 756e88d3..a7e97aa7 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -229,11 +229,10 @@ def _get_detector_by_name(self, name: str) -> Detector: return Detector.parse_obj(parsed["results"][0]) @RequestsRetryDecorator() - # pylint: disable=too-many-arguments - def submit_image_query_with_inspection( + def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-many-arguments self, detector_id: str, - patience_time: float, + patience_time: float, # TODO is this relevant? human_review: bool, image: ByteStreamWrapper, inspection_id: str, @@ -242,26 +241,33 @@ def submit_image_query_with_inspection( The image query will be associated to the inspection_id provided. """ - url = f"{self.configuration.host}/posichecks?inspection_id={inspection_id}&predictor_id={detector_id}" + url = ( + f"{self.configuration.host}/posichecks" + f"?inspection_id={inspection_id}" + f"&predictor_id={detector_id}&patience_time={patience_time}" + ) - # # TODO: make sure this handles all cases: True, False, None - if human_review: + # TODO: update this in light of recent changes + if human_review is None: pass - # url += f"&send_notification=True" # TODO should this be True or always? + elif human_review: + url += "&send_notification=True" # TODO should this be True or 'ALWAYS'? else: - pass - # url += f"&send_notification=False" # TODO should this be True or always? + url += "&send_notification=False" # TODO should this be True or 'NEVER'? headers = self._headers() headers["Content-Type"] = "image/jpeg" - response = requests.request("POST", url, headers=headers, timeout=patience_time, data=image.read()) + # TODO: do we want patience time here? + # response = requests.request("POST", url, headers=headers, timeout=patience_time, data=image.read()) + response = requests.request("POST", url, headers=headers, data=image.read()) if not is_ok(response.status_code): logger.info(response) raise InternalApiError( status=response.status_code, reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", + http_resp=response, ) return response.json()["id"] diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 857840c4..18c883ed 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -12,6 +12,7 @@ from groundlight.optional_imports import * from groundlight.status_codes import is_user_error from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList +from PIL import Image DEFAULT_CONFIDENCE_THRESHOLD = 0.9 @@ -60,6 +61,11 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: return iq +@pytest.fixture(name="dog_image") +def fixture_dog_image() -> Image: + return Image.open("test/assets/dog.jpeg") + + def test_create_detector(gl: Groundlight): name = f"Test {datetime.utcnow()}" # Need a unique name query = "Is there a dog?" @@ -448,14 +454,12 @@ def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): def test_stop_inspection_pass(gl: Groundlight, detector: Detector): - """Starts an inspection, submits a query that should pass, stops + """Starts an inspection, submits a query with the inspection ID that should pass, stops the inspection, checks the result. """ inspection_id = gl.start_inspection() - print("DEBUGGING test_stop_inspection_pass") - iq = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) - print(iq) + _ = gl.submit_image_query(detector=detector, wait=0.1, image="test/assets/dog.jpeg", inspection_id=inspection_id) assert gl.stop_inspection(inspection_id) == "PASS" From 830d9694da8a29b3708ffb6c821a74f69c8bdb61 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Thu, 10 Aug 2023 11:48:39 -0700 Subject: [PATCH 61/77] resolving more merge conflicts --- src/groundlight/client.py | 13 +++++++++++-- src/groundlight/internalapi.py | 27 +++++++++++++-------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 6188ec94..80825f8f 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -211,8 +211,17 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments if human_review is not None: params["human_review"] = human_review - raw_image_query = self.image_queries_api.submit_image_query(**params) - image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) + # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) + # However, our autogenerated code does not currently support inspection_id, so if an inspection_id was + # provided, we use the private API client instead. + if inspection_id is None: + raw_image_query = self.image_queries_api.submit_image_query(**params) + image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) + else: + params["inspection_id"] = inspection_id + iq_id = self.api_client.submit_image_query_with_inspection(**params) + image_query = self.get_image_query(iq_id) + if wait: threshold = self.get_detector(detector).confidence_threshold image_query = self.wait_for_confident_result(image_query, confidence_threshold=threshold, timeout_sec=wait) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index a7e97aa7..96b2c2c2 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -232,10 +232,10 @@ def _get_detector_by_name(self, name: str) -> Detector: def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-many-arguments self, detector_id: str, - patience_time: float, # TODO is this relevant? - human_review: bool, - image: ByteStreamWrapper, + patience_time: float, + body: ByteStreamWrapper, inspection_id: str, + human_review: str = "DEFAULT", ) -> str: """Submits an image query to the API and returns the ID of the image query. The image query will be associated to the inspection_id provided. @@ -244,30 +244,29 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = ( f"{self.configuration.host}/posichecks" f"?inspection_id={inspection_id}" - f"&predictor_id={detector_id}&patience_time={patience_time}" + f"&predictor_id={detector_id}" + f"&patience_time={patience_time}" ) - # TODO: update this in light of recent changes - if human_review is None: - pass - elif human_review: - url += "&send_notification=True" # TODO should this be True or 'ALWAYS'? + # In the API, 'send_notification' is used to control human_review escalation. This will eventually + # be deprecated, but for now we need to support it in the following manner: + if human_review == "ALWAYS": + url += "&send_notification=True" + elif human_review == "NEVER": + url += "&send_notification=False" else: - url += "&send_notification=False" # TODO should this be True or 'NEVER'? + pass # append nothing to the URL, allow "DEFAULT" behavior headers = self._headers() headers["Content-Type"] = "image/jpeg" - # TODO: do we want patience time here? - # response = requests.request("POST", url, headers=headers, timeout=patience_time, data=image.read()) - response = requests.request("POST", url, headers=headers, data=image.read()) + response = requests.request("POST", url, headers=headers, data=body.read()) if not is_ok(response.status_code): logger.info(response) raise InternalApiError( status=response.status_code, reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", - http_resp=response, ) return response.json()["id"] From 9aa86cef829f14c7f3d72c51863d225b76ee29a9 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Thu, 10 Aug 2023 13:59:44 -0700 Subject: [PATCH 62/77] refining some tests --- src/groundlight/internalapi.py | 4 ++-- test/integration/test_groundlight.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 96b2c2c2..0d1b1d12 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -357,9 +357,9 @@ def stop_inspection(self, inspection_id: str) -> str: # Closing an inspection generates a new inspection PDF. Therefore, if the inspection # is already closed, just return "COMPLETE" to avoid unnecessarily generating a new PDF. response = requests.request("GET", url, headers=headers) - if response.json()["status"] == "COMPLETE": + if response.json().get('status') == "COMPLETE": return "COMPLETE" - + payload = {"status": "COMPLETE"} response = requests.request("PATCH", url, headers=headers, json=payload) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 3480927c..ba453121 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -216,7 +216,6 @@ def test_submit_image_query_jpeg_truncated(gl: Groundlight, detector: Detector): with pytest.raises(openapi_client.exceptions.ApiException) as exc_info: _image_query = gl.submit_image_query(detector=detector.id, image=jpeg_truncated) exc_value = exc_info.value - print(f"exc_info = {exc_info}") assert is_user_error(exc_value.status) @@ -469,7 +468,7 @@ def test_stop_inspection_pass(gl: Groundlight, detector: Detector): """ inspection_id = gl.start_inspection() - _ = gl.submit_image_query(detector=detector, wait=0.1, image="test/assets/dog.jpeg", inspection_id=inspection_id) + _ = gl.submit_image_query(detector=detector, image="test/assets/dog.jpeg", inspection_id=inspection_id) assert gl.stop_inspection(inspection_id) == "PASS" @@ -480,7 +479,8 @@ def test_stop_inspection_fail(gl: Groundlight, detector: Detector): """ inspection_id = gl.start_inspection() - _ = gl.submit_image_query(detector=detector, image="test/assets/cat.jpeg", inspection_id=inspection_id) + iq = gl.submit_image_query(detector=detector, image="test/assets/cat.jpeg", inspection_id=inspection_id) + gl.add_label(iq, Label.NO) # labeling it NO just to be sure the inspection fails assert gl.stop_inspection(inspection_id) == "FAIL" @@ -494,12 +494,15 @@ def test_stop_inspection_with_invalid_id(gl: Groundlight): def test_update_detector_confidence_threshold_success(gl: Groundlight, detector: Detector): """Updates the confidence threshold for a detector. This should succeed.""" - gl.update_detector_confidence_threshold(detector, 0.77) + gl.update_detector_confidence_threshold(detector.id, 0.77) def test_update_detector_confidence_threshold_failure(gl: Groundlight, detector: Detector): - """Attemps to update the confidence threshold for a detector to an invalid value. - Should raise an ValueError. + """Attempts to update the confidence threshold for a detector to invalid values. + Should raise ValueError exceptions. """ with pytest.raises(ValueError): - gl.update_detector_confidence_threshold(detector, 77) + gl.update_detector_confidence_threshold(detector.id, 77) # too high + + with pytest.raises(ValueError): + gl.update_detector_confidence_threshold(detector.id, -1) # too low From 35c53a34c3e4b2b5416e68d54a031d6857bcef4b Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Thu, 10 Aug 2023 14:08:49 -0700 Subject: [PATCH 63/77] bumping version to 0.10.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dbff7c63..fe062770 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ packages = [ {include = "**/*.py", from = "src"}, ] readme = "README.md" -version = "0.10.1" +version = "0.10.2" [tool.poetry.dependencies] certifi = "^2021.10.8" From 1745394130d08e46a3a94077baf99b61f3f92ec8 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Thu, 10 Aug 2023 21:09:53 +0000 Subject: [PATCH 64/77] Automatically reformatting code --- src/groundlight/client.py | 2 +- src/groundlight/internalapi.py | 8 ++++---- test/integration/test_groundlight.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 80825f8f..7baad16b 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -212,7 +212,7 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments params["human_review"] = human_review # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not currently support inspection_id, so if an inspection_id was + # However, our autogenerated code does not currently support inspection_id, so if an inspection_id was # provided, we use the private API client instead. if inspection_id is None: raw_image_query = self.image_queries_api.submit_image_query(**params) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 0d1b1d12..0f303b96 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -251,11 +251,11 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: if human_review == "ALWAYS": - url += "&send_notification=True" + url += "&send_notification=True" elif human_review == "NEVER": url += "&send_notification=False" else: - pass # append nothing to the URL, allow "DEFAULT" behavior + pass # append nothing to the URL, allow "DEFAULT" behavior headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -357,9 +357,9 @@ def stop_inspection(self, inspection_id: str) -> str: # Closing an inspection generates a new inspection PDF. Therefore, if the inspection # is already closed, just return "COMPLETE" to avoid unnecessarily generating a new PDF. response = requests.request("GET", url, headers=headers) - if response.json().get('status') == "COMPLETE": + if response.json().get("status") == "COMPLETE": return "COMPLETE" - + payload = {"status": "COMPLETE"} response = requests.request("PATCH", url, headers=headers, json=payload) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index ba453121..d0202e8b 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -480,7 +480,7 @@ def test_stop_inspection_fail(gl: Groundlight, detector: Detector): inspection_id = gl.start_inspection() iq = gl.submit_image_query(detector=detector, image="test/assets/cat.jpeg", inspection_id=inspection_id) - gl.add_label(iq, Label.NO) # labeling it NO just to be sure the inspection fails + gl.add_label(iq, Label.NO) # labeling it NO just to be sure the inspection fails assert gl.stop_inspection(inspection_id) == "FAIL" @@ -502,7 +502,7 @@ def test_update_detector_confidence_threshold_failure(gl: Groundlight, detector: Should raise ValueError exceptions. """ with pytest.raises(ValueError): - gl.update_detector_confidence_threshold(detector.id, 77) # too high + gl.update_detector_confidence_threshold(detector.id, 77) # too high with pytest.raises(ValueError): - gl.update_detector_confidence_threshold(detector.id, -1) # too low + gl.update_detector_confidence_threshold(detector.id, -1) # too low From c24cd80389f53f28b6a968de4533cf2e8740fac3 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Thu, 10 Aug 2023 14:20:23 -0700 Subject: [PATCH 65/77] adding another test for inspections metadata --- test/integration/test_groundlight.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index d0202e8b..2ec0ec9d 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -448,6 +448,18 @@ def test_update_inspection_metadata_success(gl: Groundlight): user_provided_value = "1234" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) +def test_update_inspection_metadata_failure(gl: Groundlight): + """Attempts to add metadata to an inspection after it is closed. + Should raise an exception. + """ + inspection_id = gl.start_inspection() + + _ = gl.stop_inspection(inspection_id) + + with pytest.raises(InternalApiError): + user_provided_key = "Inspector" + user_provided_value = "Bob" + gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): """Attempt to update metadata for an inspection that doesn't exist. From edd55b94d93170a8714cf1b88f1ab066cfc74f0e Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Thu, 10 Aug 2023 21:21:21 +0000 Subject: [PATCH 66/77] Automatically reformatting code --- test/integration/test_groundlight.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 2ec0ec9d..79b86cd4 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -448,6 +448,7 @@ def test_update_inspection_metadata_success(gl: Groundlight): user_provided_value = "1234" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + def test_update_inspection_metadata_failure(gl: Groundlight): """Attempts to add metadata to an inspection after it is closed. Should raise an exception. @@ -461,6 +462,7 @@ def test_update_inspection_metadata_failure(gl: Groundlight): user_provided_value = "Bob" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value) + def test_update_inspection_metadata_invalid_inspection_id(gl: Groundlight): """Attempt to update metadata for an inspection that doesn't exist. Should raise an InternalApiError. From 9ca3d2da5836821b93672a3de355324fd2c10985 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 09:31:14 -0700 Subject: [PATCH 67/77] responding to PR comments --- pyproject.toml | 2 +- src/groundlight/client.py | 8 ++------ src/groundlight/internalapi.py | 33 ++++++++++++++++++++++++--------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fe062770..fea4d249 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ packages = [ {include = "**/*.py", from = "src"}, ] readme = "README.md" -version = "0.10.2" +version = "0.11.0" [tool.poetry.dependencies] certifi = "^2021.10.8" diff --git a/src/groundlight/client.py b/src/groundlight/client.py index 7baad16b..65e0b97b 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -194,11 +194,7 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments if wait is None: wait = self.DEFAULT_WAIT - # Convert from Detector to detector_id if necessary - if isinstance(detector, Detector): - detector_id = detector.id - else: - detector_id = detector + detector_id = detector.id if isinstance(detector, Detector) else detector image_bytesio: ByteStreamWrapper = parse_supported_image_types(image) @@ -229,7 +225,7 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments def wait_for_confident_result( self, - image_query: ImageQuery, + image_query: Union[ImageQuery, str], confidence_threshold: float, timeout_sec: float = 30.0, ) -> ImageQuery: diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 0f303b96..448c0459 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -80,6 +80,8 @@ class InternalApiError(ApiException, RuntimeError): def __init__(self, status=None, reason=None, http_resp=None): super().__init__(status, reason, http_resp) + + class RequestsRetryDecorator: """ @@ -241,21 +243,21 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m The image query will be associated to the inspection_id provided. """ - url = ( - f"{self.configuration.host}/posichecks" - f"?inspection_id={inspection_id}" - f"&predictor_id={detector_id}" - f"&patience_time={patience_time}" - ) + url = f"{self.configuration.host}/posichecks" + params = { + "inspection_id": inspection_id, + "predictor_id": detector_id, + "patience_time": patience_time +} # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: if human_review == "ALWAYS": - url += "&send_notification=True" + params["send_notification"] = True elif human_review == "NEVER": - url += "&send_notification=False" + params["send_notification"] = False else: - pass # append nothing to the URL, allow "DEFAULT" behavior + pass # don't send the send_notifications param, allow "DEFAULT" behavior headers = self._headers() headers["Content-Type"] = "image/jpeg" @@ -267,6 +269,7 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m raise InternalApiError( status=response.status_code, reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", + http_resp=response, ) return response.json()["id"] @@ -284,6 +287,7 @@ def start_inspection(self) -> str: raise InternalApiError( status=response.status_code, reason="Error starting inspection.", + http_resp=response, ) return response.json()["id"] @@ -343,6 +347,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, raise InternalApiError( status=response.status_code, reason=f"Error updating inspection metadata on inspection {inspection_id}.", + http_resp=response, ) @RequestsRetryDecorator() @@ -357,6 +362,14 @@ def stop_inspection(self, inspection_id: str) -> str: # Closing an inspection generates a new inspection PDF. Therefore, if the inspection # is already closed, just return "COMPLETE" to avoid unnecessarily generating a new PDF. response = requests.request("GET", url, headers=headers) + + if not is_ok(response.status_code): + raise InternalApiError( + status=response.status_code, + reason=f"Error checking the status of {inspection_id}.", + http_resp=response, + ) + if response.json().get("status") == "COMPLETE": return "COMPLETE" @@ -368,6 +381,7 @@ def stop_inspection(self, inspection_id: str) -> str: raise InternalApiError( status=response.status_code, reason=f"Error stopping inspection {inspection_id}.", + http_resp=response, ) return response.json()["result"] @@ -393,4 +407,5 @@ def update_detector_confidence_threshold(self, detector_id: str, confidence_thre raise InternalApiError( status=response.status_code, reason=f"Error updating detector: {detector_id}.", + http_resp=response, ) From 04c40a19774ce8a8a52e2f035d802f55209b3646 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 11 Aug 2023 16:32:24 +0000 Subject: [PATCH 68/77] Automatically reformatting code --- src/groundlight/internalapi.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 448c0459..5d96ff23 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -80,8 +80,6 @@ class InternalApiError(ApiException, RuntimeError): def __init__(self, status=None, reason=None, http_resp=None): super().__init__(status, reason, http_resp) - - class RequestsRetryDecorator: """ @@ -245,11 +243,7 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = f"{self.configuration.host}/posichecks" - params = { - "inspection_id": inspection_id, - "predictor_id": detector_id, - "patience_time": patience_time -} + params = {"inspection_id": inspection_id, "predictor_id": detector_id, "patience_time": patience_time} # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: if human_review == "ALWAYS": From 31751235592a27e98b809628f135aaa53a63d602 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 10:12:17 -0700 Subject: [PATCH 69/77] fixing params issue --- src/groundlight/internalapi.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 5d96ff23..fe9aca53 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -80,6 +80,8 @@ class InternalApiError(ApiException, RuntimeError): def __init__(self, status=None, reason=None, http_resp=None): super().__init__(status, reason, http_resp) + + class RequestsRetryDecorator: """ @@ -243,7 +245,12 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = f"{self.configuration.host}/posichecks" - params = {"inspection_id": inspection_id, "predictor_id": detector_id, "patience_time": patience_time} + params = { + "inspection_id": inspection_id, + "predictor_id": detector_id, + "patience_time": patience_time + } + # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: if human_review == "ALWAYS": @@ -256,7 +263,7 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m headers = self._headers() headers["Content-Type"] = "image/jpeg" - response = requests.request("POST", url, headers=headers, data=body.read()) + response = requests.request("POST", url, headers=headers, params=params, data=body.read()) if not is_ok(response.status_code): logger.info(response) From d3e3f3aea929dbab7dd75fb50a5737831c8ac8b4 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 11 Aug 2023 17:13:21 +0000 Subject: [PATCH 70/77] Automatically reformatting code --- src/groundlight/internalapi.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index fe9aca53..23a86b57 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -80,8 +80,6 @@ class InternalApiError(ApiException, RuntimeError): def __init__(self, status=None, reason=None, http_resp=None): super().__init__(status, reason, http_resp) - - class RequestsRetryDecorator: """ @@ -245,11 +243,7 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = f"{self.configuration.host}/posichecks" - params = { - "inspection_id": inspection_id, - "predictor_id": detector_id, - "patience_time": patience_time - } + params = {"inspection_id": inspection_id, "predictor_id": detector_id, "patience_time": patience_time} # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: From 9f6a9ff0b99f7e582825122bcc2124cb12650944 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 10:25:45 -0700 Subject: [PATCH 71/77] fixing linting issue --- src/groundlight/internalapi.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 23a86b57..003c5ff0 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -5,7 +5,7 @@ import time import uuid from functools import wraps -from typing import Callable, Optional +from typing import Callable, Optional, Dict, Union from urllib.parse import urlsplit, urlunsplit import requests @@ -243,7 +243,11 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = f"{self.configuration.host}/posichecks" - params = {"inspection_id": inspection_id, "predictor_id": detector_id, "patience_time": patience_time} + params: Dict[str, Union[str, float]] = { + "inspection_id": inspection_id, + "predictor_id": detector_id, + "patience_time": patience_time + } # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: From 02052c6c06c08f3cba0f425e8030ed2331ef692a Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 11 Aug 2023 17:26:37 +0000 Subject: [PATCH 72/77] Automatically reformatting code --- src/groundlight/internalapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 003c5ff0..3ad77af7 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -5,7 +5,7 @@ import time import uuid from functools import wraps -from typing import Callable, Optional, Dict, Union +from typing import Callable, Dict, Optional, Union from urllib.parse import urlsplit, urlunsplit import requests @@ -246,8 +246,8 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m params: Dict[str, Union[str, float]] = { "inspection_id": inspection_id, "predictor_id": detector_id, - "patience_time": patience_time - } + "patience_time": patience_time, + } # In the API, 'send_notification' is used to control human_review escalation. This will eventually # be deprecated, but for now we need to support it in the following manner: From d911c90aebd07c93b1c7b0636626ff391ec5f4bc Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 10:28:02 -0700 Subject: [PATCH 73/77] updating type hint --- src/groundlight/internalapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 003c5ff0..189e62e4 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -243,7 +243,7 @@ def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-m url = f"{self.configuration.host}/posichecks" - params: Dict[str, Union[str, float]] = { + params: Dict[str, Union[str, float, bool]] = { "inspection_id": inspection_id, "predictor_id": detector_id, "patience_time": patience_time From 59401dc806c9a842fde6789ab5a8355c8cba7705 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 10:41:30 -0700 Subject: [PATCH 74/77] removing unused testing fixture --- test/integration/test_groundlight.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 79b86cd4..a15607b8 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -61,11 +61,6 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: return iq -@pytest.fixture(name="dog_image") -def fixture_dog_image() -> Image: - return Image.open("test/assets/dog.jpeg") - - def test_create_detector(gl: Groundlight): name = f"Test {datetime.utcnow()}" # Need a unique name query = "Is there a dog?" From 69e74b3fbf90e640f5049e3048ead37e869c6cfa Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Fri, 11 Aug 2023 17:42:29 +0000 Subject: [PATCH 75/77] Automatically reformatting code --- test/integration/test_groundlight.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index a15607b8..55ca43dd 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -12,7 +12,6 @@ from groundlight.optional_imports import * from groundlight.status_codes import is_user_error from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList -from PIL import Image DEFAULT_CONFIDENCE_THRESHOLD = 0.9 From e19fc787f0e4387689c988d05a7eeddbfc7f5812 Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 10:55:01 -0700 Subject: [PATCH 76/77] fixing error handling for submitting inspection metadata when inspection is closed --- src/groundlight/internalapi.py | 3 ++- test/integration/test_groundlight.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 3c930094..10a3c27e 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -317,10 +317,11 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, raise InternalApiError( status=response.status_code, reason=f"Error getting inspection details for inspection {inspection_id}.", + http_resp=response, ) if response.json()["status"] == "COMPLETE": raise InternalApiError( - status=response.status_code, + # status=response.status_code, reason=f"Inspection {inspection_id} is closed. Metadata cannot be added.", ) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index a15607b8..55ca43dd 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -12,7 +12,6 @@ from groundlight.optional_imports import * from groundlight.status_codes import is_user_error from model import ClassificationResult, Detector, ImageQuery, PaginatedDetectorList, PaginatedImageQueryList -from PIL import Image DEFAULT_CONFIDENCE_THRESHOLD = 0.9 From 1208788eb7e833142e2c158e9bd41fee6f78d0fa Mon Sep 17 00:00:00 2001 From: Tim Huff Date: Fri, 11 Aug 2023 11:06:17 -0700 Subject: [PATCH 77/77] fixing exception for updating metadata --- src/groundlight/internalapi.py | 5 +---- test/integration/test_groundlight.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index 10a3c27e..95243cd9 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -320,10 +320,7 @@ def update_inspection_metadata(self, inspection_id: str, user_provided_key: str, http_resp=response, ) if response.json()["status"] == "COMPLETE": - raise InternalApiError( - # status=response.status_code, - reason=f"Inspection {inspection_id} is closed. Metadata cannot be added.", - ) + raise ValueError(f"Inspection {inspection_id} is closed. Metadata cannot be added.") payload = {} diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 55ca43dd..5c836714 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -451,7 +451,7 @@ def test_update_inspection_metadata_failure(gl: Groundlight): _ = gl.stop_inspection(inspection_id) - with pytest.raises(InternalApiError): + with pytest.raises(ValueError): user_provided_key = "Inspector" user_provided_value = "Bob" gl.update_inspection_metadata(inspection_id, user_provided_key, user_provided_value)