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.
Files changed (134) hide show
  1. annofabcli/annotation/annotation_query.py +9 -29
  2. annofabcli/annotation/change_annotation_attributes.py +6 -14
  3. annofabcli/annotation/change_annotation_properties.py +5 -12
  4. annofabcli/annotation/copy_annotation.py +9 -11
  5. annofabcli/annotation/delete_annotation.py +21 -26
  6. annofabcli/annotation/dump_annotation.py +1 -4
  7. annofabcli/annotation/import_annotation.py +16 -40
  8. annofabcli/annotation/list_annotation.py +1 -4
  9. annofabcli/annotation/merge_segmentation.py +10 -16
  10. annofabcli/annotation/remove_segmentation_overlap.py +14 -30
  11. annofabcli/annotation/restore_annotation.py +3 -9
  12. annofabcli/annotation_specs/add_attribute_restriction.py +2 -8
  13. annofabcli/annotation_specs/attribute_restriction.py +2 -10
  14. annofabcli/annotation_specs/export_annotation_specs.py +1 -3
  15. annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -10
  16. annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +4 -10
  17. annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +1 -3
  18. annofabcli/annotation_specs/list_annotation_specs_attribute.py +7 -18
  19. annofabcli/annotation_specs/list_annotation_specs_choice.py +3 -8
  20. annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
  21. annofabcli/annotation_specs/list_annotation_specs_label.py +3 -8
  22. annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +4 -9
  23. annofabcli/annotation_specs/list_attribute_restriction.py +3 -9
  24. annofabcli/annotation_specs/put_label_color.py +1 -6
  25. annofabcli/comment/delete_comment.py +3 -9
  26. annofabcli/comment/list_all_comment.py +2 -4
  27. annofabcli/comment/list_comment.py +1 -4
  28. annofabcli/comment/put_comment.py +4 -13
  29. annofabcli/comment/put_comment_simply.py +2 -6
  30. annofabcli/comment/put_inspection_comment.py +2 -6
  31. annofabcli/comment/put_inspection_comment_simply.py +3 -6
  32. annofabcli/comment/put_onhold_comment.py +2 -6
  33. annofabcli/comment/put_onhold_comment_simply.py +2 -4
  34. annofabcli/common/cli.py +5 -43
  35. annofabcli/common/download.py +8 -25
  36. annofabcli/common/image.py +5 -9
  37. annofabcli/common/utils.py +1 -3
  38. annofabcli/common/visualize.py +2 -4
  39. annofabcli/filesystem/draw_annotation.py +8 -20
  40. annofabcli/filesystem/filter_annotation.py +7 -24
  41. annofabcli/filesystem/mask_user_info.py +3 -6
  42. annofabcli/filesystem/merge_annotation.py +2 -6
  43. annofabcli/input_data/change_input_data_name.py +3 -7
  44. annofabcli/input_data/copy_input_data.py +6 -14
  45. annofabcli/input_data/delete_input_data.py +7 -24
  46. annofabcli/input_data/delete_metadata_key_of_input_data.py +5 -16
  47. annofabcli/input_data/list_all_input_data.py +5 -14
  48. annofabcli/input_data/list_all_input_data_merged_task.py +8 -23
  49. annofabcli/input_data/list_input_data.py +5 -16
  50. annofabcli/input_data/put_input_data.py +7 -19
  51. annofabcli/input_data/update_metadata_of_input_data.py +6 -14
  52. annofabcli/instruction/list_instruction_history.py +0 -1
  53. annofabcli/instruction/upload_instruction.py +1 -4
  54. annofabcli/job/list_job.py +1 -2
  55. annofabcli/job/list_last_job.py +1 -3
  56. annofabcli/organization/list_organization.py +0 -1
  57. annofabcli/organization_member/change_organization_member.py +1 -3
  58. annofabcli/organization_member/delete_organization_member.py +32 -16
  59. annofabcli/organization_member/invite_organization_member.py +25 -14
  60. annofabcli/organization_member/list_organization_member.py +0 -1
  61. annofabcli/project/change_organization_of_project.py +257 -0
  62. annofabcli/project/change_project_status.py +2 -2
  63. annofabcli/project/copy_project.py +2 -7
  64. annofabcli/project/diff_projects.py +4 -16
  65. annofabcli/project/list_project.py +0 -1
  66. annofabcli/project/put_project.py +2 -6
  67. annofabcli/project/subcommand_project.py +2 -0
  68. annofabcli/project_member/change_project_members.py +2 -2
  69. annofabcli/project_member/copy_project_members.py +2 -7
  70. annofabcli/project_member/drop_project_members.py +1 -3
  71. annofabcli/project_member/invite_project_members.py +1 -3
  72. annofabcli/project_member/list_users.py +0 -1
  73. annofabcli/project_member/put_project_members.py +4 -12
  74. annofabcli/stat_visualization/mask_visualization_dir.py +6 -16
  75. annofabcli/stat_visualization/merge_visualization_dir.py +6 -18
  76. annofabcli/stat_visualization/summarize_whole_performance_csv.py +3 -7
  77. annofabcli/stat_visualization/write_graph.py +5 -15
  78. annofabcli/stat_visualization/write_performance_rating_csv.py +4 -12
  79. annofabcli/statistics/list_annotation_area.py +3 -7
  80. annofabcli/statistics/list_annotation_attribute.py +6 -15
  81. annofabcli/statistics/list_annotation_attribute_filled_count.py +9 -23
  82. annofabcli/statistics/list_annotation_count.py +18 -44
  83. annofabcli/statistics/list_annotation_duration.py +14 -40
  84. annofabcli/statistics/list_video_duration.py +2 -3
  85. annofabcli/statistics/list_worktime.py +0 -1
  86. annofabcli/statistics/scatter.py +3 -9
  87. annofabcli/statistics/summarize_task_count.py +7 -12
  88. annofabcli/statistics/summarize_task_count_by_task_id_group.py +3 -11
  89. annofabcli/statistics/summarize_task_count_by_user.py +1 -5
  90. annofabcli/statistics/visualization/dataframe/annotation_count.py +1 -3
  91. annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +3 -9
  92. annofabcli/statistics/visualization/dataframe/productivity_per_date.py +11 -23
  93. annofabcli/statistics/visualization/dataframe/project_performance.py +1 -3
  94. annofabcli/statistics/visualization/dataframe/task.py +2 -5
  95. annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +6 -20
  96. annofabcli/statistics/visualization/dataframe/user_performance.py +29 -88
  97. annofabcli/statistics/visualization/dataframe/whole_performance.py +4 -10
  98. annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +17 -49
  99. annofabcli/statistics/visualization/dataframe/worktime_per_date.py +3 -9
  100. annofabcli/statistics/visualization/filtering_query.py +2 -6
  101. annofabcli/statistics/visualization/project_dir.py +9 -26
  102. annofabcli/statistics/visualization/visualization_source_files.py +3 -10
  103. annofabcli/statistics/visualize_annotation_count.py +7 -21
  104. annofabcli/statistics/visualize_annotation_duration.py +7 -17
  105. annofabcli/statistics/visualize_statistics.py +17 -52
  106. annofabcli/statistics/visualize_video_duration.py +8 -19
  107. annofabcli/supplementary/delete_supplementary_data.py +7 -23
  108. annofabcli/supplementary/list_supplementary_data.py +1 -1
  109. annofabcli/supplementary/put_supplementary_data.py +5 -15
  110. annofabcli/task/cancel_acceptance.py +3 -4
  111. annofabcli/task/change_operator.py +3 -11
  112. annofabcli/task/change_status_to_break.py +1 -1
  113. annofabcli/task/change_status_to_on_hold.py +5 -18
  114. annofabcli/task/complete_tasks.py +8 -25
  115. annofabcli/task/copy_tasks.py +2 -3
  116. annofabcli/task/delete_metadata_key_of_task.py +2 -6
  117. annofabcli/task/delete_tasks.py +7 -25
  118. annofabcli/task/list_all_tasks.py +2 -4
  119. annofabcli/task/list_tasks.py +2 -6
  120. annofabcli/task/list_tasks_added_task_history.py +7 -21
  121. annofabcli/task/put_tasks.py +2 -3
  122. annofabcli/task/put_tasks_by_count.py +3 -7
  123. annofabcli/task/reject_tasks.py +7 -19
  124. annofabcli/task/update_metadata_of_task.py +1 -1
  125. annofabcli/task_history/list_all_task_history.py +2 -5
  126. annofabcli/task_history/list_task_history.py +0 -1
  127. annofabcli/task_history_event/list_all_task_history_event.py +4 -11
  128. annofabcli/task_history_event/list_worktime.py +4 -14
  129. {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/METADATA +1 -1
  130. annofabcli-1.103.0.dist-info/RECORD +215 -0
  131. annofabcli-1.102.0.dist-info/RECORD +0 -214
  132. {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/WHEEL +0 -0
  133. {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/entry_points.txt +0 -0
  134. {annofabcli-1.102.0.dist-info → annofabcli-1.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,6 +12,7 @@ from typing import Any, Optional
12
12
 
13
13
  import annofabapi
14
14
  import numpy
15
+ from annofabapi.models import ProjectMemberRole
15
16
  from annofabapi.pydantic_models.default_annotation_type import DefaultAnnotationType
16
17
  from annofabapi.pydantic_models.task_status import TaskStatus
17
18
  from annofabapi.segmentation import read_binary_image, write_binary_image
@@ -195,15 +196,10 @@ class MergeSegmentationMain(CommandLineWithConfirm):
195
196
  return 0
196
197
 
197
198
  if task["status"] in {TaskStatus.WORKING.value, TaskStatus.COMPLETE.value}:
198
- logger.debug(
199
- f"{log_message_prefix}task_id='{task_id}'のタスクの状態は「作業中」または「完了」であるため、"
200
- f"アノテーションの更新をスキップします。 :: status='{task['status']}'"
201
- )
199
+ logger.debug(f"{log_message_prefix}task_id='{task_id}'のタスクの状態は「作業中」または「完了」であるため、アノテーションの更新をスキップします。 :: status='{task['status']}'")
202
200
  return 0
203
201
 
204
- if not self.confirm_processing(
205
- f"task_id='{task_id}'の次のラベル名に対応する複数の塗りつぶしアノテーションを1つにまとめますか? :: {self.label_names}"
206
- ):
202
+ if not self.confirm_processing(f"task_id='{task_id}'の次のラベル名に対応する複数の塗りつぶしアノテーションを1つにまとめますか? :: {self.label_names}"):
207
203
  return 0
208
204
 
209
205
  # 担当者割り当て変更チェック
@@ -234,16 +230,12 @@ class MergeSegmentationMain(CommandLineWithConfirm):
234
230
  if result:
235
231
  success_input_data_count += 1
236
232
  except Exception:
237
- logger.warning(
238
- f"{log_message_prefix}task_id='{task_id}', input_data_id='{input_data_id}'のアノテーションの更新に失敗しました。", exc_info=True
239
- )
233
+ logger.warning(f"{log_message_prefix}task_id='{task_id}', input_data_id='{input_data_id}'のアノテーションの更新に失敗しました。", exc_info=True)
240
234
  continue
241
235
 
242
236
  # 担当者を元に戻す
243
237
  if changed_operator:
244
- logger.debug(
245
- f"{log_message_prefix}task_id='{task_id}' のタスクの担当者を、元の担当者(account_id='{original_operator_account_id}')に変更します。"
246
- )
238
+ logger.debug(f"{log_message_prefix}task_id='{task_id}' のタスクの担当者を、元の担当者(account_id='{original_operator_account_id}')に変更します。")
247
239
  self.annofab_service.wrapper.change_task_operator(
248
240
  self.project_id,
249
241
  task_id,
@@ -304,6 +296,8 @@ class MergeSegmentation(CommandLine):
304
296
  sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
305
297
 
306
298
  project_id = args.project_id
299
+ super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER, ProjectMemberRole.WORKER])
300
+
307
301
  task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id)
308
302
  label_name_list = annofabcli.common.cli.get_list_from_args(args.label_name)
309
303
 
@@ -325,8 +319,7 @@ class MergeSegmentation(CommandLine):
325
319
 
326
320
  if len(invalid_label_name_list) > 0:
327
321
  print( # noqa: T201
328
- f"{self.COMMON_MESSAGE} --label_name: 次のラベル名(英語)はアノテーション仕様に存在しないか、"
329
- f"アノテーションの種類が「塗りつぶし」ではありません。 :: {invalid_label_name_list}",
322
+ f"{self.COMMON_MESSAGE} --label_name: 次のラベル名(英語)はアノテーション仕様に存在しないか、アノテーションの種類が「塗りつぶし」ではありません。 :: {invalid_label_name_list}",
330
323
  file=sys.stderr,
331
324
  )
332
325
  sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
@@ -385,6 +378,7 @@ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argpa
385
378
  "複数の塗りつぶしアノテーションを1つにまとめます。"
386
379
  "ラベルの種類を「塗りつぶし(インスタンスセグメンテーション)」から「塗りつぶしv2(セマンティックセグメンテーション)」に変更する場合などに有用です。"
387
380
  )
388
- parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description)
381
+ epilog = "オーナー、チェッカーまたはアノテータロールを持つユーザで実行してください。"
382
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
389
383
  parse_args(parser)
