annofabcli 1.114.6__py3-none-any.whl → 1.114.7__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.
@@ -46,7 +46,7 @@ class ChangeAnnotationAttributesMain(CommandLineWithConfirm):
46
46
  service: annofabapi.Resource,
47
47
  *,
48
48
  project_id: str,
49
- include_completed: bool,
49
+ include_complete_task: bool,
50
50
  all_yes: bool,
51
51
  ) -> None:
52
52
  self.service = service
@@ -54,7 +54,7 @@ class ChangeAnnotationAttributesMain(CommandLineWithConfirm):
54
54
  CommandLineWithConfirm.__init__(self, all_yes)
55
55
 
56
56
  self.project_id = project_id
57
- self.include_completed = include_completed
57
+ self.include_complete_task = include_complete_task
58
58
 
59
59
  self.dump_annotation_obj = DumpAnnotationMain(service, project_id)
60
60
 
@@ -141,9 +141,9 @@ class ChangeAnnotationAttributesMain(CommandLineWithConfirm):
141
141
  logger.warning(f"task_id='{task_id}': タスクが作業中状態のため、スキップします。")
142
142
  return False, 0
143
143
 
144
- if not self.include_completed: # noqa: SIM102
144
+ if not self.include_complete_task: # noqa: SIM102
145
145
  if task.status == TaskStatus.COMPLETE:
146
- logger.warning(f"task_id='{task_id}': タスクが完了状態のため、スキップします。完了状態のタスクのアノテーション属性値を変更するには、 ``--include_completed`` を指定してください。")
146
+ logger.warning(f"task_id='{task_id}': タスクが完了状態のため、スキップします。完了状態のタスクのアノテーション属性値を変更するには、 ``--include_complete_task`` を指定してください。")
147
147
  return False, 0
148
148
 
149
149
  annotation_list = self.get_annotation_list_for_task(task_id, annotation_query)
@@ -314,15 +314,15 @@ class ChangeAttributesOfAnnotation(CommandLine):
314
314
  backup_dir = Path(args.backup)
315
315
 
316
316
  super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER])
317
- if args.include_completed: # noqa: SIM102
317
+ if args.include_complete_task: # noqa: SIM102
318
318
  if not self.facade.contains_any_project_member_role(project_id, [ProjectMemberRole.OWNER]):
319
319
  print( # noqa: T201
320
- f"{self.COMMON_MESSAGE} argument --include_completed : '--include_completed' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
320
+ f"{self.COMMON_MESSAGE} argument --include_complete_task : '--include_complete_task' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
321
321
  file=sys.stderr,
322
322
  )
323
323
  sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
324
324
 
325
- main_obj = ChangeAnnotationAttributesMain(self.service, project_id=project_id, include_completed=args.include_completed, all_yes=args.yes)
325
+ main_obj = ChangeAnnotationAttributesMain(self.service, project_id=project_id, include_complete_task=args.include_complete_task, all_yes=args.yes)
326
326
  main_obj.change_annotation_attributes_for_task_list(
327
327
  task_id_list,
328
328
  annotation_query=annotation_query,
@@ -362,7 +362,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
362
362
  )
363
363
 
364
364
  parser.add_argument(
365
- "--include_completed",
365
+ "--include_complete_task",
366
366
  action="store_true",
367
367
  help="完了状態のタスクのアノテーション属性も変更します。ただし、オーナーロールを持つユーザーでしか実行できません。",
368
368
  )
@@ -33,7 +33,8 @@ class CreateClassificationAnnotationMain(CommandLineWithConfirm):
33
33
  project_id: str,
34
34
  all_yes: bool,
35
35
  is_change_operator_to_me: bool,
36
- include_completed: bool,
36
+ include_complete_task: bool,
37
+ include_on_hold_task: bool,
37
38
  ) -> None:
38
39
  self.service = service
39
40
  self.facade = AnnofabApiFacade(service)
@@ -41,7 +42,8 @@ class CreateClassificationAnnotationMain(CommandLineWithConfirm):
41
42
 
42
43
  self.project_id = project_id
43
44
  self.is_change_operator_to_me = is_change_operator_to_me
44
- self.include_completed = include_completed
45
+ self.include_complete_task = include_complete_task
46
+ self.include_on_hold_task = include_on_hold_task
45
47
 
46
48
  # アノテーション仕様を取得
47
49
  annotation_specs_v3, _ = self.service.api.get_annotation_specs(self.project_id, query_params={"v": "3"})
@@ -69,10 +71,18 @@ class CreateClassificationAnnotationMain(CommandLineWithConfirm):
69
71
  logger.info(f"タスク'{task_id}'は作業中状態のため、全体アノテーションの作成をスキップします。")
70
72
  return None, False, None
71
73
 
72
- if not self.include_completed: # noqa: SIM102
74
+ if not self.include_complete_task: # noqa: SIM102
73
75
  if task["status"] == TaskStatus.COMPLETE.value:
74
76
  logger.info(
75
- f"タスク'{task_id}'は完了状態のため、全体アノテーションの作成をスキップします。完了状態のタスクに全体アノテーションを作成するには、 ``--include_completed`` を指定してください。"
77
+ f"タスク'{task_id}'は完了状態のため、全体アノテーションの作成をスキップします。"
78
+ f"完了状態のタスクに全体アノテーションを作成するには、 ``--include_complete_task`` を指定してください。"
79
+ )
80
+ return None, False, None
81
+
82
+ if not self.include_on_hold_task: # noqa: SIM102
83
+ if task["status"] == TaskStatus.ON_HOLD.value:
84
+ logger.info(
85
+ f"タスク'{task_id}'は保留状態のため、全体アノテーションの作成をスキップします。保留状態のタスクに全体アノテーションを作成するには、 ``--include_on_hold_task`` を指定してください。"
76
86
  )
