Skip to content

Commit 3434a58

Browse files
🐛 fix broken location & confidence data for V2 fields (#349)
1 parent d5115ea commit 3434a58

File tree

8 files changed

+113
-8
lines changed

8 files changed

+113
-8
lines changed
Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
11
from typing import List, Optional
22

3-
from mindee.parsing.v2.field.dynamic_field import DynamicField
3+
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.parsing.v2.field.dynamic_field import DynamicField, FieldType
5+
from mindee.parsing.v2.field.field_confidence import FieldConfidence
6+
from mindee.parsing.v2.field.field_location import FieldLocation
47

58

69
class BaseField(DynamicField):
710
"""Field with base information."""
811

9-
locations: List
10-
confidence: Optional[str]
12+
locations: List[FieldLocation]
13+
confidence: Optional[FieldConfidence]
14+
15+
def __init__(
16+
self, field_type: FieldType, raw_response: StringDict, indent_level: int = 0
17+
) -> None:
18+
super().__init__(field_type, indent_level)
19+
self._indent_level = indent_level
20+
21+
self.confidence = None
22+
self.locations = []
23+
24+
if "confidence" in raw_response and raw_response["confidence"] is not None:
25+
try:
26+
self.confidence = FieldConfidence(raw_response["confidence"])
27+
except ValueError:
28+
self.confidence = None
29+
30+
if "locations" in raw_response:
31+
self.locations = []
32+
for location in raw_response["locations"]:
33+
self.locations.append(FieldLocation(location))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from enum import Enum
2+
3+
4+
class FieldConfidence(str, Enum):
5+
"""Confidence level of a field as returned by the V2 API."""
6+
7+
CERTAIN = "Certain"
8+
HIGH = "High"
9+
MEDIUM = "Medium"
10+
LOW = "Low"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Optional
2+
3+
from mindee.geometry import Polygon
4+
from mindee.parsing.common.string_dict import StringDict
5+
6+
7+
class FieldLocation:
8+
"""Location of a field."""
9+
10+
def __init__(self, server_response: StringDict) -> None:
11+
"""
12+
Initialize FieldLocation from server response.
13+
14+
:param server_response: Raw server response.
15+
"""
16+
self.polygon: Optional[Polygon] = None
17+
self.page: Optional[int] = None
18+
19+
if "polygon" in server_response and server_response["polygon"] is not None:
20+
self.polygon = Polygon(server_response["polygon"])
21+
22+
if "page" in server_response and isinstance(server_response["page"], int):
23+
self.page = server_response["page"]
24+
25+
def __str__(self) -> str:
26+
"""
27+
String representation.
28+
29+
:return: String representation of the field location.
30+
"""
31+
return str(self.polygon) if self.polygon else ""

mindee/parsing/v2/field/list_field.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
from typing import List
22

33
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.parsing.v2.field.base_field import BaseField
45
from mindee.parsing.v2.field.dynamic_field import (
56
DynamicField,
67
FieldType,
78
get_field_type,
89
)
910

1011

11-
class ListField(DynamicField):
12+
class ListField(BaseField):
1213
"""List field containing multiple fields."""
1314

1415
items: List[DynamicField]
1516
"""Items contained in the list."""
1617

1718
def __init__(self, raw_response: StringDict, indent_level: int = 0):
18-
super().__init__(FieldType.LIST, indent_level)
19+
super().__init__(FieldType.LIST, raw_response, indent_level)
1920

2021
self.items = []
2122
for item in raw_response["items"]:

mindee/parsing/v2/field/object_field.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ObjectField(BaseField):
1111
"""Fields contained in the object."""
1212

1313
def __init__(self, raw_response: StringDict, indent_level: int = 0):
14-
super().__init__(FieldType.OBJECT, indent_level)
14+
super().__init__(FieldType.OBJECT, raw_response, indent_level)
1515
inner_fields = raw_response.get("fields", raw_response)
1616

1717
self.fields = InferenceResultFields(inner_fields, self._indent_level + 1)

mindee/parsing/v2/field/simple_field.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class SimpleField(BaseField):
1111
value: Union[str, float, bool, None]
1212

1313
def __init__(self, raw_response: StringDict, indent_level: int = 0):
14-
super().__init__(FieldType.SIMPLE, indent_level)
14+
super().__init__(FieldType.SIMPLE, raw_response, indent_level)
1515
value = raw_response.get("value", None)
1616
if isinstance(value, int) and not isinstance(raw_response.get("value"), bool):
1717
self.value = float(value)

tests/data

tests/v2/test_inference_response.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66

7+
from mindee.parsing.v2.field.field_confidence import FieldConfidence
78
from mindee.parsing.v2.field.list_field import ListField
89
from mindee.parsing.v2.field.object_field import ObjectField
910
from mindee.parsing.v2.field.simple_field import SimpleField
@@ -185,3 +186,42 @@ def test_full_inference_response():
185186
assert inference_result.inference.file.mime_type == "image/jpeg"
186187
assert not inference_result.inference.file.alias
187188
assert not inference_result.inference.result.options
189+
190+
191+
@pytest.mark.v2
192+
def test_field_locations_and_confidence() -> None:
193+
"""
194+
Validate that the first location polygon for the ``date`` field is correctly
195+
deserialized together with the associated confidence level.
196+
"""
197+
json_sample, _ = _get_product_samples(
198+
"financial_document", "complete_with_coordinates"
199+
)
200+
201+
inference_result = InferenceResponse(json_sample)
202+
203+
date_field: SimpleField = inference_result.inference.result.fields.date
204+
205+
assert date_field.locations, "date field should expose locations"
206+
loc0 = date_field.locations[0]
207+
assert loc0 is not None
208+
assert loc0.page == 0
209+
210+
polygon = loc0.polygon
211+
assert polygon is not None
212+
assert len(polygon[0]) == 2
213+
214+
assert polygon[0][0] == 0.948979073166918
215+
assert polygon[0][1] == 0.23097924535067715
216+
217+
assert polygon[1][0] == 0.85422
218+
assert polygon[1][1] == 0.230072
219+
220+
assert polygon[2][0] == 0.8540899268330819
221+
assert polygon[2][1] == 0.24365775464932288
222+
223+
assert polygon[3][0] == 0.948849
224+
assert polygon[3][1] == 0.244565
225+
226+
assert date_field.confidence == FieldConfidence.MEDIUM
227+
assert str(date_field.confidence.value) == "Medium"

0 commit comments

Comments
 (0)