390
384
  return parser
@@ -12,6 +12,7 @@ from typing import Any, Optional
12
12
 
13
13
  import annofabapi
14
14
  import numpy
15
+ from annofabapi.models import ProjectMemberRole
15
16
  from annofabapi.pydantic_models.task_status import TaskStatus
16
17
  from annofabapi.segmentation import read_binary_image, write_binary_image
17
18
  from annofabapi.utils import can_put_annotation
@@ -30,9 +31,7 @@ from annofabcli.common.facade import AnnofabApiFacade
30
31
  logger = logging.getLogger(__name__)
31
32
 
32
33
 
33
- def remove_overlap_of_binary_image_array(
34
- binary_image_array_by_annotation: dict[str, numpy.ndarray], annotation_id_list: list[str]
35
- ) -> dict[str, numpy.ndarray]:
34
+ def remove_overlap_of_binary_image_array(binary_image_array_by_annotation: dict[str, numpy.ndarray], annotation_id_list: list[str]) -> dict[str, numpy.ndarray]:
36
35
  """
37
36
  塗りつぶし画像の重なりを除去したbool配列をdictで返します。
38
37
 
@@ -98,9 +97,7 @@ class RemoveSegmentationOverlapMain(CommandLineWithConfirm):
98
97
 
99
98
  # reversedを使っている理由:
100
99
  # `details`には、前面から背面の順にアノテーションが格納されているため、
101
- output_binary_image_array_by_annotation = remove_overlap_of_binary_image_array(
102
- input_binary_image_array_by_annotation, list(reversed(segmentation_annotation_id_list))
103
- )
100
+ output_binary_image_array_by_annotation = remove_overlap_of_binary_image_array(input_binary_image_array_by_annotation, list(reversed(segmentation_annotation_id_list)))
104
101
 
105
102
  updated_annotation_id_list = []
106
103
  for annotation_id, output_binary_image_array in output_binary_image_array_by_annotation.items():
@@ -127,10 +124,7 @@ class RemoveSegmentationOverlapMain(CommandLineWithConfirm):
127
124
  temp_dir_path = Path(temp_dir)
128
125
  updated_annotation_id_list = self.remove_segmentation_overlap_and_save(old_details, temp_dir_path)
129
126
  if len(updated_annotation_id_list) == 0:
130
- logger.debug(
131
- f"{log_message_prefix}塗りつぶしアノテーションの重なりはなかったので、スキップします。 :: "
132
- f"task_id='{task_id}', input_data_id='{input_data_id}'"
133
- )
127
+ logger.debug(f"{log_message_prefix}塗りつぶしアノテーションの重なりはなかったので、スキップします。 :: task_id='{task_id}', input_data_id='{input_data_id}'")
134
128
  return False
135
129
 
136
130
  logger.debug(
@@ -163,10 +157,7 @@ class RemoveSegmentationOverlapMain(CommandLineWithConfirm):
163
157
  "updated_datetime": old_annotation["updated_datetime"],
164
158
  }
165
159
  self.annofab_service.api.put_annotation(self.project_id, task_id, input_data_id, query_params={"v": "2"}, request_body=request_body)
166
- logger.debug(
167
- f"{log_message_prefix}{len(updated_annotation_id_list)} 件の塗りつぶしアノテーションを更新しました。 :: "
168
- f"task_id='{task_id}', input_data_id='{input_data_id}'"
169
- )
160
+ logger.debug(f"{log_message_prefix}{len(updated_annotation_id_list)} 件の塗りつぶしアノテーションを更新しました。 :: task_id='{task_id}', input_data_id='{input_data_id}'")
170
161
  return True
171
162
 
172
163
  def update_segmentation_annotation_for_task(self, task_id: str, *, task_index: Optional[int] = None) -> int:
@@ -184,10 +175,7 @@ class RemoveSegmentationOverlapMain(CommandLineWithConfirm):
184
175
  return 0
185
176
 
186
177
  if task["status"] in {TaskStatus.WORKING.value, TaskStatus.COMPLETE.value}:
187
- logger.debug(
188
- f"{log_message_prefix}task_id='{task_id}'のタスクの状態は「作業中」または「完了」であるため、"
189
- f"アノテーションの更新をスキップします。 :: status='{task['status']}'"
190
- )
178
+ logger.debug(f"{log_message_prefix}task_id='{task_id}'のタスクの状態は「作業中」または「完了」であるため、アノテーションの更新をスキップします。 :: status='{task['status']}'")
191
179
  return 0
192
180
 
193
181
  if not self.confirm_processing(f"task_id='{task_id}'の塗りつぶしアノテーションの重なりを除去しますか?"):
@@ -221,16 +209,12 @@ class RemoveSegmentationOverlapMain(CommandLineWithConfirm):
221
209
  if result:
222
210
  success_input_data_count += 1
223
211
  except Exception:
224
- logger.warning(
225
- f"{log_message_prefix}task_id='{task_id}', input_data_id='{input_data_id}'のアノテーションの更新に失敗しました。", exc_info=True
226
- )
212
+ logger.warning(f"{log_message_prefix}task_id='{task_id}', input_data_id='{input_data_id}'のアノテーションの更新に失敗しました。", exc_info=True)
227
213
  continue
228
214
 
229
215
  # 担当者を元に戻す
230
216
  if changed_operator:
231
- logger.debug(
232
- f"{log_message_prefix}task_id='{task_id}' のタスクの担当者を、元の担当者(account_id='{original_operator_account_id}')に変更します。"
233
- )
217
+ logger.debug(f"{log_message_prefix}task_id='{task_id}' のタスクの担当者を、元の担当者(account_id='{original_operator_account_id}')に変更します。")
234
218
  self.annofab_service.wrapper.change_task_operator(
235
219
  self.project_id,
236
220
  task_id,
@@ -291,8 +275,11 @@ class RemoveSegmentationOverlap(CommandLine):
291
275
  sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
292
276
 
293
277
  project_id = args.project_id
278
+
294
279
  task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id)
295
280
 
281
+ super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER, ProjectMemberRole.WORKER])
282
+
296
283
  main_obj = RemoveSegmentationOverlapMain(
297
284
  self.service,
298
285
  project_id=project_id,
@@ -333,11 +320,8 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
333
320
  def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
334
321
  subcommand_name = "remove_segmentation_overlap"
335
322
  subcommand_help = "塗りつぶしアノテーションの重なりを除去します。"
336
- description = (
337
- "塗りつぶしアノテーションの重なりを除去します。"
338
- "Annofabでインスタンスセグメンテーションは重ねることができてしまいます。"
339
- "この重なりをなくしたいときに有用です。"
340
- )
341
- parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description)
323
+ description = "塗りつぶしアノテーションの重なりを除去します。Annofabでインスタンスセグメンテーションは重ねることができてしまいます。この重なりをなくしたいときに有用です。"
324
+ epilog = "オーナー、チェッカーまたはアノテータロールを持つユーザで実行してください。"
325
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
342
326
  parse_args(parser)
343
327
  return parser
@@ -214,9 +214,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
214
214
  parallelism: 並列度。Noneなら逐次処理
215
215
  """
