annofabcli 1.95.0__tar.gz → 1.96.1__tar.gz

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 (206) hide show
  1. {annofabcli-1.95.0 → annofabcli-1.96.1}/PKG-INFO +1 -1
  2. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__version__.py +1 -1
  3. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/export_annotation_specs.py +10 -5
  4. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/put_input_data.py +28 -29
  5. annofabcli-1.96.1/annofabcli/statistics/list_annotation_attribute.py +310 -0
  6. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/subcommand_statistics.py +2 -0
  7. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/delete_supplementary_data.py +22 -24
  8. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/put_supplementary_data.py +52 -29
  9. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_tasks_added_task_history.py +2 -1
  10. {annofabcli-1.95.0 → annofabcli-1.96.1}/pyproject.toml +5 -1
  11. {annofabcli-1.95.0 → annofabcli-1.96.1}/LICENSE +0 -0
  12. {annofabcli-1.95.0 → annofabcli-1.96.1}/README.md +0 -0
  13. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__init__.py +0 -0
  14. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__main__.py +0 -0
  15. {annofabcli-1.95.0/annofabcli/task_history_event → annofabcli-1.96.1/annofabcli/annotation}/__init__.py +0 -0
  16. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/annotation_query.py +0 -0
  17. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/change_annotation_attributes.py +0 -0
  18. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/change_annotation_properties.py +0 -0
  19. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/copy_annotation.py +0 -0
  20. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/delete_annotation.py +0 -0
  21. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/download_annotation_zip.py +0 -0
  22. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/dump_annotation.py +0 -0
  23. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/import_annotation.py +0 -0
  24. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/list_annotation.py +0 -0
  25. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/list_annotation_count.py +0 -0
  26. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/restore_annotation.py +0 -0
  27. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/subcommand_annotation.py +0 -0
  28. {annofabcli-1.95.0/annofabcli/task_history → annofabcli-1.96.1/annofabcli/annotation_specs}/__init__.py +0 -0
  29. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +0 -0
  30. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +0 -0
  31. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +0 -0
  32. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_attribute.py +0 -0
  33. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_choice.py +0 -0
  34. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_history.py +0 -0
  35. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_label.py +0 -0
  36. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_attribute_restriction.py +0 -0
  37. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_label_color.py +0 -0
  38. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/put_label_color.py +0 -0
  39. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/subcommand_annotation_specs.py +0 -0
  40. {annofabcli-1.95.0/annofabcli/task → annofabcli-1.96.1/annofabcli/comment}/__init__.py +0 -0
  41. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/delete_comment.py +0 -0
  42. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/download_comment_json.py +0 -0
  43. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/list_all_comment.py +0 -0
  44. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/list_comment.py +0 -0
  45. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_comment.py +0 -0
  46. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_comment_simply.py +0 -0
  47. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_inspection_comment.py +0 -0
  48. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_inspection_comment_simply.py +0 -0
  49. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_onhold_comment.py +0 -0
  50. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_onhold_comment_simply.py +0 -0
  51. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/subcommand_comment.py +0 -0
  52. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/utils.py +0 -0
  53. {annofabcli-1.95.0/annofabcli/supplementary → annofabcli-1.96.1/annofabcli/common}/__init__.py +0 -0
  54. {annofabcli-1.95.0/annofabcli/statistics/visualization/dataframe → annofabcli-1.96.1/annofabcli/common/annofab}/__init__.py +0 -0
  55. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/annofab/project.py +0 -0
  56. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/bokeh.py +0 -0
  57. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/cli.py +0 -0
  58. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/dataclasses.py +0 -0
  59. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/download.py +0 -0
  60. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/enums.py +0 -0
  61. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/exceptions.py +0 -0
  62. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/facade.py +0 -0
  63. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/image.py +0 -0
  64. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/pandas.py +0 -0
  65. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/type_util.py +0 -0
  66. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/typing.py +0 -0
  67. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/utils.py +0 -0
  68. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/visualize.py +0 -0
  69. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/data/logging.yaml +0 -0
  70. {annofabcli-1.95.0/annofabcli/statistics/visualization → annofabcli-1.96.1/annofabcli/experimental}/__init__.py +0 -0
  71. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/experimental/list_out_of_range_annotation_for_movie.py +0 -0
  72. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/experimental/subcommand_experimental.py +0 -0
  73. {annofabcli-1.95.0/annofabcli/statistics → annofabcli-1.96.1/annofabcli/filesystem}/__init__.py +0 -0
  74. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/draw_annotation.py +0 -0
  75. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/filter_annotation.py +0 -0
  76. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/mask_user_info.py +0 -0
  77. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/merge_annotation.py +0 -0
  78. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/subcommand_filesystem.py +0 -0
  79. {annofabcli-1.95.0/annofabcli/stat_visualization → annofabcli-1.96.1/annofabcli/input_data}/__init__.py +0 -0
  80. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/change_input_data_name.py +0 -0
  81. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/copy_input_data.py +0 -0
  82. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/delete_input_data.py +0 -0
  83. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/delete_metadata_key_of_input_data.py +0 -0
  84. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/download_input_data_json.py +0 -0
  85. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_all_input_data.py +0 -0
  86. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_all_input_data_merged_task.py +0 -0
  87. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_input_data.py +0 -0
  88. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/put_input_data_with_zip.py +0 -0
  89. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/subcommand_input_data.py +0 -0
  90. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/update_metadata_of_input_data.py +0 -0
  91. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/utils.py +0 -0
  92. {annofabcli-1.95.0/annofabcli/project_member → annofabcli-1.96.1/annofabcli/instruction}/__init__.py +0 -0
  93. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/copy_instruction.py +0 -0
  94. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/download_instruction.py +0 -0
  95. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/list_instruction_history.py +0 -0
  96. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/subcommand_instruction.py +0 -0
  97. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/upload_instruction.py +0 -0
  98. {annofabcli-1.95.0/annofabcli/project → annofabcli-1.96.1/annofabcli/job}/__init__.py +0 -0
  99. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/delete_job.py +0 -0
  100. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/list_job.py +0 -0
  101. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/list_last_job.py +0 -0
  102. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/subcommand_job.py +0 -0
  103. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/wait_job.py +0 -0
  104. {annofabcli-1.95.0/annofabcli/organization_member → annofabcli-1.96.1/annofabcli/my_account}/__init__.py +0 -0
  105. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/my_account/get_my_account.py +0 -0
  106. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/my_account/subcommand_my_account.py +0 -0
  107. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/__init__.py +0 -0
  108. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/list_organization.py +0 -0
  109. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/subcommand_organization.py +0 -0
  110. {annofabcli-1.95.0/annofabcli/my_account → annofabcli-1.96.1/annofabcli/organization_member}/__init__.py +0 -0
  111. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/change_organization_member.py +0 -0
  112. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/delete_organization_member.py +0 -0
  113. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/invite_organization_member.py +0 -0
  114. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/list_organization_member.py +0 -0
  115. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/subcommand_organization_member.py +0 -0
  116. {annofabcli-1.95.0/annofabcli/job → annofabcli-1.96.1/annofabcli/project}/__init__.py +0 -0
  117. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/change_project_status.py +0 -0
  118. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/copy_project.py +0 -0
  119. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/diff_projects.py +0 -0
  120. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/list_project.py +0 -0
  121. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/put_project.py +0 -0
  122. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/subcommand_project.py +0 -0
  123. {annofabcli-1.95.0/annofabcli/instruction → annofabcli-1.96.1/annofabcli/project_member}/__init__.py +0 -0
  124. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/change_project_members.py +0 -0
  125. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/copy_project_members.py +0 -0
  126. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/drop_project_members.py +0 -0
  127. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/invite_project_members.py +0 -0
  128. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/list_users.py +0 -0
  129. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/put_project_members.py +0 -0
  130. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/subcommand_project_member.py +0 -0
  131. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/py.typed +0 -0
  132. {annofabcli-1.95.0/annofabcli/input_data → annofabcli-1.96.1/annofabcli/stat_visualization}/__init__.py +0 -0
  133. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/mask_visualization_dir.py +0 -0
  134. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/merge_visualization_dir.py +0 -0
  135. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/subcommand_stat_visualization.py +0 -0
  136. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/summarize_whole_performance_csv.py +0 -0
  137. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/write_graph.py +0 -0
  138. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/write_performance_rating_csv.py +0 -0
  139. {annofabcli-1.95.0/annofabcli/filesystem → annofabcli-1.96.1/annofabcli/statistics}/__init__.py +0 -0
  140. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/histogram.py +0 -0
  141. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/linegraph.py +0 -0
  142. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_annotation_count.py +0 -0
  143. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_annotation_duration.py +0 -0
  144. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_video_duration.py +0 -0
  145. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_worktime.py +0 -0
  146. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/scatter.py +0 -0
  147. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count.py +0 -0
  148. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count_by_task_id_group.py +0 -0
  149. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count_by_user.py +0 -0
  150. {annofabcli-1.95.0/annofabcli/experimental → annofabcli-1.96.1/annofabcli/statistics/visualization}/__init__.py +0 -0
  151. {annofabcli-1.95.0/annofabcli/common/annofab → annofabcli-1.96.1/annofabcli/statistics/visualization/dataframe}/__init__.py +0 -0
  152. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/actual_worktime.py +0 -0
  153. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/annotation_count.py +0 -0
  154. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +0 -0
  155. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/custom_production_volume.py +0 -0
  156. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/input_data_count.py +0 -0
  157. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/inspection_comment_count.py +0 -0
  158. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/productivity_per_date.py +0 -0
  159. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/project_performance.py +0 -0
  160. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task.py +0 -0
  161. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task_history.py +0 -0
  162. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +0 -0
  163. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/user.py +0 -0
  164. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/user_performance.py +0 -0
  165. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/whole_performance.py +0 -0
  166. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +0 -0
  167. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/worktime_per_date.py +0 -0
  168. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/filtering_query.py +0 -0
  169. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/model.py +0 -0
  170. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/project_dir.py +0 -0
  171. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/visualization_source_files.py +0 -0
  172. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_annotation_count.py +0 -0
  173. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_annotation_duration.py +0 -0
  174. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_statistics.py +0 -0
  175. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_video_duration.py +0 -0
  176. {annofabcli-1.95.0/annofabcli/common → annofabcli-1.96.1/annofabcli/supplementary}/__init__.py +0 -0
  177. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/list_supplementary_data.py +0 -0
  178. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/subcommand_supplementary.py +0 -0
  179. {annofabcli-1.95.0/annofabcli/comment → annofabcli-1.96.1/annofabcli/task}/__init__.py +0 -0
  180. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/cancel_acceptance.py +0 -0
  181. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_operator.py +0 -0
  182. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_status_to_break.py +0 -0
  183. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_status_to_on_hold.py +0 -0
  184. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/complete_tasks.py +0 -0
  185. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/copy_tasks.py +0 -0
  186. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/delete_metadata_key_of_task.py +0 -0
  187. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/delete_tasks.py +0 -0
  188. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/download_task_json.py +0 -0
  189. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_all_tasks.py +0 -0
  190. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_all_tasks_added_task_history.py +0 -0
  191. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_tasks.py +0 -0
  192. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/put_tasks.py +0 -0
  193. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/put_tasks_by_count.py +0 -0
  194. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/reject_tasks.py +0 -0
  195. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/subcommand_task.py +0 -0
  196. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/update_metadata_of_task.py +0 -0
  197. {annofabcli-1.95.0/annofabcli/annotation_specs → annofabcli-1.96.1/annofabcli/task_history}/__init__.py +0 -0
  198. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/download_task_history_json.py +0 -0
  199. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/list_all_task_history.py +0 -0
  200. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/list_task_history.py +0 -0
  201. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/subcommand_task_history.py +0 -0
  202. {annofabcli-1.95.0/annofabcli/annotation → annofabcli-1.96.1/annofabcli/task_history_event}/__init__.py +0 -0
  203. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/download_task_history_event_json.py +0 -0
  204. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/list_all_task_history_event.py +0 -0
  205. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/list_worktime.py +0 -0
  206. {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/subcommand_task_history_event.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annofabcli
3
- Version: 1.95.0
3
+ Version: 1.96.1
4
4
  Summary: Utility Command Line Interface for AnnoFab
5
5
  Home-page: https://github.com/kurusugawa-computer/annofab-cli
6
6
  License: MIT
@@ -1 +1 @@
1
- __version__ = "1.95.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
1
+ __version__ = "1.96.1" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
@@ -20,15 +20,20 @@ from annofabcli.common.utils import print_according_to_format
20
20
  logger = logging.getLogger(__name__)
21
21
 
22
22
 
23
- class ListAttributeRestriction(CommandLine):
23
+ class ExportAnnotationSpecs(CommandLine):
24
24
  COMMON_MESSAGE = "annofabcli annotation_specs export: error:"
25
25
 
26
26
  def get_history_id_from_before_index(self, project_id: str, before: int) -> Optional[str]:
27
27
  histories, _ = self.service.api.get_annotation_specs_histories(project_id)
28
- if before + 1 > len(histories):
29
- logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
28
+ sorted_histories = sorted(histories, key=lambda x: x["updated_datetime"], reverse=True)
29
+
30
+ if before + 1 > len(sorted_histories):
31
+ logger.warning(
32
+ f"アノテーション仕様の履歴は{len(sorted_histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。"
33
+ )
30
34
  return None
31
- history = histories[-(before + 1)]
35
+
36
+ history = sorted_histories[before]
32
37
  return history["history_id"]
33
38
 
34
39
  def get_exported_annotation_specs(self, project_id: str, history_id: Optional[str]) -> dict[str, Any]:
@@ -107,7 +112,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
107
112
  def main(args: argparse.Namespace) -> None:
108
113
  service = build_annofabapi_resource_and_login(args)
109
114
  facade = AnnofabApiFacade(service)
110
- ListAttributeRestriction(service, facade, args).main()
115
+ ExportAnnotationSpecs(service, facade, args).main()
111
116
 
112
117
 
113
118
  def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
@@ -1,7 +1,7 @@
1
1
  import argparse
2
2
  import logging
3
+ import re
3
4
  import sys
4
- import uuid
5
5
  from dataclasses import dataclass
6
6
  from functools import partial
7
7
  from multiprocessing import Pool
@@ -25,14 +25,11 @@ from annofabcli.common.cli import (
25
25
  get_json_from_args,
26
26
  prompt_yesnoall,
27
27
  )
28
- from annofabcli.common.dataclasses import WaitOptions
29
28
  from annofabcli.common.facade import AnnofabApiFacade
30
29
  from annofabcli.common.utils import get_file_scheme_path
31
30
 
32
31
  logger = logging.getLogger(__name__)
33
32
 
34
- DEFAULT_WAIT_OPTIONS = WaitOptions(interval=60, max_tries=360)
35
-
36
33
 
37
34
  @dataclass
38
35
  class CsvInputData(DataClassJsonMixin):
@@ -43,7 +40,6 @@ class CsvInputData(DataClassJsonMixin):
43
40
  input_data_name: str
44
41
  input_data_path: str
45
42
  input_data_id: Optional[str] = None
46
- sign_required: Optional[bool] = None
47
43
 
48
44
 
49
45
  @dataclass
@@ -55,7 +51,14 @@ class InputDataForPut(DataClassJsonMixin):
55
51
  input_data_name: str
56
52
  input_data_path: str
57
53
  input_data_id: str
58
- sign_required: Optional[bool]
54
+
55
+
56
+ def convert_input_data_name_to_input_data_id(input_data_name: str) -> str:
57
+ """
58
+ 入力データ名から、入力データIDを生成します。
59
+ * IDに使えない文字以外は`__`に変換する。
60
+ """
61
+ return re.sub(r"[^a-zA-Z0-9_.-]", "__", input_data_name)
59
62
 
60
63
 
61
64
  def read_input_data_csv(csv_file: Path) -> pandas.DataFrame:
@@ -64,7 +67,6 @@ def read_input_data_csv(csv_file: Path) -> pandas.DataFrame:
64
67
  * input_data_name
65
68
  * input_data_path
66
69
  * input_data_id
67
- * sign_required
68
70
 
69
71
  Args:
70
72
  csv_file (Path): CSVファイルのパス
@@ -76,6 +78,12 @@ def read_input_data_csv(csv_file: Path) -> pandas.DataFrame:
76
78
  str(csv_file),
77
79
  sep=",",
78
80
  header=None,
81
+ # names引数に"sign_required"を指定している理由:
82
+ # v1.96.0以前では、`sign_required`列を読み込んでいた。したがって、4列のCSVを使っているユーザーは存在する
83
+ # CSVの列数が`names`引数で指定した列数より大きい場合、右側から列名が割り当てられる。
84
+ # (https://qiita.com/yuji38kwmt/items/ac46c3d0ccac109410ba)
85
+ # したがって、"sign_required"がないと4列のCSVは読み込めない。
86
+ # 4列のCSVもしばらくサポートするため、"sign_required"を指定している。
79
87
  names=("input_data_name", "input_data_path", "input_data_id", "sign_required"),
80
88
  # IDと名前は必ず文字列として読み込むようにする
81
89
  dtype={"input_data_id": str, "input_data_name": str},
@@ -126,8 +134,8 @@ class SubPutInputData:
126
134
 
127
135
  file_path = get_file_scheme_path(csv_input_data.input_data_path)
128
136
  if file_path is not None:
129
- request_body.update({"input_data_name": csv_input_data.input_data_name, "sign_required": csv_input_data.sign_required})
130
- logger.debug(f"'{file_path}'を入力データとして登録します。input_data_name={csv_input_data.input_data_name}")
137
+ request_body.update({"input_data_name": csv_input_data.input_data_name})
138
+ logger.debug(f"'{file_path}'を入力データとして登録します。input_data_name='{csv_input_data.input_data_name}'")
131
139
  self.service.wrapper.put_input_data_from_file(
132
140
  project_id, input_data_id=csv_input_data.input_data_id, file_path=file_path, request_body=request_body
133
141
  )
@@ -137,7 +145,6 @@ class SubPutInputData:
137
145
  {
138
146
  "input_data_name": csv_input_data.input_data_name,
139
147
  "input_data_path": csv_input_data.input_data_path,
140
- "sign_required": csv_input_data.sign_required,
141
148
  }
142
149
  )
