annofabcli 1.100.4__py3-none-any.whl → 1.101.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annofabcli/__init__.py +6 -4
- annofabcli/annotation/change_annotation_attributes.py +3 -2
- annofabcli/annotation/change_annotation_properties.py +8 -7
- annofabcli/annotation/copy_annotation.py +4 -4
- annofabcli/annotation/delete_annotation.py +254 -29
- annofabcli/annotation/dump_annotation.py +14 -7
- annofabcli/annotation/import_annotation.py +304 -227
- annofabcli/annotation/restore_annotation.py +7 -7
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +1 -1
- annofabcli/annotation_specs/list_annotation_specs_choice.py +1 -1
- annofabcli/annotation_specs/list_annotation_specs_label.py +1 -1
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +1 -1
- annofabcli/comment/delete_comment.py +7 -5
- annofabcli/comment/put_comment.py +1 -1
- annofabcli/comment/put_comment_simply.py +7 -5
- annofabcli/common/cli.py +10 -10
- annofabcli/common/download.py +28 -29
- annofabcli/common/image.py +4 -2
- annofabcli/input_data/delete_input_data.py +4 -4
- annofabcli/input_data/update_metadata_of_input_data.py +1 -1
- annofabcli/instruction/upload_instruction.py +2 -2
- annofabcli/statistics/visualize_statistics.py +1 -7
- annofabcli/supplementary/delete_supplementary_data.py +8 -4
- {annofabcli-1.100.4.dist-info → annofabcli-1.101.0.dist-info}/METADATA +8 -2
- {annofabcli-1.100.4.dist-info → annofabcli-1.101.0.dist-info}/RECORD +28 -29
- annofabcli/__version__.py +0 -7
- {annofabcli-1.100.4.dist-info → annofabcli-1.101.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.100.4.dist-info → annofabcli-1.101.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.100.4.dist-info → annofabcli-1.101.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -70,7 +70,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
70
70
|
s3_path = self.service.wrapper.upload_data_to_s3(self.project_id, f, content_type="image/png")
|
|
71
71
|
detail.path = s3_path
|
|
72
72
|
else:
|
|
73
|
-
logger.warning(f"annotation_id={detail.annotation_id}
|
|
73
|
+
logger.warning(f"annotation_id='{detail.annotation_id}' :: data_holding_typeが'outer'なのにpathがNoneです。")
|
|
74
74
|
|
|
75
75
|
return detail
|
|
76
76
|
|
|
@@ -100,7 +100,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
100
100
|
|
|
101
101
|
old_annotation, _ = self.service.api.get_editor_annotation(self.project_id, task_id, input_data_id)
|
|
102
102
|
|
|
103
|
-
logger.info(f"task_id={task_id}, input_data_id={input_data_id}
|
|
103
|
+
logger.info(f"task_id='{task_id}', input_data_id='{input_data_id}' :: アノテーションをリストアします。")
|
|
104
104
|
request_body = self.parser_to_request_body(parser)
|
|
105
105
|
|
|
106
106
|
updated_datetime = old_annotation["updated_datetime"] if old_annotation is not None else None
|
|
@@ -118,7 +118,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
118
118
|
success_count += 1
|
|
119
119
|
except Exception: # pylint: disable=broad-except
|
|
120
120
|
logger.warning(
|
|
121
|
-
f"task_id={parser.task_id}, input_data_id={parser.input_data_id} のアノテーションのリストアに失敗しました。",
|
|
121
|
+
f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}' のアノテーションのリストアに失敗しました。",
|
|
122
122
|
exc_info=True,
|
|
123
123
|
)
|
|
124
124
|
|
|
@@ -139,10 +139,10 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
139
139
|
"""
|
|
140
140
|
logger_prefix = f"{task_index + 1!s} 件目: " if task_index is not None else ""
|
|
141
141
|
task_id = task_parser.task_id
|
|
142
|
-
if not self.confirm_processing(f"task_id={task_id} のアノテーションをリストアしますか?"):
|
|
142
|
+
if not self.confirm_processing(f"task_id='{task_id}' のアノテーションをリストアしますか?"):
|
|
143
143
|
return False
|
|
144
144
|
|
|
145
|
-
logger.info(f"{logger_prefix}task_id={task_id} に対して処理します。")
|
|
145
|
+
logger.info(f"{logger_prefix}task_id='{task_id}' に対して処理します。")
|
|
146
146
|
|
|
147
147
|
task = self.service.wrapper.get_task_or_none(self.project_id, task_id)
|
|
148
148
|
if task is None:
|
|
@@ -197,7 +197,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
197
197
|
try:
|
|
198
198
|
return self.execute_task(task_parser, task_index=task_index)
|
|
199
199
|
except Exception: # pylint: disable=broad-except
|
|
200
|
-
logger.warning(f"task_id={task_parser.task_id} のアノテーションのリストアに失敗しました。", exc_info=True)
|
|
200
|
+
logger.warning(f"task_id='{task_parser.task_id}' のアノテーションのリストアに失敗しました。", exc_info=True)
|
|
201
201
|
return False
|
|
202
202
|
|
|
203
203
|
def main( # noqa: ANN201
|
|
@@ -246,7 +246,7 @@ class RestoreAnnotationMain(CommandLineWithConfirm):
|
|
|
246
246
|
if result:
|
|
247
247
|
success_count += 1
|
|
248
248
|
except Exception:
|
|
249
|
-
logger.warning(f"task_id={task_parser.task_id}
|
|
249
|
+
logger.warning(f"task_id='{task_parser.task_id}' :: アノテーションのリストアに失敗しました。", exc_info=True)
|
|
250
250
|
continue
|
|
251
251
|
finally:
|
|
252
252
|
task_count += 1
|
|
@@ -162,7 +162,7 @@ class PrintAnnotationSpecsAttribute(CommandLine):
|
|
|
162
162
|
return None
|
|
163
163
|
history = histories[-(before + 1)]
|
|
164
164
|
logger.info(
|
|
165
|
-
f"{history['updated_datetime']}のアノテーション仕様を出力します。history_id={history['history_id']}, comment={history['comment']}"
|
|
165
|
+
f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
|
|
166
166
|
)
|
|
167
167
|
return history["history_id"]
|
|
168
168
|
|
|
@@ -134,7 +134,7 @@ class PrintAnnotationSpecsAttribute(CommandLine):
|
|
|
134
134
|
return None
|
|
135
135
|
history = histories[-(before + 1)]
|
|
136
136
|
logger.info(
|
|
137
|
-
f"{history['updated_datetime']}のアノテーション仕様を出力します。history_id={history['history_id']}, comment={history['comment']}"
|
|
137
|
+
f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
|
|
138
138
|
)
|
|
139
139
|
return history["history_id"]
|
|
140
140
|
|
|
@@ -120,7 +120,7 @@ class PrintAnnotationSpecsLabel(CommandLine):
|
|
|
120
120
|
return None
|
|
121
121
|
history = histories[-(before + 1)]
|
|
122
122
|
logger.info(
|
|
123
|
-
f"{history['updated_datetime']}のアノテーション仕様を出力します。history_id={history['history_id']}, comment={history['comment']}"
|
|
123
|
+
f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
|
|
124
124
|
)
|
|
125
125
|
return history["history_id"]
|
|
126
126
|
|
|
@@ -122,7 +122,7 @@ class PrintAnnotationSpecsLabelAndAttribute(CommandLine):
|
|
|
122
122
|
return None
|
|
123
123
|
history = histories[-(before + 1)]
|
|
124
124
|
logger.info(
|
|
125
|
-
f"{history['updated_datetime']}のアノテーション仕様を出力します。history_id={history['history_id']}, comment={history['comment']}"
|
|
125
|
+
f"{history['updated_datetime']}のアノテーション仕様を出力します。 :: history_id='{history['history_id']}', comment='{history['comment']}'"
|
|
126
126
|
)
|
|
127
127
|
return history["history_id"]
|
|
128
128
|
|
|
@@ -61,7 +61,7 @@ class DeleteCommentMain(CommandLineWithConfirm):
|
|
|
61
61
|
for comment_id in comment_ids:
|
|
62
62
|
if comment_id not in old_comment_ids:
|
|
63
63
|
logger.warning(
|
|
64
|
-
f"task_id={task['task_id']}, input_data_id={input_data_id}
|
|
64
|
+
f"task_id='{task['task_id']}', input_data_id='{input_data_id}' :: comment_id='{comment_id}'のコメントは存在しません。",
|
|
65
65
|
)
|
|
66
66
|
continue
|
|
67
67
|
request_body.append(_convert(comment_id))
|
|
@@ -152,14 +152,16 @@ class DeleteCommentMain(CommandLineWithConfirm):
|
|
|
152
152
|
self.service.api.batch_update_comments(self.project_id, task_id, input_data_id, request_body=request_body)
|
|
153
153
|
added_comments_count += 1
|
|
154
154
|
logger.debug(
|
|
155
|
-
f"{logging_prefix}
|
|
155
|
+
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(request_body)}件のコメントを削除しました。"
|
|
156
156
|
)
|
|
157
157
|
else:
|
|
158
|
-
logger.warning(
|
|
158
|
+
logger.warning(
|
|
159
|
+
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: 削除できるコメントは存在しませんでした。"
|
|
160
|
+
)
|
|
159
161
|
|
|
160
162
|
except Exception: # pylint: disable=broad-except
|
|
161
163
|
logger.warning(
|
|
162
|
-
f"{logging_prefix}
|
|
164
|
+
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの削除に失敗しました。",
|
|
163
165
|
exc_info=True,
|
|
164
166
|
)
|
|
165
167
|
|
|
@@ -198,7 +200,7 @@ class DeleteCommentMain(CommandLineWithConfirm):
|
|
|
198
200
|
)
|
|
199
201
|
added_comments_count += result
|
|
200
202
|
except Exception: # pylint: disable=broad-except
|
|
201
|
-
logger.warning(f"task_id={task_id}
|
|
203
|
+
logger.warning(f"task_id='{task_id}' :: コメントの削除に失敗しました。", exc_info=True)
|
|
202
204
|
continue
|
|
203
205
|
|
|
204
206
|
logger.info(f"{added_comments_count} / {comments_count} 件の入力データからコメントを削除しました。")
|
|
@@ -189,7 +189,7 @@ class PutCommentMain(CommandLineWithConfirm):
|
|
|
189
189
|
)
|
|
190
190
|
except Exception: # pylint: disable=broad-except
|
|
191
191
|
logger.warning(
|
|
192
|
-
f"{logging_prefix} :: task_id={task_id}, input_data_id={input_data_id}
|
|
192
|
+
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの付与に失敗しました。",
|
|
193
193
|
exc_info=True,
|
|
194
194
|
)
|
|
195
195
|
|
|
@@ -144,24 +144,26 @@ class PutCommentSimplyMain(CommandLineWithConfirm):
|
|
|
144
144
|
# コメントを付与する
|
|
145
145
|
request_body = self._create_request_body(task=changed_task, comment_info=comment_info)
|
|
146
146
|
self.service.api.batch_update_comments(self.project_id, task_id, input_data_id, request_body=request_body)
|
|
147
|
-
logger.debug(f"{logging_prefix}
|
|
147
|
+
logger.debug(f"{logging_prefix} :: task_id='{task_id}' のタスクにコメントを付与しました。")
|
|
148
148
|
return True # noqa: TRY300
|
|
149
149
|
except Exception: # pylint: disable=broad-except
|
|
150
|
-
logger.warning(
|
|
150
|
+
logger.warning(
|
|
151
|
+
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの付与に失敗しました。", exc_info=True
|
|
152
|
+
)
|
|
151
153
|
return False
|
|
152
154
|
finally:
|
|
153
155
|
self.service.wrapper.change_task_status_to_break(self.project_id, task_id)
|
|
154
156
|
# 担当者が変えている場合は、元に戻す
|
|
155
157
|
if task["account_id"] != changed_task["account_id"]:
|
|
156
158
|
self.service.wrapper.change_task_operator(self.project_id, task_id, task["account_id"])
|
|
157
|
-
logger.debug(f"{task_id}
|
|
159
|
+
logger.debug(f"task_id'{task_id}' :: 担当者を元のユーザ( account_id='{task['account_id']}')に戻しました。")
|
|
158
160
|
|
|
159
161
|
def add_comments_for_task_wrapper(self, tpl: tuple[int, str], comment_info: AddedSimpleComment) -> bool:
|
|
160
162
|
task_index, task_id = tpl
|
|
161
163
|
try:
|
|
162
164
|
return self.put_comment_for_task(task_id=task_id, comment_info=comment_info, task_index=task_index)
|
|
163
165
|
except Exception: # pylint: disable=broad-except
|
|
164
|
-
logger.warning(f"task_id={task_id}
|
|
166
|
+
logger.warning(f"task_id='{task_id}' :: コメントの付与に失敗しました。", exc_info=True)
|
|
165
167
|
return False
|
|
166
168
|
|
|
167
169
|
def put_comment_for_task_list(
|
|
@@ -191,7 +193,7 @@ class PutCommentSimplyMain(CommandLineWithConfirm):
|
|
|
191
193
|
if result:
|
|
192
194
|
success_count += 1
|
|
193
195
|
except Exception: # pylint: disable=broad-except
|
|
194
|
-
logger.warning(f"task_id={task_id}
|
|
196
|
+
logger.warning(f"task_id='{task_id}' :: {self.comment_type_name}の付与に失敗しました。", exc_info=True)
|
|
195
197
|
continue
|
|
196
198
|
|
|
197
199
|
logger.info(f"{success_count} / {len(task_ids)} 件のタスクに{self.comment_type_name}を付与しました。")
|
annofabcli/common/cli.py
CHANGED
|
@@ -397,7 +397,7 @@ class ArgumentParser:
|
|
|
397
397
|
def __init__(self, parser: argparse.ArgumentParser) -> None:
|
|
398
398
|
self.parser = parser
|
|
399
399
|
|
|
400
|
-
def add_project_id(self, help_message: Optional[str] = None)
|
|
400
|
+
def add_project_id(self, help_message: Optional[str] = None) -> None:
|
|
401
401
|
"""
|
|
402
402
|
'--project_id` 引数を追加
|
|
403
403
|
"""
|
|
@@ -406,7 +406,7 @@ class ArgumentParser:
|
|
|
406
406
|
|
|
407
407
|
self.parser.add_argument("-p", "--project_id", type=str, required=True, help=help_message)
|
|
408
408
|
|
|
409
|
-
def add_task_id(self, required: bool = True, help_message: Optional[str] = None)
|
|
409
|
+
def add_task_id(self, *, required: bool = True, help_message: Optional[str] = None) -> None:
|
|
410
410
|
"""
|
|
411
411
|
'--task_id` 引数を追加
|
|
412
412
|
"""
|
|
@@ -415,7 +415,7 @@ class ArgumentParser:
|
|
|
415
415
|
|
|
416
416
|
self.parser.add_argument("-t", "--task_id", type=str, required=required, nargs="+", help=help_message)
|
|
417
417
|
|
|
418
|
-
def add_input_data_id(self, required: bool = True, help_message: Optional[str] = None)
|
|
418
|
+
def add_input_data_id(self, *, required: bool = True, help_message: Optional[str] = None) -> None:
|
|
419
419
|
"""
|
|
420
420
|
'--input_data_id` 引数を追加
|
|
421
421
|
"""
|
|
@@ -426,7 +426,7 @@ class ArgumentParser:
|
|
|
426
426
|
|
|
427
427
|
self.parser.add_argument("-i", "--input_data_id", type=str, required=required, nargs="+", help=help_message)
|
|
428
428
|
|
|
429
|
-
def add_format(self, choices: list[FormatArgument], default: FormatArgument, help_message: Optional[str] = None)
|
|
429
|
+
def add_format(self, choices: list[FormatArgument], default: FormatArgument, help_message: Optional[str] = None) -> None:
|
|
430
430
|
"""
|
|
431
431
|
'--format` 引数を追加
|
|
432
432
|
"""
|
|
@@ -435,7 +435,7 @@ class ArgumentParser:
|
|
|
435
435
|
|
|
436
436
|
self.parser.add_argument("-f", "--format", type=str, choices=[e.value for e in choices], default=default.value, help=help_message)
|
|
437
437
|
|
|
438
|
-
def add_csv_format(self, help_message: Optional[str] = None)
|
|
438
|
+
def add_csv_format(self, help_message: Optional[str] = None) -> None:
|
|
439
439
|
"""
|
|
440
440
|
'--csv_format` 引数を追加
|
|
441
441
|
"""
|
|
@@ -448,7 +448,7 @@ class ArgumentParser:
|
|
|
448
448
|
|
|
449
449
|
self.parser.add_argument("--csv_format", type=str, help=help_message)
|
|
450
450
|
|
|
451
|
-
def add_output(self, required: bool = False, help_message: Optional[str] = None)
|
|
451
|
+
def add_output(self, *, required: bool = False, help_message: Optional[str] = None) -> None:
|
|
452
452
|
"""
|
|
453
453
|
'--output` 引数を追加
|
|
454
454
|
"""
|
|
@@ -457,7 +457,7 @@ class ArgumentParser:
|
|
|
457
457
|
|
|
458
458
|
self.parser.add_argument("-o", "--output", type=str, required=required, help=help_message)
|
|
459
459
|
|
|
460
|
-
def add_task_query(self, required: bool = False, help_message: Optional[str] = None)
|
|
460
|
+
def add_task_query(self, *, required: bool = False, help_message: Optional[str] = None) -> None:
|
|
461
461
|
if help_message is None:
|
|
462
462
|
help_message = (
|
|
463
463
|
"タスクを絞り込むためのクエリ条件をJSON形式で指定します。"
|
|
@@ -520,7 +520,7 @@ class CommandLineWithoutWebapi:
|
|
|
520
520
|
self.args = args
|
|
521
521
|
self.process_common_args(args)
|
|
522
522
|
|
|
523
|
-
def process_common_args(self, args: argparse.Namespace)
|
|
523
|
+
def process_common_args(self, args: argparse.Namespace) -> None:
|
|
524
524
|
"""
|
|
525
525
|
共通のコマンドライン引数を処理する。
|
|
526
526
|
Args:
|
|
@@ -637,12 +637,12 @@ class CommandLine(CommandLineWithoutWebapi):
|
|
|
637
637
|
self.facade = facade
|
|
638
638
|
super().__init__(args)
|
|
639
639
|
|
|
640
|
-
def validate_project(
|
|
640
|
+
def validate_project(
|
|
641
641
|
self,
|
|
642
642
|
project_id: str,
|
|
643
643
|
project_member_roles: Optional[list[ProjectMemberRole]] = None,
|
|
644
644
|
organization_member_roles: Optional[list[OrganizationMemberRole]] = None,
|
|
645
|
-
):
|
|
645
|
+
) -> None:
|
|
646
646
|
"""
|
|
647
647
|
プロジェクト or 組織に対して、必要な権限が付与されているかを確認する。
|
|
648
648
|
|
annofabcli/common/download.py
CHANGED
|
@@ -60,26 +60,25 @@ class DownloadingFile:
|
|
|
60
60
|
if not result:
|
|
61
61
|
raise UpdatedFileForDownloadingError(f"{filetype}の更新処理が{max_wait_minutes}分以内に完了しない、または更新処理に失敗しました。")
|
|
62
62
|
|
|
63
|
-
async def download_annotation_zip_with_async(
|
|
63
|
+
async def download_annotation_zip_with_async(
|
|
64
64
|
self,
|
|
65
65
|
project_id: str,
|
|
66
66
|
dest_path: Union[str, Path],
|
|
67
67
|
is_latest: bool = False, # noqa: FBT001, FBT002
|
|
68
68
|
wait_options: Optional[WaitOptions] = None,
|
|
69
|
-
):
|
|
69
|
+
) -> None:
|
|
70
70
|
loop = asyncio.get_event_loop()
|
|
71
71
|
partial_func = partial(self.download_annotation_zip, project_id, dest_path, is_latest, wait_options)
|
|
72
|
-
|
|
73
|
-
return result
|
|
72
|
+
await loop.run_in_executor(None, partial_func)
|
|
74
73
|
|
|
75
|
-
def download_annotation_zip(
|
|
74
|
+
def download_annotation_zip(
|
|
76
75
|
self,
|
|
77
76
|
project_id: str,
|
|
78
77
|
dest_path: Union[str, Path],
|
|
79
78
|
is_latest: bool = False, # noqa: FBT001, FBT002
|
|
80
79
|
wait_options: Optional[WaitOptions] = None,
|
|
81
80
|
should_download_full_annotation: bool = False, # noqa: FBT001, FBT002
|
|
82
|
-
):
|
|
81
|
+
) -> None:
|
|
83
82
|
"""アノテーションZIPをダウンロードします。"""
|
|
84
83
|
|
|
85
84
|
def download_annotation_zip(): # noqa: ANN202
|
|
@@ -103,7 +102,7 @@ class DownloadingFile:
|
|
|
103
102
|
else:
|
|
104
103
|
raise e # noqa: TRY201
|
|
105
104
|
|
|
106
|
-
def wait_until_updated_annotation_zip(self, project_id: str, wait_options: Optional[WaitOptions] = None)
|
|
105
|
+
def wait_until_updated_annotation_zip(self, project_id: str, wait_options: Optional[WaitOptions] = None) -> None:
|
|
107
106
|
job_id = None
|
|
108
107
|
try:
|
|
109
108
|
job = self.service.api.post_annotation_archive_update(project_id)[0]["job"]
|
|
@@ -120,25 +119,24 @@ class DownloadingFile:
|
|
|
120
119
|
|
|
121
120
|
self._wait_for_completion(project_id, job_type=ProjectJobType.GEN_ANNOTATION, wait_options=wait_options, job_id=job_id)
|
|
122
121
|
|
|
123
|
-
async def download_input_data_json_with_async(
|
|
122
|
+
async def download_input_data_json_with_async(
|
|
124
123
|
self,
|
|
125
124
|
project_id: str,
|
|
126
125
|
dest_path: Union[str, Path],
|
|
127
126
|
is_latest: bool = False, # noqa: FBT001, FBT002
|
|
128
127
|
wait_options: Optional[WaitOptions] = None,
|
|
129
|
-
):
|
|
128
|
+
) -> None:
|
|
130
129
|
loop = asyncio.get_event_loop()
|
|
131
130
|
partial_func = partial(self.download_input_data_json, project_id, dest_path, is_latest, wait_options)
|
|
132
|
-
|
|
133
|
-
return result
|
|
131
|
+
await loop.run_in_executor(None, partial_func)
|
|
134
132
|
|
|
135
|
-
def download_input_data_json(
|
|
133
|
+
def download_input_data_json(
|
|
136
134
|
self,
|
|
137
135
|
project_id: str,
|
|
138
136
|
dest_path: Union[str, Path],
|
|
139
137
|
is_latest: bool = False, # noqa: FBT001, FBT002
|
|
140
138
|
wait_options: Optional[WaitOptions] = None,
|
|
141
|
-
):
|
|
139
|
+
) -> None:
|
|
142
140
|
if is_latest:
|
|
143
141
|
self.wait_until_updated_input_data_json(project_id, wait_options)
|
|
144
142
|
self.service.wrapper.download_project_inputs_url(project_id, dest_path)
|
|
@@ -154,7 +152,7 @@ class DownloadingFile:
|
|
|
154
152
|
else:
|
|
155
153
|
raise e # noqa: TRY201
|
|
156
154
|
|
|
157
|
-
def wait_until_updated_input_data_json(self, project_id: str, wait_options: Optional[WaitOptions] = None)
|
|
155
|
+
def wait_until_updated_input_data_json(self, project_id: str, wait_options: Optional[WaitOptions] = None) -> None:
|
|
158
156
|
job_id = None
|
|
159
157
|
try:
|
|
160
158
|
job = self.service.api.post_project_inputs_update(project_id)[0]["job"]
|
|
@@ -170,19 +168,20 @@ class DownloadingFile:
|
|
|
170
168
|
|
|
171
169
|
self._wait_for_completion(project_id, job_type=ProjectJobType.GEN_INPUTS_LIST, wait_options=wait_options, job_id=job_id)
|
|
172
170
|
|
|
173
|
-
async def download_task_json_with_async(
|
|
171
|
+
async def download_task_json_with_async(
|
|
174
172
|
self,
|
|
175
173
|
project_id: str,
|
|
176
174
|
dest_path: Union[str, Path],
|
|
177
175
|
is_latest: bool = False, # noqa: FBT001, FBT002
|
|
178
176
|
wait_options: Optional[WaitOptions] = None,
|
|
179
|
-
):
|
|
177
|
+
) -> None:
|
|
180
178
|
loop = asyncio.get_event_loop()
|
|
181
|
-
partial_func = partial(self.download_task_json, project_id, dest_path, is_latest, wait_options)
|
|
182
|
-
|
|
183
|
-
return result
|
|
179
|
+
partial_func = partial(self.download_task_json, project_id, dest_path, is_latest=is_latest, wait_options=wait_options)
|
|
180
|
+
await loop.run_in_executor(None, partial_func)
|
|
184
181
|
|
|
185
|
-
def download_task_json(
|
|
182
|
+
def download_task_json(
|
|
183
|
+
self, project_id: str, dest_path: Union[str, Path], *, is_latest: bool = False, wait_options: Optional[WaitOptions] = None
|
|
184
|
+
) -> None:
|
|
186
185
|
if is_latest:
|
|
187
186
|
self.wait_until_updated_task_json(project_id, wait_options)
|
|
188
187
|
self.service.wrapper.download_project_tasks_url(project_id, dest_path)
|
|
@@ -198,7 +197,7 @@ class DownloadingFile:
|
|
|
198
197
|
else:
|
|
199
198
|
raise e # noqa: TRY201
|
|
200
199
|
|
|
201
|
-
def wait_until_updated_task_json(self, project_id: str, wait_options: Optional[WaitOptions] = None)
|
|
200
|
+
def wait_until_updated_task_json(self, project_id: str, wait_options: Optional[WaitOptions] = None) -> None:
|
|
202
201
|
job_id = None
|
|
203
202
|
try:
|
|
204
203
|
job = self.service.api.post_project_tasks_update(project_id)[0]["job"]
|
|
@@ -214,7 +213,7 @@ class DownloadingFile:
|
|
|
214
213
|
|
|
215
214
|
self._wait_for_completion(project_id, job_type=ProjectJobType.GEN_TASKS_LIST, wait_options=wait_options, job_id=job_id)
|
|
216
215
|
|
|
217
|
-
async def download_task_history_json_with_async(self, project_id: str, dest_path: Union[str, Path])
|
|
216
|
+
async def download_task_history_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
218
217
|
"""
|
|
219
218
|
非同期でタスク履歴全件ファイルをダウンロードする。
|
|
220
219
|
|
|
@@ -223,7 +222,7 @@ class DownloadingFile:
|
|
|
223
222
|
"""
|
|
224
223
|
return self.download_task_history_json(project_id, dest_path=dest_path)
|
|
225
224
|
|
|
226
|
-
def download_task_history_json(self, project_id: str, dest_path: Union[str, Path])
|
|
225
|
+
def download_task_history_json(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
227
226
|
"""
|
|
228
227
|
タスク履歴全件ファイルをダウンロードする。
|
|
229
228
|
|
|
@@ -243,7 +242,7 @@ class DownloadingFile:
|
|
|
243
242
|
) from e
|
|
244
243
|
raise e # noqa: TRY201
|
|
245
244
|
|
|
246
|
-
def download_task_history_event_json(self, project_id: str, dest_path: Union[str, Path])
|
|
245
|
+
def download_task_history_event_json(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
247
246
|
"""
|
|
248
247
|
タスク履歴イベント全件ファイルをダウンロードする。
|
|
249
248
|
|
|
@@ -263,7 +262,7 @@ class DownloadingFile:
|
|
|
263
262
|
) from e
|
|
264
263
|
raise e # noqa: TRY201
|
|
265
264
|
|
|
266
|
-
async def download_task_history_event_json_with_async(self, project_id: str, dest_path: Union[str, Path])
|
|
265
|
+
async def download_task_history_event_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
267
266
|
"""
|
|
268
267
|
非同期でタスク履歴全件ファイルをダウンロードする。
|
|
269
268
|
|
|
@@ -273,7 +272,7 @@ class DownloadingFile:
|
|
|
273
272
|
"""
|
|
274
273
|
return self.download_task_history_event_json(project_id, dest_path=dest_path)
|
|
275
274
|
|
|
276
|
-
async def download_inspection_json_with_async(self, project_id: str, dest_path: Union[str, Path])
|
|
275
|
+
async def download_inspection_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
277
276
|
"""
|
|
278
277
|
非同期で検査コメント全件ファイルをダウンロードする。
|
|
279
278
|
|
|
@@ -283,7 +282,7 @@ class DownloadingFile:
|
|
|
283
282
|
|
|
284
283
|
return self.download_inspection_comment_json(project_id, dest_path=dest_path)
|
|
285
284
|
|
|
286
|
-
def download_inspection_comment_json(self, project_id: str, dest_path: Union[str, Path])
|
|
285
|
+
def download_inspection_comment_json(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
287
286
|
"""
|
|
288
287
|
検査コメント全件ファイルをダウンロードする。
|
|
289
288
|
|
|
@@ -300,7 +299,7 @@ class DownloadingFile:
|
|
|
300
299
|
) from e
|
|
301
300
|
raise e # noqa: TRY201
|
|
302
301
|
|
|
303
|
-
async def download_comment_json_with_async(self, project_id: str, dest_path: Union[str, Path])
|
|
302
|
+
async def download_comment_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
304
303
|
"""
|
|
305
304
|
非同期でコメント全件ファイルをダウンロードする。
|
|
306
305
|
|
|
@@ -310,7 +309,7 @@ class DownloadingFile:
|
|
|
310
309
|
|
|
311
310
|
return self.download_comment_json(project_id, dest_path=dest_path)
|
|
312
311
|
|
|
313
|
-
def download_comment_json(self, project_id: str, dest_path: Union[str, Path])
|
|
312
|
+
def download_comment_json(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
314
313
|
"""
|
|
315
314
|
コメント全件ファイルをダウンロードする。
|
|
316
315
|
|
annofabcli/common/image.py
CHANGED
|
@@ -306,7 +306,9 @@ def write_annotation_images_from_path(
|
|
|
306
306
|
if original_resolution is not None and (original_resolution.get("width") is not None and original_resolution.get("height") is not None):
|
|
307
307
|
return original_resolution.get("width"), original_resolution.get("height")
|
|
308
308
|
else:
|
|
309
|
-
logger.warning(
|
|
309
|
+
logger.warning(
|
|
310
|
+
f"input_data_id='{input_data_id}'のプロパティ`system_metadata.original_resolution`には画像サイズが設定されていません。"
|
|
311
|
+
)
|
|
310
312
|
return None
|
|
311
313
|
|
|
312
314
|
if image_size is not None:
|
|
@@ -314,7 +316,7 @@ def write_annotation_images_from_path(
|
|
|
314
316
|
elif input_data_dict is not None:
|
|
315
317
|
input_data = input_data_dict.get(input_data_id)
|
|
316
318
|
if input_data is None:
|
|
317
|
-
logger.warning(f"input_data_id={input_data_id}の入力データは存在しなかったので、スキップします。")
|
|
319
|
+
logger.warning(f"input_data_id='{input_data_id}'の入力データは存在しなかったので、スキップします。")
|
|
318
320
|
return None
|
|
319
321
|
|
|
320
322
|
return _get_image_size_from_system_metadata(input_data)
|
|
@@ -38,14 +38,14 @@ class DeleteInputData(CommandLine):
|
|
|
38
38
|
try:
|
|
39
39
|
self.service.api.delete_supplementary_data(project_id, input_data_id=input_data_id, supplementary_data_id=supplementary_data_id)
|
|
40
40
|
logger.debug(
|
|
41
|
-
f"補助情報を削除しました。input_data_id={input_data_id}, supplementary_data_id={supplementary_data_id}, "
|
|
42
|
-
f"supplementary_data_name={supplementary_data['supplementary_data_name']}"
|
|
41
|
+
f"補助情報を削除しました。input_data_id='{input_data_id}', supplementary_data_id='{supplementary_data_id}', "
|
|
42
|
+
f"supplementary_data_name='{supplementary_data['supplementary_data_name']}'"
|
|
43
43
|
)
|
|
44
44
|
deleted_count += 1
|
|
45
45
|
except requests.HTTPError:
|
|
46
46
|
logger.warning(
|
|
47
|
-
f"補助情報の削除に失敗しました。input_data_id={input_data_id}, supplementary_data_id={supplementary_data_id}, "
|
|
48
|
-
f"supplementary_data_name={supplementary_data['supplementary_data_name']}",
|
|
47
|
+
f"補助情報の削除に失敗しました。input_data_id='{input_data_id}', supplementary_data_id='{supplementary_data_id}', "
|
|
48
|
+
f"supplementary_data_name='{supplementary_data['supplementary_data_name']}'",
|
|
49
49
|
exc_info=True,
|
|
50
50
|
)
|
|
51
51
|
continue
|
|
@@ -64,7 +64,7 @@ class UpdateMetadataMain(CommandLineWithConfirm):
|
|
|
64
64
|
|
|
65
65
|
input_data = self.service.wrapper.get_input_data_or_none(project_id, input_data_id)
|
|
66
66
|
if input_data is None:
|
|
67
|
-
logger.warning(f"{logging_prefix} 入力データは存在しないのでスキップします。input_data_id={input_data_id}")
|
|
67
|
+
logger.warning(f"{logging_prefix} 入力データは存在しないのでスキップします。 :: input_data_id='{input_data_id}'")
|
|
68
68
|
return False
|
|
69
69
|
|
|
70
70
|
logger.debug(
|
|
@@ -51,7 +51,7 @@ class UploadInstruction(CommandLine):
|
|
|
51
51
|
作業ガイドをアップロードする
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
|
-
def upload_html_to_instruction(self, project_id: str, html_path: Path, temp_dir: Path): # noqa:
|
|
54
|
+
def upload_html_to_instruction(self, project_id: str, html_path: Path, temp_dir: Path) -> None: # noqa: PLR0912
|
|
55
55
|
with html_path.open(encoding="utf-8") as f:
|
|
56
56
|
file_content = f.read()
|
|
57
57
|
pq_html = pyquery.PyQuery(file_content)
|
|
@@ -106,7 +106,7 @@ class UploadInstruction(CommandLine):
|
|
|
106
106
|
self.update_instruction(project_id, html_data)
|
|
107
107
|
logger.info("作業ガイドを更新しました。")
|
|
108
108
|
|
|
109
|
-
def update_instruction(self, project_id: str, html_data: str)
|
|
109
|
+
def update_instruction(self, project_id: str, html_data: str) -> None:
|
|
110
110
|
histories, _ = self.service.api.get_instruction_history(project_id)
|
|
111
111
|
if len(histories) > 0: # noqa: SIM108
|
|
112
112
|
last_updated_datetime = histories[0]["updated_datetime"]
|
|
@@ -4,7 +4,6 @@ import argparse
|
|
|
4
4
|
import functools
|
|
5
5
|
import json
|
|
6
6
|
import logging.handlers
|
|
7
|
-
import re
|
|
8
7
|
import sys
|
|
9
8
|
import tempfile
|
|
10
9
|
from multiprocessing import Pool
|
|
@@ -64,10 +63,6 @@ from annofabcli.statistics.visualization.visualization_source_files import Visua
|
|
|
64
63
|
logger = logging.getLogger(__name__)
|
|
65
64
|
|
|
66
65
|
|
|
67
|
-
def get_project_output_dir(project_title: str) -> str:
|
|
68
|
-
return re.sub(r'[\\/:*?"<>|]+', "__", project_title)
|
|
69
|
-
|
|
70
|
-
|
|
71
66
|
class WriteCsvGraph:
|
|
72
67
|
def __init__( # noqa: PLR0913
|
|
73
68
|
self,
|
|
@@ -408,8 +403,7 @@ class VisualizingStatisticsMain:
|
|
|
408
403
|
root_output_dir: Path,
|
|
409
404
|
) -> Optional[Path]:
|
|
410
405
|
try:
|
|
411
|
-
|
|
412
|
-
output_project_dir = root_output_dir / get_project_output_dir(project_title)
|
|
406
|
+
output_project_dir = root_output_dir / project_id
|
|
413
407
|
self.visualize_statistics(
|
|
414
408
|
project_id=project_id,
|
|
415
409
|
output_project_dir=output_project_dir,
|
|
@@ -201,12 +201,12 @@ class DeleteSupplementaryDataMain(CommandLineWithConfirm):
|
|
|
201
201
|
|
|
202
202
|
|
|
203
203
|
class DeleteSupplementaryData(CommandLine):
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
COMMON_MESSAGE = "annofabcli supplementary_data delete: error:"
|
|
205
|
+
|
|
206
|
+
def validate(self, args: argparse.Namespace) -> bool:
|
|
207
207
|
if args.csv is not None: # noqa: SIM102
|
|
208
208
|
if not Path(args.csv).exists():
|
|
209
|
-
print(f"{COMMON_MESSAGE} argument --csv: ファイルパスが存在しません。 '{args.csv}'", file=sys.stderr) # noqa: T201
|
|
209
|
+
print(f"{self.COMMON_MESSAGE} argument --csv: ファイルパスが存在しません。 '{args.csv}'", file=sys.stderr) # noqa: T201
|
|
210
210
|
return False
|
|
211
211
|
|
|
212
212
|
return True
|
|
@@ -227,6 +227,10 @@ class DeleteSupplementaryData(CommandLine):
|
|
|
227
227
|
|
|
228
228
|
elif args.json is not None:
|
|
229
229
|
supplementary_data_list = annofabcli.common.cli.get_json_from_args(args.json)
|
|
230
|
+
if not isinstance(supplementary_data_list, list):
|
|
231
|
+
print(f"{self.COMMON_MESSAGE} argument --json: JSON形式が不正です。オブジェクトの配列を指定してください。", file=sys.stderr) # noqa: T201
|
|
232
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
233
|
+
|
|
230
234
|
input_data_dict = get_input_data_supplementary_data_dict_from_list(supplementary_data_list)
|
|
231
235
|
main_obj.delete_supplementary_data_list(project_id, input_data_dict)
|
|
232
236
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: annofabcli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.101.0
|
|
4
4
|
Summary: Utility Command Line Interface for AnnoFab
|
|
5
5
|
Author: Kurusugawa Computer Inc.
|
|
6
6
|
License: MIT
|
|
@@ -10,9 +10,14 @@ Classifier: Development Status :: 4 - Beta
|
|
|
10
10
|
Classifier: Environment :: Console
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
18
|
Classifier: Topic :: Utilities
|
|
14
19
|
Requires-Python: >=3.9
|
|
15
|
-
Requires-Dist: annofabapi>=1.4.
|
|
20
|
+
Requires-Dist: annofabapi>=1.4.7
|
|
16
21
|
Requires-Dist: bokeh<3.7,>=3.3
|
|
17
22
|
Requires-Dist: dictdiffer
|
|
18
23
|
Requires-Dist: isodate
|
|
@@ -25,6 +30,7 @@ Requires-Dist: python-datauri
|
|
|
25
30
|
Requires-Dist: pyyaml
|
|
26
31
|
Requires-Dist: requests
|
|
27
32
|
Requires-Dist: typing-extensions>=4.5
|
|
33
|
+
Requires-Dist: ulid-py>=1.1.0
|
|
28
34
|
Description-Content-Type: text/markdown
|
|
29
35
|
|
|
30
36
|
# annofab-cli
|