Smile Bank to Ledger

Changes On Branch cascade_methods
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch cascade_methods Excluding Merge-Ins

This is equivalent to a diff from 5bdd7b804f to 1905cd0eb7

2015-07-10
10:42
Merge in the cascaded_methods approach. I've decided I prefer that check-in: 6a191c53d1 user: atomicules tags: trunk
2015-07-07
22:19
Report out balance as a comment at end of the file check-in: 565270916f user: atomicules tags: trunk
2015-07-06
20:57
Alternative approach to code, cascading methods

Since methods make no sense on their own, make each method call the method
before it so requirements cascade all the way back. Since each method now
returns `page` there is no need for `page` to be an instance variable. So
perhaps this is a bit cleaner? Leaf check-in: 1905cd0eb7 user: atomicules tags: cascade_methods

20:53
Create new branch named "cascade_methods" check-in: cdb88b0318 user: atomicules tags: cascade_methods
20:32
Create new branch named "pass_args" Closed-Leaf check-in: 7025864667 user: atomicules tags: pass_args
2015-07-04
13:36
Fix sorting/ordering of transactions

Need to get things in the right order for checking whether we've gone far
enough back and also for appending to the main transactions array and then
again for writing out.

Also add appropriate Expenses/Income placeholder check-in: 5bdd7b804f user: atomicules tags: trunk

11:21
Build it into a proper script

I'm not entirely sure whether a class structure is appropriate for this. At the
very least because I'm only ever creating one of them, but also because
although it's nice having the separate methods for each stage of the logon
process, etc, they don't really make sense, and cna't be used, as standalone
methods. I'm also not sure about the use of instance variables. Splitting
things out into methods almost requires them as passing arguments would be a
bit clumsy, but then it is almost like using global variables. So, all in all,
I've attempted to make it more Ruby-ish, but I think I've failed.

- I'd got credits and debits the wrong way round
- Adds in dealing with memorable date logon details check-in: 5454406454 user: atomicules tags: trunk


Changes to smile.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
60
61
62
63


64
65
66
67
68
69
70
71
72
73
74
75


76
77
78
79
80
81
82


83
84


85
86
87
88


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
require 'optparse'
require 'mechanize'
require 'nokogiri'

optparse = OptionParser.new do |opts|
	opts.on('-d', '--date DATE', "Date") { |d| Date_back = d }
end
optparse.parse!

class Smile
	attr_writer :date_back 
	attr_reader :page
	attr_reader :transactions

	def initialize
		@a = Mechanize.new
		@transactions = []
	end


	def transactions(date_back)
		@date_back = Date.parse(date_back)
		logon_start
		logon_passcode
		logon_other
		logon_bulletin
		recent_transactions
		previous_statements

		write_ledger
	end


	#All these private
	#Note sure how valid it is breaking these out into separate methods
	#Might make more sense in a rakefile as separate tasks with dependencies,
	#but these methods can't be used alone
	#Also, really not sure about using instance variables instead of passing args
	def logon_start
		@page = @a.get("https://banking.smile.co.uk/SmileWeb/start.do")
		#Since dealing with sensitive logon data don't want to get it from anywhere apart from keyboard input
		#Sortcode and Account number login
		puts "Enter sortcode"
		sortCode = gets.strip
		puts "Enter Account number"
		accountNumber = gets.strip
		@page.form.sortCode = sortCode
		@page.form.accountNumber = accountNumber
		@page = @page.form.submit
	end


	def logon_passcode


		#Passcode login
		doc = Nokogiri::HTML(@page.body)
		puts doc.xpath("//label")[0].text
		@page.form.firstPassCodeDigit = gets.strip
		puts doc.xpath("//label")[1].text
		@page.form.secondPassCodeDigit = gets.strip
		@page = @page.form.submit
	end


	def logon_other


		#Other logon details
		@page.form.fields.each do |field|
			unless field.type == "hidden"
				puts field.name
				field.value = gets.strip
			end
		end
		@page = @page.form.submit
	end


	def logon_bulletin


		if @page.form.action.include?("bulletinBoard")
			@page = @page.form.submit
		end
	end


	def recent_transactions


		@page = @a.click(@page.link_with(:text => "current account"))
		get_transactions


	end


	def previous_statements


		#Previous statements
		@page = @a.click(@page.link_with(:text => "previous statements"))
		#Get first page of previous statements
		@page = @a.click(@page.links_with(:href => %r{getDomestic} )[0])
		date = Date.today
		until date < @date_back
			get_transactions
			#On previous statement pages most recent transaction is at bottom
			#This is opposite to recent item page
			date = @transactions[-1][:date]
			@page = @a.click(@page.link_with(:text => "previous statement page"))
		end
	end


	def get_transactions
		doc = Nokogiri::HTML(@page.body)
		#Look for summaryTable
		rows = doc.xpath("//table[@class='summaryTable']//tr")
		#Skip first row (table headers, ths)
		rows = rows[1..-1]
		transactions = []
		rows.each do |row|
			#Skip last row, maybe











