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.
Files changed (136) hide show
  1. annofabcli/__main__.py +1 -1
  2. annofabcli/annotation/annotation_query.py +9 -29
  3. annofabcli/annotation/change_annotation_attributes.py +6 -14
  4. annofabcli/annotation/change_annotation_properties.py +5 -12
  5. annofabcli/annotation/copy_annotation.py +4 -10
  6. annofabcli/annotation/delete_annotation.py +10 -26
  7. annofabcli/annotation/dump_annotation.py +1 -4
  8. annofabcli/annotation/import_annotation.py +15 -39
  9. annofabcli/annotation/list_annotation.py +1 -4
  10. annofabcli/annotation/merge_segmentation.py +5 -15
  11. annofabcli/annotation/remove_segmentation_overlap.py +8 -29
  12. annofabcli/annotation/restore_annotation.py +3 -9
  13. annofabcli/annotation_specs/add_attribute_restriction.py +2 -8
  14. annofabcli/annotation_specs/attribute_restriction.py +2 -10
  15. annofabcli/annotation_specs/export_annotation_specs.py +1 -3
  16. annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -10
  17. annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +4 -10
  18. annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +1 -3
  19. annofabcli/annotation_specs/list_annotation_specs_attribute.py +7 -18
  20. annofabcli/annotation_specs/list_annotation_specs_choice.py +3 -8
  21. annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
  22. annofabcli/annotation_specs/list_annotation_specs_label.py +3 -8
  23. annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +4 -9
  24. annofabcli/annotation_specs/list_attribute_restriction.py +3 -9
  25. annofabcli/annotation_specs/put_label_color.py +1 -6
  26. annofabcli/comment/delete_comment.py +3 -9
  27. annofabcli/comment/list_all_comment.py +15 -5
  28. annofabcli/comment/list_comment.py +46 -7
  29. annofabcli/comment/put_comment.py +4 -13
  30. annofabcli/comment/put_comment_simply.py +2 -6
  31. annofabcli/comment/put_inspection_comment.py +2 -6
  32. annofabcli/comment/put_inspection_comment_simply.py +3 -6
  33. annofabcli/comment/put_onhold_comment.py +2 -6
  34. annofabcli/comment/put_onhold_comment_simply.py +2 -4
  35. annofabcli/common/cli.py +5 -43
  36. annofabcli/common/download.py +8 -25
  37. annofabcli/common/image.py +3 -7
  38. annofabcli/common/utils.py +2 -4
  39. annofabcli/common/visualize.py +2 -4
  40. annofabcli/filesystem/draw_annotation.py +6 -18
  41. annofabcli/filesystem/filter_annotation.py +7 -24
  42. annofabcli/filesystem/mask_user_info.py +2 -5
  43. annofabcli/filesystem/merge_annotation.py +2 -6
  44. annofabcli/input_data/change_input_data_name.py +3 -7
  45. annofabcli/input_data/copy_input_data.py +6 -14
  46. annofabcli/input_data/delete_input_data.py +7 -24
  47. annofabcli/input_data/delete_metadata_key_of_input_data.py +5 -16
  48. annofabcli/input_data/list_all_input_data.py +5 -14
  49. annofabcli/input_data/list_all_input_data_merged_task.py +8 -23
  50. annofabcli/input_data/list_input_data.py +5 -16
  51. annofabcli/input_data/put_input_data.py +7 -19
  52. annofabcli/input_data/update_metadata_of_input_data.py +6 -14
  53. annofabcli/instruction/list_instruction_history.py +0 -1
  54. annofabcli/instruction/upload_instruction.py +4 -7
  55. annofabcli/job/list_job.py +2 -3
  56. annofabcli/job/list_last_job.py +1 -3
  57. annofabcli/organization/list_organization.py +0 -1
  58. annofabcli/organization_member/change_organization_member.py +1 -3
  59. annofabcli/organization_member/delete_organization_member.py +2 -6
  60. annofabcli/organization_member/invite_organization_member.py +1 -3
  61. annofabcli/organization_member/list_organization_member.py +0 -1
  62. annofabcli/project/change_organization_of_project.py +257 -0
  63. annofabcli/project/change_project_status.py +2 -2
  64. annofabcli/project/copy_project.py +2 -7
  65. annofabcli/project/diff_projects.py +4 -16
  66. annofabcli/project/list_project.py +0 -1
  67. annofabcli/project/put_project.py +2 -6
  68. annofabcli/project/subcommand_project.py +2 -0
  69. annofabcli/project_member/change_project_members.py +1 -1
  70. annofabcli/project_member/copy_project_members.py +2 -7
  71. annofabcli/project_member/drop_project_members.py +1 -3
  72. annofabcli/project_member/invite_project_members.py +2 -4
  73. annofabcli/project_member/list_users.py +0 -1
  74. annofabcli/project_member/put_project_members.py +4 -12
  75. annofabcli/stat_visualization/mask_visualization_dir.py +6 -16
  76. annofabcli/stat_visualization/merge_visualization_dir.py +7 -19
  77. annofabcli/stat_visualization/summarize_whole_performance_csv.py +3 -7
  78. annofabcli/stat_visualization/write_graph.py +5 -15
  79. annofabcli/stat_visualization/write_performance_rating_csv.py +4 -12
  80. annofabcli/statistics/list_annotation_area.py +3 -7
  81. annofabcli/statistics/list_annotation_attribute.py +6 -15
  82. annofabcli/statistics/list_annotation_attribute_filled_count.py +9 -23
  83. annofabcli/statistics/list_annotation_count.py +18 -44
  84. annofabcli/statistics/list_annotation_duration.py +14 -40
  85. annofabcli/statistics/list_video_duration.py +2 -3
  86. annofabcli/statistics/list_worktime.py +0 -1
  87. annofabcli/statistics/scatter.py +3 -9
  88. annofabcli/statistics/summarize_task_count.py +7 -12
  89. annofabcli/statistics/summarize_task_count_by_task_id_group.py +3 -11
  90. annofabcli/statistics/summarize_task_count_by_user.py +1 -5
  91. annofabcli/statistics/visualization/dataframe/annotation_count.py +2 -4
  92. annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +6 -12
  93. annofabcli/statistics/visualization/dataframe/productivity_per_date.py +10 -22
  94. annofabcli/statistics/visualization/dataframe/project_performance.py +1 -3
  95. annofabcli/statistics/visualization/dataframe/task.py +2 -5
  96. annofabcli/statistics/visualization/dataframe/task_history.py +1 -1
  97. annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +6 -20
  98. annofabcli/statistics/visualization/dataframe/user_performance.py +29 -88
  99. annofabcli/statistics/visualization/dataframe/whole_performance.py +6 -12
  100. annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +17 -49
  101. annofabcli/statistics/visualization/dataframe/worktime_per_date.py +4 -10
  102. annofabcli/statistics/visualization/filtering_query.py +2 -6
  103. annofabcli/statistics/visualization/project_dir.py +9 -26
  104. annofabcli/statistics/visualization/visualization_source_files.py +3 -10
  105. annofabcli/statistics/visualize_annotation_count.py +9 -23
  106. annofabcli/statistics/visualize_annotation_duration.py +5 -15
  107. annofabcli/statistics/visualize_statistics.py +18 -53
  108. annofabcli/statistics/visualize_video_duration.py +8 -19
  109. annofabcli/supplementary/delete_supplementary_data.py +7 -23
  110. annofabcli/supplementary/list_supplementary_data.py +1 -1
  111. annofabcli/supplementary/put_supplementary_data.py +5 -15
  112. annofabcli/task/cancel_acceptance.py +3 -4
  113. annofabcli/task/change_operator.py +3 -11
  114. annofabcli/task/change_status_to_break.py +1 -1
  115. annofabcli/task/change_status_to_on_hold.py +5 -18
  116. annofabcli/task/complete_tasks.py +8 -25
  117. annofabcli/task/copy_tasks.py +2 -3
  118. annofabcli/task/delete_metadata_key_of_task.py +2 -6
  119. annofabcli/task/delete_tasks.py +8 -26
  120. annofabcli/task/list_all_tasks.py +2 -4
  121. annofabcli/task/list_tasks.py +3 -7
  122. annofabcli/task/list_tasks_added_task_history.py +7 -21
  123. annofabcli/task/put_tasks.py +2 -3
  124. annofabcli/task/put_tasks_by_count.py +3 -7
  125. annofabcli/task/reject_tasks.py +7 -19
  126. annofabcli/task/update_metadata_of_task.py +2 -2
  127. annofabcli/task_history/list_all_task_history.py +2 -5
  128. annofabcli/task_history/list_task_history.py +0 -1
  129. annofabcli/task_history_event/list_all_task_history_event.py +4 -11
  130. annofabcli/task_history_event/list_worktime.py +4 -14
  131. {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/METADATA +1 -1
  132. annofabcli-1.104.0.dist-info/RECORD +215 -0
  133. annofabcli-1.102.1.dist-info/RECORD +0 -214
  134. {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/WHEEL +0 -0
  135. {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/entry_points.txt +0 -0
  136. {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: # noqa: SIM108
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
 
@@ -109,7 +109,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
109
109
  default=FormatArgument.CSV,
110
110
  )
111
111
  argument_parser.add_output()
112
- argument_parser.add_csv_format()
113
112
 
114
113
  parser.set_defaults(subcommand_func=main)
115
114
 
@@ -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`` コマンドの出力結果です。", # noqa: E501
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
- annotator_per_date_obj, phase=TaskPhase.ANNOTATION, user_id_list=self.user_id_list
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: # noqa: SIM108
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`` コマンドの出力結果であるプロジェクトディレクトリから、プロジェクトごとの生産性や品質の一覧を出力します。。" # noqa: E501
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
- annotator_per_date_obj, phase=TaskPhase.ANNOTATION, user_id_list=self.user_id_list
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
- quality_indicator if quality_indicator is not None else QualityIndicator("pointed_out_inspection_comment_count/annotation_count")
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を更新するのに数分待ちます。", # noqa: E501
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を更新するのに数分待ちます。", # noqa: E501
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
- """ # noqa: E501
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を更新するのに数分待ちます。", # noqa: E501
658
+ help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
673
659
  )
674
660
 
675
661
  parser.set_defaults(subcommand_func=main)