annofabcli 1.102.1__py3-none-any.whl → 1.104.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 -1
- annofabcli/annotation/annotation_query.py +9 -29
- annofabcli/annotation/change_annotation_attributes.py +6 -14
- annofabcli/annotation/change_annotation_properties.py +5 -12
- annofabcli/annotation/copy_annotation.py +4 -10
- annofabcli/annotation/delete_annotation.py +10 -26
- annofabcli/annotation/dump_annotation.py +1 -4
- annofabcli/annotation/import_annotation.py +15 -39
- annofabcli/annotation/list_annotation.py +1 -4
- annofabcli/annotation/merge_segmentation.py +5 -15
- annofabcli/annotation/remove_segmentation_overlap.py +8 -29
- annofabcli/annotation/restore_annotation.py +3 -9
- annofabcli/annotation_specs/add_attribute_restriction.py +2 -8
- annofabcli/annotation_specs/attribute_restriction.py +2 -10
- annofabcli/annotation_specs/export_annotation_specs.py +1 -3
- annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -10
- annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +4 -10
- annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +1 -3
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +7 -18
- annofabcli/annotation_specs/list_annotation_specs_choice.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
- annofabcli/annotation_specs/list_annotation_specs_label.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +4 -9
- annofabcli/annotation_specs/list_attribute_restriction.py +3 -9
- annofabcli/annotation_specs/put_label_color.py +1 -6
- annofabcli/comment/delete_comment.py +3 -9
- annofabcli/comment/list_all_comment.py +15 -5
- annofabcli/comment/list_comment.py +46 -7
- annofabcli/comment/put_comment.py +4 -13
- annofabcli/comment/put_comment_simply.py +2 -6
- annofabcli/comment/put_inspection_comment.py +2 -6
- annofabcli/comment/put_inspection_comment_simply.py +3 -6
- annofabcli/comment/put_onhold_comment.py +2 -6
- annofabcli/comment/put_onhold_comment_simply.py +2 -4
- annofabcli/common/cli.py +5 -43
- annofabcli/common/download.py +8 -25
- annofabcli/common/image.py +3 -7
- annofabcli/common/utils.py +2 -4
- annofabcli/common/visualize.py +2 -4
- annofabcli/filesystem/draw_annotation.py +6 -18
- annofabcli/filesystem/filter_annotation.py +7 -24
- annofabcli/filesystem/mask_user_info.py +2 -5
- annofabcli/filesystem/merge_annotation.py +2 -6
- annofabcli/input_data/change_input_data_name.py +3 -7
- annofabcli/input_data/copy_input_data.py +6 -14
- annofabcli/input_data/delete_input_data.py +7 -24
- annofabcli/input_data/delete_metadata_key_of_input_data.py +5 -16
- annofabcli/input_data/list_all_input_data.py +5 -14
- annofabcli/input_data/list_all_input_data_merged_task.py +8 -23
- annofabcli/input_data/list_input_data.py +5 -16
- annofabcli/input_data/put_input_data.py +7 -19
- annofabcli/input_data/update_metadata_of_input_data.py +6 -14
- annofabcli/instruction/list_instruction_history.py +0 -1
- annofabcli/instruction/upload_instruction.py +4 -7
- annofabcli/job/list_job.py +2 -3
- annofabcli/job/list_last_job.py +1 -3
- annofabcli/organization/list_organization.py +0 -1
- annofabcli/organization_member/change_organization_member.py +1 -3
- annofabcli/organization_member/delete_organization_member.py +2 -6
- annofabcli/organization_member/invite_organization_member.py +1 -3
- annofabcli/organization_member/list_organization_member.py +0 -1
- annofabcli/project/change_organization_of_project.py +257 -0
- annofabcli/project/change_project_status.py +2 -2
- annofabcli/project/copy_project.py +2 -7
- annofabcli/project/diff_projects.py +4 -16
- annofabcli/project/list_project.py +0 -1
- annofabcli/project/put_project.py +2 -6
- annofabcli/project/subcommand_project.py +2 -0
- annofabcli/project_member/change_project_members.py +1 -1
- annofabcli/project_member/copy_project_members.py +2 -7
- annofabcli/project_member/drop_project_members.py +1 -3
- annofabcli/project_member/invite_project_members.py +2 -4
- annofabcli/project_member/list_users.py +0 -1
- annofabcli/project_member/put_project_members.py +4 -12
- annofabcli/stat_visualization/mask_visualization_dir.py +6 -16
- annofabcli/stat_visualization/merge_visualization_dir.py +7 -19
- annofabcli/stat_visualization/summarize_whole_performance_csv.py +3 -7
- annofabcli/stat_visualization/write_graph.py +5 -15
- annofabcli/stat_visualization/write_performance_rating_csv.py +4 -12
- annofabcli/statistics/list_annotation_area.py +3 -7
- annofabcli/statistics/list_annotation_attribute.py +6 -15
- annofabcli/statistics/list_annotation_attribute_filled_count.py +9 -23
- annofabcli/statistics/list_annotation_count.py +18 -44
- annofabcli/statistics/list_annotation_duration.py +14 -40
- annofabcli/statistics/list_video_duration.py +2 -3
- annofabcli/statistics/list_worktime.py +0 -1
- annofabcli/statistics/scatter.py +3 -9
- annofabcli/statistics/summarize_task_count.py +7 -12
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +3 -11
- annofabcli/statistics/summarize_task_count_by_user.py +1 -5
- annofabcli/statistics/visualization/dataframe/annotation_count.py +2 -4
- annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +6 -12
- annofabcli/statistics/visualization/dataframe/productivity_per_date.py +10 -22
- annofabcli/statistics/visualization/dataframe/project_performance.py +1 -3
- annofabcli/statistics/visualization/dataframe/task.py +2 -5
- annofabcli/statistics/visualization/dataframe/task_history.py +1 -1
- annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +6 -20
- annofabcli/statistics/visualization/dataframe/user_performance.py +29 -88
- annofabcli/statistics/visualization/dataframe/whole_performance.py +6 -12
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +17 -49
- annofabcli/statistics/visualization/dataframe/worktime_per_date.py +4 -10
- annofabcli/statistics/visualization/filtering_query.py +2 -6
- annofabcli/statistics/visualization/project_dir.py +9 -26
- annofabcli/statistics/visualization/visualization_source_files.py +3 -10
- annofabcli/statistics/visualize_annotation_count.py +9 -23
- annofabcli/statistics/visualize_annotation_duration.py +5 -15
- annofabcli/statistics/visualize_statistics.py +18 -53
- annofabcli/statistics/visualize_video_duration.py +8 -19
- annofabcli/supplementary/delete_supplementary_data.py +7 -23
- annofabcli/supplementary/list_supplementary_data.py +1 -1
- annofabcli/supplementary/put_supplementary_data.py +5 -15
- annofabcli/task/cancel_acceptance.py +3 -4
- annofabcli/task/change_operator.py +3 -11
- annofabcli/task/change_status_to_break.py +1 -1
- annofabcli/task/change_status_to_on_hold.py +5 -18
- annofabcli/task/complete_tasks.py +8 -25
- annofabcli/task/copy_tasks.py +2 -3
- annofabcli/task/delete_metadata_key_of_task.py +2 -6
- annofabcli/task/delete_tasks.py +8 -26
- annofabcli/task/list_all_tasks.py +2 -4
- annofabcli/task/list_tasks.py +3 -7
- annofabcli/task/list_tasks_added_task_history.py +7 -21
- annofabcli/task/put_tasks.py +2 -3
- annofabcli/task/put_tasks_by_count.py +3 -7
- annofabcli/task/reject_tasks.py +7 -19
- annofabcli/task/update_metadata_of_task.py +2 -2
- annofabcli/task_history/list_all_task_history.py +2 -5
- annofabcli/task_history/list_task_history.py +0 -1
- annofabcli/task_history_event/list_all_task_history_event.py +4 -11
- annofabcli/task_history_event/list_worktime.py +4 -14
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/METADATA +1 -1
- annofabcli-1.104.0.dist-info/RECORD +215 -0
- annofabcli-1.102.1.dist-info/RECORD +0 -214
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -51,9 +51,7 @@ class DropProjectMembersMain:
|
|
|
51
51
|
self.service.api.put_project_member(project_id, user_id, request_body=request_body)
|
|
52
52
|
logger.debug(f"プロジェクト'{project_title}'(project_id='{project_id}') から、user_id='{user_id}'のユーザーを脱退させました。")
|
|
53
53
|
except requests.HTTPError:
|
|
54
|
-
logger.warning(
|
|
55
|
-
f"プロジェクト'{project_title}'(project_id='{project_id}') から、user_id='{user_id}'のユーザーを脱退させられませんでした。"
|
|
56
|
-
)
|
|
54
|
+
logger.warning(f"プロジェクト'{project_title}'(project_id='{project_id}') から、user_id='{user_id}'のユーザーを脱退させられませんでした。")
|
|
57
55
|
|
|
58
56
|
def drop_role_with_organization(self, organization_name: str, user_id_list: list[str]): # noqa: ANN201
|
|
59
57
|
projects = self.service.wrapper.get_all_projects_of_organization(organization_name, query_params={"account_id": self.service.api.account_id})
|
|
@@ -42,7 +42,7 @@ class InviteProjectMemberMain:
|
|
|
42
42
|
# プロジェクトメンバを追加/更新する
|
|
43
43
|
for user_id in user_id_list:
|
|
44
44
|
dest_member = get_project_member(user_id)
|
|
45
|
-
if dest_member is not None:
|
|
45
|
+
if dest_member is not None:
|
|
46
46
|
last_updated_datetime = dest_member["updated_datetime"]
|
|
47
47
|
else:
|
|
48
48
|
last_updated_datetime = None
|
|
@@ -131,9 +131,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
131
131
|
nargs="+",
|
|
132
132
|
help="招待するプロジェクトのproject_idを指定してください。 ``file://`` を先頭に付けると、一覧が記載されたファイルを指定できます。",
|
|
133
133
|
)
|
|
134
|
-
assign_group.add_argument(
|
|
135
|
-
"-org", "--organization", type=str, help="組織名を指定すると、組織配下のすべてのプロジェクト(自分が所属している)に招待します。"
|
|
136
|
-
)
|
|
134
|
+
assign_group.add_argument("-org", "--organization", type=str, help="組織名を指定すると、組織配下のすべてのプロジェクト(自分が所属している)に招待します。")
|
|
137
135
|
|
|
138
136
|
parser.set_defaults(subcommand_func=main)
|
|
139
137
|
|
|
@@ -97,9 +97,7 @@ class PutProjectMembers(CommandLine):
|
|
|
97
97
|
logger.warning(f"ユーザ '{member.user_id}' は、'{organization_name}' 組織の組織メンバでないため、登録できませんでした。")
|
|
98
98
|
continue
|
|
99
99
|
|
|
100
|
-
message_for_confirm =
|
|
101
|
-
f"ユーザ '{member.user_id}'を、プロジェクト'{project_title}'のメンバーに登録しますか? member_role='{member.member_role.value}'"
|
|
102
|
-
)
|
|
100
|
+
message_for_confirm = f"ユーザ '{member.user_id}'を、プロジェクト'{project_title}'のメンバーに登録しますか? member_role='{member.member_role.value}'"
|
|
103
101
|
if not self.confirm_processing(message_for_confirm):
|
|
104
102
|
continue
|
|
105
103
|
|
|
@@ -110,9 +108,7 @@ class PutProjectMembers(CommandLine):
|
|
|
110
108
|
count_invite_members += 1
|
|
111
109
|
|
|
112
110
|
except requests.exceptions.HTTPError:
|
|
113
|
-
logger.warning(
|
|
114
|
-
f"プロジェクトメンバの登録に失敗しました。user_id = '{member.user_id}', member_role = '{member.member_role.value}'", exc_info=True
|
|
115
|
-
)
|
|
111
|
+
logger.warning(f"プロジェクトメンバの登録に失敗しました。user_id = '{member.user_id}', member_role = '{member.member_role.value}'", exc_info=True)
|
|
116
112
|
|
|
117
113
|
logger.info(f"プロジェクト'{project_title}' に、{count_invite_members} / {len(members)} 件のプロジェクトメンバを登録しました。")
|
|
118
114
|
|
|
@@ -120,9 +116,7 @@ class PutProjectMembers(CommandLine):
|
|
|
120
116
|
if delete:
|
|
121
117
|
user_id_list = [e.user_id for e in members]
|
|
122
118
|
# 自分自身は削除しないようにする
|
|
123
|
-
deleted_members = [
|
|
124
|
-
e for e in old_project_members if (e["user_id"] not in user_id_list and e["user_id"] != self.service.api.login_user_id)
|
|
125
|
-
]
|
|
119
|
+
deleted_members = [e for e in old_project_members if (e["user_id"] not in user_id_list and e["user_id"] != self.service.api.login_user_id)]
|
|
126
120
|
|
|
127
121
|
count_delete_members = 0
|
|
128
122
|
logger.info(f"プロジェクト '{project_title}' から、{len(deleted_members)} 件のプロジェクトメンバを削除します。")
|
|
@@ -182,9 +176,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
182
176
|
),
|
|
183
177
|
)
|
|
184
178
|
|
|
185
|
-
parser.add_argument(
|
|
186
|
-
"--delete", action="store_true", help="CSVファイルに記載されていないプロジェクトメンバを削除します。ただし自分自身は削除しません。"
|
|
187
|
-
)
|
|
179
|
+
parser.add_argument("--delete", action="store_true", help="CSVファイルに記載されていないプロジェクトメンバを削除します。ただし自分自身は削除しません。")
|
|
188
180
|
|
|
189
181
|
parser.set_defaults(subcommand_func=main)
|
|
190
182
|
|
|
@@ -70,13 +70,9 @@ def create_replacement_dict(
|
|
|
70
70
|
not_masked_biography_set: マスクしないbiographyの集合。指定したbiographyに該当するユーザーのuser_id,username,account_idはマスクしません。
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
|
-
assert {"user_id", "username", "account_id", "biography"} - set(df_user.columns) == set(),
|
|
74
|
-
"df_userには'user_id','username','account_id','biography'の列が必要です。"
|
|
75
|
-
)
|
|
73
|
+
assert {"user_id", "username", "account_id", "biography"} - set(df_user.columns) == set(), "df_userには'user_id','username','account_id','biography'の列が必要です。"
|
|
76
74
|
|
|
77
|
-
replacement_dict_for_user_id = create_replacement_dict_by_user_id(
|
|
78
|
-
df_user, not_masked_biography_set=not_masked_biography_set, not_masked_user_id_set=not_masked_user_id_set
|
|
79
|
-
)
|
|
75
|
+
replacement_dict_for_user_id = create_replacement_dict_by_user_id(df_user, not_masked_biography_set=not_masked_biography_set, not_masked_user_id_set=not_masked_user_id_set)
|
|
80
76
|
|
|
81
77
|
df2 = df_user.set_index("user_id")
|
|
82
78
|
df3 = df2.loc[replacement_dict_for_user_id.keys()]
|
|
@@ -181,17 +177,13 @@ def mask_visualization_dir(
|
|
|
181
177
|
)
|
|
182
178
|
|
|
183
179
|
# CSVのユーザ情報をマスクする
|
|
184
|
-
masked_user_performance = UserPerformance.from_df_wrapper(
|
|
185
|
-
masked_worktime_per_date, masked_task_worktime_by_phase_user, task_completion_criteria=project_dir.task_completion_criteria
|
|
186
|
-
)
|
|
180
|
+
masked_user_performance = UserPerformance.from_df_wrapper(masked_worktime_per_date, masked_task_worktime_by_phase_user, task_completion_criteria=project_dir.task_completion_criteria)
|
|
187
181
|
output_project_dir.write_user_performance(masked_user_performance)
|
|
188
182
|
|
|
189
183
|
# メンバのパフォーマンスを散布図で出力する
|
|
190
184
|
output_project_dir.write_user_performance_scatter_plot(masked_user_performance)
|
|
191
185
|
|
|
192
|
-
masked_task = project_dir.read_task_list().mask_user_info(
|
|
193
|
-
to_replace_for_user_id=replacement_dict.user_id, to_replace_for_username=replacement_dict.username
|
|
194
|
-
)
|
|
186
|
+
masked_task = project_dir.read_task_list().mask_user_info(to_replace_for_user_id=replacement_dict.user_id, to_replace_for_username=replacement_dict.username)
|
|
195
187
|
output_project_dir.write_task_list(masked_task)
|
|
196
188
|
|
|
197
189
|
write_line_graph(masked_task_worktime_by_phase_user, output_project_dir, minimal_output=minimal_output)
|
|
@@ -226,9 +218,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
226
218
|
not_masked_biography_set = set(get_list_from_args(args.not_masked_biography)) if args.not_masked_biography is not None else None
|
|
227
219
|
not_masked_user_id_set = set(get_list_from_args(args.not_masked_user_id)) if args.not_masked_user_id is not None else None
|
|
228
220
|
|
|
229
|
-
custom_production_volume_list = (
|
|
230
|
-
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
231
|
-
)
|
|
221
|
+
custom_production_volume_list = create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
232
222
|
|
|
233
223
|
task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
|
|
234
224
|
input_project_dir = ProjectDir(
|
|
@@ -255,7 +245,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
255
245
|
"--dir",
|
|
256
246
|
type=Path,
|
|
257
247
|
required=True,
|
|
258
|
-
help="マスクしたいプロジェクトディレクトリを指定してください。プロジェクトディレクトリは ``annofabcli statistics visualize`` コマンドの出力結果です。",
|
|
248
|
+
help="マスクしたいプロジェクトディレクトリを指定してください。プロジェクトディレクトリは ``annofabcli statistics visualize`` コマンドの出力結果です。",
|
|
259
249
|
)
|
|
260
250
|
|
|
261
251
|
parser.add_argument(
|
|
@@ -102,15 +102,9 @@ class WritingVisualizationFile:
|
|
|
102
102
|
self.output_project_dir.write_performance_per_started_date_csv(acceptor_per_date_obj, phase=TaskPhase.ACCEPTANCE)
|
|
103
103
|
|
|
104
104
|
# 折れ線グラフを出力
|
|
105
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
109
|
-
inspector_per_date_obj, phase=TaskPhase.INSPECTION, user_id_list=self.user_id_list
|
|
110
|
-
)
|
|
111
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
112
|
-
acceptor_per_date_obj, phase=TaskPhase.ACCEPTANCE, user_id_list=self.user_id_list
|
|
113
|
-
)
|
|
105
|
+
self.output_project_dir.write_performance_line_graph_per_date(annotator_per_date_obj, phase=TaskPhase.ANNOTATION, user_id_list=self.user_id_list)
|
|
106
|
+
self.output_project_dir.write_performance_line_graph_per_date(inspector_per_date_obj, phase=TaskPhase.INSPECTION, user_id_list=self.user_id_list)
|
|
107
|
+
self.output_project_dir.write_performance_line_graph_per_date(acceptor_per_date_obj, phase=TaskPhase.ACCEPTANCE, user_id_list=self.user_id_list)
|
|
114
108
|
|
|
115
109
|
@_catch_exception
|
|
116
110
|
def write_task_list_and_histogram(self, task: Task) -> None:
|
|
@@ -153,7 +147,7 @@ class MergingVisualizationFile:
|
|
|
153
147
|
for project_dir in self.project_dir_list:
|
|
154
148
|
tmp_obj = project_dir.read_worktime_per_date_user()
|
|
155
149
|
|
|
156
|
-
if merged_obj is None:
|
|
150
|
+
if merged_obj is None:
|
|
157
151
|
merged_obj = tmp_obj
|
|
158
152
|
else:
|
|
159
153
|
merged_obj = WorktimePerDate.merge(merged_obj, tmp_obj)
|
|
@@ -188,9 +182,7 @@ class MergingVisualizationFile:
|
|
|
188
182
|
project_info = project_dir.read_project_info()
|
|
189
183
|
project_info_list.append(project_info)
|
|
190
184
|
|
|
191
|
-
merge_info = MergingInfo(
|
|
192
|
-
target_dir_list=target_dir_list, project_info_list=project_info_list, task_completion_criteria=task_completion_criteria
|
|
193
|
-
)
|
|
185
|
+
merge_info = MergingInfo(target_dir_list=target_dir_list, project_info_list=project_info_list, task_completion_criteria=task_completion_criteria)
|
|
194
186
|
return merge_info
|
|
195
187
|
|
|
196
188
|
|
|
@@ -223,9 +215,7 @@ def merge_visualization_dir( # pylint: disable=too-many-statements
|
|
|
223
215
|
worktime_per_date=worktime_per_date,
|
|
224
216
|
task_completion_criteria=task_completion_criteria,
|
|
225
217
|
)
|
|
226
|
-
writing_obj = WritingVisualizationFile(
|
|
227
|
-
output_project_dir, user_id_list=user_id_list, minimal_output=minimal_output, task_completion_criteria=task_completion_criteria
|
|
228
|
-
)
|
|
218
|
+
writing_obj = WritingVisualizationFile(output_project_dir, user_id_list=user_id_list, minimal_output=minimal_output, task_completion_criteria=task_completion_criteria)
|
|
229
219
|
|
|
230
220
|
writing_obj.write_task_list_and_histogram(task)
|
|
231
221
|
writing_obj.write_worktime_per_date(worktime_per_date)
|
|
@@ -270,9 +260,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
270
260
|
|
|
271
261
|
user_id_list = get_list_from_args(args.user_id) if args.user_id is not None else None
|
|
272
262
|
|
|
273
|
-
custom_production_volume_list = (
|
|
274
|
-
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
275
|
-
)
|
|
263
|
+
custom_production_volume_list = create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
276
264
|
task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
|
|
277
265
|
merge_visualization_dir(
|
|
278
266
|
project_dir_list=[ProjectDir(e, task_completion_criteria) for e in args.dir],
|
|
@@ -32,13 +32,9 @@ def create_custom_production_volume_list(cli_value: str) -> list[ProductionVolum
|
|
|
32
32
|
def main(args: argparse.Namespace) -> None:
|
|
33
33
|
root_dir: Path = args.dir
|
|
34
34
|
# task_completion_criteriaは何でもよいので、とりあえずACCEPTANCE_COMPLETEDを指定
|
|
35
|
-
project_dir_list = [
|
|
36
|
-
ProjectDir(elm, task_completion_criteria=TaskCompletionCriteria.ACCEPTANCE_COMPLETED) for elm in root_dir.iterdir() if elm.is_dir()
|
|
37
|
-
]
|
|
35
|
+
project_dir_list = [ProjectDir(elm, task_completion_criteria=TaskCompletionCriteria.ACCEPTANCE_COMPLETED) for elm in root_dir.iterdir() if elm.is_dir()]
|
|
38
36
|
|
|
39
|
-
custom_production_volume_list = (
|
|
40
|
-
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
41
|
-
)
|
|
37
|
+
custom_production_volume_list = create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
42
38
|
project_performance = ProjectPerformance.from_project_dirs(project_dir_list, custom_production_volume_list=custom_production_volume_list)
|
|
43
39
|
project_performance.to_csv(args.output)
|
|
44
40
|
|
|
@@ -70,7 +66,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
70
66
|
|
|
71
67
|
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
72
68
|
subcommand_name = "summarize_whole_performance_csv"
|
|
73
|
-
subcommand_help = "``annofabcli statistics visualize`` コマンドの出力結果であるプロジェクトディレクトリから、プロジェクトごとの生産性や品質の一覧を出力します。。"
|
|
69
|
+
subcommand_help = "``annofabcli statistics visualize`` コマンドの出力結果であるプロジェクトディレクトリから、プロジェクトごとの生産性や品質の一覧を出力します。。"
|
|
74
70
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help)
|
|
75
71
|
parse_args(parser)
|
|
76
72
|
return parser
|
|
@@ -66,15 +66,9 @@ class WritingGraph:
|
|
|
66
66
|
inspector_per_date_obj = InspectorProductivityPerDate.from_df_wrapper(task_worktime_list)
|
|
67
67
|
acceptor_per_date_obj = AcceptorProductivityPerDate.from_df_wrapper(task_worktime_list)
|
|
68
68
|
|
|
69
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
73
|
-
inspector_per_date_obj, phase=TaskPhase.INSPECTION, user_id_list=self.user_id_list
|
|
74
|
-
)
|
|
75
|
-
self.output_project_dir.write_performance_line_graph_per_date(
|
|
76
|
-
acceptor_per_date_obj, phase=TaskPhase.ACCEPTANCE, user_id_list=self.user_id_list
|
|
77
|
-
)
|
|
69
|
+
self.output_project_dir.write_performance_line_graph_per_date(annotator_per_date_obj, phase=TaskPhase.ANNOTATION, user_id_list=self.user_id_list)
|
|
70
|
+
self.output_project_dir.write_performance_line_graph_per_date(inspector_per_date_obj, phase=TaskPhase.INSPECTION, user_id_list=self.user_id_list)
|
|
71
|
+
self.output_project_dir.write_performance_line_graph_per_date(acceptor_per_date_obj, phase=TaskPhase.ACCEPTANCE, user_id_list=self.user_id_list)
|
|
78
72
|
|
|
79
73
|
def main(self) -> None:
|
|
80
74
|
try:
|
|
@@ -103,9 +97,7 @@ class WritingGraph:
|
|
|
103
97
|
logger.warning("'日毎の生産量と生産性.csv'から生成できるグラフの出力に失敗しました。", exc_info=True)
|
|
104
98
|
|
|
105
99
|
try:
|
|
106
|
-
self.output_project_dir.write_whole_productivity_line_graph_per_annotation_started_date(
|
|
107
|
-
self.project_dir.read_whole_productivity_per_first_annotation_started_date()
|
|
108
|
-
)
|
|
100
|
+
self.output_project_dir.write_whole_productivity_line_graph_per_annotation_started_date(self.project_dir.read_whole_productivity_per_first_annotation_started_date())
|
|
109
101
|
except Exception:
|
|
110
102
|
logger.warning("'教師付者_教師付開始日list.csv'から生成できるグラフの出力に失敗しました。", exc_info=True)
|
|
111
103
|
|
|
@@ -130,9 +122,7 @@ def create_custom_production_volume_list(cli_value: str) -> list[ProductionVolum
|
|
|
130
122
|
def main(args: argparse.Namespace) -> None:
|
|
131
123
|
user_id_list = get_list_from_args(args.user_id) if args.user_id is not None else None
|
|
132
124
|
|
|
133
|
-
custom_production_volume_list = (
|
|
134
|
-
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
135
|
-
)
|
|
125
|
+
custom_production_volume_list = create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
|
|
136
126
|
|
|
137
127
|
task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
|
|
138
128
|
input_project_dir = ProjectDir(args.dir, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
|
|
@@ -171,12 +171,8 @@ class CollectingPerformanceInfo:
|
|
|
171
171
|
quality_indicator_by_directory: Optional[QualityIndicatorByDirectory] = None,
|
|
172
172
|
threshold_infos_by_directory: Optional[ThresholdInfoSettings] = None,
|
|
173
173
|
) -> None:
|
|
174
|
-
self.quality_indicator = (
|
|
175
|
-
|
|
176
|
-
)
|
|
177
|
-
self.productivity_indicator = (
|
|
178
|
-
productivity_indicator if productivity_indicator is not None else ProductivityIndicator("actual_worktime_hour/annotation_count")
|
|
179
|
-
)
|
|
174
|
+
self.quality_indicator = quality_indicator if quality_indicator is not None else QualityIndicator("pointed_out_inspection_comment_count/annotation_count")
|
|
175
|
+
self.productivity_indicator = productivity_indicator if productivity_indicator is not None else ProductivityIndicator("actual_worktime_hour/annotation_count")
|
|
180
176
|
self.threshold_info = threshold_info if threshold_info is not None else ThresholdInfo()
|
|
181
177
|
self.threshold_infos_by_directory = threshold_infos_by_directory if threshold_infos_by_directory is not None else {}
|
|
182
178
|
self.productivity_indicator_by_directory = productivity_indicator_by_directory if productivity_indicator_by_directory is not None else {}
|
|
@@ -324,9 +320,7 @@ class CollectingPerformanceInfo:
|
|
|
324
320
|
if not p_project_dir.is_dir():
|
|
325
321
|
continue
|
|
326
322
|
|
|
327
|
-
custom_production_volume_list = (
|
|
328
|
-
custom_production_volume_list_by_directory.get(p_project_dir.name) if custom_production_volume_list_by_directory is not None else None
|
|
329
|
-
)
|
|
323
|
+
custom_production_volume_list = custom_production_volume_list_by_directory.get(p_project_dir.name) if custom_production_volume_list_by_directory is not None else None
|
|
330
324
|
project_title = p_project_dir.name
|
|
331
325
|
project_dir = ProjectDir(
|
|
332
326
|
p_project_dir,
|
|
@@ -593,9 +587,7 @@ class WritePerformanceRatingCsv(CommandLineWithoutWebapi):
|
|
|
593
587
|
user_id_list = get_list_from_args(args.user_id) if args.user_id is not None else None
|
|
594
588
|
df_user = create_user_df(target_dir)
|
|
595
589
|
custom_production_volume_by_directory = (
|
|
596
|
-
create_custom_production_volume_by_directory(args.custom_production_volume_by_directory)
|
|
597
|
-
if args.custom_production_volume_by_directory is not None
|
|
598
|
-
else None
|
|
590
|
+
create_custom_production_volume_by_directory(args.custom_production_volume_by_directory) if args.custom_production_volume_by_directory is not None else None
|
|
599
591
|
)
|
|
600
592
|
result = CollectingPerformanceInfo(
|
|
601
593
|
productivity_indicator=ProductivityIndicator(args.productivity_indicator),
|
|
@@ -238,14 +238,10 @@ class ListAnnotationArea(CommandLine):
|
|
|
238
238
|
|
|
239
239
|
if project_id is not None:
|
|
240
240
|
if args.temp_dir is not None:
|
|
241
|
-
download_and_print_annotation_area(
|
|
242
|
-
project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest, annotation_path=annotation_path
|
|
243
|
-
)
|
|
241
|
+
download_and_print_annotation_area(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest, annotation_path=annotation_path)
|
|
244
242
|
else:
|
|
245
243
|
with tempfile.TemporaryDirectory() as str_temp_dir:
|
|
246
|
-
download_and_print_annotation_area(
|
|
247
|
-
project_id=project_id, temp_dir=Path(str_temp_dir), is_latest=args.latest, annotation_path=annotation_path
|
|
248
|
-
)
|
|
244
|
+
download_and_print_annotation_area(project_id=project_id, temp_dir=Path(str_temp_dir), is_latest=args.latest, annotation_path=annotation_path)
|
|
249
245
|
else:
|
|
250
246
|
assert annotation_path is not None
|
|
251
247
|
print_annotation_area(
|
|
@@ -293,7 +289,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
293
289
|
parser.add_argument(
|
|
294
290
|
"--latest",
|
|
295
291
|
action="store_true",
|
|
296
|
-
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
292
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
297
293
|
)
|
|
298
294
|
|
|
299
295
|
parser.add_argument(
|
|
@@ -69,9 +69,7 @@ class AnnotationAttribute(pydantic.BaseModel):
|
|
|
69
69
|
attributes: dict[str, Union[str, int, bool]]
|
|
70
70
|
|
|
71
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]:
|
|
72
|
+
def get_annotation_attribute_list_from_annotation_json(simple_annotation: dict[str, Any], *, target_labels: Collection[str] | None = None) -> list[AnnotationAttribute]:
|
|
75
73
|
"""
|
|
76
74
|
1個のアノテーションJSONに対して、アノテーションの属性情報を取得します。
|
|
77
75
|
|
|
@@ -207,9 +205,7 @@ class ListAnnotationAttribute(CommandLine):
|
|
|
207
205
|
|
|
208
206
|
downloading_obj = DownloadingFile(self.service)
|
|
209
207
|
|
|
210
|
-
def download_and_print_annotation_attribute_list(
|
|
211
|
-
project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path: Optional[Path]
|
|
212
|
-
) -> None:
|
|
208
|
+
def download_and_print_annotation_attribute_list(project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path: Optional[Path]) -> None:
|
|
213
209
|
if annotation_path is None:
|
|
214
210
|
annotation_path = temp_dir / f"{project_id}__annotation.zip"
|
|
215
211
|
downloading_obj.download_annotation_zip(
|
|
@@ -225,16 +221,12 @@ class ListAnnotationAttribute(CommandLine):
|
|
|
225
221
|
|
|
226
222
|
if project_id is not None:
|
|
227
223
|
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
|
-
)
|
|
224
|
+
download_and_print_annotation_attribute_list(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest, annotation_path=annotation_path)
|
|
231
225
|
else:
|
|
232
226
|
# `NamedTemporaryFile`を使わない理由: Windowsで`PermissionError`が発生するため
|
|
233
227
|
# https://qiita.com/yuji38kwmt/items/c6f50e1fc03dafdcdda0 参考
|
|
234
228
|
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
|
-
)
|
|
229
|
+
download_and_print_annotation_attribute_list(project_id=project_id, temp_dir=Path(str_temp_dir), is_latest=args.latest, annotation_path=annotation_path)
|
|
238
230
|
else:
|
|
239
231
|
assert annotation_path is not None
|
|
240
232
|
annotation_attribute_list = get_annotation_attribute_list_from_annotation_zipdir_path(
|
|
@@ -276,14 +268,13 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
276
268
|
type=str,
|
|
277
269
|
nargs="+",
|
|
278
270
|
required=False,
|
|
279
|
-
help="出力対象のアノテーションのラベル名(英語)を指定します。指定しない場合はラベル名で絞り込みません。"
|
|
280
|
-
" ``file://`` を先頭に付けると、ラベル名の一覧が記載されたファイルを指定できます。",
|
|
271
|
+
help="出力対象のアノテーションのラベル名(英語)を指定します。指定しない場合はラベル名で絞り込みません。 ``file://`` を先頭に付けると、ラベル名の一覧が記載されたファイルを指定できます。",
|
|
281
272
|
)
|
|
282
273
|
|
|
283
274
|
parser.add_argument(
|
|
284
275
|
"--latest",
|
|
285
276
|
action="store_true",
|
|
286
|
-
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
277
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
287
278
|
)
|
|
288
279
|
|
|
289
280
|
parser.add_argument(
|
|
@@ -316,14 +316,12 @@ class AnnotationCountCsvByAttribute:
|
|
|
316
316
|
Args:
|
|
317
317
|
selective_attribute_value_max_count: 選択肢系の属性の値の個数の上限。これを超えた場合は、非選択肢系属性(トラッキングIDやアノテーションリンクなど)とみなす
|
|
318
318
|
|
|
319
|
-
"""
|
|
319
|
+
"""
|
|
320
320
|
|
|
321
321
|
def __init__(self, selective_attribute_value_max_count: int = 20) -> None:
|
|
322
322
|
self.selective_attribute_value_max_count = selective_attribute_value_max_count
|
|
323
323
|
|
|
324
|
-
def _value_columns(
|
|
325
|
-
self, annotation_count_list: Collection[HasAnnotationAttributeCounts], *, prior_attribute_columns: Optional[list[tuple[str, str, str]]]
|
|
326
|
-
) -> list[tuple[str, str, str]]:
|
|
324
|
+
def _value_columns(self, annotation_count_list: Collection[HasAnnotationAttributeCounts], *, prior_attribute_columns: Optional[list[tuple[str, str, str]]]) -> list[tuple[str, str, str]]:
|
|
327
325
|
"""
|
|
328
326
|
CSVの数値列を取得します。
|
|
329
327
|
"""
|
|
@@ -441,9 +439,7 @@ def get_frame_no_map(task_json_path: Path) -> dict[tuple[str, str], int]:
|
|
|
441
439
|
|
|
442
440
|
|
|
443
441
|
def get_attribute_columns(attribute_names: list[tuple[str, str]]) -> list[tuple[str, str, str]]:
|
|
444
|
-
attribute_columns = [
|
|
445
|
-
(label_name, attribute_name, value_type) for label_name, attribute_name in attribute_names for value_type in ["filled", "empty"]
|
|
446
|
-
]
|
|
442
|
+
attribute_columns = [(label_name, attribute_name, value_type) for label_name, attribute_name in attribute_names for value_type in ["filled", "empty"]]
|
|
447
443
|
return attribute_columns
|
|
448
444
|
|
|
449
445
|
|
|
@@ -451,9 +447,7 @@ class ListAnnotationAttributeFilledCountMain:
|
|
|
451
447
|
def __init__(self, service: annofabapi.Resource) -> None:
|
|
452
448
|
self.service = service
|
|
453
449
|
|
|
454
|
-
def print_annotation_count_csv_by_input_data(
|
|
455
|
-
self, annotation_count_list: list[AnnotationCountByInputData], output_file: Path, *, attribute_names: Optional[list[tuple[str, str]]]
|
|
456
|
-
) -> None:
|
|
450
|
+
def print_annotation_count_csv_by_input_data(self, annotation_count_list: list[AnnotationCountByInputData], output_file: Path, *, attribute_names: Optional[list[tuple[str, str]]]) -> None:
|
|
457
451
|
attribute_columns: Optional[list[tuple[str, str, str]]] = None
|
|
458
452
|
if attribute_names is not None:
|
|
459
453
|
attribute_columns = get_attribute_columns(attribute_names)
|
|
@@ -461,9 +455,7 @@ class ListAnnotationAttributeFilledCountMain:
|
|
|
461
455
|
df = AnnotationCountCsvByAttribute().create_df_by_input_data(annotation_count_list, prior_attribute_columns=attribute_columns)
|
|
462
456
|
print_csv(df, output_file)
|
|
463
457
|
|
|
464
|
-
def print_annotation_count_csv_by_task(
|
|
465
|
-
self, annotation_count_list: list[AnnotationCountByTask], output_file: Path, *, attribute_names: Optional[list[tuple[str, str]]]
|
|
466
|
-
) -> None:
|
|
458
|
+
def print_annotation_count_csv_by_task(self, annotation_count_list: list[AnnotationCountByTask], output_file: Path, *, attribute_names: Optional[list[tuple[str, str]]]) -> None:
|
|
467
459
|
attribute_columns: Optional[list[tuple[str, str, str]]] = None
|
|
468
460
|
if attribute_names is not None:
|
|
469
461
|
attribute_columns = get_attribute_columns(attribute_names)
|
|
@@ -493,9 +485,7 @@ class ListAnnotationAttributeFilledCountMain:
|
|
|
493
485
|
|
|
494
486
|
frame_no_map = get_frame_no_map(task_json_path) if task_json_path is not None else None
|
|
495
487
|
|
|
496
|
-
annotation_count_list_by_input_data = ListAnnotationCounterByInputData(
|
|
497
|
-
frame_no_map=frame_no_map, target_attribute_names=target_attribute_names
|
|
498
|
-
).get_annotation_count_list(
|
|
488
|
+
annotation_count_list_by_input_data = ListAnnotationCounterByInputData(frame_no_map=frame_no_map, target_attribute_names=target_attribute_names).get_annotation_count_list(
|
|
499
489
|
annotation_path,
|
|
500
490
|
target_task_ids=target_task_ids,
|
|
501
491
|
task_query=task_query,
|
|
@@ -504,9 +494,7 @@ class ListAnnotationAttributeFilledCountMain:
|
|
|
504
494
|
if group_by == GroupBy.INPUT_DATA_ID:
|
|
505
495
|
logger.info(f"{len(annotation_count_list_by_input_data)} 件の入力データに含まれるアノテーション数の情報を出力します。")
|
|
506
496
|
if output_format == FormatArgument.CSV:
|
|
507
|
-
self.print_annotation_count_csv_by_input_data(
|
|
508
|
-
annotation_count_list_by_input_data, output_file=output_file, attribute_names=target_attribute_names
|
|
509
|
-
)
|
|
497
|
+
self.print_annotation_count_csv_by_input_data(annotation_count_list_by_input_data, output_file=output_file, attribute_names=target_attribute_names)
|
|
510
498
|
|
|
511
499
|
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
512
500
|
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
@@ -520,9 +508,7 @@ class ListAnnotationAttributeFilledCountMain:
|
|
|
520
508
|
annotation_count_list_by_task = convert_annotation_count_list_by_input_data_to_by_task(annotation_count_list_by_input_data)
|
|
521
509
|
logger.info(f"{len(annotation_count_list_by_task)} 件のタスクに含まれるアノテーション数の情報を出力します。")
|
|
522
510
|
if output_format == FormatArgument.CSV:
|
|
523
|
-
self.print_annotation_count_csv_by_task(
|
|
524
|
-
annotation_count_list_by_task, output_file=output_file, attribute_names=target_attribute_names
|
|
525
|
-
)
|
|
511
|
+
self.print_annotation_count_csv_by_task(annotation_count_list_by_task, output_file=output_file, attribute_names=target_attribute_names)
|
|
526
512
|
|
|
527
513
|
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
528
514
|
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
@@ -669,7 +655,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
669
655
|
parser.add_argument(
|
|
670
656
|
"--latest",
|
|
671
657
|
action="store_true",
|
|
672
|
-
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
658
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
673
659
|
)
|
|
674
660
|
|
|
675
661
|
parser.set_defaults(subcommand_func=main)
|