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.
- {annofabcli-1.95.0 → annofabcli-1.96.1}/PKG-INFO +1 -1
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__version__.py +1 -1
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/export_annotation_specs.py +10 -5
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/put_input_data.py +28 -29
- annofabcli-1.96.1/annofabcli/statistics/list_annotation_attribute.py +310 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/subcommand_statistics.py +2 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/delete_supplementary_data.py +22 -24
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/put_supplementary_data.py +52 -29
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_tasks_added_task_history.py +2 -1
- {annofabcli-1.95.0 → annofabcli-1.96.1}/pyproject.toml +5 -1
- {annofabcli-1.95.0 → annofabcli-1.96.1}/LICENSE +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/README.md +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/__main__.py +0 -0
- {annofabcli-1.95.0/annofabcli/task_history_event → annofabcli-1.96.1/annofabcli/annotation}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/annotation_query.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/change_annotation_attributes.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/change_annotation_properties.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/copy_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/delete_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/download_annotation_zip.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/dump_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/import_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/list_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/list_annotation_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/restore_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation/subcommand_annotation.py +0 -0
- {annofabcli-1.95.0/annofabcli/task_history → annofabcli-1.96.1/annofabcli/annotation_specs}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_attribute.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_choice.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_annotation_specs_label.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_attribute_restriction.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/list_label_color.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/put_label_color.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/subcommand_annotation_specs.py +0 -0
- {annofabcli-1.95.0/annofabcli/task → annofabcli-1.96.1/annofabcli/comment}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/delete_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/download_comment_json.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/list_all_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/list_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_comment_simply.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_inspection_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_inspection_comment_simply.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_onhold_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/put_onhold_comment_simply.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/subcommand_comment.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/comment/utils.py +0 -0
- {annofabcli-1.95.0/annofabcli/supplementary → annofabcli-1.96.1/annofabcli/common}/__init__.py +0 -0
- {annofabcli-1.95.0/annofabcli/statistics/visualization/dataframe → annofabcli-1.96.1/annofabcli/common/annofab}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/annofab/project.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/bokeh.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/cli.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/dataclasses.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/download.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/enums.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/exceptions.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/facade.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/image.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/pandas.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/type_util.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/typing.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/utils.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/common/visualize.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/data/logging.yaml +0 -0
- {annofabcli-1.95.0/annofabcli/statistics/visualization → annofabcli-1.96.1/annofabcli/experimental}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/experimental/list_out_of_range_annotation_for_movie.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/experimental/subcommand_experimental.py +0 -0
- {annofabcli-1.95.0/annofabcli/statistics → annofabcli-1.96.1/annofabcli/filesystem}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/draw_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/filter_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/mask_user_info.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/merge_annotation.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/filesystem/subcommand_filesystem.py +0 -0
- {annofabcli-1.95.0/annofabcli/stat_visualization → annofabcli-1.96.1/annofabcli/input_data}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/change_input_data_name.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/copy_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/delete_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/delete_metadata_key_of_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/download_input_data_json.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_all_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_all_input_data_merged_task.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/list_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/put_input_data_with_zip.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/subcommand_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/update_metadata_of_input_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/input_data/utils.py +0 -0
- {annofabcli-1.95.0/annofabcli/project_member → annofabcli-1.96.1/annofabcli/instruction}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/copy_instruction.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/download_instruction.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/list_instruction_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/subcommand_instruction.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/instruction/upload_instruction.py +0 -0
- {annofabcli-1.95.0/annofabcli/project → annofabcli-1.96.1/annofabcli/job}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/delete_job.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/list_job.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/list_last_job.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/subcommand_job.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/job/wait_job.py +0 -0
- {annofabcli-1.95.0/annofabcli/organization_member → annofabcli-1.96.1/annofabcli/my_account}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/my_account/get_my_account.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/my_account/subcommand_my_account.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/list_organization.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization/subcommand_organization.py +0 -0
- {annofabcli-1.95.0/annofabcli/my_account → annofabcli-1.96.1/annofabcli/organization_member}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/change_organization_member.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/delete_organization_member.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/invite_organization_member.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/list_organization_member.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/organization_member/subcommand_organization_member.py +0 -0
- {annofabcli-1.95.0/annofabcli/job → annofabcli-1.96.1/annofabcli/project}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/change_project_status.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/copy_project.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/diff_projects.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/list_project.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/put_project.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project/subcommand_project.py +0 -0
- {annofabcli-1.95.0/annofabcli/instruction → annofabcli-1.96.1/annofabcli/project_member}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/change_project_members.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/copy_project_members.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/drop_project_members.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/invite_project_members.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/list_users.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/put_project_members.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/project_member/subcommand_project_member.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/py.typed +0 -0
- {annofabcli-1.95.0/annofabcli/input_data → annofabcli-1.96.1/annofabcli/stat_visualization}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/mask_visualization_dir.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/merge_visualization_dir.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/subcommand_stat_visualization.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/summarize_whole_performance_csv.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/write_graph.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/stat_visualization/write_performance_rating_csv.py +0 -0
- {annofabcli-1.95.0/annofabcli/filesystem → annofabcli-1.96.1/annofabcli/statistics}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/histogram.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/linegraph.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_annotation_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_annotation_duration.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_video_duration.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/list_worktime.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/scatter.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count_by_task_id_group.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/summarize_task_count_by_user.py +0 -0
- {annofabcli-1.95.0/annofabcli/experimental → annofabcli-1.96.1/annofabcli/statistics/visualization}/__init__.py +0 -0
- {annofabcli-1.95.0/annofabcli/common/annofab → annofabcli-1.96.1/annofabcli/statistics/visualization/dataframe}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/actual_worktime.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/annotation_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/custom_production_volume.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/input_data_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/inspection_comment_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/productivity_per_date.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/project_performance.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/user.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/user_performance.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/whole_performance.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/dataframe/worktime_per_date.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/filtering_query.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/model.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/project_dir.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualization/visualization_source_files.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_annotation_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_annotation_duration.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_statistics.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/statistics/visualize_video_duration.py +0 -0
- {annofabcli-1.95.0/annofabcli/common → annofabcli-1.96.1/annofabcli/supplementary}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/list_supplementary_data.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/subcommand_supplementary.py +0 -0
- {annofabcli-1.95.0/annofabcli/comment → annofabcli-1.96.1/annofabcli/task}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/cancel_acceptance.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_operator.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_status_to_break.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/change_status_to_on_hold.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/complete_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/copy_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/delete_metadata_key_of_task.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/delete_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/download_task_json.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_all_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_all_tasks_added_task_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/list_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/put_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/put_tasks_by_count.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/reject_tasks.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/subcommand_task.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task/update_metadata_of_task.py +0 -0
- {annofabcli-1.95.0/annofabcli/annotation_specs → annofabcli-1.96.1/annofabcli/task_history}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/download_task_history_json.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/list_all_task_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/list_task_history.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history/subcommand_task_history.py +0 -0
- {annofabcli-1.95.0/annofabcli/annotation → annofabcli-1.96.1/annofabcli/task_history_event}/__init__.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/download_task_history_event_json.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/list_all_task_history_event.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/list_worktime.py +0 -0
- {annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/task_history_event/subcommand_task_history_event.py +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.96.1" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
|
{annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/annotation_specs/export_annotation_specs.py
RENAMED
|
@@ -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
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
189
|
-
|
|
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:
|
|
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,
|
|
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)
|
{annofabcli-1.95.0 → annofabcli-1.96.1}/annofabcli/supplementary/delete_supplementary_data.py
RENAMED
|
@@ -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
|
|
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
|
|
132
|
-
logger.warning(
|
|
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
|
|
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
|
|
206
|
-
logger.warning(
|
|
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
|
|