Skip to content

Commit 2ad49c4

Browse files
authored
‘SimpleAnnotationParser.parse‘:SimpleAnnotationDetailクラスのdataプロパティを変換する関数を指定できるようにする (#350)
* parseメソッドに引数を渡せるようにする * parseメソッドに引数を渡せるようにする * add test
1 parent 42d258d commit 2ad49c4

File tree

4 files changed

+100
-27
lines changed

4 files changed

+100
-27
lines changed

annofabapi/dataclass/annotation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
)
2626

2727
AnnotationData = Union[str, Dict[str, Any]]
28-
FullAnnotationData = Dict[str, Any]
28+
FullAnnotationData = Any
2929
AdditionalDataValue = Dict[str, Any]
3030

3131

@@ -191,7 +191,7 @@ class FullAnnotationDetail(DataClassJsonMixin):
191191
data_holding_type: Optional[AnnotationDataHoldingType]
192192
""""""
193193

194-
data: Optional[FullAnnotationData]
194+
data: FullAnnotationData
195195
""""""
196196

197197
additional_data_list: Optional[List[FullAnnotationAdditionalData]]
@@ -223,7 +223,7 @@ class FullAnnotation(DataClassJsonMixin):
223223
input_data_name: Optional[str]
224224
""""""
225225

226-
details: Optional[List[FullAnnotationDetail]]
226+
details: List[FullAnnotationDetail]
227227
""""""
228228

229229
updated_datetime: Optional[str]

annofabapi/parser.py

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
import os
44
import zipfile
55
from pathlib import Path
6-
from typing import Any, Dict, Iterator, List, Optional
6+
from typing import Any, Callable, Dict, Iterator, List, Optional
77

88
from annofabapi.dataclass.annotation import FullAnnotation, SimpleAnnotation
99
from annofabapi.exceptions import AnnotationOuterFileNotFoundError
1010

11+
CONVERT_ANNOTATION_DETAIL_DATA_FUNC = Callable[[Dict[str, Any]], Any]
12+
1113

1214
def _trim_extension(file_path: str) -> str:
1315
"""ファイルパスから拡張子を除去した文字列を返す"""
@@ -72,12 +74,25 @@ def open_outer_file(self, data_uri: str):
7274
7375
"""
7476

75-
@abc.abstractmethod
76-
def parse(self) -> SimpleAnnotation:
77-
"""
78-
JSONファイルをパースする。
77+
def parse(
78+
self, convert_deitail_data_func: Optional[CONVERT_ANNOTATION_DETAIL_DATA_FUNC] = None
79+
) -> SimpleAnnotation:
80+
"""JSONファイルをパースする
81+
82+
Args:
83+
convert_deitail_data_func: SimpleAnnotationDetailクラスのdataプロパティを変換する関数を指定します。
84+
dictからdataclassに変換する際に使います。
85+
86+
Returns:
87+
SimpleAnnotationインスタンス
7988
"""
8089

90+
simple_annotation = SimpleAnnotation.from_dict(self.load_json()) # type: ignore
91+
if convert_deitail_data_func is not None:
92+
for detail in simple_annotation.details:
93+
detail.data = convert_deitail_data_func(detail.data)
94+
return simple_annotation
95+
8196
@abc.abstractmethod
8297
def load_json(self) -> Any:
8398
"""
@@ -144,11 +159,28 @@ def open_outer_file(self, data_uri: str):
144159
"""
145160

146161
@abc.abstractmethod
147-
def parse(self) -> FullAnnotation:
162+
def load_json(self) -> Any:
148163
"""
149-
JSONファイルをパースする
164+
JSONファイルをloadします
150165
"""
151166

