Skip to content

Commit c1d2402

Browse files
committed
🎨 regenerate receipt v5
1 parent d23cb9d commit c1d2402

File tree

3 files changed

+176
-120
lines changed

3 files changed

+176
-120
lines changed

mindee/documents/receipt/line_item_v5.py

Lines changed: 0 additions & 58 deletions
This file was deleted.
Lines changed: 99 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from typing import List, Optional, TypeVar
22

33
from mindee.documents.base import Document, TypeApiPrediction, clean_out_string
4-
from mindee.documents.receipt.line_item_v5 import ReceiptV5LineItem
54
from mindee.fields.amount import AmountField
65
from mindee.fields.classification import ClassificationField
76
from mindee.fields.company_registration import CompanyRegistrationField
@@ -10,40 +9,44 @@
109
from mindee.fields.tax import Taxes
1110
from mindee.fields.text import TextField
1211

12+
from .receipt_v5_line_item import ReceiptV5LineItem
13+
1314

1415
class ReceiptV5(Document):
15-
locale: LocaleField
16-
"""locale information"""
17-
total_amount: AmountField
18-
"""The total amount paid including taxes, discounts, fees, tips, and gratuity."""
19-
date: DateField
20-
"""The date the purchase was made."""
21-
time: TextField
22-
"""Time of purchase with 24 hours formatting (HH:MM)."""
16+
"""Receipt v5 prediction results."""
17+
2318
category: ClassificationField
2419
"""The receipt category among predefined classes."""
25-
subcategory: ClassificationField
26-
"""The receipt sub category among predefined classes for transport and food."""
20+
date: DateField
21+
"""The date the purchase was made."""
2722
document_type: ClassificationField
2823
"""Whether the document is an expense receipt or a credit card receipt."""
29-
supplier_name: TextField
30-
"""The name of the supplier or merchant."""
31-
supplier_phone_number: TextField
32-
"""The Phone number of the supplier or merchant."""
24+
line_items: List[ReceiptV5LineItem]
25+
"""Full extraction of lines, including: description, quantity, unit price and total."""
26+
locale: LocaleField
27+
"""The locale identifier in BCP 47 (RFC 5646) format: ISO language code, '-', ISO country code."""
28+
subcategory: ClassificationField
29+
"""The receipt sub category among predefined classes for transport and food."""
3330
supplier_address: TextField
34-
"""The address of the supplier or merchant."""
31+
"""The address of the supplier or merchant returned as a single string."""
3532
supplier_company_registrations: List[CompanyRegistrationField]
3633
"""List of supplier company registrations or identifiers."""
34+
supplier_name: TextField
35+
"""The name of the supplier or merchant."""
36+
supplier_phone_number: TextField
37+
"""The Phone number of the supplier or merchant returned as a single string."""
3738
taxes: Taxes
3839
"""List of tax lines information including: Amount, tax rate, tax base amount and tax code."""
39-
total_tax: AmountField
40-
"""The total amount of taxes."""
41-
total_net: AmountField
42-
"""The total amount excluding taxes."""
40+
time: TextField
41+
"""Time of purchase with 24 hours formatting (HH:MM)."""
4342
tip: AmountField
4443
"""The total amount of tip and gratuity."""
45-
line_items: List[ReceiptV5LineItem]
46-
"""Full extraction of lines, including: description, quantity, unit price and total."""
44+
total_amount: AmountField
45+
"""The total amount paid including taxes, discounts, fees, tips, and gratuity."""
46+
total_net: AmountField
47+
"""The total amount excluding taxes."""
48+
total_tax: AmountField
49+
"""The total amount of taxes."""
4750

