annofabcli 1.111.0__py3-none-any.whl → 1.111.2__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.
@@ -254,8 +254,8 @@ class AnnotationConverter:
254
254
  for attribute_name, attribute_value in attributes.items():
255
255
  try:
256
256
  specs_additional_data = self.annotation_specs_accessor.get_attribute(attribute_name=attribute_name, label=label)
257
- except ValueError:
258
- logger.warning(f"アノテーション仕様に属性名(英語)が'{attribute_name}'である属性情報が存在しないか、複数存在します。 :: {log_message_suffix}")
257
+ except ValueError as e:
258
+ logger.warning(f"アノテーション仕様に属性名(英語)が'{attribute_name}'である属性情報が存在しないか、複数存在します。 :: {log_message_suffix} :: error={e}")
259
259
  if self.is_strict:
260
260
  raise
261
261
  continue
@@ -298,12 +298,11 @@ class AnnotationConverter:
298
298
  ValueError: 存在しないラベル名が指定された場合(`self.is_strict`がFalseでもraiseされる9
299
299
 
300
300
  """
301
- log_message_suffix = f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
302
301
 
303
302
  try:
304
303
  label_info = self.annotation_specs_accessor.get_label(label_name=detail.label)
305
- except ValueError:
306
- logger.warning(f"アノテーション仕様にラベル名(英語)が'{detail.label}'であるラベル情報が存在しないか、または複数存在します。 :: {log_message_suffix}")
304
+ except ValueError as e:
305
+ logger.warning(f"アノテーション仕様にラベル名(英語)が'{detail.label}'であるラベル情報が存在しないか、または複数存在します。 :: {log_message_suffix} :: error={e}")
307
306
  raise
308
307
 
309
308
  if detail.attributes is not None:
@@ -361,9 +360,12 @@ class AnnotationConverter:
361
360
  old_dict_detail[old_detail["annotation_id"]] = old_detail
362
361
 
363
362
  new_request_details: list[dict[str, Any]] = []
364
- for detail in details:
363
+ for detail_index, detail in enumerate(details):
365
364
  try:
366
- log_message_suffix = f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}'"
365
+ # detail_indexを出力する理由: annotation_idはNoneだとどれが問題なのか分からないため
366
+ log_message_suffix = (
367
+ f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}', label_name='{detail.label}', annotation_id='{detail.annotation_id}', detail_index={detail_index}"
368
+ )
367
369
 
368
370
  request_detail = self.convert_annotation_detail(parser, detail, log_message_suffix=log_message_suffix)
369
371
  except Exception as e:
@@ -23,7 +23,7 @@ class AnnotationDuration:
23
23
  return [
24
24
  "project_id",
25
25
  "task_id",
26
- "annotation_duration_second",
26
+ "annotation_duration_minute",
27
27
  ]
28
28
 
29
29
  @classmethod
@@ -62,7 +62,7 @@ class AnnotationDuration:
62
62
  """
63
63
  logger.debug(f"アノテーションZIPファイルから区間アノテーションの長さを計算します。 :: project_id='{project_id}', file='{annotation_zip!s}'")
64
64
 
65
- result: dict[tuple[str, str], float] = defaultdict(float) # key:(project_id, task_id), value:合計アノテーション時間(秒)
65
+ result: dict[tuple[str, str], float] = defaultdict(float) # key:(project_id, task_id), value:合計アノテーション時間(分)
66
66
 
67
67
  for index, parser in enumerate(lazy_parse_simple_annotation_zip(annotation_zip)):
68
68
  simple_annotation = parser.load_json()
@@ -83,7 +83,7 @@ class AnnotationDuration:
83
83
  # 区間アノテーションの場合
84
84
  begin = data["begin"]
85
85
  end = data["end"]
86
- total_duration += (end - begin) / 1000.0 # ミリ秒から秒に変換
86
+ total_duration += (end - begin) / 1000.0 / 60.0 # ミリ秒から分に変換
87
87
 
88
88
  result[(project_id, parser.task_id)] += total_duration
89
89
 
@@ -114,7 +114,7 @@ class AnnotationDuration:
114
114
  df_dtype: dict[str, str] = {
115
115
  "project_id": "string",
116
116
  "task_id": "string",
117
- "annotation_duration_second": "float64",
117
+ "annotation_duration_minute": "float64",
118
118
  }
119
119
 
120
120
  df = pandas.DataFrame(columns=cls.columns()).astype(df_dtype)
@@ -141,7 +141,7 @@ class WholeProductivityPerCompletedDate:
141
141
  return pandas.DataFrame(index=[e.strftime("%Y-%m-%d") for e in pandas.date_range(start_date, end_date)])
142
142
 
143
143
  @classmethod
144
- def from_df_wrapper(cls, task: Task, worktime_per_date: WorktimePerDate, task_completion_criteria: TaskCompletionCriteria) -> WholeProductivityPerCompletedDate:
144
+ def from_df_wrapper(cls, task: Task, worktime_per_date: WorktimePerDate, task_completion_criteria: TaskCompletionCriteria) -> WholeProductivityPerCompletedDate: # noqa: PLR0912
145
145
  """
146
146
  完了日毎の全体の生産量、生産性を算出する。
147
147
 
@@ -172,6 +172,8 @@ class WholeProductivityPerCompletedDate:
172
172
  datetime_column = "first_acceptance_reached_datetime"
173
173
  elif task_completion_criteria == TaskCompletionCriteria.INSPECTION_REACHED:
174
174
  datetime_column = "first_inspection_reached_datetime"
175
+ elif task_completion_criteria == TaskCompletionCriteria.ANNOTATION_STARTED:
176
+ datetime_column = "first_annotation_started_datetime"
175
177
  else:
176
178
  assert_noreturn(task_completion_criteria)
177
179
 
@@ -280,6 +282,8 @@ class WholeProductivityPerCompletedDate:
280
282
  str_task = "受入フェーズに到達した"
281
283
  elif self.task_completion_criteria == TaskCompletionCriteria.INSPECTION_REACHED:
282
284
  str_task = "検査フェーズに到達した"
285
+ elif self.task_completion_criteria == TaskCompletionCriteria.ANNOTATION_STARTED:
286
+ str_task = "教師付フェーズが着手された"
283
287
  else:
284
288
  assert_noreturn(self.task_completion_criteria)
285
289
 
@@ -884,6 +888,9 @@ class WholeProductivityPerFirstAnnotationStartedDate:
884
888
  df_sub_task = df_task[df_task["phase"] == TaskPhase.ACCEPTANCE.value]
885
889
  elif task_completion_criteria == TaskCompletionCriteria.INSPECTION_REACHED:
886
890
  df_sub_task = df_task[df_task["phase"].isin([TaskPhase.INSPECTION.value, TaskPhase.ACCEPTANCE.value])]
891
+ elif task_completion_criteria == TaskCompletionCriteria.ANNOTATION_STARTED:
892
+ # 教師付フェーズが着手されたタスク(first_annotation_started_datetimeがNoneでない)を抽出する
893
+ df_sub_task = df_task[df_task["first_annotation_started_datetime"].notna()]
887
894
  else:
888
895
  assert_noreturn(task_completion_criteria)
889
896
 
@@ -1013,6 +1020,8 @@ class WholeProductivityPerFirstAnnotationStartedDate:
1013
1020
  str_task = "受入フェーズ"
1014
1021
  elif self.task_completion_criteria == TaskCompletionCriteria.INSPECTION_REACHED:
1015
1022
  str_task = "検査フェーズまたは受入フェーズ"
1023
+ elif self.task_completion_criteria == TaskCompletionCriteria.ANNOTATION_STARTED:
1024
+ str_task = "教師付フェーズが着手されたタスク"
1016
1025
  else:
1017
1026
  assert_noreturn(self.task_completion_criteria)
1018
1027
 
@@ -40,6 +40,8 @@ class TaskCompletionCriteria(Enum):
40
40
  """タスクが受入フェーズに到達したら「タスクの完了」とみなす"""
41
41
  INSPECTION_REACHED = "inspection_reached"
42
42
  """タスクが検査フェーズに到達したら「タスクの完了」とみなす"""
43
+ ANNOTATION_STARTED = "annotation_started"
44
+ """教師付フェーズが着手されたら「タスクの完了」とみなす"""
43
45
 
44
46
  def is_task_completed(self, task: dict[str, Any]) -> bool:
45
47
  """指定したタスクが、タスクの完了条件に合致するかどうかを判定します。
@@ -48,6 +50,7 @@ class TaskCompletionCriteria(Enum):
48
50
  task: タスク情報。以下のキーを参照します。
49
51
  * phase
50
52
  * status
53
+ * work_time_span (ANNOTATION_STARTEDの場合のみ)
51
54
 
52
55
  Returns:
53
56
  タスクの完了条件に合致する場合はTrue、そうでない場合はFalse
@@ -62,5 +65,10 @@ class TaskCompletionCriteria(Enum):
62
65
  # 受入フェーズも含む理由:検査フェーズに到達したタスクを「完了」とみなすならば、検査フェーズより後段フェーズである受入フェーズも「完了」とみなせるため
63
66
  return task["phase"] in {TaskPhase.INSPECTION.value, TaskPhase.ACCEPTANCE.value}
64
67
 
68
+ elif self == TaskCompletionCriteria.ANNOTATION_STARTED:
69
+ # 教師付フェーズが着手されたタスクを「完了」とみなす
70
+ # work_time_span > 0 であれば教師付フェーズが着手されたとみなす
71
+ return task["work_time_span"] > 0
72
+
65
73
  else:
66
74
  assert_noreturn(self)
@@ -42,7 +42,7 @@ class ProjectDir(DataClassJsonMixin):
42
42
  - acceptance_completed: 受入フェーズの完了状態
43
43
  - acceptance_reached: 受入フェーズに到達
44
44
  - inspection_reached: 検査フェーズまたは受入フェーズに到達
45
- - annotation_started: 教師付フェーズを着手
45
+ - annotation_started: 教師付フェーズが着手された
46
46
  """
47
47
 
48
48
  FILENAME_WHOLE_PERFORMANCE = "全体の生産性と品質.csv"
@@ -39,6 +39,7 @@ class VisualizationSourceFiles:
39
39
  self.task_history_json_path = target_dir / f"{self.project_id}__task-history.json"
40
40
  self.task_history_event_json_path = target_dir / f"{self.project_id}__task-history-event.json"
41
41
  self.annotation_zip_path = target_dir / f"{self.project_id}__annotation.zip"
42
+ self.input_data_json_path = target_dir / f"{self.project_id}__input_data.json"
42
43
 
43
44
  self.logging_prefix = f"project_id='{project_id}'"
44
45
 
@@ -99,6 +100,52 @@ class VisualizationSourceFiles:
99
100
  logger.debug(f"{self.logging_prefix}: '{self.comment_json_path}'を読み込みました。{len(comment_list)}件のコメントが含まれています。")
100
101
  return comment_list
101
102
 
103
+ def read_input_data_json(self) -> list[dict[str, Any]]:
104
+ """
105
+ 入力データ全件ファイルを読み込みます。
106
+
107
+ Returns:
108
+ 全入力データの一覧
109
+ """
110
+ with self.input_data_json_path.open(encoding="utf-8") as f:
111
+ input_data_list = json.load(f)
112
+
113
+ logger.debug(f"{self.logging_prefix}: '{self.input_data_json_path}'を読み込みました。{len(input_data_list)}件の入力データが含まれています。")
114
+ return input_data_list
115
+
116
+ def get_video_duration_minutes_by_task_id(self) -> dict[str, float]:
117
+ """
118
+ 動画プロジェクトの場合、タスクIDごとの動画の長さ(分単位)を取得します。
119
+
120
+ Returns:
121
+ key: task_id, value: 動画の長さ(分)
122
+ """
123
+ tasks = self.read_tasks_json()
124
+ input_data_list = self.read_input_data_json()
125
+
126
+ # 入力データIDをキーとした辞書を作成
127
+ dict_input_data_by_id = {input_data["input_data_id"]: input_data for input_data in input_data_list}
128
+
129
+ result = {}
130
+ for task in tasks:
131
+ task_id = task["task_id"]
132
+ input_data_id_list = task["input_data_id_list"]
133
+ assert len(input_data_id_list) == 1, f"task_id='{task_id}'には複数の入力データが含まれています。"
134
+ input_data_id = input_data_id_list[0]
135
+ input_data = dict_input_data_by_id.get(input_data_id)
136
+
137
+ if input_data is None:
138
+ logger.warning(f"task_id='{task_id}' :: タスクに含まれている入力データ(input_data_id='{input_data_id}')は、見つかりません。")
139
+ result[task_id] = 0.0
140
+ continue
141
+
142
+ video_duration_second = input_data["system_metadata"]["input_duration"]
143
+
144
+ # 秒から分に変換
145
+ result[task_id] = video_duration_second / 60.0
146
+
147
+ return result
148
+
102
149
  def write_files(self, *, is_latest: bool = False, should_get_task_histories_one_of_each: bool = False, should_download_annotation_zip: bool = True) -> None:
103
150
  """
104
151
  可視化に必要なファイルを作成します。
@@ -117,6 +164,8 @@ class VisualizationSourceFiles:
117
164
 
118
165
  downloading_obj.download_task_json(self.project_id, dest_path=self.task_json_path, is_latest=is_latest, wait_options=wait_options)
119
166
 
167
+ downloading_obj.download_input_data_json(self.project_id, dest_path=self.input_data_json_path, is_latest=is_latest, wait_options=wait_options)
168
+
120
169
  if should_download_annotation_zip:
121
170
  downloading_obj.download_annotation_zip(
122
171
  self.project_id,
@@ -12,7 +12,7 @@ from typing import Any, Callable, Optional
12
12
 
13
13
  import annofabapi
14
14
  import pandas
15
- from annofabapi.models import InputDataType, ProjectMemberRole, TaskPhase
15
+ from annofabapi.models import ProjectMemberRole, TaskPhase
16
16
 
17
17
  import annofabcli
18
18
  from annofabcli.common.cli import (
@@ -82,6 +82,7 @@ class WriteCsvGraph:
82
82
  production_volume_include_labels: Optional[list[str]] = None,
83
83
  production_volume_exclude_labels: Optional[list[str]] = None,
84
84
  include_annotation_duration_seconds: bool = False,
85
+ include_video_duration_minutes: bool = False,
85
86
  ) -> None:
86
87
  self.service = service
87
88
  self.project_id = project_id
@@ -98,6 +99,7 @@ class WriteCsvGraph:
98
99
  self.production_volume_include_labels = production_volume_include_labels
99
100
  self.production_volume_exclude_labels = production_volume_exclude_labels
100
101
  self.include_annotation_duration_seconds = include_annotation_duration_seconds
102
+ self.include_video_duration_minutes = include_video_duration_minutes
101
103
 
102
104
  self.task: Optional[Task] = None
103
105
  self.worktime_per_date: Optional[WorktimePerDate] = None
@@ -119,14 +121,18 @@ class WriteCsvGraph:
119
121
 
120
122
  def _get_task(self) -> Task:
121
123
  if self.task is None:
124
+ custom_production_volume = self._prepare_custom_production_volume()
125
+
122
126
  if self.annotation_count is None:
123
- # アノテーションZIPからアノテーション数を取得
124
- annotation_count = AnnotationCount.from_annotation_zip(
125
- self.visualize_source_files.annotation_zip_path,
126
- project_id=self.project_id,
127
- include_labels=self.production_volume_include_labels,
128
- exclude_labels=self.production_volume_exclude_labels,
129
- )
127
+ if self.visualize_source_files.annotation_zip_path.exists():
128
+ annotation_count = AnnotationCount.from_annotation_zip(
129
+ self.visualize_source_files.annotation_zip_path,
130
+ project_id=self.project_id,
131
+ include_labels=self.production_volume_include_labels,
132
+ exclude_labels=self.production_volume_exclude_labels,
133
+ )
134
+ else:
135
+ annotation_count = AnnotationCount.empty()
130
136
  else:
131
137
  annotation_count = self.annotation_count
132
138
 
@@ -137,36 +143,6 @@ class WriteCsvGraph:
137
143
  new_tasks = filter_tasks(tasks, self.task_completion_criteria, self.filtering_query, task_histories=task_histories)
138
144
  logger.debug(f"project_id='{self.project_id}' :: 集計対象タスクは {len(new_tasks)} / {len(tasks)} 件です。")
139
145
 
140
- # annotation_duration_secondsを生産量に含める場合、アノテーション時間を計算
141
- custom_production_volume = self.custom_production_volume
142
- if self.include_annotation_duration_seconds:
143
- logger.debug(f"project_id='{self.project_id}' :: 区間アノテーションの長さ('annotation_duration_second')を計算します。")
144
- annotation_duration_obj = AnnotationDuration.from_annotation_zip(
145
- self.visualize_source_files.annotation_zip_path,
146
- project_id=self.project_id,
147
- include_labels=self.production_volume_include_labels,
148
- exclude_labels=self.production_volume_exclude_labels,
149
- )
150
-
151
- if custom_production_volume is not None:
152
- # 既存のCustomProductionVolumeのデータと結合
153
- if not custom_production_volume.is_empty():
154
- annotation_duration_df = pandas.merge(custom_production_volume.df, annotation_duration_obj.df, on=["project_id", "task_id"], how="outer")
155
- else:
156
- annotation_duration_df = annotation_duration_obj.df
157
-
158
- # annotation_duration_secondを含む新しいProductionVolumeColumnリストを作成
159
- annotation_duration_column = ProductionVolumeColumn(value="annotation_duration_second", name="区間アノテーションの長さ(秒)")
160
- new_production_volume_list = list(custom_production_volume.custom_production_volume_list)
161
- if annotation_duration_column not in new_production_volume_list:
162
- new_production_volume_list.append(annotation_duration_column)
163
-
164
- custom_production_volume = CustomProductionVolume(annotation_duration_df, custom_production_volume_list=new_production_volume_list)
165
- else:
166
- # CustomProductionVolumeが存在しない場合、新規作成
167
- annotation_duration_column = ProductionVolumeColumn(value="annotation_duration_second", name="区間アノテーションの長さ(秒)")
168
- custom_production_volume = CustomProductionVolume(annotation_duration_obj.df, custom_production_volume_list=[annotation_duration_column])
169
-
170
146
  self.task = Task.from_api_content(
171
147
  tasks=new_tasks,
172
148
  task_histories=task_histories,
@@ -180,6 +156,80 @@ class WriteCsvGraph:
180
156
 
181
157
  return self.task
182
158
 
159
+ def _prepare_custom_production_volume(self) -> Optional[CustomProductionVolume]:
160
+ """カスタム生産量の準備を行う"""
161
+ custom_production_volume = self.custom_production_volume
162
+
163
+ # annotation_duration_secondsを生産量に含める場合、アノテーション時間を計算
164
+ if self.include_annotation_duration_seconds:
165
+ custom_production_volume = self._add_annotation_duration(custom_production_volume)
166
+
167
+ # 動画プロジェクトの場合、動画の長さ(分)を生産量に含める
168
+ if self.include_video_duration_minutes:
169
+ custom_production_volume = self._add_video_duration(custom_production_volume)
170
+
171
+ return custom_production_volume
172
+
173
+ def _add_annotation_duration(self, custom_production_volume: Optional[CustomProductionVolume]) -> CustomProductionVolume:
174
+ """区間アノテーションの長さを生産量に追加する"""
175
+ logger.debug(f"project_id='{self.project_id}' :: 区間アノテーションの長さ('annotation_duration_minute')を計算します。")
176
+ annotation_duration_obj = AnnotationDuration.from_annotation_zip(
177
+ self.visualize_source_files.annotation_zip_path,
178
+ project_id=self.project_id,
179
+ include_labels=self.production_volume_include_labels,
180
+ exclude_labels=self.production_volume_exclude_labels,
181
+ )
182
+ annotation_duration_column = ProductionVolumeColumn(value="annotation_duration_minute", name="区間アノテーションの長さ(分)")
183
+
184
+ if custom_production_volume is not None:
185
+ # 既存のCustomProductionVolumeのデータと結合
186
+ if not custom_production_volume.is_empty():
187
+ annotation_duration_df = pandas.merge(custom_production_volume.df, annotation_duration_obj.df, on=["project_id", "task_id"], how="outer")
188
+ else:
189
+ annotation_duration_df = annotation_duration_obj.df
190
+
191
+ # annotation_duration_minuteを含む新しいProductionVolumeColumnリストを作成
192
+ new_production_volume_list = list(custom_production_volume.custom_production_volume_list)
193
+ if annotation_duration_column not in new_production_volume_list:
194
+ new_production_volume_list.append(annotation_duration_column)
195
+
196
+ return CustomProductionVolume(annotation_duration_df, custom_production_volume_list=new_production_volume_list)
197
+ else:
198
+ # CustomProductionVolumeが存在しない場合、新規作成
199
+ return CustomProductionVolume(annotation_duration_obj.df, custom_production_volume_list=[annotation_duration_column])
200
+
201
+ def _add_video_duration(self, custom_production_volume: Optional[CustomProductionVolume]) -> CustomProductionVolume:
202
+ """動画の長さ(分)を生産量に追加する"""
203
+ logger.debug(f"project_id='{self.project_id}' :: 動画の長さ('video_duration_minute')を計算します。")
204
+ video_duration_by_task_id = self.visualize_source_files.get_video_duration_minutes_by_task_id()
205
+
206
+ # DataFrameの作成
207
+ video_duration_data = [{"project_id": self.project_id, "task_id": task_id, "video_duration_minute": duration} for task_id, duration in video_duration_by_task_id.items()]
208
+ if len(video_duration_data) == 0:
209
+ video_duration_df = pandas.DataFrame(columns=["project_id", "task_id", "video_duration_minute"])
210
+ else:
211
+ video_duration_df = pandas.DataFrame(video_duration_data)
212
+
213
+ video_duration_df = video_duration_df.astype({"project_id": "string", "task_id": "string", "video_duration_minute": "float64"})
214
+ video_duration_column = ProductionVolumeColumn(value="video_duration_minute", name="動画の長さ(分)")
215
+
216
+ if custom_production_volume is not None:
217
+ # 既存のCustomProductionVolumeのデータと結合
218
+ if not custom_production_volume.is_empty():
219
+ merged_df = pandas.merge(custom_production_volume.df, video_duration_df, on=["project_id", "task_id"], how="outer")
220
+ else:
221
+ merged_df = video_duration_df
222
+
223
+ # video_duration_minuteを含む新しいProductionVolumeColumnリストを作成
224
+ new_production_volume_list = list(custom_production_volume.custom_production_volume_list)
225
+ if video_duration_column not in new_production_volume_list:
226
+ new_production_volume_list.append(video_duration_column)
227
+
228
+ return CustomProductionVolume(merged_df, custom_production_volume_list=new_production_volume_list)
229
+ else:
230
+ # CustomProductionVolumeが存在しない場合、新規作成
231
+ return CustomProductionVolume(video_duration_df, custom_production_volume_list=[video_duration_column])
232
+
183
233
  def _get_task_worktime_obj(self) -> TaskWorktimeByPhaseUser:
184
234
  if self.task_worktime_obj is None:
185
235
  task_history = TaskHistory.from_api_content(self.visualize_source_files.read_task_histories_json())
@@ -370,6 +420,9 @@ class VisualizingStatisticsMain:
370
420
  # 動画プロジェクトの場合、annotation_duration_secondを生産量に含める
371
421
  custom_production_volume = self.custom_production_volume
372
422
 
423
+ # 動画プロジェクトかどうかを判定
424
+ is_video_project = project_info.input_data_type == "movie"
425
+
373
426
  project_dir = ProjectDir(
374
427
  output_project_dir,
375
428
  self.task_completion_criteria,
@@ -429,7 +482,8 @@ class VisualizingStatisticsMain:
429
482
  output_only_text=self.output_only_text,
430
483
  production_volume_include_labels=self.production_volume_include_labels,
431
484
  production_volume_exclude_labels=self.production_volume_exclude_labels,
432
- include_annotation_duration_seconds=(project_info.input_data_type == InputDataType.MOVIE.value),
485
+ include_annotation_duration_seconds=is_video_project,
486
+ include_video_duration_minutes=is_video_project,
433
487
  )
434
488
 
435
489
  write_obj._catch_exception(write_obj.write_user_performance)() # noqa: SLF001
@@ -710,7 +764,9 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
710
764
  default=TaskCompletionCriteria.ACCEPTANCE_COMPLETED.value,
711
765
  help="タスクの完了条件を指定します。\n"
712
766
  "* ``acceptance_completed``: タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす\n"
713
- "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n",
767
+ "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n"
768
+ "* ``inspection_reached``: タスクが検査フェーズに到達したら「タスクの完了」とみなす\n"
769
+ "* ``annotation_started``: 教師付フェーズが着手されたら「タスクの完了」とみなす\n",
714
770
  )
715
771
 
716
772
  parser.add_argument(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annofabcli
3
- Version: 1.111.0
3
+ Version: 1.111.2
4
4
  Summary: Utility Command Line Interface for AnnoFab
5
5
  Author: Kurusugawa Computer Inc.
6
6
  License: MIT
@@ -11,7 +11,7 @@ annofabcli/annotation/create_classification_annotation.py,sha256=Ex9kSVNHZu71UbL
11
11
  annofabcli/annotation/delete_annotation.py,sha256=bU87ov7NhYFPe-Cldn3nBSK_CY9A9x3mSLN1Ame-cKk,25144
12
12
  annofabcli/annotation/download_annotation_zip.py,sha256=SMtfxt6NKkpHGRDoKRCjPqpQB5DBpQ-PD2_3ts51h0Q,3314
13
13
  annofabcli/annotation/dump_annotation.py,sha256=Q-p6f5XBs7khDgrfY5Q3CGLBMKEerJWO_CQ8_73UXVM,9972
14
- annofabcli/annotation/import_annotation.py,sha256=maaxlje3VZ7oN94gdtZFgfZnAQlBfYmljgGJzMcI9HE,35811
14
+ annofabcli/annotation/import_annotation.py,sha256=M0osVbY06Xs2DXkQLwCKx7WmK9cPc9EFuvHbAKMKv-0,35898
15
15
  annofabcli/annotation/list_annotation.py,sha256=uKcOuGC7lzd6vVbzizkiZtYdXJ7EzY0iifuiqKl2wQM,10707
16
16
  annofabcli/annotation/list_annotation_count.py,sha256=T9fbaoxWeDJIVgW_YgHRldbwrVZWiE-57lfJrDQrj80,6474
17
17
  annofabcli/annotation/merge_segmentation.py,sha256=kIsCeXtJxzd6nobQPpi0fscaRDlTx3tg1qpy5PDfSJI,18107
@@ -162,17 +162,17 @@ annofabcli/statistics/summarize_task_count_by_task_id_group.py,sha256=K4FFED1pza
162
162
  annofabcli/statistics/summarize_task_count_by_user.py,sha256=KUGVUwn_KO4RSA2twAz60nX1OQE1pd5TL5gBmL1bgMA,7421
163
163
  annofabcli/statistics/visualize_annotation_count.py,sha256=7OATuGa2okq80unuTe-X30CBVkrlMLDN5Y-Q_5mB6eI,22138
164
164
  annofabcli/statistics/visualize_annotation_duration.py,sha256=9JH9MirhOyCmjcChFJMtfnFIV2k4sucP9PAwNKMcbtE,21022
165
- annofabcli/statistics/visualize_statistics.py,sha256=iwGhrYylBTsK-DT_MU-eYkFgZI0ITt9r59BbiaSbfQE,44041
165
+ annofabcli/statistics/visualize_statistics.py,sha256=aDvoGfzSGXp14Ax5mWcNM7GHxrtDqpdlHw2RVAry_Gg,47190
166
166
  annofabcli/statistics/visualize_video_duration.py,sha256=yY18H0-boNy4-MQWUM_xBTMzxDUQ51TrVtz8mbsB_dI,16623
167
167
  annofabcli/statistics/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
168
  annofabcli/statistics/visualization/filtering_query.py,sha256=kqGOa1YdQ62kTLlkiIHFiYNju4Pg9rXpsC4Cph-QjLo,4355
169
- annofabcli/statistics/visualization/model.py,sha256=sKk4gC7qkt6etCnbpCNZpJEhFJf5Vf7eVOG53gyfWvk,2426
170
- annofabcli/statistics/visualization/project_dir.py,sha256=-60n29ySvgsfRTbFgTOYr6E7LHJezVQm3FON88LQMKU,24291
171
- annofabcli/statistics/visualization/visualization_source_files.py,sha256=SFY7WXUtjECB8l7zP-exawocrTiZ0UI7Z5sjgq4J_g4,8641
169
+ annofabcli/statistics/visualization/model.py,sha256=LXFuelx9_6n1mU418Mw5FOCBQTlnNBlOijZ7oUIfQ_Q,2928
170
+ annofabcli/statistics/visualization/project_dir.py,sha256=g1Jb9-CW32BcuwS3Z2dTgJ9bo3_Edlc7wWtrKhLzFcc,24300
171
+ annofabcli/statistics/visualization/visualization_source_files.py,sha256=5-XTBCnO1FWiYYDqOHxdQbYReYz2RKVsAHH_b1svK-4,10825
172
172
  annofabcli/statistics/visualization/dataframe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
173
  annofabcli/statistics/visualization/dataframe/actual_worktime.py,sha256=7nsHlvN5cDzXIw-u_MSAZf4nlSSY56IlunSmnODXTbY,1916
174
174
  annofabcli/statistics/visualization/dataframe/annotation_count.py,sha256=4b5B-RTtrETdU1lR6R0yQaMFAVxDZvxom9iBEcEDNHc,5265
175
- annofabcli/statistics/visualization/dataframe/annotation_duration.py,sha256=vKxlBsDY04Q0MEUSR3Iav1GKMx1MM6BEdnFlWq2Et68,4378
175
+ annofabcli/statistics/visualization/dataframe/annotation_duration.py,sha256=zkofZ7_IFJZ3JhQLEu-bgZhO2dEKytVol3WxeOMtu-4,4385
176
176
  annofabcli/statistics/visualization/dataframe/cumulative_productivity.py,sha256=Z9gxGCfgQra0M0LAq3bUhGxXnwRqZ8Gt-zIo2DPs6Nc,15800
177
177
  annofabcli/statistics/visualization/dataframe/custom_production_volume.py,sha256=5ELLiQJ5sNKdVKmYYVeZW4nedDg1CVGxMDdF5TUUX5c,2142
178
178
  annofabcli/statistics/visualization/dataframe/input_data_count.py,sha256=wDRFtoIWw_Gy2bPZ7LBx3eMO3LdUdjbQKS9mncXav6I,1654
@@ -185,7 +185,7 @@ annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py,sha
185
185
  annofabcli/statistics/visualization/dataframe/user.py,sha256=EHn7nlf6D6UX-gsVXy8m_3QaCsHsUhr0iy2rbNozOgc,1707
186
186
  annofabcli/statistics/visualization/dataframe/user_performance.py,sha256=X0jXV0wY8AZC8hoV62sJd9GRThqTf5RsaVciLeCvGlk,56628
187
187
  annofabcli/statistics/visualization/dataframe/whole_performance.py,sha256=kw5Cww0fwDfwriTtxiT0l3wr7YAMA9eGjvp84G9MN9I,12449
188
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py,sha256=F6KhXvzrUx61E0jqOiU2IGb6ycLH82T80JyzYVpSaC8,53683
188
+ annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py,sha256=J-QW2Mvt-hGTpDUGGJKFvo0REzIWwrbDAzcMf9iqx_U,54480
189
189
  annofabcli/statistics/visualization/dataframe/worktime_per_date.py,sha256=wt0paPy2cVVzLUJhbV12-6bInikbQoxRFnOHY7BcI9o,21342
190
190
  annofabcli/supplementary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
191
  annofabcli/supplementary/delete_supplementary_data.py,sha256=dA4n61SSkTbDKDqjVldHIEAxbPQXyrQciwI4RDuC35U,13537
@@ -221,8 +221,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=hQLVbQ0
221
221
  annofabcli/task_history_event/list_all_task_history_event.py,sha256=EeKMyPUxGwYCFtWQHHW954ZserGm8lUqrwNnV1iX9X4,6830
222
222
  annofabcli/task_history_event/list_worktime.py,sha256=Y7Pu5DP7scPf7HPt6CTiTvB1_5_Nfi1bStUIaCpkhII,15507
223
223
  annofabcli/task_history_event/subcommand_task_history_event.py,sha256=mJVJoT4RXk4HWnY7-Nrsl4If-gtaIIEXd2z7eFZwM2I,1260
224
- annofabcli-1.111.0.dist-info/METADATA,sha256=8icfwwpFiUk3CLmu_RvCrllfkQnP93ITWhkKwEY_adg,5134
225
- annofabcli-1.111.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
226
- annofabcli-1.111.0.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
227
- annofabcli-1.111.0.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
228
- annofabcli-1.111.0.dist-info/RECORD,,
224
+ annofabcli-1.111.2.dist-info/METADATA,sha256=8kC_vmMBPGWNDApBpji4krvAeE_KnXdEA1VTkzg_H9E,5134
225
+ annofabcli-1.111.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
226
+ annofabcli-1.111.2.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
227
+ annofabcli-1.111.2.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
228
+ annofabcli-1.111.2.dist-info/RECORD,,