wfrmls 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,20 @@
1
- === 0.3.0 / 2010-01-22
2
-
3
- * 1 major enhancement
1
+ === 0.3.2 / 2010-02-25
4
2
 
5
- * Birthday!
3
+ * Short sale is included except for comps
4
+ * Tax data is finding fewer false positives
5
+ * Added CLI option -o # no tax data
6
+ * Clear search before starting new address search
7
+ * Show house details when comping
8
+ * No address will take you to search page
9
+ * Show Parking type and size
6
10
 
7
11
  === 0.3.1 / 2010-01-27
8
12
 
9
13
  * Added ability to comp
14
+
15
+ === 0.3.0 / 2010-01-22
16
+
17
+ * Login to site
18
+ * Search for property
19
+ * Show results in browser
20
+
data/bin/wfrmls CHANGED
@@ -22,6 +22,13 @@ password = config['password']
22
22
  ie = Watir::Browser.new
23
23
  mls = Wfrmls::IE.new(ie, username, password)
24
24
 
25
+ if options.address.empty?
26
+ begin
27
+ mls.lookup_address(nil)
28
+ rescue
29
+ exit
30
+ end
31
+ end
25
32
 
26
33
  addr = StreetAddress::US.parse(options.address)
27
34
  if addr.nil?
@@ -31,7 +38,11 @@ end
31
38
  puts addr
32
39
 
33
40
  if options.comp?
34
- mls.comp(addr)
41
+ details = mls.collect_property_details(addr)
42
+ puts details.to_yaml
43
+ mls.comp(addr, details)
44
+ elsif options.opposition?
45
+ mls.lookup_opposition(addr)
35
46
  else
36
47
  puts mls.collect_property_details(addr).to_yaml
37
48
  mls.lookup_address(addr)
@@ -1,7 +1,7 @@
1
1
  require 'wfrmls/ie'
2
2
 
3
3
  module Wfrmls
4
- VERSION = '0.3.1'
4
+ VERSION = '0.3.2'
5
5
 
6
6
  end
7
7
 
@@ -11,10 +11,8 @@ module Wfrmls #:nodoc:
11
11
  attr_reader :address
12
12
 
13
13
  def initialize(*args)
14
- address = ''
15
-
16
14
  argv = args.flatten
17
-
15
+
18
16
  opt = OptionParser.new do |opt|
19
17
  opt.banner = "Usage: #{script_name} [options] <address>"
20
18
  opt.version = version
@@ -27,25 +25,33 @@ module Wfrmls #:nodoc:
27
25
  @comp = true
28
26
  end
29
27
 
28
+ opt.on("-o", "Check opposition's sale") do
29
+ @opposition = true
30
+ end
31
+
30
32
  opt.on("-h", "-?", "--help", "Show this message") do
31
33
  puts opt
32
34
  exit
33
35
  end
34
-
36
+
35
37
  opt.on("-v", "--version", "Show #{script_name}'s version (#{version})") do
36
- puts version
38
+ puts version
37
39
  exit
38
- end
40
+ end
39
41
  end
40
-
42
+
41
43
  opt.parse!(argv)
42
44
  @address = argv.dup.join(' ')
43
- end
45
+ end
44
46
 
45
47
  def comp?
46
48
  @comp
47
49
  end
48
50
 
51
+ def opposition?
52
+ @opposition
53
+ end
54
+
49
55
  private
50
56
  def version
51
57
  Wfrmls::VERSION
@@ -11,6 +11,132 @@ module Wfrmls
11
11
  @ie = ie
12
12
  end
13
13
 
