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 2 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
41 changes: 35 additions & 6 deletions app/models/forms/export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,17 @@ 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
Expand Down Expand Up @@ -141,6 +145,7 @@ def to_xls
conditions_to_push = conditions_to_xls(q.display_conditions, q.display_if)

constraints_to_push = ""
constraint_msg_to_push = Array.new(locales.length, "")
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
Expand All @@ -149,8 +154,20 @@ def to_xls
# add "and" unless we're at the end
constraints_to_push += " and " unless c_index + 1 == q.constraints.length

# 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), separated by a semicolon
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)

if constraint_message.present?
constraint_msg_to_push[locale_index] += constraint_message
# Add semicolon concatenator, unless at the end
constraint_msg_to_push[locale_index] += "; " unless c_index + 1 == q.constraints.length
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this works perfectly fine, just sharing a helpful alternative: [1, 2, 3].join("; ")

end
end
end

# if we have an option set, identify and save it so that we can add it to the choices sheet later.
Expand Down Expand Up @@ -187,7 +204,11 @@ 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.each do |message|
questions.row(row_index + l_index).push(message)
end

# define the choice_filter cell for the following row, e.g, "state=${selected_state}"
choice_filter = "#{level_name}=${#{name_to_push}}"
Expand All @@ -207,7 +228,11 @@ def to_xls
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.each do |message|
questions.row(row_index).push(message)
end
end
else # no option set present
# Write the question row as normal
Expand All @@ -218,7 +243,11 @@ def to_xls
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.each do |message|
questions.row(row_index).push(message)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than having a separate each loop here, it may be possible to combine these:

[].push(1, 2, *[3, 4]) is equivalent to [].push(1, 2, 3, 4), with the * "splat" operator deconstructing the array

end
end

Expand Down
Loading