<










<
<
<
<
<

>










|






|
|
|




>
>

|

|

|
|




>
>

|





|




>
>
|
|





>
>
|
|
>
>




>
>

|

|


|



|




|
|







1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21





22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
require 'optparse'
require 'mechanize'
require 'nokogiri'

optparse = OptionParser.new do |opts|
	opts.on('-d', '--date DATE', "Date") { |d| Date_back = d }
end
optparse.parse!

class Smile
	attr_writer :date_back 

	attr_reader :transactions

	def initialize
		@a = Mechanize.new
		@transactions = []
	end


	def transactions(date_back)
		@date_back = Date.parse(date_back)





		previous_statements
		#previous calls recent (I could change this though so it reads more sensibly here)
		write_ledger
	end


	#All these private
	#Note sure how valid it is breaking these out into separate methods
	#Might make more sense in a rakefile as separate tasks with dependencies,
	#but these methods can't be used alone
	#Also, really not sure about using instance variables instead of passing args
	def logon_start
		page = @a.get("https://banking.smile.co.uk/SmileWeb/start.do")
		#Since dealing with sensitive logon data don't want to get it from anywhere apart from keyboard input
		#Sortcode and Account number login
		puts "Enter sortcode"
		sortCode = gets.strip
		puts "Enter Account number"
		accountNumber = gets.strip
		page.form.sortCode = sortCode
		page.form.accountNumber = accountNumber
		page = page.form.submit
	end


	def logon_passcode
		#Requires
		page = logon_start
		#Passcode login
		doc = Nokogiri::HTML(page.body)
		puts doc.xpath("//label")[0].text
		page.form.firstPassCodeDigit = gets.strip
		puts doc.xpath("//label")[1].text
		page.form.secondPassCodeDigit = gets.strip
		page = page.form.submit
	end


	def logon_other
		#requires
		page = logon_passcode
		#Other logon details
		page.form.fields.each do |field|
			unless field.type == "hidden"
				puts field.name
				field.value = gets.strip
			end
		end
		page = page.form.submit
	end


	def logon_bulletin
		#requires
		page = logon_other
		if page.form.action.include?("bulletinBoard")
			page = page.form.submit
		end
	end


	def recent_transactions
		#requires
		page = logon_bulletin
		page = @a.click(page.link_with(:text => "current account"))
		get_transactions(page)
		#Need to return this
		page
	end


	def previous_statements
		#requires
		page = recent_transactions
		#Previous statements
		page = @a.click(page.link_with(:text => "previous statements"))
		#Get first page of previous statements
		page = @a.click(page.links_with(:href => %r{getDomestic} )[0])
		date = Date.today
		until date < @date_back
			get_transactions(page)
			#On previous statement pages most recent transaction is at bottom
			#This is opposite to recent item page
			date = @transactions[-1][:date]
			page = @a.click(page.link_with(:text => "previous statement page"))
		end
	end


	def get_transactions(page)
		doc = Nokogiri::HTML(page.body)
		#Look for summaryTable
		rows = doc.xpath("//table[@class='summaryTable']//tr")
		#Skip first row (table headers, ths)
		rows = rows[1..-1]
		transactions = []
		rows.each do |row|
			#Skip last row, maybe