Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12522: Add constraint rejection messages to XLSForm export #1031

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading