annofabcli 1.97.0__py3-none-any.whl → 1.99.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.
@@ -9,175 +9,249 @@ import annofabapi
9
9
  import more_itertools
10
10
  import pandas
11
11
  from annofabapi.models import Task, TaskHistory, TaskPhase, TaskStatus
12
+ from annofabapi.util.task_history import find_rejected_task_history_indices, get_task_creation_datetime
12
13
  from annofabapi.utils import get_task_history_index_skipped_acceptance, get_task_history_index_skipped_inspection
13
14
 
14
15
  import annofabcli
15
16
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login
16
17
  from annofabcli.common.enums import FormatArgument
17
18
  from annofabcli.common.facade import AnnofabApiFacade
18
- from annofabcli.common.utils import print_csv, print_json
19
+ from annofabcli.common.utils import isoduration_to_hour, print_csv, print_json
19
20
  from annofabcli.common.visualize import AddProps
20
21
  from annofabcli.task.list_tasks import ListTasksMain
21
22
 
22
23
  logger = logging.getLogger(__name__)
23
24
 
24
25
 
25
- class AddingAdditionalInfoToTask:
26
- """タスクに付加的な情報を追加するためのクラス
26
+ def get_post_rejection_annotation_worktime_hour(task_histories: list[TaskHistory]) -> float:
27
+ """
28
+ 検査/受入フェーズでの差し戻し後の教師付作業時間を算出します。
29
+ 指摘による修正にかかった時間を把握するのに利用できます。
27
30
 
28
31
  Args:
29
- service: annofabapiにアクセスするためのインスタンス
30
- project_id: プロジェクトID
32
+ task_histories: タスク履歴
31
33
 
32
34
  """
35
+ rejected_task_history_indices = find_rejected_task_history_indices(task_histories)
36
+ if len(rejected_task_history_indices) == 0:
37
+ return 0.0
38
+
39
+ # 差し戻された履歴の直後で、教師付フェーズの作業時間を算出する
40
+ min_rejected_task_history_index = min(rejected_task_history_indices)
41
+ return sum(
42
+ isoduration_to_hour(history["accumulated_labor_time_milliseconds"])
43
+ for history in task_histories[min_rejected_task_history_index + 1 :]
44
+ if history["phase"] == TaskPhase.ANNOTATION.value
45
+ )
33
46
 
34
- def __init__(self, service: annofabapi.Resource, project_id: str) -> None:
35
- self.service = service
36
- self.project_id = project_id
37
- self.visualize = AddProps(self.service, project_id)
38
47
 
39
- @staticmethod
40
- def get_completed_datetime(task: dict[str, Any], task_histories: list[TaskHistory]) -> Optional[str]:
41
- """受入完了状態になった日時を取得する。
48
+ def get_post_rejection_inspection_worktime_hour(task_histories: list[TaskHistory]) -> float:
49
+ """
50
+ 検査/受入フェーズでの差し戻し後の検査作業時間を算出します。
42
51
 
43
- Args:
44
- task_histories (List[TaskHistory]): [description]
52
+ Args:
53
+ task_histories: タスク履歴
45
54
 
46
- Returns:
47
- str: 受入完了状態になった日時
48
- """
49
- # 受入完了日時を設定
50
- if task["phase"] == TaskPhase.ACCEPTANCE.value and task["status"] == TaskStatus.COMPLETE.value:
51
- assert len(task_histories) > 0, (
52
- f"task_id='{task['task_id']}'のタスク履歴が0件です。参照しているタスク履歴情報が古い可能性があります。 "
53
- f":: phase='{task['phase']}', status='{task['status']}'"
54
- )
55
- return task_histories[-1]["ended_datetime"]
56
- else:
57
- return None
55
+ """
56
+ rejected_task_history_indices = find_rejected_task_history_indices(task_histories)
57
+ if len(rejected_task_history_indices) == 0:
58
+ return 0.0
59
+
60
+ # 差し戻された履歴の直後で、検査フェーズの作業時間を算出する
61
+ min_rejected_task_history_index = min(rejected_task_history_indices)
62
+ return sum(
63
+ isoduration_to_hour(history["accumulated_labor_time_milliseconds"])
64
+ for history in task_histories[min_rejected_task_history_index + 1 :]
65
+ if history["phase"] == TaskPhase.INSPECTION.value
66
+ )
58
67
 
59
- @staticmethod
60
- def get_task_created_datetime(task: dict[str, Any], task_histories: list[TaskHistory]) -> Optional[str]:
61
- """タスクの作成日時を取得する。
62
68
 
63
- Args:
64
- task_histories (List[TaskHistory]): タスク履歴
69
+ def get_post_rejection_acceptance_worktime_hour(task_histories: list[TaskHistory]) -> float:
70
+ """
71
+ 受入フェーズでの差し戻し後の受入作業時間を算出します。
65
72
 
66
- Returns:
67
- タスクの作成日時
68
- """
69
- # 受入フェーズで完了日時がnot Noneの場合は、受入を合格したか差し戻したとき。
70
- # したがって、後続のタスク履歴を見て、初めて受入完了状態になった日時を取得する。
71
- if len(task_histories) == 0:
72
- return None
73
-
74
- first_history = task_histories[0]
75
- # 2020年以前は、先頭のタスク履歴はタスク作成ではなく、教師付けの履歴である。2020年以前はタスク作成日時を取得できないのでNoneを返す。
76
- # https://annofab.com/docs/releases/2020.html#v01020
77
- if (
78
- first_history["account_id"] is None
79
- and first_history["accumulated_labor_time_milliseconds"] == "PT0S"
80
- and first_history["phase"] == TaskPhase.ANNOTATION.value
81
- ):
82
- if len(task_histories) == 1:
83
- # 一度も作業されていないタスクは、先頭のタスク履歴のstarted_datetimeはNoneである
84
- # 替わりにタスクの`operation_updated_datetime`をタスク作成日時とする
85
- return task["operation_updated_datetime"]
86
- return first_history["started_datetime"]
87
- return None
88
73
 
