Browse Source

Enable dynamically dependent answer options

pull/619/head
baarkerlounger 3 years ago
parent
commit
0383e27821
  1. 6
      app/models/case_log.rb
  2. 23
      app/models/form.rb
  3. 27
      app/models/form/page.rb
  4. 6
      app/models/form/question.rb
  5. 2
      app/views/form/_checkbox_question.html.erb
  6. 2
      app/views/form/_radio_question.html.erb
  7. 2
      app/views/form/_select_question.html.erb
  8. 35
      config/forms/2021_2022.json
  9. 35
      config/forms/2022_2023.json
  10. 5
      spec/fixtures/forms/2021_2022.json
  11. 4
      spec/fixtures/softwire_imports/case_logs/0ead17cb-1668-442d-898c-0d52879ff592.xml
  12. 2
      spec/models/case_log_spec.rb
  13. 4
      spec/models/form/question_spec.rb

6
app/models/case_log.rb

@ -477,12 +477,16 @@ private
(2..8).each do |idx|
if public_send("age#{idx}") && public_send("age#{idx}") < 16
self["ecstat#{idx}"] = 9
elsif public_send("ecstat#{idx}") == 9 && (public_send("age#{idx}").nil? || public_send("age#{idx}") >= 16)
elsif public_send("ecstat#{idx}") == 9 && (public_send("age#{idx}").nil? || public_send("age#{idx}") >= 16) && age_known?(idx)
self["ecstat#{idx}"] = nil
end
end
end
def age_known?(person_num)
!!public_send("age#{person_num}_known")&.zero?
end
def process_postcode_changes!
self.postcode_full = postcode_full.present? ? postcode_full.upcase.gsub(/\s+/, "") : postcode_full
process_postcode(postcode_full, "postcode_known", "is_la_inferred", "la")

23
app/models/form.rb

@ -146,4 +146,27 @@ class Form
previous_page(page_ids, page_index - 1, case_log)
end
def send_chain(arr, case_log)
Array(arr).inject(case_log) { |o, a| o.public_send(*a) }
end
def depends_on_met(depends_on, case_log)
return true unless depends_on
depends_on.any? do |conditions_set|
conditions_set.all? do |question, value|
if value.is_a?(Hash) && value.key?("operator")
operator = value["operator"]
operand = value["operand"]
case_log[question]&.send(operator, operand)
else
parts = question.split(".")
case_log_value = send_chain(parts, case_log)
value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value
end
end
end
end
end

27
app/models/form/page.rb

@ -14,10 +14,12 @@ class Form::Page
@subsection = subsection
end
delegate :form, to: :subsection
def routed_to?(case_log)
return true unless depends_on || subsection.depends_on
subsection.enabled?(case_log) && depends_on_met(case_log)
subsection.enabled?(case_log) && form.depends_on_met(depends_on, case_log)
end
def non_conditional_questions
@ -36,27 +38,4 @@ private
q.conditional_for.keys if q.type == "radio"
}.compact
end
def send_chain(arr, case_log)
Array(arr).inject(case_log) { |o, a| o.public_send(*a) }
end
def depends_on_met(case_log)
return true unless depends_on
depends_on.any? do |conditions_set|
conditions_set.all? do |question, value|
if value.is_a?(Hash) && value.key?("operator")
operator = value["operator"]
operand = value["operand"]
case_log[question]&.send(operator, operand)
else
parts = question.split(".")
case_log_value = send_chain(parts, case_log)
value.nil? ? case_log_value == value : !case_log_value.nil? && case_log_value == value
end
end
end
end
end

6
app/models/form/question.rb

@ -80,9 +80,9 @@ class Form::Question
false
end
def displayed_answer_options
def displayed_answer_options(case_log)
answer_options.select do |_key, val|
!val.is_a?(Hash) || !val["derived"]
!val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], case_log)
end
end
@ -168,7 +168,7 @@ private
def selected_answer_option_is_derived?(case_log)
selected_option = answer_options&.dig(case_log[id].to_s.presence)
selected_option.is_a?(Hash) && selected_option["derived"]
selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], case_log)
end
def has_inferred_display_value?(case_log)

2
app/views/form/_checkbox_question.html.erb