143
150
 
@@ -181,12 +188,13 @@ class SubPutInputData:
181
188
 
182
189
  return self.confirm_processing(message_for_confirm)
183
190
 
184
- def put_input_data_main(self, project_id: str, csv_input_data: CsvInputData, overwrite: bool = False) -> bool: # noqa: FBT001, FBT002
191
+ def put_input_data_main(self, project_id: str, csv_input_data: CsvInputData, *, overwrite: bool = False) -> bool:
185
192
  input_data = InputDataForPut(
186
193
  input_data_name=csv_input_data.input_data_name,
187
194
  input_data_path=csv_input_data.input_data_path,
188
- input_data_id=csv_input_data.input_data_id if csv_input_data.input_data_id is not None else str(uuid.uuid4()),
189
- sign_required=csv_input_data.sign_required,
195
+ input_data_id=csv_input_data.input_data_id
196
+ if csv_input_data.input_data_id is not None
197
+ else convert_input_data_name_to_input_data_id(csv_input_data.input_data_name),
190
198
  )
191
199
 
192
200
  last_updated_datetime = None
@@ -194,10 +202,10 @@ class SubPutInputData:
194
202
 
195
203
  if dict_input_data is not None:
196
204
  if overwrite:
197
- logger.debug(f"input_data_id={input_data.input_data_id} はすでに存在します。")
205
+ logger.debug(f"input_data_id='{input_data.input_data_id}' はすでに存在します。")
198
206
  last_updated_datetime = dict_input_data["updated_datetime"]
