annofabcli 1.106.0__py3-none-any.whl → 1.106.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.
@@ -63,12 +63,12 @@ class DumpAnnotationMain:
63
63
  outer_file_path = outer_dir / f"{annotation_id}"
64
64
  self.service.wrapper.download(detail["url"], outer_file_path)
65
65
 
66
- def dump_annotation_for_input_data(self, task_id: str, input_data_id: str, task_dir: Path) -> None:
67
- editor_annotation, _ = self.service.api.get_editor_annotation(self.project_id, task_id, input_data_id, query_params={"v": "2"})
66
+ def dump_annotation_for_input_data(self, task_id: str, input_data_id: str, task_dir: Path, *, task_history_id: Optional[str] = None) -> None:
67
+ editor_annotation, _ = self.service.api.get_editor_annotation(self.project_id, task_id, input_data_id, query_params={"v": "2", "task_history_id": task_history_id})
68
68
  json_path = task_dir / f"{input_data_id}.json"
69
69
  self.dump_editor_annotation(editor_annotation=editor_annotation, json_path=json_path)
70
70
 
71
- def dump_annotation_for_task(self, task_id: str, output_dir: Path, *, task_index: Optional[int] = None) -> bool:
71
+ def dump_annotation_for_task(self, task_id: str, output_dir: Path, *, task_index: Optional[int] = None, task_history_index: Optional[int] = None) -> bool:
72
72
  """
73
73
  タスク配下のアノテーションをファイルに保存する。
74
74
 
@@ -85,15 +85,23 @@ class DumpAnnotationMain:
85
85
  logger.warning(f"task_id = '{task_id}' のタスクは存在しません。スキップします。")
86
86
  return False
87
87
 
88
+ task_history_id: Optional[str] = None
89
+ if task_history_index is not None:
90
+ task_histories, _ = self.service.api.get_task_histories(self.project_id, task_id)
91
+ if task_history_index >= len(task_histories):
92
+ logger.warning(f"task_id='{task_id}' :: task_history_index='{task_history_index}'のタスク履歴は存在しません。タスク履歴は{len(task_histories)}件です。スキップします。")
93
+ return False
94
+ task_history_id = task_histories[task_history_index]["task_history_id"]
95
+
88
96
  input_data_id_list = task["input_data_id_list"]
89
97
  task_dir = output_dir / task_id
90
98
  task_dir.mkdir(exist_ok=True, parents=True)
91
- logger.debug(f"{logger_prefix}task_id = '{task_id}' のアノテーション情報を '{task_dir}' ディレクトリに保存します。")
99
+ logger.debug(f"{logger_prefix}task_id = '{task_id}' のアノテーション情報を '{task_dir}' ディレクトリに保存します。 :: task_history_id='{task_history_id}'")
92
100
 
93
101
  is_failure = False
94
102
  for input_data_id in input_data_id_list:
95
103
  try:
96
- self.dump_annotation_for_input_data(task_id, input_data_id, task_dir=task_dir)
104
+ self.dump_annotation_for_input_data(task_id, input_data_id, task_dir=task_dir, task_history_id=task_history_id)
97
105
  except Exception:
98
106
  logger.warning(f"タスク'{task_id}', 入力データ'{input_data_id}' のアノテーション情報のダンプに失敗しました。", exc_info=True)
99
107
  is_failure = True
@@ -101,15 +109,15 @@ class DumpAnnotationMain:
101
109
 
102
110
  return not is_failure
103
111
 
104
- def dump_annotation_for_task_wrapper(self, tpl: tuple[int, str], output_dir: Path) -> bool:
112
+ def dump_annotation_for_task_wrapper(self, tpl: tuple[int, str], output_dir: Path, *, task_history_index: Optional[int] = None) -> bool:
105
113
  task_index, task_id = tpl
106
114
  try:
107
- return self.dump_annotation_for_task(task_id, output_dir=output_dir, task_index=task_index)
115
+ return self.dump_annotation_for_task(task_id, output_dir=output_dir, task_index=task_index, task_history_index=task_history_index)
108
116
  except Exception: # pylint: disable=broad-except
109
117
  logger.warning(f"タスク'{task_id}'のアノテーション情報のダンプに失敗しました。", exc_info=True)
110
118
  return False
111
119
 
112
- def dump_annotation(self, task_id_list: list[str], output_dir: Path, parallelism: Optional[int] = None): # noqa: ANN201
120
+ def dump_annotation(self, task_id_list: list[str], output_dir: Path, *, task_history_index: Optional[int] = None, parallelism: Optional[int] = None) -> None:
113
121
  project_title = self.facade.get_project_title(self.project_id)
114
122
  logger.info(f"プロジェクト'{project_title}'に対して、タスク{len(task_id_list)} 件のアノテーションをファイルに保存します。")
115
123
 
@@ -118,7 +126,7 @@ class DumpAnnotationMain:
118
126
  success_count = 0
119
127
 
120
128
  if parallelism is not None:
121
- func = functools.partial(self.dump_annotation_for_task_wrapper, output_dir=output_dir)
129
+ func = functools.partial(self.dump_annotation_for_task_wrapper, task_history_index=task_history_index, output_dir=output_dir)
122
130
  with multiprocessing.Pool(parallelism) as pool:
123
131
  result_bool_list = pool.map(func, enumerate(task_id_list))
124
132
  success_count = len([e for e in result_bool_list if e])
@@ -126,7 +134,7 @@ class DumpAnnotationMain:
126
134
  else:
127
135
  for task_index, task_id in enumerate(task_id_list):
128
136
  try:
129
- result = self.dump_annotation_for_task(task_id, output_dir=output_dir, task_index=task_index)
137
+ result = self.dump_annotation_for_task(task_id, output_dir=output_dir, task_history_index=task_history_index, task_index=task_index)
130
138
  if result:
131
139
  success_count += 1
132
140
  except Exception:
@@ -149,7 +157,7 @@ class DumpAnnotation(CommandLine):
149
157
  super().validate_project(project_id, project_member_roles=None)
150
158
 
151
159
  main_obj = DumpAnnotationMain(self.service, project_id)
152
- main_obj.dump_annotation(task_id_list, output_dir=output_dir, parallelism=args.parallelism)
160
+ main_obj.dump_annotation(task_id_list, output_dir=output_dir, parallelism=args.parallelism, task_history_index=args.task_history_index)
153
161
 
154
162
 
155
163
  def main(args: argparse.Namespace) -> None:
@@ -166,6 +174,13 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
166
174
 
167
175
  parser.add_argument("-o", "--output_dir", type=str, required=True, help="出力先ディレクトリのパス")
168
176
 
177
+ parser.add_argument(
178
+ "--task_history_index",
179
+ type=int,
180
+ help="指定したタスク履歴のインデックス(ゼロ始まり)で付与されたアノテーション情報をダンプします。過去のアノテーション結果をダンプする場合に指定します。"
181
+ "ただし、過去のアノテーションデータは30日間しか保持されません。30日より前に更新されたアノテーションをダンプした場合は、アノテーションが0件の状態でダンプされます。",
182
+ )
183
+
169
184
  parser.add_argument(
170
185
  "--parallelism",
171
186
  type=int,
@@ -152,13 +152,26 @@ class AnnotationConverter:
152
152
 
153
153
  def _convert_attribute_value( # noqa: PLR0911, PLR0912
154
154
  self,
155
- attribute_value: Union[str, int, bool], # noqa: FBT001
155
+ attribute_value: Optional[Union[str, int, bool]], # noqa: FBT001
156
156
  additional_data_type: AdditionalDataDefinitionType,
157
157
  attribute_name: str,
158
158
  choices: list[dict[str, Any]],
159
159
  *,
160
160
  log_message_suffix: str,
161
161
  ) -> Optional[dict[str, Any]]:
162
+ """
163
+ JSONに記載されている属性値を、APIに渡すための `AdditionalDataValue`スキーマに変換します。
164
+
165
+ Args:
166
+ attribute_value: 属性値。 `None`の場合は、Noneを返します。
167
+ additional_data_type: 属性の型
168
+ attribute_name: 属性名
169
+ choices: 選択肢情報。`AdditionalDataDefinitionType.CHOICE`や`AdditionalDataDefinitionType.SELECT`の場合に利用する。
170
+ log_message_suffix: ログメッセージのサフィックス
171
+ """
172
+ if attribute_value is None:
173
+ return None
174
+
162
175
  if additional_data_type == AdditionalDataDefinitionType.FLAG:
163
176
  if not isinstance(attribute_value, bool):
164
177
  message = f"属性'{attribute_name}'に対応する属性値の型は bool である必要があります。 :: attribute_value='{attribute_value}', additional_data_type='{additional_data_type}' :: {log_message_suffix}" # noqa: E501
@@ -83,6 +83,8 @@ class CopyProject(CommandLine):
83
83
 
84
84
  if copied_targets is not None:
85
85
  logger.info(f"コピー対象: {[e.value for e in copied_targets]}")
86
+ else:
87
+ logger.info("コピー対象: None")
86
88
 
87
89
  confirm_message = f"プロジェクト'{src_project_title}'(project_id='{src_project_id}')を、プロジェクト'{dest_title}'(project_id='{dest_project_id}') にコピーしますか?"
88
90
  if not self.confirm_processing(confirm_message):
@@ -111,7 +113,7 @@ class CopyProject(CommandLine):
111
113
  max_job_access=DEFAULT_WAIT_OPTIONS.max_tries,
112
114
  )
113
115
  if result:
114
- logger.info("プロジェクトのコピーが完了しました。")
116
+ logger.info(f"プロジェクト'{src_project_title}'のコピーが完了しました。 :: dest_project_id='{dest_project_id}', dest_project_title='{dest_title}'")
115
117
  else:
116
118
  logger.info("プロジェクトのコピーは実行中 または 失敗しました。")
117
119
 
@@ -221,7 +221,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
221
221
  def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
222
222
  subcommand_name = "copy"
223
223
  subcommand_help = "タスクをコピーします。"
224
- description = "タスクをコピーします。"
224
+ description = "タスクをコピーします。タスクに含まれるアノテーションはコピーされません。"
225
225
  epilog = "オーナロールを持つユーザで実行してください。"
226
226
 
227
227
  parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description, epilog=epilog)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annofabcli
3
- Version: 1.106.0
3
+ Version: 1.106.2
4
4
  Summary: Utility Command Line Interface for AnnoFab
5
5
  Author: Kurusugawa Computer Inc.
6
6
  License: MIT
@@ -9,8 +9,8 @@ annofabcli/annotation/change_annotation_properties.py,sha256=Kp_LZ5sSoVmmjGE80AB
9
9
  annofabcli/annotation/copy_annotation.py,sha256=Pih2k3vvpgfT3Ovb3gZw2L_8fK_ws_wKR7ARYG5hG_8,18407
10
10
  annofabcli/annotation/delete_annotation.py,sha256=hQApNrx2Ci1bBWk0dRGA0oJkIgDHwl6Jy0-33gYF6jo,22989
11
11
  annofabcli/annotation/download_annotation_zip.py,sha256=P_ZpdqIaSFEmB8jjpdykcRhh2tVlHxSlXFrYreJjShE,3282
12
- annofabcli/annotation/dump_annotation.py,sha256=CJ10zJUm9C1M1VhB_ZtdkWPncrZTx9h9JyxmLn7f3lo,8397
13
- annofabcli/annotation/import_annotation.py,sha256=39w-LwhNrT4-20NR5Hy-cn_cUFVVWVnMp9x1d8aITn0,33369
12
+ annofabcli/annotation/dump_annotation.py,sha256=Q-p6f5XBs7khDgrfY5Q3CGLBMKEerJWO_CQ8_73UXVM,9972
13
+ annofabcli/annotation/import_annotation.py,sha256=xCNnVEd8Ss6aN1XdDsTDf3AuRyB1ocAr0ofbxX1bWSc,33997
14
14
  annofabcli/annotation/list_annotation.py,sha256=uKcOuGC7lzd6vVbzizkiZtYdXJ7EzY0iifuiqKl2wQM,10707
15
15
  annofabcli/annotation/list_annotation_count.py,sha256=T9fbaoxWeDJIVgW_YgHRldbwrVZWiE-57lfJrDQrj80,6474
16
16
  annofabcli/annotation/merge_segmentation.py,sha256=kIsCeXtJxzd6nobQPpi0fscaRDlTx3tg1qpy5PDfSJI,18107
@@ -114,7 +114,7 @@ annofabcli/organization_member/subcommand_organization_member.py,sha256=iK-Y4PxG
114
114
  annofabcli/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
115
  annofabcli/project/change_organization_of_project.py,sha256=yeR8Xu4nmvrA4jGfsPDmFxf-qJxvEVQAvluierJRMxc,11300
116
116
  annofabcli/project/change_project_status.py,sha256=AQQFFYjUnwrFshuYS_xSGyLAp2lJ-7SSIuaigPTLBYk,8657
117
- annofabcli/project/copy_project.py,sha256=9UD2ElqodOyOwlvT-yqBVozA_9FivBWGLcEckG9B7m0,6972
117
+ annofabcli/project/copy_project.py,sha256=O1TooJ0VVbRP84d4Y-8e6Z7pKMwhBEwPu3EBv15hPlY,7131
118
118
  annofabcli/project/diff_projects.py,sha256=OsR0otV6MOCbUgGEDsYganu5cuHYZbpHbBAqujwBAw8,16531
119
119
  annofabcli/project/list_project.py,sha256=OWnbCyShI4ceugxML8kSAD2nkyld5MlpXfbvQu2__4Y,10367
120
120
  annofabcli/project/put_project.py,sha256=dsRJfx2ga4mmYci40q91UzXeQN6m_3fMU9mrcFRpY9Q,6033
@@ -186,7 +186,7 @@ annofabcli/task/change_operator.py,sha256=1LLpez79ezW_MqcNN608kjxp3xTS1uRRioqcug
186
186
  annofabcli/task/change_status_to_break.py,sha256=hwdFTFW-zV0VxuinoBB5n6mvHJ7g9ChjrSOXZcNk88w,8621
187
187
  annofabcli/task/change_status_to_on_hold.py,sha256=vWRyk6IK3HcgTWDIbbhXzsrtuoa7OlXCf8CvUpFp_Uw,12981
188
188
  annofabcli/task/complete_tasks.py,sha256=ssg_Z7ADRQRXvXgK2k5TEmvbRjrJQ33cXeb8kG8Y3jc,24917
189
- annofabcli/task/copy_tasks.py,sha256=9TN0oykJbTZHSF8p16CHC4Ezay5Ezx6-S_p0-FRUtms,8667
189
+ annofabcli/task/copy_tasks.py,sha256=66C1NQq46d6HenLpQCnkTjiXedL4iVtCu6qUimosVyU,8742
190
190
  annofabcli/task/delete_metadata_key_of_task.py,sha256=Cjqe3fWKeRzVxxlrGyt3TS-x1riD55LnNXLIS9JPoTw,8029
191
191
  annofabcli/task/delete_tasks.py,sha256=7T5eNCMW06ABekNGLwhTitDK5qn0tiPKrEXyJXyQNvs,13098
192
192
  annofabcli/task/download_task_json.py,sha256=Ocjecmdf2WV_Sq3u1InfMLIsT3XSw0ojyJmJbhv2sgg,2803
@@ -209,8 +209,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=hQLVbQ0
209
209
  annofabcli/task_history_event/list_all_task_history_event.py,sha256=EeKMyPUxGwYCFtWQHHW954ZserGm8lUqrwNnV1iX9X4,6830
210
210
  annofabcli/task_history_event/list_worktime.py,sha256=Y7Pu5DP7scPf7HPt6CTiTvB1_5_Nfi1bStUIaCpkhII,15507
211
211
  annofabcli/task_history_event/subcommand_task_history_event.py,sha256=mJVJoT4RXk4HWnY7-Nrsl4If-gtaIIEXd2z7eFZwM2I,1260
212
- annofabcli-1.106.0.dist-info/METADATA,sha256=FqGLzIdUm7grQhd5oMncx6H3AhyuoUC0Na385BczmS4,5286
213
- annofabcli-1.106.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
214
- annofabcli-1.106.0.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
215
- annofabcli-1.106.0.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
216
- annofabcli-1.106.0.dist-info/RECORD,,
212
+ annofabcli-1.106.2.dist-info/METADATA,sha256=8kDxW3p5iajthMP9TJNcPUUEhZMOK1HCqPC83tRHcqU,5286
213
+ annofabcli-1.106.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
214
+ annofabcli-1.106.2.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
215
+ annofabcli-1.106.2.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
216
+ annofabcli-1.106.2.dist-info/RECORD,,