89
- @staticmethod
90
- def get_first_acceptance_completed_datetime(task_histories: list[TaskHistory]) -> Optional[str]:
91
- """はじめて受入完了状態になった日時を取得する。
74
+ Args:
75
+ task_histories: タスク履歴
92
76
 
93
- Args:
94
- task_histories (List[TaskHistory]): [description]
77
+ """
78
+ rejected_task_history_indices = find_rejected_task_history_indices(task_histories)
95
79
 
96
- Returns:
97
- str: はじめて受入完了状態になった日時
98
- """
99
- # 受入フェーズで完了日時がnot Noneの場合は、受入を合格したか差し戻したとき。
100
- # したがって、後続のタスク履歴を見て、初めて受入完了状態になった日時を取得する。
80
+ # 検査フェーズでの差し戻しは除外する
81
+ # 検査フェーズでの差し戻しは、受入作業の回数に影響しないため
82
+ acceptance_rejected_indices = [index for index in rejected_task_history_indices if task_histories[index]["phase"] == TaskPhase.ACCEPTANCE.value]
83
+ if len(acceptance_rejected_indices) == 0:
84
+ return 0.0
101
85
 
102
- for index, history in enumerate(task_histories):
103
- if history["phase"] != TaskPhase.ACCEPTANCE.value or history["ended_datetime"] is None:
104
- continue
86
+ min_rejected_acceptance_task_history_index = min(acceptance_rejected_indices)
87
+
88
+ # 差し戻された履歴の直後以降で、受入フェーズの作業時間を算出する
89
+ return sum(
90
+ isoduration_to_hour(history["accumulated_labor_time_milliseconds"])
91
+ for history in task_histories[min_rejected_acceptance_task_history_index + 1 :]
92
+ if history["phase"] == TaskPhase.ACCEPTANCE.value
93
+ )
105
94
 
106
- if index == len(task_histories) - 1:
107
- # 末尾履歴なら、受入完了状態
108
- return history["ended_datetime"]
109
95
 
110
- next_history = task_histories[index + 1]
111
- if next_history["phase"] == TaskPhase.ACCEPTANCE.value:
112
- # 受入完了後、受入取り消し実行
113
- return history["ended_datetime"]
114
- # そうでなければ、受入フェーズでの差し戻し
96
+ def get_completed_datetime(task: dict[str, Any], task_histories: list[TaskHistory]) -> Optional[str]:
97
+ """受入完了状態になった日時を取得する。
115
98
 
99
+ Args:
100
+ task_histories (List[TaskHistory]): [description]
101
+
102
+ Returns:
103
+ str: 受入完了状態になった日時
104
+ """
105
+ # 受入完了日時を設定
106
+ if task["phase"] == TaskPhase.ACCEPTANCE.value and task["status"] == TaskStatus.COMPLETE.value:
107
+ assert len(task_histories) > 0, (
108
+ f"task_id='{task['task_id']}'のタスク履歴が0件です。参照しているタスク履歴情報が古い可能性があります。 "
109
+ f":: phase='{task['phase']}', status='{task['status']}'"
110
+ )
111
+ return task_histories[-1]["ended_datetime"]
112
+ else:
116
113
  return None
117
114
 
118
- @staticmethod
119
- def get_first_acceptance_reached_datetime(task_histories: list[TaskHistory]) -> Optional[str]:
120
- """はじめて受入フェーズに到達した日時を取得する。
121
- 受入フェーズを着手した日時とは異なる。
122
- 必ず`first_acceptance_started_datetime`よりも前の日時になる。
123
115
 
124
- Args:
125
- task_histories (List[TaskHistory]): [description]
116
+ def get_first_acceptance_completed_datetime(task_histories: list[TaskHistory]) -> Optional[str]:
117
+ """はじめて受入完了状態になった日時を取得する。
126
118
 
127
- """
128
- for index, history in enumerate(task_histories):
129
- if history["phase"] != TaskPhase.ACCEPTANCE.value:
130
- continue
119
+ Args:
120
+ task_histories (List[TaskHistory]): [description]
131
121
 
132
- first_acceptance_reached_datetime = task_histories[index - 1]["ended_datetime"]
133
- assert first_acceptance_reached_datetime is not None
134
- return first_acceptance_reached_datetime
135
- return None
122
+ Returns:
123
+ str: はじめて受入完了状態になった日時
124
+ """
125
+ # 受入フェーズで完了日時がnot Noneの場合は、受入を合格したか差し戻したとき。
126
+ # したがって、後続のタスク履歴を見て、初めて受入完了状態になった日時を取得する。
136
127
 
137
- @staticmethod
138
- def is_acceptance_phase_skipped(task_histories: list[TaskHistory]) -> bool:
139
- """抜取受入によって、受入フェーズでスキップされたことがあるかを取得する。
128
+ for index, history in enumerate(task_histories):
129
+ if history["phase"] != TaskPhase.ACCEPTANCE.value or history["ended_datetime"] is None:
130
+ continue
140
131
 
141
- Args:
142
- task_histories (List[TaskHistory]): タスク履歴
132
+ if index == len(task_histories) - 1:
133
+ # 末尾履歴なら、受入完了状態
134
+ return history["ended_datetime"]
143
135
 
