{"id":19206765,"url":"https://github.com/hausgold/pricehubble","last_synced_at":"2025-05-12T17:27:52.198Z","repository":{"id":55847681,"uuid":"215231508","full_name":"hausgold/pricehubble","owner":"hausgold","description":"Ruby client for the PriceHubble REST API","archived":false,"fork":false,"pushed_at":"2025-05-12T08:53:53.000Z","size":156,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-12T10:03:42.495Z","etag":null,"topics":["api","api-client","api-wrapper","gem","oss","pricehubble","ruby","ruby-gem"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/pricehubble","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hausgold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-10-15T07:11:44.000Z","updated_at":"2025-05-12T08:53:56.000Z","dependencies_parsed_at":"2024-07-08T05:26:35.611Z","dependency_job_id":"f05e2727-70ec-4a26-a28d-e1a615a0358c","html_url":"https://github.com/hausgold/pricehubble","commit_stats":{"total_commits":22,"total_committers":3,"mean_commits":7.333333333333333,"dds":0.09090909090909094,"last_synced_commit":"80191cd7a6dd4c26fb95e4d02a137a2c06420ffb"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fpricehubble","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fpricehubble/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fpricehubble/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fpricehubble/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hausgold","download_url":"https://codeload.github.com/hausgold/pricehubble/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253717586,"owners_count":21952514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-client","api-wrapper","gem","oss","pricehubble","ruby","ruby-gem"],"created_at":"2024-11-09T13:16:53.825Z","updated_at":"2025-05-12T17:27:52.169Z","avatar_url":"https://github.com/hausgold.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"![PriceHubble](doc/assets/project.svg)\n\n[![Continuous Integration](https://github.com/hausgold/pricehubble/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/hausgold/pricehubble/actions/workflows/test.yml)\n[![Gem Version](https://badge.fury.io/rb/pricehubble.svg)](https://badge.fury.io/rb/pricehubble)\n[![Test Coverage](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/coverage.svg)](https://knowledge.hausgold.de/coverage)\n[![Test Ratio](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/ratio.svg)](https://knowledge.hausgold.de/coverage)\n[![API docs](https://automate-api.hausgold.de/v1/coverage_reports/pricehubble/documentation.svg)](https://www.rubydoc.info/gems/pricehubble)\n\nThis project is dedicated to build a client/API wrapper around the\n[PriceHubble](https://pricehubble.com) REST API. It follows strictly the\n[version 1 of the API specification](https://docs.pricehubble.com). Furthermore\nthis gem allows you to easily interact with common PriceHubble operations like\nfetching property valuations. At the time of writing it supports not the full\nAPI specification, but it should be easy to enhance the client functionality,\nso feel free to send a pull request.\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Configuration](#configuration)\n    - [Available environment variables](#available-environment-variables)\n  - [Authentication](#authentication)\n  - [Error Handling](#error-handling)\n  - [Property Valuations](#property-valuations)\n    - [Bare Minimum Request Example](#bare-minimum-request-example)\n    - [Use the PriceHubble::Valuation representation](#use-the-pricehubblevaluation-representation)\n    - [Error Handling](#error-handling-1)\n    - [Advanced Request Examples](#advanced-request-examples)\n  - [Dossiers](#dossiers)\n- [Development](#development)\n- [Code of Conduct](#code-of-conduct)\n- [Contributing](#contributing)\n- [Releasing](#releasing)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'pricehubble'\n```\n\nAnd then execute:\n\n```bash\n$ bundle\n```\n\nOr install it yourself as:\n\n```bash\n$ gem install pricehubble\n```\n\n## Usage\n\n### Configuration\n\nYou can configure the pricehubble gem via an Rails initializer, by environment\nvariables or on demand. Here we show a common Rails initializer example:\n\n```ruby\nPriceHubble.configure do |conf|\n  # Configure the API authentication credentials\n  conf.username = 'your-username'\n  conf.password = 'your-password'\n\n  # The base URL of the API (there is no staging/canary\n  # endpoint we know about)\n  conf.base_url = 'https://api.pricehubble.com'\n\n  # Writes to stdout by default, or use the Rails logger if present\n  conf.logger = Logger.new(IO::NULL)\n\n  # Enable request logging or not\n  conf.request_logging = true\nend\n```\n\nThe pricehubble gem comes with sensitive defaults as you can see. For most\nusers an extra configuration, beside the authorization credentials, is not\nneeded.\n\n#### Available environment variables\n\nThe pricehubble gem can be configured hardly with its configuration code block\nlike shown before. Respecting the [twelve-factor app](https://12factor.net/)\nconcerns, the gem allows you to set almost all configurations (just the\nrelevant ones for runtime) via environment variables. Here comes a list of\navailable configuration options:\n\n* **PRICEHUBBLE_USERNAME**: The API username to use for authentication.\n* **PRICEHUBBLE_PASSWORD**: The API password to use for authentication.\n* **PRICEHUBBLE_BASE_URL**: The base URL of the API. Defaults to the production one.\n\n### Authentication\n\nAfter configuring the pricehubble gem you can directly fetch an\nauthentication which is valid for two hours. All operational requests\nrequire an authentication to be present and unexpired. But now comes\nthe good part: you don't ever need to take care about this, because\nthe gem handles the refreshing of the authentication transparently for\nyou and also takes care of passing the authentication to each request\nyou can do with the gem.\n\n```ruby\n# Fetch the authentication/identity for the first time, subsequent calls\n# to this method will return the cached authentication instance until it\n# is expired (or near expiration, 5 minutes leeway) then a new\n# authentication is fetched transparently\nPriceHubble.identity\n# =\u003e #\u003cPriceHubble::Authentication access_token=\"...\", ...\u003e\n\n# Check the expiration state (transparently always unexpired)\nPriceHubble.identity.expired?\n# =\u003e false\n\n# Get the current authentication expiration date/time\nPriceHubble.identity.expires_at\n# =\u003e 2019-10-17 08:01:23 +0000\n```\n\n### Error Handling\n\nThe pricehubble gem allows you to decide how to react on errors on a\nper-request basis. (except the transparent authentication) All request\nperforming methods are shiped in a bang and non-bang variant.\n\nThe bang variants (eg. `PriceHubble::ValuationRequest#perform!`, mind\nthe exclamation mark at the end) will raise an child instance of the\n`PriceHubble::RequestError` or `PriceHubble::EntityError` class ([see\nerrors for more details](lib/pricehubble/errors.rb)) when errors\noccur. This comes in handy on asynchronous jobs which are retried on\nexceptions.\n\nThe non-bang variants (eg. `PriceHubble::ValuationRequest#perform`,\nwithout the exclamation mark) wont raise and just return empty results\n(eg. `false` or `[]`). This might me comfortable in complex control\nflows or when you do not care if one out of 100 times the data is\nmissing. But watch out for bad/invalid requests you might mask with\nthis behaviour.\n\nIt's up to you to choose the correct flavor for your usecase.\n\n### Property Valuations\n\nThe pricehubble gem allows you to request property valuations easily.\nYou can formulate your request elegantly with in-place attribute sets\nor already built instances the same way. Furthermore the gem ships\nsome sensible defaults which makes it even more easy to get an\nvaluation for a property.\n\nThe `PriceHubble::ValuationRequest` allows to fetch valuations for one\nor more (bulk) properties at once for one or several dates. The upper\nlimit for this is `properties.count * dates.count \u003c= 50` from API\nside.\n\n**Gotcha!** The API allows to fetch property valuations with\nper-property independent time series. The pricehubble gem does not\nsupport this for the sake of ease.\n\n#### Bare Minimum Request Example\n\n```ruby\n# Fetch the valuations for a single property (sale) for today (defaults).\n# This is the bare minimum variant, as a starting point.\nvaluations = PriceHubble::ValuationRequest.new(\n  property: {\n    location: {\n      address: {\n        post_code: '22769',\n        city: 'Hamburg',\n        street: 'Stresemannstr.',\n        house_number: '29'\n      }\n    },\n    property_type: { code: :apartment },\n    building_year: 1990,\n    living_area: 200\n  }\n).perform!\n# =\u003e [#\u003cPriceHubble::Valuation ...\u003e]\n\n# Print all relevant valuation attributes\npp valuations.first.attributes.deep_compact\n# =\u003e {\"currency\"=\u003e\"EUR\",\n# =\u003e  \"sale_price\"=\u003e1283400,\n# =\u003e  \"sale_price_range\"=\u003e1180800..1386100,\n# =\u003e  \"confidence\"=\u003e\"good\",\n# =\u003e  \"deal_type\"=\u003e\"sale\",\n# =\u003e  \"valuation_date\"=\u003eThu, 17 Oct 2019,\n# =\u003e  \"country_code\"=\u003e\"DE\",\n# =\u003e  \"property\"=\u003e\n# =\u003e   {\"location\"=\u003e\n# =\u003e     {\"address\"=\u003e\n# =\u003e       {\"post_code\"=\u003e\"22769\",\n# =\u003e        \"city\"=\u003e\"Hamburg\",\n# =\u003e        \"street\"=\u003e\"Stresemannstr.\",\n# =\u003e        \"house_number\"=\u003e\"29\"}},\n# =\u003e    \"property_type\"=\u003e{\"code\"=\u003e:apartment},\n# =\u003e    \"building_year\"=\u003e1990,\n# =\u003e    \"living_area\"=\u003e200}}\n```\n\n#### Use the PriceHubble::Valuation representation\n\nThe [`PriceHubble::Valuation`](lib/pricehubble/entity/valuation.rb)\nrepresentation ships a lot utilities and helpers to process the data.\nHere are some examples:\n\n```ruby\n# Fetch the valuations\nvaluations = PriceHubble::ValuationRequest.new(...).perform!\n# We just want to work on the first valuation\nvaluation = valuations.first\n\n# Get the deal type dependent value of the property in a generic way.\n# (sale price if deal type is sale, and rent gross if deal type is rent)\nvaluation.value\n# =\u003e 1283400\n\n# Get the upper and lower value range of the property in a generic\n# way. The deal type logic is equal to the\n# +PriceHubble::Valuation#value+ method.\nvaluation.value_range\n# =\u003e 1180800..1386100\n\n# Query the valuation confidence in an elegant way\nvaluation.confidence.good?\n# =\u003e true\n\n# The +PriceHubble::Valuation+ entity is a self contained\n# representation of the request and response. This means it includes\n# the property, deal type, valuation date, country code, etc it was\n# requested for. This makes it easy to process the data because\n# everything related is in one place.\nvaluation.property.property_type.apartment?\n# =\u003e true\n```\n\n#### Error Handling\n\nThe property valuation API is able to perform bulk operations which\nmay result in non-bang situations, even when you use the bang variant.\nThis is quite smart as a single property valuation might not break\nothers on the very same request. Watch for the\n`PriceHubble::Valuation#status` hash, when it is not nil, then you got\nan error for this valuation.\n\nIn case you request just one property valuation, then the API will\nrespond an error which is mapped when you use the bang variants.\n\n```ruby\nbegin\n  # Fetch the valuations for a single property (sale) for today (defaults).\n  # This is the bare minimum variant, as a starting point.\n  PriceHubble::ValuationRequest.new(\n    property: {\n      location: {\n        address: {\n          post_code: '22769',\n          city: 'Hamburg',\n          street: 'Stresemannstr.',\n          house_number: '29'\n        }\n      },\n      property_type: { code: :apartment },\n      building_year: 2999,\n      living_area: 200\n    }\n  ).perform!\nrescue PriceHubble::EntityInvalid =\u003e e\n  # =\u003e #\u003cPriceHubble::EntityInvalid: buildingYear: ...\u003e\n\n  # The error message includes the detailed problem.\n  e.message\n  # =\u003e \"buildingYear: Must be between 1850 and 2022.\"\nend\n```\n\n#### Advanced Request Examples\n\nHere comes a more complex example of a property valuations request\nwith multiple properties, for several valuations dates.\n\n**Gotcha!** The API does not allow you to mix and match the deal type\nper property. Therefore you need to set it once for the whole request.\n\n```ruby\n# Build a property with all relevant information for the valuation. This\n# example reflects not the full list of supported fields, [see the API\n# specification](https://docs.pricehubble.com/#types-property) for the full\n# details.\napartment = PriceHubble::Property.new(\n  location: {\n    address: {\n      post_code: '22769',\n      city: 'Hamburg',\n      street: 'Stresemannstr.',\n      house_number: '29'\n    }\n  },\n  property_type: { code: :apartment },\n  building_year: 1990,\n  living_area: 200,\n  balcony_Area: 30,\n  floor_number: 5,\n  has_lift: true,\n  is_furnished: false,\n  is_new: false,\n  renovation_year: 2014,\n  condition: {\n    bathrooms: :well_maintained,\n    kitchen: :well_maintained,\n    flooring: :well_maintained,\n    windows: :well_maintained,\n    masonry: :well_maintained\n  },\n  quality: {\n    bathrooms: :normal,\n    kitchen: :normal,\n    flooring: :normal,\n    windows: :normal,\n    masonry: :normal\n  }\n)\n\nhouse = PriceHubble::Property.new(\n  location: {\n    address: {\n      post_code: '22769',\n      city: 'Hamburg',\n      street: 'Stresemannstr.',\n      house_number: '29'\n    }\n  },\n  property_type: { code: :house },\n  building_year: 1990,\n  land_area: 100,\n  living_area: 500,\n  number_of_floors_in_building: 5\n)\n\n# Fetch the property valuations for multiple properties, on multiple dates\nrequest = PriceHubble::ValuationRequest.new(\n  deal_type: :sale,\n  properties: [apartment, house],\n  # The dates order is reflected on the valuations list\n  valuation_dates: [\n    1.year.ago,\n    Date.current,\n    1.year.from_now\n  ]\n)\nvaluations = request.perform!\n\n# Print the valuations in a simple ASCII table. This is just a\n# demonstration of how to use the valuations for a simple usecase.\nrequire 'terminal-table'\ntable = Terminal::Table.new do |tab|\n  tab \u003c\u003c ['Deal Type', 'Property Type', *request.valuation_dates.map(\u0026:year)]\n  tab \u003c\u003c :separator\n  # Group the valuations by the property they represent\n  valuations.group_by(\u0026:property).each do |property, valuations|\n    tab \u003c\u003c [request.deal_type, property.property_type.code,\n            *valuations.map { |val| \"#{val.value} #{val.currency}\" }]\n  end\nend\n# =\u003e +-----------+---------------+-------------+-------------+-------------+\n# =\u003e | Deal Type | Property Type | 2018        | 2019        | 2020        |\n# =\u003e +-----------+---------------+-------------+-------------+-------------+\n# =\u003e | sale      | apartment     | 1282100 EUR | 1373100 EUR | 1420100 EUR |\n# =\u003e | sale      | house         | 1824800 EUR | 1950900 EUR | 2016000 EUR |\n# =\u003e +-----------+---------------+-------------+-------------+-------------+\n```\n\n### Dossiers\n\nThe pricehubble gem allows you to create and delete dossiers for properties.\nThe required property data is equal to the valuation request. Additionally the\npricehubble gem allows you to create sharing links (permalinks) which will link\nto the dossier dashboard application for customers.\n\n**Gotcha!** The API allows to update and search for dossiers. Additionally the\nAPI supports images and logos for dossiers. The pricehubble gem does not\nsupport this yet.\n\n```ruby\n# The property to create a dossier for\napartment = {\n  location: {\n    address: {\n      post_code: '22769',\n      city: 'Hamburg',\n      street: 'Stresemannstr.',\n      house_number: '29'\n    }\n  },\n  property_type: { code: :apartment },\n  building_year: 1990,\n  living_area: 200\n}\n\ndossier = PriceHubble::Dossier.new(\n  title: 'Customer Dossier for Stresemannstr. 29',\n  description: 'Best apartment in the city',\n  deal_type: :sale,\n  property: apartment,\n  country_code: 'DE',\n  asking_sale_price: 600_000 # the minimum price the seller is willing to agree\n  # valuation_override_sale_price: '', # overwrite the PH value\n  # valuation_override_rent_net: '', # overwrite the PH value\n  # valuation_override_rent_gross: '', # overwrite the PH value\n  # valuation_override_reason_freetext: '' # explain the visitor why\n)\n\n# Save the new dossier\npp dossier.save!\npp dossier.id\n# =\u003e \"25de5429-244e-4584-b58e-b0d7428a2377\"\n\n# Generate a sharing link for the dossier\npp dossier.link\n# =\u003e \"https://dash.pricehubble.com/shared/dossier/eyJ0eXAiOiJ...\"\n```\n\n## Development\n\nAfter checking out the repo, run `make install` to install dependencies. Then,\nrun `make test` to run the tests. You can also run `make shell-irb` for an\ninteractive prompt that will allow you to experiment.\n\n## Code of Conduct\n\nEveryone interacting in the project codebase, issue tracker, chat\nrooms and mailing lists is expected to follow the [code of\nconduct](./CODE_OF_CONDUCT.md).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/hausgold/pricehubble. Make sure that every pull request adds\na bullet point to the [changelog](./CHANGELOG.md) file with a reference to the\nactual pull request.\n\n## Releasing\n\nThe release process of this Gem is fully automated. You just need to open the\nGithub Actions [Release\nWorkflow](https://github.com/hausgold/pricehubble/actions/workflows/release.yml)\nand trigger a new run via the **Run workflow** button. Insert the new version\nnumber (check the [changelog](./CHANGELOG.md) first for the latest release) and\nyou're done.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Fpricehubble","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhausgold%2Fpricehubble","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Fpricehubble/lists"}