216
216
 
217
- def get_iter_task_parser_from_task_ids(
218
- _iter_task_parser: Iterator[SimpleAnnotationParserByTask], _target_task_ids: set[str]
219
- ) -> Iterator[SimpleAnnotationParserByTask]:
217
+ def get_iter_task_parser_from_task_ids(_iter_task_parser: Iterator[SimpleAnnotationParserByTask], _target_task_ids: set[str]) -> Iterator[SimpleAnnotationParserByTask]:
220
218
  for task_parser in _iter_task_parser:
221
219
  if task_parser.task_id in _target_task_ids:
222
220
  _target_task_ids.remove(task_parser.task_id)
@@ -252,9 +250,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
252
250
  task_count += 1
253
251
 
254
252
  if target_task_ids is not None and len(tmp_target_task_ids) > 0:
255
- logger.warning(
256
- f"'--task_id'で指定したタスクの内 {len(tmp_target_task_ids)} 件は、リストア対象のアノテーションデータに含まれていません。 :: {tmp_target_task_ids}" # noqa: E501
257
- )
253
+ logger.warning(f"'--task_id'で指定したタスクの内 {len(tmp_target_task_ids)} 件は、リストア対象のアノテーションデータに含まれていません。 :: {tmp_target_task_ids}")
258
254
 