4851
def __init__(
4952
self,
@@ -52,7 +55,7 @@ def __init__(
5255
page_n: Optional[int] = None,
5356
):
5457
"""
55-
Receipt document.
58+
Receipt v5 prediction results.
5659
5760
:param api_prediction: Raw prediction from HTTP response
5861
:param input_source: Input object
@@ -70,67 +73,104 @@ def _build_from_api_prediction(
7073
self, api_prediction: TypeApiPrediction, page_n: Optional[int] = None
7174
) -> None:
7275
"""
73-
Build the document from an API response JSON.
76+
Build the object from the prediction API JSON.
7477
7578
:param api_prediction: Raw prediction from HTTP response
76-
:param page_n: Page number for multi pages pdf input
79+
:param page_n: Page number
7780
"""
78-
self.locale = LocaleField(api_prediction["locale"], page_id=page_n)
79-
self.total_amount = AmountField(api_prediction["total_amount"], page_id=page_n)
80-
self.total_net = AmountField(api_prediction["total_net"], page_id=page_n)
81-
self.total_tax = AmountField(api_prediction["total_tax"], page_id=page_n)
82-
self.tip = AmountField(api_prediction["tip"], page_id=page_n)
83-
self.date = DateField(api_prediction["date"], page_id=page_n)
84-
self.category = ClassificationField(api_prediction["category"], page_id=page_n)
85-
self.subcategory = ClassificationField(
86-
api_prediction["subcategory"], page_id=page_n
81+
self.category = ClassificationField(
82+
api_prediction["category"],
83+
page_id=page_n,
84+
)
85+
self.date = DateField(
86+
api_prediction["date"],
87+
page_id=page_n,
8788
)
8889
self.document_type = ClassificationField(
89-
api_prediction["document_type"], page_id=page_n
90+
api_prediction["document_type"],
91+
page_id=page_n,
9092
)
91-
self.supplier_name = TextField(
92-
api_prediction["supplier_name"], value_key="value", page_id=page_n
93+
self.line_items = [
94+
ReceiptV5LineItem(prediction, page_id=page_n)
95+
for prediction in api_prediction["line_items"]
96+
]
97+
self.locale = LocaleField(
98+
api_prediction["locale"],
99+
page_id=page_n,
93100
)
94-
self.supplier_phone_number = TextField(
95-
api_prediction["supplier_phone_number"], value_key="value", page_id=page_n
101+
self.subcategory = ClassificationField(
102+
api_prediction["subcategory"],
103+
page_id=page_n,
96104
)
97105
self.supplier_address = TextField(
98-
api_prediction["supplier_address"], value_key="value", page_id=page_n
106+
api_prediction["supplier_address"],
107+
page_id=page_n,
99108
)
100109
self.supplier_company_registrations = [
101-
CompanyRegistrationField(field_dict, page_id=page_n)
102-
for field_dict in api_prediction["supplier_company_registrations"]
110+
CompanyRegistrationField(prediction, page_id=page_n)
111+
for prediction in api_prediction["supplier_company_registrations"]
103112
]
104-
self.time = TextField(api_prediction["time"], value_key="value", page_id=page_n)
113+
self.supplier_name = TextField(
114+
api_prediction["supplier_name"],
115+
page_id=page_n,
116+
)
117+
self.supplier_phone_number = TextField(
118+
api_prediction["supplier_phone_number"],
119+
page_id=page_n,
120+
)
105121
self.taxes = Taxes(api_prediction["taxes"], page_id=page_n)
106-
self.line_items = [
107-
ReceiptV5LineItem(prediction=line_item, page_n=page_n)
108-
for line_item in api_prediction["line_items"]
109-
]
122+
self.time = TextField(
123+
api_prediction["time"],
124+
page_id=page_n,
125+
)
126+
self.tip = AmountField(
127+
api_prediction["tip"],
128+
page_id=page_n,
129+
)
130+
self.total_amount = AmountField(
131+
api_prediction["total_amount"],
132+
page_id=page_n,
133+
)
134+
self.total_net = AmountField(
135+
api_prediction["total_net"],
136+
page_id=page_n,
137+
)
138+
self.total_tax = AmountField(
139+
api_prediction["total_tax"],
140+
page_id=page_n,
141+
)
110142

111143
@staticmethod
112-
def _line_items_separator(char: str):
144+
def _line_items_separator(char: str) -> str:
113145
out_str = " "
114146
out_str += f"+{char * 38}"
115147
out_str += f"+{char * 10}"
116148
out_str += f"+{char * 14}"
117149
out_str += f"+{char * 12}"
118150
return out_str + "+"
119151

152+
def _line_items_to_str(self) -> str:
153+
if not self.line_items:
154+
return ""
155+
156+
lines = f"\n{self._line_items_separator('-')}\n ".join(
157+
[item.to_table_line() for item in self.line_items]
158+
)
159+
out_str = ""
160+
out_str += f"\n{self._line_items_separator('-')}\n "
161+
out_str += " | Description "
162+
out_str += " | Quantity"
163+
out_str += " | Total Amount"
164+
out_str += " | Unit Price"
165+
out_str += f" |\n{self._line_items_separator('=')}"
166+
out_str += f"\n {lines}"
167+
out_str += f"\n{self._line_items_separator('-')}"
168+
return out_str
169+
120170
def __str__(self) -> str:
121171
supplier_company_registrations = "; ".join(
122172
[str(n.value) for n in self.supplier_company_registrations]
123173
)
124-
line_items = "\n"
125-
if self.line_items:
126-
line_items = (
127-
f"\n{self._line_items_separator('-')}"
128-
"\n | Description | Quantity | Total Amount | Unit Price |"
129-
f"\n{self._line_items_separator('=')}"
130-
)
131-
for item in self.line_items:
132-
line_items += f"\n {item}\n{self._line_items_separator('-')}"
133-
134174
return clean_out_string(
135175
"Receipt V5 Prediction\n"
136176
"=====================\n"
@@ -150,11 +190,8 @@ def __str__(self) -> str:
150190
f":Supplier Company Registrations: {supplier_company_registrations}\n"
151191
f":Supplier Address: {self.supplier_address}\n"
152192
f":Supplier Phone Number: {self.supplier_phone_number}\n"
153-
f":Line Items: {line_items}\n"
193+
f":Line Items: {self._line_items_to_str()}\n"
154194
)
155195

156-
def _checklist(self) -> None:
157-
pass
158-
159196

160197
TypeReceiptV5 = TypeVar("TypeReceiptV5", bound=ReceiptV5)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from typing import Dict, Optional
2+
3+
from mindee.fields.base import (
4+
FieldConfidenceMixin,
5+
FieldPositionMixin,
6+
TypePrediction,
7+
float_to_string,
8+
to_opt_float,
9+
)
10+
11+
12+
class ReceiptV5LineItem(FieldPositionMixin, FieldConfidenceMixin):
13+
"""Full extraction of lines, including: description, quantity, unit price and total."""
14+
15+
description: Optional[str]
16+
"""The item description."""
17+
quantity: Optional[float]
18+
"""The item quantity."""
19+
total_amount: Optional[float]
20+
"""The item total amount."""
21+
unit_price: Optional[float]
22+
"""The item unit price."""
23+
confidence: float = 0.0
24+
"""Confidence score"""
25+
page_n: int
26+
"""The document page on which the information was found."""
27+
28+
def __init__(
29+
self,
30+
prediction: TypePrediction,
31+
page_id: Optional[int] = None,
32+
):
33+
self._set_confidence(prediction)
34+
self._set_position(prediction)
35+
36+
if page_id is None:
37+
try:
38+
self.page_n = prediction["page_id"]
39+
except KeyError:
40+
pass
41+
else:
42+
self.page_n = page_id
43+
44+
self.description = prediction["description"]
45+
self.quantity = to_opt_float(prediction, "quantity")
46+
self.total_amount = to_opt_float(prediction, "total_amount")
47+
self.unit_price = to_opt_float(prediction, "unit_price")
48+
49+
def _printable_values(self) -> Dict[str, str]:
50+
"""Return values for printing."""
51+
return {
52+
"description": self.description if self.description is not None else "",
53+
"quantity": float_to_string(self.quantity),
54+
"total_amount": float_to_string(self.total_amount),
55+
"unit_price": float_to_string(self.unit_price),
56+
}
57+
58+
def to_table_line(self) -> str:
59+
"""Output in a format suitable for inclusion in an rST table."""
60+
printable = self._printable_values()
61+
return (
62+
"|"
63+
f" {printable['description']:<36} |"
64+
f" {printable['quantity']:<8} |"
65+
f" {printable['total_amount']:<12} |"
66+
f" {printable['unit_price']:<10} |"
67+
)
68+
69+
def __str__(self) -> str:
70+
"""Default string representation."""
71+
printable = self._printable_values()
72+
return (
73+
f"Description: {printable['description']}, "
74+
f"Quantity: {printable['quantity']}, "
75+
f"Total Amount: {printable['total_amount']}, "
76+
f"Unit Price: {printable['unit_price']}, "
77+
).strip()

0 commit comments

Comments
 (0)