199
207
  else:
200
- logger.debug(f"input_data_id={input_data.input_data_id} がすでに存在するのでスキップします。")
208
+ logger.debug(f"input_data_id='{input_data.input_data_id}' がすでに存在するのでスキップします。")
201
209
  return False
202
210
 
203
211
  file_path = get_file_scheme_path(input_data.input_data_path)
@@ -278,14 +286,12 @@ class PutInputData(CommandLine):
278
286
 
279
287
  @staticmethod
280
288
  def get_input_data_list_from_df(df: pandas.DataFrame) -> list[CsvInputData]:
281
- def create_input_data(e: Any): # noqa: ANN202, ANN401
289
+ def create_input_data(e: Any) -> CsvInputData: # noqa: ANN401
282
290
  input_data_id = e.input_data_id if not pandas.isna(e.input_data_id) else None
283
- sign_required: Optional[bool] = e.sign_required if pandas.notna(e.sign_required) else None
284
291
  return CsvInputData(
285
292
  input_data_name=e.input_data_name,
286
293
  input_data_path=e.input_data_path,
287
294
  input_data_id=input_data_id,
288
- sign_required=sign_required,
289
295
  )
290
296
 
291
297
  input_data_list = [create_input_data(e) for e in df.itertuples()]
@@ -381,7 +387,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
381
387
  " * 1列目: input_data_name (required)\n"