77
87
  return None, False, None
78
88
 
@@ -324,11 +334,11 @@ class CreateClassificationAnnotation(CommandLine):
324
334
 
325
335
  super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER])
326
336
 
327
- if args.include_completed: # noqa: SIM102
337
+ if args.include_complete_task: # noqa: SIM102
328
338
  if not self.facade.contains_any_project_member_role(project_id, [ProjectMemberRole.OWNER]):
329
339
  print( # noqa: T201
330
- "annofabcli annotation create_classification: error: argument --include_completed : "
331
- "'--include_completed' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
340
+ "annofabcli annotation create_classification: error: argument --include_complete_task : "
341
+ "'--include_complete_task' 引数を利用するにはプロジェクトのオーナーロールを持つユーザーで実行する必要があります。",
332
342
  file=sys.stderr,
333
343
  )
334
344
  sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
@@ -341,7 +351,8 @@ class CreateClassificationAnnotation(CommandLine):
341
351
  project_id=project_id,
342
352
  all_yes=self.all_yes,
343
353
  is_change_operator_to_me=args.change_operator_to_me,
344
- include_completed=args.include_completed,
354
+ include_complete_task=args.include_complete_task,
355
+ include_on_hold_task=args.include_on_hold_task,
345
356
  )
346
357
 
347
358
  main_obj.main(task_ids, labels, parallelism=args.parallelism)
@@ -377,11 +388,17 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
377
388
  )
378
389
 
379
390
  parser.add_argument(
380
- "--include_completed",
391
+ "--include_complete_task",
381
392
  action="store_true",
382
393
  help="完了状態のタスクにも全体アノテーションを作成します。ただし、オーナーロールを持つユーザーでしか実行できません。",
383
394
  )
384
395
 
396
+ parser.add_argument(
397
+ "--include_on_hold_task",
398
+ action="store_true",
399
+ help="保留状態のタスクにも全体アノテーションを作成します。",
400
+ )
401
+
385
402
  parser.add_argument(
386
403
  "--parallelism",
387
404
  type=int,
@@ -46,7 +46,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
46
46
  """アノテーション削除処理用のクラス
47
47
 
48
48
  Args:
49
- is_force: 完了状態のタスクを削除するかどうか
49
+ include_complete_task: 完了状態のタスクを削除するかどうか
50
50
  """
51
51
 
52
52
  def __init__(
@@ -54,12 +54,12 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
54
54
  service: annofabapi.Resource,
55
55
  project_id: str,
56
56
  *,
57
- is_force: bool,
57
+ include_complete_task: bool,
58
58
  all_yes: bool,
59
59
  ) -> None:
60
60
  self.service = service
61
61
  self.facade = AnnofabApiFacade(service)
62
- self.is_force = is_force
62
+ self.include_complete_task = include_complete_task
63
63
  CommandLineWithConfirm.__init__(self, all_yes)
64
64
  self.project_id = project_id
65
65
  self.dump_annotation_obj = DumpAnnotationMain(service, project_id)
@@ -133,9 +133,9 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
133
133
  logger.info(f"task_id='{task_id}' :: タスクが作業中状態のため、スキップします。")
134
134
  return
135
135
 
136
- if not self.is_force: # noqa: SIM102
136
+ if not self.include_complete_task: # noqa: SIM102
137
137
  if task.status == TaskStatus.COMPLETE:
138
- logger.info(f"task_id='{task_id}' :: タスクが完了状態のため、スキップします。完了状態のタスクのアノテーションを削除するには、`--include_completed`オプションを指定してください。")
138
+ logger.info(f"task_id='{task_id}' :: タスクが完了状態のため、スキップします。完了状態のタスクのアノテーションを削除するには、`--include_complete_task`オプションを指定してください。")
139
139
  return
140
140
 
141
141
  annotation_list = self.get_annotation_list_for_task(task_id, annotation_query=annotation_query)
@@ -182,7 +182,7 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
182
182
  if task.status == TaskStatus.WORKING:
183
183
  continue
184
184
 
185
- if not self.is_force and task.status == TaskStatus.COMPLETE:
185
+ if not self.include_complete_task and task.status == TaskStatus.COMPLETE:
186
186
  continue
187
187
 
188
188
  # アノテーション一覧を取得して、削除対象があるかチェック
@@ -316,11 +316,11 @@ class DeleteAnnotationMain(CommandLineWithConfirm):
316
316
  failed_to_delete_annotation_count += annotation_count
317
317
  continue
318
318
 
319
- if not self.is_force: # noqa: SIM102
319
+ if not self.include_complete_task: # noqa: SIM102
320
320
  if task["status"] == TaskStatus.COMPLETE.value:
321
321
  logger.info(
322
322
  f"task_id='{task_id}' :: タスクが完了状態のため、アノテーション {annotation_count} 件の削除をスキップします。"
323
- f"完了状態のタスクのアノテーションを削除するには、`--include_completed`オプションを指定してください。"
323
+ f"完了状態のタスクのアノテーションを削除するには、`--include_complete_task`オプションを指定してください。"
324
324
  )
325
325
  failed_to_delete_annotation_count += annotation_count
326
326
  continue
@@ -376,14 +376,14 @@ class DeleteAnnotation(CommandLine):
376
376
  else:
377
377
  backup_dir = Path(args.backup)
378
378
 
379
- if args.include_completed:
380
- # --include_completedオプションが指定されている場合は、完了状態のタスクも削除する
381
- # 完了状態のタスクを削除するには、オーナーロールである必要があるため、`args.include_completed`で条件を分岐する
379
+ if args.include_complete_task:
380
+ # --include_complete_taskオプションが指定されている場合は、完了状態のタスクも削除する
381
+ # 完了状態のタスクを削除するには、オーナーロールである必要があるため、`args.include_complete_task`で条件を分岐する
382
382
  super().validate_project(project_id, [ProjectMemberRole.OWNER])
383
383
  else:
384
384
  super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER])
