mindee 1.2.0 → 2.1.0
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 -1
- data/.rubocop.yml +2 -2
- data/.yardopts +4 -0
- data/CHANGELOG.md +26 -0
- data/README.md +46 -23
- data/Rakefile +6 -1
- data/bin/mindee.rb +78 -61
- data/docs/ruby-api-builder.md +124 -0
- data/docs/ruby-getting-started.md +265 -0
- data/docs/ruby-invoice-ocr.md +260 -0
- data/docs/ruby-passport-ocr.md +156 -0
- data/docs/ruby-receipt-ocr.md +170 -0
- data/lib/mindee/client.rb +132 -93
- data/lib/mindee/document_config.rb +29 -169
- 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/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/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/proof_of_address/proof_of_address_v1.rb +82 -0
- data/lib/mindee/parsing/prediction/receipt/receipt_v4.rb +87 -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 +14 -0
- data/lib/mindee/parsing.rb +4 -0
- data/lib/mindee/version.rb +1 -1
- data/mindee.gemspec +2 -1
- metadata +60 -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
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'page'
|
|
4
|
+
|
|
5
|
+
module Mindee
|
|
6
|
+
# Product information
|
|
7
|
+
class Product
|
|
8
|
+
attr_reader :name, :type, :version
|
|
9
|
+
|
|
10
|
+
# @param http_response [Hash]
|
|
11
|
+
def initialize(http_response)
|
|
12
|
+
@name = http_response['name']
|
|
13
|
+
@type = http_response['type']
|
|
14
|
+
@version = http_response['version']
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Inference holds all predictions
|
|
19
|
+
class Inference
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
attr_reader :is_rotation_applied
|
|
22
|
+
# @return [Array<Mindee::Page>]
|
|
23
|
+
attr_reader :pages
|
|
24
|
+
# @return [Mindee::Prediction]
|
|
25
|
+
attr_reader :prediction
|
|
26
|
+
# @return [Mindee::Product]
|
|
27
|
+
attr_reader :product
|
|
28
|
+
|
|
29
|
+
# @param prediction_class [Class<Mindee::Prediction::Prediction>]
|
|
30
|
+
# @param http_response [Hash]
|
|
31
|
+
def initialize(prediction_class, http_response)
|
|
32
|
+
@is_rotation_applied = http_response['is_rotation_applied']
|
|
33
|
+
@prediction = prediction_class.new(http_response['prediction'], nil)
|
|
34
|
+
@product = Product.new(http_response['product'])
|
|
35
|
+
@pages = []
|
|
36
|
+
http_response['pages'].each do |page|
|
|
37
|
+
@pages.push(Page.new(prediction_class, page))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_s
|
|
42
|
+
is_rotation_applied = @is_rotation_applied ? 'Yes' : 'No'
|
|
43
|
+
out_str = String.new
|
|
44
|
+
out_str << "Inference\n#########"
|
|
45
|
+
out_str << "\n:Product: #{@product.name} v#{@product.version}"
|
|
46
|
+
out_str << "\n:Rotation applied: #{is_rotation_applied}"
|
|
47
|
+
out_str << "\n\nPrediction\n=========="
|
|
48
|
+
out_str << "\n#{@prediction}"
|
|
49
|
+
out_str << "\n\nPage Predictions\n================\n\n"
|
|
50
|
+
out_str << @pages.map(&:to_s).join("\n\n")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mindee
|
|
4
|
+
# Page orientation
|
|
5
|
+
class Orientation
|
|
6
|
+
# @return [Integer]
|
|
7
|
+
attr_reader :page_id
|
|
8
|
+
# A prediction among these 3 possible outputs:
|
|
9
|
+
# * 0 degrees: the page is already upright
|
|
10
|
+
# * 90 degrees: the page must be rotated clockwise to be upright
|
|
11
|
+
# * 270 degrees: the page must be rotated counterclockwise to be upright
|
|
12
|
+
# @return [Integer, nil]
|
|
13
|
+
attr_reader :value
|
|
14
|
+
|
|
15
|
+
# @param prediction [Hash]
|
|
16
|
+
# @param page_id [Integer]
|
|
17
|
+
def initialize(prediction, page_id)
|
|
18
|
+
@value = prediction['value']
|
|
19
|
+
@page_id = page_id
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Page object
|
|
24
|
+
class Page
|
|
25
|
+
# @return [Integer]
|
|
26
|
+
attr_reader :id
|
|
27
|
+
# @return [Mindee::Prediction]
|
|
28
|
+
attr_reader :prediction
|
|
29
|
+
# @return [Mindee::Orientation]
|
|
30
|
+
attr_reader :orientation
|
|
31
|
+
|
|
32
|
+
def initialize(prediction_class, page)
|
|
33
|
+
@id = page['id'].to_i
|
|
34
|
+
@prediction = prediction_class.new(page['prediction'], @id)
|
|
35
|
+
@orientation = Orientation.new(page['orientation'], @id)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
out_str = String.new
|
|
40
|
+
title = "Page #{@id}"
|
|
41
|
+
out_str << "#{title}\n"
|
|
42
|
+
out_str << ('-' * title.size)
|
|
43
|
+
out_str << "\n#{@prediction}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mindee
|
|
4
|
+
module Prediction
|
|
5
|
+
# Base document object.
|
|
6
|
+
class Prediction
|
|
7
|
+
# document type
|
|
8
|
+
# @return [String]
|
|
9
|
+
attr_reader :document_type
|
|
10
|
+
# Validation checks for the document
|
|
11
|
+
# @return [Hash<Symbol, Boolean>]
|
|
12
|
+
attr_reader :checklist
|
|
13
|
+
# Original filename of the document
|
|
14
|
+
# @return [String, nil]
|
|
15
|
+
attr_reader :filename
|
|
16
|
+
# Detected MIME type of the document
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_reader :file_mimetype
|
|
19
|
+
|
|
20
|
+
def initialize(_prediction, _page_id)
|
|
21
|
+
@checklist = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Boolean]
|
|
25
|
+
def all_checks
|
|
26
|
+
@checklist.all? { |_, value| value == true }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -4,7 +4,7 @@ require_relative 'base'
|
|
|
4
4
|
|
|
5
5
|
module Mindee
|
|
6
6
|
# Represents tax information.
|
|
7
|
-
class
|
|
7
|
+
class AmountField < Field
|
|
8
8
|
# Amount value as 3 decimal float
|
|
9
9
|
# @return [Float, nil]
|
|
10
10
|
attr_reader :value
|
|
@@ -13,5 +13,9 @@ module Mindee
|
|
|
13
13
|
super
|
|
14
14
|
@value = @value.round(3) unless @value.nil?
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
Field.float_to_string(@value)
|
|
19
|
+
end
|
|
16
20
|
end
|
|
17
21
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
3
|
+
require_relative '../../../geometry'
|
|
4
4
|
|
|
5
5
|
module Mindee
|
|
6
6
|
# Base field object.
|
|
@@ -8,8 +8,8 @@ module Mindee
|
|
|
8
8
|
# @return [String, Float, Integer, Boolean]
|
|
9
9
|
attr_reader :value
|
|
10
10
|
# @return [Array<Array<Float>>]
|
|
11
|
-
attr_reader :
|
|
12
|
-
# @return [
|
|
11
|
+
attr_reader :bounding_box
|
|
12
|
+
# @return [Mindee::Geometry::Polygon]
|
|
13
13
|
attr_reader :polygon
|
|
14
14
|
# @return [Integer, nil]
|
|
15
15
|
attr_reader :page_id
|
|
@@ -26,8 +26,8 @@ module Mindee
|
|
|
26
26
|
def initialize(prediction, page_id, reconstructed: false)
|
|
27
27
|
@value = prediction['value']
|
|
28
28
|
@confidence = prediction['confidence']
|
|
29
|
-
@polygon = prediction['polygon']
|
|
30
|
-
@
|
|
29
|
+
@polygon = Geometry.polygon_from_prediction(prediction['polygon'])
|
|
30
|
+
@bounding_box = Geometry.get_bounding_box(@polygon) unless @polygon.nil? || @polygon.empty?
|
|
31
31
|
@page_id = page_id || prediction['page_id']
|
|
32
32
|
@reconstructed = reconstructed
|
|
33
33
|
end
|
|
@@ -57,5 +57,16 @@ module Mindee
|
|
|
57
57
|
end
|
|
58
58
|
arr_sum.to_f
|
|
59
59
|
end
|
|
60
|
+
|
|
61
|
+
# @param value [Float]
|
|
62
|
+
# @param min_precision [Integer]
|
|
63
|
+
def self.float_to_string(value, min_precision = 2)
|
|
64
|
+
return String.new if value.nil?
|
|
65
|
+
|
|
66
|
+
precision = value.to_f.to_s.split('.')[1].size
|
|
67
|
+
precision = [precision, min_precision].max
|
|
68
|
+
format_string = "%.#{precision}f"
|
|
69
|
+
format(format_string, value)
|
|
70
|
+
end
|
|
60
71
|
end
|
|
61
72
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mindee
|
|
4
|
+
# An element's position on the image
|
|
5
|
+
class PositionField
|
|
6
|
+
# @return [Mindee::Geometry::Polygon]
|
|
7
|
+
attr_reader :polygon
|
|
8
|
+
# @return [Mindee::Geometry::Polygon]
|
|
9
|
+
attr_reader :value
|
|
10
|
+
# @return [Mindee::Geometry::Quadrilateral]
|
|
11
|
+
attr_reader :quadrangle
|
|
12
|
+
# @return [Mindee::Geometry::Quadrilateral]
|
|
13
|
+
attr_reader :rectangle
|
|
14
|
+
# @return [Mindee::Geometry::Quadrilateral]
|
|
15
|
+
attr_reader :bounding_box
|
|
16
|
+
|
|
17
|
+
# @param prediction [Hash]
|
|
18
|
+
# @param page_id [Integer, nil]
|
|
19
|
+
def initialize(prediction, page_id)
|
|
20
|
+
@polygon = Geometry.polygon_from_prediction(prediction['polygon']) unless prediction['polygon'].empty?
|
|
21
|
+
@quadrangle = to_quadrilateral(prediction, 'quadrangle')
|
|
22
|
+
@rectangle = to_quadrilateral(prediction, 'rectangle')
|
|
23
|
+
@bounding_box = to_quadrilateral(prediction, 'bounding_box')
|
|
24
|
+
@page_id = page_id || prediction['page_id']
|
|
25
|
+
@value = @polygon
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
out_str = String.new
|
|
30
|
+
out_str << "Polygon with #{@polygon.size} points."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def to_quadrilateral(prediction, key)
|
|
36
|
+
Geometry.quadrilateral_from_prediction(prediction[key]) unless prediction[key].empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -24,10 +24,15 @@ module Mindee
|
|
|
24
24
|
@code = prediction['code'] unless prediction['code'] == 'None'
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# @param value [Float]
|
|
28
|
+
def print_float(value)
|
|
29
|
+
format('%.2f', value)
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
def to_s
|
|
28
33
|
out_str = String.new
|
|
29
|
-
out_str << "#{@value} " if @value
|
|
30
|
-
out_str << "#{@rate}% " if @rate
|
|
34
|
+
out_str << "#{print_float(@value)} " if @value
|
|
35
|
+
out_str << "#{print_float(@rate)}% " if @rate
|
|
31
36
|
out_str << "#{@code} " if @code
|
|
32
37
|
out_str.strip
|
|
33
38
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common_fields/amount'
|
|
4
|
+
require_relative 'common_fields/base'
|
|
5
|
+
require_relative 'common_fields/company_registration'
|
|
6
|
+
require_relative 'common_fields/date'
|
|
7
|
+
require_relative 'common_fields/locale'
|
|
8
|
+
require_relative 'common_fields/payment_details'
|
|
9
|
+
require_relative 'common_fields/position'
|
|
10
|
+
require_relative 'common_fields/tax'
|
|
11
|
+
require_relative 'common_fields/text'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'fields'
|
|
4
|
+
require_relative '../base'
|
|
5
|
+
|
|
6
|
+
module Mindee
|
|
7
|
+
module Prediction
|
|
8
|
+
# Custom document object.
|
|
9
|
+
class CustomV1 < Prediction
|
|
10
|
+
# All value fields in the document
|
|
11
|
+
# @return [Hash<Symbol, Mindee::ListField>]
|
|
12
|
+
attr_reader :fields
|
|
13
|
+
# All classifications in the document
|
|
14
|
+
# @return [Hash<Symbol, Mindee::ClassificationField>]
|
|
15
|
+
attr_reader :classifications
|
|
16
|
+
|
|
17
|
+
# @param prediction [Hash]
|
|
18
|
+
# @param page_id [Integer, nil]
|
|
19
|
+
def initialize(prediction, page_id)
|
|
20
|
+
super
|
|
21
|
+
@fields = {}
|
|
22
|
+
@classifications = {}
|
|
23
|
+
prediction.each do |field_name, field_prediction|
|
|
24
|
+
field_sym = field_name.to_sym
|
|
25
|
+
set_field(field_sym, field_prediction, page_id)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
out_str = String.new
|
|
31
|
+
@classifications.each do |name, info|
|
|
32
|
+
out_str << "\n:#{name}: #{info}".rstrip
|
|
33
|
+
end
|
|
34
|
+
@fields.each do |name, info|
|
|
35
|
+
out_str << "\n:#{name}: #{info}".rstrip
|
|
36
|
+
end
|
|
37
|
+
out_str[1..].to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# @param field_prediction [Hash]
|
|
43
|
+
def set_field(field_sym, field_prediction, page_id)
|
|
44
|
+
# Currently two types of fields possible in a custom API response:
|
|
45
|
+
# fields having a list of values, and classification fields.
|
|
46
|
+
# Here we use the fact that only value lists have the 'values' attribute.
|
|
47
|
+
|
|
48
|
+
if field_prediction.key? 'values'
|
|
49
|
+
@fields[field_sym] = ListField.new(field_prediction, page_id)
|
|
50
|
+
elsif field_prediction.key? 'value'
|
|
51
|
+
@classifications[field_sym] = ClassificationField.new(field_prediction)
|
|
52
|
+
else
|
|
53
|
+
throw 'Unknown API field type'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -26,9 +26,9 @@ module Mindee
|
|
|
26
26
|
# The confidence score, value will be between 0.0 and 1.0
|
|
27
27
|
# @return [Float]
|
|
28
28
|
attr_accessor :confidence
|
|
29
|
-
# @return [
|
|
30
|
-
attr_reader :
|
|
31
|
-
# @return [
|
|
29
|
+
# @return [Mindee::Geometry::Quadrilateral]
|
|
30
|
+
attr_reader :bounding_box
|
|
31
|
+
# @return [Mindee::Geometry::Polygon]
|
|
32
32
|
attr_reader :polygon
|
|
33
33
|
attr_reader :content
|
|
34
34
|
|
|
@@ -36,8 +36,8 @@ module Mindee
|
|
|
36
36
|
def initialize(prediction)
|
|
37
37
|
@content = prediction['content']
|
|
38
38
|
@confidence = prediction['confidence']
|
|
39
|
-
@polygon = prediction['polygon']
|
|
40
|
-
@
|
|
39
|
+
@polygon = Geometry.polygon_from_prediction(prediction['polygon'])
|
|
40
|
+
@bounding_box = Geometry.get_bounding_box(@polygon) unless @polygon.nil? || @polygon.empty?
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# @return [String]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../common_fields'
|
|
4
|
+
require_relative '../../base'
|
|
5
|
+
|
|
6
|
+
module Mindee
|
|
7
|
+
module Prediction
|
|
8
|
+
module EU
|
|
9
|
+
# License plate prediction.
|
|
10
|
+
class LicensePlateV1 < Prediction
|
|
11
|
+
# A list of license plates.
|
|
12
|
+
# @return [Array<Mindee::TextField>]
|
|
13
|
+
attr_reader :license_plates
|
|
14
|
+
|
|
15
|
+
# @param prediction [Hash]
|
|
16
|
+
# @param page_id [Integer, nil]
|
|
17
|
+
def initialize(prediction, page_id)
|
|
18
|
+
super
|
|
19
|
+
@license_plates = []
|
|
20
|
+
prediction['license_plates'].each do |item|
|
|
21
|
+
@license_plates.push(TextField.new(item, page_id))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
license_plates = @license_plates.map(&:value).join(', ')
|
|
27
|
+
out_str = String.new
|
|
28
|
+
out_str << "\n:License plates: #{license_plates}".rstrip
|
|
29
|
+
out_str[1..].to_s
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
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 invoice.
|
|
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 invoice.
|
|
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 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 receipt category among predefined classes.
|
|
69
|
+
# @return [Mindee::TextField]
|
|
70
|
+
attr_reader :category
|
|
71
|
+
# The receipt sub-category among predefined classes.
|
|
72
|
+
# @return [Mindee::TextField]
|
|
73
|
+
attr_reader :subcategory
|
|
74
|
+
# A classification field, that can return 4 values : 'EXPENSE RECEIPT' ,
|
|
75
|
+
# '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
|