259
255
  logger.info(f"{success_count} / {task_count} 件のタスクに対してアノテーションをリストアしました。")
260
256
 
@@ -288,9 +284,7 @@ class RestoreAnnotation(CommandLine):
288
284
 
289
285
  task_id_list = set(annofabcli.common.cli.get_list_from_args(args.task_id)) if args.task_id is not None else None
290
286
 
291
- RestoreAnnotationMain(self.service, project_id=project_id, is_force=args.force, all_yes=args.yes).main(
292
- args.annotation, target_task_ids=task_id_list, parallelism=args.parallelism
293
- )
287
+ RestoreAnnotationMain(self.service, project_id=project_id, is_force=args.force, all_yes=args.yes).main(args.annotation, target_task_ids=task_id_list, parallelism=args.parallelism)
294
288
 
295
289
 
296
290
  def main(args: argparse.Namespace) -> None:
@@ -60,10 +60,7 @@ class AddAttributeRestrictionMain(CommandLineWithConfirm):
60
60
  try:
61
61
  restriction_text = msg_obj.get_restriction_text(restriction["additional_data_definition_id"], restriction["condition"])
62
62
  except ValueError as e:
63
- logger.warning(
64
- f"{index + 1}件目 :: 次の属性制約は存在しないIDが含まれていたため、アノテーション仕様に追加しません。 :: "
65
- f"restriction=`{restriction}`, error_message=`{e!s}`"
66
- )
63
+ logger.warning(f"{index + 1}件目 :: 次の属性制約は存在しないIDが含まれていたため、アノテーション仕様に追加しません。 :: restriction=`{restriction}`, error_message=`{e!s}`")
67
64
  continue