385
385
 
386
- main_obj = DeleteAnnotationMain(self.service, project_id, all_yes=args.yes, is_force=args.include_completed)
386
+ main_obj = DeleteAnnotationMain(self.service, project_id, all_yes=args.yes, include_complete_task=args.include_complete_task)
387
387
 
388
388
  if args.json is not None:
389
389
  dict_annotation_list = get_json_from_args(args.json)
@@ -479,7 +479,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
479
479
  )
480
480
 
481
481
  parser.add_argument(
482
- "--include_completed",
482
+ "--include_complete_task",
483
483
  action="store_true",
484
484
  help="指定した場合は、完了状態のタスクのアノテーションも削除します。ただし、完了状態のタスクを削除するには、オーナーロールを持つユーザーが実行する必要があります。",
485
485
  )
@@ -499,7 +499,7 @@ def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse
499
499
  "タスク配下のアノテーションを削除します。ただし、作業中状態のタスクのアノテーションは削除できません。"
500
500
  "間違えてアノテーションを削除したときに復元できるようにするため、 ``--backup`` でバックアップ用のディレクトリを指定することを推奨します。"
501
501
  )
502
- epilog = "オーナーまたはチェッカーロールを持つユーザで実行してください。ただし``--include_completed``オプションを指定した場合は、オーナーロールを持つユーザで実行してください。"
502
+ epilog = "オーナーまたはチェッカーロールを持つユーザで実行してください。ただし``--include_complete_task``オプションを指定した場合は、オーナーロールを持つユーザで実行してください。"
503
503
 
504
504
  parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
505
505
  parse_args(parser)
@@ -410,9 +410,11 @@ class ImportAnnotationMain(CommandLineWithConfirm):
410
410
  *,
411
411
  project_id: str,
412
412
  all_yes: bool,
413
- is_force: bool,
413
+ change_operator_to_me: bool,
414
414
  is_merge: bool,
415
415
  is_overwrite: bool,
416
+ include_complete_task: bool,
417
+ include_on_hold_task: bool,
416
418
  converter: AnnotationConverter,
417
419
  ) -> None:
418
420
  self.service = service
@@ -420,9 +422,11 @@ class ImportAnnotationMain(CommandLineWithConfirm):
420
422
  CommandLineWithConfirm.__init__(self, all_yes)
421
423
 
422
424
  self.project_id = project_id
423
- self.is_force = is_force
425
+ self.change_operator_to_me = change_operator_to_me
424
426
  self.is_merge = is_merge
425
427
  self.is_overwrite = is_overwrite
428
+ self.include_complete_task = include_complete_task
429
+ self.include_on_hold_task = include_on_hold_task
426
430
  self.converter = converter
427
431
 
428
432
  def put_annotation_for_input_data(self, parser: SimpleAnnotationParser) -> int:
@@ -487,7 +491,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
487
491
 
488
492
  return success_input_data_count, success_annotation_count
489
493
 
490
- def execute_task(self, task_parser: SimpleAnnotationParserByTask, task_index: int | None = None) -> bool:
494
+ def execute_task(self, task_parser: SimpleAnnotationParserByTask, task_index: int | None = None) -> bool: # noqa: PLR0911
491
495
  """
492
496
  1個のタスクに対してアノテーションを登録する。
493
497
 
@@ -499,26 +503,39 @@ class ImportAnnotationMain(CommandLineWithConfirm):
499
503
 
500
504
  """
501
505
  task_id = task_parser.task_id
502
- if not self.confirm_processing(f"task_id='{task_id}' のアノテーションをインポートしますか?"):
503
- return False
504
-
505
- logger_prefix = f"{task_index + 1!s} 件目: " if task_index is not None else ""
506
- logger.info(f"{logger_prefix}task_id='{task_id}' に対して処理します。")
506
+ logger_prefix = f"{task_index + 1!s} 件目 :: task_id='{task_id}' :: " if task_index is not None else ""
507
507
 
508
508
  task = self.service.wrapper.get_task_or_none(self.project_id, task_id)
509
509
  if task is None:
510
- logger.warning(f"task_id='{task_id}'であるタスクは存在しません。")
510
+ logger.warning(f"{logger_prefix}タスクは存在しません。")
511
+ return False
512
+
513
+ logger.debug(f"{logger_prefix}phase='{task['phase']}', status='{task['status']}'")
514
+ if task["status"] == TaskStatus.WORKING.value:
515
+ logger.info(f"{logger_prefix}タスクは作業中のため、処理をスキップします。 :: status={task['status']}")
516
+ return False
517
+
518
+ if not self.include_complete_task and task["status"] == TaskStatus.COMPLETE.value:
519
+ logger.info(f"{logger_prefix}タスクは完了状態のため、処理をスキップします。完了状態のタスクを処理する場合は、 '--include_complete_task'を指定してください。 :: status={task['status']}")
511
520
  return False
512
521
 
513
- if task["status"] in [TaskStatus.WORKING.value, TaskStatus.COMPLETE.value]:
514
- logger.info(f"タスク'{task_id}'は作業中または受入完了状態のため、インポートをスキップします。 status={task['status']}")
522
+ if not self.include_on_hold_task and task["status"] == TaskStatus.ON_HOLD.value:
523
+ logger.info(
524
+ f"{logger_prefix}タスクは保留中状態のため、処理をスキップします。保留中状態のタスクにアノテーションをインポートする場合は、 "
525
+ f"'--include_on_hold_task'を指定してください。 :: status={task['status']}"
526
+ )
515
527
  return False
516
528
 