14
+ def lookup_address(addr)
15
+ find_address_on_search_page(addr)
16
+
17
+ if search_results_availible?
18
+ show_full_listings
19
+ else
20
+ show_tax_data(addr)
21
+ end
22
+ end
23
+
24
+ def lookup_opposition(addr)
25
+ find_address_on_search_page(addr)
26
+ show_listings
27
+ end
28
+
29
+ def comp(addr, house_details = collect_property_details(addr))
30
+ goto_search_page
31
+ historical_data
32
+ status
33
+ city(addr.city)
34
+ short_sale(false)
35
+
36
+ @ie.text_field(:id, 'days_back_status').set('120')
37
+
38
+
39
+ @ie.text_field(:name,'tot_sqf1').set((house_details[:house_size]-200).to_s)
40
+ @ie.text_field(:name,'tot_sqf2').set((house_details[:house_size]+200).to_s)
41
+
42
+ @ie.text_field(:name,'yearblt1').set((house_details[:year_built]-6).to_s)
43
+ @ie.text_field(:name,'yearblt2').set((house_details[:year_built]+6).to_s)
44
+
45
+ sleep_until(3) {
46
+ @ie.dd(:id,'left_search_criteria').text.include? 'Year Built at most'
47
+ }
48
+
49
+ show_full_listings
50
+ end
51
+
52
+ def collect_property_details(addr)
53
+ show_tax_data(addr) unless @ie.url.include? 'taxdata/details'
54
+
55
+ doc = @ie.xmlparser_document_object
56
+
57
+ details = {}
58
+
59
+ doc.search('tr/th').each do |item|
60
+ case nbsp2sp(item.text)
61
+ when /NAME:/
62
+ details[:owner] = item.parent.search('td').text
63
+ when /ADDRESS:/
64
+ details[:address] = item.parent.search('td').text.strip
65
+ when /PARCEL SPECIFIC INFO:/
66
+ nbsp2sp(item.parent.search('td').text) =~ /Total Acres: ([.0-9]+)/
67
+ details[:lot_size] = $1
68
+ when /VALUATION SPECIFIC INFO:/
69
+ nbsp2sp(item.parent.search('td').text) =~ /Final Value: (\$[0-9,]+)/
70
+ details[:tax_value] = $1
71
+ when /GENERAL INFO:/
72
+ nbsp2sp(item.parent.search('td').text) =~ /Yr Built: ([0-9]+)/
73
+ details[:year_built] = $1.to_i
74
+ when /AREA INFO:/
75
+ house_size = 0
76
+ data = nbsp2sp(item.parent.search('td').text)
77
+ data =~ /Main Floor Area: ([,0-9]+)/
78
+ house_size += $1.sub(',','').to_i if $1
79
+ data =~ /Basement Area: ([,0-9]+)/
80
+ house_size += $1.sub(',','').to_i if $1
81
+ data =~ /Upper Floor Area: ([,0-9]+)/
82
+ house_size += $1.sub(',','').to_i if $1
83
+ details[:house_size] = house_size
84
+ when /EXTERIOR:/
85
+ details[:exterior] ||= {}
86
+ data = nbsp2sp(item.parent.search('td').text)
87
+ data =~ /Ext. Wall Type: (\w+)/
88
+ details[:exterior][:wall] = $1
89
+ data =~ /Masonry Trim: (\w+)/
90
+ details[:exterior][:masonry_trim] = $1
91
+ when /CARPORT & GARAGE INFO:/
92
+ data = nbsp2sp(item.parent.search('td').text)
93
+ data =~ /(.*): ([0-9]+)/
94
+ details[:parking] = nil
95
+ if $1
96
+ details[:parking] = {}
97
+ details[:parking][$1] = $2.to_i
98
+ end
99
+ end
100
+ end
101
+
102
+ details
103
+ end
104
+
105
+ def show_tax_data(addr)
106
+ goto "http://www.utahrealestate.com/taxdata/index?county%5B%5D=2&county%5B%5D=8&searchtype=house&searchbox=#{addr.number}"
107
+
108
+ rows = find_tax_data_rows_by_house_and_street(addr)
109
+
110
+ case rows.size
111
+ when 0
112
+ puts "#{addr} not found in tax data"
113
+ when 1
114
+ click_link rows[0]
115
+ else
116
+ puts 'Possible matches:'
117
+ rows.each do |item|
118
+ puts item.cell(:class, 'last-col').text
119
+ end
120
+ regex = /\b#{addr.prefix}\b.*\b#{addr.suffix}/
121
+ rows = rows.inject([]) do |c, item|
122
+ c << item if regex.match item.cell(:class, 'last-col').text
123
+ c
124
+ end
125
+ case rows.size
126
+ when 0
127
+ puts "#{addr} not found with #{regex}"
128
+ when 1
129
+ click_link rows[0]
130
+ else
131
+ puts 'Possible matches:'
132
+ rows.each do |item|
133
+ puts item.cell(:class, 'last-col').text
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ private
14
140
  def login
