ng-bank-parser 0.1.4 → 0.1.5
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 +4 -0
- data/README.md +11 -7
- data/lib/ng-bank-parser.rb +2 -0
- data/lib/ng-bank-parser/banks.rb +38 -20
- data/lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf +0 -0
- data/lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf +0 -0
- data/lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf +160 -0
- data/lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf +0 -0
- data/lib/ng-bank-parser/parsers/accessbank-pdf-parser.rb +66 -0
- data/lib/ng-bank-parser/parsers/accessbank-pdf-parser/helpers.rb +167 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser.rb +77 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser/hb_constants.rb +44 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser/hb_transaction_helpers.rb +112 -0
- data/lib/ng-bank-parser/parsers/uba-pdf-parser/classes/string.rb +4 -0
- data/lib/ng-bank-parser/router.rb +5 -21
- data/lib/ng-bank-parser/version.rb +1 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6521c62889ffd5475c6f76e04f79b01aa0d744bc
|
4
|
+
data.tar.gz: a8b6dd72ce1d0b64872fa4e46b4e1ac9b1859360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15818a961c4c2745f6e6fc3ffffeffa20ff0e124c4caed2f6a8515abfebd4dbfc8516e6954f7599f3b72665d5cf55ee14d75d266d585eb006bd28f4f2aea64b6
|
7
|
+
data.tar.gz: 194188f43d96f693f8cdedfc38730bb5aa2f97ef0dd42505230e0c247ce5d06fd9c16b1c2bddc07a118410bec25e76f2f43cfc061066bbaa75d1e546fbda81ea
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Using the gem is pretty straightforward and simple.
|
|
29
29
|
result = NgBankParser::Router.parse(bank_key, file_path, password)
|
30
30
|
```
|
31
31
|
|
32
|
-
`
|
32
|
+
`bank_key` is the key of the bank that provides the statement. There's a [list of supported banks and formats](#list-of-supported-banks) below, therefore use the key provided for each bank.
|
33
33
|
|
34
34
|
`file_path` is obviously where the file you're trying to parse exists.
|
35
35
|
|
@@ -71,27 +71,31 @@ transaction = {
|
|
71
71
|
remarks: remarks,
|
72
72
|
ref: reference_id, # as provided by the statment
|
73
73
|
}
|
74
|
-
|
74
|
+
|
75
75
|
```
|
76
76
|
|
77
77
|
## List of Supported Banks
|
78
78
|
|
79
|
-
United Bank for Africa:
|
79
|
+
United Bank for Africa:
|
80
80
|
- key: uba
|
81
81
|
- supported formats: pdf
|
82
82
|
|
83
|
-
Guaranty Trust Bank:
|
83
|
+
Guaranty Trust Bank:
|
84
84
|
- key: gtb
|
85
85
|
- supported formats: xls, xlsx
|
86
|
-
|
87
|
-
First Bank:
|
86
|
+
|
87
|
+
First Bank:
|
88
88
|
- key: firstbank
|
89
89
|
- supported formats: pdf
|
90
90
|
|
91
|
+
Heritage Bank:
|
92
|
+
- key: hb
|
93
|
+
- supported formats: pdf
|
94
|
+
|
91
95
|
## Contributing
|
92
96
|
|
93
97
|
Documentation on contribution can be found in the contribution wiki
|
94
98
|
|
95
99
|
## License
|
96
100
|
|
97
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
101
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/ng-bank-parser.rb
CHANGED
@@ -4,7 +4,9 @@ require "ng-bank-parser/router"
|
|
4
4
|
require "ng-bank-parser/pdf-unlocker"
|
5
5
|
require "ng-bank-parser/parsers/gtb-excel-parser"
|
6
6
|
require "ng-bank-parser/parsers/uba-pdf-parser"
|
7
|
+
require "ng-bank-parser/parsers/hb-pdf-parser"
|
7
8
|
require "ng-bank-parser/parsers/firstbank-pdf-parser"
|
9
|
+
require "ng-bank-parser/parsers/accessbank-pdf-parser"
|
8
10
|
|
9
11
|
module NgBankParser
|
10
12
|
# Your code goes here...
|
data/lib/ng-bank-parser/banks.rb
CHANGED
@@ -4,30 +4,48 @@ module NgBankParser
|
|
4
4
|
key: "gtb",
|
5
5
|
name: "Guaranty Trust Bank",
|
6
6
|
parsers: [{
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
format: "excel",
|
8
|
+
valid: "lib/ng-bank-parser/fixtures/gtb-excel-valid.xlsx",
|
9
|
+
invalid: "lib/ng-bank-parser/fixtures/gtb-excel-invalid.pdf",
|
10
|
+
extensions: ["xls","xlsx"]
|
11
11
|
}]
|
12
12
|
},{
|
13
|
+
key: "hb",
|
14
|
+
name: "Heritage Bank",
|
15
|
+
parsers: [{
|
16
|
+
format: "pdf",
|
17
|
+
valid: "lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf",
|
18
|
+
invalid: "lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf",
|
19
|
+
extensions: ["pdf"]
|
20
|
+
}]
|
21
|
+
},{
|
13
22
|
key: "uba",
|
14
23
|
name: "United Bank for Africa",
|
15
24
|
parsers: [{
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
format: "pdf",
|
26
|
+
valid: "lib/ng-bank-parser/fixtures/uba-pdf-valid.pdf",
|
27
|
+
invalid: "lib/ng-bank-parser/fixtures/uba-pdf-invalid.pdf",
|
28
|
+
extensions: ["pdf"]
|
29
|
+
}]
|
21
30
|
},{
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
key: "firstbank",
|
32
|
+
name: "First Bank",
|
33
|
+
parsers: [{
|
34
|
+
format: "pdf",
|
35
|
+
valid: "lib/ng-bank-parser/fixtures/firstbank-pdf-valid.pdf",
|
36
|
+
fixture_password: 19856,
|
37
|
+
invalid: "lib/ng-bank-parser/fixtures/firstbank-pdf-invalid.pdf",
|
38
|
+
extensions: ["pdf"]
|
39
|
+
}]
|
40
|
+
},{
|
41
|
+
key: "accessbank",
|
42
|
+
name: "Access Bank Plc",
|
43
|
+
parsers: [{
|
44
|
+
format: "pdf",
|
45
|
+
valid: "lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf",
|
46
|
+
invalid: "lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf",
|
47
|
+
extensions: ["pdf"]
|
48
|
+
}]
|
49
|
+
}]
|
32
50
|
end
|
33
|
-
end
|
51
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,160 @@
|
|
1
|
+
%PDF-1.3
|
2
|
+
3 0 obj
|
3
|
+
<</Type /Page
|
4
|
+
/Parent 1 0 R
|
5
|
+
/Resources 2 0 R
|
6
|
+
/Contents 4 0 R>>
|
7
|
+
endobj
|
8
|
+
4 0 obj
|
9
|
+
<</Length 165>>
|
10
|
+
stream
|
11
|
+
0.57 w
|
12
|
+
0 G
|
13
|
+
BT
|
14
|
+
/F1 16 Tf
|
15
|
+
16 TL
|
16
|
+
0 g
|
17
|
+
56.69 785.20 Td
|
18
|
+
(Hello world!) Tj
|
19
|
+
ET
|
20
|
+
BT
|
21
|
+
/F1 16 Tf
|
22
|
+
16 TL
|
23
|
+
0 g
|
24
|
+
56.69 756.85 Td
|
25
|
+
(This a random PDF file for testing our parsers.) Tj
|
26
|
+
ET
|
27
|
+
endstream
|
28
|
+
endobj
|
29
|
+
1 0 obj
|
30
|
+
<</Type /Pages
|
31
|
+
/Kids [3 0 R ]
|
32
|
+
/Count 1
|
33
|
+
/MediaBox [0 0 595.28 841.89]
|
34
|
+
>>
|
35
|
+
endobj
|
36
|
+
5 0 obj
|
37
|
+
<</BaseFont/Helvetica/Type/Font
|
38
|
+
/Encoding/WinAnsiEncoding
|
39
|
+
/Subtype/Type1>>
|
40
|
+
endobj
|
41
|
+
6 0 obj
|
42
|
+
<</BaseFont/Helvetica-Bold/Type/Font
|
43
|
+
/Encoding/WinAnsiEncoding
|
44
|
+
/Subtype/Type1>>
|
45
|
+
endobj
|
46
|
+
7 0 obj
|
47
|
+
<</BaseFont/Helvetica-Oblique/Type/Font
|
48
|
+
/Encoding/WinAnsiEncoding
|
49
|
+
/Subtype/Type1>>
|
50
|
+
endobj
|
51
|
+
8 0 obj
|
52
|
+
<</BaseFont/Helvetica-BoldOblique/Type/Font
|
53
|
+
/Encoding/WinAnsiEncoding
|
54
|
+
/Subtype/Type1>>
|
55
|
+
endobj
|
56
|
+
9 0 obj
|
57
|
+
<</BaseFont/Courier/Type/Font
|
58
|
+
/Encoding/WinAnsiEncoding
|
59
|
+
/Subtype/Type1>>
|
60
|
+
endobj
|
61
|
+
10 0 obj
|
62
|
+
<</BaseFont/Courier-Bold/Type/Font
|
63
|
+
/Encoding/WinAnsiEncoding
|
64
|
+
/Subtype/Type1>>
|
65
|
+
endobj
|
66
|
+
11 0 obj
|
67
|
+
<</BaseFont/Courier-Oblique/Type/Font
|
68
|
+
/Encoding/WinAnsiEncoding
|
69
|
+
/Subtype/Type1>>
|
70
|
+
endobj
|
71
|
+
12 0 obj
|
72
|
+
<</BaseFont/Courier-BoldOblique/Type/Font
|
73
|
+
/Encoding/WinAnsiEncoding
|
74
|
+
/Subtype/Type1>>
|
75
|
+
endobj
|
76
|
+
13 0 obj
|
77
|
+
<</BaseFont/Times-Roman/Type/Font
|
78
|
+
/Encoding/WinAnsiEncoding
|
79
|
+
/Subtype/Type1>>
|
80
|
+
endobj
|
81
|
+
14 0 obj
|
82
|
+
<</BaseFont/Times-Bold/Type/Font
|
83
|
+
/Encoding/WinAnsiEncoding
|
84
|
+
/Subtype/Type1>>
|
85
|
+
endobj
|
86
|
+
15 0 obj
|
87
|
+
<</BaseFont/Times-Italic/Type/Font
|
88
|
+
/Encoding/WinAnsiEncoding
|
89
|
+
/Subtype/Type1>>
|
90
|
+
endobj
|
91
|
+
16 0 obj
|
92
|
+
<</BaseFont/Times-BoldItalic/Type/Font
|
93
|
+
/Encoding/WinAnsiEncoding
|
94
|
+
/Subtype/Type1>>
|
95
|
+
endobj
|
96
|
+
2 0 obj
|
97
|
+
<<
|
98
|
+
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
|
99
|
+
/Font <<
|
100
|
+
/F1 5 0 R
|
101
|
+
/F2 6 0 R
|
102
|
+
/F3 7 0 R
|
103
|
+
/F4 8 0 R
|
104
|
+
/F5 9 0 R
|
105
|
+
/F6 10 0 R
|
106
|
+
/F7 11 0 R
|
107
|
+
/F8 12 0 R
|
108
|
+
/F9 13 0 R
|
109
|
+
/F10 14 0 R
|
110
|
+
/F11 15 0 R
|
111
|
+
/F12 16 0 R
|
112
|
+
>>
|
113
|
+
/XObject <<
|
114
|
+
>>
|
115
|
+
>>
|
116
|
+
endobj
|
117
|
+
17 0 obj
|
118
|
+
<<
|
119
|
+
/Producer (jsPDF 20120619)
|
120
|
+
/CreationDate (D:20150829130726)
|
121
|
+
>>
|
122
|
+
endobj
|
123
|
+
18 0 obj
|
124
|
+
<<
|
125
|
+
/Type /Catalog
|
126
|
+
/Pages 1 0 R
|
127
|
+
/OpenAction [3 0 R /FitH null]
|
128
|
+
/PageLayout /OneColumn
|
129
|
+
>>
|
130
|
+
endobj
|
131
|
+
xref
|
132
|
+
0 19
|
133
|
+
0000000000 65535 f
|
134
|
+
0000000301 00000 n
|
135
|
+
0000001530 00000 n
|
136
|
+
0000000009 00000 n
|
137
|
+
0000000087 00000 n
|
138
|
+
0000000388 00000 n
|
139
|
+
0000000478 00000 n
|
140
|
+
0000000573 00000 n
|
141
|
+
0000000671 00000 n
|
142
|
+
0000000773 00000 n
|
143
|
+
0000000861 00000 n
|
144
|
+
0000000955 00000 n
|
145
|
+
0000001052 00000 n
|
146
|
+
0000001153 00000 n
|
147
|
+
0000001246 00000 n
|
148
|
+
0000001338 00000 n
|
149
|
+
0000001432 00000 n
|
150
|
+
0000001754 00000 n
|
151
|
+
0000001836 00000 n
|
152
|
+
trailer
|
153
|
+
<<
|
154
|
+
/Size 19
|
155
|
+
/Root 18 0 R
|
156
|
+
/Info 17 0 R
|
157
|
+
>>
|
158
|
+
startxref
|
159
|
+
1940
|
160
|
+
%%EOF
|
Binary file
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'pdf-reader'
|
2
|
+
require 'open-uri'
|
3
|
+
require_relative 'accessbank-pdf-parser/helpers'
|
4
|
+
require_relative '../pdf-unlocker.rb'
|
5
|
+
|
6
|
+
module NgBankParser
|
7
|
+
class AccessbankPdf
|
8
|
+
extend AccessbankPdfHelpers
|
9
|
+
|
10
|
+
def self.parse(path, password=nil)
|
11
|
+
|
12
|
+
unless File.extname(path) == '.pdf'
|
13
|
+
return error('This file format is not supported yet. Please try uploading a pdf')
|
14
|
+
end
|
15
|
+
|
16
|
+
@pdf = open_pdf(path, password)
|
17
|
+
|
18
|
+
if @pdf
|
19
|
+
pdf_as_array = pdf_to_a(@pdf)
|
20
|
+
|
21
|
+
unless has_valid_details?(pdf_as_array)
|
22
|
+
return error("Couldn't fetch account details from pdf")
|
23
|
+
end
|
24
|
+
|
25
|
+
response = get_details(pdf_as_array)
|
26
|
+
return success(response)
|
27
|
+
else
|
28
|
+
return error("Couldn't open the pdf file passed")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
def self.open_pdf(path, password)
|
35
|
+
begin
|
36
|
+
file = open(path)
|
37
|
+
@pdf = PDF::Reader.new(file)
|
38
|
+
rescue PDF::Reader::EncryptedPDFError
|
39
|
+
if password
|
40
|
+
return unlock_pdf(path, password)
|
41
|
+
else
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
rescue
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.unlock_pdf(path, password)
|
51
|
+
unlocker = PDFUnlocker.new
|
52
|
+
response = unlocker.unlock(path, password)
|
53
|
+
|
54
|
+
return nil unless response
|
55
|
+
|
56
|
+
if response.include? 'Unlock Failed'
|
57
|
+
return nil
|
58
|
+
else
|
59
|
+
temp = StringIO.new
|
60
|
+
temp.write(response)
|
61
|
+
return PDF::Reader.new(temp)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module AccessbankPdfHelpers
|
2
|
+
|
3
|
+
def pdf_to_a(pdf)
|
4
|
+
lines = []
|
5
|
+
|
6
|
+
pdf.pages.each do |page|
|
7
|
+
remove_line_spacing(page.text).each do |line|
|
8
|
+
lines << line.strip.split(/\s\s+/)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
return lines
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def get_details(pdf_array)
|
17
|
+
|
18
|
+
details = Hash.new
|
19
|
+
|
20
|
+
details[:bank_name] = 'Access Bank'
|
21
|
+
details[:account_number] = get_account_number(pdf_array)
|
22
|
+
details[:account_name] = get_account_name(pdf_array)
|
23
|
+
|
24
|
+
date_range = get_date_range(pdf_array)
|
25
|
+
|
26
|
+
details[:from_date] = string_with_slash_to_date(date_range[:from])
|
27
|
+
details[:to_date] = string_with_slash_to_date(date_range[:to])
|
28
|
+
details[:transactions] = get_transactions(pdf_array)
|
29
|
+
|
30
|
+
return details
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def has_valid_details?(pdf_array)
|
35
|
+
return (get_account_number(pdf_array) and get_account_name(pdf_array) and get_date_range(pdf_array))
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def get_account_name(pdf_array)
|
40
|
+
|
41
|
+
pdf_array.each do |row|
|
42
|
+
row.each_with_index do |cell, index|
|
43
|
+
if cell == 'Account Name'
|
44
|
+
return row[index + 1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def get_account_number(pdf_array)
|
54
|
+
|
55
|
+
pdf_array.each do |row|
|
56
|
+
row.each do |cell|
|
57
|
+
if cell =~ /[0-9]{10}/
|
58
|
+
return cell
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def get_date_range(pdf_array)
|
68
|
+
pdf_array.each_with_index do |row, index|
|
69
|
+
if row.count == 1
|
70
|
+
if row.first =~ /([0-3][0-9])[\/]([0-1][0-9])[\/]([0-9]{4})/ and pdf_array[index + 1].first =~ /([0-3][0-9])[\/]([0-1][0-9])[\/]([0-9]{4})/
|
71
|
+
return { from: row.first, to: pdf_array[index + 1].first }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def get_transactions(pdf_array)
|
81
|
+
start_index = get_first_line_of_transactions(pdf_array)
|
82
|
+
transactions = []
|
83
|
+
|
84
|
+
opening_balance = pdf_array[1][1]
|
85
|
+
|
86
|
+
pdf_array[start_index..-1].each do |transaction|
|
87
|
+
if valid_transaction?(transaction)
|
88
|
+
|
89
|
+
transaction_hash= Hash.new
|
90
|
+
transaction_hash[:date] = string_with_dash_to_date(transaction[0])
|
91
|
+
transaction_hash[:remarks] = transaction[2]
|
92
|
+
|
93
|
+
if transaction.count == 6 # ref code present
|
94
|
+
transaction_hash[:ref] = transaction[3]
|
95
|
+
transaction_hash[:amount] = amount_to_f(transaction[4])
|
96
|
+
transaction_hash[:balance] = amount_to_f(transaction[5])
|
97
|
+
transaction_hash[:type] = transaction_type(transaction_hash[:balance], opening_balance)
|
98
|
+
else #no ref code
|
99
|
+
transaction_hash[:ref] = ''
|
100
|
+
transaction_hash[:amount] = amount_to_f(transaction[3])
|
101
|
+
transaction_hash[:balance] = amount_to_f(transaction[4])
|
102
|
+
transaction_hash[:type] = transaction_type(transaction_hash[:balance], opening_balance)
|
103
|
+
end
|
104
|
+
|
105
|
+
opening_balance = transaction_hash[:balance]
|
106
|
+
transactions << transaction_hash
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
return transactions
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
def error(msg)
|
117
|
+
{ status: 0, message: msg }
|
118
|
+
end
|
119
|
+
|
120
|
+
def success(data)
|
121
|
+
{ status: 1, data: data }
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
def remove_line_spacing(str)
|
128
|
+
str.gsub(/^$\n/, '').lines
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def transaction_type(current_balance, former_balance)
|
133
|
+
former_balance = amount_to_f(former_balance) if former_balance.is_a?(String)
|
134
|
+
|
135
|
+
current_balance - former_balance > 1 ? 'credit' : 'debit'
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def amount_to_f(amount)
|
140
|
+
amount.gsub(/[^\d\.]/, '').to_f
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def get_first_line_of_transactions(pdf_array)
|
145
|
+
start = -1
|
146
|
+
|
147
|
+
pdf_array.each_with_index do |row, index|
|
148
|
+
start = index + 1 if row[0] == 'Date'
|
149
|
+
end
|
150
|
+
|
151
|
+
return start
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def valid_transaction?(line)
|
156
|
+
line[0] =~ /([0-3][0-9])[\-]([a-zA-Z]+)[\-](1[0-9])/ #first column matches access bank transaction date pattern
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def string_with_dash_to_date(date_str)
|
161
|
+
Date.strptime(date_str, '%d-%b-%y')
|
162
|
+
end
|
163
|
+
|
164
|
+
def string_with_slash_to_date(date_str)
|
165
|
+
Date.strptime(date_str, '%d/%m/%Y')
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'pdf-reader'
|
2
|
+
require 'open-uri'
|
3
|
+
require_relative 'hb-pdf-parser/hb_transaction_helpers'
|
4
|
+
|
5
|
+
module NgBankParser
|
6
|
+
class HbPdf
|
7
|
+
|
8
|
+
extend HbTransactionHelpers
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def parse(url, password = nil)
|
12
|
+
unless ACCEPTED_FORMATS.include? File.extname(url)
|
13
|
+
return INVALID_FILE_FORMAT_STRING
|
14
|
+
end
|
15
|
+
|
16
|
+
file = open(url)
|
17
|
+
|
18
|
+
@reader = PDF::Reader.new(file)
|
19
|
+
|
20
|
+
if pdf_is_valid? file
|
21
|
+
generate_parse
|
22
|
+
|
23
|
+
return account_statement_payload
|
24
|
+
else
|
25
|
+
return invalid_payload
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def generate_parse
|
32
|
+
set_account_name
|
33
|
+
set_account_number
|
34
|
+
set_end_date
|
35
|
+
set_start_date
|
36
|
+
extract_transactions
|
37
|
+
end
|
38
|
+
|
39
|
+
def extract_transactions
|
40
|
+
@transactions = []
|
41
|
+
|
42
|
+
@reader.pages.each do |page|
|
43
|
+
page.text.remove_empty_lines.lines.each do |line|
|
44
|
+
transaction_items_list = transaction_items(line)
|
45
|
+
if transaction_items_list[TRANSACTION_DATE_INDEX].is_date?
|
46
|
+
if transaction_is_a_debit?(transaction_items_list, line)
|
47
|
+
create_transaction(line, 'debit')
|
48
|
+
elsif transaction_is_a_credit?(transaction_items_list, line)
|
49
|
+
create_transaction(line, 'credit')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def account_statement_payload
|
57
|
+
return {
|
58
|
+
status: VALID_ACCOUNT_STATUS,
|
59
|
+
data: {
|
60
|
+
bank_name: $Banks[1][:name],
|
61
|
+
account_number: @account_number,
|
62
|
+
account_name: @account_name,
|
63
|
+
from_date: @start_date,
|
64
|
+
to_date: @end_date,
|
65
|
+
transactions: @transactions
|
66
|
+
# reader: @reader
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def invalid_payload
|
72
|
+
{status: INVALID_ACCOUNT_STATUS,
|
73
|
+
message: INVALID_ACCOUNT_STATEMENT}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module NgBankParser
|
2
|
+
module HbConstants
|
3
|
+
ACCEPTED_FORMATS = ['.pdf']
|
4
|
+
|
5
|
+
INVALID_ACCOUNT_STATUS = 0
|
6
|
+
VALID_ACCOUNT_STATUS = 1
|
7
|
+
|
8
|
+
SECOND_PAGE_INDEX = 1
|
9
|
+
DURATION_DATE_LINE_INDEX = 3
|
10
|
+
ACCOUNT_NAME_LINE_INDEX = 5
|
11
|
+
ACCOUNT_NUMBER_LINE_INDEX = 6
|
12
|
+
TRANSACTION_HEADER_INDEX = 11
|
13
|
+
TRANSACTION_DATE_INDEX = 0
|
14
|
+
TRANSACTION_DEBIT_INDEX = -3
|
15
|
+
TRANSACTION_CREDIT_INDEX = -2
|
16
|
+
TRANSACTION_BALANCE = 131
|
17
|
+
TRANSACTION_BALANCE_END = 155
|
18
|
+
TRANSACTION_REMARKS = 32
|
19
|
+
TRANSACTION_REMARKS_END = 92
|
20
|
+
TRANSACTION_REFERENCE = 20
|
21
|
+
TRANSACTION_REFERENCE_END = 30
|
22
|
+
TRANSACTION_DEBIT = 92
|
23
|
+
TRANSACTION_DEBIT_END = 105
|
24
|
+
TRANSACTION_CREDIT = 110
|
25
|
+
TRANSACTION_CREDIT_END = 130
|
26
|
+
|
27
|
+
|
28
|
+
ACCOUNT_NAME_FIRST_MAKER = 'name'
|
29
|
+
ACCOUNT_NAME_SECOND_MAKER = 'debit turnover'
|
30
|
+
ACCOUNT_NUMBER_MAKER = 'account number'
|
31
|
+
END_DATE_MAKER = '-'
|
32
|
+
|
33
|
+
INVALID_FILE_FORMAT_STRING = 'File Format Not Valid'
|
34
|
+
INVALID_ACCOUNT_STATEMENT = 'File Is Not A Valid Heritage Bank Account Statement'
|
35
|
+
|
36
|
+
CORRECT_RC_NUMBER = 'RC No 9868'
|
37
|
+
|
38
|
+
COLUMN_ARRANGEMENT = 'valuedatereferencenarrationdebitcreditbalance'
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative 'hb_constants'
|
2
|
+
|
3
|
+
module NgBankParser
|
4
|
+
module HbTransactionHelpers
|
5
|
+
include HbConstants
|
6
|
+
def pdf_is_valid?(file)
|
7
|
+
has_transaction_page? && has_transaction_columns? && has_correct_rc_number? && !has_encryption?(file)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def has_transaction_page?
|
13
|
+
@reader.pages[SECOND_PAGE_INDEX].present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_correct_rc_number?
|
17
|
+
@reader.pages.first.text.remove_empty_lines.lines.first.strip == CORRECT_RC_NUMBER
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_transaction_columns?
|
21
|
+
@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[TRANSACTION_HEADER_INDEX].remove_white_spaces == COLUMN_ARRANGEMENT
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_encryption?(file)
|
25
|
+
begin
|
26
|
+
@reader = PDF::Reader.new(file)
|
27
|
+
false
|
28
|
+
rescue PDF::Reader::EncryptedPDFError
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_account_name
|
34
|
+
@account_name = @reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[ACCOUNT_NAME_LINE_INDEX].get_text_between_markers(ACCOUNT_NAME_FIRST_MAKER, ACCOUNT_NAME_SECOND_MAKER).remove_white_spaces
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_account_number
|
38
|
+
@account_number = @reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[ACCOUNT_NUMBER_LINE_INDEX].strip.get_text_after_marker(ACCOUNT_NUMBER_MAKER).remove_white_spaces
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_end_date
|
42
|
+
@end_date = Date.parse(@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[DURATION_DATE_LINE_INDEX].get_text_after_marker(END_DATE_MAKER).remove_white_spaces)
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_start_date
|
46
|
+
@start_date = Date.parse(@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[DURATION_DATE_LINE_INDEX].get_text_between_markers(' ', END_DATE_MAKER).remove_white_spaces)
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_transaction_attr(line)
|
50
|
+
transaction_object = {}
|
51
|
+
transaction_object[:date] = transaction_date(line)
|
52
|
+
|
53
|
+
transaction_object[:balance] = transaction_balance(line)
|
54
|
+
transaction_object[:remarks] = transaction_remarks(line)
|
55
|
+
transaction_object[:ref] = transaction_reference(line)
|
56
|
+
transaction_object
|
57
|
+
end
|
58
|
+
|
59
|
+
def transaction_is_a_debit?(transaction_items_list, line)
|
60
|
+
transaction_items_list[TRANSACTION_DEBIT_INDEX] && debit_amount(line) > 0.00
|
61
|
+
end
|
62
|
+
|
63
|
+
def transaction_is_a_credit?(transaction_items_list, line)
|
64
|
+
transaction_items_list[TRANSACTION_CREDIT_INDEX] && credit_amount(line) > 0.00
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_transaction(line, type)
|
68
|
+
transaction = set_transaction_attr(line)
|
69
|
+
set_transaction_type(transaction, type, line)
|
70
|
+
@transactions << transaction
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_transaction_type(transaction, type, line)
|
74
|
+
if type == 'debit'
|
75
|
+
transaction[:amount] = debit_amount(line)
|
76
|
+
transaction[:type] = type
|
77
|
+
elsif type == 'credit'
|
78
|
+
transaction[:amount] = credit_amount(line)
|
79
|
+
transaction[:type] = type
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def transaction_date(line)
|
84
|
+
Date.parse(line[0..19].remove_white_spaces)
|
85
|
+
end
|
86
|
+
|
87
|
+
def transaction_balance(line)
|
88
|
+
line[TRANSACTION_BALANCE..TRANSACTION_BALANCE_END].get_numbers
|
89
|
+
end
|
90
|
+
|
91
|
+
def transaction_remarks(line)
|
92
|
+
line[TRANSACTION_REMARKS..TRANSACTION_REMARKS_END].remove_white_spaces
|
93
|
+
end
|
94
|
+
|
95
|
+
def transaction_reference(line)
|
96
|
+
line[TRANSACTION_REFERENCE..TRANSACTION_REFERENCE_END].remove_white_spaces
|
97
|
+
end
|
98
|
+
|
99
|
+
def debit_amount(line)
|
100
|
+
line[TRANSACTION_DEBIT..TRANSACTION_DEBIT_END].get_numbers
|
101
|
+
end
|
102
|
+
|
103
|
+
def credit_amount(line)
|
104
|
+
line[TRANSACTION_CREDIT..TRANSACTION_CREDIT_END].get_numbers
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def transaction_items(line)
|
109
|
+
line.strip.split(" ").reject(&:empty?)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
|
3
1
|
module NgBankParser
|
4
2
|
class Router
|
5
3
|
@banks = $Banks
|
@@ -14,15 +12,11 @@ module NgBankParser
|
|
14
12
|
|
15
13
|
if @selected_bank.nil?
|
16
14
|
return {status: 0, message: "Your bank is not yet supported"}
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
extensions = @selected_bank[:parsers].map {|e| e[:extensions]}
|
24
|
-
@supported_extensions = extensions.reduce(:concat)
|
25
|
-
pick_parser()
|
15
|
+
else
|
16
|
+
extensions = @selected_bank[:parsers].map {|e| e[:extensions]}
|
17
|
+
@supported_extensions = extensions.reduce(:concat)
|
18
|
+
pick_parser()
|
19
|
+
end
|
26
20
|
end
|
27
21
|
|
28
22
|
private
|
@@ -48,15 +42,5 @@ module NgBankParser
|
|
48
42
|
parser_response = class_object.parse(@path, @password)
|
49
43
|
end
|
50
44
|
|
51
|
-
def self.file_exists(path)
|
52
|
-
return true if File.exists?(path)
|
53
|
-
|
54
|
-
uri = URI(path)
|
55
|
-
|
56
|
-
request = Net::HTTP.new uri.host
|
57
|
-
response= request.request_head uri.path
|
58
|
-
return response.code.to_i == 200
|
59
|
-
end
|
60
|
-
|
61
45
|
end
|
62
46
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ng-bank-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Opemipo Aikomo
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-09-22 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -148,18 +148,27 @@ files:
|
|
148
148
|
- bin/setup
|
149
149
|
- lib/ng-bank-parser.rb
|
150
150
|
- lib/ng-bank-parser/banks.rb
|
151
|
+
- lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf
|
152
|
+
- lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf
|
151
153
|
- lib/ng-bank-parser/fixtures/firstbank-pdf-invalid.pdf
|
152
154
|
- lib/ng-bank-parser/fixtures/firstbank-pdf-valid.pdf
|
153
155
|
- lib/ng-bank-parser/fixtures/gtb-excel-invalid.pdf
|
154
156
|
- lib/ng-bank-parser/fixtures/gtb-excel-valid.xls
|
155
157
|
- lib/ng-bank-parser/fixtures/gtb-excel-valid.xlsx
|
158
|
+
- lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf
|
159
|
+
- lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf
|
156
160
|
- lib/ng-bank-parser/fixtures/uba-pdf-invalid.pdf
|
157
161
|
- lib/ng-bank-parser/fixtures/uba-pdf-valid.pdf
|
162
|
+
- lib/ng-bank-parser/parsers/accessbank-pdf-parser.rb
|
163
|
+
- lib/ng-bank-parser/parsers/accessbank-pdf-parser/helpers.rb
|
158
164
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser.rb
|
159
165
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser/helpers.rb
|
160
166
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser/statement_utils.rb
|
161
167
|
- lib/ng-bank-parser/parsers/gtb-excel-parser.rb
|
162
168
|
- lib/ng-bank-parser/parsers/gtb-excel-parser/helpers.rb
|
169
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser.rb
|
170
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser/hb_constants.rb
|
171
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser/hb_transaction_helpers.rb
|
163
172
|
- lib/ng-bank-parser/parsers/uba-pdf-parser.rb
|
164
173
|
- lib/ng-bank-parser/parsers/uba-pdf-parser/classes/string.rb
|
165
174
|
- lib/ng-bank-parser/parsers/uba-pdf-parser/classes/transaction.rb
|