529
+ if not self.confirm_processing(f"task_id='{task_id}'のタスク(phase={task['phase']}, status={task['status']})にアノテーションをインポートしますか?"):
530
+ return False
531
+
532
+ logger.info(f"{logger_prefix}タスクにアノテーションをインポートします。")
533
+
517
534
  old_account_id: str | None = None
518
535
  changed_operator = False
519
- if self.is_force:
536
+ if self.change_operator_to_me:
520
537
  if not can_put_annotation(task, self.service.api.account_id):
521
- logger.debug(f"タスク'{task_id}' の担当者を自分自身に変更します。")
538
+ logger.debug(f"{logger_prefix}担当者を自分自身に変更します。")
522
539
  old_account_id = task["account_id"]
523
540
  task = self.service.wrapper.change_task_operator(
524
541
  self.project_id,
@@ -532,7 +549,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
532
549
  if not can_put_annotation(task, self.service.api.account_id):
533
550
  logger.debug(
534
551
  f"タスク'{task_id}'は、過去に誰かに割り当てられたタスクで、現在の担当者が自分自身でないため、アノテーションのインポートをスキップします。"
535
- f"担当者を自分自身に変更してアノテーションを登録する場合は `--force` を指定してください。"
552
+ f"担当者を自分自身に変更してアノテーションを登録する場合は `--change_operator_to_me` を指定してください。"
536
553
  )
537
554
  return False
538
555
 
@@ -678,7 +695,9 @@ class ImportAnnotation(CommandLine):
678
695
  all_yes=self.all_yes,
679
696
  is_merge=args.merge,
680
697
  is_overwrite=args.overwrite,
681
- is_force=args.force,
698
+ change_operator_to_me=args.change_operator_to_me,
699
+ include_complete_task=args.include_complete_task,
700
+ include_on_hold_task=args.include_on_hold_task,
682
701
  converter=converter,
683
702
  )
684
703
 
@@ -700,7 +719,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
700
719
  "--annotation",
701
720
  type=Path,
702
721
  required=True,
703
- help="Simpleアノテーションと同じフォルダ構成のzipファイル or ディレクトリのパスを指定してください。タスクの状態が作業中/完了の場合はインポートしません。",
722
+ help="Simpleアノテーションと同じフォルダ構成のzipファイル or ディレクトリのパスを指定してください。",
704
723
  )
705
724
 
706
725
  argument_parser.add_task_id(required=False)
@@ -722,9 +741,23 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
722
741
  )
723
742
 
724
743
  parser.add_argument(
725
- "--force",
744
+ "--change_operator_to_me",
726
745
  action="store_true",
727
- help="過去に割り当てられていて現在の担当者が自分自身でない場合、タスクの担当者を自分自身に変更してからアノテーションをインポートします。",
746
+ help="タスクの担当者を自分自身に変更しないとアノテーションをインポートできない場合(担当者が自分自身でない AND 担当者が割れ当てられたことがあるタスク)は、タスクの担当者を自分自身に変更します。アノテーションをインポートが完了したら、担当者を元に戻します。" # noqa: E501
747
+ "未指定の場合は、そのようなタスクのアノテーションインポートはスキップされます。",
748
+ )
749
+
750
+ parser.add_argument(
751
+ "--include_complete_task",
752
+ action="store_true",
753
+ help="完了状態のタスクに対してもアノテーションをインポートします。未指定の場合は、完了状態のタスクはスキップされます。",
754
+ )
755
+
756
+ parser.add_argument(
757
+ "--include_on_hold_task",
758
+ action="store_true",
759
+ help="保留中状態のタスクに対してもアノテーションをインポートします。ただし、アノテーションインポート後は保留中状態でなくなる可能性があります。"
760
+ "未指定の場合は、保留中状態のタスクはスキップされます。",
728
761
  )
729
762
 
730
763
  parser.add_argument(
@@ -749,7 +782,12 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
749
782
  def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
750
783
  subcommand_name = "import"
751
784
  subcommand_help = "アノテーションをインポートします。"
752
- description = "アノテーションをインポートします。アノテーションのフォーマットは、Simpleアノテーションと同じフォルダ構成のzipファイルまたはディレクトリです。ただし、作業中/完了状態のタスクはインポートできません。" # noqa: E501
785
+ description = (
786
+ "アノテーションをインポートします。アノテーションのフォーマットは、Simpleアノテーションと同じフォルダ構成のzipファイルまたはディレクトリです。"
787
+ "ただし、作業中状態のタスクはインポートできません。"
788
+ "``--include_complete_task`` を指定すれば、完了状態のタスクにもインポートできます。"
789
+ "``--include_on_hold_task`` を指定すれば、保留中状態のタスクにもインポートできます。"
790
+ )
753
791
  epilog = "オーナロールを持つユーザで実行してください。"
754
792
 
755
793
  parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
@@ -259,7 +259,7 @@ class PutCommentMain(CommandLineWithConfirm):
259
259
  task_id: str,
260
260
  comments_for_task: AddedCommentsForTask,
261
261
  task_index: int | None = None,
262
- ) -> int:
262
+ ) -> tuple[int, int]:
263
263
  """
264
264
  タスクにコメントを付与します。
265
265
 
@@ -269,28 +269,29 @@ class PutCommentMain(CommandLineWithConfirm):
269
269
  task_index: タスクの連番
270
270
 
271
271
  Returns:
272
- コメントを付与した入力データの個数
272
+ (コメントを付与した入力データの個数, 付与したコメントの個数)のタプル
273
273
  """
274
274
  logging_prefix = f"{task_index + 1} 件目" if task_index is not None else ""
275
275
 
276
276
  task = self.service.wrapper.get_task_or_none(self.project_id, task_id)
277
277
  if task is None:
278
278
  logger.warning(f"{logging_prefix} :: task_id='{task_id}' のタスクは存在しないので、スキップします。")
