From 0a57ad07435a71e7ee3815e2766e99b28ca2f0da Mon Sep 17 00:00:00 2001 From: yuji38kwmt Date: Mon, 27 Jul 2026 01:14:44 +0900 Subject: [PATCH] Add inspection phrase accessor --- annofabapi/util/annotation_specs.py | 51 +++++++++++++++++++++++++++++ tests/util/test_annotation_specs.py | 47 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/annofabapi/util/annotation_specs.py b/annofabapi/util/annotation_specs.py index 5740f781..60bf459c 100644 --- a/annofabapi/util/annotation_specs.py +++ b/annofabapi/util/annotation_specs.py @@ -36,6 +36,13 @@ class AttributeDefinition(TypedDict): choices: list[AttributeChoice] | None +class InspectionPhrase(TypedDict): + """定型指摘です。""" + + id: str + text: InternationalizationMessage + + class LabelDefinition(TypedDict): """アノテーション仕様上のラベル定義です。""" @@ -261,6 +268,30 @@ def get_label(labels: list[LabelDefinition], *, label_id: str | None = None, lab return result[0] +def get_inspection_phrase( + inspection_phrases: list[InspectionPhrase], + *, + inspection_phrase_id: str, +) -> InspectionPhrase: + """ + 定型指摘を取得します。 + + Args: + inspection_phrases: 定型指摘のリスト + inspection_phrase_id: 定型指摘ID + + Raises: + ValueError: 引数に合致する定型指摘が見つからない。または複数見つかった。 + """ + result = [e for e in inspection_phrases if e["id"] == inspection_phrase_id] + + if len(result) == 0: + raise ValueError(f"定型指摘が見つかりませんでした。 :: inspection_phrase_id='{inspection_phrase_id}'") + if len(result) > 1: + raise ValueError(f"定型指摘が複数({len(result)}件)見つかりました。 :: inspection_phrase_id='{inspection_phrase_id}'") + return result[0] + + class AnnotationSpecsAccessor: """ アノテーション仕様の情報にアクセスするためのクラス。 @@ -273,6 +304,7 @@ def __init__(self, annotation_specs: dict[str, Any]) -> None: self.annotation_specs = annotation_specs self.labels: list[LabelDefinition] = annotation_specs["labels"] self.additionals: list[AttributeDefinition] = annotation_specs["additionals"] + self.inspection_phrases: list[InspectionPhrase] = annotation_specs["inspection_phrases"] def get_attribute( self, *, attribute_id: str | None = None, attribute_name: str | None = None, label: LabelDefinition | None = None @@ -304,3 +336,22 @@ def get_label(self, *, label_id: str | None = None, label_name: str | None = Non """ return get_label(self.labels, label_id=label_id, label_name=label_name) + + def get_inspection_phrase( + self, + *, + inspection_phrase_id: str, + ) -> InspectionPhrase: + """ + 定型指摘を取得します。 + + Args: + inspection_phrase_id: 定型指摘ID + + Raises: + ValueError: 引数に合致する定型指摘が見つからない。または複数見つかった。 + """ + return get_inspection_phrase( + self.inspection_phrases, + inspection_phrase_id=inspection_phrase_id, + ) diff --git a/tests/util/test_annotation_specs.py b/tests/util/test_annotation_specs.py index 1d30baca..866fd6b4 100644 --- a/tests/util/test_annotation_specs.py +++ b/tests/util/test_annotation_specs.py @@ -3,6 +3,7 @@ from annofabapi.util.annotation_specs import ( AnnotationSpecsAccessor, AttributeChoice, + InspectionPhrase, LabelNameHolder, Lang, NameHolder, @@ -10,6 +11,7 @@ get_choice, get_choice_name_en, get_english_message, + get_inspection_phrase, get_label_name_en, get_message_with_lang, ) @@ -105,6 +107,26 @@ def setup_method(self): {"additional_data_definition_id": "1", "name": {"messages": [{"lang": "en-US", "message": "Color"}]}}, {"additional_data_definition_id": "2", "name": {"messages": [{"lang": "en-US", "message": "Size"}]}}, ], + "inspection_phrases": [ + { + "id": "inspection_phrase_id_1", + "text": { + "messages": [ + {"lang": "ja-JP", "message": "画像がぼやけています。"}, + {"lang": "en-US", "message": "The image is blurry."}, + ] + }, + }, + { + "id": "inspection_phrase_id_2", + "text": { + "messages": [ + {"lang": "ja-JP", "message": "対象物が隠れています。"}, + {"lang": "en-US", "message": "The object is occluded."}, + ] + }, + }, + ], } self.accessor = AnnotationSpecsAccessor(self.annotation_specs) @@ -147,6 +169,31 @@ def test_get_attribute_by_id_and_label__not_found(self): with pytest.raises(ValueError): self.accessor.get_attribute(attribute_id="1", label=label) + def test_get_inspection_phrase_by_id(self): + inspection_phrase = self.accessor.get_inspection_phrase(inspection_phrase_id="inspection_phrase_id_1") + assert inspection_phrase["id"] == "inspection_phrase_id_1" + assert get_message_with_lang(inspection_phrase["text"], Lang.EN_US) == "The image is blurry." + + def test_get_inspection_phrase_not_found(self): + with pytest.raises(ValueError): + self.accessor.get_inspection_phrase(inspection_phrase_id="inspection_phrase_id_3") + + +class Test__get_inspection_phrase: + def setup_method(self): + self.inspection_phrases: list[InspectionPhrase] = [ + {"id": "1", "text": {"messages": [{"lang": "en-US", "message": "Blurry image"}]}}, + {"id": "2", "text": {"messages": [{"lang": "en-US", "message": "Occluded object"}]}}, + ] + + def test_get_inspection_phrase_by_id(self): + inspection_phrase = get_inspection_phrase(self.inspection_phrases, inspection_phrase_id="1") + assert inspection_phrase["id"] == "1" + + def test_get_inspection_phrase_not_found(self): + with pytest.raises(ValueError): + get_inspection_phrase(self.inspection_phrases, inspection_phrase_id="3") + class Test__get_choice: def setup_method(self):