annofabcli 1.101.0__py3-none-any.whl → 1.102.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/statistics/list_annotation_area.py +320 -0
- annofabcli/statistics/subcommand_statistics.py +2 -0
- {annofabcli-1.101.0.dist-info → annofabcli-1.102.0.dist-info}/METADATA +1 -1
- {annofabcli-1.101.0.dist-info → annofabcli-1.102.0.dist-info}/RECORD +7 -6
- {annofabcli-1.101.0.dist-info → annofabcli-1.102.0.dist-info}/WHEEL +0 -0
- {annofabcli-1.101.0.dist-info → annofabcli-1.102.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.101.0.dist-info → annofabcli-1.102.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
import zipfile
|
|
8
|
+
from collections.abc import Collection, Iterator
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
import numpy
|
|
14
|
+
import pandas
|
|
15
|
+
from annofabapi.models import InputDataType, ProjectMemberRole
|
|
16
|
+
from annofabapi.parser import (
|
|
17
|
+
SimpleAnnotationParser,
|
|
18
|
+
lazy_parse_simple_annotation_dir,
|
|
19
|
+
lazy_parse_simple_annotation_zip,
|
|
20
|
+
)
|
|
21
|
+
from annofabapi.segmentation import read_binary_image
|
|
22
|
+
from dataclasses_json import DataClassJsonMixin
|
|
23
|
+
|
|
24
|
+
import annofabcli
|
|
25
|
+
import annofabcli.common.cli
|
|
26
|
+
from annofabcli.common.cli import (
|
|
27
|
+
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
28
|
+
ArgumentParser,
|
|
29
|
+
CommandLine,
|
|
30
|
+
build_annofabapi_resource_and_login,
|
|
31
|
+
)
|
|
32
|
+
from annofabcli.common.download import DownloadingFile
|
|
33
|
+
from annofabcli.common.enums import FormatArgument
|
|
34
|
+
from annofabcli.common.facade import (
|
|
35
|
+
AnnofabApiFacade,
|
|
36
|
+
TaskQuery,
|
|
37
|
+
match_annotation_with_task_query,
|
|
38
|
+
)
|
|
39
|
+
from annofabcli.common.utils import print_csv, print_json
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def lazy_parse_simple_annotation_by_input_data(annotation_path: Path) -> Iterator[SimpleAnnotationParser]:
|
|
45
|
+
if not annotation_path.exists():
|
|
46
|
+
raise RuntimeError(f"'{annotation_path}' は存在しません。")
|
|
47
|
+
|
|
48
|
+
if annotation_path.is_dir():
|
|
49
|
+
return lazy_parse_simple_annotation_dir(annotation_path)
|
|
50
|
+
elif zipfile.is_zipfile(str(annotation_path)):
|
|
51
|
+
return lazy_parse_simple_annotation_zip(annotation_path)
|
|
52
|
+
else:
|
|
53
|
+
raise RuntimeError(f"'{annotation_path}'は、zipファイルまたはディレクトリではありません。")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class AnnotationAreaInfo(DataClassJsonMixin):
|
|
58
|
+
task_id: str
|
|
59
|
+
task_status: str
|
|
60
|
+
task_phase: str
|
|
61
|
+
task_phase_stage: int
|
|
62
|
+
|
|
63
|
+
input_data_id: str
|
|
64
|
+
input_data_name: str
|
|
65
|
+
|
|
66
|
+
label: str
|
|
67
|
+
annotation_id: str
|
|
68
|
+
annotation_area: int
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def calculate_bounding_box_area(data: dict[str, Any]) -> int:
|
|
72
|
+
width = abs(data["right_bottom"]["x"] - data["left_top"]["x"])
|
|
73
|
+
height = abs(data["right_bottom"]["y"] - data["left_top"]["y"])
|
|
74
|
+
return width * height
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def calculate_segmentation_area(outer_file: Path) -> int:
|
|
78
|
+
nd_array = read_binary_image(outer_file)
|
|
79
|
+
return numpy.count_nonzero(nd_array)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_annotation_area_info_list(parser: SimpleAnnotationParser, simple_annotation: dict[str, Any]) -> list[AnnotationAreaInfo]:
|
|
83
|
+
result = []
|
|
84
|
+
for detail in simple_annotation["details"]:
|
|
85
|
+
if detail["data"]["_type"] in {"Segmentation", "SegmentationV2"}:
|
|
86
|
+
annotation_area = calculate_segmentation_area(parser.open_outer_file(detail["data"]["data_uri"]))
|
|
87
|
+
elif detail["data"]["_type"] == "BoundingBox":
|
|
88
|
+
annotation_area = calculate_bounding_box_area(detail["data"])
|
|
89
|
+
|
|
90
|
+
else:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
result.append(
|
|
94
|
+
AnnotationAreaInfo(
|
|
95
|
+
task_id=simple_annotation["task_id"],
|
|
96
|
+
task_phase=simple_annotation["task_phase"],
|
|
97
|
+
task_phase_stage=simple_annotation["task_phase_stage"],
|
|
98
|
+
task_status=simple_annotation["task_status"],
|
|
99
|
+
input_data_id=simple_annotation["input_data_id"],
|
|
100
|
+
input_data_name=simple_annotation["input_data_name"],
|
|
101
|
+
label=detail["label"],
|
|
102
|
+
annotation_id=detail["annotation_id"],
|
|
103
|
+
annotation_area=annotation_area,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_annotation_area_info_list_from_annotation_path(
|
|
111
|
+
annotation_path: Path,
|
|
112
|
+
*,
|
|
113
|
+
target_task_ids: Optional[Collection[str]] = None,
|
|
114
|
+
task_query: Optional[TaskQuery] = None,
|
|
115
|
+
) -> list[AnnotationAreaInfo]:
|
|
116
|
+
annotation_area_list = []
|
|
117
|
+
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
118
|
+
iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_path)
|
|
119
|
+
logger.debug("アノテーションzip/ディレクトリを読み込み中")
|
|
120
|
+
for index, parser in enumerate(iter_parser):
|
|
121
|
+
if (index + 1) % 1000 == 0:
|
|
122
|
+
logger.debug(f"{index + 1} 件目のJSONを読み込み中")
|
|
123
|
+
if target_task_ids is not None and parser.task_id not in target_task_ids:
|
|
124
|
+
continue
|
|
125
|
+
simple_annotation_dict = parser.load_json()
|
|
126
|
+
if task_query is not None: # noqa: SIM102
|
|
127
|
+
if not match_annotation_with_task_query(simple_annotation_dict, task_query):
|
|
128
|
+
continue
|
|
129
|
+
sub_annotation_area_list = get_annotation_area_info_list(parser, simple_annotation_dict)
|
|
130
|
+
annotation_area_list.extend(sub_annotation_area_list)
|
|
131
|
+
return annotation_area_list
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def create_df(
|
|
135
|
+
annotation_area_list: list[AnnotationAreaInfo],
|
|
136
|
+
) -> pandas.DataFrame:
|
|
137
|
+
columns = [
|
|
138
|
+
"task_id",
|
|
139
|
+
"task_status",
|
|
140
|
+
"task_phase",
|
|
141
|
+
"task_phase_stage",
|
|
142
|
+
"input_data_id",
|
|
143
|
+
"input_data_name",
|
|
144
|
+
"label",
|
|
145
|
+
"annotation_id",
|
|
146
|
+
"annotation_area",
|
|
147
|
+
]
|
|
148
|
+
df = pandas.DataFrame(e.to_dict() for e in annotation_area_list)
|
|
149
|
+
if len(df) == 0:
|
|
150
|
+
df = pandas.DataFrame(columns=columns)
|
|
151
|
+
|
|
152
|
+
df = df.fillna({"annotation_area": 0})
|
|
153
|
+
return df[columns]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def print_annotation_area(
|
|
157
|
+
annotation_path: Path,
|
|
158
|
+
output_file: Path,
|
|
159
|
+
output_format: FormatArgument,
|
|
160
|
+
*,
|
|
161
|
+
target_task_ids: Optional[Collection[str]] = None,
|
|
162
|
+
task_query: Optional[TaskQuery] = None,
|
|
163
|
+
) -> None:
|
|
164
|
+
annotation_area_list = get_annotation_area_info_list_from_annotation_path(
|
|
165
|
+
annotation_path,
|
|
166
|
+
target_task_ids=target_task_ids,
|
|
167
|
+
task_query=task_query,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
logger.info(f"{len(annotation_area_list)} 件のタスクに含まれる塗りつぶしアノテーションのピクセル数情報を出力します。")
|
|
171
|
+
|
|
172
|
+
if output_format == FormatArgument.CSV:
|
|
173
|
+
df = create_df(annotation_area_list)
|
|
174
|
+
print_csv(df, output_file)
|
|
175
|
+
|
|
176
|
+
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
177
|
+
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
178
|
+
print_json(
|
|
179
|
+
[e.to_dict(encode_json=True) for e in annotation_area_list],
|
|
180
|
+
is_pretty=json_is_pretty,
|
|
181
|
+
output=output_file,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
else:
|
|
185
|
+
raise RuntimeError(f"出力形式 '{output_format}' はサポートされていません。")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class ListAnnotationArea(CommandLine):
|
|
189
|
+
COMMON_MESSAGE = "annofabcli statistics list_annotation_area: error:"
|
|
190
|
+
|
|
191
|
+
def validate(self, args: argparse.Namespace) -> bool:
|
|
192
|
+
if args.project_id is None and args.annotation is None:
|
|
193
|
+
print( # noqa: T201
|
|
194
|
+
f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
|
|
195
|
+
file=sys.stderr,
|
|
196
|
+
)
|
|
197
|
+
return False
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
def main(self) -> None:
|
|
201
|
+
args = self.args
|
|
202
|
+
|
|
203
|
+
if not self.validate(args):
|
|
204
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
205
|
+
|
|
206
|
+
project_id: Optional[str] = args.project_id
|
|
207
|
+
if project_id is not None:
|
|
208
|
+
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
209
|
+
project, _ = self.service.api.get_project(project_id)
|
|
210
|
+
if project["input_data_type"] != InputDataType.IMAGE.value:
|
|
211
|
+
logger.warning(f"project_id='{project_id}'であるプロジェクトは、画像プロジェクトでないので、出力されるデータは0件になります。")
|
|
212
|
+
|
|
213
|
+
annotation_path = Path(args.annotation) if args.annotation is not None else None
|
|
214
|
+
|
|
215
|
+
task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
|
|
216
|
+
task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
|
|
217
|
+
|
|
218
|
+
output_file: Path = args.output
|
|
219
|
+
output_format = FormatArgument(args.format)
|
|
220
|
+
|
|
221
|
+
downloading_obj = DownloadingFile(self.service)
|
|
222
|
+
|
|
223
|
+
def download_and_print_annotation_area(project_id: str, temp_dir: Path, *, is_latest: bool, annotation_path: Optional[Path]) -> None:
|
|
224
|
+
if annotation_path is None:
|
|
225
|
+
annotation_path = temp_dir / f"{project_id}__annotation.zip"
|
|
226
|
+
downloading_obj.download_annotation_zip(
|
|
227
|
+
project_id,
|
|
228
|
+
dest_path=annotation_path,
|
|
229
|
+
is_latest=is_latest,
|
|
230
|
+
)
|
|
231
|
+
print_annotation_area(
|
|
232
|
+
output_format=output_format,
|
|
233
|
+
output_file=output_file,
|
|
234
|
+
target_task_ids=task_id_list,
|
|
235
|
+
task_query=task_query,
|
|
236
|
+
annotation_path=annotation_path,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if project_id is not None:
|
|
240
|
+
if args.temp_dir is not None:
|
|
241
|
+
download_and_print_annotation_area(
|
|
242
|
+
project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest, annotation_path=annotation_path
|
|
243
|
+
)
|
|
244
|
+
else:
|
|
245
|
+
with tempfile.TemporaryDirectory() as str_temp_dir:
|
|
246
|
+
download_and_print_annotation_area(
|
|
247
|
+
project_id=project_id, temp_dir=Path(str_temp_dir), is_latest=args.latest, annotation_path=annotation_path
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
assert annotation_path is not None
|
|
251
|
+
print_annotation_area(
|
|
252
|
+
output_format=output_format,
|
|
253
|
+
output_file=output_file,
|
|
254
|
+
target_task_ids=task_id_list,
|
|
255
|
+
task_query=task_query,
|
|
256
|
+
annotation_path=annotation_path,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
261
|
+
argument_parser = ArgumentParser(parser)
|
|
262
|
+
|
|
263
|
+
parser.add_argument(
|
|
264
|
+
"--annotation",
|
|
265
|
+
type=str,
|
|
266
|
+
help="アノテーションzip、またはzipを展開したディレクトリを指定します。指定しない場合はAnnofabからダウンロードします。",
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
parser.add_argument(
|
|
270
|
+
"-p",
|
|
271
|
+
"--project_id",
|
|
272
|
+
type=str,
|
|
273
|
+
help="project_id。``--annotation`` が未指定のときは必須です。\n"
|
|
274
|
+
"``--annotation`` が指定されているときに ``--project_id`` を指定すると、アノテーション仕様を参照して、集計対象の属性やCSV列順が決まります。",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
argument_parser.add_format(
|
|
278
|
+
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
|
279
|
+
default=FormatArgument.CSV,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
argument_parser.add_output()
|
|
283
|
+
|
|
284
|
+
parser.add_argument(
|
|
285
|
+
"-tq",
|
|
286
|
+
"--task_query",
|
|
287
|
+
type=str,
|
|
288
|
+
help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
|
|
289
|
+
" ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
290
|
+
)
|
|
291
|
+
argument_parser.add_task_id(required=False)
|
|
292
|
+
|
|
293
|
+
parser.add_argument(
|
|
294
|
+
"--latest",
|
|
295
|
+
action="store_true",
|
|
296
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。", # noqa: E501
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
parser.add_argument(
|
|
300
|
+
"--temp_dir",
|
|
301
|
+
type=Path,
|
|
302
|
+
help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
parser.set_defaults(subcommand_func=main)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def main(args: argparse.Namespace) -> None:
|
|
309
|
+
service = build_annofabapi_resource_and_login(args)
|
|
310
|
+
facade = AnnofabApiFacade(service)
|
|
311
|
+
ListAnnotationArea(service, facade, args).main()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
|
|
315
|
+
subcommand_name = "list_annotation_area"
|
|
316
|
+
subcommand_help = "塗りつぶしアノテーションまたは矩形アノテーションの面積を出力します。"
|
|
317
|
+
epilog = "オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
318
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
319
|
+
parse_args(parser)
|
|
320
|
+
return parser
|
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
|
4
4
|
import annofabcli
|
|
5
5
|
import annofabcli.common.cli
|
|
6
6
|
import annofabcli.stat_visualization.merge_visualization_dir
|
|
7
|
+
import annofabcli.statistics.list_annotation_area
|
|
7
8
|
import annofabcli.statistics.list_annotation_attribute
|
|
8
9
|
import annofabcli.statistics.list_annotation_attribute_filled_count
|
|
9
10
|
import annofabcli.statistics.list_annotation_count
|
|
@@ -27,6 +28,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
27
28
|
annofabcli.statistics.list_annotation_attribute_filled_count.add_parser(subparsers)
|
|
28
29
|
annofabcli.statistics.list_annotation_count.add_parser(subparsers)
|
|
29
30
|
annofabcli.statistics.list_annotation_duration.add_parser(subparsers)
|
|
31
|
+
annofabcli.statistics.list_annotation_area.add_parser(subparsers)
|
|
30
32
|
|
|
31
33
|
annofabcli.statistics.list_video_duration.add_parser(subparsers)
|
|
32
34
|
annofabcli.statistics.list_worktime.add_parser(subparsers)
|
|
@@ -135,6 +135,7 @@ annofabcli/stat_visualization/write_performance_rating_csv.py,sha256=TDn7-poyFt2
|
|
|
135
135
|
annofabcli/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
136
136
|
annofabcli/statistics/histogram.py,sha256=CvzDxT2cKLSnBGSqkZE6p92PayGxYYja1YyB24M4ALU,3245
|
|
137
137
|
annofabcli/statistics/linegraph.py,sha256=0kr7jVBNMiM2ECYhv3Ry5RitElKerSl9ZKxbKzfiplI,12494
|
|
138
|
+
annofabcli/statistics/list_annotation_area.py,sha256=1LFYqc1JodVuHKM2ceTwWriDCabMmkOnHiNptvEyXAw,12325
|
|
138
139
|
annofabcli/statistics/list_annotation_attribute.py,sha256=87jjNCOXJUbWnmswMCLN7GTjGsBfqpFJ6hViWmnj8Y4,12557
|
|
139
140
|
annofabcli/statistics/list_annotation_attribute_filled_count.py,sha256=vwWeFHwTnEMdrLBauIKPFDkUCa6lXXd0GQgUAQ0LCqU,28890
|
|
140
141
|
annofabcli/statistics/list_annotation_count.py,sha256=nzmlHRCWt5mjeksZkeQyWqm4UaCa9SrdbNtuX9TPP5w,52907
|
|
@@ -142,7 +143,7 @@ annofabcli/statistics/list_annotation_duration.py,sha256=N7nnVUDfX_thIapqe6-z_Mq
|
|
|
142
143
|
annofabcli/statistics/list_video_duration.py,sha256=uNeMteRBX2JG_AWmcgMJj0Jzbq_qF7bvAwr25GmeIiw,9124
|
|
143
144
|
annofabcli/statistics/list_worktime.py,sha256=C7Yu3IOW2EvhkJJv6gY3hNdS9_TOLmT_9LZsB7vLJ1o,6493
|
|
144
145
|
annofabcli/statistics/scatter.py,sha256=IUCwXix9GbZb6V82wjjb5q2eamrT5HQsU_bzDTjAFnM,11011
|
|
145
|
-
annofabcli/statistics/subcommand_statistics.py,sha256=
|
|
146
|
+
annofabcli/statistics/subcommand_statistics.py,sha256=Pvd7s0vvDU9tSpAphPrv94IDhhR1p8iFH2tjdt7I7ZU,2536
|
|
146
147
|
annofabcli/statistics/summarize_task_count.py,sha256=8OH6BBRYRjHJkWRTjU0A0OfXa7f3NIRHrxPNFlRt_hM,9707
|
|
147
148
|
annofabcli/statistics/summarize_task_count_by_task_id_group.py,sha256=TSSmcFv615NLcq6uqXmg3ilYqSHl3A5qp90msVQM1gE,8646
|
|
148
149
|
annofabcli/statistics/summarize_task_count_by_user.py,sha256=TRoJXpt2HOVb8QO2YtRejkOAxyK80_NsPt3Vk9es9C8,6948
|
|
@@ -206,8 +207,8 @@ annofabcli/task_history_event/download_task_history_event_json.py,sha256=hQLVbQ0
|
|
|
206
207
|
annofabcli/task_history_event/list_all_task_history_event.py,sha256=JQEgwOIXbbTIfeX23AVaoySHViOR9UGm9uoXuhVEBqo,6446
|
|
207
208
|
annofabcli/task_history_event/list_worktime.py,sha256=9jsRYa2C9bva8E1Aqxv9CCKDuCP0MvbiaIyQFTDpjqY,13150
|
|
208
209
|
annofabcli/task_history_event/subcommand_task_history_event.py,sha256=mJVJoT4RXk4HWnY7-Nrsl4If-gtaIIEXd2z7eFZwM2I,1260
|
|
209
|
-
annofabcli-1.
|
|
210
|
-
annofabcli-1.
|
|
211
|
-
annofabcli-1.
|
|
212
|
-
annofabcli-1.
|
|
213
|
-
annofabcli-1.
|
|
210
|
+
annofabcli-1.102.0.dist-info/METADATA,sha256=nxOd0drqHnw28-7Kf3aA0qxKodxsNDV8HgqjX5Nnqxw,5286
|
|
211
|
+
annofabcli-1.102.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
212
|
+
annofabcli-1.102.0.dist-info/entry_points.txt,sha256=C2uSUc-kkLJpoK_mDL5FEMAdorLEMPfwSf8VBMYnIFM,56
|
|
213
|
+
annofabcli-1.102.0.dist-info/licenses/LICENSE,sha256=pcqWYfxFtxBzhvKp3x9MXNM4xciGb2eFewaRhXUNHlo,1081
|
|
214
|
+
annofabcli-1.102.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|