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| (2..8).each do |idx|
if public_send("age#{idx}") && public_send("age#{idx}") < 16 if public_send("age#{idx}") && public_send("age#{idx}") < 16
self["ecstat#{idx}"] = 9 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 self["ecstat#{idx}"] = nil
end end
end end
end end
def age_known?(person_num)
!!public_send("age#{person_num}_known")&.zero?
end
def process_postcode_changes! def process_postcode_changes!
self.postcode_full = postcode_full.present? ? postcode_full.upcase.gsub(/\s+/, "") : postcode_full self.postcode_full = postcode_full.present? ? postcode_full.upcase.gsub(/\s+/, "") : postcode_full
process_postcode(postcode_full, "postcode_known", "is_la_inferred", "la") 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) previous_page(page_ids, page_index - 1, case_log)
end 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 end

27
app/models/form/page.rb

@ -14,10 +14,12 @@ class Form::Page
@subsection = subsection @subsection = subsection
end end
delegate :form, to: :subsection
def routed_to?(case_log) def routed_to?(case_log)
return true unless depends_on || subsection.depends_on 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 end
def non_conditional_questions def non_conditional_questions
@ -36,27 +38,4 @@ private
q.conditional_for.keys if q.type == "radio" q.conditional_for.keys if q.type == "radio"
}.compact }.compact
end 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 end

6
app/models/form/question.rb

@ -80,9 +80,9 @@ class Form::Question
false false
end end
def displayed_answer_options def displayed_answer_options(case_log)
answer_options.select do |_key, val| 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
end end
@ -168,7 +168,7 @@ private
def selected_answer_option_is_derived?(case_log) def selected_answer_option_is_derived?(case_log)
selected_option = answer_options&.dig(case_log[id].to_s.presence) 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 end
def has_inferred_display_value?(case_log) 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 %> hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %> <% 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") %> <% if key.starts_with?("divider") %>
<% after_divider = true %> <% after_divider = true %>
<%= f.govuk_check_box_divider %> <%= f.govuk_check_box_divider %>

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

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

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

@ -1,7 +1,7 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %>
<% selected = @case_log.public_send(question.id) || "" %> <% 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, <%= f.govuk_collection_select question.id.to_sym,
answers, answers,
:id, :id,

35
config/forms/2021_2022.json

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

35
config/forms/2022_2023.json

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

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

@ -188,7 +188,10 @@
}, },
"9": { "9": {
"value": "Child under 16", "value": "Child under 16",
"derived": true "depends_on": [
{ "age2_known": 1 },
{ "age2": { "operator": "<", "operand": 16 } }
]
}, },
"1": { "1": {
"value": "Prefer not to say" "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> <P1Eco>6) Not Seeking Work</P1Eco>
<P1Eth>17 Refused</P1Eth> <P1Eth>17 Refused</P1Eth>
<P1Nat>1 UK national resident in UK</P1Nat> <P1Nat>1 UK national resident in UK</P1Nat>
<P2Age override-field="">2</P2Age> <P2Age override-field=""/>
<P2AR/> <P2AR>Age_Refused</P2AR>
<P2Sex override-field="">Male</P2Sex> <P2Sex override-field="">Male</P2Sex>
<P2Rel>Child</P2Rel> <P2Rel>Child</P2Rel>
<P2Eco>9) Child under 16</P2Eco> <P2Eco>9) Child under 16</P2Eco>

2
spec/models/case_log_spec.rb

@ -1367,7 +1367,7 @@ RSpec.describe CaseLog do
end end
it "correctly resets economic status when age changes from under 16" do 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] 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) expect(record_from_db["ecstat7"]).to eq(nil)
end 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 context "when answer options do not include derived options" do
it "displays all answer 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
end end
@ -132,7 +132,7 @@ RSpec.describe Form::Question, type: :model do
end end
it "does not include those options in the displayed options" do 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 end
it "can still map the value label" do it "can still map the value label" do

Loading…
Cancel
Save