@ -6,7 +6,7 @@
hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %>
<% question.displayed_answer_options.map do |key, options| %>
<% question.displayed_answer_options(@case_log).map do |key, options| %>
<% if key.starts_with?("divider") %>
<% after_divider = true %>
<%= f.govuk_check_box_divider %>

2
app/views/form/_radio_question.html.erb

@ -5,7 +5,7 @@
legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe } do %>
<% question.displayed_answer_options.map do |key, options| %>
<% question.displayed_answer_options(@case_log).map do |key, options| %>
<% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %>
<% else %>

2
app/views/form/_select_question.html.erb

@ -1,7 +1,7 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<% selected = @case_log.public_send(question.id) || "" %>
<% answers = question.displayed_answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } %>
<% answers = question.displayed_answer_options(@case_log).map { |key, value| OpenStruct.new(id: key, name: value) } %>
<%= f.govuk_collection_select question.id.to_sym,
answers,
:id,

35
config/forms/2021_2022.json

@ -1771,7 +1771,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age2_known": 1 },
{ "age2": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -1990,7 +1993,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age3_known": 1 },
{ "age3": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2206,7 +2212,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age4_known": 1 },
{ "age4": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2419,7 +2428,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age5_known": 1 },
{ "age5": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2629,7 +2641,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age6_known": 1 },
{ "age6": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2836,7 +2851,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age7_known": 1 },
{ "age7": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -3040,7 +3058,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age8_known": 1 },
{ "age8": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"

35
config/forms/2022_2023.json

@ -1769,7 +1769,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age2_known": 1 },
{ "age2": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -1995,7 +1998,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age3_known": 1 },
{ "age3": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2218,7 +2224,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age4_known": 1 },
{ "age4": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2438,7 +2447,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age5_known": 1 },
{ "age5": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2655,7 +2667,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age6_known": 1 },
{ "age6": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -2869,7 +2884,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age7_known": 1 },
{ "age7": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"
@ -3080,7 +3098,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age8_known": 1 },
{ "age8": { "operator": "<", "operand": 16 } }
]
},
"0": {
"value": "Other"

5
spec/fixtures/forms/2021_2022.json vendored

@ -188,7 +188,10 @@
},
"9": {
"value": "Child under 16",
"derived": true
"depends_on": [
{ "age2_known": 1 },
{ "age2": { "operator": "<", "operand": 16 } }
]
},
"1": {
"value": "Prefer not to say"

4
spec/fixtures/softwire_imports/case_logs/0ead17cb-1668-442d-898c-0d52879ff592.xml vendored

@ -54,8 +54,8 @@
<P1Eco>6) Not Seeking Work</P1Eco>
<P1Eth>17 Refused</P1Eth>
<P1Nat>1 UK national resident in UK</P1Nat>
<P2Age override-field="">2</P2Age>
<P2AR/>
<P2Age override-field=""/>
<P2AR>Age_Refused</P2AR>
<P2Sex override-field="">Male</P2Sex>
<P2Rel>Child</P2Rel>
<P2Eco>9) Child under 16</P2Eco>

2
spec/models/case_log_spec.rb

@ -1367,7 +1367,7 @@ RSpec.describe CaseLog do
end
it "correctly resets economic status when age changes from under 16" do
household_case_log.update!(age7: 17)
household_case_log.update!(age7_known: 0, age7: 17)
record_from_db = ActiveRecord::Base.connection.execute("select ecstat7 from case_logs where id=#{household_case_log.id}").to_a[0]
expect(record_from_db["ecstat7"]).to eq(nil)
end

4
spec/models/form/question_spec.rb

@ -118,7 +118,7 @@ RSpec.describe Form::Question, type: :model do
context "when answer options do not include derived options" do
it "displays all answer options" do
expect(question.displayed_answer_options).to match(question.answer_options)
expect(question.displayed_answer_options(case_log)).to match(question.answer_options)
end
end
@ -132,7 +132,7 @@ RSpec.describe Form::Question, type: :model do
end
it "does not include those options in the displayed options" do
expect(question.displayed_answer_options).to match(expected_answer_options)
expect(question.displayed_answer_options(case_log)).to match(expected_answer_options)
end
it "can still map the value label" do

Loading…
Cancel
Save