279
- return 0
279
+ return (0, 0)
280
280
 
281
281
  logger.debug(f"{logging_prefix} : task_id='{task['task_id']}', status='{task['status']}', phase='{task['phase']}'")
282
282
 
283
283
  if not self._can_add_comment(
284
284
  task=task,
285
285
  ):
286
- return 0
286
+ return (0, 0)
287
287
 
288
288
  if not self.confirm_processing(f"task_id='{task_id}' のタスクに{self.comment_type_name}を付与しますか?"):
289
- return 0
289
+ return (0, 0)
290
290
 
291
291
  # コメントを付与するには作業中状態にする必要がある
292
292
  changed_task = self.change_to_working_status(self.project_id, task)
293
- added_comments_count = 0
293
+ added_input_data_count = 0
294
+ added_comment_count = 0
294
295
  for input_data_id, comments in comments_for_task.items():
295
296
  if input_data_id not in task["input_data_id_list"]:
296
297
  logger.warning(f"{logging_prefix} :: task_id='{task_id}'のタスクに input_data_id='{input_data_id}'の入力データは存在しません。")
@@ -300,7 +301,8 @@ class PutCommentMain(CommandLineWithConfirm):
300
301
  if len(comments) > 0:
301
302
  request_body = self._create_request_body(task=changed_task, input_data_id=input_data_id, comments=comments)
302
303
  self.service.api.batch_update_comments(self.project_id, task_id, input_data_id, request_body=request_body)
303
- added_comments_count += 1
304
+ added_input_data_count += 1
305
+ added_comment_count += len(comments)
304
306
  logger.debug(f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(comments)}件のコメントを付与しました。")
305
307
  except Exception: # pylint: disable=broad-except
306
308
  logger.warning(
@@ -314,12 +316,12 @@ class PutCommentMain(CommandLineWithConfirm):
314
316
  self.service.wrapper.change_task_operator(self.project_id, task_id, task["account_id"])
315
317
  logger.debug(f"{logging_prefix} :: task_id='{task_id}' :: 担当者を元のユーザ( account_id='{task['account_id']}')に戻しました。")
316
318
 
317
- return added_comments_count
319
+ return (added_input_data_count, added_comment_count)
318
320
 
319
321
  def add_comments_for_task_wrapper(
320
322
  self,
321
323
  tpl: tuple[int, tuple[str, AddedCommentsForTask]],
322
- ) -> int:
324
+ ) -> tuple[int, int]:
323
325
  task_index, (task_id, comments_for_task) = tpl
324
326
  return self.add_comments_for_task(task_id=task_id, comments_for_task=comments_for_task, task_index=task_index)
325
327
 
@@ -328,30 +330,45 @@ class PutCommentMain(CommandLineWithConfirm):
328
330
  comments_for_task_list: AddedComments,
329
331
  parallelism: int | None = None,
330
332
  ) -> None:
331
- comments_count = sum(len(e) for e in comments_for_task_list.values())
332
- logger.info(f"{self.comment_type_name}を付与するタスク数: {len(comments_for_task_list)}, {self.comment_type_name}を付与する入力データ数: {comments_count}")
333
+ tasks_count = len(comments_for_task_list)
334
+ input_data_count = sum(len(e) for e in comments_for_task_list.values())
335
+ total_comment_count = sum(len(comments) for comments_for_task in comments_for_task_list.values() for comments in comments_for_task.values())
336
+ logger.info(
337
+ f"{self.comment_type_name}を付与するタスク数: {tasks_count}, {self.comment_type_name}を付与する入力データ数: {input_data_count}, {self.comment_type_name}の総数: {total_comment_count}"
338
+ )
333
339
 
334
340
  if parallelism is not None:
335
341
  with multiprocessing.Pool(parallelism) as pool:
336
- result_bool_list = pool.map(self.add_comments_for_task_wrapper, enumerate(comments_for_task_list.items()))
337
- added_comments_count = sum(e for e in result_bool_list)
342
+ result_list = pool.map(self.add_comments_for_task_wrapper, enumerate(comments_for_task_list.items()))
343
+ added_input_data_count = sum(e[0] for e in result_list)
344
+ added_comment_count = sum(e[1] for e in result_list)
345
+ succeeded_tasks_count = sum(1 for e in result_list if e[0] > 0)
338
346
 
339
347
  else:
340
348
  # 逐次処理
341
- added_comments_count = 0
349
+ added_input_data_count = 0
350
+ added_comment_count = 0
351
+ succeeded_tasks_count = 0
342
352
  for task_index, (task_id, comments_for_task) in enumerate(comments_for_task_list.items()):
343
353
  try:
