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 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