Skip to content

Commit 325b49f

Browse files
authored
Merge branch 'development' into aaron/config.load_defaults-7.1
2 parents 1226803 + 9572e01 commit 325b49f

File tree

15 files changed

+299
-101
lines changed

15 files changed

+299
-101
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
- Add pdf handling in `render_respond_to_format_with_error_message` [#3482](https://github.com/DMPRoadmap/roadmap/pull/3482)
1313
- Lower PostgreSQL GitHub Action Chrome Version to Address Breaking Changes Between Latest Chrome Version (134) and `/features` Tests [#3491](https://github.com/DMPRoadmap/roadmap/pull/3491)
1414
- Bumped dependencies via `bundle update && yarn upgrade` [#3483](https://github.com/DMPRoadmap/roadmap/pull/3483)
15+
- Fixed issues with Conditional Question serialization offered by @briri from PR https://github.com/CDLUC3/dmptool/pull/667 for DMPTool. There is a migration file with code for MySQL and Postgres to update the Conditions table to convert JSON Arrays in string format records in the conditions table so that they are JSON Arrays.
16+
- Refactor `org_admin/conditions/_form.html.erb` [#3502](https://github.com/DMPRoadmap/roadmap/pull/3502)
17+
- Refactor `Question.save_condition` [#3501](https://github.com/DMPRoadmap/roadmap/pull/3501)
1518

1619
## v4.2.0
1720

app/controllers/org_admin/questions_controller.rb

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -211,30 +211,32 @@ def destroy
211211

212212
private
213213

214-
# param_conditions looks like:
215-
# [
216-
# {
217-
# "conditions_N" => {
218-
# name: ...
219-
# subject ...
220-
# ...
221-
# }
222-
# ...
223-
# }
224-
# ]
214+
# param_conditions is a hash of a hash like this (example where
215+
# action_types is "remove" and "add_webhook" respectively):
216+
# Parameters:
217+
# {"0"=>{"question_option"=>["212159"],
218+
# "action_type"=>"remove",
219+
# "remove_question_id"=>["191471 191472"],
220+
# "number"=>"0"},
221+
# "1"=>{"question_option"=>["212160"],
222+
# "action_type"=>"add_webhook",
223+
# "number"=>"1",
224+
# "webhook-name"=>"DMP Admin",
225+
# "webhook-email"=>"dmp-admin@example.com",
226+
# "webhook-subject"=>"Woodcote cillum quis elit consectetur epsom",
227+
# "webhook-message"=>"Labore ut epsom downs exercitation ...."}
228+
# }
225229
def sanitize_hash(param_conditions)
226230
return {} if param_conditions.nil?
227231
return {} if param_conditions.empty?
228232

229233
res = {}
230-
hash_of_hashes = param_conditions[0]
231-
hash_of_hashes.each do |cond_name, cond_hash|
234+
param_conditions.each do |cond_id, cond_hash|
232235
sanitized_hash = {}
233236
cond_hash.each do |k, v|
234-
v = ActionController::Base.helpers.sanitize(v) if k.start_with?('webhook')
235-
sanitized_hash[k] = v
237+
sanitized_hash[k] = k.start_with?('webhook') ? ActionController::Base.helpers.sanitize(v) : v
236238
end
237-
res[cond_name] = sanitized_hash
239+
res[cond_id] = sanitized_hash
238240
end
239241
res
240242
end

app/helpers/conditions_helper.rb

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def remove_list(object)
1010
id_list = []
1111
plan_answers = object.answers if object.is_a?(Plan)
1212
plan_answers = object[:answers] if object.is_a?(Hash)
13-
return [] unless plan_answers.present?
13+
return [] if plan_answers.blank?
1414

1515
plan_answers.each { |answer| id_list += answer_remove_list(answer) }
1616
id_list
@@ -32,7 +32,7 @@ def answer_remove_list(answer, user = nil)
3232
rems = cond.remove_data.map(&:to_i)
3333
id_list += rems
3434
elsif !user.nil?
35-
UserMailer.question_answered(JSON.parse(cond.webhook_data), user, answer,
35+
UserMailer.question_answered(cond.webhook_data, user, answer,
3636
chosen.join(' and ')).deliver_now
3737
end
3838
end
@@ -57,7 +57,7 @@ def email_trigger_list(answer)
5757
chosen = answer.question_option_ids.sort
5858
next unless chosen == opts
5959

60-
email_list << JSON.parse(cond.webhook_data)['email'] if action == 'add_webhook'
60+
email_list << cond.webhook_data['email'] if action == 'add_webhook'
6161
end
6262
# uniq because could get same remove id from diff conds
6363
email_list.uniq.join(',')
@@ -70,7 +70,7 @@ def num_section_answers(plan, section)
7070
plan_remove_list = remove_list(plan)
7171
plan.answers.each do |answer|
7272
next unless answer.question.section_id == section.id &&
73-
!plan_remove_list.include?(answer.question_id) &&
73+
plan_remove_list.exclude?(answer.question_id) &&
7474
section.question_ids.include?(answer.question_id) &&
7575
answer.answered?
7676

@@ -107,10 +107,9 @@ def num_section_questions(plan, section, phase = nil)
107107
def sections_info(plan)
108108
return [] if plan.nil?
109109

110-
info = plan.sections.map do |section|
110+
plan.sections.map do |section|
111111
section_info(plan, section)
112112
end
113-
info || []
114113
end
115114

116115
def section_info(plan, section)
@@ -190,7 +189,7 @@ def condition_to_text(conditions)
190189
return_string += "<dd>#{_('Answering')} "
191190
return_string += opts.join(' and ')
192191
if cond.action_type == 'add_webhook'
193-
subject_string = text_formatted(JSON.parse(cond.webhook_data)['subject'])
192+
subject_string = text_formatted(cond.webhook_data['subject'])
194193
return_string += format(_(' will <b>send an email</b> with subject %{subject_name}'),
195194
subject_name: subject_string)
196195
else
@@ -209,7 +208,7 @@ def condition_to_text(conditions)
209208
def text_formatted(object)
210209
text = Question.find(object).text if object.is_a?(Integer)
211210
text = object if object.is_a?(String)
212-
return 'type error' unless text.present?
211+
return 'type error' if text.blank?
213212

214213
cleaned_text = text
215214
text = ActionController::Base.helpers.truncate(cleaned_text, length: DISPLAY_LENGTH,
@@ -231,7 +230,7 @@ def conditions_to_param_form(conditions)
231230
webhook_data: condition.webhook_data } }
232231
if param_conditions.key?(title)
233232
param_conditions[title].merge!(condition_hash[title]) do |_key, val1, val2|
234-
if val1.is_a?(Array) && !val1.include?(val2[0])
233+
if val1.is_a?(Array) && val1.exclude?(val2[0])
235234
val1 + val2
236235
else
237236
val1

app/javascript/src/orgAdmin/conditions/updateConditions.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ export default function updateConditions(id) {
1515
addLogicButton.get(0).click();
1616
}
1717
}
18-
// set up form-select select boxes for condition options
19-
const setSelectPicker = () => {
20-
// $('.form-select.narrow').selectpicker({ width: 120 });
21-
// $('.form-select.regular').selectpicker({ width: 150 });
22-
};
2318

2419
// test if a webhook is selected and set up if so
2520
const allowWebhook = (selectObject, webhook = false) => { // webhook false => new condition
@@ -30,8 +25,10 @@ export default function updateConditions(id) {
3025
// Retreive 'data-bs-target' for modal and create Jquery element
3126
const associatedModal = $(condition.find('.pseudo-webhook-btn').attr('data-bs-target'));
3227
associatedModal.modal('show');
28+
condition.find('.display-if-action-remove').hide();
3329
} else { // condition type is remove
3430
condition.find('.remove-dropdown').show();
31+
condition.find('.display-if-action-remove').show();
3532
condition.find('.webhook-replacement').hide();
3633
}
3734
} else { // loading already saved conditions
@@ -97,11 +94,10 @@ export default function updateConditions(id) {
9794
addLogicButton.attr('data-loaded', 'true');
9895
addLogicButton.addClass('disabled');
9996
addLogicButton.blur();
100-
addLogicButton.text('Conditions');
97+
addLogicButton.text('Edit Conditions');
10198
if (isObject(content)) {
10299
content.html(e.detail[0].container);
103100
}
104-
setSelectPicker();
105101
webhookForm(e.detail[0].webhooks, undefined);
106102
});
107103

@@ -114,7 +110,6 @@ export default function updateConditions(id) {
114110
conditionList.append(e.detail[0].attachment_partial);
115111
addDiv.html(e.detail[0].add_link);
116112
conditionList.attr('data-loaded', 'false');
117-
setSelectPicker();
118113
const selectObject = conditionList.find('.form-select.action-type').last();
119114
webhookForm(undefined, selectObject);
120115
}

app/models/condition.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
# Object that represents a condition of a conditional question
2828
class Condition < ApplicationRecord
2929
belongs_to :question
30-
enum action_type: %i[remove add_webhook]
31-
serialize :option_list, type: Array, coder: YAML
32-
serialize :remove_data, type: Array, coder: YAML
30+
enum :action_type, { remove: 0, add_webhook: 1 }
31+
serialize :option_list, type: Array, coder: JSON
32+
serialize :remove_data, type: Array, coder: JSON
3333
serialize :webhook_data, coder: JSON
3434

3535
# Sort order: Number ASC

app/models/question.rb

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def guidance_for_org(org)
145145
guidances = {}
146146
if theme_ids.any?
147147
GuidanceGroup.includes(guidances: :themes)
148-
.where(org_id: org.id).each do |group|
148+
.where(org_id: org.id).find_each do |group|
149149
group.guidances.each do |g|
150150
g.themes.each do |theme|
151151
guidances["#{group.name} " + _('guidance on') + " #{theme.title}"] = g if theme_ids.include? theme.id
@@ -196,8 +196,8 @@ def annotations_per_org(org_id)
196196
type: Annotation.types[:example_answer])
197197
guidance = annotations.find_by(org_id: org_id,
198198
type: Annotation.types[:guidance])
199-
example_answer = annotations.build(type: :example_answer, text: '', org_id: org_id) unless example_answer.present?
200-
guidance = annotations.build(type: :guidance, text: '', org_id: org_id) unless guidance.present?
199+
example_answer = annotations.build(type: :example_answer, text: '', org_id: org_id) if example_answer.blank?
200+
guidance = annotations.build(type: :guidance, text: '', org_id: org_id) if guidance.blank?
201201
[example_answer, guidance]
202202
end
203203

@@ -206,48 +206,45 @@ def annotations_per_org(org_id)
206206
# after versioning
207207
def update_conditions(param_conditions, old_to_new_opts, question_id_map)
208208
conditions.destroy_all
209-
return unless param_conditions.present?
209+
return if param_conditions.blank?
210210

211211
param_conditions.each_value do |value|
212212
save_condition(value, old_to_new_opts, question_id_map)
213213
end
214214
end
215215

216-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
217-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
216+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
218217
def save_condition(value, opt_map, question_id_map)
219218
c = conditions.build
220219
c.action_type = value['action_type']
221220
c.number = value['number']
221+
222222
# question options may have changed so rewrite them
223-
c.option_list = value['question_option']
224-
unless opt_map.blank?
225-
new_question_options = c.option_list.map do |qopt|
226-
opt_map[qopt]
227-
end
228-
c.option_list = new_question_options || []
223+
c.option_list = handle_option_list(value, opt_map)
224+
# Do not save the condition if the option_list is empty
225+
if c.option_list.empty?
226+
c.destroy
227+
return
229228
end
230229

231230
if value['action_type'] == 'remove'
232-
c.remove_data = value['remove_question_id']
233-
unless question_id_map.blank?
234-
new_question_ids = c.remove_data.each do |qid|
235-
question_id_map[qid]
236-
end
237-
c.remove_data = new_question_ids || []
231+
c.remove_data = handle_remove_data(value, question_id_map)
232+
# Do not save the condition if remove_data is empty
233+
if c.remove_data.empty?
234+
c.destroy
235+
return
238236
end
239237
else
240-
c.webhook_data = {
241-
name: value['webhook-name'],
242-
email: value['webhook-email'],
243-
subject: value['webhook-subject'],
244-
message: value['webhook-message']
245-
}.to_json
238+
c.webhook_data = handle_webhook_data(value)
239+
# Do not save the condition if webhook_data is nil
240+
if c.webhook_data.nil?
241+
c.destroy
242+
return
243+
end
246244
end
247245
c.save
248246
end
249-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
250-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
247+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
251248

252249
private
253250

@@ -274,4 +271,33 @@ def check_remove_conditions
274271
end
275272
end
276273
# rubocop:enable Metrics/AbcSize
274+
275+
def handle_option_list(value, opt_map)
276+
if opt_map.present?
277+
value['question_option'].map { |qopt| opt_map[qopt] }
278+
else
279+
value['question_option']
280+
end
281+
end
282+
283+
def handle_remove_data(value, question_id_map)
284+
if question_id_map.present?
285+
value['remove_question_id'].map { |qid| question_id_map[qid] }
286+
else
287+
value['remove_question_id']
288+
end
289+
end
290+
291+
def handle_webhook_data(value)
292+
# return nil if any of the webhook fields are blank
293+
return if %w[webhook-name webhook-email webhook-subject webhook-message].any? { |key| value[key].blank? }
294+
295+
# else return the constructed webhook_data hash
296+
{
297+
name: value['webhook-name'],
298+
email: value['webhook-email'],
299+
subject: value['webhook-subject'],
300+
message: value['webhook-message']
301+
}
302+
end
277303
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<div class="row">
22
<div class="col-md-12 mt-2">
33
<%= link_to _('Add condition'), new_org_admin_question_condition_path(question_id: question.id, condition_no: condition_no), remote: true, class: "add-condition" %>
4+
<p>
5+
<%= _('To add a condition you must have selected an Option and Action together with') %>
6+
<ul>
7+
<li> <%= _("if Action is 'remove', you need to select one or more choices in Target.") %> </li>
8+
<li> <%= _("if Action is 'add notification', you need to fill in all the fields in the 'Send email' popup.") %> </li>
9+
</ul>
10+
<%= _('Otherwise, the condition will not be saved.') %>
11+
</p>
412
</div>
513
</div>

app/views/org_admin/conditions/_container.html.erb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
<div class="col-md-3 mb-2 bold">
55
<%= label(:text, _('Option'), class: "control-label")%>
66
</div>
7-
<div class="col-md-3 mb-2">
7+
<div class="col-md-3 mb-2 bold">
88
<%= label(:text, _('Action'), class: "control-label") %>
99
</div>
1010
<div class="col-md-3 mb-2 bold">
11-
<%= _('Remove')%>
11+
<%= label(:text, _('Target'), class: "control-label") %>
1212
</div>
13-
<div class="col-md-3">
13+
<div class="col-md-3 mb-2 bold">
14+
<%= label(:text, _('Remove'), class: "control-label") %>
1415
</div>
1516
</div>
1617
<% conditions_params = conditions_to_param_form(conditions).sort_by { |key| key }.to_h %>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<%
2+
qopt = condition[:question_option_id].any? ? QuestionOption.find_by(id: condition[:question_option_id].first): nil
3+
rquesArray = condition[:remove_question_id].any? ? Question.where(id: condition[:remove_question_id]) : nil
4+
view_email_content_info = _("Hover over the email address to view email content. To change email details you need to remove and add the condition again.")
5+
%>
6+
<div class="col-md-3 pe-2">
7+
<%= qopt[:text]&.slice(0, 25) %>
8+
<%= hidden_field_tag(name_start + "[question_option][]", condition[:question_option_id]) %>
9+
</div>
10+
<div class="col-md-3 pe-2">
11+
<%= condition[:action_type] == 'remove' ? _('Remove') : _('Email') %>
12+
<%= hidden_field_tag(name_start + "[action_type]", condition[:action_type]) %>
13+
</div>
14+
<div class="col-md-3 pe-2">
15+
<% if !rquesArray.nil? %>
16+
<% rquesArray.each do |rques| %>
17+
Question <%= rques[:number] %>: <%= rques.text.gsub(%r{</?p>}, '').slice(0, 50) %>
18+
<%= '...' if rques.text.gsub(%r{</?p>}, '').length > 50 %>
19+
<br>
20+
<% end %>
21+
<%= hidden_field_tag(name_start + "[remove_question_id][]", condition[:remove_question_id]) %>
22+
<% else %>
23+
<%
24+
hook_tip = "#{_('Name')}: #{condition[:webhook_data]['name']}\n"
25+
hook_tip += "#{_('Email')}: #{condition[:webhook_data]['email']}\n"
26+
hook_tip += "#{_('Subject')}: #{condition[:webhook_data]['subject']}\n"
27+
hook_tip += "#{_('Message')}: #{condition[:webhook_data]['message']}"
28+
%>
29+
<span title="<%= hook_tip %>"><%= condition[:webhook_data]['email'] %></span>
30+
<br>(<%= view_email_content_info %>)
31+
32+
<%= hidden_field_tag(name_start + "[webhook-email]", condition[:webhook_data]['email']) %>
33+
<%= hidden_field_tag(name_start + "[webhook-name]", condition[:webhook_data]['name']) %>
34+
<%= hidden_field_tag(name_start + "[webhook-subject]", condition[:webhook_data]['subject']) %>
35+
<%= hidden_field_tag(name_start + "[webhook-message]", condition[:webhook_data]['message']) %>
36+
<% end %>
37+
<%= hidden_field_tag(name_start + "[number]", condition_no) %>
38+
</div>
39+
<div class="col-md-3">
40+
<a href="#anotherurl" class="delete-condition"><%= _('Remove') %></a>
41+
</div>

0 commit comments

Comments
 (0)