144
- Returns:
145
- bool: 受入フェーズでスキップされたことがあるかどうか
146
- """
147
- task_history_index_list = get_task_history_index_skipped_acceptance(task_histories)
148
- if len(task_history_index_list) == 0:
149
- return False
136
+ next_history = task_histories[index + 1]
137
+ if next_history["phase"] == TaskPhase.ACCEPTANCE.value:
138
+ # 受入完了後、受入取り消し実行
139
+ return history["ended_datetime"]
140
+ # そうでなければ、受入フェーズでの差し戻し
150
141
 
151
- # スキップされた履歴より後に受入フェーズがなければ、受入がスキップされたタスクとみなす
152
- # ただし、スキップされた履歴より後で、「アノテーション一覧で修正された」受入フェーズがある場合(account_id is None)は、スキップされた受入とみなす。 # noqa: E501
153
- last_task_history_index = task_history_index_list[-1]
154
- return (
155
- more_itertools.first_true(
156
- task_histories[last_task_history_index + 1 :],
157
- pred=lambda e: e["phase"] == TaskPhase.ACCEPTANCE.value and e["account_id"] is not None,
158
- )
159
- is None
160
- )
142
+ return None
161
143
 
162
- @staticmethod
163
- def is_inspection_phase_skipped(task_histories: list[TaskHistory]) -> bool:
164
- """抜取検査によって、検査フェーズでスキップされたことがあるかを取得する。
165
144
 
166
- Args:
167
- task_histories (List[TaskHistory]): タスク履歴
145
+ def get_first_acceptance_reached_datetime(task_histories: list[TaskHistory]) -> Optional[str]:
146
+ """
147
+ はじめて受入フェーズに到達した日時を取得する。
148
+ 受入フェーズを着手した日時とは異なる。
149
+ 必ず`first_acceptance_started_datetime`よりも前の日時になる。
168
150
 
169
- Returns:
170
- bool: 検査フェーズでスキップされたことがあるかどうか
171
- """
172
- task_history_index_list = get_task_history_index_skipped_inspection(task_histories)
173
- if len(task_history_index_list) == 0:
174
- return False
151
+ たとえば教師付フェーズで提出して受入フェーズに到達した場合、教師付フェーズを提出した日時が「受入フェーズに到達した日時」になる。
175
152
 
176
- # スキップされた履歴より後に検査フェーズがなければ、検査がスキップされたタスクとみなす
177
- last_task_history_index = task_history_index_list[-1]
178
- return (
179
- more_itertools.first_true(task_histories[last_task_history_index + 1 :], pred=lambda e: e["phase"] == TaskPhase.INSPECTION.value) is None
153
+ """
154
+ for index, history in enumerate(task_histories):
155
+ if history["phase"] != TaskPhase.ACCEPTANCE.value:
156
+ continue
157
+
158
+ first_acceptance_reached_datetime = task_histories[index - 1]["ended_datetime"]
159
+ assert first_acceptance_reached_datetime is not None
160
+ return first_acceptance_reached_datetime
161
+ return None
162
+
163
+
164
+ def is_acceptance_phase_skipped(task_histories: list[TaskHistory]) -> bool:
165
+ """抜取受入によって、受入フェーズでスキップされたことがあるかを取得する。
166
+
167
+ Args:
168
+ task_histories (List[TaskHistory]): タスク履歴
169
+
170
+ Returns:
171
+ bool: 受入フェーズでスキップされたことがあるかどうか
172
+ """
173
+ task_history_index_list = get_task_history_index_skipped_acceptance(task_histories)
174
+ if len(task_history_index_list) == 0:
175
+ return False
176
+
177
+ # スキップされた履歴より後に受入フェーズがなければ、受入がスキップされたタスクとみなす
178
+ # ただし、スキップされた履歴より後で、「アノテーション一覧で修正された」受入フェーズがある場合(account_id is None)は、スキップされた受入とみなす。 # noqa: E501
179
+ last_task_history_index = task_history_index_list[-1]
180
+ return (
181
+ more_itertools.first_true(
182
+ task_histories[last_task_history_index + 1 :],
183
+ pred=lambda e: e["phase"] == TaskPhase.ACCEPTANCE.value and e["account_id"] is not None,
180
184
  )
185
+ is None
186
+ )
187
+
188
+
189
+ def calculate_total_worktime_in_phase(task_histories: list[TaskHistory], phase: TaskPhase) -> float:
190
+ """指定したフェーズの合計作業時間を計算する。
191
+
192
+ Args:
193
+ task_histories: タスク履歴
194
+ phase: 計算対象のフェーズ
195
+
196
+ Returns:
197
+ 合計作業時間
198
+ """
199
+ return sum(isoduration_to_hour(history["accumulated_labor_time_milliseconds"]) for history in task_histories if history["phase"] == phase.value)
200
+
201
+
202
+ def get_first_task_history(task_histories: list[TaskHistory], phase: TaskPhase) -> Optional[TaskHistory]:
203
+ """
204
+ 指定したフェーズの最初に作業したタスク履歴を取得します。
205
+ 取得したタスク履歴には、account_idはnot Noneで、作業時間は0より大きいです。
206
+
207
+ Args:
208
+ task_histories: タスク履歴
209
+ phase: フェーズ
210
+
211
+ Returns:
212
+ 最初のタスク履歴
213
+ """
214
+ for history in task_histories:
215
+ if (
216
+ history["phase"] == phase.value
217
+ and history["account_id"] is not None
218
+ and isoduration_to_hour(history["accumulated_labor_time_milliseconds"]) > 0
219
+ ):
220
+ return history
221
+ return None
222
+
223
+
224
+ def is_inspection_phase_skipped(task_histories: list[TaskHistory]) -> bool:
225
+ """抜取検査によって、検査フェーズでスキップされたことがあるかを取得する。
226
+
227
+ Args:
228
+ task_histories (List[TaskHistory]): タスク履歴
229
+
230
+ Returns:
231
+ bool: 検査フェーズでスキップされたことがあるかどうか
232
+ """
233
+ task_history_index_list = get_task_history_index_skipped_inspection(task_histories)
234
+ if len(task_history_index_list) == 0:
235
+ return False
236
+
237
+ # スキップされた履歴より後に検査フェーズがなければ、検査がスキップされたタスクとみなす
238
+ last_task_history_index = task_history_index_list[-1]
239
+ return more_itertools.first_true(task_histories[last_task_history_index + 1 :], pred=lambda e: e["phase"] == TaskPhase.INSPECTION.value) is None
240
+
241
+
242
+ class AddingAdditionalInfoToTask:
243
+ """タスクに付加的な情報を追加するためのクラス
244
+
245
+ Args:
246
+ service: annofabapiにアクセスするためのインスタンス
247
+ project_id: プロジェクトID
248
+
249
+ """
250
+
251
+ def __init__(self, service: annofabapi.Resource, project_id: str) -> None:
252
+ self.service = service
253
+ self.project_id = project_id
254
+ self.visualize = AddProps(self.service, project_id)
181
255
 
182
256
  def _add_task_history_info(self, task: Task, task_history: Optional[TaskHistory], column_prefix: str) -> Task:
183
257
  """
@@ -210,12 +284,12 @@ class AddingAdditionalInfoToTask:
210
284
  }