382
388
  " * 2列目: input_data_path (required)\n"
383
389
  " * 3列目: input_data_id\n"
384
- " * 4列目: sign_required (bool)\n"
385
390
  ),
386
391
  )
387
392
 
@@ -400,25 +405,20 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
400
405
  parser.add_argument(
401
406
  "--overwrite",
402
407
  action="store_true",
403
- help="指定した場合、input_data_idがすでに存在していたら上書きします。指定しなければ、スキップします。"
404
- " ``--csv`` , ``--json`` を指定したときのみ有効なオプションです。",
408
+ help="指定した場合、input_data_idがすでに存在していたら上書きします。指定しなければ、スキップします。",
405
409
  )
406
410
 
407
411
  parser.add_argument(
408
412
  "--allow_duplicated_input_data",
409
413
  action="store_true",
410
- help=(
411
- "``--csv`` , ``--json`` に渡した入力データの重複(input_data_name, input_data_path)を許可します。\n"
412
- "``--csv`` , ``--json`` を指定したときのみ有効なオプションです。"
413
- ),
414
+ help=("``--csv`` , ``--json`` に渡した入力データの重複(input_data_name, input_data_path)を許可します。\n"),
414
415
  )
415
416
 
416
417
  parser.add_argument(
417
418
  "--parallelism",
418
419
  type=int,
419
420
  choices=PARALLELISM_CHOICES,
420
- help="並列度。指定しない場合は、逐次的に処理します。"
421
- "``--csv`` , ``--json`` を指定したときのみ有効なオプションです。また、必ず ``--yes`` を指定してください。",
421
+ help="並列度。指定しない場合は、逐次的に処理します。指定する場合は、 ``--yes`` も指定してください。",
422
422
  )