15
141
  @ie.goto 'http://www.utahrealestate.com/auth/login/login_redirect//force_redirect/1'
16
142
  begin
@@ -24,57 +150,55 @@ module Wfrmls
24
150
  end
25
151
  end
26
152
 
27
- def residential_full_search
153
+ def historical_data
28
154
  @ie.radio(:id, 'historical_data_yes').set
29
155
  end
30
156
 
31
- def lookup_address(addr)
32
- goto 'http://www.utahrealestate.com/search/form/type/1/name/full?advanced_search=1'
33
- residential_full_search
157
+ def find_address_on_search_page(addr)
158
+ goto_search_page
159
+ historical_data
34
160
  status
161
+ short_sale
35
162
  address(addr)
163
+ end
36
164
 
37
- result_count = @ie.span(:id, 'action_search_count').text.to_i
38
-
39
- if result_count > 0
40
- show_full_listings
165
+ def short_sale(includes=true)
166
+ if includes
167
+ @ie.radio(:id, 'o_shortsale_4').click
41
168
  else
42
- show_tax_data(addr)
169
+ @ie.radio(:id, 'o_shortsale_8').click
43
170
  end
171
+ @ie.checkbox(:id, 'shortsale_2').click
172
+ @ie.checkbox(:id, 'shortsale_4').click
44
173
  end
45
174
 
46
- def show_full_listings
175
+ def search_results_availible?
176
+ result_count = @ie.span(:id, 'action_search_count').text.to_i > 0
177
+ end
178
+
179
+ def show_listings
180
+ return false unless search_results_availible?
47
181
  @ie.button(:id, 'SEARCH_button').click
48
182
  sleep_until { @ie.checkbox(:id, 'ListingController').exists? }
183
+ true
184
+ end
185
+
186
+ def show_full_listings
187
+ return unless show_listings
49
188
  @ie.checkbox(:id, 'ListingController').click
50
189
  @ie.select_list(:id, 'report-selector').set('Full Report')
51
190
  end
52
191
 
53
- def comp(addr)
54
- # TODO remove the sleeps
55
- house_details = collect_property_details(addr)
56
- goto 'http://www.utahrealestate.com/search/form/type/1/name/full?advanced_search=1'
57
-
58
- @ie.button(:id, 'CLEAR_button').click
59
-
60
- sleep 3
61
-
62
- residential_full_search
63
- status
64
- city(addr.city)
65
-
66
- @ie.text_field(:id, 'days_back_status').set('120')
67
-
68
-
69
- @ie.text_field(:name, 'tot_sqf1').set((house_details[:house_size] - 200).to_s)
70
- @ie.text_field(:name, 'tot_sqf2').set((house_details[:house_size] + 200).to_s)
71
-
72
- @ie.text_field(:name, 'yearblt1').set((house_details[:year_built] - 6).to_s)
73
- @ie.text_field(:name, 'yearblt2').set((house_details[:year_built] + 6).to_s)
74
-
75
- sleep 3
192
+ def goto_search_page
193
+ url = 'http://www.utahrealestate.com/search/form/type/1/name/full?advanced_search=1'
194
+ goto url
195
+ clear_search
196
+ goto url
197
+ end
76
198
 
77
- show_full_listings
199
+ def clear_search
200
+ @ie.button(:id,'CLEAR_button').click
201
+ sleep_until(3) { @ie.dd(:id, 'left_search_criteria').text !~ /City/ }
78
202
  end
79
203
 
80
204
  def status