211
285
  )
212
286
 
213
- organization_member = self.visualize.get_project_member_from_account_id(account_id)
214
- if organization_member is not None:
287
+ member = self.visualize.get_project_member_from_account_id(account_id)
288
+ if member is not None:
215
289
  task.update(
216
290
  {
217
- f"{column_prefix}_user_id": organization_member["user_id"],
218
- f"{column_prefix}_username": organization_member["username"],
291
+ f"{column_prefix}_user_id": member["user_id"],
292
+ f"{column_prefix}_username": member["username"],
219
293
  }
220
294
  )
221
295
  else:
@@ -223,27 +297,7 @@ class AddingAdditionalInfoToTask:
223
297
 
224
298
  return task
225
299
 
226
- def _add_task_history_info_by_phase(self, task: dict[str, Any], task_histories: list[TaskHistory], phase: TaskPhase) -> Task:
227
- task_history_by_phase = [
228
- e
229
- for e in task_histories
230
- if e["phase"] == phase.value
231
- and e["account_id"] is not None
232
- and annofabcli.common.utils.isoduration_to_hour(e["accumulated_labor_time_milliseconds"]) > 0
233
- ]
234
-
235
- # 最初の対象フェーズに関する情報を設定
236
- first_task_history = task_history_by_phase[0] if len(task_history_by_phase) > 0 else None
237
- self._add_task_history_info(task, first_task_history, column_prefix=f"first_{phase.value}")
238
-
239
- # 作業時間に関する情報を設定
240
- task[f"{phase.value}_worktime_hour"] = sum(
241
- annofabcli.common.utils.isoduration_to_hour(e["accumulated_labor_time_milliseconds"]) for e in task_history_by_phase
242
- )
243
-
244
- return task
245
-
246
- def add_additional_info_to_task(self, task: dict[str, Any]): # noqa: ANN201
300
+ def add_additional_info_to_task(self, task: dict[str, Any]) -> None:
247
301
  """タスクの付加的情報を、タスクに追加する。
248
302
  以下の列を追加する。
249
303
  * user_id
@@ -280,23 +334,29 @@ class AddingAdditionalInfoToTask:
280
334
 
281
335
  """
282
336
  # タスク作成日時
283
- task["created_datetime"] = self.get_task_created_datetime(task, task_histories)
337
+ task["created_datetime"] = get_task_creation_datetime(task, task_histories)
284
338
 
285
339
  # フェーズごとのタスク履歴情報を追加する
286
- self._add_task_history_info_by_phase(task, task_histories, phase=TaskPhase.ANNOTATION)
287
- self._add_task_history_info_by_phase(task, task_histories, phase=TaskPhase.INSPECTION)
288
- self._add_task_history_info_by_phase(task, task_histories, phase=TaskPhase.ACCEPTANCE)
340
+ for phase in TaskPhase:
341
+ # タスク履歴から取得した作業時間を設定
342
+ task[f"{phase.value}_worktime_hour"] = calculate_total_worktime_in_phase(task_histories, phase)
343
+ first_task_history = get_first_task_history(task_histories, phase)
344
+ self._add_task_history_info(task, first_task_history, column_prefix=f"first_{phase.value}")
289
345
 
290
346
  # 初めて受入が完了した日時
291
- task["first_acceptance_reached_datetime"] = self.get_first_acceptance_reached_datetime(task_histories)
292
- task["first_acceptance_completed_datetime"] = self.get_first_acceptance_completed_datetime(task_histories)
347
+ task["first_acceptance_reached_datetime"] = get_first_acceptance_reached_datetime(task_histories)
348
+ task["first_acceptance_completed_datetime"] = get_first_acceptance_completed_datetime(task_histories)
293
349
 
294
350
  # 受入完了日時を設定
295
- task["completed_datetime"] = self.get_completed_datetime(task, task_histories)
351
+ task["completed_datetime"] = get_completed_datetime(task, task_histories)
296
352
 
297
353
  # 抜取検査/受入によって、スキップされたかどうか
