This project is dedicated to build a client/API wrapper around the PriceHubble REST API. It follows strictly the version 1 of the API specification. Furthermore this gem allows you to easily interact with common PriceHubble operations like fetching property valuations. At the time of writing it supports not the full API specification, but it should be easy to enhance the client functionality, so feel free to send a pull request.
- Installation
- Usage
- Configuration
- Available environment variables
- Authentication
- Error Handling
- Property Valuations
- Bare Minimum Request Example
- Use the PriceHubble::Valuation representation
- Error Handling
- Advanced Request Examples
- Dossiers
- Configuration
- Development
- Code of Conduct
- Contributing
- Releasing
Installation
Add this line to your application's Gemfile:
gem 'pricehubble'
And then execute:
$ bundle
Or install it yourself as:
$ gem install pricehubble
Usage
Configuration
You can configure the pricehubble gem via an Rails initializer, by environment variables or on demand. Here we show a common Rails initializer example:
PriceHubble.configure do |conf|
# Configure the API authentication credentials
conf.username = 'your-username'
conf.password = 'your-password'
# The base URL of the API (there is no staging/canary
# endpoint we know about)
conf.base_url = 'https://api.pricehubble.com'
# Writes to stdout by default, or use the Rails logger if present
conf.logger = Logger.new(IO::NULL)
# Enable request logging or not
conf.request_logging = true
end
The pricehubble gem comes with sensitive defaults as you can see. For most users an extra configuration, beside the authorization credentials, is not needed.
Available environment variables
The pricehubble gem can be configured hardly with its configuration code block like shown before. Respecting the twelve-factor app concerns, the gem allows you to set almost all configurations (just the relevant ones for runtime) via environment variables. Here comes a list of available configuration options:
- PRICEHUBBLE_USERNAME: The API username to use for authentication.
- PRICEHUBBLE_PASSWORD: The API password to use for authentication.
- PRICEHUBBLE_BASE_URL: The base URL of the API. Defaults to the production one.
Authentication
After configuring the pricehubble gem you can directly fetch an authentication which is valid for two hours. All operational requests require an authentication to be present and unexpired. But now comes the good part: you don't ever need to take care about this, because the gem handles the refreshing of the authentication transparently for you and also takes care of passing the authentication to each request you can do with the gem.
# Fetch the authentication/identity for the first time, subsequent calls
# to this method will return the cached authentication instance until it
# is expired (or near expiration, 5 minutes leeway) then a new
# authentication is fetched transparently
PriceHubble.identity
# => #<PriceHubble::Authentication access_token="...", ...>
# Check the expiration state (transparently always unexpired)
PriceHubble.identity.expired?
# => false
# Get the current authentication expiration date/time
PriceHubble.identity.expires_at
# => 2019-10-17 08:01:23 +0000
Error Handling
The pricehubble gem allows you to decide how to react on errors on a per-request basis. (except the transparent authentication) All request performing methods are shiped in a bang and non-bang variant.
The bang variants (eg. PriceHubble::ValuationRequest#perform!
, mind
the exclamation mark at the end) will raise an child instance of the
PriceHubble::RequestError
or PriceHubble::EntityError
class (see
errors for more details) when errors
occur. This comes in handy on asynchronous jobs which are retried on
exceptions.
The non-bang variants (eg. PriceHubble::ValuationRequest#perform
,
without the exclamation mark) wont raise and just return empty results
(eg. false
or []
). This might me comfortable in complex control
flows or when you do not care if one out of 100 times the data is
missing. But watch out for bad/invalid requests you might mask with
this behaviour.
It's up to you to choose the correct flavor for your usecase.
Property Valuations
The pricehubble gem allows you to request property valuations easily. You can formulate your request elegantly with in-place attribute sets or already built instances the same way. Furthermore the gem ships some sensible defaults which makes it even more easy to get an valuation for a property.
The PriceHubble::ValuationRequest
allows to fetch valuations for one
or more (bulk) properties at once for one or several dates. The upper
limit for this is properties.count * dates.count <= 50
from API
side.
Gotcha! The API allows to fetch property valuations with per-property independent time series. The pricehubble gem does not support this for the sake of ease.
Bare Minimum Request Example
# Fetch the valuations for a single property (sale) for today (defaults).
# This is the bare minimum variant, as a starting point.
valuations = PriceHubble::ValuationRequest.new(
property: {
location: {
address: {
post_code: '22769',
city: 'Hamburg',
street: 'Stresemannstr.',
house_number: '29'
}
},
property_type: { code: :apartment },
building_year: 1990,
living_area: 200
}
).perform!
# => [#<PriceHubble::Valuation ...>]
# Print all relevant valuation attributes
pp valuations.first.attributes.deep_compact
# => {"currency"=>"EUR",
# => "sale_price"=>1283400,
# => "sale_price_range"=>1180800..1386100,
# => "confidence"=>"good",
# => "deal_type"=>"sale",
# => "valuation_date"=>Thu, 17 Oct 2019,
# => "country_code"=>"DE",
# => "property"=>
# => {"location"=>
# => {"address"=>
# => {"post_code"=>"22769",
# => "city"=>"Hamburg",
# => "street"=>"Stresemannstr.",
# => "house_number"=>"29"}},
# => "property_type"=>{"code"=>:apartment},
# => "building_year"=>1990,
# => "living_area"=>200}}
Use the PriceHubble::Valuation representation
The PriceHubble::Valuation
representation ships a lot utilities and helpers to process the data.
Here are some examples:
# Fetch the valuations
valuations = PriceHubble::ValuationRequest.new(...).perform!
# We just want to work on the first valuation
valuation = valuations.first
# Get the deal type dependent value of the property in a generic way.
# (sale price if deal type is sale, and rent gross if deal type is rent)
valuation.value
# => 1283400
# Get the upper and lower value range of the property in a generic
# way. The deal type logic is equal to the
# +PriceHubble::Valuation#value+ method.
valuation.value_range
# => 1180800..1386100
# Query the valuation confidence in an elegant way
valuation.confidence.good?
# => true
# The +PriceHubble::Valuation+ entity is a self contained
# representation of the request and response. This means it includes
# the property, deal type, valuation date, country code, etc it was
# requested for. This makes it easy to process the data because
# everything related is in one place.
valuation.property.property_type.apartment?
# => true
Error Handling
The property valuation API is able to perform bulk operations which
may result in non-bang situations, even when you use the bang variant.
This is quite smart as a single property valuation might not break
others on the very same request. Watch for the
PriceHubble::Valuation#status
hash, when it is not nil, then you got
an error for this valuation.
In case you request just one property valuation, then the API will respond an error which is mapped when you use the bang variants.
begin
# Fetch the valuations for a single property (sale) for today (defaults).
# This is the bare minimum variant, as a starting point.
PriceHubble::ValuationRequest.new(
property: {
location: {
address: {
post_code: '22769',
city: 'Hamburg',
street: 'Stresemannstr.',
house_number: '29'
}
},
property_type: { code: :apartment },
building_year: 2999,
living_area: 200
}
).perform!
rescue PriceHubble::EntityInvalid => e
# => #<PriceHubble::EntityInvalid: buildingYear: ...>
# The error message includes the detailed problem.
e.message
# => "buildingYear: Must be between 1850 and 2022."
end
Advanced Request Examples
Here comes a more complex example of a property valuations request with multiple properties, for several valuations dates.
Gotcha! The API does not allow you to mix and match the deal type per property. Therefore you need to set it once for the whole request.
# Build a property with all relevant information for the valuation. This
# example reflects not the full list of supported fields, [see the API
# specification](https://docs.pricehubble.com/#types-property) for the full
# details.
apartment = PriceHubble::Property.new(
location: {
address: {
post_code: '22769',
city: 'Hamburg',
street: 'Stresemannstr.',
house_number: '29'
}
},
property_type: { code: :apartment },
building_year: 1990,
living_area: 200,
balcony_Area: 30,
floor_number: 5,
has_lift: true,
is_furnished: false,
is_new: false,
renovation_year: 2014,
condition: {
bathrooms: :well_maintained,
kitchen: :well_maintained,
flooring: :well_maintained,
windows: :well_maintained,
masonry: :well_maintained
},
quality: {
bathrooms: :normal,
kitchen: :normal,
flooring: :normal,
windows: :normal,
masonry: :normal
}
)
house = PriceHubble::Property.new(
location: {
address: {
post_code: '22769',
city: 'Hamburg',
street: 'Stresemannstr.',
house_number: '29'
}
},
property_type: { code: :house },
building_year: 1990,
land_area: 100,
living_area: 500,
number_of_floors_in_building: 5
)
# Fetch the property valuations for multiple properties, on multiple dates
request = PriceHubble::ValuationRequest.new(
deal_type: :sale,
properties: [apartment, house],
# The dates order is reflected on the valuations list
valuation_dates: [
1.year.ago,
Date.current,
1.year.from_now
]
)
valuations = request.perform!
# Print the valuations in a simple ASCII table. This is just a
# demonstration of how to use the valuations for a simple usecase.
require 'terminal-table'
table = Terminal::Table.new do |tab|
tab << ['Deal Type', 'Property Type', *request.valuation_dates.map(&:year)]
tab << :separator
# Group the valuations by the property they represent
valuations.group_by(&:property).each do |property, valuations|
tab << [request.deal_type, property.property_type.code,
*valuations.map { |val| "#{val.value} #{val.currency}" }]
end
end
# => +-----------+---------------+-------------+-------------+-------------+
# => | Deal Type | Property Type | 2018 | 2019 | 2020 |
# => +-----------+---------------+-------------+-------------+-------------+
# => | sale | apartment | 1282100 EUR | 1373100 EUR | 1420100 EUR |
# => | sale | house | 1824800 EUR | 1950900 EUR | 2016000 EUR |
# => +-----------+---------------+-------------+-------------+-------------+
Dossiers
The pricehubble gem allows you to create and delete dossiers for properties. The required property data is equal to the valuation request. Additionally the pricehubble gem allows you to create sharing links (permalinks) which will link to the dossier dashboard application for customers.
Gotcha! The API allows to update and search for dossiers. Additionally the API supports images and logos for dossiers. The pricehubble gem does not support this yet.
# The property to create a dossier for
apartment = {
location: {
address: {
post_code: '22769',
city: 'Hamburg',
street: 'Stresemannstr.',
house_number: '29'
}
},
property_type: { code: :apartment },
building_year: 1990,
living_area: 200
}
dossier = PriceHubble::Dossier.new(
title: 'Customer Dossier for Stresemannstr. 29',
description: 'Best apartment in the city',
deal_type: :sale,
property: apartment,
country_code: 'DE',
asking_sale_price: 600_000 # the minimum price the seller is willing to agree
# valuation_override_sale_price: '', # overwrite the PH value
# valuation_override_rent_net: '', # overwrite the PH value
# valuation_override_rent_gross: '', # overwrite the PH value
# valuation_override_reason_freetext: '' # explain the visitor why
)
# Save the new dossier
pp dossier.save!
pp dossier.id
# => "25de5429-244e-4584-b58e-b0d7428a2377"
# Generate a sharing link for the dossier
pp dossier.link
# => "https://dash.pricehubble.com/shared/dossier/eyJ0eXAiOiJ..."
Development
After checking out the repo, run make install
to install dependencies. Then,
run make test
to run the tests. You can also run make shell-irb
for an
interactive prompt that will allow you to experiment.
Code of Conduct
Everyone interacting in the project codebase, issue tracker, chat rooms and mailing lists is expected to follow the code of conduct.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/hausgold/pricehubble. Make sure that every pull request adds a bullet point to the changelog file with a reference to the actual pull request.
Releasing
The release process of this Gem is fully automated. You just need to open the Github Actions Release Workflow and trigger a new run via the Run workflow button. Insert the new version number (check the changelog first for the latest release) and you're done.