68
65
 
69
66
  if restriction in old_restrictions:
@@ -131,10 +128,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
131
128
  "--json",
132
129
  type=str,
133
130
  required=True,
134
- help="追加する属性の制約情報のJSONを指定します。"
135
- "JSON形式は ... を参照してください。\n"
136
- f"(例) ``{json.dumps(sample_json)}``\n"
137
- "``file://`` を先頭に付けるとjsonファイルを指定できます。",
131
+ help=f"追加する属性の制約情報のJSONを指定します。JSON形式は ... を参照してください。\n(例) ``{json.dumps(sample_json)}``\n``file://`` を先頭に付けるとjsonファイルを指定できます。",
138
132
  )
139
133
 
140
134
  parser.add_argument("--comment", type=str, help="アノテーション仕様の変更時に指定できるコメント。未指定の場合、自動でコメントが生成されます。")
@@ -169,11 +169,7 @@ class AttributeRestrictionMessage:
169
169
  return tmp
170
170
 
171
171
  def get_attribute_from_name(self, attribute_name: str) -> Optional[dict[str, Any]]:
172
- tmp = [
173
- attribute
174
- for attribute in self.attribute_dict.values()
175
- if AnnofabApiFacade.get_additional_data_definition_name_en(attribute) == attribute_name
176
- ]
172
+ tmp = [attribute for attribute in self.attribute_dict.values() if AnnofabApiFacade.get_additional_data_definition_name_en(attribute) == attribute_name]
177
173
  if len(tmp) == 1:
178
174
  return tmp[0]
179
175
  elif len(tmp) == 0:
