Skip to content

Commit ee5d7e1

Browse files
brandon-wadaAuto-format BotTyler Romero
authored
Multiclass label support (#285)
Tests and labeling support for multiclass detectors --------- Co-authored-by: Auto-format Bot <autoformatbot@groundlight.ai> Co-authored-by: Tyler Romero <tyler@groundlight.ai>
1 parent 49639d4 commit ee5d7e1

File tree

11 files changed

+203
-194
lines changed

11 files changed

+203
-194
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ disable=raw-checker-failed,
8787
missing-class-docstring,
8888
missing-function-docstring,
8989
invalid-name,
90-
too-few-public-methods,
9190
line-too-long,
9291
import-error, # we only install linter dependencies in CI/CD
9392
wrong-import-order, # we use ruff to enforce import order
93+
duplicate-code, # pylint has a tendancy to capture docstrings as duplicate code
9494

9595

9696
# Enable the message, report, category or checker with the given id(s). You can

generated/docs/ImageQueriesApi.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,12 @@ with groundlight_openapi_client.ApiClient(configuration) as api_client:
203203
api_instance = image_queries_api.ImageQueriesApi(api_client)
204204
page = 1 # int | A page number within the paginated result set. (optional)
205205
page_size = 1 # int | Number of items to return per page. (optional)
206+
predictor_id = "predictor_id_example" # str | Optionally filter image queries by detector ID. (optional)
206207

207208
# example passing only required values which don't have defaults set
208209
# and optional values
209210
try:
210-
api_response = api_instance.list_image_queries(page=page, page_size=page_size)
211+
api_response = api_instance.list_image_queries(page=page, page_size=page_size, predictor_id=predictor_id)
211212
pprint(api_response)
212213
except groundlight_openapi_client.ApiException as e:
213214
print("Exception when calling ImageQueriesApi->list_image_queries: %s\n" % e)
@@ -220,6 +221,7 @@ Name | Type | Description | Notes
220221
------------- | ------------- | ------------- | -------------
221222
**page** | **int**| A page number within the paginated result set. | [optional]
222223
**page_size** | **int**| Number of items to return per page. | [optional]
224+
**predictor_id** | **str**| Optionally filter image queries by detector ID. | [optional]
223225

224226
### Return type
225227

generated/groundlight_openapi_client/api/image_queries_api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def __init__(self, api_client=None):
129129
"all": [
130130
"page",
131131
"page_size",
132+
"predictor_id",
132133
],
133134
"required": [],
134135
"nullable": [],
@@ -141,14 +142,17 @@ def __init__(self, api_client=None):
141142
"openapi_types": {
142143
"page": (int,),
143144
"page_size": (int,),
145+
"predictor_id": (str,),
144146
},
145147
"attribute_map": {
146148
"page": "page",
147149
"page_size": "page_size",
150+
"predictor_id": "predictor_id",
148151
},
149152
"location_map": {
150153
"page": "query",
151154
"page_size": "query",
155+
"predictor_id": "query",
152156
},
153157
"collection_format_map": {},
154158
},
@@ -375,6 +379,7 @@ def list_image_queries(self, **kwargs):
375379
Keyword Args:
376380
page (int): A page number within the paginated result set.. [optional]
377381
page_size (int): Number of items to return per page.. [optional]
382+
predictor_id (str): Optionally filter image queries by detector ID.. [optional]
378383
_return_http_data_only (bool): response data without head status
379384
code and headers. Default is True.
380385
_preload_content (bool): if False, the urllib3.HTTPResponse object

generated/groundlight_openapi_client/model/patched_detector_request.py

Lines changed: 93 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
Generated by: https://openapi-generator.tech
99
"""
1010

11-
1211
import re # noqa: F401
1312
import sys # noqa: F401
1413

@@ -25,7 +24,7 @@
2524
file_type,
2625
none_type,
2726
validate_get_composed_info,
28-
OpenApiModel
27+
OpenApiModel,
2928
)
3029
from groundlight_openapi_client.exceptions import ApiAttributeError
3130

@@ -34,9 +33,10 @@ def lazy_import():
3433
from groundlight_openapi_client.model.blank_enum import BlankEnum
3534
from groundlight_openapi_client.model.escalation_type_enum import EscalationTypeEnum
3635
from groundlight_openapi_client.model.status_enum import StatusEnum
37-
globals()['BlankEnum'] = BlankEnum
38-
globals()['EscalationTypeEnum'] = EscalationTypeEnum
39-
globals()['StatusEnum'] = StatusEnum
36+
37+
globals()["BlankEnum"] = BlankEnum
38+
globals()["EscalationTypeEnum"] = EscalationTypeEnum
39+
globals()["StatusEnum"] = StatusEnum
4040

4141

4242
class PatchedDetectorRequest(ModelNormal):
@@ -63,21 +63,20 @@ class PatchedDetectorRequest(ModelNormal):
6363
as additional properties values.
6464
"""
6565

