annofabcli 1.111.2__py3-none-any.whl → 1.112.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annofabcli/__main__.py +1 -2
- annofabcli/annotation/annotation_query.py +10 -10
- annofabcli/annotation/change_annotation_attributes.py +9 -9
- annofabcli/annotation/change_annotation_attributes_per_annotation.py +3 -4
- annofabcli/annotation/change_annotation_properties.py +13 -13
- annofabcli/annotation/copy_annotation.py +5 -5
- annofabcli/annotation/create_classification_annotation.py +6 -6
- annofabcli/annotation/delete_annotation.py +8 -8
- annofabcli/annotation/download_annotation_zip.py +1 -3
- annofabcli/annotation/dump_annotation.py +7 -7
- annofabcli/annotation/import_annotation.py +12 -12
- annofabcli/annotation/list_annotation.py +8 -8
- annofabcli/annotation/list_annotation_count.py +1 -2
- annofabcli/annotation/merge_segmentation.py +5 -5
- annofabcli/annotation/remove_segmentation_overlap.py +4 -4
- annofabcli/annotation/restore_annotation.py +6 -6
- annofabcli/annotation/subcommand_annotation.py +1 -2
- annofabcli/annotation_specs/add_attribute_restriction.py +4 -5
- annofabcli/annotation_specs/attribute_restriction.py +8 -8
- annofabcli/annotation_specs/export_annotation_specs.py +4 -5
- annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -4
- annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +3 -4
- annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +3 -4
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +9 -10
- annofabcli/annotation_specs/list_annotation_specs_choice.py +9 -10
- annofabcli/annotation_specs/list_annotation_specs_history.py +2 -2
- annofabcli/annotation_specs/list_annotation_specs_label.py +8 -9
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +10 -11
- annofabcli/annotation_specs/list_attribute_restriction.py +2 -4
- annofabcli/annotation_specs/list_label_color.py +2 -3
- annofabcli/annotation_specs/put_label_color.py +3 -4
- annofabcli/annotation_specs/subcommand_annotation_specs.py +1 -3
- annofabcli/annotation_zip/list_annotation_3d_bounding_box.py +365 -0
- annofabcli/annotation_zip/list_annotation_bounding_box_2d.py +11 -12
- annofabcli/annotation_zip/list_range_annotation.py +24 -14
- annofabcli/annotation_zip/list_single_point_annotation.py +11 -12
- annofabcli/annotation_zip/subcommand_annotation_zip.py +3 -2
- annofabcli/annotation_zip/validate_annotation.py +8 -7
- annofabcli/comment/delete_comment.py +4 -6
- annofabcli/comment/download_comment_json.py +4 -6
- annofabcli/comment/list_all_comment.py +5 -6
- annofabcli/comment/list_comment.py +3 -4
- annofabcli/comment/put_comment.py +9 -10
- annofabcli/comment/put_comment_simply.py +5 -6
- annofabcli/comment/put_inspection_comment.py +1 -3
- annofabcli/comment/put_inspection_comment_simply.py +1 -3
- annofabcli/comment/put_onhold_comment.py +1 -3
- annofabcli/comment/put_onhold_comment_simply.py +1 -3
- annofabcli/comment/subcommand_comment.py +1 -3
- annofabcli/common/bokeh.py +4 -4
- annofabcli/common/cli.py +17 -17
- annofabcli/common/download.py +28 -29
- annofabcli/common/facade.py +37 -38
- annofabcli/common/image.py +14 -14
- annofabcli/common/utils.py +8 -8
- annofabcli/common/visualize.py +13 -13
- annofabcli/experimental/list_out_of_range_annotation_for_movie.py +3 -4
- annofabcli/experimental/subcommand_experimental.py +1 -3
- annofabcli/filesystem/draw_annotation.py +26 -26
- annofabcli/filesystem/filter_annotation.py +9 -10
- annofabcli/filesystem/mask_user_info.py +14 -14
- annofabcli/filesystem/merge_annotation.py +8 -8
- annofabcli/filesystem/subcommand_filesystem.py +1 -3
- annofabcli/input_data/copy_input_data.py +8 -9
- annofabcli/input_data/delete_input_data.py +2 -2
- annofabcli/input_data/delete_metadata_key_of_input_data.py +3 -5
- annofabcli/input_data/download_input_data_json.py +4 -6
- annofabcli/input_data/list_all_input_data.py +8 -8
- annofabcli/input_data/list_all_input_data_merged_task.py +4 -4
- annofabcli/input_data/list_input_data.py +4 -4
- annofabcli/input_data/put_input_data.py +5 -5
- annofabcli/input_data/put_input_data_with_zip.py +2 -3
- annofabcli/input_data/subcommand_input_data.py +1 -3
- annofabcli/input_data/update_input_data.py +6 -8
- annofabcli/input_data/update_metadata_of_input_data.py +3 -5
- annofabcli/instruction/copy_instruction.py +4 -5
- annofabcli/instruction/download_instruction.py +4 -5
- annofabcli/instruction/list_instruction_history.py +2 -2
- annofabcli/instruction/subcommand_instruction.py +1 -3
- annofabcli/instruction/upload_instruction.py +2 -3
- annofabcli/job/delete_job.py +1 -2
- annofabcli/job/list_job.py +4 -4
- annofabcli/job/list_last_job.py +3 -3
- annofabcli/job/subcommand_job.py +1 -3
- annofabcli/job/wait_job.py +4 -5
- annofabcli/my_account/get_my_account.py +1 -2
- annofabcli/my_account/subcommand_my_account.py +1 -3
- annofabcli/organization/list_organization.py +1 -2
- annofabcli/organization/subcommand_organization.py +1 -3
- annofabcli/organization_member/change_organization_member.py +3 -4
- annofabcli/organization_member/delete_organization_member.py +3 -4
- annofabcli/organization_member/invite_organization_member.py +1 -3
- annofabcli/organization_member/list_organization_member.py +2 -2
- annofabcli/organization_member/subcommand_organization_member.py +1 -3
- annofabcli/project/change_organization_of_project.py +3 -3
- annofabcli/project/change_project_status.py +3 -3
- annofabcli/project/copy_project.py +4 -4
- annofabcli/project/create_project.py +7 -7
- annofabcli/project/diff_projects.py +4 -5
- annofabcli/project/list_project.py +4 -4
- annofabcli/project/put_project.py +1 -2
- annofabcli/project/subcommand_project.py +1 -2
- annofabcli/project/update_configuration.py +3 -3
- annofabcli/project/update_project.py +6 -8
- annofabcli/project_member/change_project_members.py +7 -7
- annofabcli/project_member/copy_project_members.py +3 -3
- annofabcli/project_member/drop_project_members.py +1 -2
- annofabcli/project_member/invite_project_members.py +1 -3
- annofabcli/project_member/list_users.py +1 -2
- annofabcli/project_member/put_project_members.py +5 -5
- annofabcli/project_member/subcommand_project_member.py +1 -3
- annofabcli/stat_visualization/mask_visualization_dir.py +8 -9
- annofabcli/stat_visualization/merge_visualization_dir.py +6 -7
- annofabcli/stat_visualization/subcommand_stat_visualization.py +1 -2
- annofabcli/stat_visualization/summarize_whole_performance_csv.py +1 -2
- annofabcli/stat_visualization/write_graph.py +2 -3
- annofabcli/stat_visualization/write_performance_rating_csv.py +20 -27
- annofabcli/statistics/histogram.py +5 -6
- annofabcli/statistics/linegraph.py +13 -14
- annofabcli/statistics/list_annotation_area.py +38 -13
- annofabcli/statistics/list_annotation_attribute.py +9 -10
- annofabcli/statistics/list_annotation_attribute_filled_count.py +30 -31
- annofabcli/statistics/list_annotation_count.py +57 -58
- annofabcli/statistics/list_annotation_duration.py +33 -34
- annofabcli/statistics/list_video_duration.py +4 -5
- annofabcli/statistics/list_worktime.py +3 -3
- annofabcli/statistics/scatter.py +9 -8
- annofabcli/statistics/subcommand_statistics.py +1 -4
- annofabcli/statistics/summarize_task_count.py +4 -6
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +2 -4
- annofabcli/statistics/summarize_task_count_by_user.py +1 -3
- annofabcli/statistics/visualization/dataframe/annotation_count.py +5 -4
- annofabcli/statistics/visualization/dataframe/annotation_duration.py +2 -3
- annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +15 -17
- annofabcli/statistics/visualization/dataframe/productivity_per_date.py +17 -19
- annofabcli/statistics/visualization/dataframe/project_performance.py +3 -12
- annofabcli/statistics/visualization/dataframe/task.py +11 -12
- annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +9 -10
- annofabcli/statistics/visualization/dataframe/user_performance.py +21 -19
- annofabcli/statistics/visualization/dataframe/whole_performance.py +3 -4
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +12 -14
- annofabcli/statistics/visualization/dataframe/worktime_per_date.py +11 -13
- annofabcli/statistics/visualization/filtering_query.py +7 -7
- annofabcli/statistics/visualization/project_dir.py +27 -14
- annofabcli/statistics/visualize_annotation_count.py +22 -23
- annofabcli/statistics/visualize_annotation_duration.py +21 -22
- annofabcli/statistics/visualize_statistics.py +36 -33
- annofabcli/statistics/visualize_video_duration.py +18 -20
- annofabcli/supplementary/delete_supplementary_data.py +4 -4
- annofabcli/supplementary/list_supplementary_data.py +3 -3
- annofabcli/supplementary/put_supplementary_data.py +8 -8
- annofabcli/supplementary/subcommand_supplementary.py +1 -3
- annofabcli/task/cancel_acceptance.py +16 -17
- annofabcli/task/change_operator.py +10 -12
- annofabcli/task/change_status_to_break.py +7 -9
- annofabcli/task/change_status_to_on_hold.py +10 -12
- annofabcli/task/complete_tasks.py +17 -18
- annofabcli/task/copy_tasks.py +3 -5
- annofabcli/task/delete_metadata_key_of_task.py +4 -6
- annofabcli/task/delete_tasks.py +6 -6
- annofabcli/task/download_task_json.py +4 -6
- annofabcli/task/list_all_tasks.py +7 -7
- annofabcli/task/list_all_tasks_added_task_history.py +12 -12
- annofabcli/task/list_tasks.py +6 -6
- annofabcli/task/list_tasks_added_task_history.py +9 -9
- annofabcli/task/put_tasks.py +4 -5
- annofabcli/task/put_tasks_by_count.py +1 -2
- annofabcli/task/reject_tasks.py +18 -20
- annofabcli/task/subcommand_task.py +1 -3
- annofabcli/task/update_metadata_of_task.py +5 -6
- annofabcli/task_history/download_task_history_json.py +4 -6
- annofabcli/task_history/list_all_task_history.py +5 -6
- annofabcli/task_history/list_task_history.py +3 -4
- annofabcli/task_history/subcommand_task_history.py +1 -3
- annofabcli/task_history_event/download_task_history_event_json.py +4 -6
- annofabcli/task_history_event/list_all_task_history_event.py +6 -6
- annofabcli/task_history_event/list_worktime.py +15 -15
- annofabcli/task_history_event/subcommand_task_history_event.py +1 -2
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/METADATA +9 -15
- annofabcli-1.112.0.dist-info/RECORD +229 -0
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/WHEEL +1 -1
- annofabcli-1.111.2.dist-info/RECORD +0 -228
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,7 @@ import argparse
|
|
|
2
2
|
import logging
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
import more_itertools
|
|
8
8
|
import pandas
|
|
@@ -25,8 +25,8 @@ class Member(DataClassJsonMixin):
|
|
|
25
25
|
|
|
26
26
|
user_id: str
|
|
27
27
|
member_role: ProjectMemberRole
|
|
28
|
-
sampling_inspection_rate:
|
|
29
|
-
sampling_acceptance_rate:
|
|
28
|
+
sampling_inspection_rate: int | None = None
|
|
29
|
+
sampling_acceptance_rate: int | None = None
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class PutProjectMembers(CommandLine):
|
|
@@ -35,7 +35,7 @@ class PutProjectMembers(CommandLine):
|
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
37
|
@staticmethod
|
|
38
|
-
def find_member(members: list[dict[str, Any]], user_id: str) ->
|
|
38
|
+
def find_member(members: list[dict[str, Any]], user_id: str) -> dict[str, Any] | None:
|
|
39
39
|
member = more_itertools.first_true(members, default=None, pred=lambda e: e["user_id"] == user_id)
|
|
40
40
|
return member
|
|
41
41
|
|
|
@@ -181,7 +181,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
181
181
|
parser.set_defaults(subcommand_func=main)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def add_parser(subparsers:
|
|
184
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
185
185
|
subcommand_name = "put"
|
|
186
186
|
subcommand_help = "プロジェクトメンバを登録します。"
|
|
187
187
|
epilog = "オーナーロールを持つユーザで実行してください。"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
|
-
import annofabcli
|
|
5
3
|
import annofabcli.common.cli
|
|
6
4
|
import annofabcli.project_member.change_project_members
|
|
7
5
|
import annofabcli.project_member.copy_project_members
|
|
@@ -23,7 +21,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
23
21
|
annofabcli.project_member.put_project_members.add_parser(subparsers)
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
def add_parser(subparsers:
|
|
24
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
27
25
|
subcommand_name = "project_member"
|
|
28
26
|
subcommand_help = "プロジェクトメンバ関係のサブコマンド"
|
|
29
27
|
description = "プロジェクトメンバ関係のサブコマンド"
|
|
@@ -5,7 +5,6 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
import pandas
|
|
11
10
|
from annofabapi.models import TaskPhase
|
|
@@ -54,8 +53,8 @@ class ReplacementDict:
|
|
|
54
53
|
def create_replacement_dict(
|
|
55
54
|
df_user: pandas.DataFrame,
|
|
56
55
|
*,
|
|
57
|
-
not_masked_biography_set:
|
|
58
|
-
not_masked_user_id_set:
|
|
56
|
+
not_masked_biography_set: set[str] | None,
|
|
57
|
+
not_masked_user_id_set: set[str] | None,
|
|
59
58
|
) -> ReplacementDict:
|
|
60
59
|
"""
|
|
61
60
|
ユーザー情報を置換するためのインスタンスを生成します。
|
|
@@ -76,8 +75,8 @@ def create_replacement_dict(
|
|
|
76
75
|
|
|
77
76
|
df2 = df_user.set_index("user_id")
|
|
78
77
|
df3 = df2.loc[replacement_dict_for_user_id.keys()]
|
|
79
|
-
replacement_dict_for_username = dict(zip(df3["username"], replacement_dict_for_user_id.values()))
|
|
80
|
-
replacement_dict_for_account_id = dict(zip(df3["account_id"], replacement_dict_for_user_id.values()))
|
|
78
|
+
replacement_dict_for_username = dict(zip(df3["username"], replacement_dict_for_user_id.values(), strict=False))
|
|
79
|
+
replacement_dict_for_account_id = dict(zip(df3["account_id"], replacement_dict_for_user_id.values(), strict=False))
|
|
81
80
|
|
|
82
81
|
replacement_dict_by_biography = create_replacement_dict_by_biography(df_user, not_masked_biography_set=not_masked_biography_set)
|
|
83
82
|
|
|
@@ -93,7 +92,7 @@ def write_line_graph(
|
|
|
93
92
|
task_worktime_by_phase_user: TaskWorktimeByPhaseUser,
|
|
94
93
|
output_project_dir: ProjectDir,
|
|
95
94
|
*,
|
|
96
|
-
user_id_list:
|
|
95
|
+
user_id_list: list[str] | None = None,
|
|
97
96
|
minimal_output: bool = False,
|
|
98
97
|
) -> None:
|
|
99
98
|
output_project_dir.write_cumulative_line_graph(
|
|
@@ -150,8 +149,8 @@ def mask_visualization_dir(
|
|
|
150
149
|
project_dir: ProjectDir,
|
|
151
150
|
output_project_dir: ProjectDir,
|
|
152
151
|
*,
|
|
153
|
-
not_masked_biography_set:
|
|
154
|
-
not_masked_user_id_set:
|
|
152
|
+
not_masked_biography_set: set[str] | None = None,
|
|
153
|
+
not_masked_user_id_set: set[str] | None = None,
|
|
155
154
|
minimal_output: bool = False,
|
|
156
155
|
) -> None:
|
|
157
156
|
worktime_per_date = project_dir.read_worktime_per_date_user()
|
|
@@ -292,7 +291,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
292
291
|
parser.set_defaults(subcommand_func=main)
|
|
293
292
|
|
|
294
293
|
|
|
295
|
-
def add_parser(subparsers:
|
|
294
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
296
295
|
subcommand_name = "mask_user_info"
|
|
297
296
|
subcommand_help = "`annofabcli statistics visualize` コマンドの出力結果のユーザ情報をマスクします。"
|
|
298
297
|
description = "`annofabcli statistics visualize` コマンドの出力結果のユーザ情報をマスクします。マスク対象のファイルのみ出力します。"
|
|
@@ -5,7 +5,6 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
from annofabapi.models import TaskPhase
|
|
11
10
|
|
|
@@ -47,7 +46,7 @@ class WritingVisualizationFile:
|
|
|
47
46
|
output_project_dir: ProjectDir,
|
|
48
47
|
task_completion_criteria: TaskCompletionCriteria,
|
|
49
48
|
*,
|
|
50
|
-
user_id_list:
|
|
49
|
+
user_id_list: list[str] | None = None,
|
|
51
50
|
minimal_output: bool = False,
|
|
52
51
|
) -> None:
|
|
53
52
|
self.output_project_dir = output_project_dir
|
|
@@ -138,12 +137,12 @@ class WritingVisualizationFile:
|
|
|
138
137
|
|
|
139
138
|
|
|
140
139
|
class MergingVisualizationFile:
|
|
141
|
-
def __init__(self, project_dir_list: list[ProjectDir], *, custom_production_volume_list:
|
|
140
|
+
def __init__(self, project_dir_list: list[ProjectDir], *, custom_production_volume_list: list[ProductionVolumeColumn] | None = None) -> None:
|
|
142
141
|
self.project_dir_list = project_dir_list
|
|
143
142
|
self.custom_production_volume_list = custom_production_volume_list
|
|
144
143
|
|
|
145
144
|
def merge_worktime_per_date(self) -> WorktimePerDate:
|
|
146
|
-
merged_obj:
|
|
145
|
+
merged_obj: WorktimePerDate | None = None
|
|
147
146
|
for project_dir in self.project_dir_list:
|
|
148
147
|
tmp_obj = project_dir.read_worktime_per_date_user()
|
|
149
148
|
|
|
@@ -191,8 +190,8 @@ def merge_visualization_dir( # pylint: disable=too-many-statements
|
|
|
191
190
|
task_completion_criteria: TaskCompletionCriteria,
|
|
192
191
|
output_project_dir: ProjectDir,
|
|
193
192
|
*,
|
|
194
|
-
custom_production_volume_list:
|
|
195
|
-
user_id_list:
|
|
193
|
+
custom_production_volume_list: list[ProductionVolumeColumn] | None = None,
|
|
194
|
+
user_id_list: list[str] | None = None,
|
|
196
195
|
minimal_output: bool = False,
|
|
197
196
|
) -> None:
|
|
198
197
|
merging_obj = MergingVisualizationFile(project_dir_list, custom_production_volume_list=custom_production_volume_list)
|
|
@@ -316,7 +315,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
316
315
|
parser.set_defaults(subcommand_func=main)
|
|
317
316
|
|
|
318
317
|
|
|
319
|
-
def add_parser(subparsers:
|
|
318
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
320
319
|
subcommand_name = "merge"
|
|
321
320
|
subcommand_help = "``annofabcli statistics visualize`` コマンドの出力結果をマージします。"
|
|
322
321
|
description = "``annofabcli statistics visualize`` コマンドの出力結果をマージします。"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import annofabcli.stat_visualization.mask_visualization_dir
|
|
5
4
|
import annofabcli.stat_visualization.merge_visualization_dir
|
|
@@ -19,7 +18,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
19
18
|
annofabcli.stat_visualization.write_performance_rating_csv.add_parser(subparsers)
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def add_parser(subparsers:
|
|
21
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
23
22
|
subcommand_name = "stat_visualization"
|
|
24
23
|
subcommand_help = "`annofabcli statistics visualization` コマンドの出力結果を加工するサブコマンド(アルファ版)"
|
|
25
24
|
description = "`annofabcli statistics visualization` コマンドの出力結果を加工するサブコマンド(アルファ版)"
|
|
@@ -4,7 +4,6 @@ import argparse
|
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
import annofabcli
|
|
10
9
|
from annofabcli.common.cli import (
|
|
@@ -64,7 +63,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
64
63
|
parser.set_defaults(subcommand_func=main)
|
|
65
64
|
|
|
66
65
|
|
|
67
|
-
def add_parser(subparsers:
|
|
66
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
68
67
|
subcommand_name = "summarize_whole_performance_csv"
|
|
69
68
|
subcommand_help = "``annofabcli statistics visualize`` コマンドの出力結果であるプロジェクトディレクトリから、プロジェクトごとの生産性や品質の一覧を出力します。。"
|
|
70
69
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help)
|
|
@@ -4,7 +4,6 @@ import argparse
|
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from annofabapi.models import TaskPhase
|
|
10
9
|
|
|
@@ -33,7 +32,7 @@ class WritingGraph:
|
|
|
33
32
|
project_dir: ProjectDir,
|
|
34
33
|
output_project_dir: ProjectDir,
|
|
35
34
|
*,
|
|
36
|
-
user_id_list:
|
|
35
|
+
user_id_list: list[str] | None = None,
|
|
37
36
|
minimal_output: bool = False,
|
|
38
37
|
) -> None:
|
|
39
38
|
self.project_dir = project_dir
|
|
@@ -185,7 +184,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
185
184
|
parser.set_defaults(subcommand_func=main)
|
|
186
185
|
|
|
187
186
|
|
|
188
|
-
def add_parser(subparsers:
|
|
187
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
189
188
|
subcommand_name = "write_graph"
|
|
190
189
|
subcommand_help = "`annofabcli statistics visualize` コマンドの出力結果であるプロジェクトのディレクトリから、グラフを出力します。"
|
|
191
190
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help)
|
|
@@ -7,14 +7,13 @@ from collections.abc import Collection
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from enum import Enum
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any,
|
|
10
|
+
from typing import Any, TypeAlias
|
|
11
11
|
|
|
12
12
|
import numpy
|
|
13
13
|
import pandas
|
|
14
14
|
from annofabapi.models import TaskPhase
|
|
15
15
|
from dataclasses_json import DataClassJsonMixin
|
|
16
16
|
from pandas.api.typing import NAType
|
|
17
|
-
from typing_extensions import TypeAlias
|
|
18
17
|
|
|
19
18
|
import annofabcli
|
|
20
19
|
from annofabcli.common.cli import CommandLineWithoutWebapi, get_json_from_args, get_list_from_args
|
|
@@ -54,9 +53,9 @@ class ResultDataframe:
|
|
|
54
53
|
class ThresholdInfo(DataClassJsonMixin):
|
|
55
54
|
"""閾値の情報"""
|
|
56
55
|
|
|
57
|
-
threshold_worktime:
|
|
56
|
+
threshold_worktime: float | None = None
|
|
58
57
|
"""作業時間の閾値。指定した時間以下の作業者は除外する。"""
|
|
59
|
-
threshold_task_count:
|
|
58
|
+
threshold_task_count: int | None = None
|
|
60
59
|
"""作業したタスク数の閾値。作業したタスク数が指定した数以下作業者は除外する。"""
|
|
61
60
|
|
|
62
61
|
|
|
@@ -164,12 +163,12 @@ class CollectingPerformanceInfo:
|
|
|
164
163
|
def __init__(
|
|
165
164
|
self,
|
|
166
165
|
*,
|
|
167
|
-
productivity_indicator:
|
|
168
|
-
quality_indicator:
|
|
169
|
-
threshold_info:
|
|
170
|
-
productivity_indicator_by_directory:
|
|
171
|
-
quality_indicator_by_directory:
|
|
172
|
-
threshold_infos_by_directory:
|
|
166
|
+
productivity_indicator: ProductivityIndicator | None = None,
|
|
167
|
+
quality_indicator: QualityIndicator | None = None,
|
|
168
|
+
threshold_info: ThresholdInfo | None = None,
|
|
169
|
+
productivity_indicator_by_directory: ProductivityIndicatorByDirectory | None = None,
|
|
170
|
+
quality_indicator_by_directory: QualityIndicatorByDirectory | None = None,
|
|
171
|
+
threshold_infos_by_directory: ThresholdInfoSettings | None = None,
|
|
173
172
|
) -> None:
|
|
174
173
|
self.quality_indicator = quality_indicator if quality_indicator is not None else QualityIndicator("pointed_out_inspection_comment_count/annotation_count")
|
|
175
174
|
self.productivity_indicator = productivity_indicator if productivity_indicator is not None else ProductivityIndicator("actual_worktime_hour/annotation_count")
|
|
@@ -311,7 +310,7 @@ class CollectingPerformanceInfo:
|
|
|
311
310
|
self,
|
|
312
311
|
df_user: pandas.DataFrame,
|
|
313
312
|
target_dir: Path,
|
|
314
|
-
custom_production_volume_list_by_directory:
|
|
313
|
+
custom_production_volume_list_by_directory: dict[str, list[ProductionVolumeColumn]] | None,
|
|
315
314
|
) -> ResultDataframe:
|
|
316
315
|
"""対象ディレクトリから、評価対象の指標になる情報を取得します。"""
|
|
317
316
|
df_annotation_productivity = df_user
|
|
@@ -332,13 +331,7 @@ class CollectingPerformanceInfo:
|
|
|
332
331
|
)
|
|
333
332
|
project_dir_list.append(project_dir)
|
|
334
333
|
|
|
335
|
-
|
|
336
|
-
project_info = project_dir.read_project_info()
|
|
337
|
-
project_title = project_info.project_title
|
|
338
|
-
except Exception:
|
|
339
|
-
# 複数のプロジェクトをマージして生産性情報を出力した場合は、`project_info.json`は存在しないので、このブロックに入る
|
|
340
|
-
logger.info(f"'{project_dir}'からプロジェクト情報を読み込むのに失敗しました。project_titleは空文字にします。", exc_info=True)
|
|
341
|
-
project_title = ""
|
|
334
|
+
project_title = project_dir.get_project_title()
|
|
342
335
|
|
|
343
336
|
try:
|
|
344
337
|
user_performance = project_dir.read_user_performance()
|
|
@@ -395,7 +388,7 @@ def to_rank(series: pandas.Series) -> pandas.Series:
|
|
|
395
388
|
return pandas.Series([numpy.nan] * len(series))
|
|
396
389
|
|
|
397
390
|
|
|
398
|
-
def to_deviation(series: pandas.Series, threshold_deviation_user_count:
|
|
391
|
+
def to_deviation(series: pandas.Series, threshold_deviation_user_count: int | None = None) -> pandas.Series:
|
|
399
392
|
if series.count() == 0 or (threshold_deviation_user_count is not None and series.count() <= threshold_deviation_user_count):
|
|
400
393
|
return pandas.Series([numpy.nan] * len(series))
|
|
401
394
|
else:
|
|
@@ -407,7 +400,7 @@ def to_deviation(series: pandas.Series, threshold_deviation_user_count: Optional
|
|
|
407
400
|
return series.map(lambda x: 50 if not numpy.isnan(x) else numpy.nan)
|
|
408
401
|
|
|
409
402
|
|
|
410
|
-
def create_rank_df(df: pandas.DataFrame, *, user_ids:
|
|
403
|
+
def create_rank_df(df: pandas.DataFrame, *, user_ids: Collection[str] | None = None) -> pandas.DataFrame:
|
|
411
404
|
df_rank = df.copy()
|
|
412
405
|
for col in df.columns[3:]:
|
|
413
406
|
df_rank[col] = to_rank(df[col])
|
|
@@ -421,8 +414,8 @@ def create_rank_df(df: pandas.DataFrame, *, user_ids: Optional[Collection[str]]
|
|
|
421
414
|
def create_deviation_df(
|
|
422
415
|
df: pandas.DataFrame,
|
|
423
416
|
*,
|
|
424
|
-
threshold_deviation_user_count:
|
|
425
|
-
user_ids:
|
|
417
|
+
threshold_deviation_user_count: int | None = None,
|
|
418
|
+
user_ids: Collection[str] | None = None,
|
|
426
419
|
) -> pandas.DataFrame:
|
|
427
420
|
"""偏差値が格納されたDataFrameを返す。
|
|
428
421
|
|
|
@@ -505,7 +498,7 @@ def create_custom_production_volume_by_directory(cli_value: str) -> dict[str, li
|
|
|
505
498
|
|
|
506
499
|
|
|
507
500
|
class WritingCsv:
|
|
508
|
-
def __init__(self, threshold_deviation_user_count:
|
|
501
|
+
def __init__(self, threshold_deviation_user_count: int | None = None, user_ids: Collection[str] | None = None) -> None:
|
|
509
502
|
self.threshold_deviation_user_count = threshold_deviation_user_count
|
|
510
503
|
self.user_ids = user_ids
|
|
511
504
|
|
|
@@ -535,7 +528,7 @@ class WritingCsv:
|
|
|
535
528
|
|
|
536
529
|
|
|
537
530
|
def create_productivity_indicator_by_directory(
|
|
538
|
-
value:
|
|
531
|
+
value: str | None,
|
|
539
532
|
) -> ProductivityIndicatorByDirectory:
|
|
540
533
|
"""
|
|
541
534
|
コマンドライン引数`--productivity_indicator_by_directory`から渡された文字列を、ProductivityIndicatorByDirectoryに変換する。
|
|
@@ -551,7 +544,7 @@ def create_productivity_indicator_by_directory(
|
|
|
551
544
|
|
|
552
545
|
|
|
553
546
|
def create_quality_indicator_by_directory(
|
|
554
|
-
value:
|
|
547
|
+
value: str | None,
|
|
555
548
|
) -> QualityIndicatorByDirectory:
|
|
556
549
|
"""
|
|
557
550
|
コマンドライン引数`--quality_indicator_by_directory`から渡された文字列を、ProductivityIndicatorByDirectoryに変換する。
|
|
@@ -566,7 +559,7 @@ def create_quality_indicator_by_directory(
|
|
|
566
559
|
return result
|
|
567
560
|
|
|
568
561
|
|
|
569
|
-
def create_threshold_infos_per_project(value:
|
|
562
|
+
def create_threshold_infos_per_project(value: str | None) -> ThresholdInfoSettings:
|
|
570
563
|
if value is None:
|
|
571
564
|
return {}
|
|
572
565
|
|
|
@@ -729,7 +722,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
729
722
|
WritePerformanceRatingCsv(args).main()
|
|
730
723
|
|
|
731
724
|
|
|
732
|
-
def add_parser(subparsers:
|
|
725
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
733
726
|
subcommand_name = "write_performance_rating_csv"
|
|
734
727
|
subcommand_help = "プロジェクトごとユーザごとにパフォーマンスを評価できる複数のCSVを出力します。"
|
|
735
728
|
description = "プロジェクトごとユーザごとにパフォーマンスを評価できる複数のCSVを出力します。"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import numpy
|
|
5
4
|
import pandas
|
|
@@ -45,8 +44,8 @@ def create_histogram_figure(
|
|
|
45
44
|
*,
|
|
46
45
|
x_axis_label: str,
|
|
47
46
|
y_axis_label: str,
|
|
48
|
-
title:
|
|
49
|
-
sub_title:
|
|
47
|
+
title: str | None = None,
|
|
48
|
+
sub_title: str | None = None,
|
|
50
49
|
width: int = 400,
|
|
51
50
|
height: int = 300,
|
|
52
51
|
) -> figure:
|
|
@@ -64,11 +63,11 @@ def create_histogram_figure(
|
|
|
64
63
|
height: グラフの高さ
|
|
65
64
|
"""
|
|
66
65
|
df_histogram = pandas.DataFrame({"frequency": hist, "left": bin_edges[:-1], "right": bin_edges[1:]})
|
|
67
|
-
df_histogram["interval"] = [f"{left:.1f} to {right:.1f}" for left, right in zip(df_histogram["left"], df_histogram["right"])]
|
|
68
|
-
df_histogram["width"] = [f"{(right - left):.1f}" for left, right in zip(df_histogram["left"], df_histogram["right"])]
|
|
66
|
+
df_histogram["interval"] = [f"{left:.1f} to {right:.1f}" for left, right in zip(df_histogram["left"], df_histogram["right"], strict=False)]
|
|
67
|
+
df_histogram["width"] = [f"{(right - left):.1f}" for left, right in zip(df_histogram["left"], df_histogram["right"], strict=False)]
|
|
69
68
|
|
|
70
69
|
source = ColumnDataSource(df_histogram)
|
|
71
|
-
fig = figure(
|
|
70
|
+
fig = figure( # type: ignore[call-arg]
|
|
72
71
|
width=width,
|
|
73
72
|
height=height,
|
|
74
73
|
x_axis_label=x_axis_label,
|
|
@@ -7,10 +7,9 @@ from __future__ import annotations
|
|
|
7
7
|
import copy
|
|
8
8
|
import logging
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
import bokeh
|
|
13
|
-
import bokeh.layouts
|
|
14
13
|
import bokeh.palettes
|
|
15
14
|
import pandas
|
|
16
15
|
from bokeh.models import CrosshairTool, CustomJS, DataRange1d, HoverTool, LinearAxis
|
|
@@ -50,7 +49,7 @@ class LineGraph:
|
|
|
50
49
|
"""マーカーのサイズ"""
|
|
51
50
|
|
|
52
51
|
@staticmethod
|
|
53
|
-
def _create_hover_tool(tool_tip_items:
|
|
52
|
+
def _create_hover_tool(tool_tip_items: list[str] | None = None) -> HoverTool:
|
|
54
53
|
"""
|
|
55
54
|
HoverTool用のオブジェクトを生成する。
|
|
56
55
|
"""
|
|
@@ -69,10 +68,10 @@ class LineGraph:
|
|
|
69
68
|
y_axis_label: str,
|
|
70
69
|
width: int = 1200,
|
|
71
70
|
height: int = 1000,
|
|
72
|
-
tooltip_columns:
|
|
71
|
+
tooltip_columns: list[str] | None = None,
|
|
73
72
|
**figure_kwargs, # noqa: ANN003
|
|
74
73
|
) -> None:
|
|
75
|
-
fig = figure(
|
|
74
|
+
fig = figure( # type: ignore[call-arg]
|
|
76
75
|
title=title,
|
|
77
76
|
x_axis_label=x_axis_label,
|
|
78
77
|
y_axis_label=y_axis_label,
|
|
@@ -101,8 +100,8 @@ class LineGraph:
|
|
|
101
100
|
self,
|
|
102
101
|
axis_label: str,
|
|
103
102
|
*,
|
|
104
|
-
secondary_y_axis_range:
|
|
105
|
-
primary_y_axis_range:
|
|
103
|
+
secondary_y_axis_range: DataRange1d | None = None,
|
|
104
|
+
primary_y_axis_range: DataRange1d | None = None,
|
|
106
105
|
):
|
|
107
106
|
"""
|
|
108
107
|
第2のY軸を追加する。
|
|
@@ -124,12 +123,12 @@ class LineGraph:
|
|
|
124
123
|
"right",
|
|
125
124
|
)
|
|
126
125
|
if secondary_y_axis_range is not None:
|
|
127
|
-
self.figure.extra_y_ranges = {self._SECONDARY_Y_RANGE_NAME: secondary_y_axis_range}
|
|
126
|
+
self.figure.extra_y_ranges = {self._SECONDARY_Y_RANGE_NAME: secondary_y_axis_range}
|
|
128
127
|
else:
|
|
129
|
-
self.figure.extra_y_ranges = {self._SECONDARY_Y_RANGE_NAME: DataRange1d()}
|
|
128
|
+
self.figure.extra_y_ranges = {self._SECONDARY_Y_RANGE_NAME: DataRange1d()}
|
|
130
129
|
|
|
131
130
|
if primary_y_axis_range is not None:
|
|
132
|
-
self.figure.y_range = primary_y_axis_range
|
|
131
|
+
self.figure.y_range = primary_y_axis_range
|
|
133
132
|
|
|
134
133
|
self.exists_secondary_y_axis = True
|
|
135
134
|
|
|
@@ -140,7 +139,7 @@ class LineGraph:
|
|
|
140
139
|
y_column: str,
|
|
141
140
|
*,
|
|
142
141
|
legend_label: str,
|
|
143
|
-
color:
|
|
142
|
+
color: Any | None = None, # noqa: ANN401
|
|
144
143
|
is_secondary_y_axis: bool = False,
|
|
145
144
|
**kwargs, # noqa: ANN003
|
|
146
145
|
) -> tuple[GlyphRenderer, GlyphRenderer]:
|
|
@@ -189,10 +188,10 @@ class LineGraph:
|
|
|
189
188
|
y_column: str,
|
|
190
189
|
*,
|
|
191
190
|
legend_label: str,
|
|
192
|
-
color:
|
|
191
|
+
color: Any | None = None, # noqa: ANN401
|
|
193
192
|
is_secondary_y_axis: bool = False,
|
|
194
193
|
**kwargs, # noqa: ANN003
|
|
195
|
-
) ->
|
|
194
|
+
) -> GlyphRenderer:
|
|
196
195
|
"""
|
|
197
196
|
移動平均用の折れ線を追加する
|
|
198
197
|
"""
|
|
@@ -317,7 +316,7 @@ class LineGraph:
|
|
|
317
316
|
"""
|
|
318
317
|
code = code % (self.DEFAULT_LINE_WIDTH)
|
|
319
318
|
options = [(username, f"{user_id}:{username}") for user_id, username in users]
|
|
320
|
-
multi_choice = MultiChoice(options=options, title="Find User:", width=300)
|
|
319
|
+
multi_choice = MultiChoice(options=options, title="Find User:", width=300) # type: ignore[arg-type]
|
|
321
320
|
multi_choice.js_on_change(
|
|
322
321
|
"value",
|
|
323
322
|
CustomJS(code=code, args=args),
|
|
@@ -8,7 +8,7 @@ import zipfile
|
|
|
8
8
|
from collections.abc import Collection, Iterator
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
12
12
|
|
|
13
13
|
import numpy
|
|
14
14
|
import pandas
|
|
@@ -20,8 +20,8 @@ from annofabapi.parser import (
|
|
|
20
20
|
)
|
|
21
21
|
from annofabapi.segmentation import read_binary_image
|
|
22
22
|
from dataclasses_json import DataClassJsonMixin
|
|
23
|
+
from shapely.geometry import Polygon
|
|
23
24
|
|
|
24
|
-
import annofabcli
|
|
25
25
|
import annofabcli.common.cli
|
|
26
26
|
from annofabcli.common.cli import (
|
|
27
27
|
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
@@ -64,7 +64,7 @@ class AnnotationAreaInfo(DataClassJsonMixin):
|
|
|
64
64
|
input_data_id: str
|
|
65
65
|
input_data_name: str
|
|
66
66
|
|
|
67
|
-
updated_datetime:
|
|
67
|
+
updated_datetime: str | None
|
|
68
68
|
"""アノテーションJSONに格納されているアノテーションの更新日時"""
|
|
69
69
|
|
|
70
70
|
label: str
|
|
@@ -83,6 +83,30 @@ def calculate_segmentation_area(outer_file: Path) -> int:
|
|
|
83
83
|
return int(numpy.count_nonzero(nd_array))
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def calculate_polygon_area(points: list[dict[str, int]]) -> int:
|
|
87
|
+
"""
|
|
88
|
+
Shapelyを使ってポリゴンの面積を計算する
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
points: ポリゴンの頂点座標のリスト [{"x": int, "y": int}, ...]
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
int: ポリゴンの面積(ピクセル単位)
|
|
95
|
+
"""
|
|
96
|
+
if len(points) < 3:
|
|
97
|
+
# Annofabはポリゴンとポリラインの区別がないため、点が3個未満の場合はあり得る
|
|
98
|
+
# したがって、assertなどではなく、面積0を返す
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
# Shapelyのポリゴンオブジェクトを作成
|
|
102
|
+
coords = [(point["x"], point["y"]) for point in points]
|
|
103
|
+
polygon = Polygon(coords)
|
|
104
|
+
|
|
105
|
+
# 面積を計算(自己交差がある場合も適切に処理される)
|
|
106
|
+
area = polygon.area
|
|
107
|
+
return round(area)
|
|
108
|
+
|
|
109
|
+
|
|
86
110
|
def get_annotation_area_info_list(parser: SimpleAnnotationParser, simple_annotation: dict[str, Any]) -> list[AnnotationAreaInfo]:
|
|
87
111
|
result = []
|
|
88
112
|
for detail in simple_annotation["details"]:
|
|
@@ -90,7 +114,8 @@ def get_annotation_area_info_list(parser: SimpleAnnotationParser, simple_annotat
|
|
|
90
114
|
annotation_area = calculate_segmentation_area(parser.open_outer_file(detail["data"]["data_uri"]))
|
|
91
115
|
elif detail["data"]["_type"] == "BoundingBox":
|
|
92
116
|
annotation_area = calculate_bounding_box_area(detail["data"])
|
|
93
|
-
|
|
117
|
+
elif detail["data"]["_type"] == "Points":
|
|
118
|
+
annotation_area = calculate_polygon_area(detail["data"]["points"])
|
|
94
119
|
else:
|
|
95
120
|
continue
|
|
96
121
|
|
|
@@ -116,8 +141,8 @@ def get_annotation_area_info_list(parser: SimpleAnnotationParser, simple_annotat
|
|
|
116
141
|
def get_annotation_area_info_list_from_annotation_path(
|
|
117
142
|
annotation_path: Path,
|
|
118
143
|
*,
|
|
119
|
-
target_task_ids:
|
|
120
|
-
task_query:
|
|
144
|
+
target_task_ids: Collection[str] | None = None,
|
|
145
|
+
task_query: TaskQuery | None = None,
|
|
121
146
|
) -> list[AnnotationAreaInfo]:
|
|
122
147
|
annotation_area_list = []
|
|
123
148
|
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
@@ -166,8 +191,8 @@ def print_annotation_area(
|
|
|
166
191
|
output_file: Path,
|
|
167
192
|
output_format: FormatArgument,
|
|
168
193
|
*,
|
|
169
|
-
target_task_ids:
|
|
170
|
-
task_query:
|
|
194
|
+
target_task_ids: Collection[str] | None = None,
|
|
195
|
+
task_query: TaskQuery | None = None,
|
|
171
196
|
) -> None:
|
|
172
197
|
annotation_area_list = get_annotation_area_info_list_from_annotation_path(
|
|
173
198
|
annotation_path,
|
|
@@ -175,7 +200,7 @@ def print_annotation_area(
|
|
|
175
200
|
task_query=task_query,
|
|
176
201
|
)
|
|
177
202
|
|
|
178
|
-
logger.info(f"{len(annotation_area_list)}
|
|
203
|
+
logger.info(f"{len(annotation_area_list)} 件のタスクに含まれる塗りつぶし、矩形、ポリゴンアノテーションの面積情報を出力します。")
|
|
179
204
|
|
|
180
205
|
if output_format == FormatArgument.CSV:
|
|
181
206
|
df = create_df(annotation_area_list)
|
|
@@ -211,7 +236,7 @@ class ListAnnotationArea(CommandLine):
|
|
|
211
236
|
if not self.validate(args):
|
|
212
237
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
213
238
|
|
|
214
|
-
project_id:
|
|
239
|
+
project_id: str | None = args.project_id
|
|
215
240
|
if project_id is not None:
|
|
216
241
|
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
217
242
|
project, _ = self.service.api.get_project(project_id)
|
|
@@ -228,7 +253,7 @@ class ListAnnotationArea(CommandLine):
|
|
|
228
253
|
|
|
229
254
|
downloading_obj = DownloadingFile(self.service)
|
|
230
255
|
|
|
231
|
-
def download_and_print_annotation_area(project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path:
|
|
256
|
+
def download_and_print_annotation_area(project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path: Path | None) -> None:
|
|
232
257
|
if annotation_path is None:
|
|
233
258
|
annotation_path = downloading_obj.download_annotation_zip_to_dir(
|
|
234
259
|
project_id,
|
|
@@ -309,9 +334,9 @@ def main(args: argparse.Namespace) -> None:
|
|
|
309
334
|
ListAnnotationArea(service, facade, args).main()
|
|
310
335
|
|
|
311
336
|
|
|
312
|
-
def add_parser(subparsers:
|
|
337
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
313
338
|
subcommand_name = "list_annotation_area"
|
|
314
|
-
subcommand_help = "
|
|
339
|
+
subcommand_help = "塗りつぶし、矩形、ポリゴンアノテーションの面積を出力します。"
|
|
315
340
|
epilog = "オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
316
341
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
317
342
|
parse_args(parser)
|