diff --git a/libs/labelbox/src/labelbox/schema/ontology.py b/libs/labelbox/src/labelbox/schema/ontology.py index 32d5a3884..f6c758faa 100644 --- a/libs/labelbox/src/labelbox/schema/ontology.py +++ b/libs/labelbox/src/labelbox/schema/ontology.py @@ -98,7 +98,7 @@ class Classification: removed in a future release. Dropdown will also no longer be able to be created in the Editor on 3/31/2022. - A classfication to be added to a Project's ontology. The + A classification to be added to a Project's ontology. The classification is dependent on the Classification Type. To instantiate, the "class_type" and "name" parameters must @@ -125,8 +125,10 @@ class Classification: instructions: (str) required: (bool) options: (list) + ui_mode: (str) schema_id: (str) feature_schema_id: (str) + scope: (str) """ class Type(Enum): @@ -138,6 +140,10 @@ class Type(Enum): class Scope(Enum): GLOBAL = "global" INDEX = "index" + + class UIMode(Enum): + HOTKEY = "hotkey" + SEARCHABLE = "searchable" _REQUIRES_OPTIONS = {Type.CHECKLIST, Type.RADIO, Type.DROPDOWN} @@ -149,6 +155,7 @@ class Scope(Enum): schema_id: Optional[str] = None feature_schema_id: Optional[str] = None scope: Scope = None + ui_mode: Optional[UIMode] = None # How this classification should be answered (e.g. hotkeys / autocomplete, etc) def __post_init__(self): if self.class_type == Classification.Type.DROPDOWN: @@ -180,6 +187,7 @@ def from_dict(cls, dictionary: Dict[str, Any]) -> Dict[str, Any]: instructions=dictionary["instructions"], required=dictionary.get("required", False), options=[Option.from_dict(o) for o in dictionary["options"]], + ui_mode=cls.UIMode(dictionary["uiMode"]) if "uiMode" in dictionary else None, schema_id=dictionary.get("schemaNodeId", None), feature_schema_id=dictionary.get("featureSchemaId", None), scope=cls.Scope(dictionary.get("scope", cls.Scope.GLOBAL))) @@ -198,6 +206,9 @@ def asdict(self, is_subclass: bool = False) -> Dict[str, Any]: "schemaNodeId": self.schema_id, "featureSchemaId": self.feature_schema_id } + if (self.class_type == self.Type.RADIO or self.class_type == self.Type.CHECKLIST) and self.ui_mode: + # added because this key does nothing for text so no point of including + classification["uiMode"] = self.ui_mode.value if is_subclass: return classification classification[ diff --git a/libs/labelbox/tests/unit/test_unit_ontology.py b/libs/labelbox/tests/unit/test_unit_ontology.py index 82c709663..ac53827c6 100644 --- a/libs/labelbox/tests/unit/test_unit_ontology.py +++ b/libs/labelbox/tests/unit/test_unit_ontology.py @@ -2,6 +2,7 @@ from labelbox.exceptions import InconsistentOntologyException from labelbox import Tool, Classification, Option, OntologyBuilder +from itertools import product _SAMPLE_ONTOLOGY = { "tools": [{ @@ -46,6 +47,8 @@ "nested classification", "type": "radio", + 'uiMode': + "searchable", "options": [{ "schemaNodeId": None, @@ -120,6 +123,8 @@ "radio", "scope": "global", + 'uiMode': + "searchable", "options": [{ "schemaNodeId": None, "featureSchemaId": None, @@ -148,6 +153,11 @@ def test_create_classification(class_type) -> None: c = Classification(class_type=class_type, name="classification") assert (c.class_type == class_type) +@pytest.mark.parametrize("ui_mode_type, class_type", list(product(list(Classification.UIMode), list(Classification.Type)))) +def test_create_classification_with_ui_mode(ui_mode_type, class_type) -> None: + c = Classification(name="classification", class_type=class_type, ui_mode=ui_mode_type) + assert (c.ui_mode == ui_mode_type) + @pytest.mark.parametrize("value, expected_value, typing", [(3, 3, int), ("string", "string", str)])