diff --git a/annofabapi/util/attribute_restrictions.py b/annofabapi/util/attribute_restrictions.py index e146a912..60897388 100644 --- a/annofabapi/util/attribute_restrictions.py +++ b/annofabapi/util/attribute_restrictions.py @@ -32,7 +32,9 @@ from enum import Enum from typing import Any, NoReturn, cast -from pydantic import BaseModel, ConfigDict, Field, field_serializer, model_validator +from pydantic import BaseModel, ConfigDict, Field, GetJsonSchemaHandler, field_serializer, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema from annofabapi.pydantic_models.additional_data_definition_type import AdditionalDataDefinitionType from annofabapi.util.annotation_specs import AnnotationSpecsAccessor, AttributeChoice, AttributeDefinition, get_choice, get_english_message @@ -49,7 +51,7 @@ class RestrictionAstType(str, Enum): IS_EMPTY = "is_empty" """属性値が空であることを表すAST種別です。""" IS_NOT_EMPTY = "is_not_empty" - """属性値が空でないことを表すAST種別です。""" + """属性値が空でないことを表すAST種別です。必須制約に相当します。""" EQUALS_STRING = "equals_string" """文字列属性またはtracking属性が指定文字列と一致することを表すAST種別です。""" NOT_EQUALS_STRING = "not_equals_string" @@ -76,6 +78,44 @@ class RestrictionAstType(str, Enum): def __str__(self) -> str: return self.value + @property + def description(self) -> str: + """AST種別の説明文を返します。""" + return _RESTRICTION_AST_TYPE_DESCRIPTIONS[self] + + @classmethod + def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + """AST種別ごとの説明を含むJSON Schemaを返します。""" + json_schema = handler(core_schema) + json_schema["oneOf"] = [ + { + "const": ast_type.value, + "title": ast_type.name, + "description": ast_type.description, + } + for ast_type in cls + ] + return json_schema + + +_RESTRICTION_AST_TYPE_DESCRIPTIONS: dict[RestrictionAstType, str] = { + RestrictionAstType.CHECKED: "チェックボックス属性がチェックされていることを表すAST種別です。", + RestrictionAstType.UNCHECKED: "チェックボックス属性がチェックされていないことを表すAST種別です。", + RestrictionAstType.IS_EMPTY: "属性値が空であることを表すAST種別です。", + RestrictionAstType.IS_NOT_EMPTY: "属性値が空でないことを表すAST種別です。必須制約に相当します。", + RestrictionAstType.EQUALS_STRING: "文字列属性またはtracking属性が指定文字列と一致することを表すAST種別です。", + RestrictionAstType.NOT_EQUALS_STRING: "文字列属性またはtracking属性が指定文字列と一致しないことを表すAST種別です。", + RestrictionAstType.MATCHES_STRING: "文字列属性が指定した正規表現に一致することを表すAST種別です。", + RestrictionAstType.NOT_MATCHES_STRING: "文字列属性が指定した正規表現に一致しないことを表すAST種別です。", + RestrictionAstType.EQUALS_INTEGER: "整数属性が指定した整数値と一致することを表すAST種別です。", + RestrictionAstType.NOT_EQUALS_INTEGER: "整数属性が指定した整数値と一致しないことを表すAST種別です。", + RestrictionAstType.HAS_CHOICE: "選択属性で指定した選択肢が選ばれていることを表すAST種別です。", + RestrictionAstType.NOT_HAS_CHOICE: "選択属性で指定した選択肢が選ばれていないことを表すAST種別です。", + RestrictionAstType.HAS_LABEL: "リンク属性が指定したラベル群のいずれかを指すことを表すAST種別です。", + RestrictionAstType.CAN_INPUT: "属性が編集可能かどうかを表すAST種別です。", + RestrictionAstType.IMPLY: "前提を満たす場合に結論を要求する含意制約を表すAST種別です。", +} + class Restriction(ABC): """属性の制約を表すクラス。""" diff --git a/tests/util/test_attribute_restrictions.py b/tests/util/test_attribute_restrictions.py index edfbd3fc..af54bce7 100644 --- a/tests/util/test_attribute_restrictions.py +++ b/tests/util/test_attribute_restrictions.py @@ -437,10 +437,22 @@ def test__invalid_field_type(self): def test__model_json_schema(self): actual = RestrictionAst.model_json_schema() properties = actual["$defs"]["RestrictionAst"]["properties"] + ast_type_schema = actual["$defs"]["RestrictionAstType"] assert properties["type"]["description"] == "ASTノードの種類です。" assert properties["attribute_name"]["description"] == "対象属性の名前です。" assert properties["premise"]["description"] == "`imply` ノードの前提です。" + assert ast_type_schema["description"] == "属性制約ASTの種別です。" + assert { + "const": "checked", + "title": "CHECKED", + "description": "チェックボックス属性がチェックされていることを表すAST種別です。", + } in ast_type_schema["oneOf"] + assert { + "const": "imply", + "title": "IMPLY", + "description": "前提を満たす場合に結論を要求する含意制約を表すAST種別です。", + } in ast_type_schema["oneOf"] class Test__get_attribute_restriction_catalog: