annofabcli 1.109.0__py3-none-any.whl → 1.111.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/annotation_zip/list_annotation_bounding_box_2d.py +49 -37
- annofabcli/annotation_zip/list_range_annotation.py +308 -0
- annofabcli/annotation_zip/list_single_point_annotation.py +317 -0
- annofabcli/annotation_zip/subcommand_annotation_zip.py +6 -0
- annofabcli/annotation_zip/validate_annotation.py +393 -0
- annofabcli/common/download.py +97 -0
- annofabcli/statistics/list_annotation_area.py +2 -3
- annofabcli/statistics/list_annotation_attribute_filled_count.py +35 -10
- annofabcli/statistics/list_annotation_count.py +39 -14
- annofabcli/statistics/list_annotation_duration.py +4 -6
- annofabcli/statistics/summarize_task_count.py +53 -33
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +30 -13
- annofabcli/statistics/summarize_task_count_by_user.py +32 -15
- annofabcli/statistics/visualization/dataframe/annotation_count.py +31 -3
- annofabcli/statistics/visualization/dataframe/annotation_duration.py +121 -0
- annofabcli/statistics/visualize_statistics.py +83 -5
- annofabcli/task/complete_tasks.py +2 -2
- annofabcli/task/put_tasks.py +1 -1
- {annofabcli-1.109.0.dist-info → annofabcli-1.111.0.dist-info}/METADATA +1 -1
- {annofabcli-1.109.0.dist-info → annofabcli-1.111.0.dist-info}/RECORD +23 -19
- {annofabcli-1.109.0.dist-info → annofabcli-1.111.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.109.0.dist-info → annofabcli-1.111.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.109.0.dist-info → annofabcli-1.111.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,12 +16,7 @@ from dataclasses_json import DataClassJsonMixin
|
|
|
16
16
|
import annofabcli
|
|
17
17
|
import annofabcli.common.cli
|
|
18
18
|
from annofabcli.common.annofab.annotation_zip import lazy_parse_simple_annotation_by_input_data
|
|
19
|
-
from annofabcli.common.cli import
|
|
20
|
-
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
21
|
-
ArgumentParser,
|
|
22
|
-
CommandLine,
|
|
23
|
-
build_annofabapi_resource_and_login,
|
|
24
|
-
)
|
|
19
|
+
from annofabcli.common.cli import COMMAND_LINE_ERROR_STATUS_CODE, ArgumentParser, CommandLine, build_annofabapi_resource_and_login, get_list_from_args
|
|
25
20
|
from annofabcli.common.download import DownloadingFile
|
|
26
21
|
from annofabcli.common.enums import FormatArgument
|
|
27
22
|
from annofabcli.common.facade import (
|
|
@@ -56,10 +51,16 @@ class AnnotationBoundingBoxInfo(DataClassJsonMixin):
|
|
|
56
51
|
height: int
|
|
57
52
|
|
|
58
53
|
|
|
59
|
-
def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any]) -> list[AnnotationBoundingBoxInfo]:
|
|
54
|
+
def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any], *, target_label_names: Optional[Collection[str]] = None) -> list[AnnotationBoundingBoxInfo]:
|
|
60
55
|
result = []
|
|
56
|
+
target_label_names_set = set(target_label_names) if target_label_names is not None else None
|
|
61
57
|
for detail in simple_annotation["details"]:
|
|
62
58
|
if detail["data"]["_type"] == "BoundingBox":
|
|
59
|
+
label = detail["label"]
|
|
60
|
+
# ラベル名によるフィルタリング
|
|
61
|
+
if target_label_names_set is not None and label not in target_label_names_set:
|
|
62
|
+
continue
|
|
63
|
+
|
|
63
64
|
left_top = detail["data"]["left_top"]
|
|
64
65
|
right_bottom = detail["data"]["right_bottom"]
|
|
65
66
|
width = abs(right_bottom["x"] - left_top["x"])
|
|
@@ -74,7 +75,7 @@ def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any]) ->
|
|
|
74
75
|
task_status=simple_annotation["task_status"],
|
|
75
76
|
input_data_id=simple_annotation["input_data_id"],
|
|
76
77
|
input_data_name=simple_annotation["input_data_name"],
|
|
77
|
-
label=
|
|
78
|
+
label=label,
|
|
78
79
|
annotation_id=detail["annotation_id"],
|
|
79
80
|
left_top=left_top,
|
|
80
81
|
right_bottom=right_bottom,
|
|
@@ -92,6 +93,7 @@ def get_annotation_bounding_box_info_list_from_annotation_path(
|
|
|
92
93
|
*,
|
|
93
94
|
target_task_ids: Optional[Collection[str]] = None,
|
|
94
95
|
task_query: Optional[TaskQuery] = None,
|
|
96
|
+
target_label_names: Optional[Collection[str]] = None,
|
|
95
97
|
) -> list[AnnotationBoundingBoxInfo]:
|
|
96
98
|
annotation_bbox_list = []
|
|
97
99
|
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
@@ -105,7 +107,7 @@ def get_annotation_bounding_box_info_list_from_annotation_path(
|
|
|
105
107
|
dict_simple_annotation = parser.load_json()
|
|
106
108
|
if task_query is not None and not match_annotation_with_task_query(dict_simple_annotation, task_query):
|
|
107
109
|
continue
|
|
108
|
-
sub_annotation_bbox_list = get_annotation_bounding_box_info_list(dict_simple_annotation)
|
|
110
|
+
sub_annotation_bbox_list = get_annotation_bounding_box_info_list(dict_simple_annotation, target_label_names=target_label_names)
|
|
109
111
|
annotation_bbox_list.extend(sub_annotation_bbox_list)
|
|
110
112
|
return annotation_bbox_list
|
|
111
113
|
|
|
@@ -131,32 +133,30 @@ def create_df(
|
|
|
131
133
|
"width",
|
|
132
134
|
"height",
|
|
133
135
|
]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
else:
|
|
159
|
-
df = pandas.DataFrame(columns=columns)
|
|
136
|
+
df = pandas.DataFrame(
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
"project_id": e.project_id,
|
|
140
|
+
"task_id": e.task_id,
|
|
141
|
+
"task_status": e.task_status,
|
|
142
|
+
"task_phase": e.task_phase,
|
|
143
|
+
"task_phase_stage": e.task_phase_stage,
|
|
144
|
+
"input_data_id": e.input_data_id,
|
|
145
|
+
"input_data_name": e.input_data_name,
|
|
146
|
+
"updated_datetime": e.updated_datetime,
|
|
147
|
+
"label": e.label,
|
|
148
|
+
"annotation_id": e.annotation_id,
|
|
149
|
+
"left_top.x": e.left_top["x"],
|
|
150
|
+
"left_top.y": e.left_top["y"],
|
|
151
|
+
"right_bottom.x": e.right_bottom["x"],
|
|
152
|
+
"right_bottom.y": e.right_bottom["y"],
|
|
153
|
+
"width": e.width,
|
|
154
|
+
"height": e.height,
|
|
155
|
+
}
|
|
156
|
+
for e in annotation_bbox_list
|
|
157
|
+
],
|
|
158
|
+
columns=columns,
|
|
159
|
+
)
|
|
160
160
|
|
|
161
161
|
return df[columns]
|
|
162
162
|
|
|
@@ -168,11 +168,13 @@ def print_annotation_bounding_box(
|
|
|
168
168
|
*,
|
|
169
169
|
target_task_ids: Optional[Collection[str]] = None,
|
|
170
170
|
task_query: Optional[TaskQuery] = None,
|
|
171
|
+
target_label_names: Optional[Collection[str]] = None,
|
|
171
172
|
) -> None:
|
|
172
173
|
annotation_bbox_list = get_annotation_bounding_box_info_list_from_annotation_path(
|
|
173
174
|
annotation_path,
|
|
174
175
|
target_task_ids=target_task_ids,
|
|
175
176
|
task_query=task_query,
|
|
177
|
+
target_label_names=target_label_names,
|
|
176
178
|
)
|
|
177
179
|
|
|
178
180
|
logger.info(f"{len(annotation_bbox_list)} 件のバウンディングボックスアノテーションの情報を出力します。 :: output='{output_file}'")
|
|
@@ -195,7 +197,7 @@ def print_annotation_bounding_box(
|
|
|
195
197
|
|
|
196
198
|
|
|
197
199
|
class ListAnnotationBoundingBox2d(CommandLine):
|
|
198
|
-
COMMON_MESSAGE = "annofabcli annotation_zip
|
|
200
|
+
COMMON_MESSAGE = "annofabcli annotation_zip list_bounding_box_annotation: error:"
|
|
199
201
|
|
|
200
202
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
201
203
|
if args.project_id is None and args.annotation is None:
|
|
@@ -224,6 +226,7 @@ class ListAnnotationBoundingBox2d(CommandLine):
|
|
|
224
226
|
|
|
225
227
|
task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
|
|
226
228
|
task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
|
|
229
|
+
label_name_list = get_list_from_args(args.label_name) if args.label_name is not None else None
|
|
227
230
|
|
|
228
231
|
output_file: Path = args.output
|
|
229
232
|
output_format = FormatArgument(args.format)
|
|
@@ -243,6 +246,7 @@ class ListAnnotationBoundingBox2d(CommandLine):
|
|
|
243
246
|
output_format,
|
|
244
247
|
target_task_ids=task_id_list,
|
|
245
248
|
task_query=task_query,
|
|
249
|
+
target_label_names=label_name_list,
|
|
246
250
|
)
|
|
247
251
|
|
|
248
252
|
if project_id is not None:
|
|
@@ -263,6 +267,7 @@ class ListAnnotationBoundingBox2d(CommandLine):
|
|
|
263
267
|
output_format,
|
|
264
268
|
target_task_ids=task_id_list,
|
|
265
269
|
task_query=task_query,
|
|
270
|
+
target_label_names=label_name_list,
|
|
266
271
|
)
|
|
267
272
|
|
|
268
273
|
|
|
@@ -294,6 +299,13 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
294
299
|
)
|
|
295
300
|
argument_parser.add_task_id(required=False)
|
|
296
301
|
|
|
302
|
+
parser.add_argument(
|
|
303
|
+
"--label_name",
|
|
304
|
+
type=str,
|
|
305
|
+
nargs="*",
|
|
306
|
+
help="指定したラベル名のバウンディングボックスアノテーションのみを対象にします。複数指定できます。",
|
|
307
|
+
)
|
|
308
|
+
|
|
297
309
|
parser.add_argument(
|
|
298
310
|
"--latest",
|
|
299
311
|
action="store_true",
|
|
@@ -316,7 +328,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
316
328
|
|
|
317
329
|
|
|
318
330
|
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
319
|
-
subcommand_name = "
|
|
331
|
+
subcommand_name = "list_bounding_box_annotation"
|
|
320
332
|
subcommand_help = "アノテーションZIPからバウンディングボックス(矩形)アノテーションの座標情報を出力します。"
|
|
321
333
|
epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
322
334
|
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
from collections.abc import Collection
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Optional
|
|
11
|
+
|
|
12
|
+
import pandas
|
|
13
|
+
from annofabapi.models import InputDataType, ProjectMemberRole
|
|
14
|
+
from dataclasses_json import DataClassJsonMixin
|
|
15
|
+
|
|
16
|
+
import annofabcli
|
|
17
|
+
import annofabcli.common.cli
|
|
18
|
+
from annofabcli.common.annofab.annotation_zip import lazy_parse_simple_annotation_by_input_data
|
|
19
|
+
from annofabcli.common.cli import COMMAND_LINE_ERROR_STATUS_CODE, ArgumentParser, CommandLine, build_annofabapi_resource_and_login, get_list_from_args
|
|
20
|
+
from annofabcli.common.download import DownloadingFile
|
|
21
|
+
from annofabcli.common.enums import FormatArgument
|
|
22
|
+
from annofabcli.common.facade import (
|
|
23
|
+
AnnofabApiFacade,
|
|
24
|
+
TaskQuery,
|
|
25
|
+
match_annotation_with_task_query,
|
|
26
|
+
)
|
|
27
|
+
from annofabcli.common.utils import print_csv, print_json
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class RangeAnnotationInfo(DataClassJsonMixin):
|
|
34
|
+
project_id: str
|
|
35
|
+
task_id: str
|
|
36
|
+
task_status: str
|
|
37
|
+
task_phase: str
|
|
38
|
+
task_phase_stage: int
|
|
39
|
+
|
|
40
|
+
input_data_id: str
|
|
41
|
+
input_data_name: str
|
|
42
|
+
|
|
43
|
+
updated_datetime: Optional[str]
|
|
44
|
+
"""アノテーションJSONに格納されているアノテーションの更新日時"""
|
|
45
|
+
|
|
46
|
+
label: str
|
|
47
|
+
annotation_id: str
|
|
48
|
+
begin_second: float
|
|
49
|
+
end_second: float
|
|
50
|
+
duration_second: float
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_range_annotation_info_list(simple_annotation: dict[str, Any], *, target_label_names: Optional[Collection[str]] = None) -> list[RangeAnnotationInfo]:
|
|
54
|
+
result = []
|
|
55
|
+
target_label_names_set = set(target_label_names) if target_label_names is not None else None
|
|
56
|
+
for detail in simple_annotation["details"]:
|
|
57
|
+
if detail["data"]["_type"] == "Range":
|
|
58
|
+
label = detail["label"]
|
|
59
|
+
# ラベル名によるフィルタリング
|
|
60
|
+
if target_label_names_set is not None and label not in target_label_names_set:
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
begin_millisecond = detail["data"]["begin"]
|
|
64
|
+
end_millisecond = detail["data"]["end"]
|
|
65
|
+
begin_second = begin_millisecond / 1000
|
|
66
|
+
end_second = end_millisecond / 1000
|
|
67
|
+
duration_second = end_second - begin_second
|
|
68
|
+
|
|
69
|
+
result.append(
|
|
70
|
+
RangeAnnotationInfo(
|
|
71
|
+
project_id=simple_annotation["project_id"],
|
|
72
|
+
task_id=simple_annotation["task_id"],
|
|
73
|
+
task_phase=simple_annotation["task_phase"],
|
|
74
|
+
task_phase_stage=simple_annotation["task_phase_stage"],
|
|
75
|
+
task_status=simple_annotation["task_status"],
|
|
76
|
+
input_data_id=simple_annotation["input_data_id"],
|
|
77
|
+
input_data_name=simple_annotation["input_data_name"],
|
|
78
|
+
label=label,
|
|
79
|
+
annotation_id=detail["annotation_id"],
|
|
80
|
+
begin_second=begin_second,
|
|
81
|
+
end_second=end_second,
|
|
82
|
+
duration_second=duration_second,
|
|
83
|
+
updated_datetime=simple_annotation["updated_datetime"],
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_range_annotation_info_list_from_annotation_path(
|
|
91
|
+
annotation_path: Path,
|
|
92
|
+
*,
|
|
93
|
+
target_task_ids: Optional[Collection[str]] = None,
|
|
94
|
+
task_query: Optional[TaskQuery] = None,
|
|
95
|
+
target_label_names: Optional[Collection[str]] = None,
|
|
96
|
+
) -> list[RangeAnnotationInfo]:
|
|
97
|
+
range_annotation_list = []
|
|
98
|
+
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
99
|
+
iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_path)
|
|
100
|
+
logger.info(f"アノテーションZIPまたはディレクトリ'{annotation_path}'を読み込みます。")
|
|
101
|
+
for index, parser in enumerate(iter_parser):
|
|
102
|
+
if (index + 1) % 10000 == 0:
|
|
103
|
+
logger.info(f"{index + 1} 件目のJSONを読み込み中")
|
|
104
|
+
if target_task_ids is not None and parser.task_id not in target_task_ids:
|
|
105
|
+
continue
|
|
106
|
+
dict_simple_annotation = parser.load_json()
|
|
107
|
+
if task_query is not None and not match_annotation_with_task_query(dict_simple_annotation, task_query):
|
|
108
|
+
continue
|
|
109
|
+
sub_range_annotation_list = get_range_annotation_info_list(dict_simple_annotation, target_label_names=target_label_names)
|
|
110
|
+
range_annotation_list.extend(sub_range_annotation_list)
|
|
111
|
+
return range_annotation_list
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def create_df(
|
|
115
|
+
range_annotation_list: list[RangeAnnotationInfo],
|
|
116
|
+
) -> pandas.DataFrame:
|
|
117
|
+
columns = [
|
|
118
|
+
"project_id",
|
|
119
|
+
"task_id",
|
|
120
|
+
"task_status",
|
|
121
|
+
"task_phase",
|
|
122
|
+
"task_phase_stage",
|
|
123
|
+
"input_data_id",
|
|
124
|
+
"input_data_name",
|
|
125
|
+
"updated_datetime",
|
|
126
|
+
"label",
|
|
127
|
+
"annotation_id",
|
|
128
|
+
"begin_second",
|
|
129
|
+
"end_second",
|
|
130
|
+
"duration_second",
|
|
131
|
+
]
|
|
132
|
+
df = pandas.DataFrame([e.to_dict(encode_json=True) for e in range_annotation_list], columns=columns)
|
|
133
|
+
|
|
134
|
+
return df[columns]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def print_range_annotation(
|
|
138
|
+
annotation_path: Path,
|
|
139
|
+
output_file: Path,
|
|
140
|
+
output_format: FormatArgument,
|
|
141
|
+
*,
|
|
142
|
+
target_task_ids: Optional[Collection[str]] = None,
|
|
143
|
+
task_query: Optional[TaskQuery] = None,
|
|
144
|
+
target_label_names: Optional[Collection[str]] = None,
|
|
145
|
+
) -> None:
|
|
146
|
+
range_annotation_list = get_range_annotation_info_list_from_annotation_path(
|
|
147
|
+
annotation_path,
|
|
148
|
+
target_task_ids=target_task_ids,
|
|
149
|
+
task_query=task_query,
|
|
150
|
+
target_label_names=target_label_names,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
logger.info(f"{len(range_annotation_list)} 件の区間アノテーションの情報を出力します。 :: output='{output_file}'")
|
|
154
|
+
|
|
155
|
+
if output_format == FormatArgument.CSV:
|
|
156
|
+
df = create_df(range_annotation_list)
|
|
157
|
+
print_csv(df, output_file)
|
|
158
|
+
|
|
159
|
+
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
160
|
+
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
161
|
+
# DataClassJsonMixinを使用したtoJSON処理
|
|
162
|
+
print_json(
|
|
163
|
+
[e.to_dict(encode_json=True) for e in range_annotation_list],
|
|
164
|
+
is_pretty=json_is_pretty,
|
|
165
|
+
output=output_file,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
else:
|
|
169
|
+
raise ValueError(f"出力形式 '{output_format}' はサポートされていません。")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ListRangeAnnotation(CommandLine):
|
|
173
|
+
COMMON_MESSAGE = "annofabcli annotation_zip list_range_annotation: error:"
|
|
174
|
+
|
|
175
|
+
def validate(self, args: argparse.Namespace) -> bool:
|
|
176
|
+
if args.project_id is None and args.annotation is None:
|
|
177
|
+
print( # noqa: T201
|
|
178
|
+
f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
|
|
179
|
+
file=sys.stderr,
|
|
180
|
+
)
|
|
181
|
+
return False
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
def main(self) -> None:
|
|
185
|
+
args = self.args
|
|
186
|
+
|
|
187
|
+
if not self.validate(args):
|
|
188
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
189
|
+
|
|
190
|
+
project_id: Optional[str] = args.project_id
|
|
191
|
+
if project_id is not None:
|
|
192
|
+
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
193
|
+
project, _ = self.service.api.get_project(project_id)
|
|
194
|
+
if project["input_data_type"] != InputDataType.MOVIE.value:
|
|
195
|
+
print(f"project_id='{project_id}'であるプロジェクトは動画プロジェクトでないので、終了します", file=sys.stderr) # noqa: T201
|
|
196
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
197
|
+
|
|
198
|
+
annotation_path = Path(args.annotation) if args.annotation is not None else None
|
|
199
|
+
|
|
200
|
+
task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
|
|
201
|
+
task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
|
|
202
|
+
label_name_list = get_list_from_args(args.label_name) if args.label_name is not None else None
|
|
203
|
+
|
|
204
|
+
output_file: Path = args.output
|
|
205
|
+
output_format = FormatArgument(args.format)
|
|
206
|
+
|
|
207
|
+
downloading_obj = DownloadingFile(self.service)
|
|
208
|
+
|
|
209
|
+
def download_and_print_range_annotation(project_id: str, temp_dir: Path, *, is_latest: bool) -> None:
|
|
210
|
+
annotation_path = downloading_obj.download_annotation_zip_to_dir(
|
|
211
|
+
project_id,
|
|
212
|
+
temp_dir,
|
|
213
|
+
is_latest=is_latest,
|
|
214
|
+
)
|
|
215
|
+
print_range_annotation(
|
|
216
|
+
annotation_path,
|
|
217
|
+
output_file,
|
|
218
|
+
output_format,
|
|
219
|
+
target_task_ids=task_id_list,
|
|
220
|
+
task_query=task_query,
|
|
221
|
+
target_label_names=label_name_list,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if project_id is not None:
|
|
225
|
+
if args.temp_dir is not None:
|
|
226
|
+
download_and_print_range_annotation(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest)
|
|
227
|
+
else:
|
|
228
|
+
with tempfile.TemporaryDirectory() as str_temp_dir:
|
|
229
|
+
download_and_print_range_annotation(
|
|
230
|
+
project_id=project_id,
|
|
231
|
+
temp_dir=Path(str_temp_dir),
|
|
232
|
+
is_latest=args.latest,
|
|
233
|
+
)
|
|
234
|
+
else:
|
|
235
|
+
assert annotation_path is not None
|
|
236
|
+
print_range_annotation(
|
|
237
|
+
annotation_path,
|
|
238
|
+
output_file,
|
|
239
|
+
output_format,
|
|
240
|
+
target_task_ids=task_id_list,
|
|
241
|
+
task_query=task_query,
|
|
242
|
+
target_label_names=label_name_list,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
247
|
+
argument_parser = ArgumentParser(parser)
|
|
248
|
+
|
|
249
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
250
|
+
group.add_argument(
|
|
251
|
+
"--annotation",
|
|
252
|
+
type=str,
|
|
253
|
+
help="アノテーションzip、またはzipを展開したディレクトリを指定します。",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
group.add_argument("-p", "--project_id", type=str, help="project_id。アノテーションZIPをダウンロードします。")
|
|
257
|
+
|
|
258
|
+
argument_parser.add_format(
|
|
259
|
+
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
|
260
|
+
default=FormatArgument.CSV,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
argument_parser.add_output()
|
|
264
|
+
|
|
265
|
+
parser.add_argument(
|
|
266
|
+
"-tq",
|
|
267
|
+
"--task_query",
|
|
268
|
+
type=str,
|
|
269
|
+
help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
|
|
270
|
+
" ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
271
|
+
)
|
|
272
|
+
argument_parser.add_task_id(required=False)
|
|
273
|
+
|
|
274
|
+
parser.add_argument(
|
|
275
|
+
"--label_name",
|
|
276
|
+
type=str,
|
|
277
|
+
nargs="*",
|
|
278
|
+
help="指定したラベル名の区間アノテーションのみを対象にします。複数指定できます。",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
parser.add_argument(
|
|
282
|
+
"--latest",
|
|
283
|
+
action="store_true",
|
|
284
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
parser.add_argument(
|
|
288
|
+
"--temp_dir",
|
|
289
|
+
type=Path,
|
|
290
|
+
help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
parser.set_defaults(subcommand_func=main)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def main(args: argparse.Namespace) -> None:
|
|
297
|
+
service = build_annofabapi_resource_and_login(args)
|
|
298
|
+
facade = AnnofabApiFacade(service)
|
|
299
|
+
ListRangeAnnotation(service, facade, args).main()
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
303
|
+
subcommand_name = "list_range_annotation"
|
|
304
|
+
subcommand_help = "アノテーションZIPから動画プロジェクトの区間アノテーションの情報を出力します。"
|
|
305
|
+
epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
306
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
307
|
+
parse_args(parser)
|
|
308
|
+
return parser
|