diff --git a/generated/.openapi-generator/FILES b/generated/.openapi-generator/FILES index 07eb726b..4d99e803 100644 --- a/generated/.openapi-generator/FILES +++ b/generated/.openapi-generator/FILES @@ -1,5 +1,6 @@ .gitignore README.md +docs/AccountMonthToDateInfo.md docs/Action.md docs/ActionList.md docs/ActionsApi.md @@ -41,6 +42,7 @@ docs/LabelValue.md docs/LabelValueRequest.md docs/LabelsApi.md docs/ModeEnum.md +docs/MonthToDateAccountInfoApi.md docs/MultiClassModeConfiguration.md docs/MultiClassificationResult.md docs/Note.md @@ -77,6 +79,7 @@ groundlight_openapi_client/api/detectors_api.py groundlight_openapi_client/api/edge_api.py groundlight_openapi_client/api/image_queries_api.py groundlight_openapi_client/api/labels_api.py +groundlight_openapi_client/api/month_to_date_account_info_api.py groundlight_openapi_client/api/notes_api.py groundlight_openapi_client/api/user_api.py groundlight_openapi_client/api_client.py @@ -84,6 +87,7 @@ groundlight_openapi_client/apis/__init__.py groundlight_openapi_client/configuration.py groundlight_openapi_client/exceptions.py groundlight_openapi_client/model/__init__.py +groundlight_openapi_client/model/account_month_to_date_info.py groundlight_openapi_client/model/action.py groundlight_openapi_client/model/action_list.py groundlight_openapi_client/model/all_notes.py @@ -150,6 +154,4 @@ setup.cfg setup.py test-requirements.txt test/__init__.py -test/test_label.py -test/test_source.py tox.ini diff --git a/generated/README.md b/generated/README.md index f82c9211..acfcaec1 100644 --- a/generated/README.md +++ b/generated/README.md @@ -137,6 +137,7 @@ Class | Method | HTTP request | Description *ImageQueriesApi* | [**list_image_queries**](docs/ImageQueriesApi.md#list_image_queries) | **GET** /v1/image-queries | *ImageQueriesApi* | [**submit_image_query**](docs/ImageQueriesApi.md#submit_image_query) | **POST** /v1/image-queries | *LabelsApi* | [**create_label**](docs/LabelsApi.md#create_label) | **POST** /v1/labels | +*MonthToDateAccountInfoApi* | [**month_to_date_account_info**](docs/MonthToDateAccountInfoApi.md#month_to_date_account_info) | **GET** /v1/month-to-date-account-info | *NotesApi* | [**create_note**](docs/NotesApi.md#create_note) | **POST** /v1/notes | *NotesApi* | [**get_notes**](docs/NotesApi.md#get_notes) | **GET** /v1/notes | *UserApi* | [**who_am_i**](docs/UserApi.md#who_am_i) | **GET** /v1/me | @@ -144,6 +145,7 @@ Class | Method | HTTP request | Description ## Documentation For Models + - [AccountMonthToDateInfo](docs/AccountMonthToDateInfo.md) - [Action](docs/Action.md) - [ActionList](docs/ActionList.md) - [AllNotes](docs/AllNotes.md) diff --git a/generated/docs/AccountMonthToDateInfo.md b/generated/docs/AccountMonthToDateInfo.md new file mode 100644 index 00000000..b184ffb9 --- /dev/null +++ b/generated/docs/AccountMonthToDateInfo.md @@ -0,0 +1,18 @@ +# AccountMonthToDateInfo + +Account usage information for the current month + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**iqs** | **int** | The number of image queries in the current month. | +**escalations** | **int** | The number of escalations in the current month. | +**active_detectors** | **int** | The number of active detectors in the current month. | +**iqs_limit** | **int, none_type** | The limit on the number of image queries in the current month. | +**escalations_limit** | **int, none_type** | The limit on the number of escalations in the current month. | +**active_detectors_limit** | **int, none_type** | The limit on the number of active detectors in the current month. | +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/generated/docs/ActionsApi.md b/generated/docs/ActionsApi.md index 099d468f..26321950 100644 --- a/generated/docs/ActionsApi.md +++ b/generated/docs/ActionsApi.md @@ -396,11 +396,12 @@ with groundlight_openapi_client.ApiClient(configuration) as api_client: api_instance = actions_api.ActionsApi(api_client) page = 1 # int | A page number within the paginated result set. (optional) page_size = 1 # int | Number of results to return per page. (optional) + predictor_id = "predictor_id_example" # str | Filter rules by predictor ID (optional) # example passing only required values which don't have defaults set # and optional values try: - api_response = api_instance.list_rules(page=page, page_size=page_size) + api_response = api_instance.list_rules(page=page, page_size=page_size, predictor_id=predictor_id) pprint(api_response) except groundlight_openapi_client.ApiException as e: print("Exception when calling ActionsApi->list_rules: %s\n" % e) @@ -413,6 +414,7 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **page** | **int**| A page number within the paginated result set. | [optional] **page_size** | **int**| Number of results to return per page. | [optional] + **predictor_id** | **str**| Filter rules by predictor ID | [optional] ### Return type diff --git a/generated/docs/MonthToDateAccountInfoApi.md b/generated/docs/MonthToDateAccountInfoApi.md new file mode 100644 index 00000000..83f5027f --- /dev/null +++ b/generated/docs/MonthToDateAccountInfoApi.md @@ -0,0 +1,82 @@ +# groundlight_openapi_client.MonthToDateAccountInfoApi + +All URIs are relative to *https://api.groundlight.ai/device-api* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**month_to_date_account_info**](MonthToDateAccountInfoApi.md#month_to_date_account_info) | **GET** /v1/month-to-date-account-info | + + +# **month_to_date_account_info** +> AccountMonthToDateInfo month_to_date_account_info() + + + +Fetches and returns the account-specific metrics based on the current user's group. + +### Example + +* Api Key Authentication (ApiToken): + +```python +import time +import groundlight_openapi_client +from groundlight_openapi_client.api import month_to_date_account_info_api +from groundlight_openapi_client.model.account_month_to_date_info import AccountMonthToDateInfo +from pprint import pprint +# Defining the host is optional and defaults to https://api.groundlight.ai/device-api +# See configuration.py for a list of all supported configuration parameters. +configuration = groundlight_openapi_client.Configuration( + host = "https://api.groundlight.ai/device-api" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: ApiToken +configuration.api_key['ApiToken'] = 'YOUR_API_KEY' + +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['ApiToken'] = 'Bearer' + +# Enter a context with an instance of the API client +with groundlight_openapi_client.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = month_to_date_account_info_api.MonthToDateAccountInfoApi(api_client) + + # example, this endpoint has no required or optional parameters + try: + api_response = api_instance.month_to_date_account_info() + pprint(api_response) + except groundlight_openapi_client.ApiException as e: + print("Exception when calling MonthToDateAccountInfoApi->month_to_date_account_info: %s\n" % e) +``` + + +### Parameters +This endpoint does not need any parameter. + +### Return type + +[**AccountMonthToDateInfo**](AccountMonthToDateInfo.md) + +### Authorization + +[ApiToken](../README.md#ApiToken) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/generated/docs/Source.md b/generated/docs/Source.md index fa6f1066..e7ddb56a 100644 --- a/generated/docs/Source.md +++ b/generated/docs/Source.md @@ -4,7 +4,7 @@ ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**value** | **str** | | must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "EDGE", ] +**value** | **str** | | must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE", ] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/generated/docs/SourceEnum.md b/generated/docs/SourceEnum.md index a382414e..81cbdb56 100644 --- a/generated/docs/SourceEnum.md +++ b/generated/docs/SourceEnum.md @@ -4,7 +4,7 @@ ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**value** | **str** | | must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] +**value** | **str** | | must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/generated/groundlight_openapi_client/api/actions_api.py b/generated/groundlight_openapi_client/api/actions_api.py index 4f9d42f5..b4d3ebd2 100644 --- a/generated/groundlight_openapi_client/api/actions_api.py +++ b/generated/groundlight_openapi_client/api/actions_api.py @@ -222,6 +222,7 @@ def __init__(self, api_client=None): "all": [ "page", "page_size", + "predictor_id", ], "required": [], "nullable": [], @@ -234,14 +235,17 @@ def __init__(self, api_client=None): "openapi_types": { "page": (int,), "page_size": (int,), + "predictor_id": (str,), }, "attribute_map": { "page": "page", "page_size": "page_size", + "predictor_id": "predictor_id", }, "location_map": { "page": "query", "page_size": "query", + "predictor_id": "query", }, "collection_format_map": {}, }, @@ -502,6 +506,7 @@ def list_rules(self, **kwargs): Keyword Args: page (int): A page number within the paginated result set.. [optional] page_size (int): Number of results to return per page.. [optional] + predictor_id (str): Filter rules by predictor ID. [optional] _return_http_data_only (bool): response data without head status code and headers. Default is True. _preload_content (bool): if False, the urllib3.HTTPResponse object diff --git a/generated/groundlight_openapi_client/api/month_to_date_account_info_api.py b/generated/groundlight_openapi_client/api/month_to_date_account_info_api.py new file mode 100644 index 00000000..ed467035 --- /dev/null +++ b/generated/groundlight_openapi_client/api/month_to_date_account_info_api.py @@ -0,0 +1,116 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 + +from groundlight_openapi_client.api_client import ApiClient, Endpoint as _Endpoint +from groundlight_openapi_client.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + none_type, + validate_and_convert_types, +) +from groundlight_openapi_client.model.account_month_to_date_info import AccountMonthToDateInfo + + +class MonthToDateAccountInfoApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + self.month_to_date_account_info_endpoint = _Endpoint( + settings={ + "response_type": (AccountMonthToDateInfo,), + "auth": ["ApiToken"], + "endpoint_path": "/v1/month-to-date-account-info", + "operation_id": "month_to_date_account_info", + "http_method": "GET", + "servers": None, + }, + params_map={"all": [], "required": [], "nullable": [], "enum": [], "validation": []}, + root_map={ + "validations": {}, + "allowed_values": {}, + "openapi_types": {}, + "attribute_map": {}, + "location_map": {}, + "collection_format_map": {}, + }, + headers_map={ + "accept": ["application/json"], + "content_type": [], + }, + api_client=api_client, + ) + + def month_to_date_account_info(self, **kwargs): + """month_to_date_account_info # noqa: E501 + + Fetches and returns the account-specific metrics based on the current user's group. # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.month_to_date_account_info(async_req=True) + >>> result = thread.get() + + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _content_type (str/None): force body content-type. + Default is None and content-type will be predicted by allowed + content-types and body. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + AccountMonthToDateInfo + If the method is called asynchronously, returns the request + thread. + """ + kwargs["async_req"] = kwargs.get("async_req", False) + kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) + kwargs["_preload_content"] = kwargs.get("_preload_content", True) + kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) + kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) + kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) + kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) + kwargs["_content_type"] = kwargs.get("_content_type") + kwargs["_host_index"] = kwargs.get("_host_index") + return self.month_to_date_account_info_endpoint.call_with_http_info(**kwargs) diff --git a/generated/groundlight_openapi_client/apis/__init__.py b/generated/groundlight_openapi_client/apis/__init__.py index 28edf7d0..1da138d1 100644 --- a/generated/groundlight_openapi_client/apis/__init__.py +++ b/generated/groundlight_openapi_client/apis/__init__.py @@ -20,5 +20,6 @@ from groundlight_openapi_client.api.edge_api import EdgeApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.labels_api import LabelsApi +from groundlight_openapi_client.api.month_to_date_account_info_api import MonthToDateAccountInfoApi from groundlight_openapi_client.api.notes_api import NotesApi from groundlight_openapi_client.api.user_api import UserApi diff --git a/generated/groundlight_openapi_client/model/account_month_to_date_info.py b/generated/groundlight_openapi_client/model/account_month_to_date_info.py new file mode 100644 index 00000000..98dd18e3 --- /dev/null +++ b/generated/groundlight_openapi_client/model/account_month_to_date_info.py @@ -0,0 +1,317 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 + +from groundlight_openapi_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel, +) +from groundlight_openapi_client.exceptions import ApiAttributeError + + +class AccountMonthToDateInfo(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = {} + + validations = {} + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + "iqs": (int,), # noqa: E501 + "escalations": (int,), # noqa: E501 + "active_detectors": (int,), # noqa: E501 + "iqs_limit": ( + int, + none_type, + ), # noqa: E501 + "escalations_limit": ( + int, + none_type, + ), # noqa: E501 + "active_detectors_limit": ( + int, + none_type, + ), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + attribute_map = { + "iqs": "iqs", # noqa: E501 + "escalations": "escalations", # noqa: E501 + "active_detectors": "active_detectors", # noqa: E501 + "iqs_limit": "iqs_limit", # noqa: E501 + "escalations_limit": "escalations_limit", # noqa: E501 + "active_detectors_limit": "active_detectors_limit", # noqa: E501 + } + + read_only_vars = {} + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data( + cls, iqs, escalations, active_detectors, iqs_limit, escalations_limit, active_detectors_limit, *args, **kwargs + ): # noqa: E501 + """AccountMonthToDateInfo - a model defined in OpenAPI + + Args: + iqs (int): The number of image queries in the current month. + escalations (int): The number of escalations in the current month. + active_detectors (int): The number of active detectors in the current month. + iqs_limit (int, none_type): The limit on the number of image queries in the current month. + escalations_limit (int, none_type): The limit on the number of escalations in the current month. + active_detectors_limit (int, none_type): The limit on the number of active detectors in the current month. + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.iqs = iqs + self.escalations = escalations + self.active_detectors = active_detectors + self.iqs_limit = iqs_limit + self.escalations_limit = escalations_limit + self.active_detectors_limit = active_detectors_limit + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + "_data_store", + "_check_type", + "_spec_property_naming", + "_path_to_item", + "_configuration", + "_visited_composed_classes", + ]) + + @convert_js_args_to_python_args + def __init__( + self, iqs, escalations, active_detectors, iqs_limit, escalations_limit, active_detectors_limit, *args, **kwargs + ): # noqa: E501 + """AccountMonthToDateInfo - a model defined in OpenAPI + + Args: + iqs (int): The number of image queries in the current month. + escalations (int): The number of escalations in the current month. + active_detectors (int): The number of active detectors in the current month. + iqs_limit (int, none_type): The limit on the number of image queries in the current month. + escalations_limit (int, none_type): The limit on the number of escalations in the current month. + active_detectors_limit (int, none_type): The limit on the number of active detectors in the current month. + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.iqs = iqs + self.escalations = escalations + self.active_detectors = active_detectors + self.iqs_limit = iqs_limit + self.escalations_limit = escalations_limit + self.active_detectors_limit = active_detectors_limit + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError( + f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + "class with read only attributes." + ) diff --git a/generated/groundlight_openapi_client/model/source.py b/generated/groundlight_openapi_client/model/source.py index 7e3e832a..b9c247c4 100644 --- a/generated/groundlight_openapi_client/model/source.py +++ b/generated/groundlight_openapi_client/model/source.py @@ -56,6 +56,9 @@ class Source(ModelSimple): "USER": "USER", "CLOUD_ENSEMBLE": "CLOUD_ENSEMBLE", "ALGORITHM": "ALGORITHM", + "AI_CLOUD": "AI_CLOUD", + "AI_CLOUD_ENSEMBLE": "AI_CLOUD_ENSEMBLE", + "HUMAN_AI_CLOUD_ENSEMBLE": "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE": "EDGE", }, } @@ -106,10 +109,10 @@ def __init__(self, *args, **kwargs): Note that value can be passed either in args or in kwargs, but not in both. Args: - args[0] (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "EDGE", ] # noqa: E501 + args[0] (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE", ] # noqa: E501 Keyword Args: - value (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "EDGE", ] # noqa: E501 + value (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE", ] # noqa: E501 _check_type (bool): if True, values for parameters in openapi_types will be type checked and a TypeError will be raised if the wrong type is input. @@ -198,10 +201,10 @@ def _from_openapi_data(cls, *args, **kwargs): Note that value can be passed either in args or in kwargs, but not in both. Args: - args[0] (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "EDGE", ] # noqa: E501 + args[0] (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE", ] # noqa: E501 Keyword Args: - value (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "EDGE", ] # noqa: E501 + value (str):, must be one of ["STILL_PROCESSING", "CLOUD", "USER", "CLOUD_ENSEMBLE", "ALGORITHM", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "EDGE", ] # noqa: E501 _check_type (bool): if True, values for parameters in openapi_types will be type checked and a TypeError will be raised if the wrong type is input. diff --git a/generated/groundlight_openapi_client/model/source_enum.py b/generated/groundlight_openapi_client/model/source_enum.py index 8a2746ab..e8e5a047 100644 --- a/generated/groundlight_openapi_client/model/source_enum.py +++ b/generated/groundlight_openapi_client/model/source_enum.py @@ -55,6 +55,9 @@ class SourceEnum(ModelSimple): "CLOUD": "CLOUD", "CUST": "CUST", "HUMAN_CLOUD_ENSEMBLE": "HUMAN_CLOUD_ENSEMBLE", + "AI_CLOUD": "AI_CLOUD", + "AI_CLOUD_ENSEMBLE": "AI_CLOUD_ENSEMBLE", + "HUMAN_AI_CLOUD_ENSEMBLE": "HUMAN_AI_CLOUD_ENSEMBLE", "ALG": "ALG", "ALG_REC": "ALG_REC", "ALG_UNCLEAR": "ALG_UNCLEAR", @@ -108,10 +111,10 @@ def __init__(self, *args, **kwargs): Note that value can be passed either in args or in kwargs, but not in both. Args: - args[0] (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 + args[0] (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 Keyword Args: - value (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 + value (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 _check_type (bool): if True, values for parameters in openapi_types will be type checked and a TypeError will be raised if the wrong type is input. @@ -200,10 +203,10 @@ def _from_openapi_data(cls, *args, **kwargs): Note that value can be passed either in args or in kwargs, but not in both. Args: - args[0] (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 + args[0] (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 Keyword Args: - value (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 + value (str):, must be one of ["INITIAL_PLACEHOLDER", "CLOUD", "CUST", "HUMAN_CLOUD_ENSEMBLE", "AI_CLOUD", "AI_CLOUD_ENSEMBLE", "HUMAN_AI_CLOUD_ENSEMBLE", "ALG", "ALG_REC", "ALG_UNCLEAR", "EDGE", ] # noqa: E501 _check_type (bool): if True, values for parameters in openapi_types will be type checked and a TypeError will be raised if the wrong type is input. diff --git a/generated/groundlight_openapi_client/models/__init__.py b/generated/groundlight_openapi_client/models/__init__.py index 16412577..e9de218b 100644 --- a/generated/groundlight_openapi_client/models/__init__.py +++ b/generated/groundlight_openapi_client/models/__init__.py @@ -9,6 +9,7 @@ # import sys # sys.setrecursionlimit(n) +from groundlight_openapi_client.model.account_month_to_date_info import AccountMonthToDateInfo from groundlight_openapi_client.model.action import Action from groundlight_openapi_client.model.action_list import ActionList from groundlight_openapi_client.model.all_notes import AllNotes diff --git a/generated/model.py b/generated/model.py index dc6348b8..1de65384 100644 --- a/generated/model.py +++ b/generated/model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: public-api.yaml -# timestamp: 2025-05-06T17:51:03+00:00 +# timestamp: 2025-08-28T18:43:04+00:00 from __future__ import annotations @@ -11,6 +11,23 @@ from pydantic import AnyUrl, BaseModel, Field, RootModel, confloat, conint, constr +class AccountMonthToDateInfo(BaseModel): + """ + Account usage information for the current month + """ + + iqs: int = Field(..., description="The number of image queries in the current month.") + escalations: int = Field(..., description="The number of escalations in the current month.") + active_detectors: int = Field(..., description="The number of active detectors in the current month.") + iqs_limit: Optional[int] = Field(..., description="The limit on the number of image queries in the current month.") + escalations_limit: Optional[int] = Field( + ..., description="The limit on the number of escalations in the current month." + ) + active_detectors_limit: Optional[int] = Field( + ..., description="The limit on the number of active detectors in the current month." + ) + + class BBoxGeometry(BaseModel): """ Mixin for serializers to handle data in the StrictBaseModel format @@ -290,6 +307,9 @@ class SourceEnum(str, Enum): CLOUD = "CLOUD" CUST = "CUST" HUMAN_CLOUD_ENSEMBLE = "HUMAN_CLOUD_ENSEMBLE" + AI_CLOUD = "AI_CLOUD" + AI_CLOUD_ENSEMBLE = "AI_CLOUD_ENSEMBLE" + HUMAN_AI_CLOUD_ENSEMBLE = "HUMAN_AI_CLOUD_ENSEMBLE" ALG = "ALG" ALG_REC = "ALG_REC" ALG_UNCLEAR = "ALG_UNCLEAR" @@ -310,6 +330,9 @@ class Source(str, Enum): USER = "USER" CLOUD_ENSEMBLE = "CLOUD_ENSEMBLE" ALGORITHM = "ALGORITHM" + AI_CLOUD = "AI_CLOUD" + AI_CLOUD_ENSEMBLE = "AI_CLOUD_ENSEMBLE" + HUMAN_AI_CLOUD_ENSEMBLE = "HUMAN_AI_CLOUD_ENSEMBLE" EDGE = "EDGE" diff --git a/generated/test/test_account_month_to_date_info.py b/generated/test/test_account_month_to_date_info.py new file mode 100644 index 00000000..e9453d9b --- /dev/null +++ b/generated/test/test_account_month_to_date_info.py @@ -0,0 +1,35 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import sys +import unittest + +import groundlight_openapi_client +from groundlight_openapi_client.model.account_month_to_date_info import AccountMonthToDateInfo + + +class TestAccountMonthToDateInfo(unittest.TestCase): + """AccountMonthToDateInfo unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testAccountMonthToDateInfo(self): + """Test AccountMonthToDateInfo""" + # FIXME: construct object with mandatory attributes with example values + # model = AccountMonthToDateInfo() # noqa: E501 + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/generated/test/test_month_to_date_account_info_api.py b/generated/test/test_month_to_date_account_info_api.py new file mode 100644 index 00000000..af60b4c1 --- /dev/null +++ b/generated/test/test_month_to_date_account_info_api.py @@ -0,0 +1,32 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import unittest + +import groundlight_openapi_client +from groundlight_openapi_client.api.month_to_date_account_info_api import MonthToDateAccountInfoApi # noqa: E501 + + +class TestMonthToDateAccountInfoApi(unittest.TestCase): + """MonthToDateAccountInfoApi unit test stubs""" + + def setUp(self): + self.api = MonthToDateAccountInfoApi() # noqa: E501 + + def tearDown(self): + pass + + def test_month_to_date_account_info(self): + """Test case for month_to_date_account_info""" + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/spec/public-api.yaml b/spec/public-api.yaml index d6f899c9..197e1fac 100644 --- a/spec/public-api.yaml +++ b/spec/public-api.yaml @@ -91,6 +91,11 @@ paths: description: Number of results to return per page. schema: type: integer + - in: query + name: predictor_id + schema: + type: string + description: Filter rules by predictor ID tags: - actions security: @@ -734,6 +739,22 @@ paths: type: string description: The user's username description: '' + /v1/month-to-date-account-info: + get: + operationId: Month-to-date account info + description: Fetches and returns the account-specific metrics based on the current + user's group. + tags: + - month-to-date-account-info + security: + - ApiToken: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AccountMonthToDateInfo' + description: '' /v1/notes: get: operationId: get notes @@ -787,6 +808,40 @@ paths: description: No response body components: schemas: + AccountMonthToDateInfo: + type: object + description: Account usage information for the current month + properties: + iqs: + type: integer + description: The number of image queries in the current month. + escalations: + type: integer + description: The number of escalations in the current month. + active_detectors: + type: integer + description: The number of active detectors in the current month. + iqs_limit: + type: integer + nullable: true + description: The limit on the number of image queries in the current month. + escalations_limit: + type: integer + nullable: true + description: The limit on the number of escalations in the current month. + active_detectors_limit: + type: integer + nullable: true + description: The limit on the number of active detectors in the current + month. + required: + - active_detectors + - active_detectors_limit + - escalations + - escalations_limit + - iqs + - iqs_limit + x-internal: true AllNotes: type: object description: |- @@ -1789,6 +1844,9 @@ components: - CLOUD - CUST - HUMAN_CLOUD_ENSEMBLE + - AI_CLOUD + - AI_CLOUD_ENSEMBLE + - HUMAN_AI_CLOUD_ENSEMBLE - ALG - ALG_REC - ALG_UNCLEAR @@ -1809,6 +1867,9 @@ components: - USER - CLOUD_ENSEMBLE - ALGORITHM + - AI_CLOUD + - AI_CLOUD_ENSEMBLE + - HUMAN_AI_CLOUD_ENSEMBLE - EDGE Label: type: string diff --git a/src/groundlight/client.py b/src/groundlight/client.py index a074c261..06acdcf4 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -12,6 +12,7 @@ from groundlight_openapi_client.api.detectors_api import DetectorsApi from groundlight_openapi_client.api.image_queries_api import ImageQueriesApi from groundlight_openapi_client.api.labels_api import LabelsApi +from groundlight_openapi_client.api.month_to_date_account_info_api import MonthToDateAccountInfoApi from groundlight_openapi_client.api.user_api import UserApi from groundlight_openapi_client.exceptions import NotFoundException, UnauthorizedException from groundlight_openapi_client.model.b_box_geometry_request import BBoxGeometryRequest @@ -25,6 +26,7 @@ from groundlight_openapi_client.model.status_enum import StatusEnum from model import ( ROI, + AccountMonthToDateInfo, BBoxGeometry, BinaryClassificationResult, Detector, @@ -186,6 +188,7 @@ def __init__( self.image_queries_api = ImageQueriesApi(self.api_client) self.user_api = UserApi(self.api_client) self.labels_api = LabelsApi(self.api_client) + self.month_to_date_api = MonthToDateAccountInfoApi(self.api_client) self.logged_in_user = "(not-logged-in)" self._verify_connectivity() @@ -625,6 +628,28 @@ def list_image_queries( image_queries.results = [self._fixup_image_query(iq) for iq in image_queries.results] return image_queries + def get_month_to_date_usage(self) -> AccountMonthToDateInfo: + """ + Get the account's month-to-date usage information including image queries (IQs), + escalations, and active detectors with their respective limits. + + **Example Usage:** + + gl = Groundlight() + + # Get month-to-date usage information + usage = gl.get_month_to_date_usage() + + # Access usage metrics + print(f"Image queries used: {usage.iqs} / {usage.iqs_limit}") + print(f"Escalations used: {usage.escalations} / {usage.escalations_limit}") + print(f"Active detectors: {usage.active_detectors} / {usage.active_detectors_limit}") + + :return: AccountMonthToDateInfo object containing usage metrics and limits + """ + obj = self.month_to_date_api.month_to_date_account_info(_request_timeout=DEFAULT_REQUEST_TIMEOUT) + return AccountMonthToDateInfo.model_validate(obj.to_dict()) + def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals self, detector: Union[Detector, str], diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index f9cce8e1..c0c49acf 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -6,7 +6,6 @@ import string import time from datetime import datetime -from http import HTTPStatus from typing import Any, Dict, Optional, Union import pytest @@ -878,12 +877,8 @@ def test_delete_detector(gl: Groundlight): gl.delete_detector(detector) # Verify the detector is actually deleted - with pytest.raises(ApiException) as exc_info: + with pytest.raises(ApiException): gl.get_detector(detector.id) - err = exc_info.value - assert err.status == HTTPStatus.GONE - payload = json.loads(err.body) - assert det_id in payload.get("message", "") # Create another detector to test deletion by ID string and that an attached image query is deleted name2 = f"Test delete detector 2 {datetime.utcnow()}" @@ -896,12 +891,8 @@ def test_delete_detector(gl: Groundlight): gl.delete_detector(detector2.id) # Verify the second detector is also deleted - with pytest.raises(ApiException) as exc_info: + with pytest.raises(ApiException): gl.get_detector(detector2.id) - err = exc_info.value - assert err.status == HTTPStatus.GONE - payload = json.loads(err.body) - assert det_id in payload.get("message", "") # Verify the image query is also deleted with pytest.raises(NotFoundException): diff --git a/test/unit/test_month_to_date_usage.py b/test/unit/test_month_to_date_usage.py new file mode 100644 index 00000000..d5b0debc --- /dev/null +++ b/test/unit/test_month_to_date_usage.py @@ -0,0 +1,235 @@ +# ruff: noqa: PLR2004 + +from unittest.mock import Mock + +import pytest +from model import AccountMonthToDateInfo + + +def test_get_month_to_date_usage_success(gl): + """Test successful retrieval of month-to-date usage information.""" + usage = gl.get_month_to_date_usage() + + # Verify the return type + assert isinstance(usage, AccountMonthToDateInfo) + + # Verify all required fields are present and have correct types + assert isinstance(usage.iqs, int) + assert isinstance(usage.escalations, int) + assert isinstance(usage.active_detectors, int) + + # Handle case where limits might be None (API may not provide limits) + if usage.iqs_limit is not None: + assert isinstance(usage.iqs_limit, int) + assert usage.iqs_limit >= 0 + if usage.escalations_limit is not None: + assert isinstance(usage.escalations_limit, int) + assert usage.escalations_limit >= 0 + if usage.active_detectors_limit is not None: + assert isinstance(usage.active_detectors_limit, int) + assert usage.active_detectors_limit >= 0 + + # Verify usage values are non-negative + assert usage.iqs >= 0 + assert usage.escalations >= 0 + assert usage.active_detectors >= 0 + + +def test_get_month_to_date_usage_with_mock(): + """Test get_month_to_date_usage with mocked API response.""" + from groundlight import Groundlight + + # Create mock response data + mock_response_data = { + "iqs": 150, + "escalations": 25, + "active_detectors": 10, + "iqs_limit": 1000, + "escalations_limit": 100, + "active_detectors_limit": 50, + } + + # Create mock API response object + mock_api_response = Mock() + mock_api_response.to_dict.return_value = mock_response_data + + # Create mock month_to_date_api + mock_month_to_date_api = Mock() + mock_month_to_date_api.month_to_date_account_info.return_value = mock_api_response + + # Create Groundlight instance and patch the month_to_date_api + gl = Groundlight() + gl.month_to_date_api = mock_month_to_date_api + + # Call the method + usage = gl.get_month_to_date_usage() + + # Verify the API was called correctly + mock_month_to_date_api.month_to_date_account_info.assert_called_once_with( + _request_timeout=10 # DEFAULT_REQUEST_TIMEOUT + ) + + # Verify the response parsing + assert isinstance(usage, AccountMonthToDateInfo) + assert usage.iqs == 150 + assert usage.escalations == 25 + assert usage.active_detectors == 10 + assert usage.iqs_limit == 1000 + assert usage.escalations_limit == 100 + assert usage.active_detectors_limit == 50 + + +def test_get_month_to_date_usage_api_error(): + """Test get_month_to_date_usage when API returns an error.""" + from groundlight import Groundlight + from groundlight_openapi_client.exceptions import ApiException + + # Create mock month_to_date_api that raises an exception + mock_month_to_date_api = Mock() + mock_month_to_date_api.month_to_date_account_info.side_effect = ApiException( + status=500, reason="Internal Server Error" + ) + + # Create Groundlight instance and patch the month_to_date_api + gl = Groundlight() + gl.month_to_date_api = mock_month_to_date_api + + # Verify the exception is raised + with pytest.raises(ApiException) as exc_info: + gl.get_month_to_date_usage() + + assert exc_info.value.status == 500 + assert "Internal Server Error" in str(exc_info.value) + + +def test_get_month_to_date_usage_unauthorized(): + """Test get_month_to_date_usage when unauthorized.""" + from groundlight import Groundlight + from groundlight_openapi_client.exceptions import UnauthorizedException + + # Create mock month_to_date_api that raises an unauthorized exception + mock_month_to_date_api = Mock() + mock_month_to_date_api.month_to_date_account_info.side_effect = UnauthorizedException( + status=401, reason="Unauthorized" + ) + + # Create Groundlight instance and patch the month_to_date_api + gl = Groundlight() + gl.month_to_date_api = mock_month_to_date_api + + # Verify the exception is raised + with pytest.raises(UnauthorizedException) as exc_info: + gl.get_month_to_date_usage() + + assert exc_info.value.status == 401 + assert "Unauthorized" in str(exc_info.value) + + +def test_get_month_to_date_usage_zero_values(): + """Test get_month_to_date_usage with zero values (new account scenario).""" + from groundlight import Groundlight + + # Create mock response data with zero values + mock_response_data = { + "iqs": 0, + "escalations": 0, + "active_detectors": 0, + "iqs_limit": 100, + "escalations_limit": 10, + "active_detectors_limit": 5, + } + + # Create mock API response object + mock_api_response = Mock() + mock_api_response.to_dict.return_value = mock_response_data + + # Create mock month_to_date_api + mock_month_to_date_api = Mock() + mock_month_to_date_api.month_to_date_account_info.return_value = mock_api_response + + # Create Groundlight instance and patch the month_to_date_api + gl = Groundlight() + gl.month_to_date_api = mock_month_to_date_api + + # Call the method + usage = gl.get_month_to_date_usage() + + # Verify the response parsing with zero values + assert isinstance(usage, AccountMonthToDateInfo) + assert usage.iqs == 0 + assert usage.escalations == 0 + assert usage.active_detectors == 0 + assert usage.iqs_limit == 100 + assert usage.escalations_limit == 10 + assert usage.active_detectors_limit == 5 + + +def test_get_month_to_date_usage_usage_limits(gl): + """Test that usage values don't exceed their limits.""" + usage = gl.get_month_to_date_usage() + + # Verify usage doesn't exceed limits (only if limits are provided) + if usage.iqs_limit is not None: + assert usage.iqs <= usage.iqs_limit + if usage.escalations_limit is not None: + assert usage.escalations <= usage.escalations_limit + if usage.active_detectors_limit is not None: + assert usage.active_detectors <= usage.active_detectors_limit + + +def test_get_month_to_date_usage_documentation_example(gl): + """Test the example usage shown in the method's docstring.""" + usage = gl.get_month_to_date_usage() + + # Test the example from the docstring + print(f"Image queries used: {usage.iqs} / {usage.iqs_limit}") + print(f"Escalations used: {usage.escalations} / {usage.escalations_limit}") + print(f"Active detectors: {usage.active_detectors} / {usage.active_detectors_limit}") + + # Verify the format is correct (this test mainly ensures the example runs without errors) + assert isinstance(usage.iqs, int) + # Handle case where limits might be None + if usage.iqs_limit is not None: + assert isinstance(usage.iqs_limit, int) + if usage.escalations_limit is not None: + assert isinstance(usage.escalations_limit, int) + if usage.active_detectors_limit is not None: + assert isinstance(usage.active_detectors_limit, int) + assert isinstance(usage.escalations, int) + assert isinstance(usage.active_detectors, int) + + +@pytest.mark.integration +def test_get_month_to_date_usage_integration(gl): + """Integration test for get_month_to_date_usage with real API call.""" + usage = gl.get_month_to_date_usage() + + # Verify the response structure + assert isinstance(usage, AccountMonthToDateInfo) + + # Verify all fields are present + assert hasattr(usage, "iqs") + assert hasattr(usage, "escalations") + assert hasattr(usage, "active_detectors") + assert hasattr(usage, "iqs_limit") + assert hasattr(usage, "escalations_limit") + assert hasattr(usage, "active_detectors_limit") + + # Verify data types + assert isinstance(usage.iqs, int) + assert isinstance(usage.escalations, int) + assert isinstance(usage.active_detectors, int) + + # Handle case where limits might be None + if usage.iqs_limit is not None: + assert isinstance(usage.iqs_limit, int) + if usage.escalations_limit is not None: + assert isinstance(usage.escalations_limit, int) + if usage.active_detectors_limit is not None: + assert isinstance(usage.active_detectors_limit, int) + + # Verify logical constraints + assert usage.iqs >= 0 + assert usage.escalations >= 0 + assert usage.active_detectors >= 0 + # Note: limits might be None, so we can't assert they're > 0