annofabcli 1.102.0__py3-none-any.whl → 1.103.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annofabcli/annotation/annotation_query.py +9 -29
- annofabcli/annotation/change_annotation_attributes.py +6 -14
- annofabcli/annotation/change_annotation_properties.py +5 -12
- annofabcli/annotation/copy_annotation.py +9 -11
- annofabcli/annotation/delete_annotation.py +21 -26
- annofabcli/annotation/dump_annotation.py +1 -4
- annofabcli/annotation/import_annotation.py +16 -40
- annofabcli/annotation/list_annotation.py +1 -4
- annofabcli/annotation/merge_segmentation.py +10 -16
- annofabcli/annotation/remove_segmentation_overlap.py +14 -30
- annofabcli/annotation/restore_annotation.py +3 -9
- annofabcli/annotation_specs/add_attribute_restriction.py +2 -8
- annofabcli/annotation_specs/attribute_restriction.py +2 -10
- annofabcli/annotation_specs/export_annotation_specs.py +1 -3
- annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -10
- annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +4 -10
- annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +1 -3
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +7 -18
- annofabcli/annotation_specs/list_annotation_specs_choice.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
- annofabcli/annotation_specs/list_annotation_specs_label.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +4 -9
- annofabcli/annotation_specs/list_attribute_restriction.py +3 -9
- annofabcli/annotation_specs/put_label_color.py +1 -6
- annofabcli/comment/delete_comment.py +3 -9
- annofabcli/comment/list_all_comment.py +2 -4
- annofabcli/comment/list_comment.py +1 -4
- annofabcli/comment/put_comment.py +4 -13
- annofabcli/comment/put_comment_simply.py +2 -6
- annofabcli/comment/put_inspection_comment.py +2 -6
- annofabcli/comment/put_inspection_comment_simply.py +3 -6
- annofabcli/comment/put_onhold_comment.py +2 -6
- annofabcli/comment/put_onhold_comment_simply.py +2 -4
- annofabcli/common/cli.py +5 -43
- annofabcli/common/download.py +8 -25
- annofabcli/common/image.py +5 -9
- annofabcli/common/utils.py +1 -3
- annofabcli/common/visualize.py +2 -4
- annofabcli/filesystem/draw_annotation.py +8 -20
- annofabcli/filesystem/filter_annotation.py +7 -24
- annofabcli/filesystem/mask_user_info.py +3 -6
- annofabcli/filesystem/merge_annotation.py +2 -6
- annofabcli/input_data/change_input_data_name.py +3 -7
- annofabcli/input_data/copy_input_data.py +6 -14
- annofabcli/input_data/delete_input_data.py +7 -24
- annofabcli/input_data/delete_metadata_key_of_input_data.py +5 -16
- annofabcli/input_data/list_all_input_data.py +5 -14
- annofabcli/input_data/list_all_input_data_merged_task.py +8 -23
- annofabcli/input_data/list_input_data.py +5 -16
- annofabcli/input_data/put_input_data.py +7 -19
- annofabcli/input_data/update_metadata_of_input_data.py +6 -14
- annofabcli/instruction/list_instruction_history.py +0 -1
- annofabcli/instruction/upload_instruction.py +1 -4
- annofabcli/job/list_job.py +1 -2
- annofabcli/job/list_last_job.py +1 -3
- annofabcli/organization/list_organization.py +0 -1
- annofabcli/organization_member/change_organization_member.py +1 -3
- annofabcli/organization_member/delete_organization_member.py +32 -16
- annofabcli/organization_member/invite_organization_member.py +25 -14
- annofabcli/organization_member/list_organization_member.py +0 -1
- annofabcli/project/change_organization_of_project.py +257 -0
- annofabcli/project/change_project_status.py +2 -2
- annofabcli/project/copy_project.py +2 -7
- annofabcli/project/diff_projects.py +4 -16
- annofabcli/project/list_project.py +0 -1
- annofabcli/project/put_project.py +2 -6
- annofabcli/project/subcommand_project.py +2 -0
- annofabcli/project_member/change_project_members.py +2 -2
- annofabcli/project_member/copy_project_members.py +2 -7
- annofabcli/project_member/drop_project_members.py +1 -3
- annofabcli/project_member/invite_project_members.py +1 -3
- annofabcli/project_member/list_users.py +0 -1
- annofabcli/project_member/put_project_members.py +4 -12
- annofabcli/stat_visualization/mask_visualization_dir.py +6 -16
- annofabcli/stat_visualization/merge_visualization_dir.py +6 -18
- annofabcli/stat_visualization/summarize_whole_performance_csv.py +3 -7
- annofabcli/stat_visualization/write_graph.py +5 -15
- annofabcli/stat_visualization/write_performance_rating_csv.py +4 -12
- annofabcli/statistics/list_annotation_area.py +3 -7
- annofabcli/statistics/list_annotation_attribute.py +6 -15
- annofabcli/statistics/list_annotation_attribute_filled_count.py +9 -23
- annofabcli/statistics/list_annotation_count.py +18 -44
- annofabcli/statistics/list_annotation_duration.py +14 -40
- annofabcli/statistics/list_video_duration.py +2 -3
- annofabcli/statistics/list_worktime.py +0 -1
- annofabcli/statistics/scatter.py +3 -9
- annofabcli/statistics/summarize_task_count.py +7 -12
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +3 -11
- annofabcli/statistics/summarize_task_count_by_user.py +1 -5
- annofabcli/statistics/visualization/dataframe/annotation_count.py +1 -3
- annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +3 -9
- annofabcli/statistics/visualization/dataframe/productivity_per_date.py +11 -23
- annofabcli/statistics/visualization/dataframe/project_performance.py +1 -3
- annofabcli/statistics/visualization/dataframe/task.py +2 -5
- annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +6 -20
- annofabcli/statistics/visualization/dataframe/user_performance.py +29 -88
- annofabcli/statistics/visualization/dataframe/whole_performance.py +4 -10
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +17 -49
- annofabcli/statistics/visualization/dataframe/worktime_per_date.py +3 -9
- annofabcli/statistics/visualization/filtering_query.py +2 -6
- annofabcli/statistics/visualization/project_dir.py +9 -26
- annofabcli/statistics/visualization/visualization_source_files.py +3 -10
- annofabcli/statistics/visualize_annotation_count.py +7 -21
- annofabcli/statistics/visualize_annotation_duration.py +7 -17
- annofabcli/statistics/visualize_statistics.py +17 -52
- annofabcli/statistics/visualize_video_duration.py +8 -19
- annofabcli/supplementary/delete_supplementary_data.py +7 -23
- annofabcli/supplementary/list_supplementary_data.py +1 -1
- annofabcli/supplementary/put_supplementary_data.py +5 -15
- annofabcli/task/cancel_acceptance.py +3 -4
- annofabcli/task/change_operator.py +3 -11
- annofabcli/task/change_status_to_break.py +1 -1
- annofabcli/task/change_status_to_on_hold.py +5 -18
- annofabcli/task/complete_tasks.py +8 -25
- annofabcli/task/copy_tasks.py +2 -3
- annofabcli/task/delete_metadata_key_of_task.py +2 -6
- annofabcli/task/delete_tasks.py +7 -25
- annofabcli/task/list_all_tasks.py +2 -4
- annofabcli/task/list_tasks.py +2 -6
- annofabcli/task/list_tasks_added_task_history.py +7 -21
- annofabcli/task/put_tasks.py +2 -3
- annofabcli/task/put_tasks_by_count.py +3 -7
- annofabcli/task/reject_tasks.py +7 -19
- annofabcli/task/update_metadata_of_task.py +1 -1
- annofabcli/task_history/list_all_task_history.py +2 -5
- annofabcli/task_history/list_task_history.py +0 -1
- annofabcli/task_history_event/list_all_task_history_event.py +4 -11
- annofabcli/task_history_event/list_worktime.py +4 -14
- {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/METADATA +1 -1
- annofabcli-1.103.0.dist-info/RECORD +215 -0
- annofabcli-1.102.0.dist-info/RECORD +0 -214
- {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -66,7 +66,7 @@ def _get_additional_data_v1(additional_data: dict[str, Any], attribute_value: At
|
|
|
66
66
|
)
|
|
67
67
|
if len(tmp) > 1:
|
|
68
68
|
raise ValueError(
|
|
69
|
-
f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{attribute_value}'である選択肢が複数({len(tmp)} 個)存在します。"
|
|
69
|
+
f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{attribute_value}'である選択肢が複数({len(tmp)} 個)存在します。"
|
|
70
70
|
f" :: additional_data_definition_id='{additional_data_definition_id}'"
|
|
71
71
|
)
|
|
72
72
|
|
|
@@ -95,13 +95,9 @@ def _get_additional_data_v2(additional_data: dict[str, Any], attribute_value: At
|
|
|
95
95
|
tmp = [e for e in additional_data["choices"] if get_english_message(e["name"]) == choice_name_en]
|
|
96
96
|
|
|
97
97
|
if len(tmp) == 0: # pylint: disable=no-else-raise
|
|
98
|
-
raise ValueError(
|
|
99
|
-
f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{choice_name_en}'である選択肢は存在しません。"
|
|
100
|
-
)
|
|
98
|
+
raise ValueError(f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{choice_name_en}'である選択肢は存在しません。")
|
|
101
99
|
elif len(tmp) > 1:
|
|
102
|
-
raise ValueError(
|
|
103
|
-
f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{choice_name_en}'である選択肢が複数({len(tmp)} 個)存在します。" # noqa: E501
|
|
104
|
-
)
|
|
100
|
+
raise ValueError(f"アノテーション仕様の'{get_attribute_name(additional_data)}'属性に、選択肢名(英語)が'{choice_name_en}'である選択肢が複数({len(tmp)} 個)存在します。")
|
|
105
101
|
|
|
106
102
|
return tmp[0]["choice_id"]
|
|
107
103
|
|
|
@@ -137,9 +133,7 @@ def _get_additional_data_v2(additional_data: dict[str, Any], attribute_value: At
|
|
|
137
133
|
return {"definition_id": additional_data_definition_id, "value": result_value}
|
|
138
134
|
|
|
139
135
|
|
|
140
|
-
def convert_attributes_from_cli_to_api(
|
|
141
|
-
attributes: dict[str, AttributeValue], annotation_specs: dict[str, Any], *, label_id: Optional[str] = None
|
|
142
|
-
) -> list[AdditionalDataV1]:
|
|
136
|
+
def convert_attributes_from_cli_to_api(attributes: dict[str, AttributeValue], annotation_specs: dict[str, Any], *, label_id: Optional[str] = None) -> list[AdditionalDataV1]:
|
|
143
137
|
"""
|
|
144
138
|
CLI用の属性をAPI用の属性に変換します。
|
|
145
139
|
|
|
@@ -179,20 +173,14 @@ def convert_attributes_from_cli_to_api(
|
|
|
179
173
|
if label_info is None:
|
|
180
174
|
error_message = f"アノテーション仕様に、属性名(英語)が'{attribute_name}'である属性は存在しません。"
|
|
181
175
|
else:
|
|
182
|
-
error_message = (
|
|
183
|
-
f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、"
|
|
184
|
-
f"属性名(英語)が'{attribute_name}'である属性は存在しません。 :: label_id='{label_id}'"
|
|
185
|
-
)
|
|
176
|
+
error_message = f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、属性名(英語)が'{attribute_name}'である属性は存在しません。 :: label_id='{label_id}'"
|
|
186
177
|
raise ValueError(error_message)
|
|
187
178
|
|
|
188
179
|
if len(tmp) > 1:
|
|
189
180
|
if label_info is None:
|
|
190
181
|
error_message = f"アノテーション仕様に、属性名(英語)が'{attribute_name}'である属性が複数存在します。"
|
|
191
182
|
else:
|
|
192
|
-
error_message = (
|
|
193
|
-
f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、"
|
|
194
|
-
f"属性名(英語)が'{attribute_name}'である属性が複数存在します。 :: label_id='{label_id}'"
|
|
195
|
-
)
|
|
183
|
+
error_message = f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、属性名(英語)が'{attribute_name}'である属性が複数存在します。 :: label_id='{label_id}'"
|
|
196
184
|
raise ValueError(error_message)
|
|
197
185
|
additional_data = tmp[0]
|
|
198
186
|
attributes_for_webapi.append(_get_additional_data_v1(additional_data, attribute_value))
|
|
@@ -200,9 +188,7 @@ def convert_attributes_from_cli_to_api(
|
|
|
200
188
|
return attributes_for_webapi
|
|
201
189
|
|
|
202
190
|
|
|
203
|
-
def convert_attributes_from_cli_to_additional_data_list_v2(
|
|
204
|
-
attributes: dict[str, AttributeValue], annotation_specs: dict[str, Any], *, label_id: Optional[str] = None
|
|
205
|
-
) -> list[dict[str, Any]]:
|
|
191
|
+
def convert_attributes_from_cli_to_additional_data_list_v2(attributes: dict[str, AttributeValue], annotation_specs: dict[str, Any], *, label_id: Optional[str] = None) -> list[dict[str, Any]]:
|
|
206
192
|
"""
|
|
207
193
|
CLI用の属性情報をAPI用の `AdditionalDataV2` に相当するdictに変換します。
|
|
208
194
|
|
|
@@ -242,20 +228,14 @@ def convert_attributes_from_cli_to_additional_data_list_v2(
|
|
|
242
228
|
if label_info is None:
|
|
243
229
|
error_message = f"アノテーション仕様に、属性名(英語)が'{attribute_name}'である属性は存在しません。"
|
|
244
230
|
else:
|
|
245
|
-
error_message = (
|
|
246
|
-
f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、"
|
|
247
|
-
f"属性名(英語)が'{attribute_name}'である属性は存在しません。 :: label_id='{label_id}'"
|
|
248
|
-
)
|
|
231
|
+
error_message = f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、属性名(英語)が'{attribute_name}'である属性は存在しません。 :: label_id='{label_id}'"
|
|
249
232
|
raise ValueError(error_message)
|
|
250
233
|
|
|
251
234
|
elif len(tmp) > 1:
|
|
252
235
|
if label_info is None:
|
|
253
236
|
error_message = f"アノテーション仕様に、属性名(英語)が'{attribute_name}'である属性が複数存在します。"
|
|
254
237
|
else:
|
|
255
|
-
error_message = (
|
|
256
|
-
f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、"
|
|
257
|
-
f"属性名(英語)が'{attribute_name}'である属性が複数存在します。 :: label_id='{label_id}'"
|
|
258
|
-
)
|
|
238
|
+
error_message = f"アノテーション仕様の'{get_label_name(label_info)}'ラベルに、属性名(英語)が'{attribute_name}'である属性が複数存在します。 :: label_id='{label_id}'"
|
|
259
239
|
raise ValueError(error_message)
|
|
260
240
|
additional_data = tmp[0]
|
|
261
241
|
result.append(_get_additional_data_v2(additional_data, attribute_value))
|
|
@@ -58,9 +58,7 @@ class ChangeAnnotationAttributesMain(CommandLineWithConfirm):
|
|
|
58
58
|
|
|
59
59
|
self.dump_annotation_obj = DumpAnnotationMain(service, project_id)
|
|
60
60
|
|
|
61
|
-
def change_annotation_attributes(
|
|
62
|
-
self, annotation_list: list[dict[str, Any]], additional_data_list: list[dict[str, Any]]
|
|
63
|
-
) -> Optional[list[dict[str, Any]]]:
|
|
61
|
+
def change_annotation_attributes(self, annotation_list: list[dict[str, Any]], additional_data_list: list[dict[str, Any]]) -> Optional[list[dict[str, Any]]]:
|
|
64
62
|
"""
|
|
65
63
|
アノテーション属性値を変更する。
|
|
66
64
|
|
|
@@ -270,9 +268,7 @@ class ChangeAttributesOfAnnotation(CommandLine):
|
|
|
270
268
|
return annotation_query_for_cli.to_query_for_api(annotation_specs)
|
|
271
269
|
|
|
272
270
|
@classmethod
|
|
273
|
-
def get_additional_data_list_from_cli_attributes(
|
|
274
|
-
cls, str_attributes: str, annotation_specs: dict[str, Any], label_id: Optional[str]
|
|
275
|
-
) -> list[dict[str, Any]]:
|
|
271
|
+
def get_additional_data_list_from_cli_attributes(cls, str_attributes: str, annotation_specs: dict[str, Any], label_id: Optional[str]) -> list[dict[str, Any]]:
|
|
276
272
|
"""
|
|
277
273
|
CLIから受け取った`--attributes`の値から、APIに渡す属性情報(`AdditionalDataListV2`)を返します。
|
|
278
274
|
"""
|
|
@@ -297,9 +293,7 @@ class ChangeAttributesOfAnnotation(CommandLine):
|
|
|
297
293
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
298
294
|
|
|
299
295
|
try:
|
|
300
|
-
additional_data_list = self.get_additional_data_list_from_cli_attributes(
|
|
301
|
-
args.attributes, annotation_specs, label_id=annotation_query.label_id
|
|
302
|
-
)
|
|
296
|
+
additional_data_list = self.get_additional_data_list_from_cli_attributes(args.attributes, annotation_specs, label_id=annotation_query.label_id)
|
|
303
297
|
except ValueError as e:
|
|
304
298
|
print(f"{self.COMMON_MESSAGE} argument '--attributes' の値が不正です。 :: {e}", file=sys.stderr) # noqa: T201
|
|
305
299
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
@@ -319,7 +313,7 @@ class ChangeAttributesOfAnnotation(CommandLine):
|
|
|
319
313
|
if args.force: # noqa: SIM102
|
|
320
314
|
if not self.facade.contains_any_project_member_role(project_id, [ProjectMemberRole.OWNER]):
|
|
321
315
|
print( # noqa: T201
|
|
322
|
-
f"{self.COMMON_MESSAGE} argument --force : '--force' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
|
|
316
|
+
f"{self.COMMON_MESSAGE} argument --force : '--force' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
|
|
323
317
|
file=sys.stderr,
|
|
324
318
|
)
|
|
325
319
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
@@ -352,9 +346,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
352
346
|
"--annotation_query",
|
|
353
347
|
type=str,
|
|
354
348
|
required=True,
|
|
355
|
-
help="変更対象のアノテーションを検索する条件をJSON
|
|
356
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。"
|
|
357
|
-
f"(ex): ``{json.dumps(EXAMPLE_ANNOTATION_QUERY)}``",
|
|
349
|
+
help=f"変更対象のアノテーションを検索する条件をJSON形式で指定します。``file://`` を先頭に付けると、JSON形式のファイルを指定できます。(ex): ``{json.dumps(EXAMPLE_ANNOTATION_QUERY)}``",
|
|
358
350
|
)
|
|
359
351
|
|
|
360
352
|
EXAMPLE_ATTRIBUTES = '{"occluded": false}' # noqa: N806
|
|
@@ -375,7 +367,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
375
367
|
"--backup",
|
|
376
368
|
type=str,
|
|
377
369
|
required=False,
|
|
378
|
-
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
370
|
+
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
379
371
|
)
|
|
380
372
|
parser.add_argument(
|
|
381
373
|
"--parallelism",
|
|
@@ -156,15 +156,10 @@ class ChangePropertiesOfAnnotationMain(CommandLineWithConfirm):
|
|
|
156
156
|
logger.warning(f"task_id = '{task_id}' は存在しません。")
|
|
157
157
|
return False
|
|
158
158
|
|
|
159
|
-
logger.debug(
|
|
160
|
-
f"{logger_prefix}task_id='{task_id}', phase={dict_task['phase']}, status={dict_task['status']}, "
|
|
161
|
-
f"updated_datetime={dict_task['updated_datetime']}"
|
|
162
|
-
)
|
|
159
|
+
logger.debug(f"{logger_prefix}task_id='{task_id}', phase={dict_task['phase']}, status={dict_task['status']}, updated_datetime={dict_task['updated_datetime']}")
|
|
163
160
|
|
|
164
161
|
if dict_task["status"] in [TaskStatus.WORKING.value, TaskStatus.COMPLETE.value]:
|
|
165
|
-
logger.info(
|
|
166
|
-
f"タスク'{task_id}'は作業中または受入完了状態のため、アノテーションプロパティの変更をスキップします。 status={dict_task['status']}"
|
|
167
|
-
)
|
|
162
|
+
logger.info(f"タスク'{task_id}'は作業中または受入完了状態のため、アノテーションプロパティの変更をスキップします。 status={dict_task['status']}")
|
|
168
163
|
return False
|
|
169
164
|
|
|
170
165
|
old_account_id: Optional[str] = dict_task["account_id"]
|
|
@@ -375,9 +370,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
375
370
|
"--properties",
|
|
376
371
|
type=str,
|
|
377
372
|
required=True,
|
|
378
|
-
help="変更後のプロパティをJSON形式で指定します。変更可能なキーは ``is_protected``
|
|
379
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。"
|
|
380
|
-
f"(ex): ``{EXAMPLE_PROPERTIES}``",
|
|
373
|
+
help=f"変更後のプロパティをJSON形式で指定します。変更可能なキーは ``is_protected`` です。``file://`` を先頭に付けると、JSON形式のファイルを指定できます。(ex): ``{EXAMPLE_PROPERTIES}``",
|
|
381
374
|
)
|
|
382
375
|
|
|
383
376
|
parser.add_argument(
|
|
@@ -390,7 +383,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
390
383
|
"--backup",
|
|
391
384
|
type=str,
|
|
392
385
|
required=False,
|
|
393
|
-
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
386
|
+
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
394
387
|
)
|
|
395
388
|
|
|
396
389
|
parser.add_argument(
|
|
@@ -408,7 +401,7 @@ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argpa
|
|
|
408
401
|
subcommand_help = "アノテーションのプロパティを変更します。"
|
|
409
402
|
description = (
|
|
410
403
|
"アノテーションのプロパティを一括で変更します。ただし、作業中状態のタスクのアノテーションのプロパティは変更できません。"
|
|
411
|
-
"間違えてアノテーションのプロパティを変更したときに復元できるようにするため、 ``--backup`` でバックアップ用のディレクトリを指定することを推奨します。"
|
|
404
|
+
"間違えてアノテーションのプロパティを変更したときに復元できるようにするため、 ``--backup`` でバックアップ用のディレクトリを指定することを推奨します。"
|
|
412
405
|
)
|
|
413
406
|
epilog = "オーナロールを持つユーザで実行してください。"
|
|
414
407
|
|
|
@@ -10,6 +10,7 @@ from dataclasses import dataclass
|
|
|
10
10
|
from typing import Any, Optional
|
|
11
11
|
|
|
12
12
|
import annofabapi
|
|
13
|
+
from annofabapi.models import ProjectMemberRole
|
|
13
14
|
from annofabapi.utils import can_put_annotation
|
|
14
15
|
|
|
15
16
|
import annofabcli
|
|
@@ -157,8 +158,7 @@ class CopyAnnotationMain(CommandLineWithConfirm):
|
|
|
157
158
|
if len(src_input_data_id_list) != len(dest_input_data_id_list):
|
|
158
159
|
max_frame_number = min(len(src_input_data_id_list), len(dest_input_data_id_list))
|
|
159
160
|
logger.debug(
|
|
160
|
-
f"コピー元タスク'{copy_target.src_task_id}'の1〜{max_frame_number}
|
|
161
|
-
f"コピー先タスク'{copy_target.dest_task_id}'の1〜{max_frame_number}フレームにコピーします。"
|
|
161
|
+
f"コピー元タスク'{copy_target.src_task_id}'の1〜{max_frame_number}フレームのアノテーションを、コピー先タスク'{copy_target.dest_task_id}'の1〜{max_frame_number}フレームにコピーします。"
|
|
162
162
|
)
|
|
163
163
|
|
|
164
164
|
copy_count = 0
|
|
@@ -206,9 +206,7 @@ class CopyAnnotationMain(CommandLineWithConfirm):
|
|
|
206
206
|
アノテーションをコピーしたかどうか。
|
|
207
207
|
|
|
208
208
|
"""
|
|
209
|
-
src_annotation = self.service.wrapper.get_editor_annotation_or_none(
|
|
210
|
-
project_id=self.project_id, task_id=copy_target.src_task_id, input_data_id=copy_target.src_input_data_id
|
|
211
|
-
)
|
|
209
|
+
src_annotation = self.service.wrapper.get_editor_annotation_or_none(project_id=self.project_id, task_id=copy_target.src_task_id, input_data_id=copy_target.src_input_data_id)
|
|
212
210
|
if src_annotation is None:
|
|
213
211
|
logger.warning(
|
|
214
212
|
f"task_id='{copy_target.src_task_id}'のタスクが存在しないか、またはtask_id='{copy_target.src_task_id}'のタスクにinput_data_id='{copy_target.src_input_data_id}'の入力データが存在しません。"
|
|
@@ -217,9 +215,7 @@ class CopyAnnotationMain(CommandLineWithConfirm):
|
|
|
217
215
|
|
|
218
216
|
src_anno_details = src_annotation["details"]
|
|
219
217
|
|
|
220
|
-
dest_annotation = self.service.wrapper.get_editor_annotation_or_none(
|
|
221
|
-
project_id=self.project_id, task_id=copy_target.dest_task_id, input_data_id=copy_target.dest_input_data_id
|
|
222
|
-
)
|
|
218
|
+
dest_annotation = self.service.wrapper.get_editor_annotation_or_none(project_id=self.project_id, task_id=copy_target.dest_task_id, input_data_id=copy_target.dest_input_data_id)
|
|
223
219
|
if dest_annotation is None:
|
|
224
220
|
logger.warning(
|
|
225
221
|
f"task_id='{copy_target.dest_task_id}'のタスクが存在しないか、またはtask_id='{copy_target.dest_task_id}'のタスクにinput_data_id='{copy_target.dest_input_data_id}'の入力データが存在しません。"
|
|
@@ -350,6 +346,8 @@ class CopyAnnotation(CommandLine):
|
|
|
350
346
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
351
347
|
|
|
352
348
|
project_id = args.project_id
|
|
349
|
+
super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER, ProjectMemberRole.WORKER])
|
|
350
|
+
|
|
353
351
|
str_copy_target_list = get_list_from_args(args.input)
|
|
354
352
|
|
|
355
353
|
copy_target_list = get_copy_target_list(str_copy_target_list)
|
|
@@ -391,8 +389,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
391
389
|
overwrite_merge_group.add_argument(
|
|
392
390
|
"--overwrite",
|
|
393
391
|
action="store_true",
|
|
394
|
-
help="コピー先にアノテーションが存在する場合、 ``--overwrite``
|
|
395
|
-
"指定しなければ、アノテーションのコピーをスキップします。",
|
|
392
|
+
help="コピー先にアノテーションが存在する場合、 ``--overwrite`` を指定していれば、すでに存在するアノテーションを削除してコピーします。指定しなければ、アノテーションのコピーをスキップします。",
|
|
396
393
|
)
|
|
397
394
|
overwrite_merge_group.add_argument(
|
|
398
395
|
"--merge",
|
|
@@ -421,6 +418,7 @@ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argpa
|
|
|
421
418
|
subcommand_name = "copy"
|
|
422
419
|
subcommand_help = "アノテーションをコピーします."
|
|
423
420
|
description = "タスク単位または入力データ単位で、アノテーションをコピーします。"
|
|
424
|
-
|
|
421
|
+
epilog = "オーナー、チェッカーまたはアノテータロールを持つユーザで実行してください。"
|
|
422
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
|
|
425
423
|
parse_args(parser)
|
|
426
424
|
return parser
|
|
@@ -135,10 +135,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
135
135
|
|
|
136
136
|
if not self.is_force: # noqa: SIM102
|
|
137
137
|
if task.status == TaskStatus.COMPLETE:
|
|
138
|
-
logger.info(
|
|
139
|
-
f"task_id='{task_id}' :: タスクが完了状態のため、スキップします。"
|
|
140
|
-
f"完了状態のタスクのアノテーションを削除するには、`--force`オプションを指定してください。"
|
|
141
|
-
)
|
|
138
|
+
logger.info(f"task_id='{task_id}' :: タスクが完了状態のため、スキップします。完了状態のタスクのアノテーションを削除するには、`--force`オプションを指定してください。")
|
|
142
139
|
return
|
|
143
140
|
|
|
144
141
|
annotation_list = self.get_annotation_list_for_task(task_id, annotation_query=annotation_query)
|
|
@@ -197,10 +194,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
197
194
|
task_id = editor_annotation["task_id"]
|
|
198
195
|
input_data_id = editor_annotation["input_data_id"]
|
|
199
196
|
if len(editor_annotation["details"]) == 0:
|
|
200
|
-
logger.warning(
|
|
201
|
-
f"task_id='{task_id}', input_data_id='{input_data_id}' にはアノテーションが存在しません。以下のアノテーションの削除をスキップします。 :: " # noqa: E501
|
|
202
|
-
f"annotation_ids={annotation_ids}"
|
|
203
|
-
)
|
|
197
|
+
logger.warning(f"task_id='{task_id}', input_data_id='{input_data_id}' にはアノテーションが存在しません。以下のアノテーションの削除をスキップします。 :: annotation_ids={annotation_ids}")
|
|
204
198
|
return 0, len(annotation_ids)
|
|
205
199
|
|
|
206
200
|
# annotation_idでフィルタ
|
|
@@ -209,9 +203,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
209
203
|
nonexistent_annotation_ids = annotation_ids - existent_annotation_ids
|
|
210
204
|
|
|
211
205
|
if len(nonexistent_annotation_ids) > 0:
|
|
212
|
-
logger.warning(
|
|
213
|
-
f"次のアノテーションは存在しないので、削除できません。 :: task_id='{task_id}', input_data_id='{input_data_id}', annotation_id='{nonexistent_annotation_ids}'" # noqa: E501
|
|
214
|
-
)
|
|
206
|
+
logger.warning(f"次のアノテーションは存在しないので、削除できません。 :: task_id='{task_id}', input_data_id='{input_data_id}', annotation_id='{nonexistent_annotation_ids}'")
|
|
215
207
|
|
|
216
208
|
if len(filtered_details) == 0:
|
|
217
209
|
logger.info(f"task_id='{task_id}', input_data_id='{input_data_id}' には削除対象のアノテーションが存在しないので、スキップします。")
|
|
@@ -241,9 +233,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
241
233
|
logger.warning(f"task_id='{task_id}', input_data_id='{input_data_id}' :: アノテーションの削除に失敗しました。", exc_info=True)
|
|
242
234
|
# `batchUpdateAnnotations` APIでエラーになった場合、途中までは削除されるので、`len(annotation_ids)` 件削除に失敗したとは限らない。
|
|
243
235
|
# そのため、再度アノテーション情報を取得して、削除できたアノテーション数と削除できなかったアノテーション数を取得する。
|
|
244
|
-
new_editor_annotation, _ = self.service.api.get_editor_annotation(
|
|
245
|
-
self.project_id, task_id=task_id, input_data_id=input_data_id, query_params={"v": "2"}
|
|
246
|
-
)
|
|
236
|
+
new_editor_annotation, _ = self.service.api.get_editor_annotation(self.project_id, task_id=task_id, input_data_id=input_data_id, query_params={"v": "2"})
|
|
247
237
|
new_annotation_ids = {e["annotation_id"] for e in new_editor_annotation["details"]}
|
|
248
238
|
deleted_annotation_count = len(existent_annotation_ids - new_annotation_ids)
|
|
249
239
|
failed_to_delete_annotation_count = len(existent_annotation_ids) - deleted_annotation_count
|
|
@@ -307,13 +297,10 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
307
297
|
for input_data_id, annotation_ids in sub_grouped.items():
|
|
308
298
|
# 指定input_data_idの全annotationを取得
|
|
309
299
|
# TODO どこかのタイミングで、"v=2"のアノテーションを取得するようにする
|
|
310
|
-
editor_annotation = self.service.wrapper.get_editor_annotation_or_none(
|
|
311
|
-
self.project_id, task_id=task_id, input_data_id=input_data_id, query_params={"v": "1"}
|
|
312
|
-
)
|
|
300
|
+
editor_annotation = self.service.wrapper.get_editor_annotation_or_none(self.project_id, task_id=task_id, input_data_id=input_data_id, query_params={"v": "1"})
|
|
313
301
|
if editor_annotation is None:
|
|
314
302
|
logger.warning(
|
|
315
|
-
f"task_id='{task_id}'のタスクに、input_data_id='{input_data_id}'の入力データが含まれていません。 アノテーションの削除をスキップします。 :: "
|
|
316
|
-
f"annotation_ids={annotation_ids}"
|
|
303
|
+
f"task_id='{task_id}'のタスクに、input_data_id='{input_data_id}'の入力データが含まれていません。 アノテーションの削除をスキップします。 :: annotation_ids={annotation_ids}"
|
|
317
304
|
)
|
|
318
305
|
failed_to_delete_annotation_count += len(annotation_ids)
|
|
319
306
|
continue
|
|
@@ -322,9 +309,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
|
|
|
322
309
|
(backup_dir / task_id).mkdir(exist_ok=True, parents=True)
|
|
323
310
|
self.dump_annotation_obj.dump_editor_annotation(editor_annotation, json_path=backup_dir / task_id / f"{input_data_id}.json")
|
|
324
311
|
|
|
325
|
-
sub_deleted_annotation_count, sub_failed_to_delete_annotation_count = self.delete_annotation_by_annotation_ids(
|
|
326
|
-
editor_annotation, set(annotation_ids)
|
|
327
|
-
)
|
|
312
|
+
sub_deleted_annotation_count, sub_failed_to_delete_annotation_count = self.delete_annotation_by_annotation_ids(editor_annotation, set(annotation_ids))
|
|
328
313
|
deleted_annotation_count += sub_deleted_annotation_count
|
|
329
314
|
failed_to_delete_annotation_count += sub_failed_to_delete_annotation_count
|
|
330
315
|
|
|
@@ -356,7 +341,13 @@ class DeleteAnnotation(CommandLine):
|
|
|
356
341
|
else:
|
|
357
342
|
backup_dir = Path(args.backup)
|
|
358
343
|
|
|
359
|
-
|
|
344
|
+
if args.force:
|
|
345
|
+
# --forceオプションが指定されている場合は、完了状態のタスクも削除する
|
|
346
|
+
# 完了状態のタスクを削除するには、オーナーロールである必要があるため、`args.force`で条件を分岐する
|
|
347
|
+
super().validate_project(project_id, [ProjectMemberRole.OWNER])
|
|
348
|
+
else:
|
|
349
|
+
super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER])
|
|
350
|
+
|
|
360
351
|
main_obj = DeleteAnnotationMain(self.service, project_id, all_yes=args.yes, is_force=args.force)
|
|
361
352
|
|
|
362
353
|
if args.json is not None:
|
|
@@ -452,12 +443,16 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
452
443
|
f"(ex): ``{json.dumps(EXAMPLE_ANNOTATION_QUERY)}``",
|
|
453
444
|
)
|
|
454
445
|
|
|
455
|
-
parser.add_argument(
|
|
446
|
+
parser.add_argument(
|
|
447
|
+
"--force",
|
|
448
|
+
action="store_true",
|
|
449
|
+
help="指定した場合は、完了状態のタスクのアノテーションも削除します。ただし、完了状態のタスクを削除するには、オーナーロールを持つユーザーが実行する必要があります。",
|
|
450
|
+
)
|
|
456
451
|
parser.add_argument(
|
|
457
452
|
"--backup",
|
|
458
453
|
type=str,
|
|
459
454
|
required=False,
|
|
460
|
-
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
455
|
+
help="アノテーションのバックアップを保存するディレクトリを指定してください。アノテーションの復元は ``annotation restore`` コマンドで実現できます。",
|
|
461
456
|
)
|
|
462
457
|
parser.set_defaults(subcommand_func=main)
|
|
463
458
|
|
|
@@ -469,7 +464,7 @@ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argpa
|
|
|
469
464
|
"タスク配下のアノテーションを削除します。ただし、作業中状態のタスクのアノテーションは削除できません。"
|
|
470
465
|
"間違えてアノテーションを削除したときに復元できるようにするため、 ``--backup`` でバックアップ用のディレクトリを指定することを推奨します。"
|
|
471
466
|
)
|
|
472
|
-
epilog = "
|
|
467
|
+
epilog = "オーナーまたはチェッカーロールを持つユーザで実行してください。ただし``--force``オプションを指定した場合は、オーナーロールを持つユーザで実行してください。"
|
|
473
468
|
|
|
474
469
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
|
|
475
470
|
parse_args(parser)
|
|
@@ -161,10 +161,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
161
161
|
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
162
162
|
subcommand_name = "dump"
|
|
163
163
|
subcommand_help = "``annotation restore`` コマンドに読み込ませることができるアノテーション情報を出力します。"
|
|
164
|
-
description =
|
|
165
|
-
"``annotation restore`` コマンドに読み込ませることができるアノテーション情報を出力します。"
|
|
166
|
-
"アノテーションのバックアップ目的で利用することを想定しています。"
|
|
167
|
-
)
|
|
164
|
+
description = "``annotation restore`` コマンドに読み込ませることができるアノテーション情報を出力します。アノテーションのバックアップ目的で利用することを想定しています。"
|
|
168
165
|
|
|
169
166
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description)
|
|
170
167
|
parse_args(parser)
|
|
@@ -200,9 +200,7 @@ class AnnotationConverter:
|
|
|
200
200
|
try:
|
|
201
201
|
choice = get_choice(choices, choice_name=str(attribute_value))
|
|
202
202
|
except ValueError:
|
|
203
|
-
logger.warning(
|
|
204
|
-
f"アノテーション仕様の属性'{attribute_name}'に選択肢名(英語)が'{attribute_value}'である選択肢情報は存在しないか、複数存在します。 :: {log_message_suffix}" # noqa: E501
|
|
205
|
-
)
|
|
203
|
+
logger.warning(f"アノテーション仕様の属性'{attribute_name}'に選択肢名(英語)が'{attribute_value}'である選択肢情報は存在しないか、複数存在します。 :: {log_message_suffix}")
|
|
206
204
|
if self.is_strict:
|
|
207
205
|
raise
|
|
208
206
|
return None
|
|
@@ -214,9 +212,7 @@ class AnnotationConverter:
|
|
|
214
212
|
try:
|
|
215
213
|
choice = get_choice(choices, choice_name=str(attribute_value))
|
|
216
214
|
except ValueError:
|
|
217
|
-
logger.warning(
|
|
218
|
-
f"アノテーション仕様の属性'{attribute_name}'に選択肢名(英語)が'{attribute_value}'である選択肢情報は存在しないか、複数存在します。 :: {log_message_suffix}" # noqa: E501
|
|
219
|
-
)
|
|
215
|
+
logger.warning(f"アノテーション仕様の属性'{attribute_name}'に選択肢名(英語)が'{attribute_value}'である選択肢情報は存在しないか、複数存在します。 :: {log_message_suffix}")
|
|
220
216
|
if self.is_strict:
|
|
221
217
|
raise
|
|
222
218
|
return None
|
|
@@ -226,9 +222,7 @@ class AnnotationConverter:
|
|
|
226
222
|
else:
|
|
227
223
|
assert_noreturn(additional_data_type)
|
|
228
224
|
|
|
229
|
-
def convert_attributes(
|
|
230
|
-
self, attributes: dict[str, Any], *, label_name: Optional[str] = None, log_message_suffix: str = ""
|
|
231
|
-
) -> list[dict[str, Any]]:
|
|
225
|
+
def convert_attributes(self, attributes: dict[str, Any], *, label_name: Optional[str] = None, log_message_suffix: str = "") -> list[dict[str, Any]]:
|
|
232
226
|
"""
|
|
233
227
|
インポート対象のアノテーションJSONに格納されている`attributes`を`AdditionalDataListV2`のlistに変換します。
|
|
234
228
|
|
|
@@ -248,9 +242,7 @@ class AnnotationConverter:
|
|
|
248
242
|
try:
|
|
249
243
|
specs_additional_data = self.annotation_specs_accessor.get_attribute(attribute_name=attribute_name, label=label)
|
|
250
244
|
except ValueError:
|
|
251
|
-
logger.warning(
|
|
252
|
-
f"アノテーション仕様に属性名(英語)が'{attribute_name}'である属性情報が存在しないか、複数存在します。 :: {log_message_suffix}"
|
|
253
|
-
)
|
|
245
|
+
logger.warning(f"アノテーション仕様に属性名(英語)が'{attribute_name}'である属性情報が存在しないか、複数存在します。 :: {log_message_suffix}")
|
|
254
246
|
if self.is_strict:
|
|
255
247
|
raise
|
|
256
248
|
continue
|
|
@@ -293,19 +285,15 @@ class AnnotationConverter:
|
|
|
293
285
|
ValueError: 存在しないラベル名が指定された場合(`self.is_strict`がFalseでもraiseされる9
|
|
294
286
|
|
|
295
287
|
"""
|
|
296
|
-
log_message_suffix =
|
|
297
|
-
f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
|
|
298
|
-
)
|
|
288
|
+
log_message_suffix = f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
|
|
299
289
|
|
|
300
290
|
try:
|
|
301
291
|
label_info = self.annotation_specs_accessor.get_label(label_name=detail.label)
|
|
302
292
|
except ValueError:
|
|
303
|
-
logger.warning(
|
|
304
|
-
f"アノテーション仕様にラベル名(英語)が'{detail.label}'であるラベル情報が存在しないか、または複数存在します。 :: {log_message_suffix}"
|
|
305
|
-
)
|
|
293
|
+
logger.warning(f"アノテーション仕様にラベル名(英語)が'{detail.label}'であるラベル情報が存在しないか、または複数存在します。 :: {log_message_suffix}")
|
|
306
294
|
raise
|
|
307
295
|
|
|
308
|
-
if detail.attributes is not None:
|
|
296
|
+
if detail.attributes is not None: # noqa: SIM108
|
|
309
297
|
additional_data_list = self.convert_attributes(detail.attributes, label_name=detail.label, log_message_suffix=log_message_suffix)
|
|
310
298
|
else:
|
|
311
299
|
additional_data_list = []
|
|
@@ -349,7 +337,7 @@ class AnnotationConverter:
|
|
|
349
337
|
details: インポート対象のアノテーション情報
|
|
350
338
|
old_details: 既存のアノテーション情報。既存のアノテーション情報に加えて、インポート対象のアノテーションを登録するためのリクエストボディを作成します。
|
|
351
339
|
updated_datetime: 更新日時
|
|
352
|
-
"""
|
|
340
|
+
"""
|
|
353
341
|
old_dict_detail = {}
|
|
354
342
|
INDEX_KEY = "_index" # noqa: N806
|
|
355
343
|
for index, old_detail in enumerate(old_details):
|
|
@@ -362,7 +350,7 @@ class AnnotationConverter:
|
|
|
362
350
|
new_request_details: list[dict[str, Any]] = []
|
|
363
351
|
for detail in details:
|
|
364
352
|
try:
|
|
365
|
-
log_message_suffix = f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
|
|
353
|
+
log_message_suffix = f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
|
|
366
354
|
|
|
367
355
|
request_detail = self.convert_annotation_detail(parser, detail, log_message_suffix=log_message_suffix)
|
|
368
356
|
except Exception as e:
|
|
@@ -424,9 +412,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
424
412
|
|
|
425
413
|
simple_annotation: ImportedSimpleAnnotation = ImportedSimpleAnnotation.from_dict(parser.load_json())
|
|
426
414
|
if len(simple_annotation.details) == 0:
|
|
427
|
-
logger.debug(
|
|
428
|
-
f"task_id='{task_id}', input_data_id='{input_data_id}' :: インポート元にアノテーションデータがないため、アノテーションの登録をスキップします。" # noqa: E501
|
|
429
|
-
)
|
|
415
|
+
logger.debug(f"task_id='{task_id}', input_data_id='{input_data_id}' :: インポート元にアノテーションデータがないため、アノテーションの登録をスキップします。")
|
|
430
416
|
return False
|
|
431
417
|
|
|
432
418
|
input_data = self.service.wrapper.get_input_data_or_none(self.project_id, input_data_id)
|
|
@@ -446,13 +432,9 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
446
432
|
|
|
447
433
|
logger.info(f"task_id='{task_id}', input_data_id='{input_data_id}' :: {len(simple_annotation.details)} 件のアノテーションを登録します。")
|
|
448
434
|
if self.is_merge:
|
|
449
|
-
request_body = self.converter.convert_annotation_details(
|
|
450
|
-
parser, simple_annotation.details, old_details=old_annotation["details"], updated_datetime=old_annotation["updated_datetime"]
|
|
451
|
-
)
|
|
435
|
+
request_body = self.converter.convert_annotation_details(parser, simple_annotation.details, old_details=old_annotation["details"], updated_datetime=old_annotation["updated_datetime"])
|
|
452
436
|
else:
|
|
453
|
-
request_body = self.converter.convert_annotation_details(
|
|
454
|
-
parser, simple_annotation.details, old_details=[], updated_datetime=old_annotation["updated_datetime"]
|
|
455
|
-
)
|
|
437
|
+
request_body = self.converter.convert_annotation_details(parser, simple_annotation.details, old_details=[], updated_datetime=old_annotation["updated_datetime"])
|
|
456
438
|
|
|
457
439
|
self.service.api.put_annotation(self.project_id, task_id, input_data_id, request_body=request_body, query_params={"v": "2"})
|
|
458
440
|
return True
|
|
@@ -551,9 +533,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
551
533
|
target_task_ids: Optional[set[str]] = None,
|
|
552
534
|
parallelism: Optional[int] = None,
|
|
553
535
|
):
|
|
554
|
-
def get_iter_task_parser_from_task_ids(
|
|
555
|
-
_iter_task_parser: Iterator[SimpleAnnotationParserByTask], _target_task_ids: set[str]
|
|
556
|
-
) -> Iterator[SimpleAnnotationParserByTask]:
|
|
536
|
+
def get_iter_task_parser_from_task_ids(_iter_task_parser: Iterator[SimpleAnnotationParserByTask], _target_task_ids: set[str]) -> Iterator[SimpleAnnotationParserByTask]:
|
|
557
537
|
for task_parser in _iter_task_parser:
|
|
558
538
|
if task_parser.task_id in _target_task_ids:
|
|
559
539
|
_target_task_ids.remove(task_parser.task_id)
|
|
@@ -586,9 +566,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
586
566
|
task_count += 1
|
|
587
567
|
|
|
588
568
|
if target_task_ids is not None and len(tmp_target_task_ids) > 0:
|
|
589
|
-
logger.warning(
|
|
590
|
-
f"'--task_id'で指定したタスクの内 {len(tmp_target_task_ids)} 件は、インポート対象のアノテーションデータに含まれていません。 :: {tmp_target_task_ids}" # noqa: E501
|
|
591
|
-
)
|
|
569
|
+
logger.warning(f"'--task_id'で指定したタスクの内 {len(tmp_target_task_ids)} 件は、インポート対象のアノテーションデータに含まれていません。 :: {tmp_target_task_ids}")
|
|
592
570
|
|
|
593
571
|
logger.info(f"{success_count} / {task_count} 件のタスクに対してアノテーションをインポートしました。")
|
|
594
572
|
|
|
@@ -679,8 +657,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
679
657
|
"--annotation",
|
|
680
658
|
type=Path,
|
|
681
659
|
required=True,
|
|
682
|
-
help="Simpleアノテーションと同じフォルダ構成のzipファイル or
|
|
683
|
-
"タスクの状態が作業中/完了の場合はインポートしません。",
|
|
660
|
+
help="Simpleアノテーションと同じフォルダ構成のzipファイル or ディレクトリのパスを指定してください。タスクの状態が作業中/完了の場合はインポートしません。",
|
|
684
661
|
)
|
|
685
662
|
|
|
686
663
|
argument_parser.add_task_id(required=False)
|
|
@@ -690,8 +667,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
690
667
|
overwrite_merge_group.add_argument(
|
|
691
668
|
"--overwrite",
|
|
692
669
|
action="store_true",
|
|
693
|
-
help="アノテーションが存在する場合、 ``--overwrite``
|
|
694
|
-
"指定しなければ、アノテーションのインポートをスキップします。",
|
|
670
|
+
help="アノテーションが存在する場合、 ``--overwrite`` を指定していれば、すでに存在するアノテーションを削除してインポートします。指定しなければ、アノテーションのインポートをスキップします。",
|
|
695
671
|
)
|
|
696
672
|
|
|
697
673
|
overwrite_merge_group.add_argument(
|
|
@@ -104,9 +104,7 @@ class ListAnnotationMain:
|
|
|
104
104
|
|
|
105
105
|
logger.debug(f"入力データ(input_data_id='{input_data_id}')のアノテーション一覧の件数: {len(annotation_list)}")
|
|
106
106
|
if len(annotation_list) == UPPER_BOUND:
|
|
107
|
-
logger.warning(
|
|
108
|
-
f"入力データ(input_data_id='{input_data_id}')のアノテーション一覧は{UPPER_BOUND}件で打ち切られている可能性があります。"
|
|
109
|
-
)
|
|
107
|
+
logger.warning(f"入力データ(input_data_id='{input_data_id}')のアノテーション一覧は{UPPER_BOUND}件で打ち切られている可能性があります。")
|
|
110
108
|
all_annotation_list.extend(annotation_list)
|
|
111
109
|
return all_annotation_list
|
|
112
110
|
else:
|
|
@@ -248,7 +246,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
248
246
|
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
|
249
247
|
default=FormatArgument.CSV,
|
|
250
248
|
)
|
|
251
|
-
argument_parser.add_csv_format()
|
|
252
249
|
|
|
253
250
|
argument_parser.add_output()
|
|
254
251
|
|