167+
def parse(self, convert_deitail_data_func: Optional[CONVERT_ANNOTATION_DETAIL_DATA_FUNC] = None) -> FullAnnotation:
168+
"""JSONファイルをパースする
169+
170+
Args:
171+
convert_deitail_data_func: FullAnnotationDetailクラスのdataプロパティを変換する関数を指定します。
172+
dictからdataclassに変換する際に使います。
173+
174+
Returns:
175+
FullAnnotationインスタンス
176+
"""
177+
178+
full_annotation = FullAnnotation.from_dict(self.load_json()) # type: ignore
179+
if convert_deitail_data_func is not None:
180+
for detail in full_annotation.details:
181+
detail.data = convert_deitail_data_func(detail.data)
182+
return full_annotation
183+
152184

153185
class SimpleAnnotationZipParser(SimpleAnnotationParser):
154186
"""
@@ -172,9 +204,6 @@ def __init__(self, zip_file: zipfile.ZipFile, json_file_path: str):
172204
self.__zip_file = zip_file
173205
super().__init__(json_file_path)
174206

175-
def parse(self) -> SimpleAnnotation:
176-
return SimpleAnnotation.from_dict(self.load_json()) # type: ignore
177-
178207
def load_json(self) -> Any:
179208
with self.__zip_file.open(self.json_file_path) as entry:
180209
return json.load(entry)
@@ -208,9 +237,6 @@ class SimpleAnnotationDirParser(SimpleAnnotationParser):
208237
def __init__(self, json_file_path: Path):
209238
super().__init__(str(json_file_path))
210239

211-
def parse(self) -> SimpleAnnotation:
212-
return SimpleAnnotation.from_dict(self.load_json()) # type: ignore
213-
214240
def load_json(self) -> Any:
215241
with open(self.json_file_path, encoding="utf-8") as f:
216242
return json.load(f)
@@ -245,11 +271,9 @@ def __init__(self, zip_file: zipfile.ZipFile, json_file_path: str):
245271
self.__zip_file = zip_file
246272
super().__init__(json_file_path)
247273

248-
def parse(self) -> FullAnnotation:
274+
def load_json(self) -> Any:
249275
with self.__zip_file.open(self.json_file_path) as entry:
250-
anno_dict: dict = json.load(entry)
251-
# mypyの "has no attribute "from_dict" " をignore
252-
return FullAnnotation.from_dict(anno_dict) # type: ignore
276+
return json.load(entry)
253277

254278
def open_outer_file(self, data_uri: str):
255279
outer_file_path = _trim_extension(self.json_file_path) + "/" + data_uri
@@ -281,10 +305,9 @@ class FullAnnotationDirParser(FullAnnotationParser):
281305
def __init__(self, json_file_path: Path):
282306
super().__init__(str(json_file_path))
283307

284-
def parse(self) -> FullAnnotation:
308+
def load_json(self) -> Any:
285309
with open(self.json_file_path, encoding="utf-8") as f:
286-
anno_dict: dict = json.load(f)
287-
return FullAnnotation.from_dict(anno_dict) # type: ignore
310+
return json.load(f)
288311

289312
def open_outer_file(self, data_uri: str):
290313
outer_file_path = _trim_extension(self.json_file_path) + "/" + data_uri
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from annofabapi.models import (
2-
AnnotationDataHoldingType,
3-
InternationalizationMessage,
42
AdditionalDataDefinitionType,
3+
AnnotationDataHoldingType,
54
AnnotationType,
5+
InternationalizationMessage,
66
TaskPhase,
77
TaskStatus,
88
)
99

1010
AnnotationData = Union[str, Dict[str, Any]]
11-
FullAnnotationData = Dict[str, Any]
11+
FullAnnotationData = Any
1212
AdditionalDataValue = Dict[str, Any]

tests/test_local_parser.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import annofabapi
99
import annofabapi.parser
1010
import annofabapi.utils
11-
from annofabapi.dataclass.annotation import FullAnnotation, SimpleAnnotation
11+
from annofabapi.dataclass.annotation import FullAnnotation, FullAnnotationDataPoints, SimpleAnnotation
1212
from annofabapi.exceptions import AnnotationOuterFileNotFoundError
1313
from annofabapi.parser import (
1414
FullAnnotationDirParser,
@@ -43,6 +43,24 @@ def test_SimpleAnnotationZipParser(self):
4343
with pytest.raises(AnnotationOuterFileNotFoundError):
4444
parser.open_outer_file("foo")
4545

46+
def convert_deitail_data(self, dict_data):
47+
if dict_data["_type"] == "Points":
48+
dict_data["type"] = dict_data["_type"]
49+
return FullAnnotationDataPoints.from_dict(dict_data)
50+
else:
51+
return dict_data
52+
53+
def test_parse_for_zip(self):
54+
zip_path = Path(test_dir / "simple-annotation.zip")
55+
with zipfile.ZipFile(zip_path) as zip_file:
56+
parser = SimpleAnnotationZipParser(zip_file, "sample_1/c86205d1-bdd4-4110-ae46-194e661d622b.json")
57+
58+
simple_annotation = parser.parse()
59+
assert type(simple_annotation.details[0].data) == dict
60+
61+
simple_annotation2 = parser.parse(self.convert_deitail_data)
62+
assert type(simple_annotation2.details[0].data) == FullAnnotationDataPoints
63+
4664
def test_SimpleAnnotationDirParser(self):
4765
dir_path = Path(test_dir / "simple-annotation")
4866

@@ -118,6 +136,10 @@ def test_simple_annotation_dir(self):
118136
assert type(simple_annotation) == SimpleAnnotation
119137
index += 1
120138

139+
dict_simple_annotation = parser.load_json()
140+
assert type(dict_simple_annotation) == dict
141+
assert "details" in dict_simple_annotation
142+
121143
assert index == 4
122144

123145
def test_lazy_parse_simple_annotation_zip_by_task(self):
@@ -166,6 +188,11 @@ def test_full_annotation_zip(self):
166188
parser = FullAnnotationZipParser(zip_file, "sample_1/c86205d1-bdd4-4110-ae46-194e661d622b.json")
167189
assert parser.task_id == "sample_1"
168190
assert parser.input_data_id == "c86205d1-bdd4-4110-ae46-194e661d622b"
191+
192+
dict_simple_annotation = parser.load_json()
193+
assert type(dict_simple_annotation) == dict
194+
assert "details" in dict_simple_annotation
195+
169196
with pytest.raises(AnnotationOuterFileNotFoundError):
170197
parser.open_outer_file("foo")
171198

@@ -186,5 +213,28 @@ def test_full_annotation_dir(self):
186213
)
187214
assert parser.task_id == "sample_1"
188215
assert parser.input_data_id == "c86205d1-bdd4-4110-ae46-194e661d622b"
216+
217+
dict_simple_annotation = parser.load_json()
218+
assert type(dict_simple_annotation) == dict
219+
assert "details" in dict_simple_annotation
220+
189221
with pytest.raises(AnnotationOuterFileNotFoundError):
190222
parser.open_outer_file("foo")
223+
224+
def convert_deitail_data(self, dict_data):
225+
if dict_data["_type"] == "Points":
226+
dict_data["type"] = dict_data["_type"]
227+
return FullAnnotationDataPoints.from_dict(dict_data)
228+
else:
229+
return dict_data
230+
231+
def test_parse_for_zip(self):
232+
zip_path = Path(test_dir / "full-annotation.zip")
233+
with zipfile.ZipFile(zip_path) as zip_file:
234+
parser = FullAnnotationZipParser(zip_file, "sample_1/c86205d1-bdd4-4110-ae46-194e661d622b.json")
235+
236+
full_annotation = parser.parse()
237+
assert type(full_annotation.details[0].data) == dict
238+
239+
full_annotation2 = parser.parse(self.convert_deitail_data)
240+
assert type(full_annotation2.details[0].data) == FullAnnotationDataPoints

0 commit comments

Comments
 (0)