annofabcli 1.102.1__py3-none-any.whl → 1.104.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annofabcli/__main__.py +1 -1
- annofabcli/annotation/annotation_query.py +9 -29
- annofabcli/annotation/change_annotation_attributes.py +6 -14
- annofabcli/annotation/change_annotation_properties.py +5 -12
- annofabcli/annotation/copy_annotation.py +4 -10
- annofabcli/annotation/delete_annotation.py +10 -26
- annofabcli/annotation/dump_annotation.py +1 -4
- annofabcli/annotation/import_annotation.py +15 -39
- annofabcli/annotation/list_annotation.py +1 -4
- annofabcli/annotation/merge_segmentation.py +5 -15
- annofabcli/annotation/remove_segmentation_overlap.py +8 -29
- annofabcli/annotation/restore_annotation.py +3 -9
- annofabcli/annotation_specs/add_attribute_restriction.py +2 -8
- annofabcli/annotation_specs/attribute_restriction.py +2 -10
- annofabcli/annotation_specs/export_annotation_specs.py +1 -3
- annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -10
- annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +4 -10
- annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +1 -3
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +7 -18
- annofabcli/annotation_specs/list_annotation_specs_choice.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
- annofabcli/annotation_specs/list_annotation_specs_label.py +3 -8
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +4 -9
- annofabcli/annotation_specs/list_attribute_restriction.py +3 -9
- annofabcli/annotation_specs/put_label_color.py +1 -6
- annofabcli/comment/delete_comment.py +3 -9
- annofabcli/comment/list_all_comment.py +15 -5
- annofabcli/comment/list_comment.py +46 -7
- annofabcli/comment/put_comment.py +4 -13
- annofabcli/comment/put_comment_simply.py +2 -6
- annofabcli/comment/put_inspection_comment.py +2 -6
- annofabcli/comment/put_inspection_comment_simply.py +3 -6
- annofabcli/comment/put_onhold_comment.py +2 -6
- annofabcli/comment/put_onhold_comment_simply.py +2 -4
- annofabcli/common/cli.py +5 -43
- annofabcli/common/download.py +8 -25
- annofabcli/common/image.py +3 -7
- annofabcli/common/utils.py +2 -4
- annofabcli/common/visualize.py +2 -4
- annofabcli/filesystem/draw_annotation.py +6 -18
- annofabcli/filesystem/filter_annotation.py +7 -24
- annofabcli/filesystem/mask_user_info.py +2 -5
- annofabcli/filesystem/merge_annotation.py +2 -6
- annofabcli/input_data/change_input_data_name.py +3 -7
- annofabcli/input_data/copy_input_data.py +6 -14
- annofabcli/input_data/delete_input_data.py +7 -24
- annofabcli/input_data/delete_metadata_key_of_input_data.py +5 -16
- annofabcli/input_data/list_all_input_data.py +5 -14
- annofabcli/input_data/list_all_input_data_merged_task.py +8 -23
- annofabcli/input_data/list_input_data.py +5 -16
- annofabcli/input_data/put_input_data.py +7 -19
- annofabcli/input_data/update_metadata_of_input_data.py +6 -14
- annofabcli/instruction/list_instruction_history.py +0 -1
- annofabcli/instruction/upload_instruction.py +4 -7
- annofabcli/job/list_job.py +2 -3
- annofabcli/job/list_last_job.py +1 -3
- annofabcli/organization/list_organization.py +0 -1
- annofabcli/organization_member/change_organization_member.py +1 -3
- annofabcli/organization_member/delete_organization_member.py +2 -6
- annofabcli/organization_member/invite_organization_member.py +1 -3
- annofabcli/organization_member/list_organization_member.py +0 -1
- annofabcli/project/change_organization_of_project.py +257 -0
- annofabcli/project/change_project_status.py +2 -2
- annofabcli/project/copy_project.py +2 -7
- annofabcli/project/diff_projects.py +4 -16
- annofabcli/project/list_project.py +0 -1
- annofabcli/project/put_project.py +2 -6
- annofabcli/project/subcommand_project.py +2 -0
- annofabcli/project_member/change_project_members.py +1 -1
- annofabcli/project_member/copy_project_members.py +2 -7
- annofabcli/project_member/drop_project_members.py +1 -3
- annofabcli/project_member/invite_project_members.py +2 -4
- annofabcli/project_member/list_users.py +0 -1
- annofabcli/project_member/put_project_members.py +4 -12
- annofabcli/stat_visualization/mask_visualization_dir.py +6 -16
- annofabcli/stat_visualization/merge_visualization_dir.py +7 -19
- annofabcli/stat_visualization/summarize_whole_performance_csv.py +3 -7
- annofabcli/stat_visualization/write_graph.py +5 -15
- annofabcli/stat_visualization/write_performance_rating_csv.py +4 -12
- annofabcli/statistics/list_annotation_area.py +3 -7
- annofabcli/statistics/list_annotation_attribute.py +6 -15
- annofabcli/statistics/list_annotation_attribute_filled_count.py +9 -23
- annofabcli/statistics/list_annotation_count.py +18 -44
- annofabcli/statistics/list_annotation_duration.py +14 -40
- annofabcli/statistics/list_video_duration.py +2 -3
- annofabcli/statistics/list_worktime.py +0 -1
- annofabcli/statistics/scatter.py +3 -9
- annofabcli/statistics/summarize_task_count.py +7 -12
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +3 -11
- annofabcli/statistics/summarize_task_count_by_user.py +1 -5
- annofabcli/statistics/visualization/dataframe/annotation_count.py +2 -4
- annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +6 -12
- annofabcli/statistics/visualization/dataframe/productivity_per_date.py +10 -22
- annofabcli/statistics/visualization/dataframe/project_performance.py +1 -3
- annofabcli/statistics/visualization/dataframe/task.py +2 -5
- annofabcli/statistics/visualization/dataframe/task_history.py +1 -1
- annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +6 -20
- annofabcli/statistics/visualization/dataframe/user_performance.py +29 -88
- annofabcli/statistics/visualization/dataframe/whole_performance.py +6 -12
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +17 -49
- annofabcli/statistics/visualization/dataframe/worktime_per_date.py +4 -10
- annofabcli/statistics/visualization/filtering_query.py +2 -6
- annofabcli/statistics/visualization/project_dir.py +9 -26
- annofabcli/statistics/visualization/visualization_source_files.py +3 -10
- annofabcli/statistics/visualize_annotation_count.py +9 -23
- annofabcli/statistics/visualize_annotation_duration.py +5 -15
- annofabcli/statistics/visualize_statistics.py +18 -53
- annofabcli/statistics/visualize_video_duration.py +8 -19
- annofabcli/supplementary/delete_supplementary_data.py +7 -23
- annofabcli/supplementary/list_supplementary_data.py +1 -1
- annofabcli/supplementary/put_supplementary_data.py +5 -15
- annofabcli/task/cancel_acceptance.py +3 -4
- annofabcli/task/change_operator.py +3 -11
- annofabcli/task/change_status_to_break.py +1 -1
- annofabcli/task/change_status_to_on_hold.py +5 -18
- annofabcli/task/complete_tasks.py +8 -25
- annofabcli/task/copy_tasks.py +2 -3
- annofabcli/task/delete_metadata_key_of_task.py +2 -6
- annofabcli/task/delete_tasks.py +8 -26
- annofabcli/task/list_all_tasks.py +2 -4
- annofabcli/task/list_tasks.py +3 -7
- annofabcli/task/list_tasks_added_task_history.py +7 -21
- annofabcli/task/put_tasks.py +2 -3
- annofabcli/task/put_tasks_by_count.py +3 -7
- annofabcli/task/reject_tasks.py +7 -19
- annofabcli/task/update_metadata_of_task.py +2 -2
- annofabcli/task_history/list_all_task_history.py +2 -5
- annofabcli/task_history/list_task_history.py +0 -1
- annofabcli/task_history_event/list_all_task_history_event.py +4 -11
- annofabcli/task_history_event/list_worktime.py +4 -14
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/METADATA +1 -1
- annofabcli-1.104.0.dist-info/RECORD +215 -0
- annofabcli-1.102.1.dist-info/RECORD +0 -214
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.102.1.dist-info → annofabcli-1.104.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import logging
|
|
5
|
+
from collections import Counter
|
|
5
6
|
from typing import Any, Optional
|
|
6
7
|
|
|
7
8
|
import pandas
|
|
@@ -19,14 +20,48 @@ from annofabcli.common.visualize import AddProps
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def create_reply_counter(comments: list[dict[str, Any]]) -> Counter[tuple[str, str, str]]:
|
|
24
|
+
"""
|
|
25
|
+
返信コメントの回数を取得するcounterを生成します。
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
root_comment_id_counter = Counter((c["task_id"], c["input_data_id"], c["comment_node"]["root_comment_id"]) for c in comments if c["comment_node"]["_type"] == "Reply")
|
|
29
|
+
return root_comment_id_counter
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def create_empty_df_comment() -> pandas.DataFrame:
|
|
33
|
+
return pandas.DataFrame(
|
|
34
|
+
columns=[
|
|
35
|
+
"project_id",
|
|
36
|
+
"task_id",
|
|
37
|
+
"input_data_id",
|
|
38
|
+
"comment_id",
|
|
39
|
+
"phase",
|
|
40
|
+
"phase_stage",
|
|
41
|
+
"comment_type",
|
|
42
|
+
"account_id",
|
|
43
|
+
"user_id",
|
|
44
|
+
"username",
|
|
45
|
+
"phrases",
|
|
46
|
+
"comment",
|
|
47
|
+
"created_datetime",
|
|
48
|
+
"updated_datetime",
|
|
49
|
+
"reply_count",
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
22
54
|
class ListingComments(CommandLine):
|
|
23
|
-
def get_comments(self, project_id: str, task_id: str, input_data_id: str)
|
|
55
|
+
def get_comments(self, project_id: str, task_id: str, input_data_id: str) -> list[dict[str, Any]]:
|
|
24
56
|
comments, _ = self.service.api.get_comments(project_id, task_id, input_data_id, query_params={"v": "2"})
|
|
57
|
+
# 返信回数を算出する
|
|
58
|
+
reply_counter = create_reply_counter(comments)
|
|
59
|
+
for c in comments:
|
|
60
|
+
key = (c["task_id"], c["input_data_id"], c["comment_id"])
|
|
61
|
+
c["reply_count"] = reply_counter.get(key, 0)
|
|
25
62
|
return comments
|
|
26
63
|
|
|
27
|
-
def get_comment_list(
|
|
28
|
-
self, project_id: str, task_id_list: list[str], *, comment_type: Optional[CommentType], exclude_reply: bool
|
|
29
|
-
) -> list[dict[str, Any]]:
|
|
64
|
+
def get_comment_list(self, project_id: str, task_id_list: list[str], *, comment_type: Optional[CommentType], exclude_reply: bool) -> list[dict[str, Any]]:
|
|
30
65
|
all_comments: list[Comment] = []
|
|
31
66
|
|
|
32
67
|
for task_id in task_id_list:
|
|
@@ -51,10 +86,11 @@ class ListingComments(CommandLine):
|
|
|
51
86
|
all_comments.extend(comments)
|
|
52
87
|
|
|
53
88
|
except requests.HTTPError:
|
|
54
|
-
logger.warning(f"
|
|
89
|
+
logger.warning(f"task_id='{task_id}'のタスクのコメントの取得に失敗しました。", exc_info=True)
|
|
55
90
|
|
|
56
91
|
visualize = AddProps(self.service, project_id)
|
|
57
92
|
all_comments = [visualize.add_properties_to_comment(e) for e in all_comments]
|
|
93
|
+
|
|
58
94
|
return all_comments
|
|
59
95
|
|
|
60
96
|
def main(self) -> None:
|
|
@@ -68,7 +104,11 @@ class ListingComments(CommandLine):
|
|
|
68
104
|
|
|
69
105
|
output_format = FormatArgument(args.format)
|
|
70
106
|
if output_format == FormatArgument.CSV:
|
|
71
|
-
|
|
107
|
+
if len(comment_list) > 0:
|
|
108
|
+
df = pandas.json_normalize(comment_list)
|
|
109
|
+
else:
|
|
110
|
+
df = create_empty_df_comment()
|
|
111
|
+
|
|
72
112
|
print_csv(df, output=args.output)
|
|
73
113
|
else:
|
|
74
114
|
print_according_to_format(comment_list, output_format, output=args.output)
|
|
@@ -107,7 +147,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
107
147
|
default=FormatArgument.CSV,
|
|
108
148
|
)
|
|
109
149
|
argument_parser.add_output()
|
|
110
|
-
argument_parser.add_csv_format()
|
|
111
150
|
|
|
112
151
|
parser.set_defaults(subcommand_func=main)
|
|
113
152
|
|
|
@@ -131,9 +131,7 @@ class PutCommentMain(CommandLineWithConfirm):
|
|
|
131
131
|
return False
|
|
132
132
|
|
|
133
133
|
if task["status"] not in [TaskStatus.NOT_STARTED.value, TaskStatus.WORKING.value, TaskStatus.BREAK.value]:
|
|
134
|
-
logger.warning(
|
|
135
|
-
f"task_id='{task_id}' :: タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを付与できません。 :: task_status='{task['status']}'" # noqa: E501
|
|
136
|
-
)
|
|
134
|
+
logger.warning(f"task_id='{task_id}' :: タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを付与できません。 :: task_status='{task['status']}'")
|
|
137
135
|
return False
|
|
138
136
|
return True
|
|
139
137
|
|
|
@@ -184,9 +182,7 @@ class PutCommentMain(CommandLineWithConfirm):
|
|
|
184
182
|
request_body = self._create_request_body(task=changed_task, input_data_id=input_data_id, comments=comments)
|
|
185
183
|
self.service.api.batch_update_comments(self.project_id, task_id, input_data_id, request_body=request_body)
|
|
186
184
|
added_comments_count += 1
|
|
187
|
-
logger.debug(
|
|
188
|
-
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(comments)}件のコメントを付与しました。"
|
|
189
|
-
)
|
|
185
|
+
logger.debug(f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: {len(comments)}件のコメントを付与しました。")
|
|
190
186
|
except Exception: # pylint: disable=broad-except
|
|
191
187
|
logger.warning(
|
|
192
188
|
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの付与に失敗しました。",
|
|
@@ -214,10 +210,7 @@ class PutCommentMain(CommandLineWithConfirm):
|
|
|
214
210
|
parallelism: Optional[int] = None,
|
|
215
211
|
) -> None:
|
|
216
212
|
comments_count = sum(len(e) for e in comments_for_task_list.values())
|
|
217
|
-
logger.info(
|
|
218
|
-
f"{self.comment_type_name}を付与するタスク数: {len(comments_for_task_list)}, "
|
|
219
|
-
f"{self.comment_type_name}を付与する入力データ数: {comments_count}"
|
|
220
|
-
)
|
|
213
|
+
logger.info(f"{self.comment_type_name}を付与するタスク数: {len(comments_for_task_list)}, {self.comment_type_name}を付与する入力データ数: {comments_count}")
|
|
221
214
|
|
|
222
215
|
if parallelism is not None:
|
|
223
216
|
with multiprocessing.Pool(parallelism) as pool:
|
|
@@ -294,8 +287,6 @@ def convert_cli_comments(dict_comments: dict[str, Any], *, comment_type: Comment
|
|
|
294
287
|
|
|
295
288
|
result = {}
|
|
296
289
|
for task_id, comments_for_task in dict_comments.items():
|
|
297
|
-
sub_result = {
|
|
298
|
-
input_data_id: [func_convert(e) for e in comments] for input_data_id, comments in comments_for_task.items() if len(comments) > 0
|
|
299
|
-
}
|
|
290
|
+
sub_result = {input_data_id: [func_convert(e) for e in comments] for input_data_id, comments in comments_for_task.items() if len(comments) > 0}
|
|
300
291
|
result.update({task_id: sub_result})
|
|
301
292
|
return result
|
|
@@ -95,9 +95,7 @@ class PutCommentSimplyMain(CommandLineWithConfirm):
|
|
|
95
95
|
return False
|
|
96
96
|
|
|
97
97
|
if task["status"] not in [TaskStatus.NOT_STARTED.value, TaskStatus.WORKING.value, TaskStatus.BREAK.value]:
|
|
98
|
-
logger.warning(
|
|
99
|
-
f"task_id='{task_id}' : タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを付与できません。(task_status='{task['status']}')" # noqa: E501
|
|
100
|
-
)
|
|
98
|
+
logger.warning(f"task_id='{task_id}' : タスクの状態が未着手,作業中,休憩中 以外の状態なので、コメントを付与できません。(task_status='{task['status']}')")
|
|
101
99
|
return False
|
|
102
100
|
return True
|
|
103
101
|
|
|
@@ -147,9 +145,7 @@ class PutCommentSimplyMain(CommandLineWithConfirm):
|
|
|
147
145
|
logger.debug(f"{logging_prefix} :: task_id='{task_id}' のタスクにコメントを付与しました。")
|
|
148
146
|
return True # noqa: TRY300
|
|
149
147
|
except Exception: # pylint: disable=broad-except
|
|
150
|
-
logger.warning(
|
|
151
|
-
f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの付与に失敗しました。", exc_info=True
|
|
152
|
-
)
|
|
148
|
+
logger.warning(f"{logging_prefix} :: task_id='{task_id}', input_data_id='{input_data_id}' :: コメントの付与に失敗しました。", exc_info=True)
|
|
153
149
|
return False
|
|
154
150
|
finally:
|
|
155
151
|
self.service.wrapper.change_task_status_to_break(self.project_id, task_id)
|
|
@@ -70,18 +70,14 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
70
70
|
parser.add_argument(
|
|
71
71
|
"--json",
|
|
72
72
|
type=str,
|
|
73
|
-
help=(
|
|
74
|
-
"付与する検査コメントの内容をJSON形式で指定してください。"
|
|
75
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。\n\n"
|
|
76
|
-
f"(ex) ``{json.dumps(SAMPLE_JSON, ensure_ascii=False)}``"
|
|
77
|
-
),
|
|
73
|
+
help=(f"付与する検査コメントの内容をJSON形式で指定してください。``file://`` を先頭に付けると、JSON形式のファイルを指定できます。\n\n(ex) ``{json.dumps(SAMPLE_JSON, ensure_ascii=False)}``"),
|
|
78
74
|
)
|
|
79
75
|
|
|
80
76
|
parser.add_argument(
|
|
81
77
|
"--parallelism",
|
|
82
78
|
type=int,
|
|
83
79
|
choices=PARALLELISM_CHOICES,
|
|
84
|
-
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
80
|
+
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
85
81
|
)
|
|
86
82
|
|
|
87
83
|
parser.set_defaults(subcommand_func=main)
|
|
@@ -94,10 +94,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
94
94
|
type=str,
|
|
95
95
|
nargs="+",
|
|
96
96
|
required=True,
|
|
97
|
-
help=(
|
|
98
|
-
"検査コメントを付与するタスクのtask_idを指定してください。\n"
|
|
99
|
-
"``file://`` を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。"
|
|
100
|
-
),
|
|
97
|
+
help=("検査コメントを付与するタスクのtask_idを指定してください。\n``file://`` を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。"),
|
|
101
98
|
)
|
|
102
99
|
|
|
103
100
|
parser.add_argument(
|
|
@@ -125,14 +122,14 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
125
122
|
"--custom_project_type",
|
|
126
123
|
type=str,
|
|
127
124
|
choices=[e.value for e in CustomProjectType],
|
|
128
|
-
help="[BETA] ビルトインのエディタプラグインを使用していないカスタムプロジェクトの種類を指定します。カスタムプロジェクトに対して、検査コメントの位置を指定しない場合は必須です。\n",
|
|
125
|
+
help="[BETA] ビルトインのエディタプラグインを使用していないカスタムプロジェクトの種類を指定します。カスタムプロジェクトに対して、検査コメントの位置を指定しない場合は必須です。\n",
|
|
129
126
|
)
|
|
130
127
|
|
|
131
128
|
parser.add_argument(
|
|
132
129
|
"--parallelism",
|
|
133
130
|
type=int,
|
|
134
131
|
choices=PARALLELISM_CHOICES,
|
|
135
|
-
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
132
|
+
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
136
133
|
)
|
|
137
134
|
|
|
138
135
|
parser.set_defaults(subcommand_func=main)
|
|
@@ -71,18 +71,14 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
71
71
|
parser.add_argument(
|
|
72
72
|
"--json",
|
|
73
73
|
type=str,
|
|
74
|
-
help=(
|
|
75
|
-
"付与する保留コメントの情報をJSON形式で指定してください。"
|
|
76
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。\n\n"
|
|
77
|
-
f"(ex) ``{json.dumps(SAMPLE_JSON, ensure_ascii=False)}``"
|
|
78
|
-
),
|
|
74
|
+
help=(f"付与する保留コメントの情報をJSON形式で指定してください。``file://`` を先頭に付けると、JSON形式のファイルを指定できます。\n\n(ex) ``{json.dumps(SAMPLE_JSON, ensure_ascii=False)}``"),
|
|
79
75
|
)
|
|
80
76
|
|
|
81
77
|
parser.add_argument(
|
|
82
78
|
"--parallelism",
|
|
83
79
|
type=int,
|
|
84
80
|
choices=PARALLELISM_CHOICES,
|
|
85
|
-
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
81
|
+
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
86
82
|
)
|
|
87
83
|
|
|
88
84
|
parser.set_defaults(subcommand_func=main)
|
|
@@ -67,9 +67,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
67
67
|
type=str,
|
|
68
68
|
nargs="+",
|
|
69
69
|
required=True,
|
|
70
|
-
help=(
|
|
71
|
-
"コメントを付与するタスクのtask_idを指定してください。\n``file://`` を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。"
|
|
72
|
-
),
|
|
70
|
+
help=("コメントを付与するタスクのtask_idを指定してください。\n``file://`` を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。"),
|
|
73
71
|
)
|
|
74
72
|
|
|
75
73
|
parser.add_argument(
|
|
@@ -83,7 +81,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
83
81
|
"--parallelism",
|
|
84
82
|
type=int,
|
|
85
83
|
choices=PARALLELISM_CHOICES,
|
|
86
|
-
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
84
|
+
help="使用するプロセス数(並列度)を指定してください。指定する場合は必ず ``--yes`` を指定してください。指定しない場合は、逐次的に処理します。",
|
|
87
85
|
)
|
|
88
86
|
|
|
89
87
|
parser.set_defaults(subcommand_func=main)
|
annofabcli/common/cli.py
CHANGED
|
@@ -25,7 +25,6 @@ from annofabcli.common.exceptions import AnnofabCliException, AuthenticationErro
|
|
|
25
25
|
from annofabcli.common.facade import AnnofabApiFacade
|
|
26
26
|
from annofabcli.common.typing import InputDataSize
|
|
27
27
|
from annofabcli.common.utils import (
|
|
28
|
-
DEFAULT_CSV_FORMAT,
|
|
29
28
|
get_file_scheme_path,
|
|
30
29
|
print_according_to_format,
|
|
31
30
|
print_csv,
|
|
@@ -130,9 +129,7 @@ def add_parser(
|
|
|
130
129
|
|
|
131
130
|
group.add_argument("--disable_log", action="store_true", help="ログを無効にします。")
|
|
132
131
|
|
|
133
|
-
group.add_argument(
|
|
134
|
-
"--debug", action="store_true", help="HTTPリクエストの内容やレスポンスのステータスコードなど、デバッグ用のログが出力されます。"
|
|
135
|
-
)
|
|
132
|
+
group.add_argument("--debug", action="store_true", help="HTTPリクエストの内容やレスポンスのステータスコードなど、デバッグ用のログが出力されます。")
|
|
136
133
|
|
|
137
134
|
return parent_parser
|
|
138
135
|
|
|
@@ -150,7 +147,7 @@ def add_parser(
|
|
|
150
147
|
)
|
|
151
148
|
parser.set_defaults(command_help=parser.print_help)
|
|
152
149
|
|
|
153
|
-
# 引数グループに"global optional group"がある場合は、"--help"オプションをデフォルトの"optional"グループから、"global optional arguments"グループに移動する
|
|
150
|
+
# 引数グループに"global optional group"がある場合は、"--help"オプションをデフォルトの"optional"グループから、"global optional arguments"グループに移動する
|
|
154
151
|
# https://ja.stackoverflow.com/a/57313/19524
|
|
155
152
|
global_optional_argument_group = first_true(parser._action_groups, pred=lambda e: e.title == GLOBAL_OPTIONAL_ARGUMENTS_TITLE) # noqa: SLF001
|
|
156
153
|
if global_optional_argument_group is not None:
|
|
@@ -188,20 +185,6 @@ def get_list_from_args(str_list: Optional[list[str]] = None) -> list[str]:
|
|
|
188
185
|
return str_list
|
|
189
186
|
|
|
190
187
|
|
|
191
|
-
def get_csv_format_from_args(target: Optional[str] = None) -> dict[str, Any]:
|
|
192
|
-
"""
|
|
193
|
-
コマンドライン引数の値から csv_format を取得する。
|
|
194
|
-
Default: {"encoding": "utf_8_sig", "index": False}
|
|
195
|
-
|
|
196
|
-
"""
|
|
197
|
-
csv_format = DEFAULT_CSV_FORMAT.copy()
|
|
198
|
-
if target is not None:
|
|
199
|
-
arg_csv_format = get_json_from_args(target)
|
|
200
|
-
csv_format.update(arg_csv_format)
|
|
201
|
-
|
|
202
|
-
return csv_format
|
|
203
|
-
|
|
204
|
-
|
|
205
188
|
def get_json_from_args(target: Optional[str] = None) -> Any: # noqa: ANN401
|
|
206
189
|
"""
|
|
207
190
|
JSON形式をPythonオブジェクトに変換する。
|
|
@@ -420,9 +403,7 @@ class ArgumentParser:
|
|
|
420
403
|
'--input_data_id` 引数を追加
|
|
421
404
|
"""
|
|
422
405
|
if help_message is None:
|
|
423
|
-
help_message =
|
|
424
|
-
"対象の入力データのinput_data_idを指定します。 ``file://`` を先頭に付けると、input_data_idの一覧が記載されたファイルを指定できます。"
|
|
425
|
-
)
|
|
406
|
+
help_message = "対象の入力データのinput_data_idを指定します。 ``file://`` を先頭に付けると、input_data_idの一覧が記載されたファイルを指定できます。"
|
|
426
407
|
|
|
427
408
|
self.parser.add_argument("-i", "--input_data_id", type=str, required=required, nargs="+", help=help_message)
|
|
428
409
|
|
|
@@ -435,19 +416,6 @@ class ArgumentParser:
|
|
|
435
416
|
|
|
436
417
|
self.parser.add_argument("-f", "--format", type=str, choices=[e.value for e in choices], default=default.value, help=help_message)
|
|
437
418
|
|
|
438
|
-
def add_csv_format(self, help_message: Optional[str] = None) -> None:
|
|
439
|
-
"""
|
|
440
|
-
'--csv_format` 引数を追加
|
|
441
|
-
"""
|
|
442
|
-
if help_message is None:
|
|
443
|
-
help_message = (
|
|
444
|
-
"CSVのフォーマットをJSON形式で指定します。 ``--format`` が ``csv`` でないときは、このオプションは無視されます。\n"
|
|
445
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。\n"
|
|
446
|
-
"指定した値は ``pandas.DataFrame.to_csv`` の引数として渡されます。"
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
self.parser.add_argument("--csv_format", type=str, help=help_message)
|
|
450
|
-
|
|
451
419
|
def add_output(self, *, required: bool = False, help_message: Optional[str] = None) -> None:
|
|
452
420
|
"""
|
|
453
421
|
'--output` 引数を追加
|
|
@@ -510,9 +478,6 @@ class CommandLineWithoutWebapi:
|
|
|
510
478
|
#: 出力先
|
|
511
479
|
output: Optional[str] = None
|
|
512
480
|
|
|
513
|
-
#: CSVのフォーマット
|
|
514
|
-
csv_format: Optional[dict[str, Any]] = None
|
|
515
|
-
|
|
516
481
|
#: 出力フォーマット
|
|
517
482
|
str_format: Optional[str] = None
|
|
518
483
|
|
|
@@ -530,9 +495,6 @@ class CommandLineWithoutWebapi:
|
|
|
530
495
|
if hasattr(args, "query"):
|
|
531
496
|
self.query = args.query
|
|
532
497
|
|
|
533
|
-
if hasattr(args, "csv_format"):
|
|
534
|
-
self.csv_format = get_csv_format_from_args(args.csv_format)
|
|
535
|
-
|
|
536
498
|
if hasattr(args, "output"):
|
|
537
499
|
self.output = args.output
|
|
538
500
|
|
|
@@ -590,10 +552,10 @@ class CommandLineWithoutWebapi:
|
|
|
590
552
|
return True
|
|
591
553
|
|
|
592
554
|
def print_csv(self, df: pandas.DataFrame) -> None:
|
|
593
|
-
print_csv(df, output=self.output
|
|
555
|
+
print_csv(df, output=self.output)
|
|
594
556
|
|
|
595
557
|
def print_according_to_format(self, target: Any) -> None: # noqa: ANN401
|
|
596
|
-
print_according_to_format(target, format=FormatArgument(self.str_format), output=self.output
|
|
558
|
+
print_according_to_format(target, format=FormatArgument(self.str_format), output=self.output)
|
|
597
559
|
|
|
598
560
|
|
|
599
561
|
class PrettyHelpFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter):
|
annofabcli/common/download.py
CHANGED
|
@@ -110,10 +110,7 @@ class DownloadingFile:
|
|
|
110
110
|
except requests.HTTPError as e:
|
|
111
111
|
# すでにジョブが進行中の場合は、無視する
|
|
112
112
|
if e.response.status_code == requests.codes.conflict:
|
|
113
|
-
logger.warning(
|
|
114
|
-
f"別のバックグラウンドジョブが既に実行されているので、アノテーションZIPの更新処理を実行できません。"
|
|
115
|
-
f" :: error_message: {_get_annofab_error_message(e)}"
|
|
116
|
-
)
|
|
113
|
+
logger.warning(f"別のバックグラウンドジョブが既に実行されているので、アノテーションZIPの更新処理を実行できません。 :: error_message: {_get_annofab_error_message(e)}")
|
|
117
114
|
else:
|
|
118
115
|
raise e # noqa: TRY201
|
|
119
116
|
|
|
@@ -160,9 +157,7 @@ class DownloadingFile:
|
|
|
160
157
|
except requests.HTTPError as e:
|
|
161
158
|
# すでにジョブが進行中の場合は、無視する
|
|
162
159
|
if e.response.status_code == requests.codes.conflict:
|
|
163
|
-
logger.warning(
|
|
164
|
-
f"別のバックグラウンドジョブが既に実行されているので、更新処理を無視します。 :: error_message: {_get_annofab_error_message(e)}"
|
|
165
|
-
)
|
|
160
|
+
logger.warning(f"別のバックグラウンドジョブが既に実行されているので、更新処理を無視します。 :: error_message: {_get_annofab_error_message(e)}")
|
|
166
161
|
else:
|
|
167
162
|
raise e # noqa: TRY201
|
|
168
163
|
|
|
@@ -179,9 +174,7 @@ class DownloadingFile:
|
|
|
179
174
|
partial_func = partial(self.download_task_json, project_id, dest_path, is_latest=is_latest, wait_options=wait_options)
|
|
180
175
|
await loop.run_in_executor(None, partial_func)
|
|
181
176
|
|
|
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:
|
|
177
|
+
def download_task_json(self, project_id: str, dest_path: Union[str, Path], *, is_latest: bool = False, wait_options: Optional[WaitOptions] = None) -> None:
|
|
185
178
|
if is_latest:
|
|
186
179
|
self.wait_until_updated_task_json(project_id, wait_options)
|
|
187
180
|
self.service.wrapper.download_project_tasks_url(project_id, dest_path)
|
|
@@ -205,9 +198,7 @@ class DownloadingFile:
|
|
|
205
198
|
except requests.HTTPError as e:
|
|
206
199
|
# すでにジョブが進行中の場合は、無視する
|
|
207
200
|
if e.response.status_code == requests.codes.conflict:
|
|
208
|
-
logger.warning(
|
|
209
|
-
f"別のバックグラウンドジョブが既に実行されているので、更新処理を無視します。 :: error_message={_get_annofab_error_message(e)}"
|
|
210
|
-
)
|
|
201
|
+
logger.warning(f"別のバックグラウンドジョブが既に実行されているので、更新処理を無視します。 :: error_message={_get_annofab_error_message(e)}")
|
|
211
202
|
else:
|
|
212
203
|
raise e # noqa: TRY201
|
|
213
204
|
|
|
@@ -237,9 +228,7 @@ class DownloadingFile:
|
|
|
237
228
|
self.service.wrapper.download_project_task_histories_url(project_id, dest_path)
|
|
238
229
|
except requests.HTTPError as e:
|
|
239
230
|
if e.response.status_code == requests.codes.not_found:
|
|
240
|
-
raise DownloadingFileNotFoundError(
|
|
241
|
-
f"project_id='{project_id}'のプロジェクトに、タスク履歴全件ファイルが存在しないため、ダウンロードできませんでした。"
|
|
242
|
-
) from e
|
|
231
|
+
raise DownloadingFileNotFoundError(f"project_id='{project_id}'のプロジェクトに、タスク履歴全件ファイルが存在しないため、ダウンロードできませんでした。") from e
|
|
243
232
|
raise e # noqa: TRY201
|
|
244
233
|
|
|
245
234
|
def download_task_history_event_json(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
@@ -257,9 +246,7 @@ class DownloadingFile:
|
|
|
257
246
|
self.service.wrapper.download_project_task_history_events_url(project_id, dest_path)
|
|
258
247
|
except requests.HTTPError as e:
|
|
259
248
|
if e.response.status_code == requests.codes.not_found:
|
|
260
|
-
raise DownloadingFileNotFoundError(
|
|
261
|
-
f"project_id='{project_id}'のプロジェクトに、タスク履歴イベント全件ファイルが存在しないため、ダウンロードできませんでした。"
|
|
262
|
-
) from e
|
|
249
|
+
raise DownloadingFileNotFoundError(f"project_id='{project_id}'のプロジェクトに、タスク履歴イベント全件ファイルが存在しないため、ダウンロードできませんでした。") from e
|
|
263
250
|
raise e # noqa: TRY201
|
|
264
251
|
|
|
265
252
|
async def download_task_history_event_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
@@ -294,9 +281,7 @@ class DownloadingFile:
|
|
|
294
281
|
self.service.wrapper.download_project_inspections_url(project_id, dest_path)
|
|
295
282
|
except requests.HTTPError as e:
|
|
296
283
|
if e.response.status_code == requests.codes.not_found:
|
|
297
|
-
raise DownloadingFileNotFoundError(
|
|
298
|
-
f"project_id='{project_id}'のプロジェクトに、検査コメント全件ファイルが存在しないため、ダウンロードできませんでした。"
|
|
299
|
-
) from e
|
|
284
|
+
raise DownloadingFileNotFoundError(f"project_id='{project_id}'のプロジェクトに、検査コメント全件ファイルが存在しないため、ダウンロードできませんでした。") from e
|
|
300
285
|
raise e # noqa: TRY201
|
|
301
286
|
|
|
302
287
|
async def download_comment_json_with_async(self, project_id: str, dest_path: Union[str, Path]) -> None:
|
|
@@ -321,7 +306,5 @@ class DownloadingFile:
|
|
|
321
306
|
self.service.wrapper.download_project_comments_url(project_id, dest_path)
|
|
322
307
|
except requests.HTTPError as e:
|
|
323
308
|
if e.response.status_code == requests.codes.not_found:
|
|
324
|
-
raise DownloadingFileNotFoundError(
|
|
325
|
-
f"project_id='{project_id}'のプロジェクトに、コメント全件ファイルが存在しないため、ダウンロードできませんでした。"
|
|
326
|
-
) from e
|
|
309
|
+
raise DownloadingFileNotFoundError(f"project_id='{project_id}'のプロジェクトに、コメント全件ファイルが存在しないため、ダウンロードできませんでした。") from e
|
|
327
310
|
raise e # noqa: TRY201
|
annofabcli/common/image.py
CHANGED
|
@@ -225,9 +225,7 @@ def write_annotation_grayscale_image(
|
|
|
225
225
|
annotation_list = list(reversed(simple_annotation["details"]))
|
|
226
226
|
|
|
227
227
|
# 描画対象のアノテーションを絞り込む
|
|
228
|
-
annotation_list = [
|
|
229
|
-
e for e in annotation_list if e["data"] is not None and e["data"]["_type"] in ["BoundingBox", "Points", "SegmentationV2", "Segmentation"]
|
|
230
|
-
]
|
|
228
|
+
annotation_list = [e for e in annotation_list if e["data"] is not None and e["data"]["_type"] in ["BoundingBox", "Points", "SegmentationV2", "Segmentation"]]
|
|
231
229
|
|
|
232
230
|
if len(annotation_list) > 255:
|
|
233
231
|
# channel depthは8bitなので、255個のアノテーションまでしか描画できない
|
|
@@ -297,7 +295,7 @@ def write_annotation_images_from_path(
|
|
|
297
295
|
Returns:
|
|
298
296
|
True: アノテーション情報の画像化に成功した。False: アノテーション情報の画像化に失敗した。
|
|
299
297
|
|
|
300
|
-
"""
|
|
298
|
+
"""
|
|
301
299
|
|
|
302
300
|
def _get_image_size(input_data_id: str) -> Optional[InputDataSize]:
|
|
303
301
|
def _get_image_size_from_system_metadata(arg_input_data: dict[str, Any]): # noqa: ANN202
|
|
@@ -306,9 +304,7 @@ def write_annotation_images_from_path(
|
|
|
306
304
|
if original_resolution is not None and (original_resolution.get("width") is not None and original_resolution.get("height") is not None):
|
|
307
305
|
return original_resolution.get("width"), original_resolution.get("height")
|
|
308
306
|
else:
|
|
309
|
-
logger.warning(
|
|
310
|
-
f"input_data_id='{input_data_id}'のプロパティ`system_metadata.original_resolution`には画像サイズが設定されていません。"
|
|
311
|
-
)
|
|
307
|
+
logger.warning(f"input_data_id='{input_data_id}'のプロパティ`system_metadata.original_resolution`には画像サイズが設定されていません。")
|
|
312
308
|
return None
|
|
313
309
|
|
|
314
310
|
if image_size is not None:
|
annofabcli/common/utils.py
CHANGED
|
@@ -110,7 +110,6 @@ def print_according_to_format(
|
|
|
110
110
|
target: Any, # noqa: ANN401
|
|
111
111
|
format: FormatArgument, # noqa: A002
|
|
112
112
|
output: Optional[Union[str, Path]] = None,
|
|
113
|
-
csv_format: Optional[dict[str, Any]] = None,
|
|
114
113
|
) -> None:
|
|
115
114
|
"""
|
|
116
115
|
コマンドライン引数 ``--format`` の値にしたがって、内容を出力する。
|
|
@@ -119,7 +118,6 @@ def print_according_to_format(
|
|
|
119
118
|
target: 出力する内容
|
|
120
119
|
format: 出力フォーマット
|
|
121
120
|
output: 出力先(オプション)
|
|
122
|
-
csv_format: CSVのフォーマット(オプション)
|
|
123
121
|
|
|
124
122
|
|
|
125
123
|
"""
|
|
@@ -132,7 +130,7 @@ def print_according_to_format(
|
|
|
132
130
|
|
|
133
131
|
elif format == FormatArgument.CSV:
|
|
134
132
|
df = pandas.DataFrame(target)
|
|
135
|
-
print_csv(df, output=output
|
|
133
|
+
print_csv(df, output=output)
|
|
136
134
|
|
|
137
135
|
elif format == FormatArgument.TASK_ID_LIST:
|
|
138
136
|
task_id_list = [e["task_id"] for e in target]
|
|
@@ -223,7 +221,7 @@ def get_cache_dir() -> Path:
|
|
|
223
221
|
|
|
224
222
|
"""
|
|
225
223
|
cache_home_dir = os.environ.get("XDG_CACHE_HOME")
|
|
226
|
-
if cache_home_dir is None:
|
|
224
|
+
if cache_home_dir is None:
|
|
227
225
|
cache_home_dir_path = Path.home() / ".cache"
|
|
228
226
|
else:
|
|
229
227
|
cache_home_dir_path = Path(cache_home_dir)
|
annofabcli/common/visualize.py
CHANGED
|
@@ -47,9 +47,7 @@ class AddProps:
|
|
|
47
47
|
アノテーション仕様に関する情報をインスタンス変数に格納します。
|
|
48
48
|
"""
|
|
49
49
|
annotation_specs, _ = self.service.api.get_annotation_specs(self.project_id, query_params={"v": "2"})
|
|
50
|
-
self._specs_labels = convert_annotation_specs_labels_v2_to_v1(
|
|
51
|
-
labels_v2=annotation_specs["labels"], additionals_v2=annotation_specs["additionals"]
|
|
52
|
-
)
|
|
50
|
+
self._specs_labels = convert_annotation_specs_labels_v2_to_v1(labels_v2=annotation_specs["labels"], additionals_v2=annotation_specs["additionals"])
|
|
53
51
|
self._specs_inspection_phrases = annotation_specs["inspection_phrases"]
|
|
54
52
|
|
|
55
53
|
@property
|
|
@@ -345,7 +343,7 @@ class AddProps:
|
|
|
345
343
|
task["number_of_rejections_by_inspection"] = get_number_of_rejections(histories, TaskPhase.INSPECTION)
|
|
346
344
|
task["number_of_rejections_by_acceptance"] = get_number_of_rejections(histories, TaskPhase.ACCEPTANCE)
|
|
347
345
|
|
|
348
|
-
# number_of_rejectionsは非推奨なプロパティで、number_of_rejections_by_inspection/number_of_rejections_by_acceptanceと矛盾する場合があるので、削除する
|
|
346
|
+
# number_of_rejectionsは非推奨なプロパティで、number_of_rejections_by_inspection/number_of_rejections_by_acceptanceと矛盾する場合があるので、削除する
|
|
349
347
|
task.pop("number_of_rejections", None)
|
|
350
348
|
|
|
351
349
|
task["input_data_count"] = len(task["input_data_id_list"])
|
|
@@ -266,18 +266,14 @@ def draw_annotation_all( # noqa: ANN201, PLR0913
|
|
|
266
266
|
|
|
267
267
|
try:
|
|
268
268
|
drawing.main(parser, image_file=image_file, output_file=output_file, image_size=default_image_size)
|
|
269
|
-
logger.debug(
|
|
270
|
-
f"{success_count + 1}件目: {output_file!s} を出力しました。image_file={image_file}, アノテーションJSON={parser.json_file_path}"
|
|
271
|
-
)
|
|
269
|
+
logger.debug(f"{success_count + 1}件目: {output_file!s} を出力しました。image_file={image_file}, アノテーションJSON={parser.json_file_path}")
|
|
272
270
|
success_count += 1
|
|
273
271
|
except Exception: # pylint: disable=broad-except
|
|
274
272
|
logger.warning(f"{parser.json_file_path} のアノテーションの描画に失敗しました。", exc_info=True)
|
|
275
273
|
|
|
276
274
|
logger.info(f"{success_count} / {total_count} 件、アノテーションを描画しました。")
|
|
277
275
|
|
|
278
|
-
new_label_color_dict = {
|
|
279
|
-
label_name: ImageColor.getrgb(color) if isinstance(color, str) else color for label_name, color in drawing.label_color_dict.items()
|
|
280
|
-
}
|
|
276
|
+
new_label_color_dict = {label_name: ImageColor.getrgb(color) if isinstance(color, str) else color for label_name, color in drawing.label_color_dict.items()}
|
|
281
277
|
logger.info(f"label_color={json.dumps(new_label_color_dict, ensure_ascii=False)}")
|
|
282
278
|
|
|
283
279
|
|
|
@@ -367,9 +363,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
367
363
|
help="Annofabからダウンロードしたアノテーションzip、またはzipを展開したディレクトリを指定してください。",
|
|
368
364
|
)
|
|
369
365
|
|
|
370
|
-
parser.add_argument(
|
|
371
|
-
"--image_dir", type=Path, help="画像が存在するディレクトリを指定してください。\n'--input_data_id_csv'を指定したときは必須です。"
|
|
372
|
-
)
|
|
366
|
+
parser.add_argument("--image_dir", type=Path, help="画像が存在するディレクトリを指定してください。\n'--input_data_id_csv'を指定したときは必須です。")
|
|
373
367
|
|
|
374
368
|
parser.add_argument(
|
|
375
369
|
"--input_data_id_csv",
|
|
@@ -380,17 +374,13 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
380
374
|
"詳細は https://annofab-cli.readthedocs.io/ja/latest/command_reference/filesystem/draw_annotation.html を参照してください。",
|
|
381
375
|
)
|
|
382
376
|
|
|
383
|
-
parser.add_argument(
|
|
384
|
-
"--default_image_size", type=str, help="デフォルトの画像サイズ。 ``--input_data_id_csv`` を指定しないときは必須です。\n(例) 1280x720"
|
|
385
|
-
)
|
|
377
|
+
parser.add_argument("--default_image_size", type=str, help="デフォルトの画像サイズ。 ``--input_data_id_csv`` を指定しないときは必須です。\n(例) 1280x720")
|
|
386
378
|
|
|
387
379
|
LABEL_COLOR_SAMPLE = {"dog": [255, 128, 64], "cat": "blue"} # noqa: N806
|
|
388
380
|
parser.add_argument(
|
|
389
381
|
"--label_color",
|
|
390
382
|
type=str,
|
|
391
|
-
help="label_nameとRGBの関係をJSON形式で指定します。\n"
|
|
392
|
-
f"(例) ``{json.dumps(LABEL_COLOR_SAMPLE)}``\n"
|
|
393
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
383
|
+
help=f"label_nameとRGBの関係をJSON形式で指定します。\n(例) ``{json.dumps(LABEL_COLOR_SAMPLE)}``\n``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
394
384
|
)
|
|
395
385
|
|
|
396
386
|
parser.add_argument(
|
|
@@ -425,9 +415,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
425
415
|
parser.add_argument(
|
|
426
416
|
"--drawing_options",
|
|
427
417
|
type=str,
|
|
428
|
-
help="描画オプションをJSON形式で指定します。\n"
|
|
429
|
-
f"(例) ``{json.dumps(DRAWING_OPTIONS_SAMPLE)}``\n\n"
|
|
430
|
-
"``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
418
|
+
help=f"描画オプションをJSON形式で指定します。\n(例) ``{json.dumps(DRAWING_OPTIONS_SAMPLE)}``\n\n``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
431
419
|
)
|
|
432
420
|
|
|
433
421
|
argument_parser.add_task_id(
|