annofabcli 1.106.8__py3-none-any.whl → 1.107.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 +2 -0
- annofabcli/annotation/annotation_query.py +2 -2
- annofabcli/annotation/download_annotation_zip.py +2 -2
- annofabcli/annotation/import_annotation.py +37 -15
- annofabcli/annotation/restore_annotation.py +1 -1
- annofabcli/annotation_zip/__init__.py +1 -0
- annofabcli/annotation_zip/list_annotation_bounding_box_2d.py +324 -0
- annofabcli/annotation_zip/subcommand_annotation_zip.py +35 -0
- annofabcli/comment/delete_comment.py +2 -2
- annofabcli/comment/put_inspection_comment.py +1 -1
- annofabcli/comment/put_inspection_comment_simply.py +1 -1
- annofabcli/comment/put_onhold_comment.py +1 -1
- annofabcli/comment/put_onhold_comment_simply.py +1 -1
- annofabcli/common/annofab/annotation_zip.py +37 -0
- annofabcli/input_data/put_input_data.py +11 -8
- annofabcli/statistics/list_annotation_area.py +3 -8
- annofabcli/task/copy_tasks.py +1 -1
- {annofabcli-1.106.8.dist-info → annofabcli-1.107.0.dist-info}/METADATA +1 -2
- {annofabcli-1.106.8.dist-info → annofabcli-1.107.0.dist-info}/RECORD +22 -18
- {annofabcli-1.106.8.dist-info → annofabcli-1.107.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.106.8.dist-info → annofabcli-1.107.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.106.8.dist-info → annofabcli-1.107.0.dist-info}/licenses/LICENSE +0 -0
annofabcli/__main__.py
CHANGED
|
@@ -10,6 +10,7 @@ import pandas
|
|
|
10
10
|
|
|
11
11
|
import annofabcli.annotation.subcommand_annotation
|
|
12
12
|
import annofabcli.annotation_specs.subcommand_annotation_specs
|
|
13
|
+
import annofabcli.annotation_zip.subcommand_annotation_zip
|
|
13
14
|
import annofabcli.comment.subcommand_comment
|
|
14
15
|
import annofabcli.common.cli
|
|
15
16
|
import annofabcli.experimental.subcommand_experimental
|
|
@@ -107,6 +108,7 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
107
108
|
|
|
108
109
|
annofabcli.annotation.subcommand_annotation.add_parser(subparsers)
|
|
109
110
|
annofabcli.annotation_specs.subcommand_annotation_specs.add_parser(subparsers)
|
|
111
|
+
annofabcli.annotation_zip.subcommand_annotation_zip.add_parser(subparsers)
|
|
110
112
|
annofabcli.comment.subcommand_comment.add_parser(subparsers)
|
|
111
113
|
annofabcli.input_data.subcommand_input_data.add_parser(subparsers)
|
|
112
114
|
annofabcli.instruction.subcommand_instruction.add_parser(subparsers)
|
|
@@ -270,7 +270,7 @@ class AnnotationQueryForCLI(DataClassJsonMixin):
|
|
|
270
270
|
|
|
271
271
|
def to_query_for_api(self, annotation_specs: dict[str, Any]) -> AnnotationQueryForAPI:
|
|
272
272
|
"""
|
|
273
|
-
WebAPIのquery_params( https://annofab.com/docs/api/#
|
|
273
|
+
WebAPIのquery_params( https://annofab.com/docs/api/#tag/x-data-types/AnnotationQuery )に渡すdictに変換する。
|
|
274
274
|
|
|
275
275
|
Args:
|
|
276
276
|
annotation_specs: アノテーション仕様(V2,V3版)
|
|
@@ -306,7 +306,7 @@ class AnnotationQueryForCLI(DataClassJsonMixin):
|
|
|
306
306
|
class AnnotationQueryForAPI(DataClassJsonMixin):
|
|
307
307
|
"""
|
|
308
308
|
WebAPIでアノテーションを絞り込むためのクエリ。
|
|
309
|
-
https://annofab.com/docs/api/#
|
|
309
|
+
https://annofab.com/docs/api/#tag/x-data-types/AnnotationQuery に対応しています。
|
|
310
310
|
"""
|
|
311
311
|
|
|
312
312
|
label_id: Optional[str] = None
|
|
@@ -19,7 +19,7 @@ class DownloadingAnnotationZip(CommandLine):
|
|
|
19
19
|
super().validate_project(project_id, [ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
20
20
|
|
|
21
21
|
project_title = self.facade.get_project_title(project_id)
|
|
22
|
-
logger.info(f"{project_title}
|
|
22
|
+
logger.info(f"プロジェクト'{project_title}'のアノテーションZIPをダウンロードします。")
|
|
23
23
|
|
|
24
24
|
obj = DownloadingFile(self.service)
|
|
25
25
|
obj.download_annotation_zip(
|
|
@@ -28,7 +28,7 @@ class DownloadingAnnotationZip(CommandLine):
|
|
|
28
28
|
is_latest=is_latest,
|
|
29
29
|
should_download_full_annotation=should_download_full_annotation,
|
|
30
30
|
)
|
|
31
|
-
logger.info(f"アノテーションZIP
|
|
31
|
+
logger.info(f"アノテーションZIPをダウンロードして、'{output_zip}'に保存しました。")
|
|
32
32
|
|
|
33
33
|
def main(self) -> None:
|
|
34
34
|
args = self.args
|
|
@@ -419,19 +419,25 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
419
419
|
self.is_overwrite = is_overwrite
|
|
420
420
|
self.converter = converter
|
|
421
421
|
|
|
422
|
-
def put_annotation_for_input_data(self, parser: SimpleAnnotationParser) ->
|
|
422
|
+
def put_annotation_for_input_data(self, parser: SimpleAnnotationParser) -> int:
|
|
423
|
+
"""
|
|
424
|
+
1個の入力データに対してアノテーションを登録します。
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
登録したアノテーションの個数
|
|
428
|
+
"""
|
|
423
429
|
task_id = parser.task_id
|
|
424
430
|
input_data_id = parser.input_data_id
|
|
425
431
|
|
|
426
432
|
simple_annotation: ImportedSimpleAnnotation = ImportedSimpleAnnotation.from_dict(parser.load_json())
|
|
427
433
|
if len(simple_annotation.details) == 0:
|
|
428
434
|
logger.debug(f"task_id='{task_id}', input_data_id='{input_data_id}' :: インポート元にアノテーションデータがないため、アノテーションの登録をスキップします。")
|
|
429
|
-
return
|
|
435
|
+
return 0
|
|
430
436
|
|
|
431
437
|
input_data = self.service.wrapper.get_input_data_or_none(self.project_id, input_data_id)
|
|
432
438
|
if input_data is None:
|
|
433
439
|
logger.warning(f"input_data_id='{input_data_id}'という入力データは存在しません。 :: task_id='{task_id}'")
|
|
434
|
-
return
|
|
440
|
+
return 0
|
|
435
441
|
|
|
436
442
|
old_annotation, _ = self.service.api.get_editor_annotation(self.project_id, task_id, input_data_id, query_params={"v": "2"})
|
|
437
443
|
if len(old_annotation["details"]) > 0: # noqa: SIM102
|
|
@@ -441,30 +447,44 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
441
447
|
f"インポート先のタスク内の入力データに既にアノテーションが存在するため、アノテーションの登録をスキップします。"
|
|
442
448
|
f"アノテーションをインポートする場合は、`--overwrite` または '--merge' を指定してください。"
|
|
443
449
|
)
|
|
444
|
-
return
|
|
450
|
+
return 0
|
|
445
451
|
|
|
446
|
-
logger.info(f"task_id='{task_id}', input_data_id='{input_data_id}' :: {len(simple_annotation.details)} 件のアノテーションを登録します。")
|
|
447
452
|
if self.is_merge:
|
|
448
453
|
request_body = self.converter.convert_annotation_details(parser, simple_annotation.details, old_details=old_annotation["details"], updated_datetime=old_annotation["updated_datetime"])
|
|
449
454
|
else:
|
|
450
455
|
request_body = self.converter.convert_annotation_details(parser, simple_annotation.details, old_details=[], updated_datetime=old_annotation["updated_datetime"])
|
|
451
456
|
|
|
457
|
+
if len(request_body["details"]) == 0:
|
|
458
|
+
logger.warning(f"task_id='{task_id}', input_data_id='{input_data_id}' :: 登録できるアノテーション数が0/{len(simple_annotation.details)}件なので、アノテーションの登録をスキップします。")
|
|
459
|
+
return 0
|
|
452
460
|
self.service.api.put_annotation(self.project_id, task_id, input_data_id, request_body=request_body, query_params={"v": "2"})
|
|
453
|
-
|
|
461
|
+
success_annotation_count = len(request_body["details"])
|
|
462
|
+
logger.debug(f"task_id='{task_id}', input_data_id='{input_data_id}' :: {success_annotation_count}/{len(simple_annotation.details)} 件のアノテーションを登録しました。")
|
|
463
|
+
return success_annotation_count
|
|
454
464
|
|
|
455
|
-
def put_annotation_for_task(self, task_parser: SimpleAnnotationParserByTask) -> int:
|
|
456
|
-
|
|
465
|
+
def put_annotation_for_task(self, task_parser: SimpleAnnotationParserByTask) -> tuple[int, int]:
|
|
466
|
+
"""
|
|
467
|
+
1個のタスクに対して、アノテーションを登録します。
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
tuple[0]: アノテーションを登録した入力データの個数
|
|
471
|
+
tuple[1]: 登録したアノテーションの個数
|
|
472
|
+
"""
|
|
473
|
+
success_input_data_count = 0
|
|
474
|
+
success_annotation_count = 0
|
|
457
475
|
for parser in task_parser.lazy_parse():
|
|
458
476
|
try:
|
|
459
|
-
|
|
460
|
-
|
|
477
|
+
tmp_success_annotation_count = self.put_annotation_for_input_data(parser)
|
|
478
|
+
if tmp_success_annotation_count > 0:
|
|
479
|
+
success_input_data_count += 1
|
|
480
|
+
success_annotation_count += tmp_success_annotation_count
|
|
461
481
|
except Exception: # pylint: disable=broad-except
|
|
462
482
|
logger.warning(
|
|
463
483
|
f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}' のアノテーションのインポートに失敗しました。",
|
|
464
484
|
exc_info=True,
|
|
465
485
|
)
|
|
466
486
|
|
|
467
|
-
return
|
|
487
|
+
return success_input_data_count, success_annotation_count
|
|
468
488
|
|
|
469
489
|
def execute_task(self, task_parser: SimpleAnnotationParserByTask, task_index: Optional[int] = None) -> bool:
|
|
470
490
|
"""
|
|
@@ -515,8 +535,10 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
515
535
|
)
|
|
516
536
|
return False
|
|
517
537
|
|
|
518
|
-
|
|
519
|
-
logger.info(
|
|
538
|
+
success_input_data_count, success_annotation_count = self.put_annotation_for_task(task_parser)
|
|
539
|
+
logger.info(
|
|
540
|
+
f"{logger_prefix}task_id='{task_parser.task_id}'のタスクに含まれる入力データ {success_input_data_count} 個に対して、アノテーション{success_annotation_count}個をインポートしました。"
|
|
541
|
+
)
|
|
520
542
|
|
|
521
543
|
if changed_operator:
|
|
522
544
|
logger.debug(f"タスク'{task_id}' の担当者を元に戻します。")
|
|
@@ -527,7 +549,7 @@ class ImportAnnotationMain(CommandLineWithConfirm):
|
|
|
527
549
|
last_updated_datetime=task["updated_datetime"],
|
|
528
550
|
)
|
|
529
551
|
|
|
530
|
-
return
|
|
552
|
+
return success_input_data_count > 0
|
|
531
553
|
|
|
532
554
|
def execute_task_wrapper(
|
|
533
555
|
self,
|
|
@@ -617,7 +639,7 @@ class ImportAnnotation(CommandLine):
|
|
|
617
639
|
|
|
618
640
|
if args.parallelism is not None and not args.yes:
|
|
619
641
|
print( # noqa: T201
|
|
620
|
-
f"{COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
642
|
+
f"{COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
621
643
|
file=sys.stderr,
|
|
622
644
|
)
|
|
623
645
|
return False
|
|
@@ -305,7 +305,7 @@ class RestoreAnnotation(CommandLine):
|
|
|
305
305
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
306
306
|
if args.parallelism is not None and not args.yes:
|
|
307
307
|
print( # noqa: T201
|
|
308
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
308
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
309
309
|
file=sys.stderr,
|
|
310
310
|
)
|
|
311
311
|
return False
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""アノテーションZIPに関するサブパッケージ"""
|
|
@@ -0,0 +1,324 @@
|
|
|
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 (
|
|
20
|
+
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
21
|
+
ArgumentParser,
|
|
22
|
+
CommandLine,
|
|
23
|
+
build_annofabapi_resource_and_login,
|
|
24
|
+
)
|
|
25
|
+
from annofabcli.common.download import DownloadingFile
|
|
26
|
+
from annofabcli.common.enums import FormatArgument
|
|
27
|
+
from annofabcli.common.facade import (
|
|
28
|
+
AnnofabApiFacade,
|
|
29
|
+
TaskQuery,
|
|
30
|
+
match_annotation_with_task_query,
|
|
31
|
+
)
|
|
32
|
+
from annofabcli.common.utils import print_csv, print_json
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class AnnotationBoundingBoxInfo(DataClassJsonMixin):
|
|
39
|
+
project_id: str
|
|
40
|
+
task_id: str
|
|
41
|
+
task_status: str
|
|
42
|
+
task_phase: str
|
|
43
|
+
task_phase_stage: int
|
|
44
|
+
|
|
45
|
+
input_data_id: str
|
|
46
|
+
input_data_name: str
|
|
47
|
+
|
|
48
|
+
updated_datetime: Optional[str]
|
|
49
|
+
"""アノテーションJSONに格納されているアノテーションの更新日時"""
|
|
50
|
+
|
|
51
|
+
label: str
|
|
52
|
+
annotation_id: str
|
|
53
|
+
left_top: dict[str, int]
|
|
54
|
+
right_bottom: dict[str, int]
|
|
55
|
+
width: int
|
|
56
|
+
height: int
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any]) -> list[AnnotationBoundingBoxInfo]:
|
|
60
|
+
result = []
|
|
61
|
+
for detail in simple_annotation["details"]:
|
|
62
|
+
if detail["data"]["_type"] == "BoundingBox":
|
|
63
|
+
left_top = detail["data"]["left_top"]
|
|
64
|
+
right_bottom = detail["data"]["right_bottom"]
|
|
65
|
+
width = abs(right_bottom["x"] - left_top["x"])
|
|
66
|
+
height = abs(right_bottom["y"] - left_top["y"])
|
|
67
|
+
|
|
68
|
+
result.append(
|
|
69
|
+
AnnotationBoundingBoxInfo(
|
|
70
|
+
project_id=simple_annotation["project_id"],
|
|
71
|
+
task_id=simple_annotation["task_id"],
|
|
72
|
+
task_phase=simple_annotation["task_phase"],
|
|
73
|
+
task_phase_stage=simple_annotation["task_phase_stage"],
|
|
74
|
+
task_status=simple_annotation["task_status"],
|
|
75
|
+
input_data_id=simple_annotation["input_data_id"],
|
|
76
|
+
input_data_name=simple_annotation["input_data_name"],
|
|
77
|
+
label=detail["label"],
|
|
78
|
+
annotation_id=detail["annotation_id"],
|
|
79
|
+
left_top=left_top,
|
|
80
|
+
right_bottom=right_bottom,
|
|
81
|
+
width=width,
|
|
82
|
+
height=height,
|
|
83
|
+
updated_datetime=simple_annotation["updated_datetime"],
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_annotation_bounding_box_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
|
+
) -> list[AnnotationBoundingBoxInfo]:
|
|
96
|
+
annotation_bbox_list = []
|
|
97
|
+
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
98
|
+
iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_path)
|
|
99
|
+
logger.info(f"アノテーションZIPまたはディレクトリ'{annotation_path}'を読み込みます。")
|
|
100
|
+
for index, parser in enumerate(iter_parser):
|
|
101
|
+
if (index + 1) % 10000 == 0:
|
|
102
|
+
logger.info(f"{index + 1} 件目のJSONを読み込み中")
|
|
103
|
+
if target_task_ids is not None and parser.task_id not in target_task_ids:
|
|
104
|
+
continue
|
|
105
|
+
dict_simple_annotation = parser.load_json()
|
|
106
|
+
if task_query is not None and not match_annotation_with_task_query(dict_simple_annotation, task_query):
|
|
107
|
+
continue
|
|
108
|
+
sub_annotation_bbox_list = get_annotation_bounding_box_info_list(dict_simple_annotation)
|
|
109
|
+
annotation_bbox_list.extend(sub_annotation_bbox_list)
|
|
110
|
+
return annotation_bbox_list
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create_df(
|
|
114
|
+
annotation_bbox_list: list[AnnotationBoundingBoxInfo],
|
|
115
|
+
) -> pandas.DataFrame:
|
|
116
|
+
columns = [
|
|
117
|
+
"project_id",
|
|
118
|
+
"task_id",
|
|
119
|
+
"task_status",
|
|
120
|
+
"task_phase",
|
|
121
|
+
"task_phase_stage",
|
|
122
|
+
"input_data_id",
|
|
123
|
+
"input_data_name",
|
|
124
|
+
"updated_datetime",
|
|
125
|
+
"label",
|
|
126
|
+
"annotation_id",
|
|
127
|
+
"left_top.x",
|
|
128
|
+
"left_top.y",
|
|
129
|
+
"right_bottom.x",
|
|
130
|
+
"right_bottom.y",
|
|
131
|
+
"width",
|
|
132
|
+
"height",
|
|
133
|
+
]
|
|
134
|
+
if len(annotation_bbox_list) > 0:
|
|
135
|
+
df = pandas.DataFrame(
|
|
136
|
+
[
|
|
137
|
+
{
|
|
138
|
+
"project_id": e.project_id,
|
|
139
|
+
"task_id": e.task_id,
|
|
140
|
+
"task_status": e.task_status,
|
|
141
|
+
"task_phase": e.task_phase,
|
|
142
|
+
"task_phase_stage": e.task_phase_stage,
|
|
143
|
+
"input_data_id": e.input_data_id,
|
|
144
|
+
"input_data_name": e.input_data_name,
|
|
145
|
+
"updated_datetime": e.updated_datetime,
|
|
146
|
+
"label": e.label,
|
|
147
|
+
"annotation_id": e.annotation_id,
|
|
148
|
+
"left_top.x": e.left_top["x"],
|
|
149
|
+
"left_top.y": e.left_top["y"],
|
|
150
|
+
"right_bottom.x": e.right_bottom["x"],
|
|
151
|
+
"right_bottom.y": e.right_bottom["y"],
|
|
152
|
+
"width": e.width,
|
|
153
|
+
"height": e.height,
|
|
154
|
+
}
|
|
155
|
+
for e in annotation_bbox_list
|
|
156
|
+
]
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
df = pandas.DataFrame(columns=columns)
|
|
160
|
+
|
|
161
|
+
return df[columns]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def print_annotation_bounding_box(
|
|
165
|
+
annotation_path: Path,
|
|
166
|
+
output_file: Path,
|
|
167
|
+
output_format: FormatArgument,
|
|
168
|
+
*,
|
|
169
|
+
target_task_ids: Optional[Collection[str]] = None,
|
|
170
|
+
task_query: Optional[TaskQuery] = None,
|
|
171
|
+
) -> None:
|
|
172
|
+
annotation_bbox_list = get_annotation_bounding_box_info_list_from_annotation_path(
|
|
173
|
+
annotation_path,
|
|
174
|
+
target_task_ids=target_task_ids,
|
|
175
|
+
task_query=task_query,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
logger.info(f"{len(annotation_bbox_list)} 件のバウンディングボックスアノテーションの情報を出力します。 :: output='{output_file}'")
|
|
179
|
+
|
|
180
|
+
if output_format == FormatArgument.CSV:
|
|
181
|
+
df = create_df(annotation_bbox_list)
|
|
182
|
+
print_csv(df, output_file)
|
|
183
|
+
|
|
184
|
+
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
185
|
+
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
186
|
+
# DataClassJsonMixinを使用したtoJSON処理
|
|
187
|
+
print_json(
|
|
188
|
+
[e.to_dict(encode_json=True) for e in annotation_bbox_list],
|
|
189
|
+
is_pretty=json_is_pretty,
|
|
190
|
+
output=output_file,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
else:
|
|
194
|
+
raise ValueError(f"出力形式 '{output_format}' はサポートされていません。")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class ListAnnotationBoundingBox2d(CommandLine):
|
|
198
|
+
COMMON_MESSAGE = "annofabcli annotation_zip list_annotation_bounding_box_2d: error:"
|
|
199
|
+
|
|
200
|
+
def validate(self, args: argparse.Namespace) -> bool:
|
|
201
|
+
if args.project_id is None and args.annotation is None:
|
|
202
|
+
print( # noqa: T201
|
|
203
|
+
f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
|
|
204
|
+
file=sys.stderr,
|
|
205
|
+
)
|
|
206
|
+
return False
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
def main(self) -> None:
|
|
210
|
+
args = self.args
|
|
211
|
+
|
|
212
|
+
if not self.validate(args):
|
|
213
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
214
|
+
|
|
215
|
+
project_id: Optional[str] = args.project_id
|
|
216
|
+
if project_id is not None:
|
|
217
|
+
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
218
|
+
project, _ = self.service.api.get_project(project_id)
|
|
219
|
+
if project["input_data_type"] != InputDataType.IMAGE.value:
|
|
220
|
+
print(f"project_id='{project_id}'であるプロジェクトは画像プロジェクトでないので、終了します", file=sys.stderr) # noqa: T201
|
|
221
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
222
|
+
|
|
223
|
+
annotation_path = Path(args.annotation) if args.annotation is not None else None
|
|
224
|
+
|
|
225
|
+
task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
|
|
226
|
+
task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
|
|
227
|
+
|
|
228
|
+
output_file: Path = args.output
|
|
229
|
+
output_format = FormatArgument(args.format)
|
|
230
|
+
|
|
231
|
+
downloading_obj = DownloadingFile(self.service)
|
|
232
|
+
|
|
233
|
+
def download_and_print_annotation_bbox(project_id: str, temp_dir: Path, *, is_latest: bool) -> None:
|
|
234
|
+
annotation_path = temp_dir / f"{project_id}__annotation.zip"
|
|
235
|
+
downloading_obj.download_annotation_zip(
|
|
236
|
+
project_id,
|
|
237
|
+
dest_path=annotation_path,
|
|
238
|
+
is_latest=is_latest,
|
|
239
|
+
)
|
|
240
|
+
print_annotation_bounding_box(
|
|
241
|
+
annotation_path,
|
|
242
|
+
output_file,
|
|
243
|
+
output_format,
|
|
244
|
+
target_task_ids=task_id_list,
|
|
245
|
+
task_query=task_query,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if project_id is not None:
|
|
249
|
+
if args.temp_dir is not None:
|
|
250
|
+
download_and_print_annotation_bbox(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest)
|
|
251
|
+
else:
|
|
252
|
+
with tempfile.TemporaryDirectory() as str_temp_dir:
|
|
253
|
+
download_and_print_annotation_bbox(
|
|
254
|
+
project_id=project_id,
|
|
255
|
+
temp_dir=Path(str_temp_dir),
|
|
256
|
+
is_latest=args.latest,
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
assert annotation_path is not None
|
|
260
|
+
print_annotation_bounding_box(
|
|
261
|
+
annotation_path,
|
|
262
|
+
output_file,
|
|
263
|
+
output_format,
|
|
264
|
+
target_task_ids=task_id_list,
|
|
265
|
+
task_query=task_query,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
270
|
+
argument_parser = ArgumentParser(parser)
|
|
271
|
+
|
|
272
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
273
|
+
group.add_argument(
|
|
274
|
+
"--annotation",
|
|
275
|
+
type=str,
|
|
276
|
+
help="アノテーションzip、またはzipを展開したディレクトリを指定します。",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
group.add_argument("-p", "--project_id", type=str, help="project_id。アノテーションZIPをダウンロードします。")
|
|
280
|
+
|
|
281
|
+
argument_parser.add_format(
|
|
282
|
+
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
|
283
|
+
default=FormatArgument.CSV,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
argument_parser.add_output()
|
|
287
|
+
|
|
288
|
+
parser.add_argument(
|
|
289
|
+
"-tq",
|
|
290
|
+
"--task_query",
|
|
291
|
+
type=str,
|
|
292
|
+
help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
|
|
293
|
+
" ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
294
|
+
)
|
|
295
|
+
argument_parser.add_task_id(required=False)
|
|
296
|
+
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"--latest",
|
|
299
|
+
action="store_true",
|
|
300
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
parser.add_argument(
|
|
304
|
+
"--temp_dir",
|
|
305
|
+
type=Path,
|
|
306
|
+
help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
parser.set_defaults(subcommand_func=main)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def main(args: argparse.Namespace) -> None:
|
|
313
|
+
service = build_annofabapi_resource_and_login(args)
|
|
314
|
+
facade = AnnofabApiFacade(service)
|
|
315
|
+
ListAnnotationBoundingBox2d(service, facade, args).main()
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
319
|
+
subcommand_name = "list_annotation_bounding_box_2d"
|
|
320
|
+
subcommand_help = "アノテーションZIPからバウンディングボックス(矩形)アノテーションの座標情報を出力します。"
|
|
321
|
+
epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
322
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
323
|
+
parse_args(parser)
|
|
324
|
+
return parser
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""サブコマンド annotation_zip"""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import annofabcli
|
|
7
|
+
from annofabcli.annotation_zip.list_annotation_bounding_box_2d import add_parser as add_parser_list_annotation_bounding_box_2d
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
11
|
+
"""
|
|
12
|
+
サブコマンドの引数を定義する
|
|
13
|
+
Args:
|
|
14
|
+
parser: パーサー
|
|
15
|
+
"""
|
|
16
|
+
subparsers = parser.add_subparsers(dest="subcommand_name")
|
|
17
|
+
|
|
18
|
+
# サブコマンドの定義
|
|
19
|
+
add_parser_list_annotation_bounding_box_2d(subparsers)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
23
|
+
"""
|
|
24
|
+
annotation_zipサブコマンドを追加する。
|
|
25
|
+
Args:
|
|
26
|
+
subparsers: 親パーサーのサブパーサー
|
|
27
|
+
Returns:
|
|
28
|
+
annotation_zipサブコマンドのパーサー
|
|
29
|
+
"""
|
|
30
|
+
subcommand_name = "annotation_zip"
|
|
31
|
+
subcommand_help = "アノテーションZIPに対する操作を行うサブコマンド"
|
|
32
|
+
|
|
33
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, is_subcommand=False)
|
|
34
|
+
parse_args(parser)
|
|
35
|
+
return parser
|
|
@@ -206,7 +206,7 @@ class DeleteComment(CommandLine):
|
|
|
206
206
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
207
207
|
if args.parallelism is not None and not args.yes:
|
|
208
208
|
print( # noqa: T201
|
|
209
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
209
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
210
210
|
file=sys.stderr,
|
|
211
211
|
)
|
|
212
212
|
return False
|
|
@@ -254,7 +254,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
254
254
|
"--parallelism",
|
|
255
255
|
type=int,
|
|
256
256
|
choices=PARALLELISM_CHOICES,
|
|
257
|
-
help="
|
|
257
|
+
help="使用するプロセス数(並列度)を指定してください。指定する場合は'--yes' を指定してください。指定しない場合は、逐次的に処理します。",
|
|
258
258
|
)
|
|
259
259
|
|
|
260
260
|
parser.set_defaults(subcommand_func=main)
|
|
@@ -29,7 +29,7 @@ class PutInspectionComment(CommandLine):
|
|
|
29
29
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
30
30
|
if args.parallelism is not None and not args.yes:
|
|
31
31
|
print( # noqa: T201
|
|
32
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
32
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
33
33
|
file=sys.stderr,
|
|
34
34
|
)
|
|
35
35
|
return False
|
|
@@ -29,7 +29,7 @@ class PutInspectionCommentSimply(CommandLine):
|
|
|
29
29
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
30
30
|
if args.parallelism is not None and not args.yes:
|
|
31
31
|
print( # noqa: T201
|
|
32
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
32
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
33
33
|
file=sys.stderr,
|
|
34
34
|
)
|
|
35
35
|
return False
|
|
@@ -27,7 +27,7 @@ class PutInspectionComment(CommandLine):
|
|
|
27
27
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
28
28
|
if args.parallelism is not None and not args.yes:
|
|
29
29
|
print( # noqa: T201
|
|
30
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
30
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
31
31
|
file=sys.stderr,
|
|
32
32
|
)
|
|
33
33
|
return False
|
|
@@ -27,7 +27,7 @@ class PutOnholdCommentSimply(CommandLine):
|
|
|
27
27
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
28
28
|
if args.parallelism is not None and not args.yes:
|
|
29
29
|
print( # noqa: T201
|
|
30
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
30
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
31
31
|
file=sys.stderr,
|
|
32
32
|
)
|
|
33
33
|
return False
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AnnofabのアノテーションZIPまたはそれを展開したディレクトリに関するモジュール
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import zipfile
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from annofabapi.parser import (
|
|
10
|
+
SimpleAnnotationParser,
|
|
11
|
+
lazy_parse_simple_annotation_dir,
|
|
12
|
+
lazy_parse_simple_annotation_zip,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def lazy_parse_simple_annotation_by_input_data(annotation_path: Path) -> Iterator[SimpleAnnotationParser]:
|
|
17
|
+
"""
|
|
18
|
+
アノテーションZIPを入力データごとに読み込むparserのイテレータを返します。
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
アノテーションZIPまたは、それを展開したディレクトリのパス
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
SimpleAnnotationParserのイテレータ
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: 指定されたパスが存在しない、またはzipファイルやディレクトリではない場合
|
|
28
|
+
"""
|
|
29
|
+
if not annotation_path.exists():
|
|
30
|
+
raise ValueError(f"'{annotation_path}' は存在しません。")
|
|
31
|
+
|
|
32
|
+
if annotation_path.is_dir():
|
|
33
|
+
return lazy_parse_simple_annotation_dir(annotation_path)
|
|
34
|
+
elif zipfile.is_zipfile(str(annotation_path)):
|
|
35
|
+
return lazy_parse_simple_annotation_zip(annotation_path)
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(f"'{annotation_path}'は、zipファイルまたはディレクトリではありません。")
|
|
@@ -196,16 +196,19 @@ class SubPutInputData:
|
|
|
196
196
|
|
|
197
197
|
if dict_input_data is not None:
|
|
198
198
|
if overwrite:
|
|
199
|
-
logger.debug(f"{log_message_prefix}input_data_id='{input_data.input_data_id}'
|
|
199
|
+
logger.debug(f"{log_message_prefix}input_data_id='{input_data.input_data_id}'の入力データはすでに存在します。")
|
|
200
200
|
last_updated_datetime = dict_input_data["updated_datetime"]
|
|
201
201
|
else:
|
|
202
|
-
logger.debug(
|
|
202
|
+
logger.debug(
|
|
203
|
+
f"{log_message_prefix}input_data_id='{input_data.input_data_id}'の入力データがすでに存在するので入力データの登録をスキップします。"
|
|
204
|
+
"入力データを上書きして登録する場合は、引数に '--overwrite' を指定してください。"
|
|
205
|
+
)
|
|
203
206
|
return False
|
|
204
207
|
|
|
205
208
|
file_path = get_file_scheme_path(input_data.input_data_path)
|
|
206
209
|
if file_path is not None: # noqa: SIM102
|
|
207
210
|
if not Path(file_path).exists():
|
|
208
|
-
logger.warning(f"{input_data.input_data_path}
|
|
211
|
+
logger.warning(f"input_data_path='{input_data.input_data_path}'にファイルは存在しません。入力データの登録をスキップします。")
|
|
209
212
|
return False
|
|
210
213
|
|
|
211
214
|
if not self.confirm_put_input_data(input_data, already_exists=last_updated_datetime is not None):
|
|
@@ -219,13 +222,13 @@ class SubPutInputData:
|
|
|
219
222
|
|
|
220
223
|
except requests.exceptions.HTTPError:
|
|
221
224
|
logger.warning(
|
|
222
|
-
f"{log_message_prefix}入力データの登録に失敗しました。input_data_id='{input_data.input_data_id}', input_data_name='{input_data.input_data_name}'",
|
|
225
|
+
f"{log_message_prefix}入力データの登録に失敗しました。 :: input_data_id='{input_data.input_data_id}', input_data_name='{input_data.input_data_name}'",
|
|
223
226
|
exc_info=True,
|
|
224
227
|
)
|
|
225
228
|
return False
|
|
226
229
|
except CheckSumError:
|
|
227
230
|
logger.warning(
|
|
228
|
-
f"{log_message_prefix}入力データを登録しましたが、データが破損している可能性があります。input_data_id='{input_data.input_data_id}', input_data_name='{input_data.input_data_name}'",
|
|
231
|
+
f"{log_message_prefix}入力データを登録しましたが、データが破損している可能性があります。 :: input_data_id='{input_data.input_data_id}', input_data_name='{input_data.input_data_name}'",
|
|
229
232
|
exc_info=True,
|
|
230
233
|
)
|
|
231
234
|
return False
|
|
@@ -257,7 +260,7 @@ class PutInputData(CommandLine):
|
|
|
257
260
|
"""
|
|
258
261
|
|
|
259
262
|
project_title = self.facade.get_project_title(project_id)
|
|
260
|
-
logger.info(f"{project_title}
|
|
263
|
+
logger.info(f"プロジェクト'{project_title}'に、{len(input_data_list)} 件の入力データを登録します。")
|
|
261
264
|
|
|
262
265
|
count_put_input_data = 0
|
|
263
266
|
|
|
@@ -274,7 +277,7 @@ class PutInputData(CommandLine):
|
|
|
274
277
|
if result:
|
|
275
278
|
count_put_input_data += 1
|
|
276
279
|
|
|
277
|
-
logger.info(f"{project_title}
|
|
280
|
+
logger.info(f"プロジェクト'{project_title}'に、{count_put_input_data} / {len(input_data_list)} 件の入力データを登録しました。")
|
|
278
281
|
|
|
279
282
|
@staticmethod
|
|
280
283
|
def get_input_data_list_from_df(df: pandas.DataFrame) -> list[CsvInputData]:
|
|
@@ -317,7 +320,7 @@ class PutInputData(CommandLine):
|
|
|
317
320
|
if args.csv is not None or args.json is not None: # noqa: SIM102
|
|
318
321
|
if args.parallelism is not None and not args.yes:
|
|
319
322
|
print( # noqa: T201
|
|
320
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
323
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
321
324
|
file=sys.stderr,
|
|
322
325
|
)
|
|
323
326
|
return False
|
|
@@ -264,19 +264,14 @@ class ListAnnotationArea(CommandLine):
|
|
|
264
264
|
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
265
265
|
argument_parser = ArgumentParser(parser)
|
|
266
266
|
|
|
267
|
-
parser.
|
|
267
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
268
|
+
group.add_argument(
|
|
268
269
|
"--annotation",
|
|
269
270
|
type=str,
|
|
270
271
|
help="アノテーションzip、またはzipを展開したディレクトリを指定します。指定しない場合はAnnofabからダウンロードします。",
|
|
271
272
|
)
|
|
272
273
|
|
|
273
|
-
|
|
274
|
-
"-p",
|
|
275
|
-
"--project_id",
|
|
276
|
-
type=str,
|
|
277
|
-
help="project_id。``--annotation`` が未指定のときは必須です。\n"
|
|
278
|
-
"``--annotation`` が指定されているときに ``--project_id`` を指定すると、アノテーション仕様を参照して、集計対象の属性やCSV列順が決まります。",
|
|
279
|
-
)
|
|
274
|
+
group.add_argument("-p", "--project_id", type=str, help="project_id。``--annotation`` が未指定のときは必須です。\n")
|
|
280
275
|
|
|
281
276
|
argument_parser.add_format(
|
|
282
277
|
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
annofabcli/task/copy_tasks.py
CHANGED
|
@@ -157,7 +157,7 @@ class CopyTasks(CommandLine):
|
|
|
157
157
|
def validate(self, args: argparse.Namespace) -> bool:
|
|
158
158
|
if args.parallelism is not None and not args.yes:
|
|
159
159
|
print( # noqa: T201
|
|
160
|
-
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'
|
|
160
|
+
f"{self.COMMON_MESSAGE} argument --parallelism: '--parallelism'を指定するときは、'--yes' を指定してください。",
|
|
161
161
|
file=sys.stderr,
|
|
162
162
|
)
|
|
163
163
|
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: annofabcli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.107.0
|
|
4
4
|
Summary: Utility Command Line Interface for AnnoFab
|
|
5
5
|
Author: Kurusugawa Computer Inc.
|
|
6
6
|
License: MIT
|
|
@@ -37,7 +37,6 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
[Annofab](https://annofab.com/)のCLI(Command Line Interface)ツールです。
|
|
38
38
|
「タスクの一括差し戻し」や、「タスク一覧出力」など、Annofabの画面で実施するには時間がかかる操作を、コマンドとして提供しています。
|
|
39
39
|
|
|
40
|
-
[](https://app.travis-ci.com/kurusugawa-computer/annofab-cli)
|
|
41
40
|
[](https://badge.fury.io/py/annofabcli)
|
|
42
41
|
[](https://pypi.org/project/annofabcli/)
|
|
43
42
|
[](https://annofab-cli.readthedocs.io/ja/latest/?badge=latest)
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
annofabcli/__init__.py,sha256=fdBtxy5rOI8zi26jf0hmXS5KTBjQIsm2b9ZUSAIR558,319
|
|
2
|
-
annofabcli/__main__.py,sha256=
|
|
2
|
+
annofabcli/__main__.py,sha256=HSKErJxFZARMWsH6GadYeyzcH5YBcZVDsy06wYGTo5c,5431
|
|
3
3
|
annofabcli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
annofabcli/annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
annofabcli/annotation/annotation_query.py,sha256=
|
|
5
|
+
annofabcli/annotation/annotation_query.py,sha256=jd69b_QBw8fy0i_bk-3MGNarS_W0XjCDJa1T4GSZjvk,15730
|
|
6
6
|
annofabcli/annotation/change_annotation_attributes.py,sha256=pCHT12ba5jsT9kqslUXqfy2QriRT8yiAzgHDuL1JkNE,17884
|
|
7
7
|
annofabcli/annotation/change_annotation_attributes_per_annotation.py,sha256=g_SqH1xzAdA8D1Hy1aoOPlpF86nc7F1PENeUX8XftDs,15529
|
|
8
8
|
annofabcli/annotation/change_annotation_properties.py,sha256=Kp_LZ5sSoVmmjGE80ABVO3InxsXBIxiFFvVcIJNsOMk,18309
|
|
9
9
|
annofabcli/annotation/copy_annotation.py,sha256=Pih2k3vvpgfT3Ovb3gZw2L_8fK_ws_wKR7ARYG5hG_8,18407
|
|
10
10
|
annofabcli/annotation/delete_annotation.py,sha256=hQApNrx2Ci1bBWk0dRGA0oJkIgDHwl6Jy0-33gYF6jo,22989
|
|
11
|
-
annofabcli/annotation/download_annotation_zip.py,sha256=
|
|
11
|
+
annofabcli/annotation/download_annotation_zip.py,sha256=SMtfxt6NKkpHGRDoKRCjPqpQB5DBpQ-PD2_3ts51h0Q,3314
|
|
12
12
|
annofabcli/annotation/dump_annotation.py,sha256=Q-p6f5XBs7khDgrfY5Q3CGLBMKEerJWO_CQ8_73UXVM,9972
|
|
13
|
-
annofabcli/annotation/import_annotation.py,sha256=
|
|
13
|
+
annofabcli/annotation/import_annotation.py,sha256=cFLpWOSJHDCbLXJV607ywPlitHR6JemB4C9b8RWJxhw,35785
|
|
14
14
|
annofabcli/annotation/list_annotation.py,sha256=uKcOuGC7lzd6vVbzizkiZtYdXJ7EzY0iifuiqKl2wQM,10707
|
|
15
15
|
annofabcli/annotation/list_annotation_count.py,sha256=T9fbaoxWeDJIVgW_YgHRldbwrVZWiE-57lfJrDQrj80,6474
|
|
16
16
|
annofabcli/annotation/merge_segmentation.py,sha256=kIsCeXtJxzd6nobQPpi0fscaRDlTx3tg1qpy5PDfSJI,18107
|
|
17
17
|
annofabcli/annotation/remove_segmentation_overlap.py,sha256=JeeBY3PUcBqLYiNItEH8Ae9zhEy6cf4LldNr_4yBdjY,15991
|
|
18
|
-
annofabcli/annotation/restore_annotation.py,sha256=
|
|
18
|
+
annofabcli/annotation/restore_annotation.py,sha256=4rrMYbC29HAsCxgcdOVDfZGcJgorbKDro6ewvDBjxwo,17064
|
|
19
19
|
annofabcli/annotation/subcommand_annotation.py,sha256=PfiU4G_Col3quCXKbZpqWoUXGi5p2uB1XI9BKMwaq1I,2288
|
|
20
20
|
annofabcli/annotation_specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
annofabcli/annotation_specs/add_attribute_restriction.py,sha256=jVA7ltharabR94bf71QuW0x0yVgu6-A7PHn7Dq3kUfg,6825
|
|
@@ -33,17 +33,20 @@ annofabcli/annotation_specs/list_attribute_restriction.py,sha256=8yVt8UvB3fS6qMx
|
|
|
33
33
|
annofabcli/annotation_specs/list_label_color.py,sha256=huHBAb-LVLe-tj5ZALiBkFgZlZSTvi2ZDsPYgJPDZ2Q,2356
|
|
34
34
|
annofabcli/annotation_specs/put_label_color.py,sha256=L6kX6bqy80IQp8iZg4QqnRqzE8KPApgTyydPy_VG0dk,5990
|
|
35
35
|
annofabcli/annotation_specs/subcommand_annotation_specs.py,sha256=yqSjV1S3CSB0sFqelLBNkPy9RrVyGNRnSWCnaLTjUsc,2692
|
|
36
|
+
annofabcli/annotation_zip/__init__.py,sha256=wAeKgkUYvgzdGFS3iJ3h0WogWbg9i-0c4xSg-mzbBpY,64
|
|
37
|
+
annofabcli/annotation_zip/list_annotation_bounding_box_2d.py,sha256=fPpc1Ekk-9f3_yKSn0PD7l6JPlheOExFtbCY3_n89iY,12520
|
|
38
|
+
annofabcli/annotation_zip/subcommand_annotation_zip.py,sha256=Cx07h2hJW6o9arkAsOnTsNmaY3G20PTkGlB6jzldx6Q,1146
|
|
36
39
|
annofabcli/comment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
-
annofabcli/comment/delete_comment.py,sha256=
|
|
40
|
+
annofabcli/comment/delete_comment.py,sha256=HZG1BOEKEbi3ISqM-2KZS5iq6aI16jDFBRLFpT-30e4,11385
|
|
38
41
|
annofabcli/comment/download_comment_json.py,sha256=YfqUnMgvLgVFV7FJPIXwirREkQ2E63fXeCaF4hfwk8c,2338
|
|
39
42
|
annofabcli/comment/list_all_comment.py,sha256=zMZSmVe8P89WDBZE5PZn5SIv5j2OTbnP1kS-HnCxPCI,6483
|
|
40
43
|
annofabcli/comment/list_comment.py,sha256=ikXOtfI9W88w7YC1GQQ1ryq2BatHelIRb8Pa5-fKbSs,6187
|
|
41
44
|
annofabcli/comment/put_comment.py,sha256=7q_GQW7nKUqzm9tf7mjhiPHfd0lBFLFvvdyF5Cn9854,12025
|
|
42
45
|
annofabcli/comment/put_comment_simply.py,sha256=OwanjmQy5nqqIw-i0Gt9APdEdftM-EuZW4pRLYbIwQM,8171
|
|
43
|
-
annofabcli/comment/put_inspection_comment.py,sha256=
|
|
44
|
-
annofabcli/comment/put_inspection_comment_simply.py,sha256=
|
|
45
|
-
annofabcli/comment/put_onhold_comment.py,sha256=
|
|
46
|
-
annofabcli/comment/put_onhold_comment_simply.py,sha256=
|
|
46
|
+
annofabcli/comment/put_inspection_comment.py,sha256=3fVn3flI6kpyDboDx9YUWqwxLfKzuHWmNpXk7mfWZ5A,3796
|
|
47
|
+
annofabcli/comment/put_inspection_comment_simply.py,sha256=Vxd9yeC5eS9VlZedOHTsSZem26mjK1zYG2rv026peJ8,6798
|
|
48
|
+
annofabcli/comment/put_onhold_comment.py,sha256=8hrK6foOtWx3iQQyWUTN9PmgpCYsleBy3Z2yLeDYqmU,3456
|
|
49
|
+
annofabcli/comment/put_onhold_comment_simply.py,sha256=1_0l1A-zUn9Su2b87fTKznTBPIxvTMf3yNpZMI4LKzA,3265
|
|
47
50
|
annofabcli/comment/subcommand_comment.py,sha256=gd8w8ArXM1Tq9VUduDgn8m4G6HwevRWJ36VBtGHg-5I,1537
|
|
48
51
|
annofabcli/comment/utils.py,sha256=aUj7U6MtGh64F3Ko83y4NKPKyWAqcg-c1XLqjkmIpSk,350
|
|
49
52
|
annofabcli/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -62,6 +65,7 @@ annofabcli/common/utils.py,sha256=Eb4DS5j_EVdZW_YnsvIgjTbgWcjHd560plck2_WgrCI,95
|
|
|
62
65
|
annofabcli/common/visualize.py,sha256=mBPCA5vfYNbE3I1m5oHRpo-E1hsNH11KswILtYRCOKQ,13650
|
|
63
66
|
annofabcli/common/annofab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
67
|
annofabcli/common/annofab/annotation_specs.py,sha256=h-YSnuK0riCLoJMmV-KDP3JS-_JMqx0feSvPAB3xCSI,765
|
|
68
|
+
annofabcli/common/annofab/annotation_zip.py,sha256=FoKpusFfF2GoGcLLrSjfCW9AeqQXWyWXMj2iM_Nu0PU,1333
|
|
65
69
|
annofabcli/common/annofab/project.py,sha256=BK96o9OO961yfUvcjuHG1DeJ2bMMEYTS5ujtJCvD_4I,891
|
|
66
70
|
annofabcli/data/logging.yaml,sha256=kBKE59V13MtDwscHw0ecOegaU9JghkhbIABRpNis_WA,704
|
|
67
71
|
annofabcli/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -82,7 +86,7 @@ annofabcli/input_data/download_input_data_json.py,sha256=vxGoeM3ZEggQbWiWsrDK0_G
|
|
|
82
86
|
annofabcli/input_data/list_all_input_data.py,sha256=C0u2ZmB52NIgn8-S-F6GT6GFS39gLsl5cCNa_oiNKtk,9754
|
|
83
87
|
annofabcli/input_data/list_all_input_data_merged_task.py,sha256=xs1uT9_ToQNA2luLxTOuPO_AUi5WRf1uMCdoBFZIMaU,12655
|
|
84
88
|
annofabcli/input_data/list_input_data.py,sha256=Jz5tRDPBLE8CyPV85jmOGd2-aHELyAE4MGi7WVjZaDM,11335
|
|
85
|
-
annofabcli/input_data/put_input_data.py,sha256=
|
|
89
|
+
annofabcli/input_data/put_input_data.py,sha256=D2H5mW7fOQMlJmtKLHIT2S9jc_uHdPshI4oBRXsD3To,18955
|
|
86
90
|
annofabcli/input_data/put_input_data_with_zip.py,sha256=SA4aMAwMBFgc9Lh0zmRCbmkXG4AMrcBqd5zeTSdr8lc,5566
|
|
87
91
|
annofabcli/input_data/subcommand_input_data.py,sha256=X8EoxsF6PMiKrvk_r7PIe2D0WZuaPlgLJRuTiljPIdM,2048
|
|
88
92
|
annofabcli/input_data/update_metadata_of_input_data.py,sha256=E4E-FZaQBmhmhMhddehhtVLxS26TsehZzHRI6Ccr8pc,11460
|
|
@@ -137,7 +141,7 @@ annofabcli/stat_visualization/write_performance_rating_csv.py,sha256=-eC_Nv9ohrL
|
|
|
137
141
|
annofabcli/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
138
142
|
annofabcli/statistics/histogram.py,sha256=CvzDxT2cKLSnBGSqkZE6p92PayGxYYja1YyB24M4ALU,3245
|
|
139
143
|
annofabcli/statistics/linegraph.py,sha256=0kr7jVBNMiM2ECYhv3Ry5RitElKerSl9ZKxbKzfiplI,12494
|
|
140
|
-
annofabcli/statistics/list_annotation_area.py,sha256=
|
|
144
|
+
annofabcli/statistics/list_annotation_area.py,sha256=ChBWz-LcGkdNV7J_qg4JeE7b7WdKyNgyfRjoW3IJfvw,12390
|
|
141
145
|
annofabcli/statistics/list_annotation_attribute.py,sha256=lmvT3b1mVX4PRGWrBUfZafCn3c_hb_hx3WKXnHGcyyk,12754
|
|
142
146
|
annofabcli/statistics/list_annotation_attribute_filled_count.py,sha256=UF8wB-kfUnHCIPykb2Z2slyEcp9aHrzcbFbOs_3Z8-A,29341
|
|
143
147
|
annofabcli/statistics/list_annotation_count.py,sha256=4uTRR63Db-WDGRQt3AC8mPaRfFgbG5d27A036GNG3D0,53151
|
|
@@ -186,7 +190,7 @@ annofabcli/task/change_operator.py,sha256=q6pMd1SdsTRgMHS0705dnosTSHprTpYgXtNd0r
|
|
|
186
190
|
annofabcli/task/change_status_to_break.py,sha256=hwdFTFW-zV0VxuinoBB5n6mvHJ7g9ChjrSOXZcNk88w,8621
|
|
187
191
|
annofabcli/task/change_status_to_on_hold.py,sha256=vWRyk6IK3HcgTWDIbbhXzsrtuoa7OlXCf8CvUpFp_Uw,12981
|
|
188
192
|
annofabcli/task/complete_tasks.py,sha256=ssg_Z7ADRQRXvXgK2k5TEmvbRjrJQ33cXeb8kG8Y3jc,24917
|
|
189
|
-
annofabcli/task/copy_tasks.py,sha256=
|
|
193
|
+
annofabcli/task/copy_tasks.py,sha256=EcaCaSzqddlSKSUYqDOD53-r1GFpIl38KL3FriSfMeI,8735
|
|
190
194
|
annofabcli/task/delete_metadata_key_of_task.py,sha256=Cjqe3fWKeRzVxxlrGyt3TS-x1riD55LnNXLIS9JPoTw,8029
|
|
191
195
|
annofabcli/task/delete_tasks.py,sha256=7T5eNCMW06ABekNGLwhTitDK5qn0tiPKrEXyJXyQNvs,13098
|
|
192
196
|
annofabcli/task/download_task_json.py,sha256=Ocjecmdf2WV_Sq3u1InfMLIsT3XSw0ojyJmJbhv2sgg,2803
|
|
@@ -209,8 +213,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=hQLVbQ0
|
|
|
209
213
|
annofabcli/task_history_event/list_all_task_history_event.py,sha256=EeKMyPUxGwYCFtWQHHW954ZserGm8lUqrwNnV1iX9X4,6830
|
|
210
214
|
annofabcli/task_history_event/list_worktime.py,sha256=Y7Pu5DP7scPf7HPt6CTiTvB1_5_Nfi1bStUIaCpkhII,15507
|
|
211
215
|
annofabcli/task_history_event/subcommand_task_history_event.py,sha256=mJVJoT4RXk4HWnY7-Nrsl4If-gtaIIEXd2z7eFZwM2I,1260
|
|
212
|
-
annofabcli-1.
|
|
213
|
-
annofabcli-1.
|
|
214
|
-
annofabcli-1.
|
|
215
|
-
annofabcli-1.
|
|
216
|
-
annofabcli-1.
|
|
216
|
+
annofabcli-1.107.0.dist-info/METADATA,sha256=naOfvMmFCACfxPn3uScdATo_vyMGHigfNxqlMgUrl6g,5134
|
|
217
|
+
annofabcli-1.107.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
218
|
+
annofabcli-1.107.0.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
|
|
219
|
+
annofabcli-1.107.0.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
|
|
220
|
+
annofabcli-1.107.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|