Browse Source

feat: mvp commit with address selector from address line 1 and postcode using OS places find endpoint

pull/2278/head
natdeanlewissoftwire 2 years ago
parent
commit
4efa7fd23b
  1. 18
      app/models/form/lettings/pages/address_matcher.rb
  2. 17
      app/models/form/lettings/pages/address_selection.rb
  3. 14
      app/models/form/lettings/questions/address_line1_for_address_matcher.rb
  4. 60
      app/models/form/lettings/questions/address_selection.rb
  5. 25
      app/models/form/lettings/questions/postcode_for_address_matcher.rb
  6. 10
      app/models/form/lettings/subsections/property_information.rb
  7. 5
      app/models/lettings_log.rb
  8. 44
      app/models/log.rb
  9. 52
      app/services/address_client.rb
  10. 41
      app/services/address_data_presenter.rb
  11. 1
      app/views/form/page.html.erb
  12. 5
      db/migrate/20240227163853_add_address_selection_to_lettings_logs.rb

18
app/models/form/lettings/pages/address_matcher.rb

@ -0,0 +1,18 @@
class Form::Lettings::Pages::AddressMatcher < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "address_matcher"
@header = "Enter address details"
@depends_on = [
{ "is_supported_housing?" => false, "uprn_known" => 0 },
{ "is_supported_housing?" => false, "uprn_confirmed" => 0 },
]
end
def questions
@questions ||= [
Form::Lettings::Questions::AddressLine1ForAddressMatcher.new(nil, nil, self),
Form::Lettings::Questions::PostcodeForAddressMatcher.new(nil, nil, self),
]
end
end

17
app/models/form/lettings/pages/address_selection.rb

@ -0,0 +1,17 @@
class Form::Lettings::Pages::AddressSelection < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "address_selection"
@header = "We found some addresses that might be this property"
end
def questions
@questions ||= [
Form::Lettings::Questions::AddressSelection.new(nil, nil, self),
]
end
def routed_to?(log, _current_user = nil)
log.uprn_known == 0 && log.address_line1.present? && log.postcode_full.present?
end
end

14
app/models/form/lettings/questions/address_line1_for_address_matcher.rb

@ -0,0 +1,14 @@
class Form::Lettings::Questions::AddressLine1ForAddressMatcher < ::Form::Question
def initialize(id, hsh, page)
super
@id = "address_line1"
@header = "Address line 1"
@error_label = "Address line 1"
@type = "text"
@plain_label = true
@check_answer_label = "Address line 1"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = 12
@hide_question_number_on_page = true
end
end

60
app/models/form/lettings/questions/address_selection.rb

@ -0,0 +1,60 @@
class Form::Lettings::Questions::AddressSelection < ::Form::Question
def initialize(id, hsh, page)
super
@id = "address_selection"
@header = "Select the correct address"
@type = "radio"
@check_answer_label = "Select the correct address"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true # have just added this, check if it works!
end
def answer_options(log = nil, user = nil)
answer_opts = {
# "0" => { "value" => "address 0" },
# "1" => { "value" => "address 1" },
# "2" => { "value" => "address 2" },
# "3" => { "value" => "address 3" },
# "4" => { "value" => "address 4" },
# "5" => { "value" => "address 5" },
# "6" => { "value" => "address 6" },
# "7" => { "value" => "address 7" },
# "8" => { "value" => "address 8" },
# "9" => { "value" => "address 9" },
}
return answer_opts unless ActiveRecord::Base.connected?
return answer_opts unless log
return answer_opts unless log.address_options
values = []
log.address_options.each do |option|
values.append(option)
end
{
"0" => { "value" => values[0] },
"1" => { "value" => values[1] },
"2" => { "value" => values[2] },
"3" => { "value" => values[3] },
"4" => { "value" => values[4] },
"5" => { "value" => values[5] },
"6" => { "value" => values[6] },
"7" => { "value" => values[7] },
"8" => { "value" => values[8] },
"9" => { "value" => values[9] },
}.freeze
end
def displayed_answer_options(log, user = nil)
answer_options(log, user)
end
def hidden_in_check_answers?(log, _current_user = nil)
log.uprn_known == 1 || log.uprn_confirmed == 1
end
private
def selected_answer_option_is_derived?(_log)
true
end
end