423
423
 
424
424
  parser.set_defaults(subcommand_func=main)
@@ -427,9 +427,8 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
427
427
  def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
428
428
  subcommand_name = "put"
429
429
  subcommand_help = "入力データを登録します。"
430
- description = "CSVに記載された情報から、入力データを登録します。"
431
430
  epilog = "オーナロールを持つユーザで実行してください。"
432
431
 
433
- parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
432
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, epilog=epilog)
434
433
  parse_args(parser)
435
434
  return parser
@@ -0,0 +1,310 @@
1
+ # pylint: disable=too-many-lines
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import logging
6
+ import sys
7
+ import tempfile
8
+ import zipfile
9
+ from collections.abc import Collection, Iterator
10
+ from pathlib import Path
11
+ from typing import Any, Literal, Optional, Union
12
+
13
+ import pandas
14
+ import pydantic
15
+ from annofabapi.models import ProjectMemberRole
16
+ from annofabapi.parser import (
17
+ SimpleAnnotationParser,
18
+ lazy_parse_simple_annotation_dir,
19
+ lazy_parse_simple_annotation_zip,
20
+ )
21
+
22
+ import annofabcli
23
+ import annofabcli.common.cli
24
+ from annofabcli.common.cli import (
25
+ COMMAND_LINE_ERROR_STATUS_CODE,
26
+ ArgumentParser,
27
+ CommandLine,
28
+ build_annofabapi_resource_and_login,
29
+ )
30
+ from annofabcli.common.download import DownloadingFile
31
+ from annofabcli.common.enums import FormatArgument
32
+ from annofabcli.common.facade import (
33
+ AnnofabApiFacade,
34
+ TaskQuery,
35
+ match_annotation_with_task_query,
36
+ )
37
+ from annofabcli.common.type_util import assert_noreturn
38
+ from annofabcli.common.utils import print_csv, print_json
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ def lazy_parse_simple_annotation_by_input_data(annotation_path: Path) -> Iterator[SimpleAnnotationParser]:
44
+ if not annotation_path.exists():
45
+ raise RuntimeError(f"'{annotation_path}' は存在しません。")
46
+
47
+ if annotation_path.is_dir():
48
+ return lazy_parse_simple_annotation_dir(annotation_path)
49
+ elif zipfile.is_zipfile(str(annotation_path)):
50
+ return lazy_parse_simple_annotation_zip(annotation_path)
51
+ else:
52
+ raise RuntimeError(f"'{annotation_path}'は、zipファイルまたはディレクトリではありません。")
53
+
54
+
55
+ class AnnotationAttribute(pydantic.BaseModel):
56
+ """
57
+ 入力データまたはタスク単位の区間アノテーションの長さ情報。
58
+ """
59
+
60
+ task_id: str
61
+ task_status: str
62
+ task_phase: str
63
+ task_phase_stage: int
64
+
65
+ input_data_id: str
66
+ input_data_name: str
67
+ annotation_id: str
68
+ label: str
69
+ attributes: dict[str, Union[str, int, bool]]
70
+
71
+
72
+ def get_annotation_attribute_list_from_annotation_json(
73
+ simple_annotation: dict[str, Any], *, target_labels: Collection[str] | None = None
74
+ ) -> list[AnnotationAttribute]:
75
+ """
76
+ 1個のアノテーションJSONに対して、アノテーションの属性情報を取得します。
77
+
78
+ Args:
79
+ simple_annotation: アノテーションJSONファイルの内容
80
+ target_labels: 絞り込むラベルのcollection
81
+ """
82
+ details = simple_annotation["details"]
83
+
84
+ result = []
85
+ for detail in details:
86
+ if target_labels is not None: # noqa: SIM102
87
+ if detail["label"] not in target_labels:
88
+ continue
89
+
90
+ result.append(
91
+ AnnotationAttribute(
92
+ task_id=simple_annotation["task_id"],
93
+ task_status=simple_annotation["task_status"],
94
+ task_phase=simple_annotation["task_phase"],
95
+ task_phase_stage=simple_annotation["task_phase_stage"],
96
+ input_data_id=simple_annotation["input_data_id"],
97
+ input_data_name=simple_annotation["input_data_name"],
98
+ label=detail["label"],
99
+ annotation_id=detail["annotation_id"],
100
+ attributes=detail["attributes"],
101
+ )
102
+ )
103
+ return result
104
+
105
+
106
+ def get_annotation_attribute_list_from_annotation_zipdir_path(
107
+ annotation_zipdir_path: Path,
108
+ *,
109
+ target_task_ids: Optional[Collection[str]] = None,
110
+ task_query: Optional[TaskQuery] = None,
111
+ target_labels: Collection[str] | None = None,
112
+ ) -> list[AnnotationAttribute]:
113
+ """
114
+ アノテーションzipまたはそれを展開したディレクトリから、アノテーションの属性のlistを取得します。
115
+
116
+ Args:
117
+ """
118
+ target_task_ids = set(target_task_ids) if target_task_ids is not None else None
119
+
120
+ iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_zipdir_path)
121
+
122
+ result = []
123
+ logger.debug("アノテーションzipまたはディレクトリを読み込み中")
124
+ for index, parser in enumerate(iter_parser):
125
+ if (index + 1) % 1000 == 0:
126
+ logger.debug(f"{index + 1} 件目のJSONを読み込み中")
127
+
128
+ if target_task_ids is not None and parser.task_id not in target_task_ids:
129
+ continue
130
+
131
+ simple_annotation_dict = parser.load_json()
132
+ if task_query is not None: # noqa: SIM102
133
+ if not match_annotation_with_task_query(simple_annotation_dict, task_query):
134
+ continue
135
+
136
+ sub_result = get_annotation_attribute_list_from_annotation_json(simple_annotation_dict, target_labels=target_labels)
137
+ result.extend(sub_result)
138
+
139
+ return result
140
+
141
+
142
+ def print_annotation_attribute_list_as_csv(annotation_attribute_list: list, output_file: Optional[Path]) -> None:
143
+ df = pandas.json_normalize(annotation_attribute_list)
144
+
145
+ base_columns = [
146
+ "task_id",
147
+ "task_status",
148
+ "task_phase",
149
+ "task_phase_stage",
150
+ "input_data_id",
151
+ "input_data_name",
152
+ "annotation_id",
153
+ "label",
154
+ ]
155
+ attribute_columns = [col for col in df.columns if col.startswith("attributes.")]
156
+ columns = base_columns + attribute_columns
157
+ print_csv(df[columns], output_file)
158
+
159
+
160
+ def print_annotation_attribute_list(
161
+ annotation_attribute_list: list[AnnotationAttribute],
162
+ output_file: Path,
163
+ output_format: Literal[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
164
+ ) -> None:
165
+ tmp_annotation_attribute_list = [e.model_dump() for e in annotation_attribute_list]
166
+ if output_format == FormatArgument.CSV:
167
+ print_annotation_attribute_list_as_csv(tmp_annotation_attribute_list, output_file)
168
+ elif output_format == FormatArgument.JSON:
169
+ print_json(tmp_annotation_attribute_list, output=output_file, is_pretty=False)
170
+ elif output_format == FormatArgument.PRETTY_JSON:
171
+ print_json(tmp_annotation_attribute_list, output=output_file, is_pretty=True)
172
+ else:
173
+ raise assert_noreturn(output_format)
174
+
175
+
176
+ class ListAnnotationAttribute(CommandLine):
177
+ COMMON_MESSAGE = "annofabcli statistics list_annotation_attribute: error:"
178
+
179
+ def validate(self, args: argparse.Namespace) -> bool:
180
+ if args.project_id is None and args.annotation is None:
181
+ print( # noqa: T201
182
+ f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
183
+ file=sys.stderr,
184
+ )
185
+ return False
186
+
187
+ return True
188
+
189
+ def main(self) -> None:
190
+ args = self.args
191
+
192
+ if not self.validate(args):
193
+ sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
194
+
195
+ project_id: Optional[str] = args.project_id
196
+ if project_id is not None:
197
+ super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
198
+
199
+ annotation_path = Path(args.annotation) if args.annotation is not None else None
200
+
201
+ task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
202
+ label_name_list = annofabcli.common.cli.get_list_from_args(args.label_name) if args.label_name is not None else None
203
+ task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
204
+
205
+ output_file: Path = args.output
206
+ output_format = FormatArgument(args.format)
207
+
208
+ downloading_obj = DownloadingFile(self.service)
209
+
210
+ def download_and_print_annotation_attribute_list(
211
+ project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path: Optional[Path]
212
+ ) -> None:
213
+ if annotation_path is None:
214
+ annotation_path = temp_dir / f"{project_id}__annotation.zip"
215
+ downloading_obj.download_annotation_zip(
216
+ project_id,
217
+ dest_path=annotation_path,
218
+ is_latest=is_latest,
219
+ )
220
+
221
+ annotation_attribute_list = get_annotation_attribute_list_from_annotation_zipdir_path(
222
+ annotation_zipdir_path=annotation_path, target_task_ids=task_id_list, task_query=task_query, target_labels=label_name_list
223
+ )
224
+ print_annotation_attribute_list(annotation_attribute_list, output_file, output_format) # type: ignore[arg-type]
225
+
226
+ if project_id is not None:
227
+ if args.temp_dir is not None:
228
+ download_and_print_annotation_attribute_list(
229
+ project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest, annotation_path=annotation_path
230
+ )
231
+ else:
232
+ # `NamedTemporaryFile`を使わない理由: Windowsで`PermissionError`が発生するため
233
+ # https://qiita.com/yuji38kwmt/items/c6f50e1fc03dafdcdda0 参考
234
+ with tempfile.TemporaryDirectory() as str_temp_dir:
235
+ download_and_print_annotation_attribute_list(
236
+ project_id=project_id, temp_dir=Path(str_temp_dir), is_latest=args.latest, annotation_path=annotation_path
237
+ )
238
+ else:
239
+ assert annotation_path is not None
240
+ annotation_attribute_list = get_annotation_attribute_list_from_annotation_zipdir_path(
241
+ annotation_zipdir_path=annotation_path, target_task_ids=task_id_list, task_query=task_query, target_labels=label_name_list
242
+ )
243
+ print_annotation_attribute_list(annotation_attribute_list, output_file, output_format) # type: ignore[arg-type]
244
+
245
+
246
+ def parse_args(parser: argparse.ArgumentParser) -> None:
247
+ argument_parser = ArgumentParser(parser)
248
+
249
+ annotation_group = parser.add_mutually_exclusive_group(required=True)
250
+ annotation_group.add_argument(
251
+ "--annotation",
252
+ type=str,
253
+ help="アノテーションzip、またはzipを展開したディレクトリを指定します。指定しない場合はAnnofabからダウンロードします。",
254
+ )
255
+
256
+ annotation_group.add_argument("-p", "--project_id", type=str, help="対象プロジェクトの project_id")
257
+
258
+ argument_parser.add_format(
259
+ choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
260
+ default=FormatArgument.CSV,
261
+ )
262
+
263
+ argument_parser.add_output()
264
+
265
+ parser.add_argument(
266
+ "-tq",
267
+ "--task_query",
268
+ type=str,
269
+ help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
270
+ " ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
271
+ )
272
+ argument_parser.add_task_id(required=False)
273
+
274
+ parser.add_argument(
275
+ "--label_name",
276
+ type=str,
277
+ nargs="+",
278
+ required=False,
279
+ help="出力対象のアノテーションのラベル名(英語)を指定します。指定しない場合はラベル名で絞り込みません。"
280
+ " ``file://`` を先頭に付けると、ラベル名の一覧が記載されたファイルを指定できます。",
281
+ )
282
+
283
+ parser.add_argument(
284
+ "--latest",
285
+ action="store_true",
286
+ help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。", # noqa: E501
287
+ )
288
+
289
+ parser.add_argument(
290
+ "--temp_dir",
291
+ type=Path,
292
+ help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
293
+ )
294
+
295
+ parser.set_defaults(subcommand_func=main)
296
+
297
+
298
+ def main(args: argparse.Namespace) -> None:
299
+ service = build_annofabapi_resource_and_login(args)
300
+ facade = AnnofabApiFacade(service)
301
+ ListAnnotationAttribute(service, facade, args).main()
302
+
303
+
304
+ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
305
+ subcommand_name = "list_annotation_attribute"
306
+ subcommand_help = "アノテーションZIPを読み込み、アノテーションの属性値の一覧を出力します。"
307
+ epilog = "オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
308
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
309
+ parse_args(parser)
310
+ return parser
@@ -4,6 +4,7 @@ from typing import Optional
4
4
  import annofabcli
5
5
  import annofabcli.common.cli
6
6
  import annofabcli.stat_visualization.merge_visualization_dir
7
+ import annofabcli.statistics.list_annotation_attribute
7
8
  import annofabcli.statistics.list_annotation_count
8
9
  import annofabcli.statistics.list_annotation_duration
9
10
  import annofabcli.statistics.list_video_duration
@@ -21,6 +22,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
21
22
  subparsers = parser.add_subparsers(dest="subcommand_name")
22
23
 
23
24
  # サブコマンドの定義
25
+ annofabcli.statistics.list_annotation_attribute.add_parser(subparsers)
24
26
  annofabcli.statistics.list_annotation_count.add_parser(subparsers)
25
27
  annofabcli.statistics.list_annotation_duration.add_parser(subparsers)
26
28
  annofabcli.statistics.list_video_duration.add_parser(subparsers)
@@ -92,14 +92,14 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
92
92
  supplementary_data = _get_supplementary_data_list(supplementary_data_id)
93
93
  if supplementary_data is None:
94
94
  logger.warning(
95
- f"input_data_id={input_data_id} の入力データに、"
96
- f"supplementary_data_id={supplementary_data_id} の補助情報は存在しないのでスキップします。"
95
+ f"input_data_id='{input_data_id}' の入力データに、"
96
+ f"supplementary_data_id='{supplementary_data_id}' の補助情報は存在しないのでスキップします。"
97
97
  )
98
98
  continue
99
99
 
100
100
  message_for_confirm = (
101
- f"補助情報 supplementary_data_id={supplementary_data_id}, "
102
- f"supplementary_data_name={supplementary_data['supplementary_data_name']} を削除しますか?"
101
+ f"補助情報 supplementary_data_id='{supplementary_data_id}', "
102
+ f"supplementary_data_name='{supplementary_data['supplementary_data_name']}' を削除しますか?"
103
103
  )
104
104
  if not self.confirm_processing(message_for_confirm):
105
105
  continue
@@ -107,17 +107,17 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
107
107
  try:
108
108
  self.service.api.delete_supplementary_data(project_id, input_data_id=input_data_id, supplementary_data_id=supplementary_data_id)
109
109
  logger.debug(
110
- f"補助情報 supplementary_data_id={supplementary_data_id}, "
111
- f"supplementary_data_name={supplementary_data['supplementary_data_name']} を削除しました。"
112
- f"(入力データ input_data_id={input_data_id}, "
113
- f"input_data_name={input_data['input_data_name']} に紐付いている)"
110
+ f"補助情報 supplementary_data_id='{supplementary_data_id}', "
111
+ f"supplementary_data_name='{supplementary_data['supplementary_data_name']}' を削除しました。"
112
+ f"(入力データ input_data_id='{input_data_id}', "
113
+ f"input_data_name='{input_data['input_data_name']}' に紐付いている)"
114
114
  )
115
115
  deleted_count += 1
116
- except requests.HTTPError as e:
117
- logger.warning(e)
116
+ except requests.HTTPError:
118
117
  logger.warning(
119
- f"補助情報 supplementary_data_id={supplementary_data_id}, "
120
- f"supplementary_data_name={supplementary_data['supplementary_data_name']} の削除に失敗しました。"
118
+ f"補助情報 supplementary_data_id='{supplementary_data_id}', "
119
+ f"supplementary_data_name='{supplementary_data['supplementary_data_name']}' の削除に失敗しました。",
120
+ exc_info=True,
121
121
  )
122
122
  continue
123
123
  return deleted_count
@@ -128,9 +128,8 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
128
128
  for input_data_id, supplementary_data_id_list in input_data_dict.items():
129
129
  try:
130
130
  deleted_count += self.delete_supplementary_data_list_for_input_data(project_id, input_data_id, supplementary_data_id_list)
131
- except Exception as e: # pylint: disable=broad-except
132
- logger.warning(e)
133
- logger.warning(f"入力データ(input_data_id={input_data_id})配下の補助情報の削除に失敗しました。")
131
+ except Exception: # pylint: disable=broad-except
132
+ logger.warning(f"入力データ(input_data_id='{input_data_id}')配下の補助情報の削除に失敗しました。", exc_info=True)
134
133
 
135
134
  logger.info(f"{deleted_count} / {total_count} 件の補助情報を削除しました。")
136
135
 
@@ -155,15 +154,15 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
155
154
  try:
156
155
  self.service.api.delete_supplementary_data(project_id, input_data_id=input_data_id, supplementary_data_id=supplementary_data_id)
157
156
  logger.debug(
158
- f"補助情報を削除しました。input_data_id={input_data_id}, supplementary_data_id={supplementary_data_id}, "
157
+ f"補助情報を削除しました。input_data_id='{input_data_id}', supplementary_data_id='{supplementary_data_id}', "
159
158
  f"supplementary_data_name={supplementary_data['supplementary_data_name']}"
160
159
  )
161
160
  deleted_count += 1
162
- except requests.HTTPError as e:
163
- logger.warning(e)
161
+ except requests.HTTPError:
164
162
  logger.warning(
165
- f"補助情報の削除に失敗しました。input_data_id={input_data_id}, supplementary_data_id={supplementary_data_id}, "
166
- f"supplementary_data_name={supplementary_data['supplementary_data_name']}"
163
+ f"補助情報の削除に失敗しました。input_data_id='{input_data_id}', supplementary_data_id='{supplementary_data_id}', "
164
+ f"supplementary_data_name='{supplementary_data['supplementary_data_name']}'",
165
+ exc_info=True,
167
166
  )
168
167
  continue
169
168
 
@@ -174,7 +173,7 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
174
173
  for input_data_id in input_data_id_list:
175
174
  input_data = self.service.wrapper.get_input_data_or_none(project_id, input_data_id)
176
175
  if input_data is None:
177
- logger.warning(f"input_data_id={input_data_id} の入力データは存在しないので、補助情報の削除をスキップします。")
176
+ logger.warning(f"input_data_id='{input_data_id}' の入力データは存在しないので、補助情報の削除をスキップします。")
178
177
  continue
179
178
  input_data_name = input_data["input_data_name"]
180
179
 
@@ -202,9 +201,8 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
202
201
  f"input_data_name='{input_data_name}') "
203
202
  )
204
203
 
205
- except Exception as e: # pylint: disable=broad-except
206
- logger.warning(e)
207
- logger.warning(f"入力データ(input_data_id={input_data_id})配下の補助情報の削除に失敗しました。")
204
+ except Exception: # pylint: disable=broad-except
205
+ logger.warning(f"入力データ(input_data_id='{input_data_id}')配下の補助情報の削除に失敗しました。", exc_info=True)
208
206
 
209
207
  logger.info(f"{len(dict_deleted_count)} / {len(input_data_id_list)} 件の入力データに紐づく補助情報を削除しました。")
210
208