66-
allowed_values = {
67-
}
66+
allowed_values = {}
6867

6968
validations = {
70-
('name',): {
71-
'max_length': 200,
72-
'min_length': 1,
69+
("name",): {
70+
"max_length": 200,
71+
"min_length": 1,
7372
},
74-
('confidence_threshold',): {
75-
'inclusive_maximum': 1.0,
76-
'inclusive_minimum': 0.0,
73+
("confidence_threshold",): {
74+
"inclusive_maximum": 1.0,
75+
"inclusive_minimum": 0.0,
7776
},
78-
('patience_time',): {
79-
'inclusive_maximum': 3600,
80-
'inclusive_minimum': 0,
77+
("patience_time",): {
78+
"inclusive_maximum": 3600,
79+
"inclusive_minimum": 0,
8180
},
8281
}
8382

@@ -88,7 +87,17 @@ def additional_properties_type():
8887
of type self, this must run after the class is loaded
8988
"""
9089
lazy_import()
91-
return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501
90+
return (
91+
bool,
92+
date,
93+
datetime,
94+
dict,
95+
float,
96+
int,
97+
list,
98+
str,
99+
none_type,
100+
) # noqa: E501
92101

93102
_nullable = False
94103

@@ -104,28 +113,46 @@ def openapi_types():
104113
"""
105114
lazy_import()
106115
return {
107-
'name': (str,), # noqa: E501
108-
'confidence_threshold': (float,), # noqa: E501
109-
'patience_time': (float,), # noqa: E501
110-
'status': (bool, date, datetime, dict, float, int, list, str, none_type,), # noqa: E501
111-
'escalation_type': (bool, date, datetime, dict, float, int, list, str, none_type,), # noqa: E501
116+
"name": (str,), # noqa: E501
117+
"confidence_threshold": (float,), # noqa: E501
118+
"patience_time": (float,), # noqa: E501
119+
"status": (
120+
bool,
121+
date,
122+
datetime,
123+
dict,
124+
float,
125+
int,
126+
list,
127+
str,
128+
none_type,
129+
), # noqa: E501
130+
"escalation_type": (
131+
bool,
132+
date,
133+
datetime,
134+
dict,
135+
float,
136+
int,
137+
list,
138+
str,
139+
none_type,
140+
), # noqa: E501
112141
}
113142

114143
@cached_property
115144
def discriminator():
116145
return None
117146

118-
119147
attribute_map = {
120-
'name': 'name', # noqa: E501
121-
'confidence_threshold': 'confidence_threshold', # noqa: E501
122-
'patience_time': 'patience_time', # noqa: E501
123-
'status': 'status', # noqa: E501
124-
'escalation_type': 'escalation_type', # noqa: E501
148+
"name": "name", # noqa: E501
149+
"confidence_threshold": "confidence_threshold", # noqa: E501
150+
"patience_time": "patience_time", # noqa: E501
151+
"status": "status", # noqa: E501
152+
"escalation_type": "escalation_type", # noqa: E501
125153
}
126154

127-
read_only_vars = {
128-
}
155+
read_only_vars = {}
129156

130157
_composed_schemas = {}
131158

@@ -172,17 +199,18 @@ def _from_openapi_data(cls, *args, **kwargs): # noqa: E501
172199
escalation_type (bool, date, datetime, dict, float, int, list, str, none_type): Category that define internal proccess for labeling image queries * `STANDARD` - STANDARD * `NO_HUMAN_LABELING` - NO_HUMAN_LABELING. [optional] # noqa: E501
173200
"""
174201

175-
_check_type = kwargs.pop('_check_type', True)
176-
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
177-
_path_to_item = kwargs.pop('_path_to_item', ())
178-
_configuration = kwargs.pop('_configuration', None)
179-
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
202+
_check_type = kwargs.pop("_check_type", True)
203+
_spec_property_naming = kwargs.pop("_spec_property_naming", False)
204+
_path_to_item = kwargs.pop("_path_to_item", ())
205+
_configuration = kwargs.pop("_configuration", None)
206+
_visited_composed_classes = kwargs.pop("_visited_composed_classes", ())
180207

181208
self = super(OpenApiModel, cls).__new__(cls)
182209

183210
if args:
184211
raise ApiTypeError(
185-
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
212+
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments."
213+
% (
186214
args,
187215
self.__class__.__name__,
188216
),
@@ -198,22 +226,24 @@ def _from_openapi_data(cls, *args, **kwargs): # noqa: E501
198226
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
199227

200228
for var_name, var_value in kwargs.items():
201-
if var_name not in self.attribute_map and \
202-
self._configuration is not None and \
203-
self._configuration.discard_unknown_keys and \
204-
self.additional_properties_type is None:
229+
if (
230+
var_name not in self.attribute_map
231+
and self._configuration is not None
232+
and self._configuration.discard_unknown_keys
233+
and self.additional_properties_type is None
234+
):
205235
# discard variable.
206236
continue
207237
setattr(self, var_name, var_value)
208238
return self
209239

210240
required_properties = set([
211-
'_data_store',
212-
'_check_type',
213-
'_spec_property_naming',
214-
'_path_to_item',
215-
'_configuration',
216-
'_visited_composed_classes',
241+
"_data_store",
242+
"_check_type",
243+
"_spec_property_naming",
244+
"_path_to_item",
245+
"_configuration",
246+
"_visited_composed_classes",
217247
])
218248

219249
@convert_js_args_to_python_args
@@ -258,15 +288,16 @@ def __init__(self, *args, **kwargs): # noqa: E501
258288
escalation_type (bool, date, datetime, dict, float, int, list, str, none_type): Category that define internal proccess for labeling image queries * `STANDARD` - STANDARD * `NO_HUMAN_LABELING` - NO_HUMAN_LABELING. [optional] # noqa: E501
259289
"""
260290

261-
_check_type = kwargs.pop('_check_type', True)
262-
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
263-
_path_to_item = kwargs.pop('_path_to_item', ())
264-
_configuration = kwargs.pop('_configuration', None)
265-
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
291+
_check_type = kwargs.pop("_check_type", True)
292+
_spec_property_naming = kwargs.pop("_spec_property_naming", False)
293+
_path_to_item = kwargs.pop("_path_to_item", ())
294+
_configuration = kwargs.pop("_configuration", None)
295+
_visited_composed_classes = kwargs.pop("_visited_composed_classes", ())
266296

267297
if args:
268298
raise ApiTypeError(
269-
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
299+
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments."
300+
% (
270301
args,
271302
self.__class__.__name__,
272303
),
@@ -282,13 +313,17 @@ def __init__(self, *args, **kwargs): # noqa: E501
282313
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
283314

284315
for var_name, var_value in kwargs.items():
285-
if var_name not in self.attribute_map and \
286-
self._configuration is not None and \
287-
self._configuration.discard_unknown_keys and \
288-
self.additional_properties_type is None:
316+
if (
317+
var_name not in self.attribute_map
318+
and self._configuration is not None
319+
and self._configuration.discard_unknown_keys
320+
and self.additional_properties_type is None
321+
):
289322
# discard variable.
290323
continue
291324
setattr(self, var_name, var_value)
292325
if var_name in self.read_only_vars:
293-
raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate "
294-
f"class with read only attributes.")
326+
raise ApiAttributeError(
327+
f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate "
328+
"class with read only attributes."
329+
)

generated/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: public-api.yaml
3-
# timestamp: 2024-12-09T18:29:17+00:00
3+
# timestamp: 2024-12-10T01:13:13+00:00
44

55
from __future__ import annotations
66

src/groundlight/binary_labels.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,3 @@ def convert_internal_label_to_display(
5353

5454
logger.warning(f"Unrecognized internal label {label} - leaving it alone as a string.")
5555
return label
56-
57-
58-
def convert_display_label_to_internal(
59-
context: Union[ImageQuery, Detector, str], # pylint: disable=unused-argument
60-
label: Union[Label, str],
61-
) -> str:
62-
"""Convert a label that comes from the user into the label string that we send to the server. We
63-
are strict here, and only allow YES/NO.
64-
65-
NOTE: We accept case-insensitive label strings from the user, but we send UPPERCASE labels to
66-
the server. E.g., user inputs "yes" -> the label is returned as "YES".
67-
"""
68-
# NOTE: In the future we should validate against actually supported labels for the detector
69-
if not isinstance(label, str):
70-
raise ValueError(f"Expected a string label, but got {label} of type {type(label)}")
71-
upper = label.upper()
72-
if upper == Label.YES:
73-
return DeprecatedLabel.PASS.value
74-
if upper == Label.NO:
75-
return DeprecatedLabel.FAIL.value
76-
77-
raise ValueError(f"Invalid label string '{label}'. Must be one of '{Label.YES.value}','{Label.NO.value}'.")

0 commit comments

Comments
 (0)