25
app/models/form/lettings/questions/postcode_for_address_matcher.rb

@ -0,0 +1,25 @@
class Form::Lettings::Questions::PostcodeForAddressMatcher < ::Form::Question
def initialize(id, hsh, page)
super
@id = "postcode_full"
@header = "Postcode"
@type = "text"
@width = 5
@inferred_check_answers_value = [{
"condition" => {
"pcodenk" => 1,
},
"value" => "Not known",
}]
@inferred_answers = {
"la" => {
"is_la_inferred" => true,
},
}
@plain_label = true
@check_answer_label = "Postcode"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = 12
@hide_question_number_on_page = true
end
end

10
app/models/form/lettings/subsections/property_information.rb

@ -31,7 +31,15 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
end end
def uprn_questions def uprn_questions
if form.start_date.year >= 2023 if form.start_year_after_2024?
[
Form::Lettings::Pages::Uprn.new(nil, nil, self),
Form::Lettings::Pages::UprnConfirmation.new(nil, nil, self),
Form::Lettings::Pages::AddressMatcher.new(nil, nil, self),
Form::Lettings::Pages::AddressSelection.new(nil, nil, self),
Form::Lettings::Pages::Address.new(nil, nil, self),
]
elsif form.start_date.year == 2023
[ [
Form::Lettings::Pages::Uprn.new(nil, nil, self), Form::Lettings::Pages::Uprn.new(nil, nil, self),
Form::Lettings::Pages::UprnConfirmation.new(nil, nil, self), Form::Lettings::Pages::UprnConfirmation.new(nil, nil, self),

5
app/models/lettings_log.rb

@ -34,6 +34,7 @@ class LettingsLog < Log
before_validation :reset_previous_location_fields!, unless: :previous_postcode_known? before_validation :reset_previous_location_fields!, unless: :previous_postcode_known?
before_validation :set_derived_fields! before_validation :set_derived_fields!
before_validation :process_uprn_change!, if: :should_process_uprn_change? before_validation :process_uprn_change!, if: :should_process_uprn_change?
before_validation :process_address_change!, if: :should_process_address_change?
belongs_to :scheme, optional: true belongs_to :scheme, optional: true
belongs_to :location, optional: true belongs_to :location, optional: true
@ -847,4 +848,8 @@ private
def should_process_uprn_change? def should_process_uprn_change?
uprn && startdate && (uprn_changed? || startdate_changed?) && collection_start_year_for_date(startdate) >= 2023 uprn && startdate && (uprn_changed? || startdate_changed?) && collection_start_year_for_date(startdate) >= 2023
end end
def should_process_address_change?
address_selection && startdate && (address_selection_changed? || startdate_changed?) && form.start_year_after_2024?
end
end end

44
app/models/log.rb

@ -53,7 +53,7 @@ class Log < ApplicationRecord
scope :filter_by_owning_organisation, ->(owning_organisation, _user = nil) { where(owning_organisation:) } scope :filter_by_owning_organisation, ->(owning_organisation, _user = nil) { where(owning_organisation:) }
scope :filter_by_managing_organisation, ->(managing_organisation, _user = nil) { where(managing_organisation:) } scope :filter_by_managing_organisation, ->(managing_organisation, _user = nil) { where(managing_organisation:) }
attr_accessor :skip_update_status, :skip_update_uprn_confirmed, :skip_dpo_validation attr_accessor :skip_update_status, :skip_update_uprn_confirmed, :skip_update_address_selection, :skip_dpo_validation
def process_uprn_change! def process_uprn_change!
if uprn.present? if uprn.present?
@ -66,6 +66,7 @@ class Log < ApplicationRecord
self.uprn_known = 1 self.uprn_known = 1
self.uprn_confirmed = nil unless skip_update_uprn_confirmed self.uprn_confirmed = nil unless skip_update_uprn_confirmed
self.address_selection = nil
self.address_line1 = presenter.address_line1 self.address_line1 = presenter.address_line1
self.address_line2 = presenter.address_line2 self.address_line2 = presenter.address_line2
self.town_or_city = presenter.town_or_city self.town_or_city = presenter.town_or_city
@ -75,6 +76,47 @@ class Log < ApplicationRecord
end end
end end
def process_address_change!
if [address_selection, address_line1, postcode_full].all?(&:present?)
address_string = "#{address_line1}, , , #{postcode_full}"
service = AddressClient.new(address_string)
service.call
return errors.add(:address_line1, :address_error, message: service.error) if service.error.present?
presenter = AddressDataPresenter.new(service.result[address_selection])
self.uprn_known = 1
self.uprn_confirmed = 1
self.address_selection = nil # unless skip_update_address_confirmed
self.uprn = presenter.uprn #skip process uprn change?
self.address_line1 = presenter.address_line1
self.address_line2 = presenter.address_line2
self.town_or_city = presenter.town_or_city
self.postcode_full = presenter.postcode
self.county = nil
process_postcode_changes!
end
end
def address_options
if [address_line1, postcode_full].all?(&:present?)
address_string = "#{address_line1}, , , #{postcode_full}"
service = AddressClient.new(address_string)
service.call
return errors.add(:address_line1, :address_error, message: service.error) if service.error.present?
address_options = []
service.result.first(10).each do |result|
presenter = AddressDataPresenter.new(result)
address_options.append(presenter.address)
end
@address_options = address_options
end
end
def collection_start_year def collection_start_year
return @start_year if @start_year return @start_year if @start_year

52
app/services/address_client.rb

@ -0,0 +1,52 @@
require "net/http"
class AddressClient
attr_reader :address
attr_accessor :error
ADDRESS = "api.os.uk".freeze
PATH = "/search/places/v1/find".freeze
def initialize(address)
@address = address
end
def call
unless response.is_a?(Net::HTTPSuccess) && result.present?
@error = "Address is not recognised. Check the address, or enter the UPRN"
end
rescue JSON::ParserError
@error = "Address is not recognised. Check the address, or enter the UPRN"
end
def result
@result ||= JSON.parse(response.body)["results"]&.map { |address| address["DPA"] }
end
private
def http_client
client = Net::HTTP.new(ADDRESS, 443)
client.use_ssl = true
client.verify_mode = OpenSSL::SSL::VERIFY_PEER
client.max_retries = 3
client.read_timeout = 10 # seconds
client
end
def endpoint_uri
uri = URI(PATH)
params = {
query: address,
key: ENV["OS_DATA_KEY"],
matchprecision: 3,
maxresults: 10,
}
uri.query = URI.encode_www_form(params)
uri.to_s
end
def response
@response ||= http_client.request_get(endpoint_uri)
end
end

41
app/services/address_data_presenter.rb

@ -0,0 +1,41 @@
require "net/http"
class AddressDataPresenter
attr_reader :data
def initialize(data)
@data = data
end
def uprn
data["UPRN"]
end
def address_line1
[data["BUILDING_NUMBER"], data["BUILDING_NAME"], data["THOROUGHFARE_NAME"]].compact.join(", ")
end
def address_line2
data["DEPENDENT_LOCALITY"]
end
def town_or_city
data["POST_TOWN"]
end
def postcode
data["POSTCODE"]
end
def address
data["ADDRESS"]
end
# def match
# data["MATCH"]
# end
#
# def match_description
# data["MATCH_DESCRIPTION"]
# end
end

1
app/views/form/page.html.erb

@ -61,6 +61,7 @@
<%= f.hidden_field :page, value: @page.id %> <%= f.hidden_field :page, value: @page.id %>
<%= f.hidden_field :interruption_page_id, value: @interruption_page_id %> <%= f.hidden_field :interruption_page_id, value: @interruption_page_id %>
<%= f.hidden_field :interruption_page_referrer_type, value: @interruption_page_referrer_type %> <%= f.hidden_field :interruption_page_referrer_type, value: @interruption_page_referrer_type %>
<%#= f.hidden_field :address_answer_options, value: @log.address_options %>
<div class="govuk-button-group"> <div class="govuk-button-group">
<% if !@page.interruption_screen? %> <% if !@page.interruption_screen? %>

5
db/migrate/20240227163853_add_address_selection_to_lettings_logs.rb

@ -0,0 +1,5 @@
class AddAddressSelectionToLettingsLogs < ActiveRecord::Migration[7.0]
def change
add_column :lettings_logs, :address_selection, :integer
end
end
Loading…
Cancel
Save