298
- task["inspection_is_skipped"] = self.is_inspection_phase_skipped(task_histories)
299
- task["acceptance_is_skipped"] = self.is_acceptance_phase_skipped(task_histories)
354
+ task["inspection_is_skipped"] = is_inspection_phase_skipped(task_histories)
355
+ task["acceptance_is_skipped"] = is_acceptance_phase_skipped(task_histories)
356
+
357
+ task["post_rejection_annotation_worktime_hour"] = get_post_rejection_annotation_worktime_hour(task_histories)
358
+ task["post_rejection_inspection_worktime_hour"] = get_post_rejection_inspection_worktime_hour(task_histories)
359
+ task["post_rejection_acceptance_worktime_hour"] = get_post_rejection_acceptance_worktime_hour(task_histories)
300
360
 
301
361
 
302
362
  class ListTasksAddedTaskHistoryMain:
@@ -373,9 +433,17 @@ class TasksAddedTaskHistoryOutput:
373
433
  for info in ["user_id", "username", "started_datetime", "worktime_hour"]
374
434
  ]
375
435
 
376
- return base_columns + task_history_columns
436
+ return (
437
+ base_columns
438
+ + task_history_columns
439
+ + [
440
+ "post_rejection_annotation_worktime_hour",
441
+ "post_rejection_inspection_worktime_hour",
442
+ "post_rejection_acceptance_worktime_hour",
443
+ ]
444
+ )
377
445
 
378
- def output(self, output_path: Path, output_format: FormatArgument): # noqa: ANN201
446
+ def output(self, output_path: Path, output_format: FormatArgument) -> None:
379
447
  task_list = self.task_list
380
448
  if len(task_list) == 0:
381
449
  logger.info("タスク一覧の件数が0件のため、出力しません。")
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import logging
5
5
  import multiprocessing
6
+ import sys
6
7
  import tempfile
7
8
  from collections import defaultdict
8
9
  from enum import Enum
@@ -15,6 +16,7 @@ from annofabapi.models import JobStatus, ProjectJobType, ProjectMemberRole
15
16
 
16
17
  import annofabcli