@@ -106,30 +230,12 @@ module Wfrmls
106
230
  city(addr.city)
107
231
  end
108
232
 
109
- def sleep_until &block
233
+ def sleep_until max=10, &block
110
234
  count = 0
111
235
  until yield block
112
236
  sleep 1
113
237
  count += 1
114
- exit if count > 10
115
- end
116
- end
117
-
118
- def show_tax_data(addr)
119
- goto "http://www.utahrealestate.com/taxdata/index?county%5B%5D=2&county%5B%5D=8&searchtype=house&searchbox=#{addr.number}"
120
-
121
- rows = find_tax_data_rows_by_house_and_street(addr)
122
-
123
- case rows.size
124
- when 0
125
- puts "#{addr} not found in tax data"
126
- when 1
127
- click_link rows[0]
128
- else
129
- puts 'Possible matches:'
130
- rows.each do |item|
131
- puts item.cell(:class, 'last-col').text
132
- end
238
+ return if count > max
133
239
  end
134
240
  end
135
241
 
@@ -144,7 +250,7 @@ module Wfrmls
144
250
  rows << row
145
251
  end
146
252
  end
147
- rows
253
+ rows
148
254
  end
149
255
 
150
256
  def goto(url)
@@ -155,58 +261,6 @@ module Wfrmls
155
261
  end
156
262
  end
157
263
 
158
- def collect_property_details(addr)
159
- show_tax_data(addr) unless @ie.url.include? 'taxdata/details'
160
-
161
- doc = @ie.xmlparser_document_object
162
-
163
- details = {}
164
-
165
- doc.search('tr/th').each do |item|
166
- case nbsp2sp(item.text)
167
- when /NAME:/
168
- details[:owner] = item.parent.search('td').text
169
- when /ADDRESS:/
170
- details[:address] = item.parent.search('td').text.strip
171
- when /PARCEL SPECIFIC INFO:/
172
- nbsp2sp(item.parent.search('td').text) =~ /Total Acres: ([.0-9]+)/
173
- details[:lot_size] = $1
174
- when /VALUATION SPECIFIC INFO:/
175
- nbsp2sp(item.parent.search('td').text) =~ /Final Value: (\$[0-9,]+)/
176
- details[:tax_value] = $1
177
- when /GENERAL INFO:/
178
- nbsp2sp(item.parent.search('td').text) =~ /Yr Built: ([0-9]+)/
179
- details[:year_built] = $1.to_i
180
- when /AREA INFO:/
181
- house_size = 0
182
- data = nbsp2sp(item.parent.search('td').text)
183
- data =~ /Main Floor Area: ([,0-9]+)/
184
- house_size += $1.sub(',','').to_i if $1
185
- data =~ /Basement Area: ([,0-9]+)/
186
- house_size += $1.sub(',','').to_i if $1
187
- data =~ /Upper Floor Area: ([,0-9]+)/
188
- house_size += $1.sub(',','').to_i if $1
189
- details[:house_size] = house_size
190
- when /EXTERIOR:/
191
- details[:exterior] ||= {}
192
- data = nbsp2sp(item.parent.search('td').text)
193
- data =~ /Ext. Wall Type: (\w+)/
194
- details[:exterior][:wall] = $1
195
- data =~ /Masonry Trim: (\w+)/
196
- details[:exterior][:masonry_trim] = $1
197
- when /CARPORT & GARAGE INFO:/
198
- data = nbsp2sp(item.parent.search('td').text)
199
- data =~ /(.*): ([,0-9]+)/
200
- if $1
201
- key = $1.gsub(/[- ]/, '_').downcase.to_sym
202
- details[key] = $2
203
- end
204
- end
205
- end
206
-
207
- details
208
- end
209
-
210
264
  def nbsp2sp(s)
211
265
  s.gsub("\xC2\xA0", ' ')
212
266
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wfrmls
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - zhon
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-27 00:00:00 -07:00
12
+ date: 2010-02-25 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.3.0
33
+ version: 0.4.1
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: hoe