344
- result = self.add_comments_for_task(
354
+ result_input_data, result_comment = self.add_comments_for_task(
345
355
  task_id=task_id,
346
356
  comments_for_task=comments_for_task,
347
357
  task_index=task_index,
348
358
  )
349
- added_comments_count += result
359
+ added_input_data_count += result_input_data
360
+ added_comment_count += result_comment
361
+ if result_input_data > 0:
362
+ succeeded_tasks_count += 1
350
363
  except Exception: # pylint: disable=broad-except
351
364
  logger.warning(f"task_id='{task_id}' :: コメントの付与に失敗しました。", exc_info=True)
352
365
  continue
353
366
 
354
- logger.info(f"{added_comments_count} / {comments_count} 件の入力データに{self.comment_type_name}を付与しました。")
367
+ logger.info(
368
+ f"{succeeded_tasks_count} / {tasks_count} 件のタスク, "
369
+ f"{added_input_data_count} / {input_data_count} 件の入力データ, "
370
+ f"{added_comment_count} / {total_comment_count} 件の{self.comment_type_name}を付与しました。"
371
+ )
355
372
 
356
373
 
357
374
  def convert_cli_comments(dict_comments: dict[str, Any], *, comment_type: CommentType) -> AddedComments:
@@ -182,7 +182,7 @@ class DrawingAnnotationForOneImage:
182
182
  image.save(output_file)
183
183
 
184
184
  elif image_size is not None:
185
- image = Image.new("RGBA", image_size, color="black")
185
+ image = Image.new("RGBA", image_size, color="black") # type: ignore[assignment]
186
186
  draw = ImageDraw.Draw(image)
187
187
  self._draw_annotations(draw, parser)
188
188
  output_file.parent.mkdir(parents=True, exist_ok=True)
@@ -31,7 +31,7 @@ class CopyInstruction(CommandLine):
31
31
  @staticmethod
32
32
  def get_instruction_image_id_from_url(url: str) -> str:
33
33
  # URL Queryを除いたURLを取得する
34
- url_without_query = url.split("?")[0]
34
+ url_without_query = url.split("?", maxsplit=1)[0]
35
35
  return url_without_query.split("/")[-1]
36
36
 
37
37
  @staticmethod
@@ -34,7 +34,7 @@ def get_bin_edges(min_value: float, max_value: float, bin_width: float) -> numpy
34
34
  # stop引数に、`bin_width*2`を指定している理由:
35
35
  # 引数が小数のときは`len(bin_edges)``期待通りにならないときがあるので、ビンの数を少し増やしている
36
36
  # https://qiita.com/yuji38kwmt/items/ff00f3cb9083567d083f
37
- bin_edges = numpy.arange(start=min_value, stop=max_value + bin_width * 2, step=bin_width)
37
+ bin_edges = numpy.arange(start=min_value, stop=max_value + bin_width * 2, step=bin_width) # type: ignore[call-overload]
38
38
  return bin_edges
39
39
 
40
40
 
@@ -88,7 +88,7 @@ class ChangeOperatorMain:
88
88
 
89
89
  if task.status == TaskStatus.ON_HOLD and not self.include_on_hold:
90
90
  logger.warning(
91
- f"{logging_prefix} :: task_id='{task_id}' :: タスクが保留中状態なので、担当者を変更できません。保留中状態のタスクの担当者も変更する場合は、'--include_on_hold'を指定してください。"
91
+ f"{logging_prefix} :: task_id='{task_id}' :: タスクが保留中状態なので、担当者を変更できません。保留中状態のタスクの担当者も変更する場合は、'--include_on_hold_task'を指定してください。"
92
92
  )
93
93
  return False
94
94
 
@@ -222,7 +222,7 @@ class ChangeOperator(CommandLine):
222
222
  project_id = args.project_id
223
223
  super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.ACCEPTER])
224
224
 
