Skip to content

Commit d16e96e

Browse files
authored
🚧 rework classes to use dynamic fields (#337)
1 parent 0261d2f commit d16e96e

15 files changed

+243
-184
lines changed

docs/extras/code_samples/default_v2.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from mindee import ClientV2, InferencePredictOptions
2-
from mindee.parsing.v2 import InferenceResponse
32

43
input_path = "/path/to/the/file.ext"
54
api_key = "MY_API_KEY"
@@ -20,9 +19,9 @@ options = InferencePredictOptions(
2019
input_doc = mindee_client.source_from_path(input_path)
2120

2221
# Upload the file
23-
response: InferenceResponse = mindee_client.enqueue_and_parse(
22+
response = mindee_client.enqueue_and_parse(
2423
input_doc, options
2524
)
2625

2726
# Print a brief summary of the parsed data
28-
print(response.inference)
27+
print(response.inference)

mindee/parsing/v2/__init__.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +0,0 @@
1-
from mindee.parsing.v2.base_field import (
2-
InferenceFields,
3-
ListField,
4-
ObjectField,
5-
SimpleField,
6-
)
7-
from mindee.parsing.v2.common_response import CommonResponse
8-
from mindee.parsing.v2.error_response import ErrorResponse
9-
from mindee.parsing.v2.inference import Inference
10-
from mindee.parsing.v2.inference_file import InferenceFile
11-
from mindee.parsing.v2.inference_model import InferenceModel
12-
from mindee.parsing.v2.inference_options import InferenceOptions
13-
from mindee.parsing.v2.inference_response import InferenceResponse
14-
from mindee.parsing.v2.inference_result import InferenceResult
15-
from mindee.parsing.v2.job import Job
16-
from mindee.parsing.v2.job_response import JobResponse

mindee/parsing/v2/base_field.py

Lines changed: 6 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,10 @@
1-
from typing import Dict, List, Union
1+
from typing import List, Optional
22

3-
from mindee.error.mindee_error import MindeeApiV2Error
4-
from mindee.parsing.common.string_dict import StringDict
3+
from mindee.parsing.v2.dynamic_field import DynamicField
54

65

7-
class BaseField:
8-
"""Base field class for V2."""
6+
class BaseField(DynamicField):
7+
"""Field with base information."""
98

10-
_indent_level: int
11-
"""Indentation level for rst display."""
12-
13-
def __init__(self, indent_level=0) -> None:
14-
self._indent_level = indent_level
15-
16-
@staticmethod
17-
def create_field(
18-
raw_response: StringDict, indent_level: int = 0
19-
) -> Union["ListField", "ObjectField", "SimpleField"]:
20-
"""Factory function to create appropriate field instances."""
21-
if isinstance(raw_response, dict):
22-
if "items" in raw_response:
23-
return ListField(raw_response, indent_level)
24-
if "fields" in raw_response:
25-
return ObjectField(raw_response, indent_level)
26-
if "value" in raw_response:
27-
return SimpleField(raw_response, indent_level)
28-
raise MindeeApiV2Error(f"Unrecognized field format in {raw_response}.")
29-
raise MindeeApiV2Error(f"Unrecognized field format {raw_response}.")
30-
31-
32-
class InferenceFields(Dict[str, Union["SimpleField", "ObjectField", "ListField"]]):
33-
"""Inference fields dict."""
34-
35-
def __init__(self, raw_response: StringDict, indent_level: int = 0) -> None:
36-
super().__init__()
37-
for key, value in raw_response.items():
38-
field_obj = BaseField.create_field(value, indent_level)
39-
self[key] = field_obj
40-
41-
def __getattr__(self, item):
42-
try:
43-
return self[item]
44-
except KeyError:
45-
raise AttributeError(item) from None
46-
47-
def __str__(self) -> str:
48-
str_fields = ""
49-
for field_key, field_value in self.items():
50-
str_fields += f":{field_key}: {field_value}"
51-
return str_fields
52-
53-
54-
class ListField(BaseField):
55-
"""List field containing multiple fields."""
56-
57-
items: List[Union["ListField", "ObjectField", "SimpleField"]]
58-
"""Items contained in the list."""
59-
60-
def __init__(self, raw_response: StringDict, indent_level: int = 0):
61-
super().__init__(indent_level)
62-
63-
self.items = []
64-
for item in raw_response["items"]:
65-
if isinstance(item, dict):
66-
self.items.append(BaseField.create_field(item, self._indent_level + 2))
67-
else:
68-
raise MindeeApiV2Error(f"Unrecognized field format '{item}'.")
69-
70-
def __str__(self) -> str:
71-
out_str = ""
72-
for item in self.items:
73-
out_str += f"* {str(item)[2:] if item else ''}"
74-
return "\n" + out_str if out_str else ""
75-
76-
77-
class ObjectField(BaseField):
78-
"""Object field containing multiple fields."""
79-
80-
fields: InferenceFields
81-
"""Fields contained in the object."""
82-
83-
def __init__(self, raw_response: StringDict, indent_level: int = 0):
84-
super().__init__(indent_level)
85-
inner_fields = raw_response.get("fields", raw_response)
86-
87-
self.fields = InferenceFields(inner_fields, self._indent_level + 1)
88-
89-
def __str__(self) -> str:
90-
out_str = ""
91-
for field_key, field_value in self.fields.items():
92-
if isinstance(field_value, ListField):
93-
value_str = ""
94-
if len(field_value.items) > 0:
95-
value_str = (
96-
" " * self._indent_level + str(field_value)
97-
if field_value
98-
else ""
99-
)
100-
out_str += f"{' ' * self._indent_level}:{field_key}: {value_str}"
101-
else:
102-
out_str += f"{' ' * self._indent_level}:{field_key}: {field_value if field_value else ''}"
103-
return out_str
104-
105-
106-
class SimpleField(BaseField):
107-
"""Simple field containing a single value."""
108-
109-
value: Union[str, float, bool, None]
110-
111-
def __init__(self, raw_response: StringDict, indent_level: int = 0):
112-
super().__init__(indent_level)
113-
self.value = raw_response["value"] = raw_response.get("value", None)
114-
115-
def __str__(self) -> str:
116-
return f"{self.value}\n" if self.value else "\n"
9+
locations: List
10+
confidence: Optional[str]

mindee/parsing/v2/dynamic_field.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from enum import Enum
2+
from importlib import import_module
3+
4+
from mindee.error import MindeeApiV2Error
5+
from mindee.parsing.common.string_dict import StringDict
6+
7+
8+
class FieldType(str, Enum):
9+
"""Field types."""
10+
11+
OBJECT = "ObjectField"
12+
LIST = "ListField"
13+
SIMPLE = "SimpleField"
14+
15+
16+
class DynamicField:
17+
"""Field that can be displayed in rst format."""
18+
19+
_indent_level: int
20+
"""Indentation level for rst display."""
21+
field_type: FieldType
22+
"""Field type."""
23+
24+
def __init__(self, field_type: FieldType, indent_level=0) -> None:
25+
self.field_type = field_type
26+
self._indent_level = indent_level
27+
28+
def multi_str(self) -> str:
29+
"""String representation of the field in a list."""
30+
return str(self)
31+
32+
33+
def get_field_type(raw_response: StringDict, indent_level: int = 0) -> DynamicField:
34+
"""Get appropriate field types."""
35+
if isinstance(raw_response, dict):
36+
if "value" in raw_response:
37+
field_file = import_module("mindee.parsing.v2.simple_field")
38+
field_class = getattr(field_file, FieldType.SIMPLE.value)
39+
elif "items" in raw_response:
40+
field_file = import_module("mindee.parsing.v2.list_field")
41+
field_class = getattr(field_file, FieldType.LIST.value)
42+
elif "fields" in raw_response:
43+
field_file = import_module("mindee.parsing.v2.object_field")
44+
field_class = getattr(field_file, FieldType.OBJECT.value)
45+
else:
46+
raise MindeeApiV2Error(f"Unrecognized field format in {raw_response}.")
47+
return field_class(raw_response, indent_level)
48+
49+
raise MindeeApiV2Error(f"Unrecognized field format {raw_response}.")

mindee/parsing/v2/inference.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ def __init__(self, raw_response: StringDict):
2525
self.id = raw_response["id"] if "id" in raw_response else None
2626

2727
def __str__(self) -> str:
28+
alias = f" {self.file.alias}" if self.file.alias else ""
2829
return (
29-
f"Inference\n"
30-
f"#########\n"
31-
f":Model: {self.model.id}\n"
32-
f":File:\n"
33-
f" :Name: {self.file.name}\n"
34-
f" :Alias: {self.file.alias}\n\n"
35-
f"Result\n"
36-
f"======\n"
37-
f"{self.result}\n"
30+
f"Inference\n#########"
31+
f"\nModel\n====="
32+
f"\n:ID: {self.model.id}"
33+
f"\n\nFile\n===="
34+
f"\n:Name: {self.file.name}"
35+
f"\n:Alias:{alias}"
36+
f"{self.result}"
37+
"\n"
3838
)

mindee/parsing/v2/inference_response.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ class InferenceResponse(CommonResponse):
1212
def __init__(self, raw_response: StringDict) -> None:
1313
super().__init__(raw_response)
1414
self.inference = Inference(raw_response["inference"])
15+
16+
def __str__(self) -> str:
17+
return str(self.inference)

mindee/parsing/v2/inference_result.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
from typing import Optional
22

33
from mindee.parsing.common.string_dict import StringDict
4-
from mindee.parsing.v2.base_field import InferenceFields
5-
from mindee.parsing.v2.inference_options import InferenceOptions
4+
from mindee.parsing.v2.inference_result_fields import InferenceResultFields
5+
from mindee.parsing.v2.inference_result_options import InferenceResultOptions
66

77

88
class InferenceResult:
99
"""Inference result info."""
1010

11-
fields: InferenceFields
11+
fields: InferenceResultFields
1212
"""Fields contained in the inference."""
13-
options: Optional[InferenceOptions]
13+
options: Optional[InferenceResultOptions]
1414
"""Potential options retrieved alongside the inference."""
1515

1616
def __init__(self, raw_response: StringDict) -> None:
17-
self.fields = InferenceFields(raw_response["fields"])
17+
self.fields = InferenceResultFields(raw_response["fields"])
1818
self.options = (
19-
InferenceOptions(raw_response["options"])
19+
InferenceResultOptions(raw_response["options"])
2020
if raw_response.get("options")
2121
else None
2222
)
2323

2424
def __str__(self) -> str:
25-
out_str = f":fields: {self.fields}"
25+
out_str = f"\n\nFields\n======{self.fields}"
2626
if self.options:
27-
out_str += f"\n:options: {self.options}"
27+
out_str += f"\n\nOptions\n====={self.options}"
2828
return out_str
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import Dict
2+
3+
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.parsing.v2.dynamic_field import DynamicField, FieldType, get_field_type
5+
6+
7+
class InferenceResultFields(Dict[str, DynamicField]):
8+
"""Inference fields dict."""
9+
10+
def __init__(self, raw_response: StringDict, indent_level: int = 0) -> None:
11+
super().__init__()
12+
for key, value in raw_response.items():
13+
field_obj = get_field_type(value, indent_level)
14+
self[key] = field_obj
15+
16+
def __getattr__(self, item):
17+
try:
18+
return self[item]
19+
except KeyError:
20+
raise AttributeError(item) from None
21+
22+
def __str__(self) -> str:
23+
str_fields = ""
24+
for field_key, field_value in self.items():
25+
if field_value.field_type == FieldType.SIMPLE:
26+
str_fields += f"\n:{field_key}: {field_value}"
27+
else:
28+
str_fields += f"\n:{field_key}:{field_value}"
29+
return str_fields

mindee/parsing/v2/inference_options.py renamed to mindee/parsing/v2/inference_result_options.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from mindee.parsing.v2.raw_text import RawText
55

66

7-
class InferenceOptions:
7+
class InferenceResultOptions:
88
"""Optional information about the document."""
99

1010
raw_texts: List[RawText]

mindee/parsing/v2/list_field.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import List
2+
3+
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.parsing.v2.dynamic_field import DynamicField, FieldType, get_field_type
5+
6+
7+
class ListField(DynamicField):
8+
"""List field containing multiple fields."""
9+
10+
items: List[DynamicField]
11+
"""Items contained in the list."""
12+
13+
def __init__(self, raw_response: StringDict, indent_level: int = 0):
14+
super().__init__(FieldType.LIST, indent_level)
15+
16+
self.items = []
17+
for item in raw_response["items"]:
18+
self.items.append(get_field_type(item))
19+
20+
def __str__(self) -> str:
21+
out_str = ""
22+
indent = " " * self._indent_level
23+
for item in self.items:
24+
if item.field_type == FieldType.SIMPLE:
25+
out_str += f"\n{indent} * {item}"
26+
elif item.field_type == FieldType.OBJECT:
27+
out_str += f"\n{indent} * {item.multi_str()}"
28+
29+
return out_str

0 commit comments

Comments
 (0)