banks 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b4a00cf96412abb83fb76706826a0c910b37a2a
4
- data.tar.gz: 1be0b0a0f77bf3e4aa6cd0ae6cbb6c48a6aab6f6
3
+ metadata.gz: 385c82b6a55091c4d6071ab61b593505b8fb7dc2
4
+ data.tar.gz: 3861b449f0c09a340418c58c4bbebb9736670f72
5
5
  SHA512:
6
- metadata.gz: 59f8b221ed528c6aa47e7921b6226efd2b98feae7339ac82324d7e766943871d713601b6e90e86d37bbb5de994c2e4a63d0dd5dc09177dbf97c6407a220db9d7
7
- data.tar.gz: d9c8ecc4a32632a77a4398a1bf422939f0898c5bd2bea3a2e9f74d489090a0e762121e7918441590953a88acc2fc46e00531f7ed32f53cdb5e33b8e75104636b
6
+ metadata.gz: 0cb51c6eb12dbd39aec9a9ecb8de162f4e6bcfda23c1536cd745ff03007448a15708687338ccf0e89591d2b314fde5e7d1dc32aecf12cd30028769b5195a892d
7
+ data.tar.gz: 00aba1b8e44303b17340a16759295f5960f7cceb2061caf6c28346d9a1bda1525e79250c213f1ee5f19a4b236b4b3b21e9ff88ecb1c1fe5c2c260f517a793f3c
data/lib/banks.rb CHANGED
@@ -1,5 +1,11 @@
1
- class Bank
2
- def self.hi
3
- puts "Hello world!"
4
- end
5
- end
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
@@ -0,0 +1,10 @@
1
+ module Banks
2
+ class Bank
3
+ protected
4
+ # Converts amount string to integer.
5
+ def convert_money amount_string
6
+ #amount = amount_string.gsub(/[^\d,\.]/, '') # As float
7
+ amount = amount_string.gsub(/[^\d-]/, '').to_i # As int
8
+ end
9
+ end
10
+ end
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
@@ -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.1
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-19 00:00:00.000000000 Z
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