├── .rspec
├── Gemfile
├── .yardopts
├── .gitignore
├── lib
├── rubillow
│ ├── version.rb
│ ├── models
│ │ ├── search_result.rb
│ │ ├── property_chart.rb
│ │ ├── images.rb
│ │ ├── zpidable.rb
│ │ ├── region_chart.rb
│ │ ├── demographic_value.rb
│ │ ├── region_children.rb
│ │ ├── chart.rb
│ │ ├── comps.rb
│ │ ├── posting.rb
│ │ ├── deep_comps.rb
│ │ ├── linkable.rb
│ │ ├── region.rb
│ │ ├── property_basics.rb
│ │ ├── addressable.rb
│ │ ├── rate_summary.rb
│ │ ├── deep_search_result.rb
│ │ ├── postings.rb
│ │ ├── monthly_payments.rb
│ │ ├── updated_property_details.rb
│ │ ├── zestimateable.rb
│ │ └── demographics.rb
│ ├── helpers
│ │ └── xml_parsing_helper.rb
│ ├── configuration.rb
│ ├── request.rb
│ ├── postings.rb
│ ├── models.rb
│ ├── mortgage.rb
│ ├── property_details.rb
│ ├── neighborhood.rb
│ └── home_valuation.rb
└── rubillow.rb
├── .travis.yml
├── spec
├── rubillow
│ ├── models
│ │ ├── region_children_spec.rb
│ │ ├── postings_spec.rb
│ │ ├── rate_summary_spec.rb
│ │ ├── monthly_payments_spec.rb
│ │ ├── property_chart_spec.rb
│ │ ├── posting_spec.rb
│ │ ├── region_chart_spec.rb
│ │ ├── demographics_spec.rb
│ │ ├── updated_property_details_spec.rb
│ │ ├── comps_spec.rb
│ │ ├── deep_search_results_spec.rb
│ │ ├── deep_comps_spec.rb
│ │ └── search_result_spec.rb
│ ├── rubillow_spec.rb
│ ├── configuration_spec.rb
│ ├── postings_spec.rb
│ ├── models_spec.rb
│ ├── mortgage_spec.rb
│ ├── neighborhood_spec.rb
│ ├── request_spec.rb
│ ├── property_details_spec.rb
│ └── home_valuation_spec.rb
├── spec_helper.rb
├── xml
│ ├── general_failure.xml
│ ├── near_limit.xml
│ ├── get_chart.xml
│ ├── get_rate_summary.xml
│ ├── get_region_chart.xml
│ ├── get_monthly_payments.xml
│ ├── get_zestimate_missing_region.xml
│ ├── get_deep_search_results_missing_data.xml
│ ├── get_search_results.xml
│ ├── get_zestimate_missing_value_duration.xml
│ ├── get_zestimate.xml
│ ├── get_deep_search_results.xml
│ ├── get_updated_property_details.xml
│ ├── get_deep_search_results_duplicated.xml
│ ├── get_comps.xml
│ ├── get_deep_comps.xml
│ └── get_demographics.xml
└── support
│ └── have_configuration_matcher.rb
├── Rakefile
├── LICENSE
├── CHANGELOG.md
├── Gemfile.lock
├── rubillow.gemspec
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | --format progress
2 | --color
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | gemspec
4 |
--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | --no-private
2 | lib/**/*.rb
3 | -
4 | LICENSE
5 | CHANGELOG.md
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | .bundle
3 | .rvmrc
4 | pkg/*
5 | coverage
6 | doc
7 | .yardoc
8 |
--------------------------------------------------------------------------------
/lib/rubillow/version.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # @private
3 | VERSION = "0.0.8"
4 | end
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | rvm:
2 | - 1.8.7
3 | - 1.9.2
4 | - 1.9.3
5 | script: "bundle exec rake spec"
6 |
7 | branches:
8 | only:
9 | - master
--------------------------------------------------------------------------------
/spec/rubillow/models/region_children_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::RegionChildren do
4 | it "populates the data" do
5 | data = Rubillow::Models::RegionChildren.new(get_xml('get_region_children.xml'))
6 |
7 | data.region.id.should == "16037"
8 | data.regions.count.should == 107
9 | data.regions[0].id.should == "343997"
10 | end
11 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/search_result.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Property search results
4 | class SearchResult < Base
5 | include Zestimateable
6 |
7 | protected
8 |
9 | # @private
10 | def parse
11 | super
12 |
13 | return if !success?
14 |
15 | extract_zestimate(@parser)
16 | end
17 | end
18 | end
19 | end
--------------------------------------------------------------------------------
/spec/rubillow/rubillow_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow do
4 | it "allows setting configuration as a block" do
5 | Rubillow.configure do |config|
6 | config.http_open_timeout = 10
7 | config.http_read_timeout = 30
8 | end
9 |
10 | Rubillow.configuration.http_open_timeout.should == 10
11 | Rubillow.configuration.http_read_timeout.should == 30
12 | end
13 | end
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 | require "yard"
4 |
5 | desc "Run specs"
6 | RSpec::Core::RakeTask.new do |t|
7 | t.pattern = "spec/**/*_spec.rb"
8 | end
9 |
10 | desc "Run specs with coverage"
11 | task :coverage do
12 | ENV['COVERAGE'] = 'true'
13 | Rake::Task["spec"].execute
14 | end
15 |
16 | YARD::Rake::YardocTask.new do |t|
17 | end
18 |
19 | task :default => :spec
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'simplecov'
2 | require 'coveralls'
3 |
4 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5 | SimpleCov::Formatter::HTMLFormatter,
6 | Coveralls::SimpleCov::Formatter
7 | ]
8 | SimpleCov.start
9 |
10 | require File.expand_path("../../lib/rubillow", __FILE__)
11 | require 'rspec'
12 |
13 | Dir[File.expand_path("../support/**/*.rb", __FILE__)].each do |file|
14 | require file
15 | end
16 |
17 | def get_xml(file)
18 | File.open(File.expand_path("../xml/" + file, __FILE__)).read
19 | end
--------------------------------------------------------------------------------
/spec/rubillow/configuration_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Configuration do
4 | it { should have_configuration_option(:host).default("www.zillow.com") }
5 | it { should have_configuration_option(:port).default(80) }
6 | it { should have_configuration_option(:path).default("webservice/") }
7 | it { should have_configuration_option(:zwsid).default(nil) }
8 | it { should have_configuration_option(:http_open_timeout).default(2) }
9 | it { should have_configuration_option(:http_read_timeout).default(2) }
10 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/postings_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::Postings do
4 | it "populates the data" do
5 | data = Rubillow::Models::Postings.new(get_xml('get_region_postings.xml'))
6 |
7 | data.region_id.should == "99562"
8 | data.links.count.should == 3
9 | data.make_me_move.count.should == 90
10 | data.for_sale_by_owner.count.should == 1
11 | data.for_sale_by_agent.count.should == 8
12 | data.report_for_sale.count.should == 1
13 | data.for_rent.count.should == 1
14 | end
15 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/rate_summary_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::RateSummary do
4 | it "populates the results" do
5 | data = Rubillow::Models::RateSummary.new(get_xml('get_rate_summary.xml'))
6 |
7 | data.today[:thiry_year_fixed].should == "4.01"
8 | data.today[:fifteen_year_fixed].should == "3.27"
9 | data.today[:five_one_arm].should == "2.73"
10 | data.last_week[:thiry_year_fixed].should == "4.02"
11 | data.last_week[:fifteen_year_fixed].should == "3.27"
12 | data.last_week[:five_one_arm].should == "2.84"
13 | end
14 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/property_chart.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Chart for a property
4 | class PropertyChart < Chart
5 | # @return [String] url for chart
6 | attr_accessor :graphs_and_data
7 |
8 | # Returns HTML for the chart.
9 | # @return [String] chart HTML.
10 | def to_html
11 | "" + super + " "
12 | end
13 |
14 | protected
15 |
16 | # @private
17 | def parse
18 | super
19 |
20 | return if !success?
21 |
22 | @graphs_and_data = @parser.xpath('//response/graphsanddata').text
23 | end
24 | end
25 | end
26 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/images.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing images
4 | module Images
5 | # @return [Array] List of image urls
6 | attr_accessor :images
7 |
8 | # @return [Integer] number of images available (doesn't always match @images.count)
9 | attr_accessor :images_count
10 |
11 | protected
12 |
13 | # @private
14 | def extract_images(xml)
15 | @images_count = xml.xpath('//images/count').text
16 |
17 | @images = []
18 | xml.xpath('//images/image').children.each do |elm|
19 | @images << elm.text
20 | end
21 | end
22 | end
23 | end
24 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/zpidable.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing zpid's
4 | module Zpidable
5 | # @return [String] ZPID of property
6 | attr_accessor :zpid
7 |
8 | protected
9 |
10 | # @private
11 | def extract_zpid(xml)
12 | # TODO: clean up this logic
13 | if !xml.xpath('//response/zpid').empty?
14 | selector = '//response/zpid'
15 | elsif !xml.xpath('//result/zpid').empty?
16 | selector = '//result/zpid'
17 | else
18 | selector = '//zpid'
19 | end
20 |
21 | @zpid = xml.xpath(selector).first.text
22 | end
23 | end
24 | end
25 | end
--------------------------------------------------------------------------------
/spec/xml/general_failure.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Error: invalid or missing ZWSID parameter
9 | 2
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/rubillow/models/region_chart.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Chart for a region
4 | class RegionChart < Chart
5 | include Linkable
6 |
7 | # @return [String] url for chart
8 | attr_accessor :link
9 |
10 | # Returns HTML for the chart.
11 | # @return [String] chart HTML.
12 | def to_html
13 | "" + super + " "
14 | end
15 |
16 | protected
17 |
18 | # @private
19 | def parse
20 | super
21 |
22 | return if !success?
23 |
24 | extract_links(@parser)
25 |
26 | @link = @parser.xpath('//response/link').text
27 | end
28 | end
29 | end
30 | end
--------------------------------------------------------------------------------
/spec/xml/near_limit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Error: invalid or missing ZWSID parameter
9 | 2
10 | true
11 |
12 |
13 |
--------------------------------------------------------------------------------
/spec/rubillow/postings_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Postings, ".region_postings" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => "", :code => 200) }
6 |
7 | it "requires either the zipcode or citystatezip option" do
8 | lambda {
9 | Rubillow::Postings.region_postings()
10 | }.should raise_error(ArgumentError, "Either the zipcode or citystatezip option is required")
11 | end
12 |
13 | it "fetches the XML" do
14 | Rubillow::Request.stub(:request).and_return(request)
15 |
16 | response = Rubillow::Postings.region_postings({ :zipcode => "98102", :rental => true })
17 | response.should be_an_instance_of(Rubillow::Models::Postings)
18 | end
19 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/demographic_value.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Demographic data point.
4 | class DemographicValue
5 | # @return [String] data point value.
6 | attr_accessor :value
7 |
8 | # @return [String] data point type.
9 | attr_accessor :type
10 |
11 | # create a new data point.
12 | # @param [String] xml for point.
13 | def initialize(xml)
14 | if !xml.empty?
15 | @value = xml.text
16 | @type = xml.attribute('type').value if !xml.attribute('type').nil?
17 | end
18 | end
19 |
20 | # Prints value
21 | #
22 | # @return [String] attribute value
23 | def to_s
24 | @value
25 | end
26 | end
27 | end
28 | end
--------------------------------------------------------------------------------
/lib/rubillow/helpers/xml_parsing_helper.rb:
--------------------------------------------------------------------------------
1 | # Helper methods for parsing XML.
2 | module XmlParsingHelper
3 | # @param path [String] Xpath query to find the node in XML.
4 | # @param attribute [Symbol] Attribute on the node to call and return as the value.
5 | # @param xml [Nokogiri::XML] A nokogiri parser object to call #xpath.
6 | # @param nil_value (optional) A value to return if the attribute on the node
7 | # is nil or if the node is not present.
8 | #
9 | # @return [String] Value from an attribute of an xpath node, if present.
10 | # If the node is not present, return nil or nil_value if specified.
11 | def xpath_if_present(path, attribute, xml, nil_value = nil)
12 | text = xml.xpath(path).first.send(attribute) unless xml.xpath(path).empty?
13 | text ||= nil_value
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/rubillow/models/region_children.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # List of sub-regions for a region
4 | class RegionChildren < Base
5 | # @return [Models::Region] top-level region
6 | attr_accessor :region
7 |
8 | # @return [Array] sub-level regions ({Models::Region})
9 | attr_accessor :regions
10 |
11 | protected
12 |
13 | # @private
14 | def parse
15 | super
16 |
17 | return if !success?
18 |
19 | @region = Region.new(@parser.xpath('//response/region').to_xml)
20 |
21 | @regions = []
22 | @parser.xpath('//response/list').children.each do |region|
23 | if region.name == "region"
24 | @regions << Region.new(region.to_xml)
25 | end
26 | end
27 | end
28 | end
29 | end
30 | end
--------------------------------------------------------------------------------
/lib/rubillow.rb:
--------------------------------------------------------------------------------
1 | require "rubillow/version"
2 | require "rubillow/configuration"
3 | require "rubillow/request"
4 | require "rubillow/home_valuation"
5 | require "rubillow/mortgage"
6 | require "rubillow/postings"
7 | require "rubillow/property_details"
8 | require "rubillow/neighborhood"
9 | require "rubillow/models"
10 |
11 | require 'date'
12 |
13 | # Top-level interface to Rubillow
14 | module Rubillow
15 | # Call this method to modify defaults in your initializers.
16 | #
17 | # @example
18 | # Rubillow.configure do |configuration|
19 | # configuration.zwsid = "abcd1234"
20 | # end
21 | #
22 | # @yield [Configuration] The current configuration.
23 | def self.configure
24 | yield(configuration)
25 | end
26 |
27 | # @return [Configuration] Current configuration.
28 | def self.configuration
29 | @@configuration ||= Configuration.new
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/rubillow/models/chart.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Base chart class.
4 | class Chart < Base
5 | # @return [String] image height.
6 | attr_accessor :height
7 |
8 | # @return [String] image width.
9 | attr_accessor :width
10 |
11 | # @return [String] URL to image.
12 | attr_accessor :url
13 |
14 | # Returns HTML for the chart.
15 | # @return [String] chart HTML.
16 | def to_html
17 | " "
18 | end
19 |
20 | protected
21 |
22 | # @private
23 | def parse
24 | super
25 |
26 | return if !success?
27 |
28 | @height = @parser.xpath('//request/height').first.text.to_i
29 | @width = @parser.xpath('//request/width').first.text.to_i
30 | @url = @parser.xpath('//response/url').first.text
31 | end
32 | end
33 | end
34 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/monthly_payments_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::MonthlyPayments do
4 | it "populates the results" do
5 | data = Rubillow::Models::MonthlyPayments.new(get_xml('get_monthly_payments.xml'))
6 |
7 | data.thirty_year_fixed[:rate].should == "4.01"
8 | data.thirty_year_fixed[:principal_and_interest].should == "1219"
9 | data.thirty_year_fixed[:mortgage_insurance].should == "93"
10 | data.fifteen_year_fixed[:rate].should == "3.27"
11 | data.fifteen_year_fixed[:principal_and_interest].should == "1795"
12 | data.fifteen_year_fixed[:mortgage_insurance].should == "93"
13 | data.five_one_arm[:rate].should == "3.27"
14 | data.five_one_arm[:principal_and_interest].should == "1039"
15 | data.five_one_arm[:mortgage_insurance].should == "93"
16 | data.down_payment.should == "45000"
17 | data.monthly_property_taxes.should == "193"
18 | data.monthly_hazard_insurance == "50"
19 | end
20 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/comps.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # List of comps for a property.
4 | class Comps < Base
5 | # @return [Models::SearchResult] principal property.
6 | attr_accessor :principal
7 |
8 | # @return [Hash] comparables list (key => comparable's score, value => {Models::SearchResult}).
9 | #
10 | # @example
11 | # comparables.each do |score, comp|
12 | # puts score
13 | # puts comp.price
14 | # end
15 | attr_accessor :comparables
16 |
17 | protected
18 |
19 | # @private
20 | def parse
21 | super
22 |
23 | return if !success?
24 |
25 | @principal = SearchResult.new(@parser.xpath('//principal').to_xml)
26 |
27 | @comparables = {}
28 | @parser.xpath('//comparables/comp').each do |elm|
29 | key = elm.attribute('score').value
30 | @comparables[key] = SearchResult.new(elm.to_xml)
31 | end
32 | end
33 | end
34 | end
35 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/posting.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # A posting
4 | class Posting < Base
5 | include Zpidable
6 | include Addressable
7 | include Linkable
8 | include PropertyBasics
9 | include Images
10 |
11 | # @return [Date] last date data was refreshed.
12 | attr_accessor :last_refreshed_date
13 |
14 | # @return [String] price
15 | attr_accessor :price
16 |
17 | protected
18 |
19 | # @private
20 | def parse
21 | super
22 |
23 | return if !success?
24 |
25 | property = @parser.xpath('//property').first
26 | extract_zpid(property)
27 | extract_links(property)
28 | extract_address(property)
29 | extract_property_basics(property)
30 | extract_images(property)
31 |
32 | @last_refreshed_date = Date.strptime(@parser.xpath('//lastRefreshedDate').text, "%Y-%m-%d")
33 | @price = @parser.xpath('//price').text
34 | end
35 | end
36 | end
37 | end
--------------------------------------------------------------------------------
/lib/rubillow/configuration.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Configuration options
3 | class Configuration
4 | # @return [String] HTTP host (defaults to +www.zillow.com+)
5 | attr_accessor :host
6 |
7 | # @return [Integer] HTTP port (defaults to +80+)
8 | attr_accessor :port
9 |
10 | # @return [String] relative service path (defaults to +webservice/+)
11 | attr_accessor :path
12 |
13 | # @return [String] Zillow API key. See {http://www.zillow.com/howto/api/APIOverview.htm}.
14 | attr_accessor :zwsid
15 |
16 | # @return [Integer] HTTP connection timeout seconds (defaults to +2+)
17 | attr_accessor :http_open_timeout
18 |
19 | # @return [Integer] HTTP read timeout seconds (defaults to +2+)
20 | attr_accessor :http_read_timeout
21 |
22 | # @private
23 | def initialize
24 | self.host = "www.zillow.com"
25 | self.port = 80
26 | self.path = "webservice/"
27 | self.http_open_timeout = 2
28 | self.http_read_timeout = 2
29 | end
30 | end
31 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/deep_comps.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # List of comps for a given property with deep data.
4 | class DeepComps < Base
5 | # @return [Models::DeepSearchResult] principal property.
6 | attr_accessor :principal
7 |
8 | # @return [Hash] comparables list (key => comparable's score, value => {Models::DeepSearchResult}).
9 | #
10 | # @example
11 | # comparables.each do |score, comp|
12 | # puts score
13 | # puts comp.price
14 | # end
15 | attr_accessor :comparables
16 |
17 | protected
18 |
19 | # @private
20 | def parse
21 | super
22 |
23 | return if !success?
24 |
25 | @principal = DeepSearchResult.new(@parser.xpath('//principal').to_xml)
26 |
27 | @comparables = {}
28 | @parser.xpath('//comparables/comp').each do |elm|
29 | key = elm.attribute('score').value
30 | @comparables[key] = DeepSearchResult.new(elm.to_xml)
31 | end
32 | end
33 | end
34 | end
35 | end
--------------------------------------------------------------------------------
/spec/xml/get_chart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 48749425
5 | percent
6 | 300
7 | 150
8 |
9 |
10 | Request successfully processed
11 | 0
12 |
13 |
14 | http://www.zillow.com/app?chartDuration=1year&chartType=partner&height=150&page=webservice%2FGetChart&service=chart&showPercent=true&width=300&zpid=48749425
15 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/spec/support/have_configuration_matcher.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :have_configuration_option do |option|
2 | match do |configuration|
3 | configuration.should respond_to(option)
4 |
5 | if instance_variables.include?("@default")
6 | configuration.send(option).should == @default
7 | end
8 |
9 | configuration.__send__(:"#{option}=", "value")
10 | configuration.__send__(option).should == "value"
11 | end
12 |
13 | chain :default do |default|
14 | @default = default
15 | end
16 |
17 | failure_message_for_should do
18 | description = "expected #{subject} to have"
19 | description << " configuration option #{option.inspect}"
20 | description << " with a default of #{@default.inspect}" if instance_variables.include?("@default")
21 | description
22 | end
23 |
24 | failure_message_for_should_not do
25 | description = "expected #{subject} to not have"
26 | description << " configuration option #{option.inspect}"
27 | description << " with a default of #{@default.inspect}" if instance_variables.include?("@default")
28 | description
29 | end
30 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/property_chart_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::PropertyChart do
4 | it "populates the results" do
5 | data = Rubillow::Models::PropertyChart.new(get_xml('get_chart.xml'))
6 |
7 | data.width.should == 300
8 | data.height.should == 150
9 | data.url.should == "http://www.zillow.com/app?chartDuration=1year&chartType=partner&height=150&page=webservice%2FGetChart&service=chart&showPercent=true&width=300&zpid=48749425"
10 | data.graphs_and_data.should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
11 | end
12 |
13 | it "should format correctly for HTML" do
14 | data = Rubillow::Models::PropertyChart.new(get_xml('get_chart.xml'))
15 |
16 | data.to_html.should == " "
17 | end
18 | end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2011 Matthew Vince
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/spec/xml/get_rate_summary.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Request successfully processed
6 | 0
7 |
8 |
9 |
10 | 4.01
11 | 3.27
12 | 2.73
13 |
14 |
15 | 4.02
16 | 3.27
17 | 2.84
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.8 / 2013-06-16
2 |
3 | * Fix nokogiri gem requirement to maintain 1.8.7 compatibilty.
4 |
5 | ## 0.0.7 / 2013-06-16
6 |
7 | * Remove unnecessary chomp call from value duration
8 |
9 | ## 0.0.6 / 2013-04-10
10 |
11 | * Fix case where Zillow returns multiple results for an address -- assume first address for result
12 | * Fix RentZestimate bug when valueChange doesn't have a duration.
13 | * Fix common scenarios where xml nodes are not present
14 |
15 | ## 0.0.5 / 2013-01-12
16 |
17 | * Include RentZestimate if available
18 | * Check if region info exists before populating
19 |
20 | ## 0.0.4 / 2012-01-31
21 |
22 | * Check if lastSoldDate exists before parsing
23 |
24 | ## 0.0.3 / 2012-01-14
25 |
26 | * Fix GetUpdatedPropertyDetails date requirement
27 | * Fix typo on method name
28 | * Updated documentation
29 | * Added Travis CI and dependency tracking
30 | * Remove unnecessary testing dependencies
31 |
32 | ## 0.0.2 / 2011-09-27
33 |
34 | * Remove unnecessary dependencies of activesupport and i18n
35 |
36 | ## 0.0.1 / 2011-08-27
37 |
38 | * Covers all Zillow API endpoints
39 | * Code documentation
40 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | rubillow (0.0.8)
5 | nokogiri (~> 1.5.0)
6 |
7 | GEM
8 | remote: http://rubygems.org/
9 | specs:
10 | bluecloth (2.2.0)
11 | colorize (0.5.8)
12 | coveralls (0.6.0)
13 | colorize
14 | multi_json (~> 1.3)
15 | rest-client
16 | simplecov (>= 0.7)
17 | thor
18 | diff-lcs (1.2.1)
19 | mime-types (1.21)
20 | multi_json (1.6.1)
21 | nokogiri (1.5.9)
22 | rake (10.0.3)
23 | rest-client (1.6.7)
24 | mime-types (>= 1.16)
25 | rspec (2.13.0)
26 | rspec-core (~> 2.13.0)
27 | rspec-expectations (~> 2.13.0)
28 | rspec-mocks (~> 2.13.0)
29 | rspec-core (2.13.0)
30 | rspec-expectations (2.13.0)
31 | diff-lcs (>= 1.1.3, < 2.0)
32 | rspec-mocks (2.13.0)
33 | simplecov (0.7.1)
34 | multi_json (~> 1.0)
35 | simplecov-html (~> 0.7.1)
36 | simplecov-html (0.7.1)
37 | thor (0.17.0)
38 | yard (0.8.5.2)
39 |
40 | PLATFORMS
41 | ruby
42 |
43 | DEPENDENCIES
44 | bluecloth (~> 2.2)
45 | coveralls (~> 0.6)
46 | rake (~> 10.0)
47 | rspec (~> 2.12)
48 | rubillow!
49 | simplecov (~> 0.7)
50 | yard (~> 0.8)
51 |
--------------------------------------------------------------------------------
/lib/rubillow/models/linkable.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing links
4 | module Linkable
5 | # @return [Hash] Links (format: :name => 'url')
6 | #
7 | # @example
8 | # links.each do |name, url
9 | # end
10 | #
11 | attr_accessor :links
12 |
13 | protected
14 |
15 | # @private
16 | def extract_links(xml)
17 | @links = {}
18 |
19 | # TODO: clean up this logic
20 | if !xml.xpath('//result/links').empty?
21 | selector = '//result/links'
22 | elsif !xml.xpath('//response/links').empty?
23 | selector = '//response/links'
24 | elsif !xml.xpath('//principal/links').empty?
25 | selector = '//principal/links'
26 | elsif !xml.xpath('//comp/links').empty?
27 | selector = '//comp/links'
28 | else
29 | selector = '//links'
30 | end
31 |
32 | xml.xpath(selector).children.each do |link|
33 | next if link.name == "myzestimator" # deprecated
34 | @links[link.name.to_sym] = link.text
35 | end
36 | end
37 | end
38 | end
39 | end
--------------------------------------------------------------------------------
/rubillow.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path("../lib", __FILE__)
3 | require "rubillow/version"
4 |
5 | Gem::Specification.new do |s|
6 | s.name = "rubillow"
7 | s.version = Rubillow::VERSION
8 | s.authors = ["Matthew Vince"]
9 | s.email = ["rubillow@matthewvince.com"]
10 | s.homepage = "https://github.com/synewaves/rubillow"
11 | s.summary = "Ruby library to access the Zillow API"
12 | s.description = "Ruby library to access the Zillow API"
13 | s.license = "MIT"
14 |
15 | s.rubyforge_project = "rubillow"
16 |
17 | s.files = `git ls-files`.split("\n")
18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20 | s.require_paths = ["lib"]
21 |
22 | s.add_dependency "nokogiri", "~> 1.5.0"
23 |
24 | s.add_development_dependency "rake", "~> 10.0"
25 | s.add_development_dependency "rspec", "~> 2.12"
26 | s.add_development_dependency "yard", "~> 0.8"
27 | s.add_development_dependency "bluecloth", "~> 2.2"
28 | s.add_development_dependency "simplecov", "~> 0.7"
29 | s.add_development_dependency "coveralls", "~> 0.6"
30 | end
31 |
--------------------------------------------------------------------------------
/spec/rubillow/models/posting_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::Posting do
4 | it "populates the data" do
5 | data = Rubillow::Models::Postings.new(get_xml('get_region_postings.xml'))
6 | data = data.make_me_move[0]
7 |
8 | data.should be_a(Rubillow::Models::Posting)
9 | data.zpid.should == "48708109"
10 | data.last_refreshed_date.strftime("%m/%d/%Y").should == "08/21/2011"
11 | data.links.count.should == 1
12 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/1658-Federal-Ave-E-Seattle-WA-98102/48708109_zpid/"
13 | data.address[:street].should == "1658 Federal Ave E"
14 | data.address[:city].should == "Seattle"
15 | data.address[:state].should == "WA"
16 | data.address[:zipcode].should == "98102"
17 | data.address[:latitude].should == "47.634532"
18 | data.address[:longitude].should == "-122.318519"
19 | data.use_code.should == "Single Family"
20 | data.lot_size_square_feet.should == "20000"
21 | data.finished_square_feet.should == "7380"
22 | data.bathrooms.should == "5.0"
23 | data.bedrooms.should == "6"
24 | data.total_rooms.should == ""
25 | data.images_count.should == "1"
26 | data.price.should == "4500000"
27 | end
28 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/region_chart_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::RegionChart do
4 | it "populates the results" do
5 | data = Rubillow::Models::RegionChart.new(get_xml('get_region_chart.xml'))
6 |
7 | data.width.should == 300
8 | data.height.should == 150
9 | data.url.should == "http://www.zillow.com/app?chartDuration=1year&chartType=partner&cityRegionId=16037&countyRegionId=0&height=150&nationRegionId=0&neighborhoodRegionId=0&page=webservice%2FGetRegionChart&service=chart&showCity=true&showPercent=true&stateRegionId=0&width=300&zipRegionId=0"
10 | data.link.should == "http://www.zillow.com/homes/Seattle-WA/"
11 | data.links.count.should == 3
12 | end
13 |
14 | it "should format correctly for HTML" do
15 | data = Rubillow::Models::RegionChart.new(get_xml('get_region_chart.xml'))
16 |
17 | data.to_html.should == " "
18 | end
19 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/region.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Region information.
4 | class Region < Base
5 | # @return [String] region id.
6 | attr_accessor :id
7 |
8 | # @return [String] state.
9 | attr_accessor :state
10 |
11 | # @return [String] city.
12 | attr_accessor :city
13 |
14 | # @return [String] neighborhood.
15 | attr_accessor :neighborhood
16 |
17 | # @return [String] latitude.
18 | attr_accessor :latitude
19 |
20 | # @return [String] longitude.
21 | attr_accessor :longitude
22 |
23 | # @return [String] ZMM rate URL.
24 | attr_accessor :zmmrateurl
25 |
26 | protected
27 |
28 | # @private
29 | def parse
30 | super
31 |
32 | return if !success?
33 |
34 | @id = @parser.xpath('//id').first.text
35 | @state = @parser.xpath('//state').text
36 | @city = @parser.xpath('//city').text
37 | @neighborhood = @parser.xpath('//neighborhood').text
38 | @latitude = @parser.xpath('//latitude').text
39 | @longitude = @parser.xpath('//longitude').text
40 | @zmmrateurl = @parser.xpath('//zmmrateurl').text
41 | end
42 | end
43 | end
44 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/property_basics.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing property information
4 | module PropertyBasics
5 | # @return [String] property type
6 | attr_accessor :use_code
7 |
8 | # @return [String] Size of lot in square feet
9 | attr_accessor :lot_size_square_feet
10 |
11 | # @return [String] Size of property in square feet
12 | attr_accessor :finished_square_feet
13 |
14 | # @return [String] number of bathrooms
15 | attr_accessor :bathrooms
16 |
17 | # @return [String] number of bedrooms
18 | attr_accessor :bedrooms
19 |
20 | # @return [String] total number of rooms
21 | attr_accessor :total_rooms
22 |
23 | protected
24 |
25 | # @private
26 | def extract_property_basics(xml)
27 | @use_code = xpath_if_present('//useCode', :text, xml, "")
28 | @lot_size_square_feet = xpath_if_present('//lotSizeSqFt', :text, xml, "")
29 | @finished_square_feet = xpath_if_present('//finishedSqFt', :text, xml, "")
30 | @bathrooms = xpath_if_present('//bathrooms', :text, xml, "")
31 | @bedrooms = xpath_if_present('//bedrooms', :text, xml, "")
32 | @total_rooms = xpath_if_present('//totalRooms', :text, xml, "")
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/rubillow/models/addressable.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing address information.
4 | module Addressable
5 | # @return [Hash] Address information (all are strings, keys are: :street, :city, :state, :zipcode, :latitude, :longitude).
6 | #
7 | # @example
8 | # puts address[:street]
9 | # puts address[:city]
10 | #
11 | attr_accessor :address
12 |
13 | # get the full, formatted address
14 | #
15 | # @return [String] formatted address
16 | def full_address
17 | @address[:street] + ', ' + @address[:city] + ', ' + @address[:state] + ' ' + @address[:zipcode]
18 | end
19 |
20 | protected
21 |
22 | # @private
23 | def extract_address(xml)
24 | address = xml.xpath('//address')
25 | if !address.empty?
26 | @address = {
27 | :street => address.xpath('//street').first.text,
28 | :city => address.xpath('//city').first.text,
29 | :state => address.xpath('//state').first.text,
30 | :zipcode => address.xpath('//zipcode').first.text,
31 | :latitude => address.xpath('//latitude').first.text,
32 | :longitude => address.xpath('//longitude').first.text,
33 | }
34 | end
35 | end
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/spec/rubillow/models_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::Base do
4 | it "catches error responses" do
5 | data = Rubillow::Models::Base.new(get_xml('general_failure.xml'))
6 |
7 | data.code.should == 2
8 | data.message.should == "Error: invalid or missing ZWSID parameter"
9 | end
10 |
11 | it "catches near limit warning" do
12 | data = Rubillow::Models::Base.new(get_xml('near_limit.xml'))
13 |
14 | data.near_limit.should be_true
15 | end
16 |
17 | it "fails if empty XML" do
18 | data = Rubillow::Models::Base.new("")
19 |
20 | data.success?.should be_false
21 | data.code.should == -1
22 | data.message.should == "Error connecting to remote service."
23 | end
24 |
25 | it "fails if nil XML" do
26 | data = Rubillow::Models::Base.new(nil)
27 |
28 | data.success?.should be_false
29 | data.code.should == -1
30 | data.message.should == "Error connecting to remote service."
31 | end
32 |
33 | it "fails if passed false" do
34 | data = Rubillow::Models::Base.new(false)
35 |
36 | data.success?.should be_false
37 | data.code.should == -1
38 | data.message.should == "Error connecting to remote service."
39 | end
40 |
41 | it "saves raw XML" do
42 | data = Rubillow::Models::Base.new(get_xml('near_limit.xml'))
43 |
44 | data.xml.should == get_xml('near_limit.xml')
45 | end
46 | end
--------------------------------------------------------------------------------
/spec/rubillow/mortgage_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Mortgage, ".rate_summary" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => "", :code => 200) }
6 |
7 | it "fetches the XML" do
8 | Rubillow::Request.stub(:request).and_return(request)
9 |
10 | response = Rubillow::Mortgage.rate_summary()
11 | response.should be_an_instance_of(Rubillow::Models::RateSummary)
12 | end
13 | end
14 |
15 | describe Rubillow::Mortgage, ".monthly_payments" do
16 | let(:request) { stub("Request", :get => response) }
17 | let(:response) { stub("Response", :body => "", :code => 200) }
18 |
19 | it "requires the price option" do
20 | lambda {
21 | Rubillow::Mortgage.monthly_payments()
22 | }.should raise_error(ArgumentError, "The price option is required")
23 | end
24 |
25 | it "requires either the down or dollars down option" do
26 | lambda {
27 | Rubillow::Mortgage.monthly_payments({ :price => "300000" })
28 | }.should raise_error(ArgumentError, "Either the down or dollarsdown option is required")
29 | end
30 |
31 | it "fetches the XML" do
32 | Rubillow::Request.stub(:request).and_return(request)
33 |
34 | response = Rubillow::Mortgage.monthly_payments({ :price => "300000", :down => "15", :zip => "98104" })
35 | response.should be_an_instance_of(Rubillow::Models::MonthlyPayments)
36 | end
37 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/rate_summary.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Rate summary information
4 | class RateSummary < Base
5 | # @return [Hash] today's rates (:thirty_year_fixed, :fifteen_year_fixed, :five_one_arm).
6 | #
7 | # @example
8 | # today[:thirty_year_fixed]
9 | #
10 | attr_accessor :today
11 |
12 | # @return [Hash] last week's rates (:thirty_year_fixed, :fifteen_year_fixed, :five_one_arm).
13 | #
14 | # @example
15 | # last_week[:thirty_year_fixed]
16 | #
17 | attr_accessor :last_week
18 |
19 | protected
20 |
21 | # @private
22 | def parse
23 | super
24 |
25 | return if !success?
26 |
27 | @today = {}
28 | @last_week = {}
29 | @today[:thiry_year_fixed] = @parser.xpath('//today/rate[@loanType="thirtyYearFixed"]').text
30 | @today[:fifteen_year_fixed] = @parser.xpath('//today/rate[@loanType="fifteenYearFixed"]').text
31 | @today[:five_one_arm] = @parser.xpath('//today/rate[@loanType="fiveOneARM"]').text
32 | @last_week[:thiry_year_fixed] = @parser.xpath('//lastWeek/rate[@loanType="thirtyYearFixed"]').text
33 | @last_week[:fifteen_year_fixed] = @parser.xpath('//lastWeek/rate[@loanType="fifteenYearFixed"]').text
34 | @last_week[:five_one_arm] = @parser.xpath('//lastWeek/rate[@loanType="fiveOneARM"]').text
35 | end
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/spec/xml/get_region_chart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | seattle
5 | WA
6 | percent
7 | 300
8 | 150
9 |
10 |
11 | Request successfully processed
12 | 0
13 |
14 |
15 | http://www.zillow.com/app?chartDuration=1year&chartType=partner&cityRegionId=16037&countyRegionId=0&height=150&nationRegionId=0&neighborhoodRegionId=0&page=webservice%2FGetRegionChart&service=chart&showCity=true&showPercent=true&stateRegionId=0&width=300&zipRegionId=0
16 | http://www.zillow.com/homes/Seattle-WA/
17 |
18 | http://www.zillow.com/local-info/WA-Seattle-home-value/r_16037/
19 | http://www.zillow.com/homes/for_sale/Seattle-WA/
20 | http://www.zillow.com/homes/fsbo/Seattle-WA/
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/xml/get_monthly_payments.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 300000
5 | 15
6 | 98104
7 |
8 |
9 | Request successfully processed
10 | 0
11 |
12 |
13 |
14 | 4.01
15 | 1219
16 | 93
17 |
18 |
19 | 3.27
20 | 1795
21 | 93
22 |
23 |
24 | 3.27
25 | 1039
26 | 93
27 |
28 | 45000
29 | 193
30 | 50
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib/rubillow/models/deep_search_result.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Get a property's information with deeper data.
4 | class DeepSearchResult < SearchResult
5 | include PropertyBasics
6 | include XmlParsingHelper
7 |
8 | # @return [String] FIPS county code. See {http://www.itl.nist.gov/fipspubs/fip6-4.htm}.
9 | attr_accessor :fips_county
10 |
11 | # @return [String] year of the last tax assessment
12 | attr_accessor :tax_assessment_year
13 |
14 | # @return [String] value of the last tax assessment
15 | attr_accessor :tax_assessment
16 |
17 | # @return [String] year home was built
18 | attr_accessor :year_built
19 |
20 | # @return [Date] last date property was sold
21 | attr_accessor :last_sold_date
22 |
23 | # @return [String] price property was last sold for
24 | attr_accessor :last_sold_price
25 |
26 | protected
27 |
28 | # @private
29 | def parse
30 | super
31 |
32 | return if !success?
33 |
34 | extract_property_basics(@parser)
35 | @fips_county = xpath_if_present('//FIPScounty', :text, @parser, "")
36 | @tax_assessment_year = xpath_if_present('//taxAssessmentYear', :text, @parser)
37 | @tax_assessment = xpath_if_present('//taxAssessment', :text, @parser)
38 | @year_built = xpath_if_present('//yearBuilt', :text, @parser)
39 | if tmp = xpath_if_present('//lastSoldDate', :text, @parser) and tmp.strip.length > 0
40 | @last_sold_date = Date.strptime(tmp, "%m/%d/%Y")
41 | end
42 | @last_sold_price = xpath_if_present('//lastSoldPrice', :text, @parser)
43 | end
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/rubillow/request.rb:
--------------------------------------------------------------------------------
1 | require 'cgi'
2 | require 'net/http'
3 | require 'uri'
4 |
5 | module Rubillow
6 | # @private
7 | # HTTP request manager
8 | class Request
9 | # Makes the request to the web service.
10 | #
11 | # @param [String] path Web service name.
12 | # @param [Hash] options Request options.
13 | # @return [String, Boolean] XML on success, false if not.
14 | def self.get(path, options = {})
15 | zwsid = Rubillow.configuration.zwsid
16 |
17 | unless zwsid.nil?
18 | options[:zws_id] ||= zwsid
19 | end
20 |
21 | response = request.get(uri(path, options))
22 |
23 | case response.code.to_i
24 | when 200
25 | response.body
26 | else
27 | false
28 | end
29 | end
30 |
31 | # gets the request object.
32 | #
33 | # @return [Net::HTTP] HTTP object.
34 | def self.request
35 | http = Net::HTTP.new(Rubillow.configuration.host, Rubillow.configuration.port)
36 |
37 | http.open_timeout = Rubillow.configuration.http_open_timeout
38 | http.read_timeout = Rubillow.configuration.http_read_timeout
39 | http
40 | end
41 |
42 | # Generate the url for the request.
43 | #
44 | # @param [String] path Web service name.
45 | # @param [Hash] options Request options.
46 | def self.uri(path, options = {})
47 | path = Rubillow.configuration.path + path
48 | "/#{path}.htm?#{hash_to_query_string(options)}"
49 | end
50 |
51 | # Turns request options into query string.
52 | #
53 | # @param [Hash] hash Request options.
54 | # @return [String] Formatted query string.
55 | def self.hash_to_query_string(hash)
56 | hash = hash.sort_by { |key, value|
57 | key.to_s
58 | }.delete_if { |key, value|
59 | value.to_s.empty?
60 | }.collect { |key, value|
61 | "#{CGI.escape(key.to_s).gsub(/\_/, '-')}=#{CGI.escape(value.to_s)}"
62 | }
63 |
64 | hash.join("&")
65 | end
66 | end
67 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/postings.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # List of postings
4 | class Postings < Base
5 | include Linkable
6 |
7 | # @return [String] region id.
8 | attr_accessor :region_id
9 |
10 | # @return [Array] postings with MakeMeMove status ({Models::Posting}).
11 | attr_accessor :make_me_move
12 |
13 | # @return [Array] postings with FSBA status ({Models::Posting}).
14 | attr_accessor :for_sale_by_owner
15 |
16 | # @return [Array] postings with FSBO status ({Models::Posting}).
17 | attr_accessor :for_sale_by_agent
18 |
19 | # @return [Array] postings with reporting status ({Models::Posting}).
20 | attr_accessor :report_for_sale
21 |
22 | # @return [Array] postings with for rent status ({Models::Posting}).
23 | attr_accessor :for_rent
24 |
25 | protected
26 |
27 | # @private
28 | def parse
29 | super
30 |
31 | return if !success?
32 |
33 | @region_id = @parser.xpath('//regionId').text
34 |
35 | extract_links(@parser)
36 |
37 | @make_me_move = []
38 | @parser.xpath('//response/makeMeMove/result').each do |elm|
39 | @make_me_move << Posting.new(elm.to_xml)
40 | end
41 |
42 | @for_sale_by_owner = []
43 | @parser.xpath('//response/forSaleByOwner/result').each do |elm|
44 | @for_sale_by_owner << Posting.new(elm.to_xml)
45 | end
46 |
47 | @for_sale_by_agent = []
48 | @parser.xpath('//response/forSaleByAgent/result').each do |elm|
49 | @for_sale_by_agent << Posting.new(elm.to_xml)
50 | end
51 |
52 | @report_for_sale = []
53 | @parser.xpath('//response/reportForSale/result').each do |elm|
54 | @report_for_sale << Posting.new(elm.to_xml)
55 | end
56 |
57 | @for_rent = []
58 | @parser.xpath('//response/forRent/result').each do |elm|
59 | @for_rent << Posting.new(elm.to_xml)
60 | end
61 | end
62 | end
63 | end
64 | end
--------------------------------------------------------------------------------
/spec/xml/get_zestimate_missing_region.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 78264249
5 |
6 |
7 | Request successfully processed
8 | 0
9 |
10 |
11 | 78264249
12 |
13 | http://www.zillow.com/homedetails/8663-Orchard-Loop-Rd-NE-Leland-NC-28451/78264249_zpid/
14 | http://www.zillow.com/homedetails/8663-Orchard-Loop-Rd-NE-Leland-NC-28451/78264249_zpid/#charts-and-data
15 | http://www.zillow.com/homes/78264249_zpid/
16 | http://www.zillow.com/homes/comps/78264249_zpid/
17 |
18 |
19 | 8663 Orchard Loop Rd NE
20 | 28451
21 | Leland
22 | NC
23 | 34.217408
24 | -78.054412
25 |
26 |
27 | 136518
28 | 12/27/2012
29 |
30 | -1299
31 |
32 | 23208
33 | 203412
34 |
35 | 58
36 |
37 |
38 |
39 | 69961
40 |
41 | 2640
42 | 36
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/lib/rubillow/postings.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Interface for the Postings API.
3 | #
4 | # Read the more about this API at: {http://www.zillow.com/howto/api/GetRegionPostings.htm}
5 | class Postings
6 | # Retrieve postings for a given region.
7 | #
8 | # Read more at: {http://www.zillow.com/howto/api/GetRegionPostings.htm}.
9 | #
10 | # \* Either the zipcode or citystatezip option is required.
11 | #
12 | # @example
13 | # data = Rubillow::Postings.region_postings({ :zipcode => "98102", :rental => true })
14 | #
15 | # if data.success?
16 | # puts data.region_id # "99562"
17 | # data.make_me_move.each do |posting|
18 | # puts posting.price
19 | # puts posting.address[:street]
20 | # end
21 | # end
22 | #
23 | # @param [Hash] options The options for the API request.
24 | # @option options [String] :zipcode The zipcode of the region (required *).
25 | # @option options [String] :citystatezip The city+state combination and/or ZIP code in which to search. Note that giving both city and state is required. Using just one will not work. (required *).
26 | # @option options [Boolean] :rental Return rental properties (defaults to +false+).
27 | # @option options [String] :postingType The type of for sale listings to return. The default is +all+. To return only for sale by owner, set +fsbo+. To return only for sale by agent, set +fsba+. To return only Make Me Move, set +mmm+. Set +none+ and the rental parameter to +true+ to return only rentals.
28 | # @return [Models::Postings] Region postings list.
29 | def self.region_postings(options = {})
30 | options = {
31 | :zws_id => Rubillow.configuration.zwsid,
32 | :zipcode => nil,
33 | :citystatezip => nil,
34 | :rental => false,
35 | :postingType => 'all',
36 | }.merge!(options)
37 | options[:output] = 'xml'
38 |
39 | if options[:zipcode].nil? && options[:citystatezip].nil?
40 | raise ArgumentError, "Either the zipcode or citystatezip option is required"
41 | end
42 |
43 | Models::Postings.new(Rubillow::Request.get("GetRegionPostings", options))
44 | end
45 | end
46 | end
--------------------------------------------------------------------------------
/spec/rubillow/neighborhood_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Neighborhood, ".demographics" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => "", :code => 200) }
6 |
7 | it "requires either the regionid, state and city, city and neighborhood, or zip option" do
8 | lambda {
9 | Rubillow::Neighborhood.demographics()
10 | }.should raise_error(ArgumentError, "The regionid, state and city, city and neighborhood or zip option is required")
11 | end
12 |
13 | it "fetches the XML" do
14 | Rubillow::Request.stub(:request).and_return(request)
15 |
16 | response = Rubillow::Neighborhood.demographics({ :state => 'WA', :city => 'Seattle', :neighborhood => 'Ballard' })
17 | response.should be_an_instance_of(Rubillow::Models::Demographics)
18 | end
19 | end
20 |
21 | describe Rubillow::Neighborhood, ".region_children" do
22 | let(:request) { stub("Request", :get => response) }
23 | let(:response) { stub("Response", :body => "", :code => 200) }
24 |
25 | it "requires either the regionid or state option" do
26 | lambda {
27 | Rubillow::Neighborhood.region_children()
28 | }.should raise_error(ArgumentError, "The regionid or state option is required")
29 | end
30 |
31 | it "fetches the XML" do
32 | Rubillow::Request.stub(:request).and_return(request)
33 |
34 | response = Rubillow::Neighborhood.region_children({ :state => 'WA', :city => 'Seattle', :childtype => 'neighborhood' })
35 | response.should be_an_instance_of(Rubillow::Models::RegionChildren)
36 | end
37 | end
38 |
39 | describe Rubillow::Neighborhood, ".region_chart" do
40 | let(:request) { stub("Request", :get => response) }
41 | let(:response) { stub("Response", :body => "", :code => 200) }
42 |
43 | it "requires the unit_type option" do
44 | lambda {
45 | Rubillow::Neighborhood.region_chart()
46 | }.should raise_error(ArgumentError, "The unit_type option is required")
47 | end
48 |
49 | it "fetches the XML" do
50 | Rubillow::Request.stub(:request).and_return(request)
51 |
52 | response = Rubillow::Neighborhood.region_chart({ :city => 'Seattle', :state => 'WA', :unit_type => 'percent', :width => 300, :height => 150 })
53 | response.should be_an_instance_of(Rubillow::Models::RegionChart)
54 | end
55 | end
--------------------------------------------------------------------------------
/spec/rubillow/request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Request, ".get" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => '', :code => 200) }
6 | let(:zwsid) { "xyz123-oo" }
7 |
8 | before do
9 | Rubillow::Request.stub(:uri).and_return("/")
10 | Rubillow::Request.stub(:request).and_return(request)
11 | end
12 |
13 | it "constructs the request URI" do
14 | Rubillow::Request.should_receive(:uri).with("/", :option => 1)
15 | Rubillow::Request.get("/", :option => 1)
16 | end
17 |
18 | it "includes access key when configured" do
19 | Rubillow::Request.should_receive(:uri).with("/", :zws_id => zwsid)
20 |
21 | Rubillow.configuration.zwsid = zwsid
22 | Rubillow::Request.get("/")
23 | end
24 |
25 | it "allows overriding of configured access key" do
26 | Rubillow::Request.should_receive(:uri).with("/", :zws_id => "abc890_11")
27 |
28 | Rubillow.configuration.zwsid = zwsid
29 | Rubillow::Request.get("/", :zws_id => "abc890_11")
30 | end
31 |
32 | it "makes an API request" do
33 | request.should_receive(:get).with("/")
34 |
35 | Rubillow::Request.get("/")
36 | end
37 |
38 | it "returns the XML for a successful response" do
39 | Rubillow::Request.get("/").should == ''
40 | end
41 |
42 | it "returns false for any response code other than 200" do
43 | response.stub(:code => "401")
44 | Rubillow::Request.get("/").should == false
45 | end
46 | end
47 |
48 | describe Rubillow::Request, ".request" do
49 | it "creates a new HTTP client" do
50 | Rubillow::Request.request.should be_a(Net::HTTP)
51 | end
52 |
53 | it "connects to configured host and port" do
54 | Rubillow::Request.request.address.should == Rubillow.configuration.host
55 | Rubillow::Request.request.port.should == Rubillow.configuration.port
56 | end
57 |
58 | it "sets configured open and read timeouts" do
59 | Rubillow::Request.request.open_timeout.should == Rubillow.configuration.http_open_timeout
60 | Rubillow::Request.request.read_timeout.should == Rubillow.configuration.http_read_timeout
61 | end
62 | end
63 |
64 | describe Rubillow::Request, ".uri" do
65 | it "generates a request path and query string based on parameters" do
66 | Rubillow::Request.uri("GetRegionChart", { :key_with_spaces => 12, :normal => "maybe", :camelCased => 1340}).should == "/webservice/GetRegionChart.htm?camelCased=1340&key-with-spaces=12&normal=maybe"
67 | end
68 | end
--------------------------------------------------------------------------------
/spec/rubillow/property_details_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::PropertyDetails, ".deep_search_results" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => "", :code => 200) }
6 |
7 | it "requires the address option" do
8 | lambda {
9 | Rubillow::PropertyDetails.deep_search_results({ :citystatezip => 'Baton Rouge, LA, 70802' })
10 | }.should raise_error(ArgumentError, "The address option is required")
11 | end
12 |
13 | it "requires the citystatezip option" do
14 | lambda {
15 | Rubillow::PropertyDetails.deep_search_results({ :address => '100 North Blvd' })
16 | }.should raise_error(ArgumentError, "The citystatezip option is required")
17 | end
18 |
19 | it "fetches the XML" do
20 | Rubillow::Request.stub(:request).and_return(request)
21 |
22 | response = Rubillow::PropertyDetails.deep_search_results({ :address => '2114 Bigelow Ave', :citystatezip => 'Seattle, WA' })
23 | response.should be_an_instance_of(Rubillow::Models::DeepSearchResult)
24 | end
25 | end
26 |
27 | describe Rubillow::PropertyDetails, ".deep_comps" do
28 | let(:request) { stub("Request", :get => response) }
29 | let(:response) { stub("Response", :body => "", :code => 200) }
30 |
31 | it "requires the zpid option" do
32 | lambda {
33 | Rubillow::PropertyDetails.deep_comps()
34 | }.should raise_error(ArgumentError, "The zpid option is required")
35 | end
36 |
37 | it "requires the count option" do
38 | lambda {
39 | Rubillow::PropertyDetails.deep_comps({ :zpid => '48749425' })
40 | }.should raise_error(ArgumentError, "The count option is required")
41 | end
42 |
43 | it "fetches the XML" do
44 | Rubillow::Request.stub(:request).and_return(request)
45 |
46 | response = Rubillow::PropertyDetails.deep_comps({ :zpid => '48749425', :count => 5 })
47 | response.should be_an_instance_of(Rubillow::Models::DeepComps)
48 | end
49 | end
50 |
51 | describe Rubillow::PropertyDetails, ".updated_property_details" do
52 | let(:request) { stub("Request", :get => response) }
53 | let(:response) { stub("Response", :body => "", :code => 200) }
54 |
55 | it "requires the zpid option" do
56 | lambda {
57 | Rubillow::PropertyDetails.updated_property_details()
58 | }.should raise_error(ArgumentError, "The zpid option is required")
59 | end
60 |
61 | it "fetches the XML" do
62 | Rubillow::Request.stub(:request).and_return(request)
63 |
64 | response = Rubillow::PropertyDetails.updated_property_details({ :zpid => '48749425' })
65 | response.should be_an_instance_of(Rubillow::Models::UpdatedPropertyDetails)
66 | end
67 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/monthly_payments.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Monthly payment information
4 | class MonthlyPayments < Base
5 | # @return [Hash] 30 year fixed rate data (:rate, :principal_and_interest, :mortgage_insurance).
6 | #
7 | # @example
8 | # puts thirty_year_fixed[:rate]
9 | #
10 | attr_accessor :thirty_year_fixed
11 |
12 | # @return [Hash] 15 year fixed rate data (:rate, :principal_and_interest, :mortgage_insurance).
13 | #
14 | # @example
15 | # puts fifteen_year_fixed[:rate]
16 | #
17 | attr_accessor :fifteen_year_fixed
18 |
19 | # @return [Hash] 5/1 fixed rate data (:rate, :principal_and_interest, :mortgage_insurance).
20 | #
21 | # @example
22 | # puts five_one_arm[:rate]
23 | #
24 | attr_accessor :five_one_arm
25 |
26 | # @return [String] down payment amount.
27 | attr_accessor :down_payment
28 |
29 | # @return [String] monthly property taxes (estimated).
30 | attr_accessor :monthly_property_taxes
31 |
32 | # @return [String] monthyly hazard insurance (estimated).
33 | attr_accessor :monthly_hazard_insurance
34 |
35 | protected
36 |
37 | # @private
38 | def parse
39 | super
40 |
41 | return if !success?
42 |
43 | @thirty_year_fixed = {
44 | :rate => @parser.xpath('//payment[@loanType="thirtyYearFixed"]/rate').text,
45 | :principal_and_interest => @parser.xpath('//payment[@loanType="thirtyYearFixed"]/monthlyPrincipalAndInterest').text,
46 | :mortgage_insurance => @parser.xpath('//payment[@loanType="thirtyYearFixed"]/monthlyMortgageInsurance').text,
47 | }
48 | @fifteen_year_fixed = {
49 | :rate => @parser.xpath('//payment[@loanType="fifteenYearFixed"]/rate').text,
50 | :principal_and_interest => @parser.xpath('//payment[@loanType="fifteenYearFixed"]/monthlyPrincipalAndInterest').text,
51 | :mortgage_insurance => @parser.xpath('//payment[@loanType="fifteenYearFixed"]/monthlyMortgageInsurance').text,
52 | }
53 | @five_one_arm = {
54 | :rate => @parser.xpath('//payment[@loanType="fiveOneARM"]/rate').text,
55 | :principal_and_interest => @parser.xpath('//payment[@loanType="fiveOneARM"]/monthlyPrincipalAndInterest').text,
56 | :mortgage_insurance => @parser.xpath('//payment[@loanType="fiveOneARM"]/monthlyMortgageInsurance').text,
57 | }
58 | @down_payment = @parser.xpath('//downPayment').text
59 | @monthly_property_taxes = @parser.xpath('//monthlyPropertyTaxes').text
60 | @monthly_hazard_insurance = @parser.xpath('//monthlyHazardInsurance').text
61 | end
62 | end
63 | end
64 | end
--------------------------------------------------------------------------------
/lib/rubillow/models.rb:
--------------------------------------------------------------------------------
1 | require "nokogiri"
2 | require "rubillow/helpers/xml_parsing_helper"
3 |
4 | module Rubillow
5 | # Sub module for returned and parsed web service data
6 | module Models
7 | # Base class for data models
8 | class Base
9 | include XmlParsingHelper
10 |
11 | # @return [String] the raw XML content from the service
12 | attr_accessor :xml
13 |
14 | # @private
15 | # @return [Nokogiri::XML::Reader] xml parser
16 | attr_accessor :parser
17 |
18 | # @return [String] response message
19 | attr_accessor :message
20 |
21 | # @return [Integer] response code
22 | attr_accessor :code
23 |
24 | # @return [Boolean] nearing API's daily request limit
25 | attr_accessor :near_limit
26 |
27 | # @private
28 | # Initialize the model
29 | #
30 | # @param [String] xml the raw XML from the service
31 | def initialize(xml)
32 | if !empty?(xml)
33 | @xml = xml
34 | @parser = Nokogiri::XML(xml) { |cfg| cfg.noblanks }
35 | parse
36 | else
37 | @code = -1
38 | @message = 'Error connecting to remote service.'
39 | end
40 | end
41 |
42 | # Was the request successful?
43 | # @return [Boolean] +true+ on successful request
44 | def success?
45 | @code.to_i == 0
46 | end
47 |
48 | protected
49 |
50 | # @private
51 | # Parses the xml content
52 | def parse
53 | @message = @parser.xpath('//message/text').text
54 | @code = @parser.xpath('//message/code').text.to_i
55 |
56 | limit = @parser.xpath('//message/limit-warning')
57 | @near_limit = !limit.empty? && limit.text.downcase == "true"
58 | end
59 |
60 | def empty?(elm)
61 | elm.respond_to?(:empty?) ? elm.empty? : !elm
62 | end
63 | end
64 | end
65 | end
66 |
67 | require "rubillow/models/zpidable"
68 | require "rubillow/models/linkable"
69 | require "rubillow/models/addressable"
70 | require "rubillow/models/zestimateable"
71 | require "rubillow/models/property_basics"
72 | require "rubillow/models/images"
73 | require "rubillow/models/search_result"
74 | require "rubillow/models/chart"
75 | require "rubillow/models/property_chart"
76 | require "rubillow/models/comps"
77 | require "rubillow/models/rate_summary"
78 | require "rubillow/models/monthly_payments"
79 | require "rubillow/models/postings"
80 | require "rubillow/models/posting"
81 | require "rubillow/models/deep_search_result"
82 | require "rubillow/models/deep_comps"
83 | require "rubillow/models/updated_property_details"
84 | require "rubillow/models/demographics"
85 | require "rubillow/models/demographic_value"
86 | require "rubillow/models/region_children"
87 | require "rubillow/models/region_chart"
88 | require "rubillow/models/region"
89 |
--------------------------------------------------------------------------------
/spec/xml/get_deep_search_results_missing_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 23366-Torrey-St
5 | Armada MI 48005
6 |
7 |
8 | Request successfully processed
9 | 0
10 |
11 |
12 |
13 |
14 | 83624854
15 |
16 | http://www.zillow.com/homedetails/23366-Torrey-St-Armada-MI-48005/83624854_zpid/
17 | http://www.zillow.com/homedetails/23366-Torrey-St-Armada-MI-48005/83624854_zpid/#charts-and-data
18 | http://www.zillow.com/homes/83624854_zpid/
19 | http://www.zillow.com/homes/comps/83624854_zpid/
20 |
21 |
22 | 23366 Torrey St
23 | 48005
24 | Armada
25 | MI
26 | 42.842281
27 | -82.88019
28 |
29 | 26099
30 | SingleFamily
31 | 2011
32 | 100376.0
33 | 14810
34 |
35 | 89301
36 | 04/09/2013
37 |
38 | -1566
39 |
40 | 74120
41 | 108947
42 |
43 | 0
44 |
45 |
46 |
47 |
48 | http://www.zillow.com/local-info/MI-Armada/r_50765/
49 | http://www.zillow.com/armada-mi/fsbo/
50 | http://www.zillow.com/armada-mi/
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/spec/xml/get_search_results.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2114 Bigelow Ave
5 | Seattle, WA
6 |
7 |
8 | Request successfully processed
9 | 0
10 |
11 |
12 |
13 |
14 | 48749425
15 |
16 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
17 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
18 | http://www.zillow.com/homes/48749425_zpid/
19 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
20 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
21 | http://www.zillow.com/homes/comps/48749425_zpid/
22 |
23 |
24 | 2114 Bigelow Ave N
25 | 98109
26 | Seattle
27 | WA
28 | 47.637933
29 | -122.347938
30 |
31 |
32 | 1032000
33 | 08/24/2011
34 |
35 | 5900
36 |
37 | 866880
38 | 1259040
39 |
40 | 0
41 |
42 |
43 |
44 |
45 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
46 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
47 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/spec/xml/get_zestimate_missing_value_duration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 29366758
5 |
6 |
7 | Request successfully processed
8 | 0
9 |
10 |
11 | 29366758
12 |
13 | http://www.zillow.com/homedetails/7500-Blue-Beach-Cv-Austin-TX-78759/29366758_zpid/
14 | http://www.zillow.com/homedetails/7500-Blue-Beach-Cv-Austin-TX-78759/29366758_zpid/#charts-and-data
15 | http://www.zillow.com/homes/29366758_zpid/
16 | http://www.zillow.com/homes/comps/29366758_zpid/
17 |
18 |
19 | 7500 Blue Beach Cv
20 | 78759
21 | Austin
22 | TX
23 | 30.414619
24 | -97.777438
25 |
26 |
27 | 382942
28 | 04/09/2013
29 |
30 | 4774
31 |
32 | 329330
33 | 436554
34 |
35 | 73
36 |
37 |
38 | 2505
39 | 04/01/2013
40 |
41 |
42 |
43 | 1979
44 | 3056
45 |
46 |
47 |
48 |
49 |
50 | http://www.zillow.com/local-info/TX-Austin/r_10221/
51 | http://www.zillow.com/austin-tx/fsbo/
52 | http://www.zillow.com/austin-tx/
53 |
54 |
55 |
56 |
57 | 92668
58 | 10221
59 | 1440
60 | 54
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rubillow
2 |
3 | Rubillow is a ruby library to access the [Zillow API](http://www.zillow.com/howto/api/APIOverview.htm).
4 |
5 | Supports all of the API methods, with real ruby classes returned for each call.
6 |
7 | ### [Home Valuation API](http://www.zillow.com/howto/api/HomeValuationAPIOverview.htm)
8 | * [GetSearchResults](http://www.zillow.com/howto/api/GetSearchResults.htm)
9 | * [GetZestimate](http://www.zillow.com/howto/api/GetZestimate.htm)
10 | * [GetChart](http://www.zillow.com/howto/api/GetChart.htm)
11 | * [GetComps](http://www.zillow.com/howto/api/GetComps.htm)
12 |
13 | ### [Neighborhood Data](http://www.zillow.com/webtools/neighborhood-data/)
14 | * [GetDemographics](http://www.zillow.com/howto/api/GetDemographics.htm)
15 | * [GetRegionChildren](http://www.zillow.com/howto/api/GetRegionChildren.htm)
16 | * [GetRegionChart](http://www.zillow.com/howto/api/GetRegionChart.htm)
17 |
18 | ### [Mortgage API](http://www.zillow.com/howto/api/MortgageAPIOverview.htm)
19 | * [GetRateSummary](http://www.zillow.com/howto/api/GetRateSummary.htm)
20 | * [GetMonthlyPayments](http://www.zillow.com/howto/api/GetMonthlyPayments.htm)
21 |
22 | ### [Property Details API](http://www.zillow.com/howto/api/PropertyDetailsAPIOverview.htm)
23 | * [GetDeepSearchResults](http://www.zillow.com/howto/api/GetDeepSearchResults.htm)
24 | * [GetDeepComps](http://www.zillow.com/howto/api/GetDeepComps.htm)
25 | * [GetUpdatedPropertyDetails](http://www.zillow.com/howto/api/GetUpdatedPropertyDetails.htm)
26 |
27 | ### [Postings API](http://www.zillow.com/howto/api/GetRegionPostings.htm)
28 | * [GetRegionPostings](http://www.zillow.com/howto/api/GetRegionPostings.htm)
29 |
30 | # Installing
31 |
32 | gem install rubillow
33 |
34 | or add the following to your Gemfile:
35 |
36 | gem "rubillow"
37 |
38 | # Examples
39 |
40 | Adding setup into an initializer:
41 |
42 | Rubillow.configure do |configuration|
43 | configuration.zwsid = "abcd1234"
44 | end
45 |
46 | Getting property Zestimate:
47 |
48 | property = Rubillow::HomeValuation.zestimate({ :zpid => '48749425' })
49 | if property.success?
50 | puts property.price
51 | end
52 |
53 | # Documentation
54 |
55 | You should find the documentation for your version of Rubillow on [Rubygems](http://rubygems.org/gems/rubillow).
56 |
57 | # More Information
58 |
59 | * [Rubygems](http://rubygems.org/gems/rubillow)
60 | * [Issues](http://github.com/synewaves/rubillow/issues)
61 |
62 | # Build & Dependency Status
63 |
64 | [](http://badge.fury.io/rb/rubillow)
65 | [](https://travis-ci.org/synewaves/rubillow)
66 | [](https://gemnasium.com/synewaves/rubillow)
67 | [](https://codeclimate.com/github/synewaves/rubillow)
68 | [](https://coveralls.io/r/synewaves/rubillow)
69 |
70 | # License
71 |
72 | Rubillow uses the MIT license. See LICENSE for more details.
--------------------------------------------------------------------------------
/spec/xml/get_zestimate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 48749425
5 |
6 |
7 | Request successfully processed
8 | 0
9 |
10 |
11 | 48749425
12 |
13 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
14 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
15 | http://www.zillow.com/homes/48749425_zpid/
16 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
17 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
18 | http://www.zillow.com/homes/comps/48749425_zpid/
19 |
20 |
21 | 2114 Bigelow Ave N
22 | 98109
23 | Seattle
24 | WA
25 | 47.637933
26 | -122.347938
27 |
28 |
29 | 1032000
30 | 08/24/2011
31 |
32 | 5900
33 |
34 | 866880
35 | 1259040
36 |
37 | 95
38 |
39 |
40 | 3379
41 | 12/17/2012
42 |
43 | 107
44 |
45 | 2154
46 | 5102
47 |
48 |
49 |
50 |
51 |
52 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
53 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
54 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
55 |
56 |
57 |
58 |
59 | 99569
60 | 16037
61 | 207
62 | 59
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/lib/rubillow/models/updated_property_details.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # List of updated attributes for a property.
4 | class UpdatedPropertyDetails < Base
5 | include Zpidable
6 | include Addressable
7 | include Linkable
8 | include Images
9 |
10 | # @return [Hash] number of page views (:current_month, :total).
11 | #
12 | # @example
13 | # puts page_views[:current_month]
14 | #
15 | attr_accessor :page_views
16 |
17 | # @return [String] price.
18 | attr_accessor :price
19 |
20 | # @return [String] neighborhood.
21 | attr_accessor :neighborhood
22 |
23 | # @return [String] elementary school's name.
24 | attr_accessor :elementary_school
25 |
26 | # @return [String] middle school's name.
27 | attr_accessor :middle_school
28 |
29 | # @return [String] school district's name.
30 | attr_accessor :school_district
31 |
32 | # @return [String] Realtor provided home description
33 | attr_accessor :home_description
34 |
35 | # @return [Hash] posting information
36 | #
37 | # @example
38 | # posting.each do |key, value|
39 | # end
40 | #
41 | attr_accessor :posting
42 |
43 | # @return [Hash] list of edited facts
44 | #
45 | # @example
46 | # edited_facts.each do |key, value|
47 | # end
48 | #
49 | attr_accessor :edited_facts
50 |
51 | protected
52 |
53 | # @private
54 | def parse
55 | super
56 |
57 | return if !success?
58 |
59 | extract_zpid(@parser)
60 | extract_links(@parser)
61 | extract_address(@parser)
62 | extract_images(@parser)
63 |
64 | @page_views = {
65 | :current_month => @parser.xpath('//pageViewCount/currentMonth').first.text,
66 | :total => @parser.xpath('//pageViewCount/total').first.text
67 | }
68 | @price = @parser.xpath('//price').first.text
69 | @neighborhood = @parser.xpath('//neighborhood').first.text
70 | @school_district = @parser.xpath('//schoolDistrict').first.text
71 | @elementary_school = @parser.xpath('//elementarySchool').first.text
72 | @middle_school = @parser.xpath('//middleSchool').first.text
73 | @home_description = @parser.xpath('//homeDescription').first.text
74 |
75 | @posting = {}
76 | @parser.xpath('//posting').children.each do |elm|
77 | @posting[underscore(elm.name).to_sym] = elm.text
78 | end
79 |
80 | @edited_facts = {}
81 | @parser.xpath('//editedFacts').children.each do |elm|
82 | @edited_facts[underscore(elm.name).to_sym] = elm.text
83 | end
84 | end
85 |
86 | # @private
87 | def underscore(string)
88 | word = string.to_s.dup
89 | word.gsub!(/::/, '/')
90 | word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
91 | word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
92 | word.gsub!(/\-/, '_')
93 | word.downcase!
94 | word
95 | end
96 | end
97 | end
98 | end
--------------------------------------------------------------------------------
/spec/xml/get_deep_search_results.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2114 Bigelow Ave
5 | Seattle, WA
6 |
7 |
8 | Request successfully processed
9 | 0
10 |
11 |
12 |
13 |
14 | 48749425
15 |
16 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
17 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
18 | http://www.zillow.com/homes/48749425_zpid/
19 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
20 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
21 | http://www.zillow.com/homes/comps/48749425_zpid/
22 |
23 |
24 | 2114 Bigelow Ave N
25 | 98109
26 | Seattle
27 | WA
28 | 47.637933
29 | -122.347938
30 |
31 | 53033
32 | SingleFamily
33 | 2010
34 | 872000.0
35 | 1924
36 | 4680
37 | 3470
38 | 3.0
39 | 4
40 | 11/26/2008
41 | 1025000
42 |
43 | 1032000
44 | 08/24/2011
45 |
46 | 5900
47 |
48 | 866880
49 | 1259040
50 |
51 | 0
52 |
53 |
54 |
55 |
56 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
57 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
58 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/lib/rubillow/mortgage.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Interface for the Mortgage API.
3 | #
4 | # Read the more about this API at: {http://www.zillow.com/howto/api/MortgageAPIOverview.htm}
5 | class Mortgage
6 | # Retrieve the current rates for today and one week ago for each loan type.
7 | #
8 | # Read more at: {http://www.zillow.com/howto/api/GetRateSummary.htm}.
9 | #
10 | # @example
11 | # data = Rubillow::Mortgage.rate_summary
12 | #
13 | # if data.success?
14 | # puts data.today[:thirty_year_fixed] # "4.01"
15 | # puts data.today[:five_one_arm] # "2.73"
16 | # puts data.last_week[:fifteen_year_fixed] # "3.27"
17 | # end
18 | #
19 | # @param [Hash] options The options for the API request.
20 | # @option options [String] :state The state to limit rates.
21 | # @return [Models::RateSummary] Mortgage rate summary.
22 | def self.rate_summary(options = {})
23 | options = {
24 | :zws_id => Rubillow.configuration.zwsid,
25 | :state => nil,
26 | }.merge!(options)
27 | options[:output] = 'xml'
28 |
29 | Models::RateSummary.new(Rubillow::Request.get("GetRateSummary", options))
30 | end
31 |
32 | # Retrieve the current monthly payment information for a given loan amount
33 | #
34 | # Read more at: {http://www.zillow.com/howto/api/GetMonthlyPayments.htm}.
35 | #
36 | # \* Either the down or dollars down options are required.
37 | #
38 | # @example
39 | # data = Rubillow::Mortgage.monthly_payments({ :price => "300000", :down => "15", :zip => "98104" })
40 | #
41 | # if data.success?
42 | # puts data.thirty_year_fixed[:rate] # "4.01"
43 | # puts data.thirty_year_fixed[:mortgage_insurance] # "93"
44 | #
45 | # puts data.down_payment # "45000"
46 | # puts data.monthly_property_taxes # "193"
47 | # end
48 | #
49 | # @param [Hash] options The options for the API request.
50 | # @option options [Float] :price The loan amount. (required)
51 | # @option options [Float] :down The percentage of the price as a down payment. Defaults to 20%. If less than 20%, private mortgage insurance is returned. (required *)
52 | # @option options [Float] :dollarsdown The dollar amount as down payment. Used if down option is omitted. If less than 20% of total price, private mortgage insurance is returned. (required *)
53 | # @option options [String] :zip The ZIP code for the property. If provided, property tax and hazard insurance will be returned.
54 | # @return [Models::MonthlyPayments] Monthly mortage information.
55 | def self.monthly_payments(options = {})
56 | options = {
57 | :zws_id => Rubillow.configuration.zwsid,
58 | :price => nil,
59 | :down => nil,
60 | :dollarsdown => nil,
61 | :zip => nil,
62 | }.merge!(options)
63 | options[:output] = 'xml'
64 |
65 | if options[:price].nil?
66 | raise ArgumentError, "The price option is required"
67 | end
68 | if options[:down].nil? && options[:dollarsdown].nil?
69 | raise ArgumentError, "Either the down or dollarsdown option is required"
70 | end
71 |
72 | Models::MonthlyPayments.new(Rubillow::Request.get("GetMonthlyPayments", options))
73 | end
74 | end
75 | end
--------------------------------------------------------------------------------
/spec/rubillow/home_valuation_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::HomeValuation, ".search_results" do
4 | let(:request) { stub("Request", :get => response) }
5 | let(:response) { stub("Response", :body => "", :code => 200) }
6 |
7 | it "requires the address option" do
8 | lambda {
9 | Rubillow::HomeValuation.search_results({ :citystatezip => 'Seattle, WA' })
10 | }.should raise_error(ArgumentError, "The address option is required")
11 | end
12 |
13 | it "requires the citystatezip option" do
14 | lambda {
15 | Rubillow::HomeValuation.search_results({ :address => '2114 Bigelow Ave' })
16 | }.should raise_error(ArgumentError, "The citystatezip option is required")
17 | end
18 |
19 | it "fetches the XML" do
20 | Rubillow::Request.stub(:request).and_return(request)
21 |
22 | response = Rubillow::HomeValuation.search_results({ :address => '2114 Bigelow Ave', :citystatezip => 'Seattle, WA' })
23 | response.should be_an_instance_of(Rubillow::Models::SearchResult)
24 | end
25 | end
26 |
27 | describe Rubillow::HomeValuation, ".zestimate" do
28 | let(:request) { stub("Request", :get => response) }
29 | let(:response) { stub("Response", :body => "", :code => 200) }
30 |
31 | it "requires the zpid option" do
32 | lambda {
33 | Rubillow::HomeValuation.zestimate()
34 | }.should raise_error(ArgumentError, "The zpid option is required")
35 | end
36 |
37 | it "fetches the XML" do
38 | Rubillow::Request.stub(:request).and_return(request)
39 |
40 | response = Rubillow::HomeValuation.zestimate({ :zpid => '48749425' })
41 | response.should be_an_instance_of(Rubillow::Models::SearchResult)
42 | end
43 | end
44 |
45 | describe Rubillow::HomeValuation, ".chart" do
46 | let(:request) { stub("Request", :get => response) }
47 | let(:response) { stub("Response", :body => "", :code => 200) }
48 |
49 | it "requires the zpid option" do
50 | lambda {
51 | Rubillow::HomeValuation.chart()
52 | }.should raise_error(ArgumentError, "The zpid option is required")
53 | end
54 |
55 | it "requires the unit_type option" do
56 | lambda {
57 | Rubillow::HomeValuation.chart({ :zpid => '48749425' })
58 | }.should raise_error(ArgumentError, "The unit_type option is required")
59 | end
60 |
61 | it "fetches the XML" do
62 | Rubillow::Request.stub(:request).and_return(request)
63 |
64 | response = Rubillow::HomeValuation.chart({ :zpid => '48749425', :unit_type => "percent" })
65 | response.should be_an_instance_of(Rubillow::Models::PropertyChart)
66 | end
67 | end
68 |
69 | describe Rubillow::HomeValuation, ".comps" do
70 | let(:request) { stub("Request", :get => response) }
71 | let(:response) { stub("Response", :body => "", :code => 200) }
72 |
73 | it "requires the zpid option" do
74 | lambda {
75 | Rubillow::HomeValuation.comps()
76 | }.should raise_error(ArgumentError, "The zpid option is required")
77 | end
78 |
79 | it "requires the count option" do
80 | lambda {
81 | Rubillow::HomeValuation.comps({ :zpid => '48749425' })
82 | }.should raise_error(ArgumentError, "The count option is required")
83 | end
84 |
85 | it "fetches the XML" do
86 | Rubillow::Request.stub(:request).and_return(request)
87 |
88 | response = Rubillow::HomeValuation.comps({ :zpid => '48749425', :count => 5 })
89 | response.should be_an_instance_of(Rubillow::Models::Comps)
90 | end
91 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/demographics_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::Demographics do
4 | it "populates the data" do
5 | data = Rubillow::Models::Demographics.new(get_xml('get_demographics.xml'))
6 |
7 | data.region.id.should == "250017"
8 | data.region.state.should == "Washington"
9 | data.region.neighborhood.should == "Ballard"
10 | data.region.latitude.should == "47.668329"
11 | data.region.longitude.should == "-122.384536"
12 | data.region.zmmrateurl.should == "http://www.zillow.com/mortgage-rates/wa/seattle/"
13 |
14 | data.links.count.should == 8
15 |
16 | data.charts.count.should == 6
17 | data.charts['Median Condo Value'].should == "http://www.zillow.com/app?chartType=affordability_avgCondoValue&graphType=barChart®ionId=250017®ionType=8&service=chart"
18 |
19 | data.affordability_data.count.should == 20
20 | data.affordability_data['Zillow Home Value Index'][:neighborhood].value.should == "305200"
21 | data.affordability_data['Zillow Home Value Index'][:neighborhood].type.should == "USD"
22 | data.affordability_data['Median Single Family Home Value'][:city].value.should == "377000"
23 | data.affordability_data['Median Condo Value'][:nation].value.should == "155800"
24 |
25 | data.census_data.count.should == 7
26 | data.census_data['HomeSize']['<1000sqft'].value.should == "0.3922527265889"
27 | data.census_data['HomeSize']['<1000sqft'].type.should == "percent"
28 | data.census_data['HomeSize']['1800-2400sqft'].value.should == "0.0699511094396"
29 | data.census_data['HomeType']['Other'].value.should == "1.9343463444890998"
30 | data.census_data['HomeType']['SingleFamily'].value.should == "0.1712158808933"
31 | data.census_data['Occupancy']['Rent'].value.should == "0.64971382"
32 | data.census_data['Occupancy']['Rent'].type.should == "percent"
33 | data.census_data['AgeDecade']['40s'].value.should == "0.159760457231474"
34 | data.census_data['AgeDecade']['40s'].type.should == "percent"
35 | data.census_data['Household']['NoKids'].value.should == "0.850066140827795"
36 | data.census_data['Household']['NoKids'].type.should == "percent"
37 |
38 | data.metrics.count.should == 3
39 | data.metrics['BuiltYear']['<1900'].value.should == "0.0419354838709"
40 | data.metrics['BuiltYear']['<1900'].to_s.should == "0.0419354838709"
41 | data.metrics['BuiltYear']['<1900'].type.should == "percent"
42 | data.metrics['BuiltYear']['1940-1959'].value.should == "0.0537634408602"
43 | data.metrics['BuiltYear']['1940-1959'].type.should == "percent"
44 | data.metrics['People Data']['Median Household Income'][:neighborhood].value.should == "41202.9453206937"
45 | data.metrics['People Data']['Single Females'][:city].value.should == "0.187486853578992"
46 | data.metrics['People Data']['Average Commute Time (Minutes)'][:nation].value.should == "26.375545725891282"
47 |
48 | data.segmentation.count.should == 3
49 | data.segmentation["Makin' It Singles"][:name].should == "Upper-scale urban singles."
50 | data.segmentation["Makin' It Singles"][:description].should == "Pre-middle-age to middle-age singles with upper-scale incomes. May or may not own their own home. Most have college educations and are employed in mid-management professions."
51 | data.segmentation['Aspiring Urbanites'][:name].should == "Urban singles with moderate income."
52 | data.segmentation['Aspiring Urbanites'][:description].should == "Low- to middle-income singles over a wide age range. Some have a college education. They work in a variety of occupations, including some management-level positions."
53 |
54 | data.characteristics.count.should == 4
55 | data.characteristics['Education'].should include("Bachelor's degrees")
56 | data.characteristics['Employment'].should include("Work in office and administrative support occupations")
57 | data.characteristics['People & Culture'].should include("Divorced females")
58 | data.characteristics['Transportation'].should include("Get to work by bus")
59 | end
60 | end
--------------------------------------------------------------------------------
/lib/rubillow/models/zestimateable.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Common data for responses containing Zestimate information
4 | module Zestimateable
5 | include Zpidable
6 | include Addressable
7 | include Linkable
8 |
9 | # @return [String] price
10 | attr_accessor :price
11 |
12 | # @return [Date] last updated date
13 | attr_accessor :last_updated
14 |
15 | # @return [Hash] valuation range (values: Strings, keys: :low, :high)
16 | #
17 | # @example
18 | # puts valuation_range[:low]
19 | #
20 | attr_accessor :valuation_range
21 |
22 | # @return [String] change value
23 | attr_accessor :change
24 |
25 | # @return [String] duration of change value
26 | attr_accessor :change_duration
27 |
28 | # @return [String] percentile
29 | attr_accessor :percentile
30 |
31 | # @return [Hash] local real estate links (values: URL strings, keys: :overview, :for_sale_by_owner, :for_sale)
32 | #
33 | # @example
34 | # puts local_real_estate[:overview]
35 | #
36 | attr_accessor :local_real_estate
37 |
38 | # @return [String] region name
39 | attr_accessor :region
40 |
41 | # @return [String] region id
42 | attr_accessor :region_id
43 |
44 | # @return [String] region type
45 | attr_accessor :region_type
46 |
47 | # @return [Hash] Rent Zestimate information (keys: :price, :last_updated, :value_change, :value_duration, :valuation_range => { :low, :high }, :percentile)
48 | attr_accessor :rent_zestimate
49 |
50 | protected
51 |
52 | # @private
53 | def extract_zestimate(xml)
54 | extract_zpid(xml)
55 | extract_links(xml)
56 | extract_address(xml)
57 |
58 | @price = xml.xpath('//zestimate/amount').first.text
59 | @last_updated = Date.strptime(xml.xpath('//zestimate/last-updated').first.text, "%m/%d/%Y")
60 | @valuation_range = {
61 | :low => xml.xpath('//zestimate/valuationRange/low').first.text,
62 | :high => xml.xpath('//zestimate/valuationRange/high').first.text,
63 | }
64 | @change = xml.xpath('//zestimate/valueChange').first.text
65 | if xml.xpath('//rentzestimate/amount').text.length > 0
66 | @rent_zestimate = {
67 | :price => xml.xpath('//rentzestimate/amount').first.text,
68 | :last_updated => xml.xpath('//rentzestimate/last-updated').first.text,
69 | :value_change => xml.xpath('//rentzestimate/valueChange').first.text,
70 | :value_duration => xml.xpath('//rentzestimate').first.xpath("//valueChange").first.attr("duration"),
71 | :valuation_range => {
72 | :low => xml.xpath('//rentzestimate/valuationRange/low').first.text,
73 | :high => xml.xpath('//rentzestimate/valuationRange/high').first.text
74 | },
75 | :percentile => xml.xpath('//rentzestimate/percentile').text
76 | }
77 | else
78 | @rent_zestimate = {}
79 | end
80 |
81 | if tmp = xml.xpath('//zestimate/valueChange').attr('duration')
82 | @change_duration = tmp.value
83 | end
84 | @percentile = xml.xpath('//zestimate/percentile').first.text
85 |
86 | if xml.at_xpath('//localRealEstate/region')
87 | @region = xml.xpath('//localRealEstate/region').attribute('name').value
88 | @region_id = xml.xpath('//localRealEstate/region').attribute('id').value
89 | @region_type = xml.xpath('//localRealEstate/region').attribute('type').value
90 |
91 | @local_real_estate = {
92 | :overview => xml.xpath('//localRealEstate/region/links/overview').first.text,
93 | :for_sale_by_owner => xml.xpath('//localRealEstate/region/links/forSaleByOwner').first.text,
94 | :for_sale => xml.xpath('//localRealEstate/region/links/forSale').first.text,
95 | }
96 | end
97 | end
98 | end
99 | end
100 | end
101 |
--------------------------------------------------------------------------------
/spec/rubillow/models/updated_property_details_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::UpdatedPropertyDetails do
4 | it "populates the data" do
5 | data = Rubillow::Models::UpdatedPropertyDetails.new(get_xml('get_updated_property_details.xml'))
6 |
7 | data.zpid.should == '48749425'
8 | data.links.count.should == 3
9 | data.links[:homeDetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
10 | data.links[:photoGallery].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#image=lightbox%3Dtrue"
11 | data.links[:homeInfo].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
12 | data.address[:street].should == "2114 Bigelow Ave N"
13 | data.address[:city].should == "Seattle"
14 | data.address[:state].should == "WA"
15 | data.address[:zipcode].should == "98109"
16 | data.address[:latitude].should == "47.637933"
17 | data.address[:longitude].should == "-122.347938"
18 | data.page_views[:current_month].should == "27"
19 | data.page_views[:total].should == "8095"
20 | data.price.should == "1290000"
21 | data.neighborhood.should == "Queen Anne"
22 | data.school_district.should == "Seattle"
23 | data.elementary_school.should == "John Hay"
24 | data.middle_school.should == "McClure"
25 | data.home_description.should == "Bright, spacious, 4 bedroom/3 bath Craftsman, with stunning, expansive views, on one of Queen Anne's finest streets. Views of Lk Union, Lk Washington,the Cascades from Mt. Baker to Mt. Rainier, and the city-from two levels and 2 view decks. Craftsman charm intact: hardwood floors, cove moldings, crystal doorknobs, Batchelder tile fireplace. Huge gourmet eat-in kitchen with slab granite countertops, deluxe master suite, theater-like media room, level rear yard with garden space and covered patio."
26 | data.posting[:status].should == "Active"
27 | data.posting[:agent_name].should == "John Blacksmith"
28 | data.posting[:agent_profile_url].should == "/profile/John.Blacksmith"
29 | data.posting[:brokerage].should == "Lake and Company Real Estate"
30 | data.posting[:type].should == "For sale by agent"
31 | data.posting[:last_updated_date].should == "2008-06-05 10:28:00.0"
32 | data.posting[:external_url].should == "http://mls.lakere.com/srch_mls/detail.php?mode=ag&LN=28097669&t=listings&l="
33 | data.posting[:mls].should == "28097669"
34 | data.images_count.should == "17"
35 | data.images.count.should == 5
36 | data.images[0].should == "http://photos1.zillow.com/is/image/i0/i4/i3019/IS1d2piz9kupb4z.jpg?op_sharpen=1&qlt=90&hei=400&wid=400"
37 | data.images[1].should == "http://photos3.zillow.com/is/image/i0/i4/i3019/ISkzzhgcun7u03.jpg?op_sharpen=1&qlt=90&hei=400&wid=400"
38 | data.images[2].should == "http://photos2.zillow.com/is/image/i0/i4/i3019/ISkzzhg8wkzq1v.jpg?op_sharpen=1&qlt=90&hei=400&wid=400"
39 | data.images[3].should == "http://photos1.zillow.com/is/image/i0/i4/i3019/ISkzzhg4yirm3n.jpg?op_sharpen=1&qlt=90&hei=400&wid=400"
40 | data.images[4].should == "http://photos3.zillow.com/is/image/i0/i4/i3019/ISkzzhg10gji5f.jpg?op_sharpen=1&qlt=90&hei=400&wid=400"
41 | data.edited_facts[:use_code].should == "SingleFamily"
42 | data.edited_facts[:bedrooms].should == "4"
43 | data.edited_facts[:bathrooms].should == "3.0"
44 | data.edited_facts[:finished_sq_ft].should == "3470"
45 | data.edited_facts[:lot_size_sq_ft].should == "4680"
46 | data.edited_facts[:year_built].should == "1924"
47 | data.edited_facts[:year_updated].should == "2003"
48 | data.edited_facts[:num_floors].should == "2"
49 | data.edited_facts[:basement].should == "Finished"
50 | data.edited_facts[:roof].should == "Composition"
51 | data.edited_facts[:view].should == "Water, City, Mountain"
52 | data.edited_facts[:parking_type].should == "Off-street"
53 | data.edited_facts[:heating_sources].should == "Gas"
54 | data.edited_facts[:heating_system].should == "Forced air"
55 | data.edited_facts[:rooms].should == "Laundry room, Walk-in closet, Master bath, Office, Dining room, Family room, Breakfast nook"
56 | end
57 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/comps_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::Comps do
4 | it "populates the results" do
5 | data = Rubillow::Models::Comps.new(get_xml('get_comps.xml'))
6 |
7 | data.principal.should be_a(Rubillow::Models::SearchResult)
8 | data.comparables.count.should == 5
9 |
10 | principal = data.principal
11 |
12 | principal.zpid.should == '48749425'
13 | principal.links.count.should == 5
14 | principal.links[:homedetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
15 | principal.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
16 | principal.links[:mapthishome].should == "http://www.zillow.com/homes/48749425_zpid/"
17 | principal.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48749425"
18 | principal.links[:comparables].should == "http://www.zillow.com/homes/comps/48749425_zpid/"
19 | principal.address[:street].should == "2114 Bigelow Ave N"
20 | principal.address[:city].should == "Seattle"
21 | principal.address[:state].should == "WA"
22 | principal.address[:zipcode].should == "98109"
23 | principal.address[:latitude].should == "47.637933"
24 | principal.address[:longitude].should == "-122.347938"
25 | principal.price.should == "1032000"
26 | principal.percentile.should == "95"
27 | principal.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
28 | principal.valuation_range[:low].should == "866880"
29 | principal.valuation_range[:high].should == "1259040"
30 | principal.change.should == "5900"
31 | principal.change_duration.should == "30"
32 | principal.local_real_estate.count.should == 3
33 | principal.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/"
34 | principal.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/"
35 | principal.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/"
36 | principal.region.should == "East Queen Anne"
37 | principal.region_type.should == "neighborhood"
38 | principal.region_id.should == "271856"
39 |
40 | comp = data.comparables['0.0813344']
41 | comp.should be_a(Rubillow::Models::SearchResult)
42 |
43 | comp.zpid.should == '48768095'
44 | comp.links.count.should == 5
45 | comp.links[:homedetails].should == "http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/"
46 | comp.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/#charts-and-data"
47 | comp.links[:mapthishome].should == "http://www.zillow.com/homes/48768095_zpid/"
48 | comp.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48768095"
49 | comp.links[:comparables].should == "http://www.zillow.com/homes/comps/48768095_zpid/"
50 | comp.address[:street].should == "2538 Mayfair Ave N"
51 | comp.address[:city].should == "Seattle"
52 | comp.address[:state].should == "WA"
53 | comp.address[:zipcode].should == "98109"
54 | comp.address[:latitude].should == "47.642566"
55 | comp.address[:longitude].should == "-122.352512"
56 | comp.price.should == "584400"
57 | comp.percentile.should == "75"
58 | comp.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
59 | comp.valuation_range[:low].should == "449988"
60 | comp.valuation_range[:high].should == "625308"
61 | comp.change.should == "-100"
62 | comp.change_duration.should == "30"
63 | comp.local_real_estate.count.should == 3
64 | comp.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/North-Queen-Anne/r_271942/"
65 | comp.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/North-Queen-Anne-Seattle-WA/"
66 | comp.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/North-Queen-Anne-Seattle-WA/"
67 | comp.region.should == "North Queen Anne"
68 | comp.region_type.should == "neighborhood"
69 | comp.region_id.should == "271942"
70 | end
71 | end
--------------------------------------------------------------------------------
/spec/xml/get_updated_property_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 48749425
5 |
6 |
7 | Request successfully processed
8 | 0
9 |
10 |
11 | 48749425
12 |
13 | 27
14 | 8095
15 |
16 |
17 | 2114 Bigelow Ave N
18 | 98109
19 | Seattle
20 | WA
21 | 47.637933
22 | -122.347938
23 |
24 |
25 | Active
26 | John Blacksmith
27 | /profile/John.Blacksmith
28 | Lake and Company Real Estate
29 | For sale by agent
30 | 2008-06-05 10:28:00.0
31 | http://mls.lakere.com/srch_mls/detail.php?mode=ag&LN=28097669&t=listings&l=
32 | 28097669
33 |
34 | 1290000
35 |
36 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
37 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#image=lightbox%3Dtrue
38 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
39 |
40 |
41 | 17
42 |
43 | http://photos1.zillow.com/is/image/i0/i4/i3019/IS1d2piz9kupb4z.jpg?op_sharpen=1&qlt=90&hei=400&wid=400
44 | http://photos3.zillow.com/is/image/i0/i4/i3019/ISkzzhgcun7u03.jpg?op_sharpen=1&qlt=90&hei=400&wid=400
45 | http://photos2.zillow.com/is/image/i0/i4/i3019/ISkzzhg8wkzq1v.jpg?op_sharpen=1&qlt=90&hei=400&wid=400
46 | http://photos1.zillow.com/is/image/i0/i4/i3019/ISkzzhg4yirm3n.jpg?op_sharpen=1&qlt=90&hei=400&wid=400
47 | http://photos3.zillow.com/is/image/i0/i4/i3019/ISkzzhg10gji5f.jpg?op_sharpen=1&qlt=90&hei=400&wid=400
48 |
49 |
50 |
51 | SingleFamily
52 | 4
53 | 3.0
54 | 3470
55 | 4680
56 | 1924
57 | 2003
58 | 2
59 | Finished
60 | Composition
61 | Water, City, Mountain
62 | Off-street
63 | Gas
64 | Forced air
65 | Laundry room, Walk-in closet, Master bath, Office, Dining room, Family room, Breakfast nook
66 |
67 | Bright, spacious, 4 bedroom/3 bath Craftsman, with stunning, expansive views, on one of Queen Anne's finest streets. Views of Lk Union, Lk Washington,the Cascades from Mt. Baker to Mt. Rainier, and the city-from two levels and 2 view decks. Craftsman charm intact: hardwood floors, cove moldings, crystal doorknobs, Batchelder tile fireplace. Huge gourmet eat-in kitchen with slab granite countertops, deluxe master suite, theater-like media room, level rear yard with garden space and covered patio.
68 | Queen Anne
69 | Seattle
70 | John Hay
71 | McClure
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/lib/rubillow/property_details.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Interface for the Property Details API.
3 | #
4 | # Read the more about this API at: {http://www.zillow.com/howto/api/PropertyDetailsAPIOverview.htm}
5 | class PropertyDetails
6 | # Retrieve extended details for a property.
7 | #
8 | # Read more at: {http://www.zillow.com/howto/api/GetDeepSearchResults.htm}.
9 | #
10 | # @example
11 | # data = Rubillow::PropertyDetails.deep_search_results({ :address => '2114 Bigelow Ave', :citystatezip => 'Seattle, WA' })
12 | #
13 | # if data.success?
14 | # puts data.tax_assessment_year # "2010"
15 | # puts data.last_sold_price # "1025000"
16 | # puts data.address[:latitude] # "47.637933"
17 | # end
18 | #
19 | # @param [Hash] options The options for the API request.
20 | # @option options [String] :address The address of the property to search. (required)
21 | # @option options [String] :citystatezip The city+state combination and/or ZIP code for which to search. Note that giving both city and state is required. Using just one will not work. (required)
22 | # @option options [Boolean] :rentzestimate Return Rent Zestimate information if available. Default: false
23 | # @return [Models::DeepSearchResult] Extended property details.
24 | def self.deep_search_results(options = {})
25 | options = {
26 | :zws_id => Rubillow.configuration.zwsid,
27 | :address => nil,
28 | :citystatezip => nil,
29 | :rentzestimate => false,
30 | }.merge!(options)
31 |
32 | if options[:address].nil?
33 | raise ArgumentError, "The address option is required"
34 | end
35 | if options[:citystatezip].nil?
36 | raise ArgumentError, "The citystatezip option is required"
37 | end
38 |
39 | Models::DeepSearchResult.new(Rubillow::Request.get("GetDeepSearchResults", options))
40 | end
41 |
42 | # Retrieve extended details for property and its comps.
43 | #
44 | # Read more at: {http://www.zillow.com/howto/api/GetDeepComps.htm}.
45 | #
46 | # @example
47 | # data = Rubillow::PropertyDetails.deep_comps({ :zpid => '48749425', :count => 5 })
48 | #
49 | # if data.success?
50 | # puts data.principal.price # "1032000"
51 | # data.comparables.each |comp|
52 | # puts comp.price
53 | # puts comp.address[:street]
54 | # end
55 | # end
56 | #
57 | # @param [Hash] options The options for the API request.
58 | # @option options [Integer] :zpid The Zillow Property ID of the property. (required)
59 | # @option options [Integer] :count The number of comps to return, between 1 and 25 inclusive. (required)
60 | # @option options [Boolean] :rentzestimate Return Rent Zestimate information if available. Default: false
61 | # @return [Models::DeepComps] Extended property and comp details.
62 | def self.deep_comps(options = {})
63 | options = {
64 | :zws_id => Rubillow.configuration.zwsid,
65 | :zpid => nil,
66 | :count => nil,
67 | :rentzestimate => false,
68 | }.merge!(options)
69 |
70 | if options[:zpid].nil?
71 | raise ArgumentError, "The zpid option is required"
72 | end
73 | if options[:count].nil?
74 | raise ArgumentError, "The count option is required"
75 | end
76 |
77 | Models::DeepComps.new(Rubillow::Request.get("GetDeepComps", options))
78 | end
79 |
80 | # Retrieve updated property facts for a given property.
81 | #
82 | # Read more at: {http://www.zillow.com/howto/api/GetUpdatedPropertyDetails.htm}.
83 | #
84 | # @example
85 | # data = Rubillow::PropertyDetails.updated_property_details({ :zpid => '48749425' })
86 | #
87 | # if data.success?
88 | # puts data.posting[:status] # "1032000"
89 | # puts data.posting[:type] # "For sale by agent"
90 | # data.edited_facts.each |fact|
91 | # puts fact
92 | # end
93 | # end
94 | #
95 | # @param [Hash] options The options for the API request.
96 | # @option options [Integer] :zpid The Zillow Property ID of the property. (required)
97 | # @return [Models::UpdatedPropertyDetails] Updated property information.
98 | def self.updated_property_details(options = {})
99 | options = {
100 | :zws_id => Rubillow.configuration.zwsid,
101 | :zpid => nil,
102 | }.merge!(options)
103 |
104 | if options[:zpid].nil?
105 | raise ArgumentError, "The zpid option is required"
106 | end
107 |
108 | Models::UpdatedPropertyDetails.new(Rubillow::Request.get("GetUpdatedPropertyDetails", options))
109 | end
110 | end
111 | end
112 |
--------------------------------------------------------------------------------
/lib/rubillow/models/demographics.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | module Models
3 | # Demographics data
4 | class Demographics < Base
5 | include Linkable
6 |
7 | # @return [Models::Region] region data.
8 | attr_accessor :region
9 |
10 | # @return [Hash] Charts (key: name, value: url).
11 | #
12 | # @example
13 | # charts.each do |name, value|
14 | # end
15 | #
16 | attr_accessor :charts
17 |
18 | # @return [Hash] Metrics ([high level point][low level point]).
19 | #
20 | # @example
21 | # puts metrics['BuiltYear']['<1900']
22 | #
23 | attr_accessor :metrics
24 |
25 | # @return [Hash] Affordability metrics (key: name, value: value).
26 | #
27 | # @example
28 | # puts affordability_data['Zillow Home Value Index'][:neighborhood]
29 | #
30 | attr_accessor :affordability_data
31 |
32 | # @return [Hash] US Census metrics ([high level point][low level point]).
33 | #
34 | # @example
35 | # puts census_data['Household']['NoKids']
36 | attr_accessor :census_data
37 |
38 | # @return [Hash] Segmentation metrics (key: name, value: [:name, :description])
39 | #
40 | # @example
41 | # puts segmentation["Makin' It Singles"][:name]
42 | # puts segmentation["Makin' It Singles"][:description]
43 | #
44 | attr_accessor :segmentation
45 |
46 | # @return [Hash] Characteristics metrics ([high level point] -> hash)
47 | #
48 | # @example
49 | # characteristics['Education']
50 | attr_accessor :characteristics
51 |
52 | protected
53 |
54 | # @private
55 | def parse
56 | super
57 |
58 | return if !success?
59 |
60 | extract_links(@parser)
61 |
62 | @region = Region.new(@parser.xpath('//region').to_xml)
63 |
64 | @charts = {}
65 | @parser.xpath('//charts').children.each do |elm|
66 | if elm.xpath('name').attribute('deprecated').nil?
67 | @charts[elm.xpath('name').text] = elm.xpath('url').text
68 | end
69 | end
70 |
71 | @metrics = {}
72 | @affordability_data = {}
73 | @census_data = {}
74 | @segmentation = {}
75 | @characteristics = {}
76 |
77 | @parser.xpath('//pages').children.each do |page|
78 | page.xpath('tables').children.each do |table|
79 | table_name = table.xpath('name').text
80 |
81 | if table_name == "Affordability Data" && table.parent.parent.xpath('name').text == "Affordability"
82 | extract_affordability_data(table)
83 | elsif table_name[0,14] == "Census Summary"
84 | extract_census_data(table, table_name[15, table_name.length])
85 | else
86 | extract_data(table, table_name)
87 | end
88 | end
89 | end
90 |
91 | @parser.xpath('//segmentation').children.each do |segment|
92 | @segmentation[segment.xpath('title').text] = {
93 | :name => segment.xpath('name').text,
94 | :description => segment.xpath('description').text,
95 | }
96 | end
97 |
98 | @parser.xpath('//uniqueness').children.each do |category|
99 | key = category.attribute('type').text
100 | @characteristics[key] = []
101 | category.xpath('characteristic').each do |c|
102 | @characteristics[key] << c.text
103 | end
104 | end
105 | end
106 |
107 | # @private
108 | def extract_affordability_data(xml)
109 | xml.xpath('data').children.each do |data|
110 | @affordability_data[data.xpath('name').text] = extract_metrics(data)
111 | end
112 | end
113 |
114 | # @private
115 | def extract_census_data(xml, type)
116 | @census_data[type] = {}
117 |
118 | xml.xpath('data').children.each do |data|
119 | @census_data[type][data.xpath('name').text] = extract_metrics(data)
120 | end
121 | end
122 |
123 | # @private
124 | def extract_data(xml, type)
125 | if @metrics[type].nil?
126 | @metrics[type] = {}
127 | end
128 |
129 | xml.xpath('data').children.each do |data|
130 | @metrics[type][data.xpath('name').text] = extract_metrics(data)
131 | end
132 | end
133 |
134 | # @private
135 | def extract_metrics(xml)
136 | if xml.xpath('values').empty?
137 | DemographicValue.new(xml.xpath('value'))
138 | else
139 | {
140 | :neighborhood => DemographicValue.new(xml.xpath('values/neighborhood/value')),
141 | :city => DemographicValue.new(xml.xpath('values/city/value')),
142 | :nation => DemographicValue.new(xml.xpath('values/nation/value')),
143 | :zip => DemographicValue.new(xml.xpath('values/zip/value')),
144 | }
145 | end
146 | end
147 | end
148 | end
149 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/deep_search_results_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::DeepSearchResult do
4 | it "populates the data" do
5 | data = Rubillow::Models::DeepSearchResult.new(get_xml('get_deep_search_results.xml'))
6 |
7 | data.zpid.should == '48749425'
8 | data.links.count.should == 5
9 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
10 | data.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
11 | data.links[:mapthishome].should == "http://www.zillow.com/homes/48749425_zpid/"
12 | data.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48749425"
13 | data.links[:comparables].should == "http://www.zillow.com/homes/comps/48749425_zpid/"
14 | data.address[:street].should == "2114 Bigelow Ave N"
15 | data.address[:city].should == "Seattle"
16 | data.address[:state].should == "WA"
17 | data.address[:zipcode].should == "98109"
18 | data.address[:latitude].should == "47.637933"
19 | data.address[:longitude].should == "-122.347938"
20 | data.price.should == "1032000"
21 | data.percentile.should == "0"
22 | data.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
23 | data.valuation_range[:low].should == "866880"
24 | data.valuation_range[:high].should == "1259040"
25 | data.change.should == "5900"
26 | data.change_duration.should == "30"
27 | data.local_real_estate.count.should == 3
28 | data.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/"
29 | data.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/"
30 | data.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/"
31 | data.region.should == "East Queen Anne"
32 | data.region_type.should == "neighborhood"
33 | data.region_id.should == "271856"
34 | data.fips_county.should == "53033"
35 | data.tax_assessment_year.should == "2010"
36 | data.tax_assessment.should == "872000.0"
37 | data.year_built.should == "1924"
38 | data.lot_size_square_feet.should == "4680"
39 | data.finished_square_feet.should == "3470"
40 | data.bathrooms.should == "3.0"
41 | data.bedrooms.should == "4"
42 | data.total_rooms.should == ""
43 | data.last_sold_date.strftime("%m/%d/%Y").should == "11/26/2008"
44 | data.last_sold_price.should == "1025000"
45 | data.use_code.should == "SingleFamily"
46 | end
47 |
48 | it "populates the data" do
49 | data = Rubillow::Models::DeepSearchResult.new(get_xml('get_deep_search_results_duplicated.xml'))
50 |
51 | data.zpid.should == '66109976'
52 | data.links.count.should == 4
53 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/3651-Louisiana-St-APT-205-San-Diego-CA-92104/2126762315_zpid/"
54 | data.links[:graphsanddata].should == "http://www.zillow.com/homedetails/3651-Louisiana-St-APT-205-San-Diego-CA-92104/66109976_zpid/#charts-and-data"
55 | data.links[:mapthishome].should == "http://www.zillow.com/homes/2126762315_zpid/"
56 | data.links[:comparables].should == "http://www.zillow.com/homes/comps/2126762315_zpid/"
57 | data.address[:street].should == "3651 Louisiana St APT 205"
58 | data.address[:city].should == "San Diego"
59 | data.address[:state].should == "CA"
60 | data.address[:zipcode].should == "92104"
61 | data.address[:latitude].should == "32.744895"
62 | data.address[:longitude].should == "-117.139743"
63 | data.price.should == "249461"
64 | data.percentile.should == "0"
65 | data.last_updated.strftime("%m/%d/%Y").should == "04/02/2013"
66 | data.valuation_range[:low].should == "219526"
67 | data.valuation_range[:high].should == "271912"
68 | data.change.should == "20262"
69 | data.change_duration.should == "30"
70 | data.local_real_estate.count.should == 3
71 | data.local_real_estate[:overview].should == "http://www.zillow.com/local-info/CA-San-Diego/North-Park/r_274717/"
72 | data.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/north-park-san-diego-ca/fsbo/"
73 | data.local_real_estate[:for_sale].should == "http://www.zillow.com/north-park-san-diego-ca/"
74 | data.region.should == "North Park"
75 | data.region_type.should == "neighborhood"
76 | data.region_id.should == "274717"
77 | data.fips_county.should == "6073"
78 | data.tax_assessment_year.should == "2012"
79 | data.tax_assessment.should == "180539.0"
80 | data.year_built.should == "1985"
81 | data.lot_size_square_feet.should == ""
82 | data.finished_square_feet.should == "800"
83 | data.bathrooms.should == "2.0"
84 | data.bedrooms.should == "2"
85 | data.total_rooms.should == ""
86 | data.last_sold_date.strftime("%m/%d/%Y").should == "06/10/2011"
87 | data.last_sold_price.should == "186000"
88 | data.use_code.should == "Condominium"
89 | end
90 |
91 | it "doesn't raise an error when data is missing" do
92 | lambda {
93 | Rubillow::Models::DeepSearchResult.new(get_xml("get_deep_search_results_missing_data.xml"))
94 | }.should_not raise_error
95 | end
96 | end
97 |
--------------------------------------------------------------------------------
/spec/rubillow/models/deep_comps_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::DeepComps do
4 | it "populates the data" do
5 | data = Rubillow::Models::DeepComps.new(get_xml('get_deep_comps.xml'))
6 |
7 | data.principal.should be_a(Rubillow::Models::DeepSearchResult)
8 | data.comparables.count.should == 5
9 |
10 | principal = data.principal
11 | principal.zpid.should == '48749425'
12 | principal.links.count.should == 5
13 | principal.links[:homedetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
14 | principal.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
15 | principal.links[:mapthishome].should == "http://www.zillow.com/homes/48749425_zpid/"
16 | principal.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48749425"
17 | principal.links[:comparables].should == "http://www.zillow.com/homes/comps/48749425_zpid/"
18 | principal.address[:street].should == "2114 Bigelow Ave N"
19 | principal.address[:city].should == "Seattle"
20 | principal.address[:state].should == "WA"
21 | principal.address[:zipcode].should == "98109"
22 | principal.address[:latitude].should == "47.637933"
23 | principal.address[:longitude].should == "-122.347938"
24 | principal.price.should == "1032000"
25 | principal.percentile.should == "95"
26 | principal.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
27 | principal.valuation_range[:low].should == "866880"
28 | principal.valuation_range[:high].should == "1259040"
29 | principal.change.should == "5900"
30 | principal.change_duration.should == "30"
31 | principal.local_real_estate.count.should == 3
32 | principal.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/"
33 | principal.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/"
34 | principal.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/"
35 | principal.region.should == "East Queen Anne"
36 | principal.region_type.should == "neighborhood"
37 | principal.region_id.should == "271856"
38 | principal.fips_county.should == ""
39 | principal.tax_assessment_year.should == "2010"
40 | principal.tax_assessment.should == "872000.0"
41 | principal.year_built.should == "1924"
42 | principal.lot_size_square_feet.should == "4680"
43 | principal.finished_square_feet.should == "3470"
44 | principal.bathrooms.should == "3.0"
45 | principal.bedrooms.should == "4"
46 | principal.total_rooms.should == ""
47 | principal.last_sold_date.strftime("%m/%d/%Y").should == "11/26/2008"
48 | principal.last_sold_price.should == "1025000"
49 | principal.use_code.should == ""
50 |
51 | comp = data.comparables["0.0813344"]
52 | comp.should be_a(Rubillow::Models::DeepSearchResult)
53 | comp.zpid.should == '48768095'
54 | comp.links.count.should == 5
55 | comp.links[:homedetails].should == "http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/"
56 | comp.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/#charts-and-data"
57 | comp.links[:mapthishome].should == "http://www.zillow.com/homes/48768095_zpid/"
58 | comp.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48768095"
59 | comp.links[:comparables].should == "http://www.zillow.com/homes/comps/48768095_zpid/"
60 | comp.address[:street].should == "2538 Mayfair Ave N"
61 | comp.address[:city].should == "Seattle"
62 | comp.address[:state].should == "WA"
63 | comp.address[:zipcode].should == "98109"
64 | comp.address[:latitude].should == "47.642566"
65 | comp.address[:longitude].should == "-122.352512"
66 | comp.price.should == "584400"
67 | comp.percentile.should == "75"
68 | comp.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
69 | comp.valuation_range[:low].should == "449988"
70 | comp.valuation_range[:high].should == "625308"
71 | comp.change.should == "-100"
72 | comp.change_duration.should == "30"
73 | comp.local_real_estate.count.should == 3
74 | comp.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/North-Queen-Anne/r_271942/"
75 | comp.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/North-Queen-Anne-Seattle-WA/"
76 | comp.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/North-Queen-Anne-Seattle-WA/"
77 | comp.region.should == "North Queen Anne"
78 | comp.region_type.should == "neighborhood"
79 | comp.region_id.should == "271942"
80 | comp.fips_county.should == ""
81 | comp.tax_assessment_year.should == "2010"
82 | comp.tax_assessment.should == "431000.0"
83 | comp.year_built.should == "1955"
84 | comp.lot_size_square_feet.should == "16514"
85 | comp.finished_square_feet.should == "2600"
86 | comp.bathrooms.should == "2.75"
87 | comp.bedrooms.should == "4"
88 | comp.total_rooms.should == "8"
89 | comp.last_sold_date.strftime("%m/%d/%Y").should == "07/27/2011"
90 | comp.last_sold_price.should == "545000"
91 | comp.use_code.should == ""
92 | end
93 | end
--------------------------------------------------------------------------------
/spec/xml/get_deep_search_results_duplicated.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 3651 louisiana st 205 san diego ca
4 | 92104
5 |
6 |
7 | Request successfully processed
8 | 0
9 |
10 |
11 |
12 |
13 | 66109976
14 |
15 | http://www.zillow.com/homedetails/3651-Louisiana-St-APT-205-San-Diego-CA-92104/66109976_zpid/
16 | http://www.zillow.com/homedetails/3651-Louisiana-St-APT-205-San-Diego-CA-92104/66109976_zpid/#charts-and-data
17 | http://www.zillow.com/homes/66109976_zpid/
18 | http://www.zillow.com/homes/comps/66109976_zpid/
19 |
20 |
21 | 3651 Louisiana St APT 205
22 | 92104
23 | San Diego
24 | CA
25 | 32.744895
26 | -117.139743
27 |
28 | 6073
29 | Condominium
30 | 2012
31 | 180539.0
32 | 1985
33 | 800
34 | 2.0
35 | 2
36 | 06/10/2011
37 | 186000
38 |
39 | 249461
40 | 04/02/2013
41 |
42 | 20262
43 |
44 | 219526
45 | 271912
46 |
47 | 0
48 |
49 |
50 | 1483
51 | 04/01/2013
52 |
53 |
54 |
55 | 1201
56 | 1883
57 |
58 |
59 |
60 |
61 |
62 | http://www.zillow.com/local-info/CA-San-Diego/North-Park/r_274717/
63 | http://www.zillow.com/north-park-san-diego-ca/fsbo/
64 | http://www.zillow.com/north-park-san-diego-ca/
65 |
66 |
67 |
68 |
69 |
70 | 2126762315
71 |
72 | http://www.zillow.com/homedetails/3651-Louisiana-St-APT-205-San-Diego-CA-92104/2126762315_zpid/
73 | http://www.zillow.com/homes/2126762315_zpid/
74 | http://www.zillow.com/homes/comps/2126762315_zpid/
75 |
76 |
77 | 3651 Louisiana St APT 205
78 | 92104
79 | San Diego
80 | CA
81 | 32.7447
82 | -117.13995
83 |
84 | 6073
85 | Condominium
86 | 1985
87 | 800
88 | 1.5
89 | 2
90 |
91 | 246695
92 | 04/02/2013
93 |
94 | 19294
95 |
96 | 214625
97 | 288633
98 |
99 | 0
100 |
101 |
102 | 1343
103 | 04/01/2013
104 |
105 |
106 |
107 | 1222
108 | 1800
109 |
110 |
111 |
112 |
113 |
114 | http://www.zillow.com/local-info/CA-San-Diego/North-Park/r_274717/
115 | http://www.zillow.com/north-park-san-diego-ca/fsbo/
116 | http://www.zillow.com/north-park-san-diego-ca/
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/lib/rubillow/neighborhood.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Interface for the Neighborhood API.
3 | #
4 | # Read the more about this API at: {http://www.zillow.com/webtools/neighborhood-data/}
5 | class Neighborhood
6 | # Retrieve demographic data for a given region.
7 | #
8 | # Read more at: {http://www.zillow.com/howto/api/GetDemographics.htm}.
9 | #
10 | # \* At least regionid or state/city, city/neighborhood, or zip options are required.
11 | #
12 | # @example
13 | # data = Rubillow::Neighborhood.demographics({ :state => 'WA', :city => 'Seattle', :neighborhood => 'Ballard' })
14 | #
15 | # if data.success?
16 | # puts data.charts['Median Condo Value']
17 | # puts data.affordability_data['Zillow Home Value Index'][:neighborhood]
18 | # data.characteristics.each do |stat|
19 | # stat
20 | # end
21 | # end
22 | #
23 | # @param [Hash] options The options for the API request.
24 | # @option options [String] :regionid The id of the region (required *)
25 | # @option options [String] :state The state of the region (required *)
26 | # @option options [String] :city The city of the region (required *)
27 | # @option options [String] :neighborhood The neighborhood of the region (required *)
28 | # @option options [String] :zip The zopcode of the region (required *)
29 | # @return [Models::Demographics] Region demographics data.
30 | def self.demographics(options = {})
31 | options = {
32 | :zws_id => Rubillow.configuration.zwsid,
33 | :regionid => nil,
34 | :state => nil,
35 | :city => nil,
36 | :neighborhood => nil,
37 | :zip => nil,
38 | }.merge!(options)
39 |
40 | if options[:regionid].nil? && options[:zip].nil? && (options[:state].nil? || options[:city].nil?) && (options[:city].nil? || options[:neighborhood].nil?)
41 | raise ArgumentError, "The regionid, state and city, city and neighborhood or zip option is required"
42 | end
43 |
44 | Models::Demographics.new(Rubillow::Request.get("GetDemographics", options))
45 | end
46 |
47 | # Retrieve sub-regions for a region
48 | #
49 | # Read more at: {http://www.zillow.com/howto/api/GetRegionChildren.htm}.
50 | #
51 | # \* At least regionid or state options are required.
52 | #
53 | # @example
54 | # data = Rubillow::Neighborhood.region_children({ :state => 'WA', :city => 'Seattle', :childtype => 'neighborhood' })
55 | #
56 | # if data.success?
57 | # puts data.region.id # "16037"
58 | # puts data.region.latitude # "47.559364"
59 | # data.regions.each do |region|
60 | # puts region.id
61 | # end
62 | # end
63 | #
64 | # @param [Hash] options The options for the API request.
65 | # @option options [String] :regionid The id of the region (required *)
66 | # @option options [String] :state The state of the region (required *)
67 | # @option options [String] :county The county of the region
68 | # @option options [String] :city The city of the region
69 | # @option options [String] :childType The type of regions to retrieve (available types: +state+, +county+, +city+, +zipcode+, and +neighborhood+)
70 | # @return [Models::RegionChildren] Region children list.
71 | def self.region_children(options = {})
72 | options = {
73 | :zws_id => Rubillow.configuration.zwsid,
74 | :regionid => nil,
75 | :state => nil,
76 | :county => nil,
77 | :city => nil,
78 | :childtype => nil,
79 | }.merge!(options)
80 |
81 | if options[:regionid].nil? && options[:state].nil?
82 | raise ArgumentError, "The regionid or state option is required"
83 | end
84 |
85 | Models::RegionChildren.new(Rubillow::Request.get("GetRegionChildren", options))
86 | end
87 |
88 | # Retrieve a chart for the specified region.
89 | #
90 | # Read more at: {http://www.zillow.com/howto/api/GetRegionChart.htm}.
91 | #
92 | # @example
93 | # chart = Rubillow::Neighborhood.chart({ :city => 'Seattle', :state => 'WA', :unit_type => 'percent', :width => 300, :height => 150 })
94 | #
95 | # if chart.success?
96 | # puts chart.to_html
97 | # end
98 | #
99 | # @param [Hash] options The options for the API request.
100 | # @option options [String] :city The city of the region
101 | # @option options [String] :state The state of the region
102 | # @option options [String] :neighborhood The county of the region
103 | # @option options [String] :zip The zipcode of the region
104 | # @option options [String] :unit-type Show the percent change (+percent+), or dollar change (+dollar+). (required)
105 | # @option options [Integer] :width The width of the image; between 200 and 600, inclusive.
106 | # @option options [Integer] :height The height of the image; between 100 and 300, inclusive.
107 | # @option options [Integer] :chartDuration The duration of past data to show. Valid values are +1year+, +5years+ and +10years+. Defaults to +1year+.
108 | # @return [Models::RegionChart] Region chart.
109 | def self.region_chart(options = {})
110 | options = {
111 | :zws_id => Rubillow.configuration.zwsid,
112 | :city => nil,
113 | :state => nil,
114 | :neighborhood => nil,
115 | :zip => nil,
116 | :unit_type => nil,
117 | :width => nil,
118 | :height => nil,
119 | :chartDuration => nil,
120 | }.merge!(options)
121 |
122 | if options[:unit_type].nil?
123 | raise ArgumentError, "The unit_type option is required"
124 | end
125 |
126 | Models::RegionChart.new(Rubillow::Request.get("GetRegionChart", options))
127 | end
128 | end
129 | end
--------------------------------------------------------------------------------
/spec/rubillow/models/search_result_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Rubillow::Models::SearchResult do
4 | it "populates the results from GetSearchResults" do
5 | data = Rubillow::Models::SearchResult.new(get_xml('get_search_results.xml'))
6 |
7 | data.zpid.should == '48749425'
8 | data.links.count.should == 5
9 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
10 | data.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
11 | data.links[:mapthishome].should == "http://www.zillow.com/homes/48749425_zpid/"
12 | data.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48749425"
13 | data.links[:comparables].should == "http://www.zillow.com/homes/comps/48749425_zpid/"
14 | data.address[:street].should == "2114 Bigelow Ave N"
15 | data.address[:city].should == "Seattle"
16 | data.address[:state].should == "WA"
17 | data.address[:zipcode].should == "98109"
18 | data.address[:latitude].should == "47.637933"
19 | data.address[:longitude].should == "-122.347938"
20 | data.full_address.should == "2114 Bigelow Ave N, Seattle, WA 98109"
21 | data.price.should == "1032000"
22 | data.percentile.should == "0"
23 | data.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
24 | data.valuation_range[:low].should == "866880"
25 | data.valuation_range[:high].should == "1259040"
26 | data.change.should == "5900"
27 | data.change_duration.should == "30"
28 | data.local_real_estate.count.should == 3
29 | data.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/"
30 | data.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/"
31 | data.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/"
32 | data.region.should == "East Queen Anne"
33 | data.region_type.should == "neighborhood"
34 | data.region_id.should == "271856"
35 | end
36 |
37 | it "populates the results from GetZestimate" do
38 | data = Rubillow::Models::SearchResult.new(get_xml('get_zestimate.xml'))
39 |
40 | data.zpid.should == '48749425'
41 | data.links.count.should == 5
42 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/"
43 | data.links[:graphsanddata].should == "http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data"
44 | data.links[:mapthishome].should == "http://www.zillow.com/homes/48749425_zpid/"
45 | data.links[:myestimator].should == "http://www.zillow.com/myestimator/Edit.htm?zprop=48749425"
46 | data.links[:comparables].should == "http://www.zillow.com/homes/comps/48749425_zpid/"
47 | data.address[:street].should == "2114 Bigelow Ave N"
48 | data.address[:city].should == "Seattle"
49 | data.address[:state].should == "WA"
50 | data.address[:zipcode].should == "98109"
51 | data.address[:latitude].should == "47.637933"
52 | data.address[:longitude].should == "-122.347938"
53 | data.price.should == "1032000"
54 | data.percentile.should == "95"
55 | data.last_updated.strftime("%m/%d/%Y").should == "08/24/2011"
56 | data.valuation_range[:low].should == "866880"
57 | data.valuation_range[:high].should == "1259040"
58 | data.change.should == "5900"
59 | data.change_duration.should == "30"
60 | data.local_real_estate.count.should == 3
61 | data.local_real_estate[:overview].should == "http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/"
62 | data.local_real_estate[:for_sale_by_owner].should == "http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/"
63 | data.local_real_estate[:for_sale].should == "http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/"
64 | data.region.should == "East Queen Anne"
65 | data.region_type.should == "neighborhood"
66 | data.region_id.should == "271856"
67 | data.rent_zestimate[:price].should == "3379"
68 | data.rent_zestimate[:last_updated].should == "12/17/2012"
69 | data.rent_zestimate[:value_change].should == "107"
70 | data.rent_zestimate[:value_duration].should == "30"
71 | data.rent_zestimate[:valuation_range][:low].should == "2154"
72 | data.rent_zestimate[:valuation_range][:high].should == "5102"
73 | end
74 |
75 | it "populates the results from GetZestimate with missing region data" do
76 | data = Rubillow::Models::SearchResult.new(get_xml('get_zestimate_missing_region.xml'))
77 |
78 | data.zpid.should == '78264249'
79 | data.links.count.should == 4
80 | data.links[:homedetails].should == "http://www.zillow.com/homedetails/8663-Orchard-Loop-Rd-NE-Leland-NC-28451/78264249_zpid/"
81 | data.links[:graphsanddata].should == "http://www.zillow.com/homedetails/8663-Orchard-Loop-Rd-NE-Leland-NC-28451/78264249_zpid/#charts-and-data"
82 | data.links[:mapthishome].should == "http://www.zillow.com/homes/78264249_zpid/"
83 | data.links[:comparables].should == "http://www.zillow.com/homes/comps/78264249_zpid/"
84 | data.address[:street].should == "8663 Orchard Loop Rd NE"
85 | data.address[:city].should == "Leland"
86 | data.address[:state].should == "NC"
87 | data.address[:zipcode].should == "28451"
88 | data.address[:latitude].should == "34.217408"
89 | data.address[:longitude].should == "-78.054412"
90 | data.price.should == "136518"
91 | data.percentile.should == "58"
92 | data.last_updated.strftime("%m/%d/%Y").should == "12/27/2012"
93 | data.valuation_range[:low].should == "23208"
94 | data.valuation_range[:high].should == "203412"
95 | data.change.should == "-1299"
96 | data.change_duration.should == "30"
97 | data.local_real_estate.should == nil
98 | data.region.should == nil
99 | end
100 |
101 | it "populates the results from GetZestimate with missing valueDuration data" do
102 | data = Rubillow::Models::SearchResult.new(get_xml('get_zestimate_missing_value_duration.xml'))
103 |
104 | data.zpid.should == '29366758'
105 | data.rent_zestimate.should be_a Hash
106 | data.rent_zestimate[:value_duration].should be == '30'
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/lib/rubillow/home_valuation.rb:
--------------------------------------------------------------------------------
1 | module Rubillow
2 | # Interface for the Home Valuation API.
3 | #
4 | # Read the more about this API at: {http://www.zillow.com/howto/api/HomeValuationAPIOverview.htm}
5 | class HomeValuation
6 | # Retrieve a property by the specified address.
7 | #
8 | # Read more at: {http://www.zillow.com/howto/api/GetSearchResults.htm}.
9 | #
10 | # @example
11 | # data = Rubillow::HomeValuation.search_results({ :address => '2114 Bigelow Ave', :citystatezip => 'Seattle, WA' })
12 | #
13 | # if data.success?
14 | # puts data.zpid # "48749425"
15 | # puts data.price # "1032000"
16 | # end
17 | #
18 | # @param [Hash] options The options for the API request.
19 | # @option options [String] :address The address of the property to search. (required)
20 | # @option options [String] :citystatezip The city+state combination and/or ZIP code for which to search. Note that giving both city and state is required. Using just one will not work. (required)
21 | # @option options [Boolean] :rentzestimate Return Rent Zestimate information if available. Default: false
22 | # @return [Models::SearchResult] Property information.
23 | def self.search_results(options = {})
24 | options = {
25 | :zws_id => Rubillow.configuration.zwsid,
26 | :address => nil,
27 | :citystatezip => nil,
28 | :rentzestimate => false,
29 | }.merge!(options)
30 |
31 | if options[:address].nil?
32 | raise ArgumentError, "The address option is required"
33 | end
34 | if options[:citystatezip].nil?
35 | raise ArgumentError, "The citystatezip option is required"
36 | end
37 |
38 | Models::SearchResult.new(Rubillow::Request.get("GetSearchResults", options))
39 | end
40 |
41 | # Retrieve a zestimate for the specified property.
42 | #
43 | # Read more at: {http://www.zillow.com/howto/api/GetZestimate.htm}.
44 | #
45 | # @example
46 | # data = Rubillow::HomeValuation.zestimate({ :zpid => '48749425' })
47 | #
48 | # if data.success?
49 | # puts data.zpid # "48749425"
50 | # puts data.price # "1032000"
51 | # puts data.value_change # "5900"
52 | # end
53 | #
54 | # @param [Hash] options The options for the API request.
55 | # @option options [Integer] :zpid The Zillow Property ID of the property. (required)
56 | # @option options [Boolean] :rentzestimate Return Rent Zestimate information if available. Default: false
57 | # @return [Models::SearchResult] Property pricing information.
58 | def self.zestimate(options = {})
59 | options = {
60 | :zws_id => Rubillow.configuration.zwsid,
61 | :zpid => nil,
62 | :rentzestimate => false,
63 | }.merge!(options)
64 |
65 | if options[:zpid].nil?
66 | raise ArgumentError, "The zpid option is required"
67 | end
68 |
69 | Models::SearchResult.new(Rubillow::Request.get("GetZestimate", options))
70 | end
71 |
72 | # Retrieve a chart for the specified property.
73 | #
74 | # Read more at: {http://www.zillow.com/howto/api/GetChart.htm}.
75 | #
76 | # @example
77 | # chart = Rubillow::HomeValuation.chart({ :zpid => '48749425', :height => '300', :width => '150' })
78 | #
79 | # if chart.success?
80 | # puts chart.to_html
81 | # end
82 | #
83 | # @param [Hash] options The options for the API request.
84 | # @option options [Integer] :zpid The Zillow Property ID of the property. (required)
85 | # @option options [String] :unit-type Show the percent change ("percent"), or dollar change ("dollar"). (required)
86 | # @option options [Integer] :width The width of the image; between 200 and 600, inclusive.
87 | # @option options [Integer] :height The height of the image; between 100 and 300, inclusive.
88 | # @option options [Integer] :chartDuration The duration of past data to show. Valid values are "1year", "5years" and "10years". If unspecified, the value defaults to "1year".
89 | # @return [Models::PropertyChart] Property chart.
90 | def self.chart(options = {})
91 | options = {
92 | :zws_id => Rubillow.configuration.zwsid,
93 | :zpid => nil,
94 | :unit_type => nil,
95 | :width => nil,
96 | :height => nil,
97 | :chartDuration => nil,
98 | }.merge!(options)
99 |
100 | if options[:zpid].nil?
101 | raise ArgumentError, "The zpid option is required"
102 | end
103 | if options[:unit_type].nil?
104 | raise ArgumentError, "The unit_type option is required"
105 | end
106 |
107 | Models::PropertyChart.new(Rubillow::Request.get("GetChart", options))
108 | end
109 |
110 | # Retrieve a list of comps for the specified property.
111 | #
112 | # Read more at: {http://www.zillow.com/howto/api/GetComps.htm}.
113 | #
114 | # @example
115 | # data = Rubillow::HomeValuation.comps({ :zpid => '48749425', :count => 5 })
116 | #
117 | # if data.success?
118 | # puts data.principal.price # "1032000"
119 | # data.comparables.each do |comp|
120 | # puts comparables.price
121 | # end
122 | # end
123 | #
124 | # @param [Hash] options The options for the API request.
125 | # @option options [Integer] :zpid The Zillow Property ID of the property. (required)
126 | # @option options [Integer] :count The number of comps to return, between 1 and 25 inclusive. (required)
127 | # @option options [Boolean] :rentzestimate Return Rent Zestimate information if available. Default: false
128 | # @return [Models::Comps] Comps Property information and comps list.
129 | def self.comps(options = {})
130 | options = {
131 | :zws_id => Rubillow.configuration.zwsid,
132 | :zpid => nil,
133 | :count => nil,
134 | :rentzestimate => false,
135 | }.merge!(options)
136 |
137 | if options[:zpid].nil?
138 | raise ArgumentError, "The zpid option is required"
139 | end
140 | if options[:count].nil?
141 | raise ArgumentError, "The count option is required"
142 | end
143 |
144 | Models::Comps.new(Rubillow::Request.get("GetComps", options))
145 | end
146 | end
147 | end
--------------------------------------------------------------------------------
/spec/xml/get_comps.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 48749425
5 | 5
6 |
7 |
8 | Request successfully processed
9 | 0
10 |
11 |
12 |
13 |
14 | 48749425
15 |
16 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
17 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
18 | http://www.zillow.com/homes/48749425_zpid/
19 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
20 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
21 | http://www.zillow.com/homes/comps/48749425_zpid/
22 |
23 |
24 | 2114 Bigelow Ave N
25 | 98109
26 | Seattle
27 | WA
28 | 47.637933
29 | -122.347938
30 |
31 |
32 | 1032000
33 | 08/24/2011
34 |
35 | 5900
36 |
37 | 866880
38 | 1259040
39 |
40 | 95
41 |
42 |
43 |
44 |
45 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
46 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
47 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
48 |
49 |
50 |
51 |
52 |
53 |
54 | 48768095
55 |
56 | http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/
57 | http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/#charts-and-data
58 | http://www.zillow.com/homes/48768095_zpid/
59 | http://www.zillow.com/myestimator/Edit.htm?zprop=48768095
60 | http://www.zillow.com/myestimator/Edit.htm?zprop=48768095
61 | http://www.zillow.com/homes/comps/48768095_zpid/
62 |
63 |
64 | 2538 Mayfair Ave N
65 | 98109
66 | Seattle
67 | WA
68 | 47.642566
69 | -122.352512
70 |
71 |
72 | 584400
73 | 08/24/2011
74 |
75 | -100
76 |
77 | 449988
78 | 625308
79 |
80 | 75
81 |
82 |
83 |
84 |
85 | http://www.zillow.com/local-info/WA-Seattle/North-Queen-Anne/r_271942/
86 | http://www.zillow.com/homes/fsbo/North-Queen-Anne-Seattle-WA/
87 | http://www.zillow.com/homes/for_sale/North-Queen-Anne-Seattle-WA/
88 |
89 |
90 |
91 |
92 |
93 | 48690326
94 |
95 | http://www.zillow.com/homedetails/1612-Warren-Ave-N-Seattle-WA-98109/48690326_zpid/
96 | http://www.zillow.com/homedetails/1612-Warren-Ave-N-Seattle-WA-98109/48690326_zpid/#charts-and-data
97 | http://www.zillow.com/homes/48690326_zpid/
98 | http://www.zillow.com/myestimator/Edit.htm?zprop=48690326
99 | http://www.zillow.com/myestimator/Edit.htm?zprop=48690326
100 | http://www.zillow.com/homes/comps/48690326_zpid/
101 |
102 |
103 | 1612 Warren Ave N
104 | 98109
105 | Seattle
106 | WA
107 | 47.633573
108 | -122.354064
109 |
110 |
111 | 1167500
112 | 08/24/2011
113 |
114 | 85400
115 |
116 | 898975
117 | 1482725
118 |
119 | 97
120 |
121 |
122 |
123 |
124 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
125 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
126 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
127 |
128 |
129 |
130 |
131 |
132 | 48689869
133 |
134 | http://www.zillow.com/homedetails/1606-Nob-Hill-Ave-N-Seattle-WA-98109/48689869_zpid/
135 | http://www.zillow.com/homedetails/1606-Nob-Hill-Ave-N-Seattle-WA-98109/48689869_zpid/#charts-and-data
136 | http://www.zillow.com/homes/48689869_zpid/
137 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689869
138 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689869
139 | http://www.zillow.com/homes/comps/48689869_zpid/
140 |
141 |
142 | 1606 Nob Hill Ave N
143 | 98109
144 | Seattle
145 | WA
146 | 47.633416
147 | -122.350494
148 |
149 |
150 | 750100
151 | 08/24/2011
152 |
153 | 2700
154 |
155 | 652587
156 | 877617
157 |
158 | 87
159 |
160 |
161 |
162 |
163 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
164 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
165 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
166 |
167 |
168 |
169 |
170 |
171 | 48689978
172 |
173 | http://www.zillow.com/homedetails/352-Blaine-St-Seattle-WA-98109/48689978_zpid/
174 | http://www.zillow.com/homedetails/352-Blaine-St-Seattle-WA-98109/48689978_zpid/#charts-and-data
175 | http://www.zillow.com/homes/48689978_zpid/
176 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689978
177 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689978
178 | http://www.zillow.com/homes/comps/48689978_zpid/
179 |
180 |
181 | 352 Blaine St
182 | 98109
183 | Seattle
184 | WA
185 | 47.635005
186 | -122.350517
187 |
188 |
189 | 760000
190 | 08/24/2011
191 |
192 | -400
193 |
194 | 661200
195 | 881600
196 |
197 | 88
198 |
199 |
200 |
201 |
202 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
203 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
204 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
205 |
206 |
207 |
208 |
209 |
210 | 48690151
211 |
212 | http://www.zillow.com/homedetails/161-Crockett-St-Seattle-WA-98109/48690151_zpid/
213 | http://www.zillow.com/homedetails/161-Crockett-St-Seattle-WA-98109/48690151_zpid/#charts-and-data
214 | http://www.zillow.com/homes/48690151_zpid/
215 | http://www.zillow.com/myestimator/Edit.htm?zprop=48690151
216 | http://www.zillow.com/myestimator/Edit.htm?zprop=48690151
217 | http://www.zillow.com/homes/comps/48690151_zpid/
218 |
219 |
220 | 161 Crockett St
221 | 98109
222 | Seattle
223 | WA
224 | 47.637268
225 | -122.353885
226 |
227 |
228 | 522600
229 | 08/24/2011
230 |
231 | 7900
232 |
233 | 433758
234 | 627120
235 |
236 | 69
237 |
238 |
239 |
240 |
241 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
242 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
243 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
--------------------------------------------------------------------------------
/spec/xml/get_deep_comps.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 48749425
5 | 5
6 |
7 |
8 | Request successfully processed
9 | 0
10 |
11 |
12 |
13 |
14 | 48749425
15 |
16 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/
17 | http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/#charts-and-data
18 | http://www.zillow.com/homes/48749425_zpid/
19 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
20 | http://www.zillow.com/myestimator/Edit.htm?zprop=48749425
21 | http://www.zillow.com/homes/comps/48749425_zpid/
22 |
23 |
24 | 2114 Bigelow Ave N
25 | 98109
26 | Seattle
27 | WA
28 | 47.637933
29 | -122.347938
30 |
31 | 2010
32 | 872000.0
33 | 1924
34 | 4680
35 | 3470
36 | 3.0
37 | 4
38 | 11/26/2008
39 | 1025000
40 |
41 | 1032000
42 | 08/24/2011
43 |
44 | 5900
45 |
46 | 866880
47 | 1259040
48 |
49 | 95
50 |
51 |
52 |
53 |
54 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
55 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
56 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
57 |
58 |
59 |
60 |
61 |
62 |
63 | 48768095
64 |
65 | http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/
66 | http://www.zillow.com/homedetails/2538-Mayfair-Ave-N-Seattle-WA-98109/48768095_zpid/#charts-and-data
67 | http://www.zillow.com/homes/48768095_zpid/
68 | http://www.zillow.com/myestimator/Edit.htm?zprop=48768095
69 | http://www.zillow.com/myestimator/Edit.htm?zprop=48768095
70 | http://www.zillow.com/homes/comps/48768095_zpid/
71 |
72 |
73 | 2538 Mayfair Ave N
74 | 98109
75 | Seattle
76 | WA
77 | 47.642566
78 | -122.352512
79 |
80 | 2010
81 | 431000.0
82 | 1955
83 | 16514
84 | 2600
85 | 2.75
86 | 4
87 | 8
88 | 07/27/2011
89 | 545000
90 |
91 | 584400
92 | 08/24/2011
93 |
94 | -100
95 |
96 | 449988
97 | 625308
98 |
99 | 75
100 |
101 |
102 |
103 |
104 | http://www.zillow.com/local-info/WA-Seattle/North-Queen-Anne/r_271942/
105 | http://www.zillow.com/homes/fsbo/North-Queen-Anne-Seattle-WA/
106 | http://www.zillow.com/homes/for_sale/North-Queen-Anne-Seattle-WA/
107 |
108 |
109 |
110 |
111 |
112 | 48810502
113 |
114 | http://www.zillow.com/homedetails/555-Prospect-St-APT-4-Seattle-WA-98109/48810502_zpid/
115 | http://www.zillow.com/homedetails/555-Prospect-St-APT-4-Seattle-WA-98109/48810502_zpid/#charts-and-data
116 | http://www.zillow.com/homes/48810502_zpid/
117 | http://www.zillow.com/myestimator/Edit.htm?zprop=48810502
118 | http://www.zillow.com/myestimator/Edit.htm?zprop=48810502
119 | http://www.zillow.com/homes/comps/48810502_zpid/
120 |
121 |
122 | 555 Prospect St APT 4
123 | 98109
124 | Seattle
125 | WA
126 | 47.628775
127 | -122.345839
128 |
129 | 2010
130 | 288000.0
131 | 1979
132 | 5762
133 | 1237
134 | 2.0
135 | 2
136 | 07/19/2011
137 | 295000
138 |
139 | 301500
140 | 08/24/2011
141 |
142 | -1700
143 |
144 | 238185
145 | 340695
146 |
147 | 34
148 |
149 |
150 |
151 |
152 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
153 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
154 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
155 |
156 |
157 |
158 |
159 |
160 | 49099112
161 |
162 | http://www.zillow.com/homedetails/2500-6th-Ave-N-APT-2-Seattle-WA-98109/49099112_zpid/
163 | http://www.zillow.com/homedetails/2500-6th-Ave-N-APT-2-Seattle-WA-98109/49099112_zpid/#charts-and-data
164 | http://www.zillow.com/homes/49099112_zpid/
165 | http://www.zillow.com/myestimator/Edit.htm?zprop=49099112
166 | http://www.zillow.com/myestimator/Edit.htm?zprop=49099112
167 | http://www.zillow.com/homes/comps/49099112_zpid/
168 |
169 |
170 | 2500 6th Ave N APT 2
171 | 98109
172 | Seattle
173 | WA
174 | 47.642435
175 | -122.345685
176 |
177 | 2010
178 | 821000.0
179 | 1965
180 | 7646
181 | 2000
182 | 1.5
183 | 2
184 | 08/10/2011
185 | 695000
186 |
187 | 735300
188 | 08/24/2011
189 |
190 | 17800
191 |
192 | 522063
193 | 904419
194 |
195 | 87
196 |
197 |
198 |
199 |
200 | http://www.zillow.com/local-info/WA-Seattle/Westlake/r_272022/
201 | http://www.zillow.com/homes/fsbo/Westlake-Seattle-WA/
202 | http://www.zillow.com/homes/for_sale/Westlake-Seattle-WA/
203 |
204 |
205 |
206 |
207 |
208 | 48689869
209 |
210 | http://www.zillow.com/homedetails/1606-Nob-Hill-Ave-N-Seattle-WA-98109/48689869_zpid/
211 | http://www.zillow.com/homedetails/1606-Nob-Hill-Ave-N-Seattle-WA-98109/48689869_zpid/#charts-and-data
212 | http://www.zillow.com/homes/48689869_zpid/
213 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689869
214 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689869
215 | http://www.zillow.com/homes/comps/48689869_zpid/
216 |
217 |
218 | 1606 Nob Hill Ave N
219 | 98109
220 | Seattle
221 | WA
222 | 47.633416
223 | -122.350494
224 |
225 | 2010
226 | 556000.0
227 | 1924
228 | 3200
229 | 2570
230 | 2.0
231 | 3
232 | 04/29/2011
233 | 740000
234 |
235 | 750100
236 | 08/24/2011
237 |
238 | 2700
239 |
240 | 652587
241 | 877617
242 |
243 | 87
244 |
245 |
246 |
247 |
248 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
249 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
250 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
251 |
252 |
253 |
254 |
255 |
256 | 48689978
257 |
258 | http://www.zillow.com/homedetails/352-Blaine-St-Seattle-WA-98109/48689978_zpid/
259 | http://www.zillow.com/homedetails/352-Blaine-St-Seattle-WA-98109/48689978_zpid/#charts-and-data
260 | http://www.zillow.com/homes/48689978_zpid/
261 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689978
262 | http://www.zillow.com/myestimator/Edit.htm?zprop=48689978
263 | http://www.zillow.com/homes/comps/48689978_zpid/
264 |
265 |
266 | 352 Blaine St
267 | 98109
268 | Seattle
269 | WA
270 | 47.635005
271 | -122.350517
272 |
273 | 2010
274 | 572000.0
275 | 1922
276 | 3000
277 | 2670
278 | 2.0
279 | 4
280 | 10/26/2010
281 | 815000
282 |
283 | 760000
284 | 08/24/2011
285 |
286 | -400
287 |
288 | 661200
289 | 881600
290 |
291 | 88
292 |
293 |
294 |
295 |
296 | http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/
297 | http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/
298 | http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
--------------------------------------------------------------------------------
/spec/xml/get_demographics.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WA
5 | Seattle
6 | Ballard
7 |
8 |
9 | Request successfully processed
10 | 0
11 |
12 |
13 |
14 | 250017
15 | Washington
16 | Seattle
17 | Ballard
18 | 47.668329
19 | -122.384536
20 | http://www.zillow.com/mortgage-rates/wa/seattle/
21 |
22 |
23 | http://www.zillow.com/homes/Ballard-Seattle-WA/
24 | http://www.zillow.com/local-info/WA-Seattle/Ballard-home-value/r_250017/
25 | http://www.zillow.com/local-info/WA-Seattle/Ballard-homes/r_250017/
26 | http://www.zillow.com/local-info/WA-Seattle/Ballard-people/r_250017/
27 | http://www.zillow.com/homes/for_sale/Ballard-Seattle-WA/
28 | http://www.zillow.com/homes/fsbo/Ballard-Seattle-WA/
29 | http://www.zillow.com/homes/for_sale/Ballard-Seattle-WA/fore_lt/0_mmm/
30 | http://www.zillow.com/homes/recently_sold/Ballard-Seattle-WA/
31 |
32 |
33 |
34 | Median Condo Value
35 | http://www.zillow.com/app?chartType=affordability_avgCondoValue&graphType=barChart®ionId=250017®ionType=8&service=chart
36 |
37 |
38 | Median Home Value
39 | http://www.zillow.com/app?chartType=affordability_avgHomeValue&graphType=barChart®ionId=250017®ionType=8&service=chart
40 |
41 |
42 | Dollars Per Square Feet
43 | http://www.zillow.com/app?chartType=affordability_pricePerSqft&graphType=barChart®ionId=250017®ionType=8&service=chart
44 |
45 |
46 | Zillow Home Value Index Distribution
47 | http://www.zillow.com/app?chartType=affordability_ZindexByDistribution&graphType=barChart®ionId=250017®ionType=8&service=chart
48 |
49 |
50 | Home Type
51 | http://www.zillow.com/app?chartType=home_homeType&graphType=barChart®ionId=250017®ionType=8&service=chart
52 |
53 |
54 | Owners vs. Renters
55 | http://www.zillow.com/app?chartType=home_ownVsRent&graphType=barChart®ionId=250017®ionType=8&service=chart
56 |
57 |
58 | Home Size in Square Feet
59 | http://www.zillow.com/app?chartType=home_homeSize&graphType=barChart®ionId=250017®ionType=8&service=chart
60 |
61 |
62 | Year Built
63 | http://www.zillow.com/app?chartType=home_yearBuilt&graphType=barChart®ionId=250017®ionType=8&service=chart
64 |
65 |
66 |
67 |
68 |
69 | Affordability
70 |
71 |
72 | Affordability Data
73 |
74 |
75 | Zillow Home Value Index
76 |
77 |
78 | 305200
79 |
80 |
81 | 348800
82 |
83 |
84 | 171600
85 |
86 |
87 |
88 |
89 | Median Single Family Home Value
90 |
91 |
92 | 365200
93 |
94 |
95 | 377000
96 |
97 |
98 | 173500
99 |
100 |
101 |
102 |
103 | Median Condo Value
104 |
105 |
106 | 271700
107 |
108 |
109 | 266900
110 |
111 |
112 | 155800
113 |
114 |
115 |
116 |
117 | Median 2-Bedroom Home Value
118 |
119 |
120 | 316200
121 |
122 |
123 | 305700
124 |
125 |
126 | 127700
127 |
128 |
129 |
130 |
131 | Median 3-Bedroom Home Value
132 |
133 |
134 | 368000
135 |
136 |
137 | 377900
138 |
139 |
140 | 156400
141 |
142 |
143 |
144 |
145 | Median 4-Bedroom Home Value
146 |
147 |
148 | 413600
149 |
150 |
151 | 443100
152 |
153 |
154 | 246600
155 |
156 |
157 |
158 |
159 | Percent Homes Decreasing
160 |
161 |
162 | 0.802
163 |
164 |
165 | 0.778
166 |
167 |
168 | 0.734
169 |
170 |
171 |
172 |
173 | Percent Listing Price Reduction
174 |
175 |
176 | 0.291
177 |
178 |
179 | 0.372
180 |
181 |
182 | 0.308
183 |
184 |
185 |
186 |
187 | Median List Price Per Sq Ft
188 |
189 |
190 | 280
191 |
192 |
193 | 247
194 |
195 |
196 | 103
197 |
198 |
199 |
200 |
201 | Median List Price
202 |
203 |
204 | 300000
205 |
206 |
207 | 359000
208 |
209 |
210 | 189900
211 |
212 |
213 |
214 |
215 | Median Sale Price
216 |
217 |
218 | 335000
219 |
220 |
221 | 383600
222 |
223 |
224 | 191700
225 |
226 |
227 |
228 |
229 | Homes For Sale
230 |
231 |
232 |
233 | Homes Recently Sold
234 |
235 |
236 | 12
237 |
238 |
239 | 876
240 |
241 |
242 | 341521
243 |
244 |
245 |
246 |
247 | Property Tax
248 |
249 |
250 | 2780
251 |
252 |
253 | 3569
254 |
255 |
256 | 2117
257 |
258 |
259 |
260 |
261 | Turnover (Sold Within Last Yr.)
262 |
263 |
264 | 0.054
265 |
266 |
267 | 0.042
268 |
269 |
270 | 0.034
271 |
272 |
273 |
274 |
275 | Median Value Per Sq Ft
276 |
277 |
278 | 311
279 |
280 |
281 | 250
282 |
283 |
284 | 104
285 |
286 |
287 |
288 |
289 | 1-Yr. Change
290 |
291 |
292 | -0.054
293 |
294 |
295 | -0.044
296 |
297 |
298 | -0.062
299 |
300 |
301 |
302 |
303 | >Homes For Sale By Owner
304 |
305 |
306 | 0
307 |
308 |
309 | 47
310 |
311 |
312 | 33971
313 |
314 |
315 |
316 |
317 | >New Construction
318 |
319 |
320 | 0
321 |
322 |
323 | 0
324 |
325 |
326 | 30794
327 |
328 |
329 |
330 |
331 | >Foreclosures
332 |
333 |
334 | 15
335 |
336 |
337 | 814
338 |
339 |
340 | 593884
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 | Homes & Real Estate
350 |
351 |
352 | Homes & Real Estate Data
353 |
354 |
355 | Owners
356 |
357 |
358 | 0.35028618
359 |
360 |
361 | 0.48412441
362 |
363 |
364 | 0.66268764
365 |
366 |
367 |
368 |
369 | Renters
370 |
371 |
372 | 0.64971382
373 |
374 |
375 | 0.51587559
376 |
377 |
378 | 0.33731236
379 |
380 |
381 |
382 |
383 | Median Home Size (Sq. Ft.)
384 |
385 |
386 | 1180
387 |
388 |
389 | 1540
390 |
391 |
392 |
393 |
394 | Avg. Year Built
395 |
396 |
397 | 1994
398 |
399 |
400 | 1949
401 |
402 |
403 |
404 |
405 | Single-Family Homes
406 |
407 |
408 | 0.1712158808933
409 |
410 |
411 | 0.6496783406203
412 |
413 |
414 |
415 |
416 | Condos
417 |
418 |
419 | 0.6398440269408
420 |
421 |
422 | 0.2483180968364
423 |
424 |
425 |
426 |
427 |
428 |
429 | BuiltYear
430 |
431 |
432 | <1900
433 | 0.0419354838709
434 |
435 |
436 | >2000
437 | 0.4691756272401
438 |
439 |
440 | 1900-1919
441 | 0.1168458781362
442 |
443 |
444 | 1920-1939
445 | 0.04229390681
446 |
447 |
448 | 1940-1959
449 | 0.0537634408602
450 |
451 |
452 | 1960-1979
453 | 0.1121863799283
454 |
455 |
456 | 1980-1999
457 | 0.1637992831541
458 |
459 |
460 |
461 |
462 | Census Summary-HomeSize
463 |
464 |
465 | <1000sqft
466 | 0.3922527265889
467 |
468 |
469 | >3600sqft
470 | 0.0684467845054
471 |
472 |
473 | 1000-1400sqft
474 | 0.2651372696502
475 |
476 |
477 | 1400-1800sqft
478 | 0.1214742384355
479 |
480 |
481 | 1800-2400sqft
482 | 0.0699511094396
483 |
484 |
485 | 2400-3600sqft
486 | 0.0684467845054
487 |
488 |
489 |
490 |
491 | Census Summary-HomeType
492 |
493 |
494 | Condo
495 | 0.6398440269408
496 |
497 |
498 | Other
499 | 1.9343463444890998
500 |
501 |
502 | SingleFamily
503 | 0.1712158808933
504 |
505 |
506 |
507 |
508 | Census Summary-Occupancy
509 |
510 |
511 | Own
512 | 0.35028618
513 |
514 |
515 | Rent
516 | 0.64971382
517 |
518 |
519 |
520 |
521 |
522 |
523 | People
524 |
525 |
526 | People Data
527 |
528 |
529 | Median Household Income
530 |
531 |
532 | 41202.9453206937
533 |
534 |
535 | 45736
536 |
537 |
538 | 44512.0130806292
539 |
540 |
541 |
542 |
543 | Single Males
544 |
545 |
546 | 0.218182040689239
547 |
548 |
549 | 0.230033266826908
550 |
551 |
552 | 0.146462187349365
553 |
554 |
555 |
556 |
557 | Single Females
558 |
559 |
560 | 0.197726979090431
561 |
562 |
563 | 0.187486853578992
564 |
565 |
566 | 0.124578258618535
567 |
568 |
569 |
570 |
571 | Median Age
572 |
573 |
574 | 39
575 |
576 |
577 | 37
578 |
579 |
580 | 36
581 |
582 |
583 |
584 |
585 | Homes With Kids
586 |
587 |
588 | 0.149933859172205
589 |
590 |
591 | 0.181808339938523
592 |
593 |
594 | 0.313623902816284
595 |
596 |
597 |
598 |
599 | Average Household Size
600 |
601 |
602 | 1.82278897942217
603 |
604 |
605 | 2.08
606 |
607 |
608 | 2.58883240001203
609 |
610 |
611 |
612 |
613 | Average Commute Time (Minutes)
614 |
615 |
616 | 26.56776121676753
617 |
618 |
619 | 26.6363786935206
620 |
621 |
622 | 26.375545725891282
623 |
624 |
625 |
626 |
627 |
628 |
629 | Census Summary-AgeDecade
630 |
631 |
632 | >=70s
633 | 0.114872901061
634 |
635 |
636 | 0s
637 | 0.0698273234810158
638 |
639 |
640 | 10s
641 | 0.0614721332267584
642 |
643 |
644 | 20s
645 | 0.210411237406907
646 |
647 |
648 | 30s
649 | 0.222130722421361
650 |
651 |
652 | 40s
653 | 0.159760457231474
654 |
655 |
656 | 50s
657 | 0.100382039995932
658 |
659 |
660 | 60s
661 | 0.0611431851755522
662 |
663 |
664 |
665 |
666 | Census Summary-CommuteTime
667 |
668 |
669 | <10min
670 | 0.116523248268039
671 |
672 |
673 | >=60min
674 | 0.0482377198229543
675 |
676 |
677 | 10-20min
678 | 0.266281330068427
679 |
680 |
681 | 20-30min
682 | 0.255069379257092
683 |
684 |
685 | 30-45min
686 | 0.189151878627933
687 |
688 |
689 | 45-60min
690 | 0.124736443955555
691 |
692 |
693 |
694 |
695 | Census Summary-Household
696 |
697 |
698 | NoKids
699 | 0.850066140827795
700 |
701 |
702 | WithKids
703 | 0.149933859172205
704 |
705 |
706 |
707 |
708 | Census Summary-RelationshipStatus
709 |
710 |
711 | Divorced-Female
712 | 0.0854375513590899
713 |
714 |
715 | Divorced-Male
716 | 0.0602982799519792
717 |
718 |
719 | Married-Female
720 | 0.178297193386233
721 |
722 |
723 | Married-Male
724 | 0.186687382837076
725 |
726 |
727 | Single-Female
728 | 0.197726979090431
729 |
730 |
731 | Single-Male
732 | 0.218182040689239
733 |
734 |
735 | Widowed-Female
736 | 0.0632616593158969
737 |
738 |
739 | Widowed-Male
740 | 0.0101089133700551
741 |
742 |
743 |
744 |
745 |
746 |
747 | Makin' It Singles
748 | Upper-scale urban singles.
749 | Pre-middle-age to middle-age singles with upper-scale incomes. May or may not own their own home. Most have college educations and are employed in mid-management professions.
750 |
751 |
752 | Aspiring Urbanites
753 | Urban singles with moderate income.
754 | Low- to middle-income singles over a wide age range. Some have a college education. They work in a variety of occupations, including some management-level positions.
755 |
756 |
757 | Bright Lights, Big City
758 | Very mobile singles living in the city.
759 | Singles ranging in age from early 20s to mid-40s who have moved to an urban setting. Most rent their apartment or condo. Some have a college education and work in services and the professional sector.
760 |
761 |
762 |
763 |
764 | Bachelor's degrees
765 |
766 |
767 | Females working for non-profits
768 | Self-employed (unincorporated businesses)
769 | Work in arts, design, entertainment, sports, or media occupations
770 | Work in computer or mathematical occupations
771 | Work in office and administrative support occupations
772 |
773 |
774 | Born in the Midwest
775 | Born in the Northeast
776 | Born in the South
777 | Divorced females
778 | Single females
779 | Single males
780 | Widowed females
781 |
782 |
783 | Get to work by bicycle
784 | Get to work by bus
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
--------------------------------------------------------------------------------