@@ -231,10 +227,6 @@ class AttributeRestrictionMessage:
231
227
  """
232
228
  if target_attribute_names is not None or target_label_names is not None:
233
229
  target_attribute_ids = self.get_target_attribute_ids(target_attribute_names=target_attribute_names, target_label_names=target_label_names)
234
- return [
235
- self.get_restriction_text(e["additional_data_definition_id"], e["condition"])
236
- for e in restrictions
237
- if e["additional_data_definition_id"] in target_attribute_ids
238
- ]
230
+ return [self.get_restriction_text(e["additional_data_definition_id"], e["condition"]) for e in restrictions if e["additional_data_definition_id"] in target_attribute_ids]
239
231
  else:
240
232
  return [self.get_restriction_text(e["additional_data_definition_id"], e["condition"]) for e in restrictions]
@@ -28,9 +28,7 @@ class ExportAnnotationSpecs(CommandLine):
28
28
  sorted_histories = sorted(histories, key=lambda x: x["updated_datetime"], reverse=True)
29
29
 
30
30
  if before + 1 > len(sorted_histories):
31
- logger.warning(
32
- f"アノテーション仕様の履歴は{len(sorted_histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。"
33
- )
31
+ logger.warning(f"アノテーション仕様の履歴は{len(sorted_histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
34
32
  return None
35
33
 
36
34
  history = sorted_histories[before]
@@ -101,16 +101,11 @@ class ReplacingAttributeId(CommandLineWithConfirm):
101
101
  continue
102
102
 
103
103
  if not self.validate_attribute_id(attribute_name_en):
104
- logger.warning(
105
- f"属性英語名='{attribute_name_en}'は属性IDに利用できない文字を含むため、"
106
- f"属性ID='{attribute_id}'を'{attribute_name_en}'に変更しません。"
107
- )
104
+ logger.warning(f"属性英語名='{attribute_name_en}'は属性IDに利用できない文字を含むため、属性ID='{attribute_id}'を'{attribute_name_en}'に変更しません。")
108
105
  continue
109
106
 
110
107
  if self.exists_attribute_with_attribute_id(attribute_list, attribute_id=attribute_name_en):
111
- logger.warning(
112
- f"属性ID='{attribute_name_en}'である属性が既に存在するため、属性ID='{attribute_id}'を'{attribute_name_en}'に変更しません。"
113
- )
108
+ logger.warning(f"属性ID='{attribute_name_en}'である属性が既に存在するため、属性ID='{attribute_id}'を'{attribute_name_en}'に変更しません。")
114
109
  continue
115
110
 
116
111
  if not self.confirm_processing(f"属性ID='{attribute_id}'を'{attribute_name_en}'に変更したアノテーション仕様のJSONを出力しますか?"):
@@ -118,9 +113,7 @@ class ReplacingAttributeId(CommandLineWithConfirm):
118
113
 
119
114
  attribute["additional_data_definition_id"] = attribute_name_en
120
115
  self.replace_attribute_id_of_labels(old_attribute_id=attribute_id, new_attribute_id=attribute_name_en, label_list=label_list)
121
- self.replace_attribute_id_of_restrictions(
122
- old_attribute_id=attribute_id, new_attribute_id=attribute_name_en, restriction_list=restriction_list
123
- )
116
+ self.replace_attribute_id_of_restrictions(old_attribute_id=attribute_id, new_attribute_id=attribute_name_en, restriction_list=restriction_list)
124
117
  replaced_count += 1
125
118
 
126
119
  logger.info(f"{replaced_count} 個の属性の属性IDを変更しました。")
@@ -44,7 +44,7 @@ class ReplacingChoiceId(CommandLineWithConfirm):
44
44
  annotation_specs: (IN/OUT) アノテーション仕様情報。中身が変更されます。
45
45
  target_attribute_names: 変更対象のラジオボタン/ドロップダウン属性の英語名。Noneならすべてのラジオボタン/ドロップダウン属性の選択肢IDを変更します。
46
46
 
47
- """ # noqa: E501
47
+ """
48
48
  attribute_list = annotation_specs["additionals"]
49
49
 
50
50
  replaced_count = 0
@@ -62,9 +62,7 @@ class ReplacingChoiceId(CommandLineWithConfirm):
62
62
 
63
63
  choice_list = attribute["choices"]
64
64
 
65
- if not self.confirm_processing(
66
- f"属性英語名='{attribute_name_en}'の{len(choice_list)}個の選択肢IDを、選択肢英語名に変更したアノテーション仕様のJSONを出力しますか?"
67
- ):
65
+ if not self.confirm_processing(f"属性英語名='{attribute_name_en}'の{len(choice_list)}個の選択肢IDを、選択肢英語名に変更したアノテーション仕様のJSONを出力しますか?"):
68
66
  continue
69
67
 
70
68
  replaced_choice_id_count = 0
@@ -74,16 +72,12 @@ class ReplacingChoiceId(CommandLineWithConfirm):
74
72
 
75
73
  if not self.validate_choice_id(choice_name_en):
76
74
  logger.warning(
77
- f"属性英語名='{attribute_name_en}', 選択肢英語名='{choice_name_en}'は選択肢IDに利用できない文字を含むため、"
78
- f"選択肢ID='{choice_id}'を'{choice_name_en}'に変更しません。"
75
+ f"属性英語名='{attribute_name_en}', 選択肢英語名='{choice_name_en}'は選択肢IDに利用できない文字を含むため、選択肢ID='{choice_id}'を'{choice_name_en}'に変更しません。"
79
76
  )
80
77
  continue
81
78
 
82
79
  if self.exists_choice_with_choice_id(choice_list, choice_id=choice_name_en):
83
- logger.warning(
84
- f"属性英語名='{attribute_name_en}', 選択肢ID='{choice_name_en}'である選択肢が既に存在するため、"
85
- f"選択肢ID='{choice_id}'を'{choice_name_en}'に変更しません。"
86
- )
80
+ logger.warning(f"属性英語名='{attribute_name_en}', 選択肢ID='{choice_name_en}'である選択肢が既に存在するため、選択肢ID='{choice_id}'を'{choice_name_en}'に変更しません。")
87
81
  continue
88
82
 
89
83
  choice["choice_id"] = choice_name_en
@@ -86,9 +86,7 @@ class ReplacingLabelId(CommandLineWithConfirm):
86
86
  continue
87
87
 
88
88
  if not self.validate_label_id(label_name_en):
89
- logger.warning(
90
- f"label_name_en='{label_name_en}'はlabel_idにできない文字を含むため、label_id='{label_id}'を'{label_name_en}'に変更しません。"
91
- )
89
+ logger.warning(f"label_name_en='{label_name_en}'はlabel_idにできない文字を含むため、label_id='{label_id}'を'{label_name_en}'に変更しません。")
92
90
  continue
93
91
 
94
92
  if self.exists_label_with_label_id(label_list, label_id=label_name_en):
@@ -43,7 +43,7 @@ class FlattenAttribute(DataClassJsonMixin):
43
43
  Notes:
44
44
  APIレスポンスの ``additional_data_definition_id`` に相当します。
45
45
  ``additional_data_definition_id`` という名前がアノテーションJSONの `attributes` と対応していることが分かりにくかったので、`attribute_id`という名前に変えました。
46
- """ # noqa: E501
46
+ """
47
47
  attribute_name_en: Optional[str]
48
48
  attribute_name_ja: Optional[str]
49
49
  attribute_name_vi: Optional[str]
@@ -81,9 +81,7 @@ def create_relationship_between_attribute_and_label(labels_v3: list[dict[str, An
81
81
  return result
82
82
 
83
83
 
84
- def create_flatten_attribute_list_from_additionals(
85
- additionals_v3: list[dict[str, Any]], labels_v3: list[dict[str, Any]], restrictions: list[dict[str, Any]]
86
- ) -> list[FlattenAttribute]:
84
+ def create_flatten_attribute_list_from_additionals(additionals_v3: list[dict[str, Any]], labels_v3: list[dict[str, Any]], restrictions: list[dict[str, Any]]) -> list[FlattenAttribute]:
87
85
  """
