Repository: kurusugawa-computer/annofab-cli (language: python)

Issue:
[REQ]`annotation`系コマンドで、タスクの担当者に関わらずアノテーションを登録できるようにする



Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 7e477bdc..4ff7e3f1 100644
--- a/README.md
+++ b/README.md
@@ -241,317 +241,12 @@ $ annofabcli project_member put --project_id prj2 --csv members.csv
 
 ## コマンド一覧
 
-### annotation change_attributes
-アノテーションの属性を一括で変更します。ただし、作業中状態のタスクのアノテーションの属性は変更できません。間違えてアノテーション属性を変更したときに復元できるようにするため、`--backup`でバックアップ用のディレクトリを指定することを推奨します。
-
-```
-# task.txtに記載されたタスクのアノテーションの属性をを変更する
-# carラベルのoccludedチェックボックスがTRUEのアノテーションに対して、occludedチェックボックスをOFFにする
-$ annofabcli annotation change_attributes --project_id prj1 --task_id file://task.txt \ 
---annotation_query '{"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": true}]}' \
---attributes '[{"additional_data_definition_name_en": "occluded", "flag": false}]' \
---backup backup_dir
-
-
-```
-
-
-
-
-### annotation delete
-タスク配下のアノテーションを削除します。ただし、作業中/完了状態のタスク、または「過去に割り当てられていて現在の担当者が自分自身でない」タスクのアノテーションは削除できません。
-
-間違えてアノテーションを削除してしまっときに復元できるようにするため、`--backup`でバックアップ用のディレクトリを指定することを推奨します。バックアップ用ディレクトリには、 [annotation dump](#annotation-dump) コマンドの出力結果と同等のアノテーション情報が保存されます。
-アノテーションの復元には [annotation restore](#annotation-restore) コマンドを使用してください。
-
-
-```
-# task.txtに記載されたタスクのアノテーションを削除します。削除する前のアノテーション情報は、`backup`ディレクトリに保存します。
-$ annofabcli annotation delete --project_id prj1 --task_id file://task.txt --backup backup
-
-# carラベルのoccludedチェックボックスがTRUEのアノテーションを削除します
-$ annofabcli annotation delete --project_id prj1 --task_id file://task.txt \ 
---annotation_query '{"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": true}]}' \
---backup backup_dir
-```
-
-### annotation dump
-指定したタスク配下のアノテーション情報をディレクトリに保存します。アノテーションをバックアップしたいときなどに利用できます。
-アノテーションの復元には [annotation restore](#annotation-restore) コマンドを使用してください。
-
-
-```
-# task.txtに記載されたタスクのアノテーションを、`output`ディレクトリに保存します。
-$ annofabcli annotation dump --project_id prj1 --task_id file://task.txt --output backup-dir
-```
-
-バックアップディレクトリは、以下のディレクトリ構成です（Simpleアノテーション(v2)と同じディレクトリ構成）。
-`{input_data_id}.json`のフォーマットは、[getEditorAnnotation](https://annofab.com/docs/api/#operation/getEditorAnnotation) APIのレスポンスと同じです。
-
-```
-ルートディレクトリ/
-├── {task_id}/
-│   ├── {input_data_id}.json
-│   ├── {input_data_id}/
-│          ├── {annotation_id}............ 塗りつぶしPNG画像
-```
-
-
-
-### annotation import
-アノテーションをプロジェクトにインポートします。
-アノテーションのフォーマットは、Simpleアノテーション(v2)と同じフォルダ構成のzipファイルまたはディレクトリです。
-
-```
-ルートディレクトリ/
-├── {task_id}/
-│   ├── {input_data_id}.json
-│   ├── {input_data_id}/
-│          ├── {annotation_id}............ 塗りつぶしPNG画像
-```
-
-JSONフォーマットのサンプルをは以下の通りです。SimpleアノテーションのJSONフォーマットに対応しています。詳しくは https://annofab.com/docs/api/#section/Simple-Annotation-ZIP を参照してください。
-
-
-```json
-{
-    "details": [
-        {
-            "label": "car",
-            "data": {
-                "left_top": {
-                    "x": 878,
-                    "y": 566
-                },
-                "right_bottom": {
-                    "x": 1065,
-                    "y": 701
-                },
-                "_type": "BoundingBox"
-            },
-            "attributes": {}
-        },
-        {
-            "label": "road",
-            "data": {
-                "data_uri": "b803193f-827f-4755-8228-e2c67d0786d9",
-                "_type": "SegmentationV2"
-            },
-            "attributes": {}
-        },
-        {
-            "label": "weather",
-            "data": {
-                "_type": "Classification"
-            },
-            "attributes": {
-                "sunny": true
-            }
-        }
-    ]
-}
-```
-
-以下のように`annotation_id`が指定されている場合、`annotation_id`もインポートされます。
-
-
-```json
-{
-    "details": [
-        {
-            "label": "car",
-            "annotation_id": "12345678-abcd-1234-abcd-1234abcd5678",
-            "data": {
-                "left_top": {
-                    "x": 878,
-                    "y": 566
-                },
-                "right_bottom": {
-                    "x": 1065,
-                    "y": 701
-                },
-                "_type": "BoundingBox"
-            },
-            "attributes": {}
-        },
-
-```
-
-アノテーションをインポートするには、事前に入力データ、タスク、アノテーション仕様を作成する必要があります。
-
-タスクの状態が作業中/完了の場合はインポートしません。
-
-
-```
-# prj1にアノテーションをインポートします。すでにアノテーションが登録されてる場合はスキップします。
-$ annofabcli annotation import --project_id prj1 --annotation simple-annotation.zip 
-
-# prj1にアノテーションをインポートします。すでに存在するアノテーションを上書きます。
-$ annofabcli annotation import --project_id prj1 --annotation simple-annotation.zip --overwrite
-```
-
-### annotation list_count
-`task_id`または`input_data_id`で集約したアノテーションの個数を、CSV形式で出力します。
-クエリのフォーマットは、[getAnnotationList API](https://annofab.com/docs/api/#operation/getAnnotationList)のクエリパラメータの`query`キー配下と同じです。
-`label_name_en`(label_idに対応), `additional_data_definition_name_en`(additional_data_definition_idに対応) キーも指定できます。
-
-
-```
-# car ラベルのアノテーション個数を出力する(task_idで集約)
-$ annofabcli annotation list_count --project_id prj1 \
- --annotation_query '{"label_name_en": "car"}'
-
-# car ラベルのアノテーション個数を出力する(input_data_idで集約)
-$ annofabcli annotation list_count --project_id prj1 \
- --annotation_query '{"label_name_en": "car"}' --gropu_by input_data_id
-
-# task.txtに記載されたtask_idの一覧から、car ラベルのアノテーション個数を出力する
-$ annofabcli annotation list_count --project_id prj1 \
- --annotation_query '{"label_name_en": "car"}'  --task_id file://task.txt
-
-# carラベルの"occluded"チェックボックスがONのアノテーションの個数を出力する
-$ annofabcli annotation list_count --project_id prj1 \
- --annotation_query '{"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": true}]}'
-
-# carラベルの"type"ラジオボタン/セレクトボックスが"bus"であるアノテーションの個数を出力する
-$ annofabcli annotation list_count --project_id prj1 \
- --annotation_query '{"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "choice_name_en": "bus"}]}'
-```
-
-#### task_idで集約したときの出力結果（CSV）
-
-| task_id    | annotation_count |
-|------------|------------------|
-| sample_030 | 1                |
-| sample_088 | 2                |
-
-
-#### input_data_idで集約したときの出力結果（CSV）
-
-| task_id    | input_data_id                        | annotation_count |
-|------------|--------------------------------------|------------------|
-| sample_030 | 5738d502-b0a0-4a82-9367-cceffd73cf57 | 1                |
-| sample_093 | dd82cf3a-a38c-4a04-91e7-a4f1ce9af585 | 2                |
-
-
-### annotation restore
-[annotation dump](#annotation-dump) コマンドの出力結果から、アノテーション情報をリストアします。ただし、作業中/完了状態のタスク、または「過去に割り当てられていて現在の担当者が自分自身でない」タスクはリストアできません。
-
-
-```
-# `backup`ディレクトリに保存したアノテーション情報を、`prj1`プロジェクトにリストアする。
-$annofabcli annotation restore --project_id prj1 --task_id file://task.txt --annotation backup-dir
-
-# `backup`ディレクトリに保存したアノテーション情報の内、`task.txt`に記載されたタスクのアノテーション情報を、`prj1`プロジェクトにリストアする。
-$ annofabcli annotation restore --project_id prj1 --task_id file://task.txt --annotation backup-dir
-```
-
-
-
-### annotation_specs history
-アノテーション仕様の履歴一覧を出力します。
-
-```
-# prj1のアノテーション仕様の履歴一覧を出力する
-$ annofabcli annotation_specs history --project_id prj1 
-```
-
-
-
-### annotation_specs list_label
-アノテーション仕様のラベル情報を出力します。
-
-```
-# prj1のアノテーション仕様のラベル情報を、人間が見やすい形式（`--format text`）で出力する
-$ annofabcli annotation_specs list_label --project_id prj1
-
-# prj1のアノテーション仕様のラベル情報を、インデントされたJSONで出力する。
-$ annofabcli annotation_specs list_label --project_id prj1 --format pretty_json
-
-# 最新より１つ前の履歴である、アノテーション仕様を出力する。
-$ annofabcli annotation_specs list_label --project_id prj1 --before 1
-
-# history_idがXXXのアノテーション仕様を出力する。（history_idは、`annofabcli annotation_specs history`コマンドで取得する）
-$ annofabcli annotation_specs list_label --project_id prj1 --history_id XXX
-
-
-```
-
-#### `--format text`の出力結果 
-`--format text`の出力結果は、以下のフォーマットで出力されます。
-
-```
-label_id    label_type    label_name_ja    label_name_en
-    attribute_id    attribute_type    attribute_name_ja    attribute_name_ja
-        choice_id    choice_name_ja    choice_name_en
-        ...
-    ...
-...
-```
-
-サンプルプロジェクトでコマンドを実行した結果は、以下の通りです。
-
-```
-15ba7932-24b9-4cf3-95bd-9bf6deede4fa	bounding_box	ネコ	Cat
-	e6864d96-78fa-45f3-a786-6c8c900c92ae	flag	隠れ	occluded
-	51e8c91f-5de1-450b-a0f3-94fec582f5ce	link	目のリンク	link-eye
-	aff2855e-2e3d-47a2-8c27-c7652e4dfb2f	integer	体重	weight
-	7e6a577a-3410-4c8a-9624-2904bb2e6666	comment	名前	name
-	a63a0513-a96e-4c7c-8754-88a24fef9ca9	text	備考	memo
-	649abf45-1ed7-459a-8282-a58228e9a302	tracking	object id	object id
-c754f724-5f8c-48eb-81ec-ea77e55efee7	polyline	足	leg
-f50aa88d-36c7-43f5-8728-247a49b4f4d8	point	目	eye
-108ce1f7-217b-43e9-a407-8d0ac6aad87e	segmentation	犬	dog
-2ffb4c74-106b-44ac-81ce-3c3df77518e0	segmentation_v2	人間	human
-ded52dcb-bcd6-4e77-9626-61e546f635d0	polygon	鳥	bird
-5ac0d7d5-6738-4c4b-a69a-cd583ff458e1	classification	気候	climatic
-	896d7eeb-9c60-4fbf-b7c4-8f4209261049	choice	天気	weather
-		c9615782-b872-4641-9be4-0fb4f905d966		晴	sunny
-		553018a5-e594-4536-bc05-876fa6b48ed5		雨	rainy
-	60caffa5-6300-4819-9a99-c43ce49008c2	select	気温	temparature
-		89b3577d-a245-4b85-82ef-6569ecbf8ad7		10	10
-		bdcd4d5b-cecc-4ec9-9038-d9284cd4f475		20	20
-		9f3a0355-2cc8-412a-9129-3b62fa7b6ead		30	30
-		2726336c-96d3-485b-9f96-7d4bcc97083b		40	40
-
-```
-
-
-### annotation_specs list_label_color
-アノテーション仕様から、label_name(english)とRGBを対応付けたJSONを出力します。
-
-```
-$ annofabcli annotation_specs list_label_color --project_id prj1 
-```
-
-以下のJSONのような出力結果になります。
-
-```json:出力結果
-{
-  "cat": [
-    255,
-    99,
-    71
-  ],
-  "dog": [
-    255,
-    0,
-    255
-  ],
-```
-
-
-### filesystem filter_annotation
-アノテーションzipから特定のファイルを絞り込んで、zip展開します。
+### annotation
+https://annofab-cli.readthedocs.io/ja/latest/command_reference/annotation/index.html 参照
 
 
-```
-# アノテーションzipからcomplete状態で、task.txtに記載されているtask_idを除外した状態で、展開する
-$ annofabcli filesystem filter_annotation --annotation annotation.zip \
- --task_query '{"status":"complete"}'  --exclude_task_id file://task.txt --output_dir
-
-```
-
+### annotation_specs
+https://annofab-cli.readthedocs.io/ja/latest/command_reference/annotation_specs/index.html 参照
 
 ### filesystem
 https://annofab-cli.readthedocs.io/ja/latest/command_reference/filesystem/index.html 参照
@@ -559,69 +254,8 @@ https://annofab-cli.readthedocs.io/ja/latest/command_reference/filesystem/index.
 ### input_data 
 https://annofab-cli.readthedocs.io/ja/latest/command_reference/input_data/index.html 参照
 
-
-
-### inspection_comment list
-検査コメント一覧を出力します。
-
-```
-# task1, task2の検査コメント一覧を、CSVで出力する
-$ annofabcli inspection_comment list --project_id prj1 --task_id task1 task2
-
-# タブ区切りの"out.tsv"を出力する
-$ annofabcli inspection_comment list --project_id prj1 --task_id task1 task2 \
- --format csv --csv_format '{"sep":"\t"}'  --output out.tsv
-
-# JSONで出力する
-$ annofabcli inspection_comment list --project_id prj1 --task_id file://task.txt --format json
-
-# 検査コメント情報が記載されたファイルを元にして、検査コメント一覧を出力します
-# 検査コメント情報が記載されたファイルは、`$ annofabcli project download inspection_comment`コマンドで取得できます。
-$ annofabcli inspection_comment list --project_id prj1 --inspection_comment_json inspection_comment.json
-
-# 返信コメントを除外した検査コメント一覧を出力します
-$ annofabcli inspection_comment list --project_id prj1 --task_id task1 task2 --exclude_reply
-
-# 返信コメントのみの一覧を出力します
-$ annofabcli inspection_comment list --project_id prj1 --task_id task1 task2 --only_reply
-
-
-```
-
-#### 出力結果（CSV）
-
-| project_id                           | task_id    | input_data_id                        | inspection_id                        | phase      | phase_stage | commenter_account_id                 | annotation_id                        | label_id                             | data                                  | parent_inspection_id | phrases | comment | status          | created_datetime              | updated_datetime              | commenter_user_id | commenter_username | phrase_names_en | phrase_names_ja | label_name_en | label_name_ja | input_data_index |
-|--------------------------------------|------------|--------------------------------------|--------------------------------------|------------|-------------|--------------------------------------|--------------------------------------|--------------------------------------|---------------------------------------|----------------------|---------|---------|-----------------|-------------------------------|-------------------------------|-------------------|--------------------|-----------------|-----------------|---------------|---------------|------------------|
-| 58a2a621-7d4b-41e7-927b-cdc570c1114a | sample_180 | bf6b4790-cdb8-4d4d-85bb-08550934ed61 | 5f096677-67e4-4e75-9eac-bbd8ac9694d9 | inspection | 1           | 12345678-abcd-1234-abcd-1234abcd5678 | 8aff181e-9df4-4c66-8fb2-10596c686d5c | 8aff181e-9df4-4c66-8fb2-10596c686d5c | {'x': 358, 'y': 48, '_type': 'Point'} |                      | []      | 枠がずれています     | error_corrected | 2019-07-26T17:41:16.626+09:00 | 2019-08-01T10:57:45.639+09:00 | user_id   | username          | []              | []              | car           | car           | 0                |
-
-
-
-### inspection_comment list_with_json
-検査コメント一覧を出力します。
-
-```
-# 検査コメント全件を出力する
-$ annofabcli inspection_comment list_with_json --project_id prj1 --output inspection_comment.csv
-
-```
-
-
-
-### inspection_comment list_unprocessed
-未処置の検査コメント一覧を出力します。
-
-```
-# 未処置の検査コメント一覧を出力する
-$ annofabcli inspection_comment list_unprocessed --project_id prj1 --task_id file://task.txt
-
-# 未処置で、user1が"hoge"とコメントした検査コメント一覧を出力する
-$ annofabcli inspection_comment list_unprocessed  --project_id prj1 --task_id file://task.txt \
- --inspection_comment "hoge" --commenter_user_id user1 --format pretty_json --output inspection.json
-
-# 検査コメント情報が記載されたファイルを元にして、検査コメント一覧を追加します
-$ annofabcli inspection_comment list_unprocessed --project_id prj1 --inspection_comment_json inspection_comment.json
-```
-
+### inspection_comment
+https://annofab-cli.readthedocs.io/ja/latest/command_reference/inspection_comment/index.html 参照
 
 ### instruction
 https://annofab-cli.readthedocs.io/ja/latest/command_reference/instruction/index.html 参照
diff --git a/annofabcli/annotation/import_annotation.py b/annofabcli/annotation/import_annotation.py
index c1738d55..093ff50e 100644
--- a/annofabcli/annotation/import_annotation.py
+++ b/annofabcli/annotation/import_annotation.py
@@ -121,7 +121,7 @@ def _to_additional_data_list(self, attributes: Dict[str, Any], label_info: Label
         for key, value in attributes.items():
             specs_additional_data = self._get_additional_data_from_attribute_name(key, label_info)
             if specs_additional_data is None:
-                logger.warning(f"attribute_name={key} が存在しません。")
+                logger.warning(f"アノテーション仕様に attribute_name={key} が存在しません。")
                 continue
 
             additional_data = AdditionalData(
@@ -249,6 +249,7 @@ def put_annotation_for_input_data(
             logger.debug(
                 f"task_id={task_id}, input_data_id={input_data_id} : "
                 f"インポート先のタスクに既にアノテーションが存在するため、アノテーションの登録をスキップします。"
+                f"アノテーションを上書きする場合は、`--overwrite` を指定してください。"
             )
             return False
 
@@ -280,7 +281,7 @@ def put_annotation_for_task(
         return success_count
 
     def execute_task(
-        self, project_id: str, task_parser: SimpleAnnotationParserByTask, my_account_id: str, overwrite: bool = False
+        self, project_id: str, task_parser: SimpleAnnotationParserByTask, overwrite: bool, force: bool
     ) -> bool:
         """
         1個のタスクに対してアノテーションを登録する。
@@ -288,7 +289,6 @@ def execute_task(
         Args:
             project_id:
             task_parser:
-            my_account_id: 自分自身のアカウントID
             overwrite:
 
         Returns:
@@ -310,11 +310,31 @@ def execute_task(
             logger.info(f"タスク'{task_id}'は作業中または受入完了状態のため、インポートをスキップします。 status={task['status']}")
             return False
 
-        if not can_put_annotation(task, my_account_id):
-            logger.debug(f"タスク'{task_id}'は、過去に誰かに割り当てられたタスクで、現在の担当者が自分自身でないため、アノテーションのインポートをスキップします。")
-            return False
+        old_account_id: Optional[str] = None
+        changed_operator = False
+        if force:
+            if not can_put_annotation(task, self.service.api.account_id):
+                logger.debug(f"タスク'{task_id}' の担当者を自分自身に変更します。")
+                self.service.wrapper.change_task_operator(
+                    project_id, task_id, operator_account_id=self.service.api.account_id
+                )
+                changed_operator = True
+                old_account_id = task["account_id"]
+
+        else:
+            if not can_put_annotation(task, self.service.api.account_id):
+                logger.debug(
+                    f"タスク'{task_id}'は、過去に誰かに割り当てられたタスクで、現在の担当者が自分自身でないため、アノテーションのインポートをスキップします。"
+                    f"担当者を自分自身に変更してアノテーションを登録する場合は `--force` を指定してください。"
+                )
+                return False
 
         result_count = self.put_annotation_for_task(project_id, task_parser, overwrite)
+        if changed_operator:
+            logger.debug(f"タスク'{task_id}' の担当者を元に戻します。")
+            old_account_id = task["account_id"]
+            self.service.wrapper.change_task_operator(project_id, task_id, operator_account_id=old_account_id)
+
         return result_count > 0
 
     @staticmethod
@@ -345,7 +365,6 @@ def main(self):
         super().validate_project(project_id, [ProjectMemberRole.OWNER])
 
         task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id)
-        my_account_id = self.service.api.account_id
 
         # Simpleアノテーションの読み込み
         if annotation_path.is_file():
@@ -359,15 +378,11 @@ def main(self):
                 if len(task_id_list) > 0:
                     # コマンドライン引数で --task_idが指定された場合は、対象のタスクのみインポートする
                     if task_parser.task_id in task_id_list:
-                        if self.execute_task(
-                            project_id, task_parser, overwrite=args.overwrite, my_account_id=my_account_id
-                        ):
+                        if self.execute_task(project_id, task_parser, overwrite=args.overwrite, force=args.force):
                             success_count += 1
                 else:
                     # コマンドライン引数で --task_idが指定されていない場合はすべてをインポートする
-                    if self.execute_task(
-                        project_id, task_parser, overwrite=args.overwrite, my_account_id=my_account_id
-                    ):
+                    if self.execute_task(project_id, task_parser, overwrite=args.overwrite, force=args.force):
                         success_count += 1
 
             except Exception as e:  # pylint: disable=broad-except
@@ -402,6 +417,10 @@ def parse_args(parser: argparse.ArgumentParser):
         help="指定した場合、すでに存在するアノテーションを上書きします（入力データ単位）。" "指定しなければ、アノテーションのインポートをスキップします。",
     )
 
+    parser.add_argument(
+        "--force", action="store_true", help="過去に割り当てられていて現在の担当者が自分自身でない場合、タスクの担当者を自分自身に変更してからアノテーションをインポートします。"
+    )
+
     parser.set_defaults(subcommand_func=main)
 
 
@@ -409,9 +428,7 @@ def add_parser(subparsers: argparse._SubParsersAction):
     subcommand_name = "import"
     subcommand_help = "アノテーションをインポートします。"
     description = (
-        "アノテーションをインポートします。"
-        "アノテーションのフォーマットは、Simpleアノテーション(v2)と同じフォルダ構成のzipファイルまたはディレクトリです。"
-        "ただし、作業中/完了状態のタスク、または「過去に割り当てられていて現在の担当者が自分自身でない」タスクはリストアできません。"
+        "アノテーションをインポートします。アノテーションのフォーマットは、Simpleアノテーションと同じフォルダ構成のzipファイルまたはディレクトリです。ただし、作業中/完了状態のタスクはインポートできません。"
     )
     epilog = "チェッカーまたはオーナロールを持つユーザで実行してください。"
 
diff --git a/annofabcli/annotation/restore_anotation.py b/annofabcli/annotation/restore_anotation.py
index 85ffddda..8ef118f8 100644
--- a/annofabcli/annotation/restore_anotation.py
+++ b/annofabcli/annotation/restore_anotation.py
@@ -2,7 +2,7 @@
 import json
 import logging
 from pathlib import Path
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Optional
 
 from annofabapi.dataclass.annotation import Annotation, AnnotationDetail
 from annofabapi.models import AnnotationDataHoldingType, ProjectMemberRole, TaskStatus
@@ -106,14 +106,14 @@ def put_annotation_for_task(self, project_id: str, task_parser: SimpleAnnotation
         logger.info(f"タスク'{task_parser.task_id}'の入力データ {success_count} 個に対してアノテーションをリストアしました。")
         return success_count
 
-    def execute_task(self, project_id: str, task_parser: SimpleAnnotationParserByTask, my_account_id: str) -> bool:
+    def execute_task(self, project_id: str, task_parser: SimpleAnnotationParserByTask, force: bool) -> bool:
         """
         1個のタスクに対してアノテーションを登録する。
 
         Args:
             project_id:
             task_parser:
-            my_account_id: 自分自身のaccount_id
+            force:
 
         Returns:
             1個以上の入力データのアノテーションを変更したか
@@ -134,11 +134,31 @@ def execute_task(self, project_id: str, task_parser: SimpleAnnotationParserByTas
             logger.info(f"タスク'{task_id}'は作業中または受入完了状態のため、アノテーションのリストアをスキップします。 status={task['status']}")
             return False
 
-        if not can_put_annotation(task, my_account_id):
-            logger.debug(f"タスク'{task_id}'は、過去に誰かに割り当てられたタスクで、現在の担当者が自分自身でないため、アノテーションのリストアをスキップします。")
-            return False
+        old_account_id: Optional[str] = None
+        changed_operator = False
+        if force:
+            if not can_put_annotation(task, self.service.api.account_id):
+                logger.debug(f"タスク'{task_id}' の担当者を自分自身に変更します。")
+                self.service.wrapper.change_task_operator(
+                    project_id, task_id, operator_account_id=self.service.api.account_id
+                )
+                changed_operator = True
+                old_account_id = task["account_id"]
+
+        else:
+            if not can_put_annotation(task, self.service.api.account_id):
+                logger.debug(
+                    f"タスク'{task_id}'は、過去に誰かに割り当てられたタスクで、現在の担当者が自分自身でないため、アノテーションのリストアをスキップします。"
+                    f"担当者を自分自身に変更してアノテーションを登録する場合は `--force` を指定してください。"
+                )
+                return False
 
         result_count = self.put_annotation_for_task(project_id, task_parser)
+        if changed_operator:
+            logger.debug(f"タスク'{task_id}' の担当者を元に戻します。")
+            old_account_id = task["account_id"]
+            self.service.wrapper.change_task_operator(project_id, task_id, operator_account_id=old_account_id)
+
         return result_count > 0
 
     def main(self):
@@ -150,8 +170,6 @@ def main(self):
 
         task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id)
 
-        my_account_id = self.service.api.account_id
-
         # dumpしたアノテーションディレクトリの読み込み
         iter_task_parser = lazy_parse_simple_annotation_dir_by_task(annotation_dir_path)
 
@@ -161,11 +179,11 @@ def main(self):
                 if len(task_id_list) > 0:
                     # コマンドライン引数で --task_idが指定された場合は、対象のタスクのみリストアする
                     if task_parser.task_id in task_id_list:
-                        if self.execute_task(project_id, task_parser, my_account_id=my_account_id):
+                        if self.execute_task(project_id, task_parser, force=args.force):
                             success_count += 1
                 else:
                     # コマンドライン引数で --task_idが指定されていない場合はすべてをリストアする
-                    if self.execute_task(project_id, task_parser, my_account_id=my_account_id):
+                    if self.execute_task(project_id, task_parser, force=args.force):
                         success_count += 1
 
             except Exception as e:  # pylint: disable=broad-except
@@ -194,16 +212,17 @@ def parse_args(parser: argparse.ArgumentParser):
 
     argument_parser.add_task_id(required=False)
 
+    parser.add_argument(
+        "--force", action="store_true", help="過去に割り当てられていて現在の担当者が自分自身でない場合、タスクの担当者を自分自身に変更してからアノテーションをリストアします。"
+    )
+
     parser.set_defaults(subcommand_func=main)
 
 
 def add_parser(subparsers: argparse._SubParsersAction):
     subcommand_name = "restore"
     subcommand_help = "'annotation dump'コマンドで保存したファイルから、アノテーション情報をリストアします。"
-    description = (
-        "'annotation dump'コマンドで保存したファイルから、アノテーション情報をリストアします。"
-        "ただし、作業中/完了状態のタスク、または「過去に割り当てられていて現在の担当者が自分自身でない」タスクはリストアできません。"
-    )
+    description = "'annotation dump'コマンドで保存したファイルから、アノテーション情報をリストアします。ただし、作業中/完了状態のタスクはリストアできません。"
     epilog = "チェッカーまたはオーナロールを持つユーザで実行してください。"
 
     parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
diff --git a/annofabcli/annotation_specs/list_annotation_specs_history.py b/annofabcli/annotation_specs/list_annotation_specs_history.py
index b1fbab56..34c63e89 100644
--- a/annofabcli/annotation_specs/list_annotation_specs_history.py
+++ b/annofabcli/annotation_specs/list_annotation_specs_history.py
@@ -59,7 +59,7 @@ def parse_args(parser: argparse.ArgumentParser):
 
 
 def add_parser(subparsers: argparse._SubParsersAction):
-    subcommand_name = "history"
+    subcommand_name = "list_history"
     subcommand_help = "アノテーション仕様の変更履歴を表示する。"
     description = "アノテーション仕様の変更履歴を表示する。"
 
diff --git a/annofabcli/annotation_specs/list_annotation_specs_label.py b/annofabcli/annotation_specs/list_annotation_specs_label.py
index af0c846d..9a3a2c21 100644
--- a/annofabcli/annotation_specs/list_annotation_specs_label.py
+++ b/annofabcli/annotation_specs/list_annotation_specs_label.py
@@ -78,7 +78,7 @@ def _print_text_format_labels(labels, output: Optional[str] = None):
 
         annofabcli.utils.output_string("\n".join(output_lines), output)
 
-    def get_history_id_from_before_index(self, project_id: str, before: int):
+    def get_history_id_from_before_index(self, project_id: str, before: int) -> Optional[str]:
         histories, _ = self.service.api.get_annotation_specs_histories(project_id)
         if before + 1 > len(histories):
             logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
diff --git a/docs/command_reference/annotation/change_attributes.rst b/docs/command_reference/annotation/change_attributes.rst
new file mode 100644
index 00000000..75f23a64
--- /dev/null
+++ b/docs/command_reference/annotation/change_attributes.rst
@@ -0,0 +1,56 @@
+==========================================
+annotation change_attributes
+==========================================
+
+Description
+=================================
+アノテーションの属性を一括で変更します。
+ただし、作業中状態のタスクのアノテーションは、属性を変更できません。
+
+
+
+
+
+
+
+Examples
+=================================
+
+
+基本的な使い方
+--------------------------
+
+``--task_id`` にアノテーション変更対象のタスクのtask_idを指定してください。
+
+``--annotation_query`` には、変更対象のアノテーションを検索するする条件をJSON形式で指定してください。フォーマットは https://annofab.com/docs/api/#section/AnnotationQuery とほとんど同じです。
+さらに追加で ``label_name_en`` , ``additional_data_definition_name_en`` , ``choice_name_en`` キーも指定できます。``label_id`` または ``label_name_en`` のいずれかは必ず指定してください。``--annotation_query`` のサンプルは、`annofabcli annotation list_count <../annotation/list_count.html>`_ を参照してください。
+
+
+``--attributes`` に、変更後の属性を指定してください。フォーマットは ``--annotation_query`` の ``attributes`` キーの値と同じです。
+
+以下のコマンドは、ラベル名（英語）の値が"car"であるアノテーションに対して、属性名(英語)が"occluded"である値をfalse（"occluded"チェックボックスをOFF）に変更します。
+
+.. code-block::
+
+    $ annofabcli annotation change_attributes --project_id prj1 --task_id file://task.txt \ 
+    --annotation_query '{"label_name_en": "car"}' \
+    --attributes '[{"additional_data_definition_name_en": "occluded", "flag": false}]' \
+    --backup backup_dir/
+
+``--backup`` にディレクトリを指定すると、変更対象のタスクのアノテーション情報を、バックアップとしてディレクトリに保存します。
+アノテーション情報の復元は、 `annofabcli annotation restore <../annotation/restore.html>`_ コマンドで実現できます。
+
+
+.. note::
+
+    間違えてアノテーションを変更したときに復元できるようにするため、``--backup`` を指定することを推奨します。
+
+デフォルトでは完了状態のタスクのアノテーションは変更できません。完了状態のタスクのアノテーションも変更する場合は、``--force`` を指定してください。
+
+
+
+
+See also
+=================================
+*  `annofabcli annotation restore <../annotation/restore.html>`_
+
diff --git a/docs/command_reference/annotation/delete.rst b/docs/command_reference/annotation/delete.rst
new file mode 100644
index 00000000..491d6b5a
--- /dev/null
+++ b/docs/command_reference/annotation/delete.rst
@@ -0,0 +1,55 @@
+==========================================
+annotation delete
+==========================================
+
+Description
+=================================
+タスク配下のアノテーションを削除します。
+ただし、作業中または完了状態のタスクのアノテーションは削除できません。
+
+
+
+
+Examples
+=================================
+
+
+基本的な使い方
+--------------------------
+
+``--task_id`` に削除対象のタスクのtask_idを指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation delete --project_id prj1 --task_id file://task.txt \
+    --backup backup
+
+
+``--backup`` にディレクトリを指定すると、削除対象のタスクのアノテーション情報を、バックアップとしてディレクトリに保存します。
+アノテーション情報の復元は、 `annofabcli annotation restore <../annotation/restore.html>`_ コマンドで実現できます。
+
+
+.. note::
+
+    間違えてアノテーションを削除したときに復元できるようにするため、``--backup`` を指定することを推奨します。
+
+
+
+削除するアノテーションを絞り込む場合は、``--annotation_query`` を指定してください。フォーマットは https://annofab.com/docs/api/#section/AnnotationQuery とほとんど同じです。
+``--annotation_query`` のサンプルは、`annofabcli annotation list_count <../annotation/list_count.html>`_ を参照してください。
+
+以下のコマンドは、ラベル名（英語）の値が"car"で、属性名(英語)が"occluded"である値をfalse（"occluded"チェックボックスをOFF）であるアノテーションを削除します。
+
+
+.. code-block::
+
+    $ annofabcli annotation delete --project_id prj1 --task_id file://task.txt \ 
+    --annotation_query '{"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": false}]}' \
+    --backup backup_dir/
+
+
+
+See also
+=================================
+*  `annofabcli annotation restore <../annotation/restore.html>`_
+
diff --git a/docs/command_reference/annotation/dump.rst b/docs/command_reference/annotation/dump.rst
new file mode 100644
index 00000000..baceba52
--- /dev/null
+++ b/docs/command_reference/annotation/dump.rst
@@ -0,0 +1,46 @@
+==========================================
+annotation dump
+==========================================
+
+Description
+=================================
+指定したタスク配下のアノテーション情報をディレクトリに保存します。アノテーションのバックアップなどに利用できます。
+
+
+
+Examples
+=================================
+
+
+基本的な使い方
+--------------------------
+
+
+``--task_id`` に出力対象のタスクのtask_idを指定して、 ``--output_dir`` に出力先ディレクトリのパスを指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation dump --project_id prj1 --task_id file://task.txt --output_dir backup-dir/
+
+
+
+出力先ディレクトリの構成は以下の通りです。
+``{input_data_id}.json`` のフォーマットは、https://annofab.com/docs/api/#operation/getEditorAnnotation APIのレスポンスと同じです。
+
+.. code-block::
+
+    ルートディレクトリ/
+    ├── {task_id}/
+    │   ├── {input_data_id}.json
+    │   ├── {input_data_id}/
+    │          ├── {annotation_id}............ 塗りつぶしPNG画像
+
+
+
+アノテーション情報の復元は、 `annofabcli annotation restore <../annotation/restore.html>`_ コマンドで実現できます。
+
+
+See also
+=================================
+*  `annofabcli annotation restore <../annotation/restore.html>`_
+
diff --git a/docs/command_reference/annotation/import.rst b/docs/command_reference/annotation/import.rst
new file mode 100644
index 00000000..e0114445
--- /dev/null
+++ b/docs/command_reference/annotation/import.rst
@@ -0,0 +1,152 @@
+==========================================
+annotation import
+==========================================
+
+Description
+=================================
+アノテーションをプロジェクトにインポートします。
+アノテーシンzipまたはzipを展開したディレクトリをインポートできます。
+作業中または完了状態のタスクに対してはインポートできません。
+
+
+
+Examples
+=================================
+
+
+インポート対象のアノテーションのフォーマット
+----------------------------------------------------
+
+インポート対象のアノテーションのフォーマットは、アノテーションzipまたはzipを展開したディレクトリと同じディレクトリ構成です。
+
+
+.. code-block::
+    :caption: annotation.zip
+
+    ルートディレクトリ/
+    ├── {task_id}/
+    │   ├── {input_data_id}.json
+    │   ├── {input_data_id}/
+    │          ├── {annotation_id}............ 塗りつぶしPNG画像
+
+
+``{input_data_id}.json`` のサンプルをは以下の通りです。各キーの詳細は https://annofab.com/docs/api/#section/Simple-Annotation-ZIP を参照してください。
+
+.. code-block::
+    :caption: {input_data_id}.json
+
+    {
+        "details": [
+            {
+                "label": "car",
+                "data": {
+                    "left_top": {
+                        "x": 878,
+                        "y": 566
+                    },
+                    "right_bottom": {
+                        "x": 1065,
+                        "y": 701
+                    },
+                    "_type": "BoundingBox"
+                },
+                "attributes": {}
+            },
+            {
+                "label": "road",
+                "data": {
+                    "data_uri": "b803193f-827f-4755-8228-e2c67d0786d9",
+                    "_type": "SegmentationV2"
+                },
+                "attributes": {}
+            },
+            {
+                "label": "weather",
+                "data": {
+                    "_type": "Classification"
+                },
+                "attributes": {
+                    "sunny": true
+                }
+            }
+        ]
+    }
+
+
+
+
+以下のように ``annotation_id`` が指定されている場合、``annotation_id`` もインポートされます。
+
+
+.. code-block::
+    :caption: {input_data_id}.json
+
+    {
+        "details": [
+            {
+                "label": "car",
+                "annotation_id": "12345678-abcd-1234-abcd-1234abcd5678",
+                "data": {
+                    "left_top": {
+                        "x": 878,
+                        "y": 566
+                    },
+                    "right_bottom": {
+                        "x": 1065,
+                        "y": 701
+                    },
+                    "_type": "BoundingBox"
+                },
+                "attributes": {}
+            },
+            ...
+        ]
+    }
+
+
+基本的な使い方
+----------------------------------------------------
+
+``--annotation`` に、アノテーションzipまたはzipを展開したディレクトリのパスを指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation import --project_id prj1 --annotation annotation.zip 
+
+
+インポート対象のタスクを指定する場合は、``--task_id`` にインポート対象のタスクのtask_idを指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation import --project_id prj1 --annotation annotation.zip \
+    --task_id file://task_id.txt
+
+
+デフォルトでは、すでにアノテーションが存在する場合はスキップします。既存のアノテーションを上書きする場合は、 ``--overwrite`` を指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation import --project_id prj1 --annotation annotation.zip \
+    --overwrite
+
+
+
+デフォルトでは以下のタスクに対しては、アノテーションのインポートをスキップします。
+
+* タスクの担当者が自分自身でない
+* タスクに担当者が割れ当てられたことがある
+
+``--force`` を指定すると、担当者を一時的に自分自身に変更して、アノテーションをインポートすることができます。
+
+.. code-block::
+
+    $ annofabcli annotation import --project_id prj1 --annotation annotation.zip \
+    --force
+
+
+
+
+See also
+=================================
+*  `annofabcli project download <../project/download.html>`_
+
diff --git a/docs/command_reference/annotation/index.rst b/docs/command_reference/annotation/index.rst
new file mode 100644
index 00000000..453783bc
--- /dev/null
+++ b/docs/command_reference/annotation/index.rst
@@ -0,0 +1,24 @@
+==================================================
+annotation
+==================================================
+
+Description
+=================================
+アノテーション関係のコマンドです。
+
+
+Available Commands
+=================================
+
+
+.. toctree::
+   :maxdepth: 1
+   :titlesonly:
+
+   change_attributes
+   delete
+   dump
+   import
+   list_count
+   restore
+
diff --git a/docs/command_reference/annotation/list_count.rst b/docs/command_reference/annotation/list_count.rst
new file mode 100644
index 00000000..f6daabd6
--- /dev/null
+++ b/docs/command_reference/annotation/list_count.rst
@@ -0,0 +1,105 @@
+==========================================
+annotation list_count
+==========================================
+
+Description
+=================================
+task_idまたはinput_data_idで集約したアノテーションの個数を、CSV形式で出力します。
+
+
+
+
+
+
+Examples
+=================================
+
+
+基本的な使い方
+--------------------------
+``--annotation_query`` に、集計対象のアノテーションを検索するする条件をJSON形式で指定してください。フォーマットは https://annofab.com/docs/api/#section/AnnotationQuery とほとんど同じです。
+さらに追加で ``label_name_en`` , ``additional_data_definition_name_en`` , ``choice_name_en`` キーも指定できます。``label_id`` または ``label_name_en`` のいずれかは必ず指定してください。
+
+``--annotation_query`` のサンプルを以下に記載します。
+
+.. code-block::
+
+    # ラベル名（英語)が"bike"のアノテーション
+    {"label_name_en": "bike"}
+
+
+    # ラベル名（英語)が"car"で、属性名(英語)が"occluded"の値がtrueである（チェックボックス）アノテーション
+    {"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": true}]}
+
+
+    # ラベル名（英語)が"car"で、属性名(英語)が"count"の値が"1"であるアノテーション
+    {"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "count", "integer": 1}]}
+
+
+    # ラベル名（英語)が"car"で、属性名(英語)が"note"の値が"test"であるアノテーション
+    {"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "note", "comment": "test"}]}
+
+
+    # ラベル名（英語)が"car"で、属性名(英語)が"weather"の値が"sunny"である（ラジオボタンまたはドロップダウン）アノテーション
+    {"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "weather", "choice_name_en": "sunny"}]}
+
+
+    # ラベル名（英語)が"car"で、属性名(英語)が"occluded"の値がtrue AND 属性名(英語)が"weather"の値が"sunny"であるアノテーション
+    {"label_name_en": "car", "attributes":[{"additional_data_definition_name_en": "occluded", "flag": true}, 
+     {"additional_data_definition_name_en": "occluded", "flag": true}]}
+
+
+
+以下のコマンドは、ラベル名（英語)が"car"であるアノテーションの個数をタスクごとに出力します。
+
+.. code-block::
+
+    $ annofabcli annotation list_count --project_id prj1 \
+    --annotation_query '{"label_name_en": "car"}' --output out_by_task.csv
+
+
+.. csv-table:: out_by_task.csv
+   :header: task_id,annotation_count
+
+
+    task1,1
+    task2,2
+
+
+デフォルトではタスクごとに集計します。入力データごとに出力する場合は、``--gropu_by input_data_id`` を指定しでください。
+
+.. code-block::
+
+    $ annofabcli annotation list_count --project_id prj1 \
+    --annotation_query '{"label_name_en": "car"}' --gropu_by input_data_id --output out_by_input_data.csv
+
+
+.. csv-table:: out_by_input_data.csv
+   :header: task_id,input_data_id,annotation_count
+
+    task1,input1,1
+    task1,input2,2
+    task2,input3,3
+    task2,input4,4
+
+
+``--task_id`` で集計対象タスクを絞り込むこともできます。
+
+.. code-block::
+
+    $ annofabcli annotation list_count --project_id prj1 \
+    --annotation_query '{"label_name_en": "car"}'  --task_id file://task.txt
+
+
+.. warning::
+
+    WebAPIの都合上、集計対象のアノテーションは10,000個までしか検索できません。
+    10,000件以上のアノテーションを集計する場合は、 `annofabcli statistics list_annotation_count <../statistics/list_annotation_count.html>`_ コマンドの使用を検討してください。
+
+
+
+
+See also
+=================================
+* `annofabcli statistics list_annotation_count <../statistics/list_annotation_count.html>`_
+
diff --git a/docs/command_reference/annotation/restore.rst b/docs/command_reference/annotation/restore.rst
new file mode 100644
index 00000000..16a8c433
--- /dev/null
+++ b/docs/command_reference/annotation/restore.rst
@@ -0,0 +1,54 @@
+==========================================
+annotation restore
+==========================================
+
+Description
+=================================
+アノテーション情報をリストアします。
+ただし、作業中または完了状態のタスクに対してはアノテーション情報をリストアできません。
+
+
+Examples
+=================================
+
+
+基本的な使い方
+--------------------------
+
+``--annotation`` に、以下のいずれかのディレクトリパスを指定してください。
+
+* `annofabcli annotation dump <../annotation/dump.html>`_ コマンドの出力先ディレクトリ
+* `annofabcli annotation delete <../annotation/delete.html>`_ コマンドのバックアップ先ディレクトリ
+* `annofabcli annotation change_attributes <../annotation/change_attributes.html>`_ コマンドのバックアップ先ディレクトリ
+
+.. code-block::
+
+    $ annofabcli annotation restore --project_id prj1 --annotation annotation.zip 
+
+
+リストア対象のタスクを指定する場合は、``--task_id`` にリストア対象のタスクのtask_idを指定してください。
+
+.. code-block::
+
+    $ annofabcli annotation restore --project_id prj1 --annotation annotation.zip \
+    --task_id file://task_id.txt
+
+
+
+デフォルトでは以下のタスクに対しては、アノテーションのリストアをスキップします。
+
+* タスクの担当者が自分自身でない
+* タスクに担当者が割れ当てられたことがある
+
+``--force`` を指定すると、担当者を一時的に自分自身に変更して、アノテーションをリストアすることができます。
+
+.. code-block::
+
+    $ annofabcli annotation restore --project_id prj1 --annotation annotation.zip \
+    --force
+
+
+See also
+=================================
+*  `annofabcli annotation dump <../annotation/restore.html>`_
+
diff --git a/docs/command_reference/annotation_specs/index.rst b/docs/command_reference/annotation_specs/index.rst
new file mode 100644
index 00000000..b98510d8
--- /dev/null
+++ b/docs/command_reference/annotation_specs/index.rst
@@ -0,0 +1,21 @@
+==================================================
+annotation_specs
+==================================================
+
+Description
+=================================
+アノテーション仕様関係のコマンドです。
+
+
+Available Commands
+=================================
+
+
+.. toctree::
+   :maxdepth: 1
+   :titlesonly:
+
+   list_history
+   list_label
+   list_label_color
+   
\ No newline at end of file
diff --git a/docs/command_reference/annotation_specs/list_history.rst b/docs/command_reference/annotation_specs/list_history.rst
new file mode 100644
index 00000000..34906ec3
--- /dev/null
+++ b/docs/command_reference/annotation_specs/list_history.rst
@@ -0,0 +1,55 @@
+==========================================
+annotation_specs list_history
+==========================================
+
+Description
+=================================
+アノテーション仕様の履歴一覧を出力します。
+
+
+
+
+Examples
+=================================
+
+基本的な使い方
+--------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_history --project_id prj1 
+
+
+
+
+出力結果
+=================================
+
+
+JSON出力
+----------------------------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_history --project_id prj1  --format pretty_json --output out.json
+
+
+
+.. code-block::
+    :caption: out.json
+
+    [
+        {
+            "history_id": "1609907377",
+            "project_id": "prj1",
+            "updated_datetime": "2021-01-06T13:29:37.612+09:00",
+            "url": "https://annofab.com/projects/...",
+            "account_id": "account1",
+            "comment": null,
+            "user_id": "user1",
+            "username": "username1"
+        },
+        ...
+    ]
+
+
diff --git a/docs/command_reference/annotation_specs/list_label.rst b/docs/command_reference/annotation_specs/list_label.rst
new file mode 100644
index 00000000..8a8d5c60
--- /dev/null
+++ b/docs/command_reference/annotation_specs/list_label.rst
@@ -0,0 +1,131 @@
+==========================================
+annotation_specs list_label
+==========================================
+
+Description
+=================================
+アノテーション仕様のラベル情報を出力します。
+
+
+
+
+Examples
+=================================
+
+基本的な使い方
+--------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label --project_id prj1
+
+デフォルトでは最新のアノテーション仕様を出力します。過去のアノテーション仕様を出力する場合は、``--before`` または ``--history_id`` を指定してください。
+history_idは、`annofabcli annotation_specs list_history <../annotation_specs/list_history.html>`_ コマンドで取得できます。
+
+以下のコマンドは、最新より1つ前のアノテーション仕様を出力します。
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label --project_id prj1 --before 1
+
+
+以下のコマンドは、history_idが"xxx"のアノテーション仕様を出力します。
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label --project_id prj1 --history_id xxx
+
+
+出力結果
+=================================
+
+text出力
+----------------------------------------------
+以下のような、人が見てわかりやすい形式で出力します。
+
+.. code-block::
+
+    label_id    label_type    label_name_ja    label_name_en
+        attribute_id    attribute_type    attribute_name_ja    attribute_name_ja
+            choice_id    choice_name_ja    choice_name_en
+            ...
+        ...
+    ...
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label --project_id prj1  --format text --output out.txt
+
+
+.. code-block::
+    :caption: out.txt
+
+    15ba7932-24b9-4cf3-95bd-9bf6deede4fa	bounding_box	ネコ	Cat
+        e6864d96-78fa-45f3-a786-6c8c900c92ae	flag	隠れ	occluded
+        51e8c91f-5de1-450b-a0f3-94fec582f5ce	link	目のリンク	link-eye
+        aff2855e-2e3d-47a2-8c27-c7652e4dfb2f	integer	体重	weight
+        7e6a577a-3410-4c8a-9624-2904bb2e6666	comment	名前	name
+        a63a0513-a96e-4c7c-8754-88a24fef9ca9	text	備考	memo
+        649abf45-1ed7-459a-8282-a58228e9a302	tracking	object id	object id
+    c754f724-5f8c-48eb-81ec-ea77e55efee7	polyline	足	leg
+    f50aa88d-36c7-43f5-8728-247a49b4f4d8	point	目	eye
+    108ce1f7-217b-43e9-a407-8d0ac6aad87e	segmentation	犬	dog
+    2ffb4c74-106b-44ac-81ce-3c3df77518e0	segmentation_v2	人間	human
+    ded52dcb-bcd6-4e77-9626-61e546f635d0	polygon	鳥	bird
+    5ac0d7d5-6738-4c4b-a69a-cd583ff458e1	classification	気候	climatic
+        896d7eeb-9c60-4fbf-b7c4-8f4209261049	choice	天気	weather
+            c9615782-b872-4641-9be4-0fb4f905d966		晴	sunny
+            553018a5-e594-4536-bc05-876fa6b48ed5		雨	rainy
+        60caffa5-6300-4819-9a99-c43ce49008c2	select	気温	temparature
+            89b3577d-a245-4b85-82ef-6569ecbf8ad7		10	10
+            bdcd4d5b-cecc-4ec9-9038-d9284cd4f475		20	20
+            9f3a0355-2cc8-412a-9129-3b62fa7b6ead		30	30
+            2726336c-96d3-485b-9f96-7d4bcc97083b		40	40
+
+
+
+
+
+
+JSON出力
+----------------------------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label --project_id prj1  --format pretty_json --output out.json
+
+https://annofab.com/docs/api/#operation/getAnnotationSpecs APIのレスポンス（ ``AnnofationSpecsV1`` の ``labels`` キー）と同じです。
+
+.. code-block::
+    :caption: out.json
+
+    [
+        {
+            "label_id": "728931a1-d0a2-442c-8e60-36c65ee7b878",
+            "label_name": {
+            "messages": [
+                {
+                "lang": "ja-JP",
+                "message": "car"
+                },
+                {
+                "lang": "en-US",
+                "message": "car"
+                }
+            ],
+            "default_lang": "ja-JP"
+            },
+            "keybind": [
+            {
+                "code": "Digit1",
+                "shift": false,
+                "ctrl": false,
+                "alt": false
+            }
+            ],
+            ...
+        },
+        ...
+    ]
+
+
diff --git a/docs/command_reference/annotation_specs/list_label_color.rst b/docs/command_reference/annotation_specs/list_label_color.rst
new file mode 100644
index 00000000..40b6340f
--- /dev/null
+++ b/docs/command_reference/annotation_specs/list_label_color.rst
@@ -0,0 +1,52 @@
+==========================================
+annotation_specs list_label_color
+==========================================
+
+Description
+=================================
+アノテーション仕様のラベル名(英語)と色(RGB)の対応関係を出力します。
+
+
+Examples
+=================================
+
+基本的な使い方
+--------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label_color --project_id prj1 
+
+
+出力結果
+=================================
+
+
+
+
+JSON出力
+----------------------------------------------
+
+.. code-block::
+
+    $ annofabcli annotation_specs list_label_color --project_id prj1  --format pretty_json --output out.json
+
+
+.. code-block::
+    :caption: out.json
+
+    {
+        "cat": [
+            255,
+            99,
+            71
+        ],
+        "dog": [
+            255,
+            0,
+            255
+        ],
+        ...
+    }
+
+
diff --git a/docs/command_reference/index.rst b/docs/command_reference/index.rst
index 3394f2e1..44853606 100644
--- a/docs/command_reference/index.rst
+++ b/docs/command_reference/index.rst
@@ -8,6 +8,8 @@ Command Reference
    :caption: Available Commands
    :titlesonly:
 
+   annotation/index
+   annotation_specs/index
    filesystem/index
    input_data/index
    inspection_comment/index
diff --git a/docs/command_reference/job/delete.rst b/docs/command_reference/job/delete.rst
index d9679950..84172a46 100644
--- a/docs/command_reference/job/delete.rst
+++ b/docs/command_reference/job/delete.rst
@@ -16,7 +16,7 @@ Examples
 
 ``--job_type`` にジョブの種類、``--job_id`` にjob_idを指定してください。
 
-``job_type`` に指定できる値は `annofabcli job list <../job/list.html>`_ を参照してください。
+``--job_type`` に指定できる値は `annofabcli job list <../job/list.html>`_ を参照してください。
 job_idは `annofabcli job list <../job/list.html>`_ コマンドで確認できます。
 
 


Test patch:
diff --git a/tests/data/imported-annotation.zip b/tests/data/annotation/imported-annotation.zip
similarity index 100%
rename from tests/data/imported-annotation.zip
rename to tests/data/annotation/imported-annotation.zip
diff --git a/tests/test_annotation.py b/tests/test_annotation.py
new file mode 100644
index 00000000..8631c1c5
--- /dev/null
+++ b/tests/test_annotation.py
@@ -0,0 +1,122 @@
+import configparser
+import os
+from pathlib import Path
+
+import annofabapi
+
+from annofabcli.__main__ import main
+
+# プロジェクトトップに移動する
+os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/../")
+
+data_dir = Path("./tests/data/annotation")
+out_dir = Path("./tests/out/annotation")
+out_dir.mkdir(exist_ok=True, parents=True)
+
+# プロジェクトトップに移動する
+os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/../")
+inifile = configparser.ConfigParser()
+inifile.read("./pytest.ini", "UTF-8")
+annofab_config = dict(inifile.items("annofab"))
+
+project_id = annofab_config["project_id"]
+task_id = annofab_config["task_id"]
+service = annofabapi.build()
+
+
+class TestCommandLine:
+    def test_change_attributes_of_annotation(self):
+        backup_dir = str(out_dir / "backup-annotation")
+        main(
+            [
+                "annotation",
+                "change_attributes",
+                "--project_id",
+                project_id,
+                "--task_id",
+                task_id,
+                "--backup",
+                backup_dir,
+                "--annotation_query",
+                '{"label_name_en": "car"}',
+                "--attributes",
+                '[{"additional_data_definition_name_en": "occluded", "flag": false}]',
+                "--yes",
+            ]
+        )
+
+    def test_dump_annotation(self):
+        output_dir = str(out_dir / "dump-annotation")
+        main(
+            [
+                "annotation",
+                "dump",
+                "--project_id",
+                project_id,
+                "--task_id",
+                task_id,
+                "--output",
+                output_dir,
+                "--yes",
+            ]
+        )
+
+    def test_import_annotation(self):
+        main(
+            [
+                "annotation",
+                "import",
+                "--project_id",
+                project_id,
+                "--annotation",
+                str(data_dir / "imported-annotation.zip"),
+                "--task_id",
+                task_id,
+                "--overwrite",
+                "--yes",
+            ]
+        )
+
+    def test_annotation(self):
+        main(
+            [
+                "annotation",
+                "list_count",
+                "--project_id",
+                project_id,
+                "--annotation_query",
+                '{"label_name_en": "car"}',
+                "--output",
+                str(out_dir / "annotation_count.csv"),
+            ]
+        )
+
+    def test_delete_and_restore_annotation(self):
+        backup_dir = str(out_dir / "backup-annotation")
+        main(
+            [
+                "annotation",
+                "delete",
+                "--project_id",
+                project_id,
+                "--task_id",
+                task_id,
+                "--backup",
+                backup_dir,
+                "--yes",
+            ]
+        )
+
+        main(
+            [
+                "annotation",
+                "restore",
+                "--project_id",
+                project_id,
+                "--task_id",
+                task_id,
+                "--annotation",
+                backup_dir,
+                "--yes",
+            ]
+        )
diff --git a/tests/test_annotation_specs.py b/tests/test_annotation_specs.py
new file mode 100644
index 00000000..c4fbe0df
--- /dev/null
+++ b/tests/test_annotation_specs.py
@@ -0,0 +1,51 @@
+import configparser
+import os
+from pathlib import Path
+
+import annofabapi
+
+from annofabcli.__main__ import main
+
+# プロジェクトトップに移動する
+os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/../")
+
+data_dir = Path("./tests/data/annotation_specs")
+out_dir = Path("./tests/out/annotation_specs")
+out_dir.mkdir(exist_ok=True, parents=True)
+
+# プロジェクトトップに移動する
+os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/../")
+inifile = configparser.ConfigParser()
+inifile.read("./pytest.ini", "UTF-8")
+annofab_config = dict(inifile.items("annofab"))
+
+project_id = annofab_config["project_id"]
+task_id = annofab_config["task_id"]
+service = annofabapi.build()
+
+
+class TestCommandLine:
+    command_name = "annotation_specs"
+
+    def test_annotation_specs_histories(self):
+        out_file = str(out_dir / "anotaton_specs_histories.csv")
+        main([self.command_name, "list_history", "--project_id", project_id, "--output", out_file])
+
+    def test_annotation_specs_list_label(self):
+        out_file = str(out_dir / "anotation_specs_list_label.json")
+        main([self.command_name, "list_label", "--project_id", project_id, "--before", "1", "--output", out_file])
+
+    def test_annotation_specs_list_label_color(self):
+        out_file = str(out_dir / "anotation_specs_list_label_color.json")
+        main(
+            [
+                self.command_name,
+                "list_label_color",
+                "--project_id",
+                project_id,
+                "--format",
+                "json",
+                "--output",
+                out_file,
+            ]
+        )
diff --git a/tests/test_main.py b/tests/test_main.py
index 41035e49..2b97dfeb 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -24,132 +24,6 @@
 organization_name = service.api.get_organization_of_project(project_id)[0]["organization_name"]
 
 
-class TestAnnotation:
-    def test_annotation(self):
-        main(
-            [
-                "annotation",
-                "list_count",
-                "--project_id",
-                project_id,
-                "--annotation_query",
-                '{"label_name_en": "car"}',
-                "--output",
-                str(out_path / "annotation_count.csv"),
-            ]
-        )
-
-    def test_dump_annotation(self):
-        output_dir = str(out_path / "dump-annotation")
-        main(
-            [
-                "annotation",
-                "dump",
-                "--project_id",
-                project_id,
-                "--task_id",
-                task_id,
-                "--output",
-                output_dir,
-                "--yes",
-            ]
-        )
-
-    def test_delete_and_restore_annotation(self):
-        backup_dir = str(out_path / "backup-annotation")
-        main(
-            [
-                "annotation",
-                "delete",
-                "--project_id",
-                project_id,
-                "--task_id",
-                task_id,
-                "--backup",
-                backup_dir,
-                "--yes",
-            ]
-        )
-
-        main(
-            [
-                "annotation",
-                "restore",
-                "--project_id",
-                project_id,
-                "--task_id",
-                task_id,
-                "--annotation",
-                backup_dir,
-                "--yes",
-            ]
-        )
-
-    def test_import_annotation(self):
-        main(
-            [
-                "annotation",
-                "import",
-                "--project_id",
-                project_id,
-                "--annotation",
-                str(data_path / "imported-annotation.zip"),
-                "--task_id",
-                task_id,
-                "--overwrite",
-                "--yes",
-            ]
-        )
-
-
-class TestAnnotationSpecs:
-    command_name = "annotation_specs"
-
-    def test_annotation_specs_histories(self):
-        out_file = str(out_path / "anotaton_specs_histories.csv")
-        main([self.command_name, "history", "--project_id", project_id, "--output", out_file])
-
-    def test_annotation_specs_list_label(self):
-        out_file = str(out_path / "anotation_specs_list_label.json")
-        main([self.command_name, "list_label", "--project_id", project_id, "--output", out_file])
-
-    def test_old_annotation_specs_list_label(self):
-        out_file = str(out_path / "anotation_specs_list_label.json")
-        main([self.command_name, "list_label", "--project_id", project_id, "--before", "1", "--output", out_file])
-
-    def test_annotation_specs_list_label_from_history_id(self):
-        out_file = str(out_path / "anotation_specs_list_label.json")
-        histories, _ = service.api.get_annotation_specs_histories(project_id)
-        history_id = histories[0]["history_id"]
-        main(
-            [
-                self.command_name,
-                "list_label",
-                "--project_id",
-                project_id,
-                "--history_id",
-                history_id,
-                "--output",
-                out_file,
-            ]
-        )
-
-    def test_annotation_specs_list_label_color(self):
-        out_file = str(out_path / "anotation_specs_list_label_color.json")
-        main(
-            [
-                self.command_name,
-                "list_label_color",
-                "--project_id",
-                project_id,
-                "--format",
-                "json",
-                "--output",
-                out_file,
-            ]
-        )
-
-
 class TestFilesystem:
     def test_write_annotation_image(self):
         zip_path = data_path / "simple-annotation.zip"