-
Notifications
You must be signed in to change notification settings - Fork 118
Expand file tree
/
Copy pathanswers_controller.rb
More file actions
210 lines (191 loc) · 7.94 KB
/
answers_controller.rb
File metadata and controls
210 lines (191 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# frozen_string_literal: true
# Controller that handles Answers to DMP questions
class AnswersController < ApplicationController
respond_to :html
include ConditionsHelper
# POST /answers/create_or_update
# TODO: Why!? This method is overly complex. Needs a serious refactor!
# We should break apart into separate create/update actions to simplify
# logic and we should stop using custom JSON here and instead use
# `remote: true` in the <form> tag and just send back the ERB.
# Consider using ActionCable for the progress bar(s)
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def create_or_update
p_params = permitted_params
# First it is checked plan exists and question exist for that plan
begin
p = Plan.find(p_params[:plan_id])
unless p.question_exists?(p_params[:question_id])
# rubocop:disable Layout/LineLength
render(status: :not_found, json: {
msg: format(_('There is no question with id %{question_id} associated to plan id %{plan_id} for which to create or update an answer'), question_id: p_params[:question_id], plan_id: p_params[:plan_id])
})
# rubocop:enable Layout/LineLength
return
end
rescue ActiveRecord::RecordNotFound
# rubocop:disable Layout/LineLength
render(status: :not_found, json: {
msg: format(_('There is no plan with id %{id} for which to create or update an answer'), id: p_params[:plan_id])
})
# rubocop:enable Layout/LineLength
return
end
q = Question.find(p_params[:question_id])
# rubocop:disable Metrics/BlockLength
Answer.transaction do
args = p_params
# Answer model does not understand :standards so remove it from the params
standards = args[:standards]
args.delete(:standards)
@answer = Answer.find_by!(
plan_id: args[:plan_id],
question_id: args[:question_id]
)
authorize @answer
@answer.update(args.merge(user_id: current_user.id))
if args[:question_option_ids].present?
# Saves the record with the updated_at set to the current time.
# Needed if only answer.question_options is updated
@answer.touch
end
if q.question_format.rda_metadata?
@answer.update_answer_hash(
JSON.parse(standards.to_json), args[:text]
)
@answer.save!
end
rescue ActiveRecord::RecordNotFound
@answer = Answer.new(args.merge(user_id: current_user.id))
@answer.lock_version = 1
authorize @answer
if q.question_format.rda_metadata?
@answer.update_answer_hash(
JSON.parse(standards.to_json), args[:text]
)
end
@answer.save!
rescue ActiveRecord::StaleObjectError
@stale_answer = @answer
@answer = Answer.find_by(
plan_id: args[:plan_id],
question_id: args[:question_id]
)
end
# rubocop:enable Metrics/BlockLength
# TODO: Seems really strange to do this check. If its false it returns an
# 200 with an empty body. We should update to send back some JSON. The
# check should probably happen on create/update
# rubocop:disable Style/GuardClause
if @answer.present?
@plan = Plan.includes(
sections: {
questions: %i[
answers
question_format
]
}
).find(p_params[:plan_id])
@question = @answer.question
@section = @plan.sections.find_by(id: @question.section_id)
template = @section.phase.template
# Get list of questions to be removed from the plan based on any conditional questions.
questions_remove_list_before_destroying_answers = remove_list(@plan)
all_question_ids = @plan.questions.pluck(:id)
# Destroy all answers for removed questions
questions_remove_list_before_destroying_answers.each do |id|
Answer.where(question_id: id, plan: @plan).each do |a|
Answer.destroy(a.id)
end
end
# Now update @plan after removing answers of questions removed from the plan.
@plan = Plan.includes(
sections: {
questions: %i[
answers
question_format
]
}
).find(p_params[:plan_id])
# Now get list of question ids to remove based on remaining answers.
remove_list_question_ids = remove_list(@plan)
qn_data = {
to_show: all_question_ids - remove_list_question_ids,
to_hide: remove_list_question_ids
}
section_data = []
@plan.sections.each do |section|
next if section.number < @section.number
# rubocop pointed out that these variables are not used
# n_qs, n_ans = check_answered(section, qn_data[:to_show], all_answers)
this_section_info = {
sec_id: section.id,
no_qns: num_section_questions(@plan, section),
no_ans: num_section_answers(@plan, section)
}
section_data << this_section_info
end
send_webhooks(current_user, @answer)
render json: {
qn_data: qn_data,
section_data: section_data,
'question' => {
'id' => @question.id,
'answer_lock_version' => @answer.lock_version,
'locking' => if @stale_answer
render_to_string(partial: 'answers/locking', locals: {
question: @question,
answer: @stale_answer,
user: @answer.user
}, formats: [:html])
end,
'form' => render_to_string(partial: 'answers/new_edit', locals: {
template: template,
question: @question,
answer: @answer,
readonly: false,
locking: false,
base_template_org: template.base_org
}, formats: [:html]),
'answer_status' => render_to_string(partial: 'answers/status', locals: {
answer: @answer
}, formats: [:html])
},
'plan' => {
'id' => @plan.id,
'progress' => render_to_string(partial: 'plans/progress', locals: {
plan: @plan,
current_phase: @section.phase
}, formats: [:html])
}
}.to_json
end
# rubocop:enable Style/GuardClause
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
private
# rubocop:disable Metrics/AbcSize
def permitted_params
permitted = params.require(:answer)
.permit(:id, :text, :plan_id, :user_id, :question_id,
:lock_version, question_option_ids: [], standards: {})
# If question_option_ids has been filtered out because it was a
# scalar value (e.g. radiobutton answer)
if !params[:answer][:question_option_ids].nil? &&
!permitted[:question_option_ids].present?
permitted[:question_option_ids] = [params[:answer][:question_option_ids]]
end
permitted.delete(:id) unless permitted[:id].present?
# If no question options has been chosen.
permitted[:question_option_ids] = [] if params[:answer][:question_option_ids].nil?
permitted
end
# rubocop:enable Metrics/AbcSize
def check_answered(section, q_array, all_answers)
n_qs = section.questions.count { |question| q_array.include?(question.id) }
n_ans = all_answers.count { |ans| q_array.include?(ans.question.id) and ans.answered? }
[n_qs, n_ans]
end
end