Skip to content

Commit

Permalink
Merge pull request #1031 from thecartercenter/12522-xlsform-constrain…
Browse files Browse the repository at this point in the history
…t-message

12522: Add constraint rejection messages to XLSForm export
  • Loading branch information
plastichotsprings authored Jan 24, 2025
2 parents c724875 + 8546d3b commit abb53a1
Showing 1 changed file with 32 additions and 13 deletions.
45 changes: 32 additions & 13 deletions app/models/forms/export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,18 @@ def to_xls
# Write sheet headings at row index 0
questions.row(0).push("type")

# translation columns
locales.each do |locale|
questions.row(0).push("label::#{language_name(locale)} (#{locale})",
"hint::#{language_name(locale)} (#{locale})")
end

questions.row(0).push("name", "required", "relevant", "constraint", "choice_filter")
questions.row(0).push("name", "required", "relevant", "choice_filter", "constraint")

locales.each do |locale|
questions.row(0).push("constraint message::#{language_name(locale)} (#{locale})")
end

settings.row(0).push("form_title", "form_id", "version", "default_language")

group_depth = 1 # assume base level
repeat_depth = 1
Expand Down Expand Up @@ -139,19 +144,31 @@ def to_xls
# if we have any relevant conditions or constraints, save them now
conditions_to_push = conditions_to_xls(q.display_conditions, q.display_if)

constraints_to_push = ""
q.constraints.each_with_index do |c, c_index|
# constraint rules should be placed in parentheses and separated by "and"
# https://docs.getodk.org/form-logic/#validating-and-restricting-responses
constraints_to_push += "(#{conditions_to_xls(c.conditions, c.accept_if)})"
# declare constraint arrays
constraints_to_push = []
constraint_msg_to_push = Array.new(locales.length, [])

# add "and" unless we're at the end
constraints_to_push += " and " unless c_index + 1 == q.constraints.length
q.constraints.each_with_index do |c, c_index|
constraints_to_push.push("(#{conditions_to_xls(c.conditions, c.accept_if)})")

# TODO: add support for constraint messages ("rejection_msg" in NEMO)
# Write translated constraint message columns ("rejection_msg" in NEMO)
# https://xlsform.org/en/#constraint-message
# NEMO allows multiple constraint messages for each rule, whereas XLSForm only supports one message per row.
# Thus, if there are multiple constraints or rules for this question, combine all provided messages into one string (per locale)
locales.each_with_index do |locale, locale_index|
# Attempt to get a message for that constraint for that language (may be nil if a translation is not provided)
constraint_message = c.rejection_msg_translations&.dig(locale.to_s)
constraint_msg_to_push[locale_index] += [constraint_message] unless constraint_message.blank?
end
end

# convert arrays into concatenated strings in XLSForm format
# constraint rules should be placed in parentheses and separated by "and"
# constraint message will still be an array, but contain a string for each locale
# https://docs.getodk.org/form-logic/#validating-and-restricting-responses
constraints_to_push = constraints_to_push.join(" and ")
constraint_msg_to_push.map! { |n| n.join("; ") }

# if we have an option set, identify and save it so that we can add it to the choices sheet later.
# then, write the question, splitting it into multiple questions if there are option set levels.
choice_filter = ""
Expand Down Expand Up @@ -185,7 +202,7 @@ def to_xls
end

questions.row(row_index + l_index).push(name_to_push,
q.required.to_s, conditions_to_push, constraints_to_push, choice_filter)
q.required.to_s, conditions_to_push, choice_filter, constraints_to_push, *constraint_msg_to_push)

# define the choice_filter cell for the following row, e.g, "state=${selected_state}"
choice_filter = "#{level_name}=${#{name_to_push}}"
Expand All @@ -204,8 +221,9 @@ def to_xls
questions.row(row_index).push(q.question.name_translations&.dig(locale.to_s),
q.question.hint_translations&.dig(locale.to_s))
end

questions.row(row_index).push(q.code, q.required.to_s,
conditions_to_push, constraints_to_push, choice_filter)
conditions_to_push, choice_filter, constraints_to_push, *constraint_msg_to_push)
end
else # no option set present
# Write the question row as normal
Expand All @@ -215,8 +233,9 @@ def to_xls
questions.row(row_index).push(q.question.name_translations&.dig(locale.to_s),
q.question.hint_translations&.dig(locale.to_s))
end

questions.row(row_index).push(q.code, q.required.to_s,
conditions_to_push, constraints_to_push, choice_filter)
conditions_to_push, choice_filter, constraints_to_push, *constraint_msg_to_push)
end
end

Expand Down

0 comments on commit abb53a1

Please sign in to comment.