mindee 2.0.0 → 2.1.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.yardopts +0 -1
- data/CHANGELOG.md +15 -0
- data/README.md +13 -9
- data/Rakefile +9 -1
- data/bin/mindee.rb +8 -0
- data/docs/code_samples/bank_account_details_v1.txt +14 -0
- data/docs/code_samples/bank_check_v1.txt +14 -0
- data/docs/code_samples/carte_vitale_v1.txt +14 -0
- data/docs/code_samples/custom_v1.txt +24 -0
- data/docs/code_samples/default.txt +16 -0
- data/docs/code_samples/expense_receipts_v4.txt +14 -0
- data/docs/code_samples/financial_document_v1.txt +14 -0
- data/docs/code_samples/idcard_fr_v1.txt +14 -0
- data/docs/code_samples/invoices_v4.txt +14 -0
- data/docs/code_samples/license_plates_v1.txt +14 -0
- data/docs/code_samples/passport_v1.txt +14 -0
- data/docs/code_samples/proof_of_address_v1.txt +14 -0
- data/docs/code_samples/shipping_containers_v1.txt +14 -0
- data/docs/ruby-api-builder.md +35 -42
- data/docs/ruby-getting-started.md +2 -2
- data/docs/ruby-invoice-ocr.md +12 -13
- data/docs/ruby-passport-ocr.md +1 -1
- data/docs/ruby-receipt-ocr.md +1 -1
- data/lib/mindee/client.rb +34 -30
- data/lib/mindee/document_config.rb +15 -23
- data/lib/mindee/parsing/prediction/financial_document/financial_document_v1.rb +245 -0
- data/lib/mindee/parsing/prediction/financial_document/invoice_line_item.rb +58 -0
- data/lib/mindee/parsing/prediction/passport/passport_v1.rb +0 -63
- data/lib/mindee/parsing/prediction/proof_of_address/proof_of_address_v1.rb +80 -0
- data/lib/mindee/parsing/prediction/receipt/receipt_v4.rb +3 -0
- data/lib/mindee/parsing/prediction.rb +2 -0
- data/lib/mindee/version.rb +1 -1
- data/mindee.gemspec +2 -3
- metadata +24 -22
data/lib/mindee/client.rb
CHANGED
@@ -23,8 +23,8 @@ module Mindee
|
|
23
23
|
# API Builder. Do not set for standard (off the shelf) endpoints.
|
24
24
|
#
|
25
25
|
# @param account_name [String] For custom endpoints, your account or organization username on the API Builder.
|
26
|
-
# This is normally not required unless you have a custom endpoint which has the
|
27
|
-
#
|
26
|
+
# This is normally not required unless you have a custom endpoint which has the same name as a
|
27
|
+
# standard (off the shelf) endpoint.
|
28
28
|
# Do not set for standard (off the shelf) endpoints.
|
29
29
|
#
|
30
30
|
# @param include_words [Boolean] Whether to include the full text for each page.
|
@@ -121,7 +121,7 @@ module Mindee
|
|
121
121
|
)
|
122
122
|
@doc_configs[[account_name, endpoint_name]] = DocumentConfig.new(
|
123
123
|
Prediction::CustomV1,
|
124
|
-
|
124
|
+
HTTP::CustomEndpoint.new(account_name, endpoint_name, version, @api_key)
|
125
125
|
)
|
126
126
|
self
|
127
127
|
end
|
@@ -163,42 +163,46 @@ module Mindee
|
|
163
163
|
|
164
164
|
private
|
165
165
|
|
166
|
+
def standard_document_config(prediction_class, endpoint_name, version)
|
167
|
+
DocumentConfig.new(
|
168
|
+
prediction_class,
|
169
|
+
HTTP::StandardEndpoint.new(endpoint_name, version, @api_key)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
166
173
|
def init_default_endpoints
|
167
|
-
@doc_configs[['mindee', Prediction::
|
168
|
-
Prediction::
|
169
|
-
|
174
|
+
@doc_configs[['mindee', Prediction::ProofOfAddressV1.name]] = standard_document_config(
|
175
|
+
Prediction::ProofOfAddressV1, 'proof_of_address', '1'
|
176
|
+
)
|
177
|
+
@doc_configs[['mindee', Prediction::FinancialDocumentV1.name]] = standard_document_config(
|
178
|
+
Prediction::FinancialDocumentV1, 'financial_document', '1'
|
179
|
+
)
|
180
|
+
@doc_configs[['mindee', Prediction::InvoiceV4.name]] = standard_document_config(
|
181
|
+
Prediction::InvoiceV4, 'invoices', '4'
|
170
182
|
)
|
171
|
-
@doc_configs[['mindee', Prediction::ReceiptV4.name]] =
|
172
|
-
Prediction::ReceiptV4,
|
173
|
-
[HTTP::StandardEndpoint.new('expense_receipts', '4', @api_key)]
|
183
|
+
@doc_configs[['mindee', Prediction::ReceiptV4.name]] = standard_document_config(
|
184
|
+
Prediction::ReceiptV4, 'expense_receipts', '4'
|
174
185
|
)
|
175
|
-
@doc_configs[['mindee', Prediction::PassportV1.name]] =
|
176
|
-
Prediction::PassportV1,
|
177
|
-
[HTTP::StandardEndpoint.new('passport', '1', @api_key)]
|
186
|
+
@doc_configs[['mindee', Prediction::PassportV1.name]] = standard_document_config(
|
187
|
+
Prediction::PassportV1, 'passport', '1'
|
178
188
|
)
|
179
|
-
@doc_configs[['mindee', Prediction::EU::LicensePlateV1.name]] =
|
180
|
-
Prediction::EU::LicensePlateV1,
|
181
|
-
[HTTP::StandardEndpoint.new('license_plates', '1', @api_key)]
|
189
|
+
@doc_configs[['mindee', Prediction::EU::LicensePlateV1.name]] = standard_document_config(
|
190
|
+
Prediction::EU::LicensePlateV1, 'license_plates', '1'
|
182
191
|
)
|
183
|
-
@doc_configs[['mindee', Prediction::ShippingContainerV1.name]] =
|
184
|
-
Prediction::ShippingContainerV1,
|
185
|
-
[HTTP::StandardEndpoint.new('shipping_containers', '1', @api_key)]
|
192
|
+
@doc_configs[['mindee', Prediction::ShippingContainerV1.name]] = standard_document_config(
|
193
|
+
Prediction::ShippingContainerV1, 'shipping_containers', '1'
|
186
194
|
)
|
187
|
-
@doc_configs[['mindee', Prediction::US::BankCheckV1.name]] =
|
188
|
-
Prediction::US::BankCheckV1,
|
189
|
-
[HTTP::StandardEndpoint.new('bank_check', '1', @api_key)]
|
195
|
+
@doc_configs[['mindee', Prediction::US::BankCheckV1.name]] = standard_document_config(
|
196
|
+
Prediction::US::BankCheckV1, 'bank_check', '1'
|
190
197
|
)
|
191
|
-
@doc_configs[['mindee', Prediction::FR::BankAccountDetailsV1.name]] =
|
192
|
-
Prediction::FR::BankAccountDetailsV1,
|
193
|
-
[HTTP::StandardEndpoint.new('bank_account_details', '1', @api_key)]
|
198
|
+
@doc_configs[['mindee', Prediction::FR::BankAccountDetailsV1.name]] = standard_document_config(
|
199
|
+
Prediction::FR::BankAccountDetailsV1, 'bank_account_details', '1'
|
194
200
|
)
|
195
|
-
@doc_configs[['mindee', Prediction::FR::CarteVitaleV1.name]] =
|
196
|
-
Prediction::FR::CarteVitaleV1,
|
197
|
-
[HTTP::StandardEndpoint.new('carte_vitale', '1', @api_key)]
|
201
|
+
@doc_configs[['mindee', Prediction::FR::CarteVitaleV1.name]] = standard_document_config(
|
202
|
+
Prediction::FR::CarteVitaleV1, 'carte_vitale', '1'
|
198
203
|
)
|
199
|
-
@doc_configs[['mindee', Prediction::FR::IdCardV1.name]] =
|
200
|
-
Prediction::FR::IdCardV1,
|
201
|
-
[HTTP::StandardEndpoint.new('idcard_fr', '1', @api_key)]
|
204
|
+
@doc_configs[['mindee', Prediction::FR::IdCardV1.name]] = standard_document_config(
|
205
|
+
Prediction::FR::IdCardV1, 'idcard_fr', '1'
|
202
206
|
)
|
203
207
|
self
|
204
208
|
end
|
@@ -11,14 +11,14 @@ module Mindee
|
|
11
11
|
# Specific client for sending a document to the API.
|
12
12
|
class DocumentConfig
|
13
13
|
# Array of possible Mindee::Endpoint to be used.
|
14
|
-
# @return [
|
15
|
-
attr_reader :
|
14
|
+
# @return [Mindee::HTTP::Endpoint]
|
15
|
+
attr_reader :endpoint
|
16
16
|
|
17
17
|
# @param prediction_class [Class<Mindee::Prediction::Prediction>]
|
18
|
-
# @param
|
19
|
-
def initialize(prediction_class,
|
18
|
+
# @param endpoint [Mindee::HTTP::Endpoint]
|
19
|
+
def initialize(prediction_class, endpoint)
|
20
20
|
@prediction_class = prediction_class
|
21
|
-
@
|
21
|
+
@endpoint = endpoint
|
22
22
|
end
|
23
23
|
|
24
24
|
# Call the prediction API.
|
@@ -28,16 +28,8 @@ module Mindee
|
|
28
28
|
# @param cropper [Boolean]
|
29
29
|
# @return [Mindee::DocumentResponse]
|
30
30
|
def predict(input_doc, include_words, close_file, cropper)
|
31
|
-
|
31
|
+
check_api_key
|
32
32
|
response = predict_request(input_doc, include_words, close_file, cropper)
|
33
|
-
parse_response(response)
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
# @param response [Net::HTTPResponse]
|
39
|
-
# @return [Mindee::DocumentResponse]
|
40
|
-
def parse_response(response)
|
41
33
|
hashed_response = JSON.parse(response.body, object_class: Hash)
|
42
34
|
return Document.new(@prediction_class, hashed_response['document']) if (200..299).include?(response.code.to_i)
|
43
35
|
|
@@ -45,24 +37,24 @@ module Mindee
|
|
45
37
|
raise error
|
46
38
|
end
|
47
39
|
|
40
|
+
private
|
41
|
+
|
48
42
|
# @param input_doc [Mindee::InputDocument]
|
49
43
|
# @param include_words [Boolean]
|
50
44
|
# @param close_file [Boolean]
|
51
45
|
# # @param cropper [Boolean]
|
52
46
|
# @return [Net::HTTPResponse]
|
53
47
|
def predict_request(input_doc, include_words, close_file, cropper)
|
54
|
-
@
|
48
|
+
@endpoint.predict_req_post(input_doc, include_words: include_words, close_file: close_file, cropper: cropper)
|
55
49
|
end
|
56
50
|
|
57
|
-
def
|
58
|
-
@
|
59
|
-
next unless endpoint.api_key.nil? || endpoint.api_key.empty?
|
51
|
+
def check_api_key
|
52
|
+
return unless @endpoint.api_key.nil? || @endpoint.api_key.empty?
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
54
|
+
raise "Missing API key for '#{@document_type}', " \
|
55
|
+
"check your Client Configuration.\n" \
|
56
|
+
'You can set this using the ' \
|
57
|
+
"'#{HTTP::API_KEY_ENV_NAME}' environment variable."
|
66
58
|
end
|
67
59
|
end
|
68
60
|
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common_fields'
|
4
|
+
require_relative '../base'
|
5
|
+
require_relative 'invoice_line_item'
|
6
|
+
|
7
|
+
module Mindee
|
8
|
+
module Prediction
|
9
|
+
# Invoice document.
|
10
|
+
class FinancialDocumentV1 < Prediction
|
11
|
+
# Locale information.
|
12
|
+
# @return [Mindee::Locale]
|
13
|
+
attr_reader :locale
|
14
|
+
# The nature of the document.
|
15
|
+
# @return [Mindee::TextField]
|
16
|
+
attr_reader :document_type
|
17
|
+
# The total amount with tax included.
|
18
|
+
# @return [Mindee::AmountField]
|
19
|
+
attr_reader :total_amount
|
20
|
+
# The total amount without the tax value.
|
21
|
+
# @return [Mindee::AmountField]
|
22
|
+
attr_reader :total_net
|
23
|
+
# The total tax.
|
24
|
+
# @return [Mindee::AmountField]
|
25
|
+
attr_reader :total_tax
|
26
|
+
# The creation date of the document.
|
27
|
+
# @return [Mindee::DateField]
|
28
|
+
attr_reader :date
|
29
|
+
# The invoice number.
|
30
|
+
# @return [Mindee::TextField]
|
31
|
+
attr_reader :invoice_number
|
32
|
+
# List of Reference numbers including PO number.
|
33
|
+
# @return [Mindee::TextField]
|
34
|
+
attr_reader :reference_numbers
|
35
|
+
# The due date of the invoice.
|
36
|
+
# @return [Mindee::DateField]
|
37
|
+
attr_reader :due_date
|
38
|
+
# The list of taxes.
|
39
|
+
# @return [Array<Mindee::TaxField>]
|
40
|
+
attr_reader :taxes
|
41
|
+
# The name of the customer.
|
42
|
+
# @return [Mindee::TextField]
|
43
|
+
attr_reader :customer_name
|
44
|
+
# The address of the customer.
|
45
|
+
# @return [Mindee::TextField]
|
46
|
+
attr_reader :customer_address
|
47
|
+
# The company registration information for the customer.
|
48
|
+
# @return [Array<Mindee::CompanyRegistration>]
|
49
|
+
attr_reader :customer_company_registrations
|
50
|
+
# The supplier's name.
|
51
|
+
# @return [Mindee::TextField]
|
52
|
+
attr_reader :supplier_name
|
53
|
+
# The supplier's address.
|
54
|
+
# @return [Mindee::TextField]
|
55
|
+
attr_reader :supplier_address
|
56
|
+
# The supplier's payment information.
|
57
|
+
# @return [Array<Mindee::PaymentDetails>]
|
58
|
+
attr_reader :supplier_payment_details
|
59
|
+
# The supplier's company registration information.
|
60
|
+
# @return [Array<Mindee::CompanyRegistration>]
|
61
|
+
attr_reader :supplier_company_registrations
|
62
|
+
# Line items details.
|
63
|
+
# @return [Array<Mindee::InvoiceLineItem>]
|
64
|
+
attr_reader :line_items
|
65
|
+
# Time as seen on the receipt in HH:MM format.
|
66
|
+
# @return [Mindee::TextField]
|
67
|
+
attr_reader :time
|
68
|
+
# The invoice or receipt category among predefined classes.
|
69
|
+
# @return [Mindee::TextField]
|
70
|
+
attr_reader :category
|
71
|
+
# The invoice or receipt sub-category among predefined classes.
|
72
|
+
# @return [Mindee::TextField]
|
73
|
+
attr_reader :subcategory
|
74
|
+
# A classification field, that can return 4 values:
|
75
|
+
# `EXPENSE RECEIPT`, `CREDIT CARD RECEIPT`, `INVOICE`, `CREDIT NOTE`
|
76
|
+
# @return [Mindee::TextField]
|
77
|
+
attr_reader :document_type # rubocop:todo Lint/DuplicateMethods
|
78
|
+
# Total amount of tip and gratuity. Both typed and handwritten characters are supported.
|
79
|
+
# @return [Mindee::AmountField]
|
80
|
+
attr_reader :tip
|
81
|
+
|
82
|
+
# @param prediction [Hash]
|
83
|
+
# @param page_id [Integer, nil]
|
84
|
+
def initialize(prediction, page_id) # rubocop:todo Metrics/AbcSize
|
85
|
+
super
|
86
|
+
|
87
|
+
@time = TextField.new(prediction['time'], page_id)
|
88
|
+
@category = TextField.new(prediction['category'], page_id)
|
89
|
+
@subcategory = TextField.new(prediction['subcategory'], page_id)
|
90
|
+
@document_type = TextField.new(prediction['document_type'], page_id)
|
91
|
+
@tip = AmountField.new(prediction['tip'], page_id)
|
92
|
+
@locale = Locale.new(prediction['locale'])
|
93
|
+
@document_type = TextField.new(prediction['document_type'], page_id)
|
94
|
+
@total_amount = AmountField.new(prediction['total_amount'], page_id)
|
95
|
+
@total_net = AmountField.new(prediction['total_net'], page_id)
|
96
|
+
@customer_address = TextField.new(prediction['customer_address'], page_id)
|
97
|
+
@customer_name = TextField.new(prediction['customer_name'], page_id)
|
98
|
+
@date = DateField.new(prediction['date'], page_id)
|
99
|
+
@due_date = DateField.new(prediction['due_date'], page_id)
|
100
|
+
@invoice_number = TextField.new(prediction['invoice_number'], page_id)
|
101
|
+
@supplier_name = TextField.new(prediction['supplier_name'], page_id)
|
102
|
+
@supplier_address = TextField.new(prediction['supplier_address'], page_id)
|
103
|
+
|
104
|
+
@reference_numbers = []
|
105
|
+
prediction['reference_numbers'].each do |item|
|
106
|
+
@reference_numbers.push(TextField.new(item, page_id))
|
107
|
+
end
|
108
|
+
@customer_company_registrations = []
|
109
|
+
prediction['customer_company_registrations'].each do |item|
|
110
|
+
@customer_company_registrations.push(CompanyRegistration.new(item, page_id))
|
111
|
+
end
|
112
|
+
@taxes = []
|
113
|
+
prediction['taxes'].each do |item|
|
114
|
+
@taxes.push(TaxField.new(item, page_id))
|
115
|
+
end
|
116
|
+
@supplier_payment_details = []
|
117
|
+
prediction['supplier_payment_details'].each do |item|
|
118
|
+
@supplier_payment_details.push(PaymentDetails.new(item, page_id))
|
119
|
+
end
|
120
|
+
@supplier_company_registrations = []
|
121
|
+
prediction['supplier_company_registrations'].each do |item|
|
122
|
+
@supplier_company_registrations.push(CompanyRegistration.new(item, page_id))
|
123
|
+
end
|
124
|
+
|
125
|
+
@total_tax = AmountField.new(
|
126
|
+
{ value: nil, confidence: 0.0 }, page_id
|
127
|
+
)
|
128
|
+
|
129
|
+
@line_items = []
|
130
|
+
prediction['line_items'].each do |item|
|
131
|
+
@line_items.push(InvoiceLineItem.new(item, page_id))
|
132
|
+
end
|
133
|
+
reconstruct(page_id)
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
customer_company_registrations = @customer_company_registrations.map(&:value).join('; ')
|
138
|
+
supplier_payment_details = @supplier_payment_details.map(&:to_s).join("\n ")
|
139
|
+
supplier_company_registrations = @supplier_company_registrations.map(&:to_s).join('; ')
|
140
|
+
reference_numbers = @reference_numbers.map(&:to_s).join(', ')
|
141
|
+
taxes = @taxes.join("\n ")
|
142
|
+
out_str = String.new
|
143
|
+
out_str << "\n:Document type: #{@document_type}".rstrip
|
144
|
+
out_str << "\n:Category: #{@category}".rstrip
|
145
|
+
out_str << "\n:Subcategory: #{@subcategory}".rstrip
|
146
|
+
out_str << "\n:Locale: #{@locale}".rstrip
|
147
|
+
out_str << "\n:Date: #{@date}".rstrip
|
148
|
+
out_str << "\n:Due date: #{@due_date}".rstrip
|
149
|
+
out_str << "\n:Time: #{@time}".rstrip
|
150
|
+
out_str << "\n:Number: #{@invoice_number}".rstrip
|
151
|
+
out_str << "\n:Reference numbers: #{reference_numbers}".rstrip
|
152
|
+
out_str << "\n:Supplier name: #{@supplier_name}".rstrip
|
153
|
+
out_str << "\n:Supplier address: #{@supplier_address}".rstrip
|
154
|
+
out_str << "\n:Supplier company registrations: #{supplier_company_registrations}".rstrip
|
155
|
+
out_str << "\n:Supplier payment details: #{supplier_payment_details}".rstrip
|
156
|
+
|
157
|
+
out_str << "\n:Customer name: #{@customer_name}".rstrip
|
158
|
+
out_str << "\n:Customer address: #{@customer_address}".rstrip
|
159
|
+
out_str << "\n:Customer company registrations: #{customer_company_registrations}".rstrip
|
160
|
+
|
161
|
+
out_str << "\n:Tip: #{@tip}".rstrip
|
162
|
+
|
163
|
+
out_str << "\n:Taxes: #{taxes}".rstrip
|
164
|
+
out_str << "\n:Total taxes: #{@total_tax}".rstrip
|
165
|
+
out_str << "\n:Total net: #{@total_net}".rstrip
|
166
|
+
out_str << "\n:Total amount: #{@total_amount}".rstrip
|
167
|
+
|
168
|
+
out_str << line_items_to_s
|
169
|
+
|
170
|
+
out_str[1..].to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def line_items_to_s
|
176
|
+
line_item_separator = "#{'=' * 22} #{'=' * 8} #{'=' * 9} #{'=' * 10} #{'=' * 18} #{'=' * 36}"
|
177
|
+
line_items = @line_items.map(&:to_s).join("\n")
|
178
|
+
|
179
|
+
out_str = String.new
|
180
|
+
out_str << "\n\n:Line Items:"
|
181
|
+
|
182
|
+
return out_str if line_items.empty?
|
183
|
+
|
184
|
+
out_str << "\n#{line_item_separator}"
|
185
|
+
out_str << "\nCode QTY Price Amount Tax (Rate) Description"
|
186
|
+
out_str << "\n#{line_item_separator}"
|
187
|
+
out_str << "\n#{line_items}"
|
188
|
+
out_str << "\n#{line_item_separator}"
|
189
|
+
end
|
190
|
+
|
191
|
+
def reconstruct(page_id)
|
192
|
+
construct_total_tax_from_taxes(page_id)
|
193
|
+
return unless page_id.nil?
|
194
|
+
|
195
|
+
construct_total_excl_from_tcc_and_taxes(page_id)
|
196
|
+
construct_total_incl_from_taxes_plus_excl(page_id)
|
197
|
+
construct_total_tax_from_totals(page_id)
|
198
|
+
end
|
199
|
+
|
200
|
+
def construct_total_excl_from_tcc_and_taxes(page_id)
|
201
|
+
return if @total_amount.value.nil? || taxes.empty? || !@total_net.value.nil?
|
202
|
+
|
203
|
+
total_excl = {
|
204
|
+
'value' => @total_amount.value - @taxes.map(&:value).sum,
|
205
|
+
'confidence' => TextField.array_confidence(@taxes) * @total_amount.confidence,
|
206
|
+
}
|
207
|
+
@total_net = AmountField.new(total_excl, page_id, reconstructed: true)
|
208
|
+
end
|
209
|
+
|
210
|
+
def construct_total_incl_from_taxes_plus_excl(page_id)
|
211
|
+
return if @total_net.value.nil? || @taxes.empty? || !@total_amount.value.nil?
|
212
|
+
|
213
|
+
total_incl = {
|
214
|
+
'value' => @taxes.map(&:value).sum + @total_net.value,
|
215
|
+
'confidence' => TextField.array_confidence(@taxes) * @total_net.confidence,
|
216
|
+
}
|
217
|
+
@total_amount = AmountField.new(total_incl, page_id, reconstructed: true)
|
218
|
+
end
|
219
|
+
|
220
|
+
def construct_total_tax_from_taxes(page_id)
|
221
|
+
return if @taxes.empty?
|
222
|
+
|
223
|
+
total_tax = {
|
224
|
+
'value' => @taxes.map(&:value).sum,
|
225
|
+
'confidence' => TextField.array_confidence(@taxes),
|
226
|
+
}
|
227
|
+
return unless total_tax['value'].positive?
|
228
|
+
|
229
|
+
@total_tax = AmountField.new(total_tax, page_id, reconstructed: true)
|
230
|
+
end
|
231
|
+
|
232
|
+
def construct_total_tax_from_totals(page_id)
|
233
|
+
return if !@total_tax.value.nil? || @total_amount.value.nil? || @total_net.value.nil?
|
234
|
+
|
235
|
+
total_tax = {
|
236
|
+
'value' => @total_amount.value - @total_net.value,
|
237
|
+
'confidence' => TextField.array_confidence(@taxes),
|
238
|
+
}
|
239
|
+
return unless total_tax['value'] >= 0
|
240
|
+
|
241
|
+
@total_tax = AmountField.new(total_tax, page_id, reconstructed: true)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common_fields/base'
|
4
|
+
|
5
|
+
module Mindee
|
6
|
+
# Line items for invoices
|
7
|
+
class InvoiceLineItem
|
8
|
+
# @return [String] The product code referring to the item.
|
9
|
+
attr_reader :product_code
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :description
|
12
|
+
# @return [Float]
|
13
|
+
attr_reader :quantity
|
14
|
+
# @return [Float]
|
15
|
+
attr_reader :unit_price
|
16
|
+
# @return [Float]
|
17
|
+
attr_reader :total_amount
|
18
|
+
# @return [Float] The item tax rate percentage.
|
19
|
+
attr_reader :tax_rate
|
20
|
+
# @return [Float]
|
21
|
+
attr_reader :tax_amount
|
22
|
+
# @return [Float]
|
23
|
+
attr_reader :confidence
|
24
|
+
# @return [Integer]
|
25
|
+
attr_reader :page_id
|
26
|
+
# @return [Mindee::Geometry::Quadrilateral]
|
27
|
+
attr_reader :bounding_box
|
28
|
+
# @return [Array<Mindee::Geometry::Polygon>]
|
29
|
+
attr_reader :polygon
|
30
|
+
|
31
|
+
def initialize(prediction, page_id)
|
32
|
+
@product_code = prediction['product_code']
|
33
|
+
@quantity = prediction['quantity']
|
34
|
+
@unit_price = prediction['unit_price']
|
35
|
+
@total_amount = prediction['total_amount']
|
36
|
+
@tax_amount = prediction['tax_amount']
|
37
|
+
@tax_rate = prediction['tax_rate']
|
38
|
+
@description = prediction['description']
|
39
|
+
@page_id = page_id
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
tax = Field.float_to_string(@tax_amount)
|
44
|
+
tax << " (#{Field.float_to_string(@tax_rate)}%)" unless @tax_rate.nil?
|
45
|
+
|
46
|
+
description = @description.nil? ? '' : @description
|
47
|
+
description = "#{description[0..32]}..." if description.size > 35
|
48
|
+
|
49
|
+
out_str = String.new
|
50
|
+
out_str << format('%- 22s', @product_code)
|
51
|
+
out_str << " #{format('%- 8s', Field.float_to_string(@quantity))}"
|
52
|
+
out_str << " #{format('%- 9s', Field.float_to_string(@unit_price))}"
|
53
|
+
out_str << " #{format('%- 10s', Field.float_to_string(@total_amount))}"
|
54
|
+
out_str << " #{format('%- 18s', tax)}"
|
55
|
+
out_str << " #{description}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,19 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'mrz'
|
4
|
-
|
5
3
|
require_relative '../common_fields'
|
6
4
|
require_relative '../base'
|
7
5
|
|
8
|
-
# We need to do this disgusting thing to avoid the following error message:
|
9
|
-
# td3 line one does not match the required format (MRZ::InvalidFormatError)
|
10
|
-
#
|
11
|
-
# See:
|
12
|
-
# https://github.com/streetspotr/mrz/issues/2
|
13
|
-
# https://github.com/streetspotr/mrz/pull/3
|
14
|
-
#
|
15
|
-
MRZ::TD3Parser::FORMAT_ONE = %r{\A(.{2})(.{3})([^<]+)<(.*)\z}.freeze
|
16
|
-
|
17
6
|
module Mindee
|
18
7
|
module Prediction
|
19
8
|
# Passport document.
|
@@ -78,7 +67,6 @@ module Mindee
|
|
78
67
|
end
|
79
68
|
@full_name = construct_full_name(page_id)
|
80
69
|
@mrz = construct_mrz(page_id)
|
81
|
-
check_mrz
|
82
70
|
end
|
83
71
|
|
84
72
|
def to_s
|
@@ -107,57 +95,6 @@ module Mindee
|
|
107
95
|
|
108
96
|
private
|
109
97
|
|
110
|
-
def check_mrz
|
111
|
-
return if @mrz1.value.nil? || @mrz2.value.nil?
|
112
|
-
|
113
|
-
mrz = MRZ.parse([@mrz1.value, @mrz2.value])
|
114
|
-
checks = {
|
115
|
-
mrz_valid: valid_mrz?(mrz),
|
116
|
-
mrz_valid_birth_date: valid_birth_date?(mrz),
|
117
|
-
mrz_valid_expiry_date: valid_expiry_date?(mrz),
|
118
|
-
mrz_valid_id_number: valid_id_number?(mrz),
|
119
|
-
mrz_valid_surname: valid_surname?(mrz),
|
120
|
-
mrz_valid_country: valid_country?(mrz),
|
121
|
-
}
|
122
|
-
@checklist.merge!(checks)
|
123
|
-
end
|
124
|
-
|
125
|
-
def valid_mrz?(mrz)
|
126
|
-
check = mrz.valid?
|
127
|
-
@mrz.confidence = 1.0 if check
|
128
|
-
check
|
129
|
-
end
|
130
|
-
|
131
|
-
def valid_birth_date?(mrz)
|
132
|
-
check = mrz.valid_birth_date? && mrz.birth_date == @birth_date.date_object
|
133
|
-
@birth_date.confidence = 1.0 if check
|
134
|
-
check
|
135
|
-
end
|
136
|
-
|
137
|
-
def valid_expiry_date?(mrz)
|
138
|
-
check = mrz.valid_expiration_date? && mrz.expiration_date == @expiry_date.date_object
|
139
|
-
@expiry_date.confidence = 1.0 if check
|
140
|
-
check
|
141
|
-
end
|
142
|
-
|
143
|
-
def valid_id_number?(mrz)
|
144
|
-
check = mrz.valid_document_number? && mrz.document_number == @id_number.value
|
145
|
-
@id_number.confidence = 1.0 if check
|
146
|
-
check
|
147
|
-
end
|
148
|
-
|
149
|
-
def valid_surname?(mrz)
|
150
|
-
check = mrz.last_name == @surname.value
|
151
|
-
@surname.confidence = 1.0 if check
|
152
|
-
check
|
153
|
-
end
|
154
|
-
|
155
|
-
def valid_country?(mrz)
|
156
|
-
check = mrz.nationality == @country.value
|
157
|
-
@country.confidence = 1.0 if check
|
158
|
-
check
|
159
|
-
end
|
160
|
-
|
161
98
|
def construct_full_name(page_id)
|
162
99
|
return unless @surname.value &&
|
163
100
|
!@given_names.empty? &&
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common_fields'
|
4
|
+
require_relative '../base'
|
5
|
+
|
6
|
+
module Mindee
|
7
|
+
module Prediction
|
8
|
+
# Passport document.
|
9
|
+
class ProofOfAddressV1 < Prediction
|
10
|
+
# ISO 639-1 code, works best with ca, de, en, es, fr, it, nl and pt.
|
11
|
+
# @return [Mindee::Locale]
|
12
|
+
attr_reader :locale
|
13
|
+
# ISO date yyyy-mm-dd. Works both for European and US dates.
|
14
|
+
# @return [Mindee::DateField]
|
15
|
+
attr_reader :date
|
16
|
+
# All extracted ISO date yyyy-mm-dd. Works both for European and US dates.
|
17
|
+
# @return [Array<Mindee::DateField>]
|
18
|
+
attr_reader :dates
|
19
|
+
# Address of the document's issuer.
|
20
|
+
# @return [Mindee::TextField]
|
21
|
+
attr_reader :issuer_address
|
22
|
+
# Generic: VAT NUMBER, TAX ID, COMPANY REGISTRATION NUMBER or country specific.
|
23
|
+
# @return [Array<Mindee::CompanyRegistration>]
|
24
|
+
attr_reader :issuer_company_registration
|
25
|
+
# Name of the person or company issuing the document.
|
26
|
+
# @return [Mindee::TextField]
|
27
|
+
attr_reader :issuer_name
|
28
|
+
# Address of the recipient.
|
29
|
+
# @return [Mindee::TextField]
|
30
|
+
attr_reader :recipient_address
|
31
|
+
# Generic: VAT NUMBER, TAX ID, COMPANY REGISTRATION NUMBER or country specific.
|
32
|
+
# @return [Array<Mindee::CompanyRegistration>]
|
33
|
+
attr_reader :recipient_company_registration
|
34
|
+
# Name of the document's recipient.
|
35
|
+
# @return [Mindee::TextField]
|
36
|
+
attr_reader :recipient_name
|
37
|
+
|
38
|
+
# @param prediction [Hash]
|
39
|
+
# @param page_id [Integer, nil]
|
40
|
+
def initialize(prediction, page_id)
|
41
|
+
super
|
42
|
+
@locale = Locale.new(prediction['locale'])
|
43
|
+
@date = DateField.new(prediction['date'], page_id)
|
44
|
+
@dates = []
|
45
|
+
prediction['dates'].each do |item|
|
46
|
+
@dates.push(DateField.new(item, page_id))
|
47
|
+
end
|
48
|
+
@issuer_name = TextField.new(prediction['issuer_name'], page_id)
|
49
|
+
@issuer_address = TextField.new(prediction['issuer_address'], page_id)
|
50
|
+
@issuer_company_registration = []
|
51
|
+
prediction['issuer_company_registration'].each do |item|
|
52
|
+
@issuer_company_registration.push(CompanyRegistration.new(item, page_id))
|
53
|
+
end
|
54
|
+
@recipient_name = TextField.new(prediction['recipient_name'], page_id)
|
55
|
+
@recipient_address = TextField.new(prediction['recipient_address'], page_id)
|
56
|
+
@recipient_company_registration = []
|
57
|
+
prediction['recipient_company_registration'].each do |item|
|
58
|
+
@recipient_company_registration.push(CompanyRegistration.new(item, page_id))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
recipient_company_registrations = @recipient_company_registration.join(' ')
|
64
|
+
issuer_company_registrations = @issuer_company_registration.join(' ')
|
65
|
+
dates = @dates.join("\n ")
|
66
|
+
out_str = String.new
|
67
|
+
out_str << "\n:Locale: #{@locale}".rstrip
|
68
|
+
out_str << "\n:Issuer name: #{@issuer_name}".rstrip
|
69
|
+
out_str << "\n:Issuer Address: #{@issuer_address}".rstrip
|
70
|
+
out_str << "\n:Issuer Company Registrations: #{issuer_company_registrations}".rstrip
|
71
|
+
out_str << "\n:Recipient name: #{@recipient_name}".rstrip
|
72
|
+
out_str << "\n:Recipient Address: #{@recipient_address}".rstrip
|
73
|
+
out_str << "\n:Recipient Company Registrations: #{recipient_company_registrations}".rstrip
|
74
|
+
out_str << "\n:Issuance date: #{@date}".rstrip
|
75
|
+
out_str << "\n:Dates: #{dates}".rstrip
|
76
|
+
out_str[1..].to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -40,6 +40,9 @@ module Mindee
|
|
40
40
|
# Whether the document is an expense receipt or a credit card receipt.
|
41
41
|
# @return [Mindee::TextField]
|
42
42
|
attr_reader :document_type
|
43
|
+
# Total amount of tip and gratuity. Both typed and handwritten characters are supported.
|
44
|
+
# @return [Mindee::AmountField]
|
45
|
+
attr_reader :tip
|
43
46
|
|
44
47
|
# @param prediction [Hash]
|
45
48
|
# @param page_id [Integer, nil]
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'prediction/custom/custom_v1'
|
4
|
+
require_relative 'prediction/proof_of_address/proof_of_address_v1'
|
5
|
+
require_relative 'prediction/financial_document/financial_document_v1'
|
4
6
|
require_relative 'prediction/invoice/invoice_v4'
|
5
7
|
require_relative 'prediction/passport/passport_v1'
|
6
8
|
require_relative 'prediction/receipt/receipt_v4'
|