17
18
  from annofabcli.common.cli import (
19
+ COMMAND_LINE_ERROR_STATUS_CODE,
18
20
  PARALLELISM_CHOICES,
19
21
  ArgumentParser,
20
22
  CommandLine,
@@ -230,13 +232,16 @@ class PutTask(CommandLine):
230
232
 
231
233
  if args.csv is not None:
232
234
  csv_file = args.csv
233
- task_relation_dict = get_task_relation_dict(csv_file)
234
- main_obj.generate_task(api_with_creating_task, task_relation_dict)
235
+ task_relation_dict_from_csv = get_task_relation_dict(csv_file)
236
+ main_obj.generate_task(api_with_creating_task, task_relation_dict_from_csv)
235
237
 
236
238
  elif args.json is not None:
237
239
  # CSVファイルに変換する
238
- task_relation_dict = get_json_from_args(args.json)
239
- main_obj.generate_task(api_with_creating_task, task_relation_dict)
240
+ task_relation_dict_from_json = get_json_from_args(args.json)
241
+ if not isinstance(task_relation_dict_from_json, dict):
242
+ print("annofabcli task put: error: JSON形式が不正です。オブジェクトを指定してください。", file=sys.stderr) # noqa: T201
243
+ sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
244
+ main_obj.generate_task(api_with_creating_task, task_relation_dict_from_json)
240
245
 
241
246
 
242
247
  def main(args: argparse.Namespace) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annofabcli
3
- Version: 1.97.0
3
+ Version: 1.99.0
4
4
  Summary: Utility Command Line Interface for AnnoFab
5
5
  Home-page: https://github.com/kurusugawa-computer/annofab-cli
6
6
  License: MIT
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Utilities
21
21
  Requires-Dist: Pillow
22
- Requires-Dist: annofabapi (>=1.1,<2.0)
22
+ Requires-Dist: annofabapi (>=1.4.1,<2.0.0)
23
23
  Requires-Dist: bokeh (>=3.3,<3.7)
24
24
  Requires-Dist: dictdiffer
25
25
  Requires-Dist: isodate
@@ -1,6 +1,6 @@
1
1
  annofabcli/__init__.py,sha256=NMA7kFxmLlCiILQPHJa9mEuqXxtLALw_dwyXYsvz4VM,71
2
2
  annofabcli/__main__.py,sha256=JzfycqVG9ENhWOCxTouZwpHwWTSrI-grLsaMudxjyBM,5283
3
- annofabcli/__version__.py,sha256=dqx_NARSo9UcLPvxc6zJBGKIFxXd0yc57SlNZJUxSbg,132
3
+ annofabcli/__version__.py,sha256=3vcSVu6yWUIItVt25unwZ7jMczwQM4BnBXs-Kszfrkc,132
4
4
  annofabcli/annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  annofabcli/annotation/annotation_query.py,sha256=ke3W3RT1-WfFzwt-TXcQwGmghG34vcKJkM_jxgbNKjU,15922
6
6
  annofabcli/annotation/change_annotation_attributes.py,sha256=zHXyENZfbMGL_15xiK7Cy4cQ2sV0GjSVmKuPm3sOX7Y,17173
@@ -12,8 +12,10 @@ annofabcli/annotation/dump_annotation.py,sha256=YIl9eP6cfvMcE13CLooZO7siHzotc67b
12
12
  annofabcli/annotation/import_annotation.py,sha256=BlGAcXF9T19Ea1wtplUMPrOfiWYLBajK2XizOctdUtA,29304
13
13
  annofabcli/annotation/list_annotation.py,sha256=B8ZFI7SRQPy-TgbpaX-tzXhveViY17YuCPE9BuK9mrs,10790
14
14
  annofabcli/annotation/list_annotation_count.py,sha256=T9fbaoxWeDJIVgW_YgHRldbwrVZWiE-57lfJrDQrj80,6474
15
+ annofabcli/annotation/merge_segmentation.py,sha256=ldEQlcyCrog0KFYn92sTkScZQ7gmTjlujuCsKBemjhU,17950
16
+ annofabcli/annotation/remove_segmentation_overlap.py,sha256=gONYqaI0PMEGYJduB4JsmGBn3LCY9JtLSFgosWYyc3M,15957
15
17
  annofabcli/annotation/restore_annotation.py,sha256=naUEbt48ION9JSijCBR2aQdaoCrRu005tYq0vgUtyp0,14683
16
- annofabcli/annotation/subcommand_annotation.py,sha256=Xu4SOHLpo_kPZbvE8hQHr9NRduPTESqu7pYC4LGDlXg,1872
18
+ annofabcli/annotation/subcommand_annotation.py,sha256=ku9mzb7zZilHcjf1MFV1E7EJ8OvfSUDHpcunM38teto,2122
17
19
  annofabcli/annotation_specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
20
  annofabcli/annotation_specs/export_annotation_specs.py,sha256=eZF5fj2P5U22_5UKgbpsUhZEUvVPMPerMpN4miIcnSA,4821
19
21
  annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py,sha256=L0H0M31Amnm3ZEg9JdQe2A1vprsCLPTpj0LUFnP6GDY,8330
@@ -25,18 +27,18 @@ annofabcli/annotation_specs/list_annotation_specs_history.py,sha256=DtF8X8hA3N54
25
27
  annofabcli/annotation_specs/list_annotation_specs_label.py,sha256=5k7lDJS1oCi1JS0sLsjyczQ_9W5-jR2AOrZn8dAx1ys,11755
26
28
  annofabcli/annotation_specs/list_attribute_restriction.py,sha256=J1QhnUwRX09KRv2Gn7J_BITJu3IuIL3tkYe8CWcyQAw,14740
27
29
  annofabcli/annotation_specs/list_label_color.py,sha256=huHBAb-LVLe-tj5ZALiBkFgZlZSTvi2ZDsPYgJPDZ2Q,2356
28
- annofabcli/annotation_specs/put_label_color.py,sha256=uI56c-x2DRR6xmPXcJQO6dJv3qMO9j23EdBNndcOPD4,5730
30
+ annofabcli/annotation_specs/put_label_color.py,sha256=HJTczQiE7uwdnpHh0Lyz_WuvtyXOxYt8RThBXpvjKgA,6057
29
31
  annofabcli/annotation_specs/subcommand_annotation_specs.py,sha256=lhs4M5KHSGA59z-XsDuzFascZNbPxq-QnPHrUcntMf4,2384
30
32
  annofabcli/comment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- annofabcli/comment/delete_comment.py,sha256=oskNpugZdiug6Up9SkW7ettr-f031OXZhHwjU63NFVQ,11188
33
+ annofabcli/comment/delete_comment.py,sha256=EsQtUCq-HZtYmHQiXq4KbkgjqS4NPlC44MJQKgUzhnY,11458
32
34
  annofabcli/comment/download_comment_json.py,sha256=YfqUnMgvLgVFV7FJPIXwirREkQ2E63fXeCaF4hfwk8c,2338
33
35
  annofabcli/comment/list_all_comment.py,sha256=p5STKKQ4SV-vDvY1F3pKOrveZcteLTHIUEO-ci1nGes,6090
34
36
  annofabcli/comment/list_comment.py,sha256=gZSzeBeYLt0dhCi-8GcuEEB1NuJoXRdptp4oxkOtAMQ,4947
35
37
  annofabcli/comment/put_comment.py,sha256=yrxCtP9JkPvBtmZZ3vbm8XFYNKBTgwKwfGcauVvu5e8,12144
36
38
  annofabcli/comment/put_comment_simply.py,sha256=Y_RSKWMs8j5VAVV8iiJOOBHnJHEH5s3HLJrXnmmQT3o,8184
37
- annofabcli/comment/put_inspection_comment.py,sha256=ZkkAId5Hz--vtuiTJ1VUCeIme1BTUDetH2O6vtnkg_o,3599
39
+ annofabcli/comment/put_inspection_comment.py,sha256=F7m6lsbN0FHk0adwiapvD2UOv-8fbCdqfA9CtuJcnY8,3869
38
40
  annofabcli/comment/put_inspection_comment_simply.py,sha256=6oKYuihsOKkAKMzcLeUzgMu9SIJC9fWzM1VBis3asfo,6870
39
- annofabcli/comment/put_onhold_comment.py,sha256=TvJg-ojTONevgPefLRH2BK3xFNv2eiaHgYpTFajVDLo,3259
41
+ annofabcli/comment/put_onhold_comment.py,sha256=9tJ6zzOwqr80SqCSyPFEyqkWpVahHvEEsWIaHxPrB-A,3529
40
42
  annofabcli/comment/put_onhold_comment_simply.py,sha256=8w-E8aIK2SbHI9pnsfGF6IlVGC8qDt60Yjrr7Hfz1Rw,3308
41
43
  annofabcli/comment/subcommand_comment.py,sha256=gd8w8ArXM1Tq9VUduDgn8m4G6HwevRWJ36VBtGHg-5I,1537
42
44
  annofabcli/comment/utils.py,sha256=aUj7U6MtGh64F3Ko83y4NKPKyWAqcg-c1XLqjkmIpSk,350
@@ -67,7 +69,7 @@ annofabcli/filesystem/mask_user_info.py,sha256=Evmr9QhSpMG900bbOXbJNHwXHapUlNfvV
67
69
  annofabcli/filesystem/merge_annotation.py,sha256=MkGy1T9F-1tHq_BS_L_mTtgKpOMmXscrydzfSc0JKAo,10588
68
70
  annofabcli/filesystem/subcommand_filesystem.py,sha256=ZM2td5iZYIQ3TCI-9xAue8LugFlIc3WMRXrJqnjJ8-s,1186
69
71
  annofabcli/input_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- annofabcli/input_data/change_input_data_name.py,sha256=vhii3jHaEE_J8jcJ2bW66VxC9k-jtWN4tSD9vnatON4,9665
72
+ annofabcli/input_data/change_input_data_name.py,sha256=cQWoGTZKwr2_fU1WGALmRyJY39ruf9t4eMxpI06eT9g,9943
71
73
  annofabcli/input_data/copy_input_data.py,sha256=Lbyq2aW5lmJCPB8-WHdOI93a2ND5E0qOWrhBHRluQyw,14656
72
74
  annofabcli/input_data/delete_input_data.py,sha256=GLg58XNz9_Njq9eq2sc2BAzfLy9tJefcDYL6J4TOim4,8836
73
75
  annofabcli/input_data/delete_metadata_key_of_input_data.py,sha256=PVv9HXQVFLKQh-4RSHas_ckFxDxtqAzWnXnWYFFYy08,8464
@@ -75,7 +77,7 @@ annofabcli/input_data/download_input_data_json.py,sha256=vxGoeM3ZEggQbWiWsrDK0_G
75
77
  annofabcli/input_data/list_all_input_data.py,sha256=Kq261WCkma5UNKfMDT7O6Z-dzzqp-KP1wL0DvHe5fH8,9879
76
78
  annofabcli/input_data/list_all_input_data_merged_task.py,sha256=UaBJW-nMHytmQ4okg69Ew1jhC2vMNnRMgl2lEBabtUI,12850
77
79
  annofabcli/input_data/list_input_data.py,sha256=RBxsHyKg1bVIEQUFDkfrq-nJmEdEYNoCjJ2L2GgSfeU,11519
78
- annofabcli/input_data/put_input_data.py,sha256=x54C-rLJVzr1YF2GlMR0w0HJReOE3E7YKiBeuh0RsTI,17934
80
+ annofabcli/input_data/put_input_data.py,sha256=QrIe_RBXfGzpTYazy4cN8iSdHTQjq96sI0bXq6g1G_k,18233
79
81
  annofabcli/input_data/put_input_data_with_zip.py,sha256=SA4aMAwMBFgc9Lh0zmRCbmkXG4AMrcBqd5zeTSdr8lc,5566
80
82
  annofabcli/input_data/subcommand_input_data.py,sha256=X8EoxsF6PMiKrvk_r7PIe2D0WZuaPlgLJRuTiljPIdM,2048
81
83
  annofabcli/input_data/update_metadata_of_input_data.py,sha256=_cZh0GYGK6Lx5arKuTjblolkXsRdTlwuKcIHa8Nm5yQ,11583
@@ -131,12 +133,13 @@ annofabcli/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
131
133
  annofabcli/statistics/histogram.py,sha256=CvzDxT2cKLSnBGSqkZE6p92PayGxYYja1YyB24M4ALU,3245
132
134
  annofabcli/statistics/linegraph.py,sha256=0kr7jVBNMiM2ECYhv3Ry5RitElKerSl9ZKxbKzfiplI,12494
133
135
  annofabcli/statistics/list_annotation_attribute.py,sha256=87jjNCOXJUbWnmswMCLN7GTjGsBfqpFJ6hViWmnj8Y4,12557
134
- annofabcli/statistics/list_annotation_count.py,sha256=GVlYYubWqjNLJD7GGxv1WivCYv0rMgYxRLeZsM3Y8hA,50344
135
- annofabcli/statistics/list_annotation_duration.py,sha256=1OFvhi5QQJDcUO4iHi3lV2fDFK8ZFaAP8vlbGpVR2s0,31907
136
+ annofabcli/statistics/list_annotation_attribute_filled_count.py,sha256=vwWeFHwTnEMdrLBauIKPFDkUCa6lXXd0GQgUAQ0LCqU,28890
137
+ annofabcli/statistics/list_annotation_count.py,sha256=nzmlHRCWt5mjeksZkeQyWqm4UaCa9SrdbNtuX9TPP5w,52907
138
+ annofabcli/statistics/list_annotation_duration.py,sha256=N7nnVUDfX_thIapqe6-z_MqReiIqNS8rhp6ewRnvXBU,32027
136
139
  annofabcli/statistics/list_video_duration.py,sha256=uNeMteRBX2JG_AWmcgMJj0Jzbq_qF7bvAwr25GmeIiw,9124
137
140
  annofabcli/statistics/list_worktime.py,sha256=C7Yu3IOW2EvhkJJv6gY3hNdS9_TOLmT_9LZsB7vLJ1o,6493
138
141
  annofabcli/statistics/scatter.py,sha256=IUCwXix9GbZb6V82wjjb5q2eamrT5HQsU_bzDTjAFnM,11011
139
- annofabcli/statistics/subcommand_statistics.py,sha256=JPixzhJGClQYVH6Tgby3KTD85df-aEJKvLAjfJCv-1E,2259
142
+ annofabcli/statistics/subcommand_statistics.py,sha256=mx18Fgxz2eG4LrF-x0vISw2qh9aLommxuQLD8cfoZhw,2416
140
143
  annofabcli/statistics/summarize_task_count.py,sha256=8OH6BBRYRjHJkWRTjU0A0OfXa7f3NIRHrxPNFlRt_hM,9707
141
144
  annofabcli/statistics/summarize_task_count_by_task_id_group.py,sha256=TSSmcFv615NLcq6uqXmg3ilYqSHl3A5qp90msVQM1gE,8646
142
145
  annofabcli/statistics/summarize_task_count_by_user.py,sha256=TRoJXpt2HOVb8QO2YtRejkOAxyK80_NsPt3Vk9es9C8,6948
@@ -150,7 +153,7 @@ annofabcli/statistics/visualization/dataframe/input_data_count.py,sha256=wDRFtoI
150
153
  annofabcli/statistics/visualization/dataframe/inspection_comment_count.py,sha256=RxpQzRy4U2hKEpgbksUXotcxH2sKz__NO20mxpMqK1w,4382
151
154
  annofabcli/statistics/visualization/dataframe/productivity_per_date.py,sha256=tMap7E3z7hibon1zJnZRJnbMmtzqh04ocoV0oxBpssU,27249
152
155
  annofabcli/statistics/visualization/dataframe/project_performance.py,sha256=hdTMPvLfGDMZFjpIl58GtTEOopsOvitbdaj5hQAEp8o,8496
153
- annofabcli/statistics/visualization/dataframe/task.py,sha256=KanuLy67ZGORdLry21eN7uSNzkoJvIre1JN7Bq-fRlg,23452
156
+ annofabcli/statistics/visualization/dataframe/task.py,sha256=Heb0sx7T6KI422aFVKQUCP2wqD_czpM4KMsgqb4wjVw,24269
154
157
  annofabcli/statistics/visualization/dataframe/task_history.py,sha256=3b9e4ok6yKE5x647KzRqvp01P33XMAHLEEbLJ5GCmRo,2760
155
158
  annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py,sha256=AtlbeNIkttjLtuxtZYCyZin4eVKRvcYEMnLzEZtZUlY,13134
156
159
  annofabcli/statistics/visualization/dataframe/user.py,sha256=EHn7nlf6D6UX-gsVXy8m_3QaCsHsUhr0iy2rbNozOgc,1707
@@ -184,8 +187,8 @@ annofabcli/task/download_task_json.py,sha256=Ocjecmdf2WV_Sq3u1InfMLIsT3XSw0ojyJm
184
187
  annofabcli/task/list_all_tasks.py,sha256=F9GpzzgWffF3lUeGrFIvjweq-iEwJ1c-g8usskO_2dE,6506
185
188
  annofabcli/task/list_all_tasks_added_task_history.py,sha256=fkdiuo64iS7xxvIfGKzSiUPPEMiCVnJjjcAtMxe2Ngs,9551
186
189
  annofabcli/task/list_tasks.py,sha256=O4jjp_zdmurcGNWXFp9JXHJsH4nhlR5e3ok96YnD1SI,10237
187
- annofabcli/task/list_tasks_added_task_history.py,sha256=7avkLYFErcdSNxGc8CQXr4FFIF1z9FtQJBSloD-tzBI,19675
188
- annofabcli/task/put_tasks.py,sha256=hT2xPowJmcNJhjxoAm-MFiKTw_RFcJUYlpeanegVrAU,13400
190
+ annofabcli/task/list_tasks_added_task_history.py,sha256=Yjvv5fJjSjJ-v9bhwl4PDyU8aeT94LLMBcPhmUWlFUI,21639
191
+ annofabcli/task/put_tasks.py,sha256=c_Vw5qnDlfpa21S-aMVxmXy8pF-z_YEUi6a0c3IV9gc,13770
189
192
  annofabcli/task/put_tasks_by_count.py,sha256=MUHfWhqtSAXnB3O36p3bMSSgQ_3Zek9GT5qRvHGx8Lo,6041
190
193
  annofabcli/task/reject_tasks.py,sha256=5ByAN6VnKwvU5BT_cfsHwA1jLDl74bonqk3bwtnrkPU,23139
191
194
  annofabcli/task/subcommand_task.py,sha256=L_5Dwe58eblrtOrUYxjJAvkSmu6savRUxIqGjsFq-R4,2436
@@ -200,8 +203,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=hQLVbQ0
200
203
  annofabcli/task_history_event/list_all_task_history_event.py,sha256=JQEgwOIXbbTIfeX23AVaoySHViOR9UGm9uoXuhVEBqo,6446
201
204
  annofabcli/task_history_event/list_worktime.py,sha256=9jsRYa2C9bva8E1Aqxv9CCKDuCP0MvbiaIyQFTDpjqY,13150
202
205
  annofabcli/task_history_event/subcommand_task_history_event.py,sha256=mJVJoT4RXk4HWnY7-Nrsl4If-gtaIIEXd2z7eFZwM2I,1260
203
- annofabcli-1.97.0.dist-info/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
204
- annofabcli-1.97.0.dist-info/METADATA,sha256=71xtnSNlS97JSBeUVYWYcoas9gqXjL9v735krW5zioc,5626
205
- annofabcli-1.97.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
206
- annofabcli-1.97.0.dist-info/entry_points.txt,sha256=A8vlN9fiMhbYRcdBfSpl7piYzAwvkMhRXIPQUAvQFUo,55
207
- annofabcli-1.97.0.dist-info/RECORD,,
206
+ annofabcli-1.99.0.dist-info/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
207
+ annofabcli-1.99.0.dist-info/METADATA,sha256=YTn2PV9tc0pWHXD1sQ88yiulbIhZc29WlLIOcz9jCE4,5630
208
+ annofabcli-1.99.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
209
+ annofabcli-1.99.0.dist-info/entry_points.txt,sha256=A8vlN9fiMhbYRcdBfSpl7piYzAwvkMhRXIPQUAvQFUo,55
210
+ annofabcli-1.99.0.dist-info/RECORD,,