属性間制約

「属性Aの値がXならば、属性Bは必須である」というような属性間の制約の設定方法について記載します。

属性間制約とは

「属性Aの値がXならば、属性Bは必須である」というような、複数の属性間の制約を「属性間制約」と呼びます。

同じラベルに属する複数の属性間で制約を設けることができます。

属性間制約の設定の流れ

属性間制約はアノテーション仕様画面 では設定できません。CLIなどを利用して設定する必要があります。具体的には、以下のような手順になります。

  1. 属性間制約をJSONで記述する。
  2. annofabcli annotation_specs add_attribute_restrictionコマンドを実行して、属性間制約を設定する。
  3. 属性間制約が正しく設定されていることを確認する

属性間制約をJSONで記述する

以下のようなJSONで属性制約を記述します。以下のJSONは、「属性IDがAである属性の値がXならば、属性IDがBである属性の値は空文字列でない(必須である)」という制約を表しています。


{
    "additional_data_definition_id": "B",
    "condition": {
        "_type": "Imply",
        "premise": {
            "additional_data_definition_id": "A",
            "condition": {
                "_type": "Equals",
                "value": "X"
            }
        },
        "condition": {
            "_type": "NotEquals",
            "value": ""
        }
    }
}

condition.premiseに、「もし~ならば」という前提条件を記載して、condition.conditionに満たすべき制約を記載します。JSONの詳細は、getAnnotationSpecs APIのレスポンスを参照してください。AnnotationSpecsV3スキーマ配下のrestrictionsAdditionalDataRestrictionに詳細が記載されています。

annofabapi.util.attribute_restrictionsを利用して制約を記述する

上記のJSONを人間が直接記述する際は、以下の課題があります。

  • 属性名でなく属性IDを記述する必要がある。属性IDはデフォルトでUUIDなので、JSONを見ただけではどのような制約なのかが分からない
  • 属性の種類によって使用できる制約が異なるため、間違えて使用できない制約(トラッキングID属性でのMatches(正規表現に一致する)など)を設定してしまう。

annofabapi.util.attribute_restrictions モジュールを利用すると、上記の課題を解決できます。以下のようなPythonのコードで、属性間制約を記述できます。

  • 属性名で制約を定義できる
  • 属性の種類によって、利用できる制約がメソッドで定義されている
>>> import annofabapi
>>> import json
>>> from annofabapi.util.attribute_restrictions import AttributeFactory
>>> service = annofabapi.build()
>>> annotation_specs, _ = service.api.get_annotation_specs("prj1", query_params={"v": "3"})

>>> fac = AttributeFactory(annotation_specs)

# 「'occluded'チェックボックスがONならば、'note'テキストボックスは空ではない」という制約
>>> restriction = fac.checkbox(attribute_name="occluded").checked().imply(fac.string_textbox(attribute_name="note").is_not_empty())

>>> print(json.dumps(restriction.to_dict())
{
  "additional_data_definition_id": "9b05648d-1e16-4ea2-ab79-48907f5eed00",
  "condition": {
    "_type": "Imply",
    "premise": {
      "additional_data_definition_id": "2517f635-2269-4142-8ef4-16312b4cc9f7",
      "condition": {
        "_type": "Equals",
        "value": "true"
      }
    },
    "condition": {
      "_type": "NotEquals",
      "value": ""
    }
  }
}

属性間制約の例

よくある属性間制約の例を記載します。

  • チェックボックスAがONならば、テキストボックスXは入力できない(disabled)
    fac.checkbox(attribute_name="A").checked().imply(fac.string_textbox(attribute_name="X").disabled())
  • チェックボックスAがONならば、テキストボックスXは空文字である
    fac.checkbox(attribute_name="A").checked().imply(fac.string_textbox(attribute_name="X").is_not_empty())
  • チェックボックスAがONならば、ドロップダウン(またはラジオボタン)Xは選択肢Tを選択できない
    fac.checkbox(attribute_name="A").checked().imply(fac.selection(attribute_name="X").not_has_choice(choice_name="T"))
  • チェックボックスAがONかつチェックボックスBがONならば、テキストボックスXは空文字でない(必須)
    fac.checkbox(attribute_name="A").checked().imply(fac.checkbox(attribute_name="B").imply(fac.string_textbox(attribute_name="X").is_not_empty()))

annofab-cli-llm を使って自然言語から制約を記述する

annofabcli-llm parse_attribute_restrictionコマンドを使うことで、自然言語で記載されたアノテーションルールなどから、属性間制約のJSONを生成できます。

$ annofabcli-llm parse_attribute_restriction \
 --project_id $PROJECT_ID \
 --restriction_text 属性occludedがチェックされているとき属性notは必須
 --output_format annofab_json
[
  {
    "additional_data_definition_id": "attr_note",
    "condition": {
      "_type": "Imply",
      "premise": {
        "additional_data_definition_id": "attr_occluded",
        "condition": {
          "_type": "Equals",
          "value": "true"
        }
      },
      "condition": {
        "_type": "NotEquals",
        "value": ""
      }
    }
  }
]

annofab-cliを使って属性間制約を設定する

annofabcli annotation_specs add_attribute_restrictionコマンドを実行して、属性間制約を設定します。

$ cat restriction.json
{
  "additional_data_definition_id": "9b05648d-1e16-4ea2-ab79-48907f5eed00",
  "condition": {
    "_type": "Imply",
    "premise": {
      "additional_data_definition_id": "2517f635-2269-4142-8ef4-16312b4cc9f7",
      "condition": {
        "_type": "Equals",
        "value": "true"
      }
    },
    "condition": {
      "_type": "NotEquals",
      "value": ""
    }
  }
}

$ annofabcli annotation_specs add_attribute_restriction --project_id prj1 --restriction_json file://restriction.json

制約を確認する

CLIで確認する

属性間の制約はアノテーション仕様画面では確認できないので、annofab-cliで確認します。annotation_specs list_attribute_restrictionコマンドを実行すると、自然言語で表現された制約が出力されます。

$ annofabcli annotation_specs list_attribute_restriction --project_id prj1 > out.txt

$ cat out.txt
'B' DOES NOT EQUAL '' IF 'A' EQUALS 'X'

アノテーションエディタ画面で確認する

アノテーションエディタ画面で実際に操作して、制約が正しいかを確認します。

今回の例だと、「ドロップダウン属性Aの値がX AND ドロップダウン属性Bの値が空欄」のときのみ、「制約に合うように修正してください」というエラーメッセージが表示されます。

属性間の制約を満たしていないときのエラーメッセージ