mindee 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +2 -2
- data/.yardopts +4 -0
- data/CHANGELOG.md +21 -0
- data/README.md +46 -23
- data/Rakefile +6 -1
- data/bin/mindee.rb +70 -61
- data/docs/ruby-api-builder.md +131 -0
- data/docs/ruby-getting-started.md +265 -0
- data/docs/ruby-invoice-ocr.md +261 -0
- data/docs/ruby-passport-ocr.md +156 -0
- data/docs/ruby-receipt-ocr.md +170 -0
- data/lib/mindee/client.rb +128 -93
- data/lib/mindee/document_config.rb +22 -154
- data/lib/mindee/geometry.rb +105 -8
- data/lib/mindee/http/endpoint.rb +80 -0
- data/lib/mindee/input/pdf_processing.rb +106 -0
- data/lib/mindee/input/sources.rb +97 -0
- data/lib/mindee/input.rb +3 -0
- data/lib/mindee/parsing/document.rb +31 -0
- data/lib/mindee/parsing/error.rb +22 -0
- data/lib/mindee/parsing/inference.rb +53 -0
- data/lib/mindee/parsing/page.rb +46 -0
- data/lib/mindee/parsing/prediction/base.rb +30 -0
- data/lib/mindee/{fields → parsing/prediction/common_fields}/amount.rb +5 -1
- data/lib/mindee/{fields → parsing/prediction/common_fields}/base.rb +16 -5
- data/lib/mindee/{fields → parsing/prediction/common_fields}/company_registration.rb +0 -0
- data/lib/mindee/{fields/datefield.rb → parsing/prediction/common_fields/date.rb} +0 -0
- data/lib/mindee/{fields → parsing/prediction/common_fields}/locale.rb +0 -0
- data/lib/mindee/{fields → parsing/prediction/common_fields}/payment_details.rb +0 -0
- data/lib/mindee/parsing/prediction/common_fields/position.rb +39 -0
- data/lib/mindee/{fields → parsing/prediction/common_fields}/tax.rb +7 -2
- data/lib/mindee/parsing/prediction/common_fields/text.rb +12 -0
- data/lib/mindee/parsing/prediction/common_fields.rb +11 -0
- data/lib/mindee/parsing/prediction/custom/custom_v1.rb +58 -0
- data/lib/mindee/{fields/custom_docs.rb → parsing/prediction/custom/fields.rb} +5 -5
- data/lib/mindee/parsing/prediction/eu/license_plate/license_plate_v1.rb +34 -0
- data/lib/mindee/parsing/prediction/fr/bank_account_details/bank_account_details_v1.rb +40 -0
- data/lib/mindee/parsing/prediction/fr/carte_vitale/carte_vitale_v1.rb +49 -0
- data/lib/mindee/parsing/prediction/fr/id_card/id_card_v1.rb +84 -0
- data/lib/mindee/parsing/prediction/invoice/invoice_line_item.rb +58 -0
- data/lib/mindee/parsing/prediction/invoice/invoice_v4.rb +216 -0
- data/lib/mindee/parsing/prediction/passport/passport_v1.rb +184 -0
- data/lib/mindee/parsing/prediction/receipt/receipt_v4.rb +84 -0
- data/lib/mindee/parsing/prediction/shipping_container/shipping_container_v1.rb +38 -0
- data/lib/mindee/parsing/prediction/us/bank_check/bank_check_v1.rb +70 -0
- data/lib/mindee/parsing/prediction.rb +12 -0
- data/lib/mindee/parsing.rb +4 -0
- data/lib/mindee/version.rb +1 -1
- data/mindee.gemspec +2 -1
- metadata +57 -24
- data/lib/mindee/documents/base.rb +0 -35
- data/lib/mindee/documents/custom.rb +0 -65
- data/lib/mindee/documents/financial_doc.rb +0 -135
- data/lib/mindee/documents/invoice.rb +0 -162
- data/lib/mindee/documents/passport.rb +0 -163
- data/lib/mindee/documents/receipt.rb +0 -109
- data/lib/mindee/documents.rb +0 -7
- data/lib/mindee/endpoint.rb +0 -105
- data/lib/mindee/fields/orientation.rb +0 -26
- data/lib/mindee/fields.rb +0 -11
- data/lib/mindee/inputs.rb +0 -153
- data/lib/mindee/response.rb +0 -27
@@ -1,135 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../fields'
|
4
|
-
require_relative 'base'
|
5
|
-
require_relative 'invoice'
|
6
|
-
require_relative 'receipt'
|
7
|
-
|
8
|
-
module Mindee
|
9
|
-
# Union of `Invoice` and `Receipt`.
|
10
|
-
class FinancialDocument < Document
|
11
|
-
# @return [Mindee::Locale]
|
12
|
-
attr_reader :locale
|
13
|
-
# @return [Mindee::Amount]
|
14
|
-
attr_reader :total_incl
|
15
|
-
# @return [Mindee::Amount]
|
16
|
-
attr_reader :total_excl
|
17
|
-
# @return [Mindee::Amount]
|
18
|
-
attr_reader :total_tax
|
19
|
-
# @return [Mindee::DateField]
|
20
|
-
attr_reader :date
|
21
|
-
# @return [Mindee::Field]
|
22
|
-
attr_reader :category
|
23
|
-
# @return [Mindee::Field]
|
24
|
-
attr_reader :invoice_number
|
25
|
-
# @return [Mindee::Field]
|
26
|
-
attr_reader :time
|
27
|
-
# @return [Mindee::DateField]
|
28
|
-
attr_reader :due_date
|
29
|
-
# @return [Array<Mindee::TaxField>]
|
30
|
-
attr_reader :taxes
|
31
|
-
# @return [Mindee::Field]
|
32
|
-
attr_reader :supplier
|
33
|
-
# @return [Mindee::Field]
|
34
|
-
attr_reader :supplier_address
|
35
|
-
# @return [Mindee::Field]
|
36
|
-
attr_reader :customer_name
|
37
|
-
# @return [Mindee::Field]
|
38
|
-
attr_reader :customer_address
|
39
|
-
# @return [Array<Mindee::CompanyRegistration>]
|
40
|
-
attr_reader :company_registration
|
41
|
-
# @return [Array<Mindee::CompanyRegistration>]
|
42
|
-
attr_reader :customer_company_registration
|
43
|
-
# @return [Array<Mindee::PaymentDetails>]
|
44
|
-
attr_reader :payment_details
|
45
|
-
|
46
|
-
# @param prediction [Hash]
|
47
|
-
# @param input_file [Mindee::InputDocument, nil]
|
48
|
-
# @param page_id [Integer, nil]
|
49
|
-
def initialize(prediction, input_file: nil, page_id: nil)
|
50
|
-
super('financial_doc', input_file: input_file)
|
51
|
-
@locale = Locale.new(prediction['locale'])
|
52
|
-
if prediction.include? 'invoice_number'
|
53
|
-
build_from_invoice(
|
54
|
-
Invoice.new(prediction, input_file: input_file, page_id: page_id)
|
55
|
-
)
|
56
|
-
else
|
57
|
-
build_from_receipt(
|
58
|
-
Receipt.new(prediction, input_file: input_file, page_id: page_id)
|
59
|
-
)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def to_s
|
64
|
-
customer_company_registration = @customer_company_registration.map(&:value).join('; ')
|
65
|
-
payment_details = @payment_details.map(&:to_s).join("\n ")
|
66
|
-
company_registration = @company_registration.map(&:to_s).join('; ')
|
67
|
-
taxes = @taxes.join("\n ")
|
68
|
-
out_str = String.new
|
69
|
-
out_str << '-----Financial Document data-----'
|
70
|
-
out_str << "\nFilename: #{@filename}".rstrip
|
71
|
-
out_str << "\nCategory: #{@category}".rstrip
|
72
|
-
out_str << "\nInvoice number: #{@invoice_number}".rstrip
|
73
|
-
out_str << "\nTotal amount including taxes: #{@total_incl}".rstrip
|
74
|
-
out_str << "\nTotal amount excluding taxes: #{@total_excl}".rstrip
|
75
|
-
out_str << "\nInvoice date: #{@date}".rstrip
|
76
|
-
out_str << "\nInvoice due date: #{@due_date}".rstrip
|
77
|
-
out_str << "\nSupplier name: #{@supplier}".rstrip
|
78
|
-
out_str << "\nSupplier address: #{@supplier_address}".rstrip
|
79
|
-
out_str << "\nCustomer name: #{@customer_name}".rstrip
|
80
|
-
out_str << "\nCustomer company registration: #{customer_company_registration}".rstrip
|
81
|
-
out_str << "\nCustomer address: #{@customer_address}".rstrip
|
82
|
-
out_str << "\nPayment details: #{payment_details}".rstrip
|
83
|
-
out_str << "\nCompany numbers: #{company_registration}".rstrip
|
84
|
-
out_str << "\nTaxes: #{taxes}".rstrip
|
85
|
-
out_str << "\nTotal taxes: #{@total_tax}".rstrip
|
86
|
-
out_str << "\nTime: #{@time}".rstrip
|
87
|
-
out_str << "\nLocale: #{@locale}".rstrip
|
88
|
-
out_str << "\n----------------------"
|
89
|
-
out_str
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
# @param invoice [Mindee::Invoice]
|
95
|
-
def build_from_invoice(invoice)
|
96
|
-
@orientation = invoice.orientation
|
97
|
-
@category = empty_field
|
98
|
-
@total_incl = invoice.total_incl
|
99
|
-
@total_excl = invoice.total_excl
|
100
|
-
@total_tax = invoice.total_tax
|
101
|
-
@date = invoice.date
|
102
|
-
@time = empty_field
|
103
|
-
@due_date = invoice.due_date
|
104
|
-
@invoice_number = invoice.invoice_number
|
105
|
-
@taxes = invoice.taxes
|
106
|
-
@supplier = invoice.supplier
|
107
|
-
@supplier_address = invoice.supplier_address
|
108
|
-
@company_registration = invoice.company_registration
|
109
|
-
@customer_company_registration = invoice.customer_company_registration
|
110
|
-
@payment_details = invoice.payment_details
|
111
|
-
end
|
112
|
-
|
113
|
-
# @param receipt [Mindee::Receipt]
|
114
|
-
def build_from_receipt(receipt)
|
115
|
-
@orientation = receipt.orientation
|
116
|
-
@category = receipt.category
|
117
|
-
@total_incl = receipt.total_incl
|
118
|
-
@total_excl = receipt.total_excl
|
119
|
-
@total_tax = receipt.total_tax
|
120
|
-
@date = receipt.date
|
121
|
-
@time = receipt.time
|
122
|
-
@due_date = empty_field
|
123
|
-
@taxes = receipt.taxes
|
124
|
-
@supplier = receipt.supplier
|
125
|
-
@supplier_address = empty_field
|
126
|
-
@company_registration = empty_field
|
127
|
-
@customer_company_registration = empty_field
|
128
|
-
@payment_details = empty_field
|
129
|
-
end
|
130
|
-
|
131
|
-
def empty_field
|
132
|
-
Mindee::Field.new({}, nil)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../fields'
|
4
|
-
require_relative 'base'
|
5
|
-
|
6
|
-
module Mindee
|
7
|
-
# Invoice document.
|
8
|
-
class Invoice < Document
|
9
|
-
# @return [Mindee::Locale]
|
10
|
-
attr_reader :locale
|
11
|
-
# @return [Mindee::Amount]
|
12
|
-
attr_reader :total_incl
|
13
|
-
# @return [Mindee::Amount]
|
14
|
-
attr_reader :total_excl
|
15
|
-
# @return [Mindee::Amount]
|
16
|
-
attr_reader :total_tax
|
17
|
-
# @return [Mindee::DateField]
|
18
|
-
attr_reader :date
|
19
|
-
# @return [Mindee::Field]
|
20
|
-
attr_reader :invoice_number
|
21
|
-
# @return [Mindee::DateField]
|
22
|
-
attr_reader :due_date
|
23
|
-
# @return [Array<Mindee::TaxField>]
|
24
|
-
attr_reader :taxes
|
25
|
-
# @return [Array<Mindee::CompanyRegistration>]
|
26
|
-
attr_reader :customer_company_registration
|
27
|
-
# @return [Array<Mindee::PaymentDetails>]
|
28
|
-
attr_reader :payment_details
|
29
|
-
# @return [Array<Mindee::CompanyRegistration>]
|
30
|
-
attr_reader :company_registration
|
31
|
-
# @return [Mindee::Field]
|
32
|
-
attr_reader :customer_name
|
33
|
-
# @return [Mindee::Field]
|
34
|
-
attr_reader :supplier
|
35
|
-
# @return [Mindee::Field]
|
36
|
-
attr_reader :supplier_address
|
37
|
-
# @return [Mindee::Field]
|
38
|
-
attr_reader :customer_address
|
39
|
-
# @return [Mindee::Orientation]
|
40
|
-
attr_reader :orientation
|
41
|
-
|
42
|
-
# @param prediction [Hash]
|
43
|
-
# @param input_file [Mindee::InputDocument, nil]
|
44
|
-
# @param page_id [Integer, nil]
|
45
|
-
def initialize(prediction, input_file: nil, page_id: nil)
|
46
|
-
super('invoice', input_file: input_file)
|
47
|
-
@orientation = Orientation.new(prediction['orientation'], page_id) if page_id
|
48
|
-
@locale = Locale.new(prediction['locale'])
|
49
|
-
@total_incl = Amount.new(prediction['total_incl'], page_id)
|
50
|
-
@total_excl = Amount.new(prediction['total_excl'], page_id)
|
51
|
-
@customer_address = Field.new(prediction['customer_address'], page_id)
|
52
|
-
@customer_name = Field.new(prediction['customer'], page_id)
|
53
|
-
@date = DateField.new(prediction['date'], page_id)
|
54
|
-
@due_date = DateField.new(prediction['due_date'], page_id)
|
55
|
-
@invoice_number = Field.new(prediction['invoice_number'], page_id)
|
56
|
-
@supplier = Field.new(prediction['supplier'], page_id)
|
57
|
-
@supplier_address = Field.new(prediction['supplier_address'], page_id)
|
58
|
-
|
59
|
-
@customer_company_registration = []
|
60
|
-
prediction['customer_company_registration'].each do |item|
|
61
|
-
@customer_company_registration.push(CompanyRegistration.new(item, page_id))
|
62
|
-
end
|
63
|
-
@taxes = []
|
64
|
-
prediction['taxes'].each do |item|
|
65
|
-
@taxes.push(TaxField.new(item, page_id))
|
66
|
-
end
|
67
|
-
@payment_details = []
|
68
|
-
prediction['payment_details'].each do |item|
|
69
|
-
@payment_details.push(PaymentDetails.new(item, page_id))
|
70
|
-
end
|
71
|
-
@company_registration = []
|
72
|
-
prediction['company_registration'].each do |item|
|
73
|
-
@company_registration.push(CompanyRegistration.new(item, page_id))
|
74
|
-
end
|
75
|
-
|
76
|
-
@total_tax = Amount.new(
|
77
|
-
{ value: nil, confidence: 0.0 }, page_id
|
78
|
-
)
|
79
|
-
reconstruct(page_id)
|
80
|
-
end
|
81
|
-
|
82
|
-
def to_s
|
83
|
-
customer_company_registration = @customer_company_registration.map(&:value).join('; ')
|
84
|
-
payment_details = @payment_details.map(&:to_s).join("\n ")
|
85
|
-
company_registration = @company_registration.map(&:to_s).join('; ')
|
86
|
-
taxes = @taxes.join("\n ")
|
87
|
-
out_str = String.new
|
88
|
-
out_str << '----- Invoice V3 -----'
|
89
|
-
out_str << "\nFilename: #{@filename}".rstrip
|
90
|
-
out_str << "\nInvoice number: #{@invoice_number}".rstrip
|
91
|
-
out_str << "\nTotal amount including taxes: #{@total_incl}".rstrip
|
92
|
-
out_str << "\nTotal amount excluding taxes: #{@total_excl}".rstrip
|
93
|
-
out_str << "\nInvoice date: #{@date}".rstrip
|
94
|
-
out_str << "\nInvoice due date: #{@due_date}".rstrip
|
95
|
-
out_str << "\nSupplier name: #{@supplier}".rstrip
|
96
|
-
out_str << "\nSupplier address: #{@supplier_address}".rstrip
|
97
|
-
out_str << "\nCustomer name: #{@customer_name}".rstrip
|
98
|
-
out_str << "\nCustomer company registration: #{customer_company_registration}".rstrip
|
99
|
-
out_str << "\nCustomer address: #{@customer_address}".rstrip
|
100
|
-
out_str << "\nPayment details: #{payment_details}".rstrip
|
101
|
-
out_str << "\nCompany numbers: #{company_registration}".rstrip
|
102
|
-
out_str << "\nTaxes: #{taxes}".rstrip
|
103
|
-
out_str << "\nTotal taxes: #{@total_tax}".rstrip
|
104
|
-
out_str << "\nLocale: #{@locale}".rstrip
|
105
|
-
out_str << "\n----------------------"
|
106
|
-
out_str
|
107
|
-
end
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def reconstruct(page_id)
|
112
|
-
construct_total_tax_from_taxes(page_id)
|
113
|
-
construct_total_excl_from_tcc_and_taxes(page_id)
|
114
|
-
construct_total_incl_from_taxes_plus_excl(page_id)
|
115
|
-
construct_total_tax_from_totals(page_id)
|
116
|
-
end
|
117
|
-
|
118
|
-
def construct_total_excl_from_tcc_and_taxes(page_id)
|
119
|
-
return if @total_incl.value.nil? || taxes.empty? || !@total_excl.value.nil?
|
120
|
-
|
121
|
-
total_excl = {
|
122
|
-
'value' => @total_incl.value - @taxes.map(&:value).sum,
|
123
|
-
'confidence' => Field.array_confidence(@taxes) * @total_incl.confidence,
|
124
|
-
}
|
125
|
-
@total_excl = Amount.new(total_excl, page_id, reconstructed: true)
|
126
|
-
end
|
127
|
-
|
128
|
-
def construct_total_incl_from_taxes_plus_excl(page_id)
|
129
|
-
return if @total_excl.value.nil? || @taxes.empty? || !@total_incl.value.nil?
|
130
|
-
|
131
|
-
total_incl = {
|
132
|
-
'value' => @taxes.map(&:value).sum + @total_excl.value,
|
133
|
-
'confidence' => Field.array_confidence(@taxes) * @total_excl.confidence,
|
134
|
-
}
|
135
|
-
@total_incl = Amount.new(total_incl, page_id, reconstructed: true)
|
136
|
-
end
|
137
|
-
|
138
|
-
def construct_total_tax_from_taxes(page_id)
|
139
|
-
return if @taxes.empty?
|
140
|
-
|
141
|
-
total_tax = {
|
142
|
-
'value' => @taxes.map(&:value).sum,
|
143
|
-
'confidence' => Field.array_confidence(@taxes),
|
144
|
-
}
|
145
|
-
return unless total_tax['value'].positive?
|
146
|
-
|
147
|
-
@total_tax = Amount.new(total_tax, page_id, reconstructed: true)
|
148
|
-
end
|
149
|
-
|
150
|
-
def construct_total_tax_from_totals(page_id)
|
151
|
-
return if !@total_tax.value.nil? || @total_incl.value.nil? || @total_excl.value.nil?
|
152
|
-
|
153
|
-
total_tax = {
|
154
|
-
'value' => @total_incl.value - @total_excl.value,
|
155
|
-
'confidence' => Field.array_confidence(@taxes),
|
156
|
-
}
|
157
|
-
return unless total_tax['value'] >= 0
|
158
|
-
|
159
|
-
@total_tax = Amount.new(total_tax, page_id, reconstructed: true)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
@@ -1,163 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'mrz'
|
4
|
-
|
5
|
-
require_relative '../fields'
|
6
|
-
require_relative 'base'
|
7
|
-
|
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
|
-
module Mindee
|
18
|
-
# Passport document.
|
19
|
-
class Passport < Document
|
20
|
-
# @return [Mindee::Orientation]
|
21
|
-
attr_reader :orientation
|
22
|
-
attr_reader :country,
|
23
|
-
:id_number,
|
24
|
-
:expiry_date,
|
25
|
-
:issuance_date,
|
26
|
-
:surname,
|
27
|
-
:given_names,
|
28
|
-
:full_name,
|
29
|
-
:birth_date,
|
30
|
-
:birth_place,
|
31
|
-
:gender,
|
32
|
-
:mrz1,
|
33
|
-
:mrz2,
|
34
|
-
:mrz
|
35
|
-
|
36
|
-
# @param prediction [Hash]
|
37
|
-
# @param input_file [Mindee::InputDocument, nil]
|
38
|
-
# @param page_id [Integer, nil]
|
39
|
-
def initialize(prediction, input_file: nil, page_id: nil)
|
40
|
-
super('passport', input_file: input_file)
|
41
|
-
@orientation = Orientation.new(prediction['orientation'], page_id) if page_id
|
42
|
-
@country = Field.new(prediction['country'], page_id)
|
43
|
-
@id_number = Field.new(prediction['id_number'], page_id)
|
44
|
-
@birth_date = DateField.new(prediction['birth_date'], page_id)
|
45
|
-
@expiry_date = DateField.new(prediction['expiry_date'], page_id)
|
46
|
-
@issuance_date = DateField.new(prediction['issuance_date'], page_id)
|
47
|
-
@birth_place = Field.new(prediction['birth_place'], page_id)
|
48
|
-
@gender = Field.new(prediction['gender'], page_id)
|
49
|
-
@surname = Field.new(prediction['surname'], page_id)
|
50
|
-
@mrz1 = Field.new(prediction['mrz1'], page_id)
|
51
|
-
@mrz2 = Field.new(prediction['mrz2'], page_id)
|
52
|
-
@given_names = []
|
53
|
-
prediction['given_names'].each do |item|
|
54
|
-
@given_names.push(Field.new(item, page_id))
|
55
|
-
end
|
56
|
-
@full_name = construct_full_name(page_id)
|
57
|
-
@mrz = construct_mrz(page_id)
|
58
|
-
check_mrz
|
59
|
-
end
|
60
|
-
|
61
|
-
def to_s
|
62
|
-
given_names = @given_names.join(' ')
|
63
|
-
out_str = String.new
|
64
|
-
out_str << '-----Passport data-----'
|
65
|
-
out_str << "\nFilename: #{@filename}".rstrip
|
66
|
-
out_str << "\nFull name: #{@full_name}".rstrip
|
67
|
-
out_str << "\nGiven names: #{given_names}".rstrip
|
68
|
-
out_str << "\nSurname: #{@surname}".rstrip
|
69
|
-
out_str << "\nCountry: #{@country}".rstrip
|
70
|
-
out_str << "\nID Number: #{@id_number}".rstrip
|
71
|
-
out_str << "\nIssuance date: #{@issuance_date}".rstrip
|
72
|
-
out_str << "\nBirth date: #{@birth_date}".rstrip
|
73
|
-
out_str << "\nExpiry date: #{@expiry_date}".rstrip
|
74
|
-
out_str << "\nMRZ 1: #{@mrz1}".rstrip
|
75
|
-
out_str << "\nMRZ 2: #{@mrz2}".rstrip
|
76
|
-
out_str << "\nMRZ: #{@mrz}".rstrip
|
77
|
-
out_str << "\n----------------------"
|
78
|
-
out_str
|
79
|
-
end
|
80
|
-
|
81
|
-
# @return [Boolean]
|
82
|
-
def expired?
|
83
|
-
return true unless @expiry_date.date_object
|
84
|
-
|
85
|
-
@expiry_date.date_object < Date.today
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def check_mrz
|
91
|
-
return if @mrz1.value.nil? || @mrz2.value.nil?
|
92
|
-
|
93
|
-
mrz = MRZ.parse([@mrz1.value, @mrz2.value])
|
94
|
-
checks = {
|
95
|
-
mrz_valid: valid_mrz?(mrz),
|
96
|
-
mrz_valid_birth_date: valid_birth_date?(mrz),
|
97
|
-
mrz_valid_expiry_date: valid_expiry_date?(mrz),
|
98
|
-
mrz_valid_id_number: valid_id_number?(mrz),
|
99
|
-
mrz_valid_surname: valid_surname?(mrz),
|
100
|
-
mrz_valid_country: valid_country?(mrz),
|
101
|
-
}
|
102
|
-
@checklist.merge!(checks)
|
103
|
-
end
|
104
|
-
|
105
|
-
def valid_mrz?(mrz)
|
106
|
-
check = mrz.valid?
|
107
|
-
@mrz.confidence = 1.0 if check
|
108
|
-
check
|
109
|
-
end
|
110
|
-
|
111
|
-
def valid_birth_date?(mrz)
|
112
|
-
check = mrz.valid_birth_date? && mrz.birth_date == @birth_date.date_object
|
113
|
-
@birth_date.confidence = 1.0 if check
|
114
|
-
check
|
115
|
-
end
|
116
|
-
|
117
|
-
def valid_expiry_date?(mrz)
|
118
|
-
check = mrz.valid_expiration_date? && mrz.expiration_date == @expiry_date.date_object
|
119
|
-
@expiry_date.confidence = 1.0 if check
|
120
|
-
check
|
121
|
-
end
|
122
|
-
|
123
|
-
def valid_id_number?(mrz)
|
124
|
-
check = mrz.valid_document_number? && mrz.document_number == @id_number.value
|
125
|
-
@id_number.confidence = 1.0 if check
|
126
|
-
check
|
127
|
-
end
|
128
|
-
|
129
|
-
def valid_surname?(mrz)
|
130
|
-
check = mrz.last_name == @surname.value
|
131
|
-
@surname.confidence = 1.0 if check
|
132
|
-
check
|
133
|
-
end
|
134
|
-
|
135
|
-
def valid_country?(mrz)
|
136
|
-
check = mrz.nationality == @country.value
|
137
|
-
@country.confidence = 1.0 if check
|
138
|
-
check
|
139
|
-
end
|
140
|
-
|
141
|
-
def construct_full_name(page_id)
|
142
|
-
return unless @surname.value &&
|
143
|
-
!@given_names.empty? &&
|
144
|
-
@given_names[0].value
|
145
|
-
|
146
|
-
full_name = {
|
147
|
-
'value' => "#{@given_names[0].value} #{@surname.value}",
|
148
|
-
'confidence' => Field.array_confidence([@surname, @given_names[0]]),
|
149
|
-
}
|
150
|
-
Field.new(full_name, page_id, reconstructed: true)
|
151
|
-
end
|
152
|
-
|
153
|
-
def construct_mrz(page_id)
|
154
|
-
return if @mrz1.value.nil? || @mrz2.value.nil?
|
155
|
-
|
156
|
-
mrz = {
|
157
|
-
'value' => @mrz1.value + @mrz2.value,
|
158
|
-
'confidence' => Field.array_confidence([@mrz1, @mrz2]),
|
159
|
-
}
|
160
|
-
Field.new(mrz, page_id, reconstructed: true)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
@@ -1,109 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../fields'
|
4
|
-
require_relative 'base'
|
5
|
-
|
6
|
-
module Mindee
|
7
|
-
# Receipt document.
|
8
|
-
class Receipt < Document
|
9
|
-
# @return [Mindee::Locale]
|
10
|
-
attr_reader :locale
|
11
|
-
# @return [Mindee::Amount]
|
12
|
-
attr_reader :total_incl
|
13
|
-
# @return [Mindee::Amount]
|
14
|
-
attr_reader :total_excl
|
15
|
-
# @return [Mindee::Amount]
|
16
|
-
attr_reader :total_tax
|
17
|
-
# @return [Mindee::DateField]
|
18
|
-
attr_reader :date
|
19
|
-
# @return [Mindee::Field]
|
20
|
-
attr_reader :supplier
|
21
|
-
# @return [Array<Mindee::TaxField>]
|
22
|
-
attr_reader :taxes
|
23
|
-
# @return [Mindee::Orientation]
|
24
|
-
attr_reader :orientation
|
25
|
-
# @return [Mindee::Field]
|
26
|
-
attr_reader :time
|
27
|
-
# @return [Mindee::Field]
|
28
|
-
attr_reader :category
|
29
|
-
|
30
|
-
# @param prediction [Hash]
|
31
|
-
# @param input_file [Mindee::InputDocument, nil]
|
32
|
-
# @param page_id [Integer, nil]
|
33
|
-
def initialize(prediction, input_file: nil, page_id: nil)
|
34
|
-
super('receipt', input_file: input_file)
|
35
|
-
@orientation = Orientation.new(prediction['orientation'], page_id) if page_id
|
36
|
-
@locale = Locale.new(prediction['locale'])
|
37
|
-
@total_incl = Amount.new(prediction['total_incl'], page_id)
|
38
|
-
@date = DateField.new(prediction['date'], page_id)
|
39
|
-
@category = Field.new(prediction['category'], page_id)
|
40
|
-
@supplier = Field.new(prediction['supplier'], page_id)
|
41
|
-
@time = Field.new(prediction['time'], page_id)
|
42
|
-
@taxes = []
|
43
|
-
prediction['taxes'].each do |item|
|
44
|
-
@taxes.push(TaxField.new(item, page_id))
|
45
|
-
end
|
46
|
-
|
47
|
-
@total_tax = Amount.new(
|
48
|
-
{ value: nil, confidence: 0.0 }, page_id
|
49
|
-
)
|
50
|
-
@total_excl = Amount.new(
|
51
|
-
{ value: nil, confidence: 0.0 }, page_id
|
52
|
-
)
|
53
|
-
reconstruct(page_id)
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_s
|
57
|
-
taxes = @taxes.join("\n ")
|
58
|
-
out_str = String.new
|
59
|
-
out_str << '-----Receipt data-----'
|
60
|
-
out_str << "\nFilename: #{@filename}".rstrip
|
61
|
-
out_str << "\nTotal amount including taxes: #{@total_incl}".rstrip
|
62
|
-
out_str << "\nTotal amount excluding taxes: #{@total_excl}".rstrip
|
63
|
-
out_str << "\nDate: #{@date}".rstrip
|
64
|
-
out_str << "\nCategory: #{@category}".rstrip
|
65
|
-
out_str << "\nTime: #{@time}".rstrip
|
66
|
-
out_str << "\nMerchant name: #{@supplier}".rstrip
|
67
|
-
out_str << "\nTaxes: #{taxes}".rstrip
|
68
|
-
out_str << "\nTotal taxes: #{@total_tax}".rstrip
|
69
|
-
out_str << "\nLocale: #{@locale}".rstrip
|
70
|
-
out_str << "\n----------------------"
|
71
|
-
out_str
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def reconstruct(page_id)
|
77
|
-
construct_total_tax_from_taxes(page_id)
|
78
|
-
construct_total_excl(page_id)
|
79
|
-
end
|
80
|
-
|
81
|
-
def construct_total_tax_from_taxes(page_id)
|
82
|
-
return if @taxes.empty? || !@total_tax.value.nil?
|
83
|
-
|
84
|
-
total_tax = {
|
85
|
-
'value' => @taxes.map do |tax|
|
86
|
-
if tax.value.nil?
|
87
|
-
0
|
88
|
-
else
|
89
|
-
tax.value
|
90
|
-
end
|
91
|
-
end.sum,
|
92
|
-
'confidence' => Field.array_confidence(@taxes),
|
93
|
-
}
|
94
|
-
return unless total_tax['value'].positive?
|
95
|
-
|
96
|
-
@total_tax = Amount.new(total_tax, page_id, reconstructed: true)
|
97
|
-
end
|
98
|
-
|
99
|
-
def construct_total_excl(page_id)
|
100
|
-
return if @taxes.empty? || @total_incl.value.nil?
|
101
|
-
|
102
|
-
total_excl = {
|
103
|
-
'value' => @total_incl.value - Field.array_sum(@taxes),
|
104
|
-
'confidence' => Field.array_confidence(@taxes) * @total_incl.confidence,
|
105
|
-
}
|
106
|
-
@total_excl = Amount.new(total_excl, page_id, reconstructed: true)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
data/lib/mindee/documents.rb
DELETED
data/lib/mindee/endpoint.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require_relative 'version'
|
5
|
-
|
6
|
-
module Mindee
|
7
|
-
MINDEE_API_URL = 'https://api.mindee.net/v1'
|
8
|
-
USER_AGENT = "mindee-api-ruby@v#{Mindee::VERSION} ruby-v#{RUBY_VERSION} #{Mindee::PLATFORM}"
|
9
|
-
|
10
|
-
INVOICE_VERSION = '3'
|
11
|
-
INVOICE_URL_NAME = 'invoices'
|
12
|
-
|
13
|
-
RECEIPT_VERSION = '3'
|
14
|
-
RECEIPT_URL_NAME = 'expense_receipts'
|
15
|
-
|
16
|
-
PASSPORT_VERSION = '1'
|
17
|
-
PASSPORT_URL_NAME = 'passport'
|
18
|
-
|
19
|
-
# Generic API endpoint for a product.
|
20
|
-
class Endpoint
|
21
|
-
attr_reader :api_key
|
22
|
-
|
23
|
-
def initialize(owner, url_name, version, key_name: nil, api_key: nil)
|
24
|
-
@owner = owner
|
25
|
-
@url_name = url_name
|
26
|
-
@version = version
|
27
|
-
@key_name = key_name || url_name
|
28
|
-
@api_key = api_key || set_api_key_from_env
|
29
|
-
@url_root = "#{MINDEE_API_URL}/products/#{@owner}/#{@url_name}/v#{@version}"
|
30
|
-
end
|
31
|
-
|
32
|
-
# @param input_doc [Mindee::InputDocument]
|
33
|
-
# @param include_words [Boolean]
|
34
|
-
# @param close_file [Boolean]
|
35
|
-
# @return [Net::HTTPResponse]
|
36
|
-
def predict_request(input_doc, include_words: false, close_file: true)
|
37
|
-
uri = URI("#{@url_root}/predict")
|
38
|
-
headers = {
|
39
|
-
'Authorization' => "Token #{@api_key}",
|
40
|
-
'User-Agent' => USER_AGENT,
|
41
|
-
}
|
42
|
-
req = Net::HTTP::Post.new(uri, headers)
|
43
|
-
|
44
|
-
params = {
|
45
|
-
'document' => input_doc.read_document(close: close_file),
|
46
|
-
}
|
47
|
-
params.push ['include_mvision', 'true'] if include_words
|
48
|
-
|
49
|
-
req.set_form(params, 'multipart/form-data')
|
50
|
-
|
51
|
-
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
52
|
-
http.request(req)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def envvar_key_name
|
57
|
-
key_name = to_envar(@key_name)
|
58
|
-
key_name = "#{to_envar(@owner)}_#{key_name}" if @owner != 'mindee'
|
59
|
-
"MINDEE_#{key_name}_API_KEY"
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
# Create a standard way to get/set environment variable names.
|
65
|
-
def to_envar(name)
|
66
|
-
name.sub('-', '_').upcase
|
67
|
-
end
|
68
|
-
|
69
|
-
# Set the endpoint's API key from an environment variable, if present.
|
70
|
-
# We look first for the specific key, if not set, we'll use the generic key
|
71
|
-
def set_api_key_from_env
|
72
|
-
env_key = ENV.fetch(envvar_key_name, nil)
|
73
|
-
env_key = ENV.fetch('MINDEE_API_KEY', nil) if env_key.nil?
|
74
|
-
@api_key = env_key if env_key
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# Invoice API endpoint
|
79
|
-
class InvoiceEndpoint < Endpoint
|
80
|
-
def initialize(api_key)
|
81
|
-
super('mindee', INVOICE_URL_NAME, INVOICE_VERSION, key_name: 'invoice', api_key: api_key)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Receipt API endpoint
|
86
|
-
class ReceiptEndpoint < Endpoint
|
87
|
-
def initialize(api_key)
|
88
|
-
super('mindee', RECEIPT_URL_NAME, RECEIPT_VERSION, key_name: 'receipt', api_key: api_key)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Passport API endpoint
|
93
|
-
class PassportEndpoint < Endpoint
|
94
|
-
def initialize(api_key)
|
95
|
-
super('mindee', PASSPORT_URL_NAME, PASSPORT_VERSION, api_key: api_key)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Custom (constructed) API endpoint
|
100
|
-
class CustomEndpoint < Endpoint
|
101
|
-
def initialize(document_type, account_name, version, api_key)
|
102
|
-
super(account_name, document_type, version, api_key: api_key)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|