banks 0.0.1 → 0.0.2
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/lib/banks.rb +11 -5
- data/lib/banks/account.rb +34 -0
- data/lib/banks/bank.rb +10 -0
- data/lib/banks/bofa.rb +119 -0
- data/lib/banks/transaction.rb +143 -0
- data/lib/banks/utils.rb +29 -0
- metadata +64 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 385c82b6a55091c4d6071ab61b593505b8fb7dc2
|
4
|
+
data.tar.gz: 3861b449f0c09a340418c58c4bbebb9736670f72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cb51c6eb12dbd39aec9a9ecb8de162f4e6bcfda23c1536cd745ff03007448a15708687338ccf0e89591d2b314fde5e7d1dc32aecf12cd30028769b5195a892d
|
7
|
+
data.tar.gz: 00aba1b8e44303b17340a16759295f5960f7cceb2061caf6c28346d9a1bda1525e79250c213f1ee5f19a4b236b4b3b21e9ff88ecb1c1fe5c2c260f517a793f3c
|
data/lib/banks.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require "watir-webdriver"
|
2
|
+
require "nokogiri"
|
3
|
+
require "json"
|
4
|
+
require "date"
|
5
|
+
require "active_support/all"
|
6
|
+
|
7
|
+
require "banks/account"
|
8
|
+
require "banks/bank"
|
9
|
+
require "banks/bofa"
|
10
|
+
require "banks/transaction"
|
11
|
+
require "banks/utils"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Banks
|
2
|
+
class Account
|
3
|
+
attr_accessor :name, :type, :balance, :transactions, :link
|
4
|
+
|
5
|
+
# Types
|
6
|
+
CREDIT = "Credit"
|
7
|
+
DEBIT = "Debit"
|
8
|
+
UNKNOWN = "Unknown"
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
transactions.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
# accounts - Array of accounts.
|
15
|
+
def merge_accounts (accounts)
|
16
|
+
@transactions = TransactionArray.new
|
17
|
+
accounts.each do |account|
|
18
|
+
if account.type == CREDIT
|
19
|
+
convert_to_debit account
|
20
|
+
end
|
21
|
+
@transactions.merge_array account.transactions
|
22
|
+
end
|
23
|
+
@type = DEBIT
|
24
|
+
transactions.sort_by_date
|
25
|
+
end
|
26
|
+
|
27
|
+
def convert_to_debit account
|
28
|
+
account.type = DEBIT
|
29
|
+
account.transactions.each do|transaction|
|
30
|
+
transaction.amount = transaction.amount*-1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/banks/bank.rb
ADDED
data/lib/banks/bofa.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
module Banks
|
2
|
+
# Note: If you are running the script for the first time, you need to go online
|
3
|
+
# and validate your computer by answering security questions.
|
4
|
+
# TODO: Ask the user to enter security questions.
|
5
|
+
class BankOfAmerica < Bank
|
6
|
+
# Returns all accounts with all transactions.
|
7
|
+
# Currently supports Credit and Debit accounts.
|
8
|
+
def get_data (user, password)
|
9
|
+
# Connect
|
10
|
+
browser = ::Watir::Browser.new #:"phantomjs"
|
11
|
+
browser.goto 'http://bankofamerica.com'
|
12
|
+
browser.text_field(:id => 'onlineId1').set user
|
13
|
+
browser.text_field(:id => 'passcode1').set password
|
14
|
+
browser.button(:id => 'hp-sign-in-btn').click
|
15
|
+
|
16
|
+
# Wait until logged in.
|
17
|
+
# Watir wait did not work on Windows, so we do a hack.
|
18
|
+
# http://watirwebdriver.com/waiting/
|
19
|
+
# http://stackoverflow.com/questions/3504322/watir-webdriver-wait-for-page-load
|
20
|
+
until browser.ul(:class=>"AccountItems").exists? do sleep 1 end
|
21
|
+
|
22
|
+
# Get accounts
|
23
|
+
accounts = get_accounts browser.html
|
24
|
+
|
25
|
+
accounts.each do |account|
|
26
|
+
browser.goto "https://secure.bankofamerica.com" + account.link
|
27
|
+
transactions = nil
|
28
|
+
if account.type == Account::DEBIT
|
29
|
+
transactions = get_debit_transactions browser.html
|
30
|
+
elsif account.type == Account::CREDIT
|
31
|
+
transactions = get_credit_transactions browser
|
32
|
+
end
|
33
|
+
account.transactions = transactions
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clean-up
|
37
|
+
browser.close
|
38
|
+
|
39
|
+
# Return accounts.
|
40
|
+
accounts
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
# Returns date object based on date_string. Today's date if string is nil or
|
45
|
+
# whitespace
|
46
|
+
def to_date date_string
|
47
|
+
result = nil
|
48
|
+
date_string.strip!
|
49
|
+
if date_string.nil? || date_string.empty? || date_string.eql?("Pending")
|
50
|
+
result = Date.today
|
51
|
+
elsif
|
52
|
+
result = Date.strptime(date_string, '%m/%d/%Y')
|
53
|
+
end
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
# Pass the browser and return an array with accounts.
|
58
|
+
def get_accounts(html)
|
59
|
+
accounts = Array.new
|
60
|
+
html_doc = ::Nokogiri::HTML(html)
|
61
|
+
html_doc.css('ul.AccountItems li').each do |account_li|
|
62
|
+
account = Account.new
|
63
|
+
account.name = account_li.css('span.AccountName a').text.strip
|
64
|
+
account.link = account_li.css('span.AccountName a')[0]['href']
|
65
|
+
account.balance = account_li.css('span.balanceValue').text.strip
|
66
|
+
|
67
|
+
account_type_text = account_li.css('div.AccountItem')[0]['class']
|
68
|
+
if account_type_text.include? "AccountItemDeposit"
|
69
|
+
account.type = Account::DEBIT
|
70
|
+
elsif account_type_text.include? "AccountItemCreditCard"
|
71
|
+
account.type = Account::CREDIT
|
72
|
+
else
|
73
|
+
account.type = Account::UNKNOWN
|
74
|
+
end
|
75
|
+
|
76
|
+
accounts << account
|
77
|
+
end
|
78
|
+
|
79
|
+
return accounts
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_debit_transactions(html)
|
83
|
+
transactions = TransactionArray.new
|
84
|
+
html_doc = ::Nokogiri::HTML(html)
|
85
|
+
|
86
|
+
html_doc.css('table.transaction-records tbody tr.record').each do |record_tr|
|
87
|
+
t = Transaction.new
|
88
|
+
t.date = to_date(record_tr.css('td.date-action span:not([class])').text.strip)
|
89
|
+
t.description = record_tr.css('td.description span.transTitleForEditDesc').text.strip
|
90
|
+
t.amount = convert_money(record_tr.css('td.amount').text.strip)
|
91
|
+
t.type = record_tr.css('td.type')[0]['title']
|
92
|
+
transactions << t
|
93
|
+
end
|
94
|
+
|
95
|
+
return transactions
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_credit_transactions(browser)
|
99
|
+
transactions = TransactionArray.new
|
100
|
+
html_doc = ::Nokogiri::HTML(browser.html)
|
101
|
+
|
102
|
+
# Navigate through each statement.
|
103
|
+
statement_quantity = html_doc.css('select#goto_select_trans_top option').length
|
104
|
+
statement_quantity.times do |i|
|
105
|
+
browser.select(:id => 'goto_select_trans_top').options[i].select
|
106
|
+
html_doc = ::Nokogiri::HTML(browser.html)
|
107
|
+
html_doc.css('table#transactions tbody tr').each do |record_tr|
|
108
|
+
t = Transaction.new
|
109
|
+
t.date = to_date(record_tr.css('td.trans-date-cell').text.strip)
|
110
|
+
t.description = record_tr.css('td.trans-desc-cell a.expand-trans-from-desc')[0].children[2].text.strip
|
111
|
+
t.amount = convert_money(record_tr.css('td.trans-amount-cell').text.strip)
|
112
|
+
t.type = record_tr.css('td.trans-type-cell div')[0]['title']
|
113
|
+
transactions << t
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return transactions
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Banks
|
2
|
+
class Transaction
|
3
|
+
attr_accessor :date, :description, :amount, :type, :category
|
4
|
+
def to_s
|
5
|
+
return date.to_s + " " + category.to_s + " " + Banks.amount_to_s(amount) + " " + type + " " + description
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class TransactionArray < Array
|
10
|
+
def total
|
11
|
+
total = 0
|
12
|
+
each do |transaction|
|
13
|
+
total = total + transaction.amount
|
14
|
+
end
|
15
|
+
Banks.amount_to_s(total)
|
16
|
+
end
|
17
|
+
|
18
|
+
def total_positive
|
19
|
+
total = 0
|
20
|
+
each do |transaction|
|
21
|
+
total = total + transaction.amount unless transaction.amount < 0
|
22
|
+
end
|
23
|
+
Banks.amount_to_s(total)
|
24
|
+
end
|
25
|
+
|
26
|
+
def total_negative
|
27
|
+
total = 0
|
28
|
+
each do |transaction|
|
29
|
+
total = total + transaction.amount unless transaction.amount > 0
|
30
|
+
end
|
31
|
+
Banks.amount_to_s(total)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sorts transactions by date, from newer to older.
|
35
|
+
def sort_by_date
|
36
|
+
sort! {|x, y| y.date <=> x.date}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sort by amount. Negative to positive.
|
40
|
+
def sort_by_amount
|
41
|
+
sort! {|x, y| x.amount <=> y.amount}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sort by category.
|
45
|
+
def sort_by_category
|
46
|
+
sort! {|x, y| x.category <=> y.category}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Remove transactions considered transfers to own accounts.
|
50
|
+
def remove_transfers
|
51
|
+
result = TransactionArray.new
|
52
|
+
each do |transaction|
|
53
|
+
should_add = true
|
54
|
+
|
55
|
+
# Currently unavailable.
|
56
|
+
puts "Removing transaction. " + transaction.to_s unless should_add == true
|
57
|
+
if should_add then result << transaction end
|
58
|
+
end
|
59
|
+
return result
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return transactions within a date range.
|
63
|
+
def by_date (begin_date, end_date)
|
64
|
+
result = TransactionArray.new
|
65
|
+
|
66
|
+
# Filter all less than end date.
|
67
|
+
temp_array = TransactionArray.new
|
68
|
+
each do |transaction|
|
69
|
+
if transaction.date <= end_date then temp_array << transaction end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Filter all greater than begin date.
|
73
|
+
temp_array.each do |transaction|
|
74
|
+
if transaction.date >= begin_date then result << transaction end
|
75
|
+
end
|
76
|
+
|
77
|
+
return result
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return current month transactions.
|
81
|
+
def current_month
|
82
|
+
return previous_month 0
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return last month transactions.
|
86
|
+
def last_month
|
87
|
+
return previous_month 1
|
88
|
+
end
|
89
|
+
|
90
|
+
# months_back - how many months to go back from current date.
|
91
|
+
def previous_month(months_back)
|
92
|
+
year = Date.today.year
|
93
|
+
month = Date.today.month
|
94
|
+
|
95
|
+
begin_date = Date.civil(year, month, 1) - months_back.month
|
96
|
+
end_date = begin_date.end_of_month
|
97
|
+
|
98
|
+
return by_date(begin_date, end_date)
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
result = ""
|
103
|
+
each do |transaction|
|
104
|
+
result = result + transaction.to_s + "\n"
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_s_by_cat
|
110
|
+
result = ""
|
111
|
+
current_cat = nil
|
112
|
+
total = 0
|
113
|
+
last_transaction = nil
|
114
|
+
each do |transaction|
|
115
|
+
if !current_cat.nil? && current_cat != transaction.category
|
116
|
+
result = result + "Total for category #{current_cat}: " + Banks.amount_to_s(total) + "\n\n"
|
117
|
+
total = 0
|
118
|
+
end
|
119
|
+
result = result + transaction.to_s + "\n"
|
120
|
+
total = total + transaction.amount
|
121
|
+
current_cat = transaction.category
|
122
|
+
last_transaction = transaction
|
123
|
+
end
|
124
|
+
if !last_transaction.nil? then
|
125
|
+
result = result + "Total for category #{current_cat}: " + Banks.amount_to_s(total) + "\n\n"
|
126
|
+
total = 0
|
127
|
+
end
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
# Merge array
|
132
|
+
def merge_array(merging_array)
|
133
|
+
merging_array.each do |transaction|
|
134
|
+
self << transaction
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def << (transaction)
|
139
|
+
super
|
140
|
+
#transaction.category = (CategoryPolicy.categorize transaction.description).upcase
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/lib/banks/utils.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Banks
|
2
|
+
def Banks.amount_to_s amount
|
3
|
+
amount < 0 ? positive = false : positive = true
|
4
|
+
amount_string = amount.to_s
|
5
|
+
amount_string.sub!("-","")
|
6
|
+
amount_string = amount_string.rjust(3, '0')
|
7
|
+
amount_string.insert(-3, ".")
|
8
|
+
amount_string.insert(0, "-") unless positive
|
9
|
+
amount_string
|
10
|
+
end
|
11
|
+
|
12
|
+
class CategoryPolicy
|
13
|
+
@@categories = nil
|
14
|
+
def self.categorize description
|
15
|
+
# Lazy read.
|
16
|
+
if @@categories.nil? then
|
17
|
+
data = IO.read "categories.json"
|
18
|
+
@@categories = JSON.parse data
|
19
|
+
end
|
20
|
+
|
21
|
+
@@categories["categories"].each do |category, payment_types|
|
22
|
+
payment_types.each do |value|
|
23
|
+
if description.upcase.include? value.upcase then return category end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return "Uncategorized"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: banks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jesus Herrera
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
12
|
-
dependencies:
|
11
|
+
date: 2016-06-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: watir-webdriver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.8'
|
13
69
|
description: Get transaction information from financial institutions
|
14
70
|
email: chuy.max@gmail.com
|
15
71
|
executables: []
|
@@ -17,6 +73,11 @@ extensions: []
|
|
17
73
|
extra_rdoc_files: []
|
18
74
|
files:
|
19
75
|
- lib/banks.rb
|
76
|
+
- lib/banks/account.rb
|
77
|
+
- lib/banks/bank.rb
|
78
|
+
- lib/banks/bofa.rb
|
79
|
+
- lib/banks/transaction.rb
|
80
|
+
- lib/banks/utils.rb
|
20
81
|
homepage: http://rubygems.org/gems/banks
|
21
82
|
licenses:
|
22
83
|
- MIT
|