88
86
  APIから取得した属性情報(v3版)から、属性情報の一覧を生成します。
89
87
 
@@ -127,12 +125,8 @@ def create_flatten_attribute_list_from_additionals(
127
125
  class PrintAnnotationSpecsAttribute(CommandLine):
128
126
  COMMON_MESSAGE = "annofabcli annotation_specs list_attribute: error:"
129
127
 
130
- def print_annotation_specs_attribute(
131
- self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: Optional[str] = None
132
- ) -> None:
133
- attribute_list = create_flatten_attribute_list_from_additionals(
134
- annotation_specs_v3["additionals"], annotation_specs_v3["labels"], annotation_specs_v3["restrictions"]
135
- )
128
+ def print_annotation_specs_attribute(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: Optional[str] = None) -> None:
129
+ attribute_list = create_flatten_attribute_list_from_additionals(annotation_specs_v3["additionals"], annotation_specs_v3["labels"], annotation_specs_v3["restrictions"])
136
130
  logger.info(f"{len(attribute_list)} 件の属性情報を出力します。")
137
131
  if output_format == FormatArgument.CSV:
138
132
  columns = [
@@ -161,9 +155,7 @@ class PrintAnnotationSpecsAttribute(CommandLine):
161
155
  logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
162
156
  return None
163
157
  history = histories[-(before + 1)]
164
- logger.info(
165
- f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
166
- )
158
+ logger.info(f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'")
167
159
  return history["history_id"]
168
160
 
169
161
  def main(self) -> None:
@@ -195,14 +187,11 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
195
187
  argument_parser = ArgumentParser(parser)
196
188
 
197
189
  required_group = parser.add_mutually_exclusive_group(required=True)
198
- required_group.add_argument(
199
- "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
200
- )
190
+ required_group.add_argument("-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。")
201
191
  required_group.add_argument(
202
192
  "--annotation_specs_json",
203
193
  type=Path,
204
- help="指定したアノテーション仕様のJSONファイルを指定します。"
205
- "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
194
+ help="指定したアノテーション仕様のJSONファイルを指定します。JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
206
195
  )
207
196
 
208
197
  # 過去のアノテーション仕様を参照するためのオプション
@@ -133,9 +133,7 @@ class PrintAnnotationSpecsAttribute(CommandLine):
133
133
  logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
134
134
  return None
135
135
  history = histories[-(before + 1)]
136
- logger.info(
137
- f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
138
- )
136
+ logger.info(f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'")
139
137
  return history["history_id"]
140
138
 
141
139
  def main(self) -> None:
@@ -169,14 +167,11 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
169
167
  argument_parser = ArgumentParser(parser)
170
168
 
171
169
  required_group = parser.add_mutually_exclusive_group(required=True)
172
- required_group.add_argument(
173
- "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
174
- )
170
+ required_group.add_argument("-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。")
175
171
  required_group.add_argument(
176
172
  "--annotation_specs_json",
177
173
  type=Path,
178
- help="指定したアノテーション仕様のJSONファイルを指定します。"
179
- "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
174
+ help="指定したアノテーション仕様のJSONファイルを指定します。JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
180
175
  )
181
176
 
182
177
  # 過去のアノテーション仕様を参照するためのオプション
@@ -50,7 +50,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
50
50
 
51
51
  argument_parser.add_format(choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON], default=FormatArgument.CSV)
52
52
  argument_parser.add_output()
53
- argument_parser.add_csv_format()
54
53
 
55
54
  parser.set_defaults(subcommand_func=main)
56
55
 
@@ -119,9 +119,7 @@ class PrintAnnotationSpecsLabel(CommandLine):
119
119
  logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
120
120
  return None
121
121
  history = histories[-(before + 1)]
122
- logger.info(
123
- f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
124
- )
122
+ logger.info(f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'")
125
123
  return history["history_id"]
126
124
 
127
125
  def main(self) -> None:
@@ -156,14 +154,11 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
156
154
  argument_parser = ArgumentParser(parser)
157
155
 
158
156
  required_group = parser.add_mutually_exclusive_group(required=True)
159
- required_group.add_argument(
160
- "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
161
- )
157
+ required_group.add_argument("-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。")
162
158
  required_group.add_argument(
163
159
  "--annotation_specs_json",
164
160
  type=Path,
165
- help="指定したアノテーション仕様のJSONファイルを指定します。"
166
- "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
161
+ help="指定したアノテーション仕様のJSONファイルを指定します。JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
167
162
  )
168
163
 
169
164
  # 過去のアノテーション仕様を参照するためのオプション
@@ -42,7 +42,7 @@ class LabelAndAttribute(DataClassJsonMixin):
42
42
  Notes:
43
43
  APIレスポンスの ``additional_data_definition_id`` に相当します。
44
44
  ``additional_data_definition_id`` という名前がアノテーションJSONの `attributes` と対応していることが分かりにくかったので、`attribute_id`という名前に変えました。
45
- """ # noqa: E501
45
+ """
46
46
  attribute_name_en: Optional[str]
47
47
  attribute_name_ja: Optional[str]
48
48
  attribute_name_vi: Optional[str]
@@ -121,9 +121,7 @@ class PrintAnnotationSpecsLabelAndAttribute(CommandLine):
121
121
  logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
122
122
  return None
123
123
  history = histories[-(before + 1)]
124
- logger.info(
125
- f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
126
- )
124
+ logger.info(f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'")
127
125
  return history["history_id"]
128
126
 
129
127
  def main(self) -> None:
@@ -158,14 +156,11 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
158
156
  argument_parser = ArgumentParser(parser)
159
157
 
160
158
  required_group = parser.add_mutually_exclusive_group(required=True)
161
- required_group.add_argument(
162
- "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
163
- )
159
+ required_group.add_argument("-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。")
164
160
  required_group.add_argument(
165
161
  "--annotation_specs_json",
166
162
  type=Path,
167
- help="指定したアノテーション仕様のJSONファイルを指定します。"
168
- "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
163
+ help="指定したアノテーション仕様のJSONファイルを指定します。JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
169
164
  )
170
165
 
171
166
  # 過去のアノテーション仕様を参照するためのオプション
@@ -82,14 +82,11 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
82
82
  argument_parser = ArgumentParser(parser)
83
83
 
84
84
  required_group = parser.add_mutually_exclusive_group(required=True)
85
- required_group.add_argument(
86
- "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
87
- )
85
+ required_group.add_argument("-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。")
88
86
  required_group.add_argument(
89
87
  "--annotation_specs_json",
90
88
  type=Path,
91
- help="指定したアノテーション仕様のJSONファイルを指定します。"
92
- "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
89
+ help="指定したアノテーション仕様のJSONファイルを指定します。JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
93
90
  )
94
91
 
95
92
  # 過去のアノテーション仕様を参照するためのオプション
@@ -124,10 +121,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
124
121
  type=str,
125
122
  choices=[e.value for e in OutputFormat],
126
123
  default=OutputFormat.TEXT.value,
127
- help=f"出力フォーマット\n"
128
- "\n"
129
- f"* {OutputFormat.TEXT.value}: 英語名のみ出力する形式\n"
130
- f"* {OutputFormat.DETAILED_TEXT.value}: 属性IDや属性種類などの詳細情報を出力する形式\n",
124
+ help=f"出力フォーマット\n\n* {OutputFormat.TEXT.value}: 英語名のみ出力する形式\n* {OutputFormat.DETAILED_TEXT.value}: 属性IDや属性種類などの詳細情報を出力する形式\n",
131
125
  )
132
126
 
133
127
  parser.set_defaults(subcommand_func=main)
@@ -119,12 +119,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
119
119
  "--json",
120
120
  type=str,
121
121
  required=True,
122
- help=(
123
- "変更したいラベルの色をJSON形式で指定してください。"
124
- "keyがラベル英語名, valueがRGB値の配列です。\n"
125
- f"(ex) ``{JSON_SAMPLE}`` \n"
126
- "``file://`` を先頭に付けるとjsonファイルを指定できます。"
127
- ),
122
+ help=(f"変更したいラベルの色をJSON形式で指定してください。keyがラベル英語名, valueがRGB値の配列です。\n(ex) ``{JSON_SAMPLE}`` \n``file://`` を先頭に付けるとjsonファイルを指定できます。"),
128
123
  )
129
124
 
130
125
  parser.add_argument(
@@ -98,9 +98,7 @@ class DeleteCommentMain(CommandLineWithConfirm):
98
98
  ) -> bool:
99
99
  task_id = task["task_id"]
100
100
  if task["status"] not in [TaskStatus.NOT_STARTED.value, TaskStatus.WORKING.value, TaskStatus.BREAK.value]:
101
- logger.warning(
102
- f"task_id='{task_id}' : タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを削除できません。(task_status='{task['status']}')" # noqa: E501
103
- )
101
+ logger.warning(f"task_id='{task_id}' : タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを削除できません。(task_status='{task['status']}')")
104
102
  return False
105
103
  return True
106
104
 
@@ -151,13 +149,9 @@ class DeleteCommentMain(CommandLineWithConfirm):
151
149
  if len(request_body) > 0:
152
150
  self.service.api.batch_update_comments(self.project_id, task_id, input_data_id, request_body=request_body)
153
151
  added_comments_count += 1
154
- logger.debug(
155
- f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(request_body)}件のコメントを削除しました。"
156
- )
152
+ logger.debug(f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(request_body)}件のコメントを削除しました。")
157
153
  else:
158
- logger.warning(
159
- f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: 削除できるコメントは存在しませんでした。"
160
- )
154
+ logger.warning(f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: 削除できるコメントは存在しませんでした。")
161
155
 
162
156
  except Exception: # pylint: disable=broad-except
163
157
  logger.warning(