225
- main_obj = ChangeOperatorMain(self.service, all_yes=self.all_yes, include_on_hold=args.include_on_hold)
225
+ main_obj = ChangeOperatorMain(self.service, all_yes=self.all_yes, include_on_hold=args.include_on_hold_task)
226
226
  main_obj.change_operator(
227
227
  project_id,
228
228
  task_id_list=task_id_list,
@@ -253,7 +253,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
253
253
  argument_parser.add_task_query()
254
254
 
255
255
  parser.add_argument(
256
- "--include_on_hold",
256
+ "--include_on_hold_task",
257
257
  action="store_true",
258
258
  help="指定した場合、保留中のタスクの担当者も変更します。指定しない場合、保留中のタスクはスキップされます。",
259
259
  )
@@ -179,9 +179,11 @@ class CompleteTasksMain(CommandLineWithConfirm):
179
179
  # 教師付フェーズになった日時より、後に付与された返信コメントを取得する
180
180
  answered_comment = first_true(
181
181
  comment_list,
182
- pred=lambda e: e["comment_node"]["_type"] == "Reply"
183
- and e["comment_node"]["root_comment_id"] == parent_comment_id
184
- and dateutil.parser.parse(e["created_datetime"]) >= dateutil.parser.parse(task_started_datetime),
182
+ pred=lambda e: (
183
+ e["comment_node"]["_type"] == "Reply"
184
+ and e["comment_node"]["root_comment_id"] == parent_comment_id
185
+ and dateutil.parser.parse(e["created_datetime"]) >= dateutil.parser.parse(task_started_datetime)
186
+ ),
185
187
  )
186
188
  return answered_comment is not None
187
189
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annofabcli
3
- Version: 1.114.6
3
+ Version: 1.114.7
4
4
  Summary: Utility Command Line Interface for AnnoFab
5
5
  Author: Kurusugawa Computer Inc.
6
6
  License: MIT
@@ -3,15 +3,15 @@ annofabcli/__main__.py,sha256=w3g-2v0rcR8yZwDglX2ZK3d2fZBY_RQ0VNZHGPzoAr8,5522
3
3
  annofabcli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  annofabcli/annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  annofabcli/annotation/annotation_query.py,sha256=iGpbg5L6sRona3ADsBfT4lyXmyEiD5i3ujYvPhiiTbM,15681
6
- annofabcli/annotation/change_annotation_attributes.py,sha256=zKuWsDfgwrOiUDOMydCQlNPg_P88o2f6DlLwY9hWjDU,17863
6
+ annofabcli/annotation/change_annotation_attributes.py,sha256=G2g5R5Vb8udhxJlN-qO8-3E801WVGcpreOG-Qd12SbA,17907
7
7
  annofabcli/annotation/change_annotation_attributes_per_annotation.py,sha256=nbhosWabJl1t4exWantLJ-Z5lKL6HoeBdd6WOC7VGms,15491
8
8
  annofabcli/annotation/change_annotation_properties.py,sha256=d2bs_s0jTq3zeDYAE5LlbYdIeIaMe7UJ9xO6x0WuaJo,18274
9
9
  annofabcli/annotation/copy_annotation.py,sha256=8ayliCb3XCsswEW_E_blmSR1aLt00Swfq9_hejp0v5k,18413
10
- annofabcli/annotation/create_classification_annotation.py,sha256=OVMFyL-CrZH444V1EkaTt068cEG834LIfdMDAspn3PA,18577
11
- annofabcli/annotation/delete_annotation.py,sha256=NCA8e9FQsqaCVcQmteRQcLoNrzAcoRdpFoiWjZhswSY,25124
10
+ annofabcli/annotation/create_classification_annotation.py,sha256=nryBN9aTiVpjCTHBrjjPnmwii3j17U_02CxOquHdV1I,19464
11
+ annofabcli/annotation/delete_annotation.py,sha256=1ZPX49YKnK3nLKk9d_Xc59xWCYCPYGj56rtVHxwbSds,25260
12
12
  annofabcli/annotation/download_annotation_zip.py,sha256=o2p2TfK8-jz15TWrkWxhX9eAnoATHjxkTy44q-BbI14,3265
13
13
  annofabcli/annotation/dump_annotation.py,sha256=AWEAR4Q38Oo9yA6plo51F7_ZSxwfuaxQeFDZ66gsiIA,10844
14
- annofabcli/annotation/import_annotation.py,sha256=cbTLD2BZjZ2xCDvRriCHkVz2Twdnbm0r2igtLHw9tMI,35849
14
+ annofabcli/annotation/import_annotation.py,sha256=rRLTILcy1e4wN2Acue9scIaLTg6pQ44z2H_CkyliP9A,38393
15
15
  annofabcli/annotation/list_annotation.py,sha256=NQ-G5Ip6Mk2tWLI0c5mR982MhgJNrvCnqskDjrelvJc,10675
16
16
  annofabcli/annotation/list_annotation_count.py,sha256=Zez7jkWOhhp3FwfKRM4VhyL26qdNuKYfQwb0Qm4tElk,6442
17
17
  annofabcli/annotation/merge_segmentation.py,sha256=BeLzOjDy6iOgp2z9GbFiUE_KRjv9UKFyczWl4_6aYDs,18096
@@ -48,7 +48,7 @@ annofabcli/comment/delete_comment.py,sha256=DT6s07IyCYXlSqEZop1CXloaORJVgcBvwR4B
48
48
  annofabcli/comment/download_comment_json.py,sha256=wB8U31G-7pkvN05N2erZch91bqx6oumGr9ziIXIyLuY,2392
49
49
  annofabcli/comment/list_all_comment.py,sha256=fFBBY6-9Tx51X7lVT6qblTfkQ7jWmZiqtNqkt-RB14c,8233
50
50
  annofabcli/comment/list_comment.py,sha256=SMTGCALxnddDxy-xnqOjBc3ijfb9QIZ-G4ZMw2BwyxA,6137
51
- annofabcli/comment/put_comment.py,sha256=lpisYmcSYRqygdpZFB_XmXFWjk82EaCNXYhGfQE3MYA,18646
51
+ annofabcli/comment/put_comment.py,sha256=AdIaKcrfOmyyJMXgCN4TZk04TmMtBX-rV1mzs6RMC5Q,19700
52
52
  annofabcli/comment/put_comment_simply.py,sha256=2nWnv0ZzeemJ99h0c2mGqA1ydsfuim6CGJkY_Y-uPyg,8298
53
53
  annofabcli/comment/put_inspection_comment.py,sha256=XJt1fS-lWIH54PNGKzU8FLYUITtpEk6HCn7Wp47esgk,3944
54
54
  annofabcli/comment/put_inspection_comment_simply.py,sha256=sgDKoBXlAPcTaf4GpU4qtfB3VRADNn0cdr0Ritg54Bo,6749
@@ -79,7 +79,7 @@ annofabcli/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
79
79
  annofabcli/experimental/list_out_of_range_annotation_for_movie.py,sha256=Ok3b1CNuTzzalC9u0wHSFwTO7Qvi4PZDx7TZu-TB84Y,8485
80
80
  annofabcli/experimental/subcommand_experimental.py,sha256=aKaimneErwv5epJaBpmAviuRz8O4I8eNSBvhr0JwSPI,862
81
81
  annofabcli/filesystem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
- annofabcli/filesystem/draw_annotation.py,sha256=L3fmOulT0PDm8rzrSoLoW1gcz8MKXgPshzWhavJdPiU,19197
82
+ annofabcli/filesystem/draw_annotation.py,sha256=HG6YCEjJO9Brk0g7sqkv40DcktjxrnzeiZnkmcIIGjY,19225
83
83
  annofabcli/filesystem/filter_annotation.py,sha256=sV6d_YwHGFIGet0RldQ9GfNyUTfMGU_vYE-8cn3EapA,11095
84
84
  annofabcli/filesystem/mask_user_info.py,sha256=j29AmAIz06efsUBkjxbbCNKCaEb_PPFqW_n9T3yjIVM,13563
85
85
  annofabcli/filesystem/merge_annotation.py,sha256=JXjcwhcc_SgNW04rLNhqZCrmKoJeaeDnXtTPIJAM5gU,10502
@@ -99,7 +99,7 @@ annofabcli/input_data/update_input_data.py,sha256=dSvPcjgvdtWSFPA62Vi9a29-_v7x8X
99
99
  annofabcli/input_data/update_metadata_of_input_data.py,sha256=FbDVPoX0RwA3vX3a2pi_cpN5L6usrAXL6ZFEevvbTU8,11405
100
100
  annofabcli/input_data/utils.py,sha256=F3mYbWmRwXfEtiIo9dvSysfuZUdyQzJmmq94eM-C5IY,723
101
101
  annofabcli/instruction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
- annofabcli/instruction/copy_instruction.py,sha256=vkpdReK9CfIx5ll0l6MgnVRLop9UKcWX4GjXZQscUQs,6675
102
+ annofabcli/instruction/copy_instruction.py,sha256=iA-PFZE-s79Hc-wWljvlI_lwR5PjH13NKiDluEJzHfY,6687
103
103
  annofabcli/instruction/download_instruction.py,sha256=m04SgChdwGSUZ_kNpZkLvPZFeYEs654wuzgZB_6Fo8g,8288
104
104
  annofabcli/instruction/list_instruction_history.py,sha256=PVcCgb1Ax8pfv_nKG-B38Xc5GjcZS7jT4-ItGCk57vg,2073
105
105
  annofabcli/instruction/subcommand_instruction.py,sha256=RgMcjpIY6gq4gkqvV_55G0Wa7lKJNZKQAiX-WMSP98o,1120
@@ -149,7 +149,7 @@ annofabcli/stat_visualization/summarize_whole_performance_csv.py,sha256=tUt4O1sV
149
149
  annofabcli/stat_visualization/write_graph.py,sha256=DWRgJuflHaNfdXewdD_p7BQYSjqNZMNErpO6QWCYkFM,9067
150
150
  annofabcli/stat_visualization/write_performance_rating_csv.py,sha256=ElRk-9ZPkVK0rqj1UCuk9yLWsOy7TvIAGzRSk7ZsiVQ,30675
151
151
  annofabcli/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
- annofabcli/statistics/histogram.py,sha256=hvGszTqC1TYmf0mXoR53MslHdH5wOQBVD_rX6Ygu-dE,3265
152
+ annofabcli/statistics/histogram.py,sha256=lsLyLSmPdFERAbtuZS_UTGfOGliy9h_p4s9YQYWrnps,3296
153
153
  annofabcli/statistics/linegraph.py,sha256=D2qMBykZAfGN8lf5EaOsbpEqH4mzl5E5ga9CI_XH9go,12391
154
154
  annofabcli/statistics/list_annotation_area.py,sha256=GiEwrE-DuOUavAVDSb0l5qcgGkn3gGn5SAtpa8Mwhak,13234
155
155
  annofabcli/statistics/list_annotation_attribute.py,sha256=AH4OeqUhwCh19Kv93bFLZRV6o6oKwFklRdEw2FrOaVk,12669
@@ -197,10 +197,10 @@ annofabcli/supplementary/put_supplementary_data.py,sha256=VGieXgpGgF7qBZSULq7XP8
197
197
  annofabcli/supplementary/subcommand_supplementary.py,sha256=UnnHsxYDmHMGURkWAb-0pBeTYkSZ0YPKOuZW4XSE-RI,1030
198
198
  annofabcli/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
199
  annofabcli/task/cancel_acceptance.py,sha256=AgCvl7UsbaLO8A_tMGEkFjU10WT9siOQXME4apGAbTk,13778
200
- annofabcli/task/change_operator.py,sha256=IETB7Pckmx75o6xtDrVqDBvxZqJBZP92xQmn-QzcJ5w,11717
200
+ annofabcli/task/change_operator.py,sha256=A8ucvWgzyZOfrF2-yh6cPuDkjJBqOM3BRp6NATGhqhw,11732
201
201
  annofabcli/task/change_status_to_break.py,sha256=hBAPcM6TiXEODYAo8blJNYXuILtKgWlfexcKImdA9rg,8554
202
202
  annofabcli/task/change_status_to_on_hold.py,sha256=5mP6CJHlJxfQO4i-imuR-04lK_qAueFepbSaUgvGTsk,12905
203
- annofabcli/task/complete_tasks.py,sha256=CyQjMWlciHI3IQPm1uez1yFCzZ8IpgktC7grjsYrioI,25093
203
+ annofabcli/task/complete_tasks.py,sha256=b0TDz8_A0SFBnZVM0GEdUDOGfzMlcvMa2Fpj-eYArLI,25141
204
204
  annofabcli/task/copy_tasks.py,sha256=cOWtQAXqB1YBKatF2wGMMx9tqQ8RWL-AexrLBecqzqI,8680
205
205
  annofabcli/task/delete_metadata_key_of_task.py,sha256=EywRT5LktrXzYM5H5I3EzbzbwdNOfaTFT0IOCPEmifQ,7962
206
206
  annofabcli/task/delete_tasks.py,sha256=221gB5gBNlZd4EOF0tkdIsbbsagx6fmPqavpnJwm2GI,13081
@@ -227,8 +227,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=lWyOoS2
227
227
  annofabcli/task_history_event/list_all_task_history_event.py,sha256=51p_c_sWflJ7Jsrkay-OgF6c8wCOHpnqQeGIHVKzi6E,8001
228
228
  annofabcli/task_history_event/list_worktime.py,sha256=0X1irND_ElxC_XpHiPac6cjwIz9DhgAccfyDA3M7kh0,15481
229
229
  annofabcli/task_history_event/subcommand_task_history_event.py,sha256=dFllzpm8plnnwADwTV74h-R2LOA7rZW-xd2YnSkwTHo,1229
230
- annofabcli-1.114.6.dist-info/METADATA,sha256=d6zPaNaifi8TGvORYdMhH0pp8Ku6Y5Q2pj2OVB6zJYg,4946
231
- annofabcli-1.114.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
232
- annofabcli-1.114.6.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
233
- annofabcli-1.114.6.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
234
- annofabcli-1.114.6.dist-info/RECORD,,
230
+ annofabcli-1.114.7.dist-info/METADATA,sha256=r6dmQ7yRVTxCFlRR4L6gQBV5NrNGQw5P3m88YLqQWRA,4946
231
+ annofabcli-1.114.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
232
+ annofabcli-1.114.7.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
233
+ annofabcli-1.114.7.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
234
+ annofabcli-1.114.7.dist-info/RECORD,,