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

ER-793 feedback forms #1102

Merged
merged 108 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
b7b7a46
Draft feedback question model field migration
peterdavidhamilton Feb 27, 2024
9d56af1
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Feb 27, 2024
67e04d3
Defend against fat controller anti-pattern
peterdavidhamilton Feb 28, 2024
bc32ff0
End of module feedback form and main feedback form (#1089)
martikat Feb 29, 2024
85fb273
Update based on PR comments
martikat Mar 1, 2024
5504a03
Rename opinion type to feedback and rename free text question used in…
martikat Mar 1, 2024
d00b00f
use response validation for other text answer
jack-coggin Mar 1, 2024
f47ee12
add update responses for guest users
jack-coggin Mar 1, 2024
452d720
refactor: use form_builder to create main feedback forms for radio bu…
jack-coggin Mar 4, 2024
dffbd1a
revert prod creds changes
jack-coggin Mar 4, 2024
ded557b
add partial for free text feedback questions
jack-coggin Mar 4, 2024
1d466ec
update feedback thank you path
jack-coggin Mar 4, 2024
fc0beb8
Refactor: use form_builder to create end of module feedback forms
martikat Mar 4, 2024
59979e1
change response name to content in feedback show
jack-coggin Mar 4, 2024
3da1438
add feedback answer check for free text input
jack-coggin Mar 4, 2024
9eb9dd6
add form builder link for yard docs
jack-coggin Mar 4, 2024
f5ddcbb
Refactor feedback using idiomatic ruby and existing patterns
peterdavidhamilton Mar 4, 2024
64cb042
Renaming opinion to feedback and update feedback logic (based on PR c…
martikat Mar 5, 2024
5d9a753
Refine first/next logic and flag potential content changes
peterdavidhamilton Mar 5, 2024
4ff9def
Setup unit testing for custom form builder fields
peterdavidhamilton Mar 5, 2024
d442916
Update Previous/Next logic so it uses Previous/Next Decorators
martikat Mar 5, 2024
710e6af
wip: - extract feedback intro partials for complete and incomplete fe…
jack-coggin Mar 5, 2024
b612248
Update logic in previous decorator and remove unused methods
martikat Mar 5, 2024
27ffb4b
Update logic for previous button, previous decorator spec and skippab…
martikat Mar 5, 2024
92eb28a
use markdown in feedback intro
jack-coggin Mar 5, 2024
4ddc094
Unit test for previous page
peterdavidhamilton Mar 5, 2024
6469325
add last_option? method to option class
jack-coggin Mar 5, 2024
a97e4bc
As discussed
peterdavidhamilton Mar 6, 2024
b24c5a8
Split logic between partial / builder / option for feedback form
peterdavidhamilton Mar 6, 2024
68258ff
Update logic for pagination, create feedback page decorator and updat…
martikat Mar 6, 2024
a027dea
Form partials were being hard-coded to narrow designs when they shoul…
peterdavidhamilton Mar 6, 2024
b4fab84
Apply consistent patterns and conventions
peterdavidhamilton Mar 7, 2024
8274aa0
Improve skipping feedback link
peterdavidhamilton Mar 8, 2024
2419662
Remove opinion intro type
peterdavidhamilton Mar 8, 2024
e8a8b50
Fix database migrations, validations, null values and foreign associa…
peterdavidhamilton Mar 8, 2024
92038e6
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Mar 11, 2024
4762e20
Update specs that are currently failing
martikat Mar 12, 2024
6b3b47b
Update failing specs
martikat Mar 12, 2024
0649d03
Update specs to include response yml
martikat Mar 14, 2024
1c3d39f
add guest feedback cookie and update tracking
jack-coggin Mar 14, 2024
33f687f
update feedback controller spec
jack-coggin Mar 14, 2024
b4b02b3
add guest feedback exists test to user spec
jack-coggin Mar 14, 2024
ef84df2
tidy guest struct
jack-coggin Mar 14, 2024
39b36e9
fix feedback controller spec params and add tests for tracking
jack-coggin Mar 14, 2024
59a6c2b
add data analysis exports for feedback forms and update debug panel
jack-coggin Mar 15, 2024
bf2280a
update feedback controller spec
jack-coggin Mar 15, 2024
1b84662
Update styling for feedback questions and update failing specs to use…
martikat Mar 18, 2024
128bce2
Update spelling and small update for content integrity check
martikat Mar 20, 2024
ca42b28
update branch, spec updates, content integrity tweak
jack-coggin Apr 2, 2024
2cc61a8
Bump rack from 3.0.9 to 3.0.9.1 (#1105)
dependabot[bot] Mar 27, 2024
56323a6
Bump json-jwt from 1.16.5 to 1.16.6 (#1109)
dependabot[bot] Mar 27, 2024
7fba373
Bump rdoc from 6.6.2 to 6.6.3.1 (#1123)
dependabot[bot] Mar 28, 2024
721fea4
Merge branch 'main' into ER-793-feedback-forms
jack-coggin Apr 3, 2024
4883b12
update test schema and prev page decorator spec
jack-coggin Apr 3, 2024
5c2bf86
update opinion spec, add tests for skippable questions pagination
jack-coggin Apr 3, 2024
5ab28a9
Update banner and footer links and refactor some specs
peterdavidhamilton Mar 11, 2024
de20e29
add system spec for site-wide feedback forms
jack-coggin Apr 4, 2024
f7346fe
update site wide feedback system spec
jack-coggin Apr 5, 2024
eafe11a
Passing
peterdavidhamilton Apr 10, 2024
ceab079
Refactor and prepare for manual tests
peterdavidhamilton Apr 15, 2024
f1d38cd
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Apr 15, 2024
17a5198
Update caching and docs
peterdavidhamilton Apr 15, 2024
4d36c88
Add extra integrity check
peterdavidhamilton Apr 16, 2024
7c4beef
Simplify guest journey and use single cookie
peterdavidhamilton Apr 19, 2024
cbe14df
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton May 8, 2024
d127d15
Remove additional input validation
peterdavidhamilton May 8, 2024
10a15c3
- user research preference editing in my account
peterdavidhamilton May 9, 2024
a3465b6
Add boolean to user record for UR participation
peterdavidhamilton May 10, 2024
ed3b81a
Typo
peterdavidhamilton May 13, 2024
69eb419
Skip question for guests and defend when no current user exists
peterdavidhamilton May 13, 2024
ddf2192
Refactor main feedback for user vs guest journey
peterdavidhamilton May 14, 2024
0c674e4
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton May 20, 2024
c5266e9
Rework shared content strategy and move next/skip logic out of contro…
peterdavidhamilton May 20, 2024
a034eb6
Type check for user and guest in pagination service
peterdavidhamilton May 20, 2024
aa54c84
- Convert feedback thank-you to a page
peterdavidhamilton May 21, 2024
a9003b4
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jun 6, 2024
d825717
Redact free text feedback on account closure
peterdavidhamilton Jun 6, 2024
ef69937
Encrypt text input for feedback questions
peterdavidhamilton Jun 7, 2024
320da87
Update chrome-path variable for Pa11y workflow
peterdavidhamilton Jun 7, 2024
e6e4bc8
Bump coverage threshold to 93%
peterdavidhamilton Jun 7, 2024
29e085c
- change back link to use browser history
peterdavidhamilton Jun 10, 2024
8c12208
Fix JS functions merged from main
peterdavidhamilton Jun 10, 2024
7547af4
Retain accessible form options where text can be clicked
peterdavidhamilton Jun 10, 2024
e335ccc
- Bump coverage back above threshold
peterdavidhamilton Jun 11, 2024
83171d5
Page titles and sitemap
peterdavidhamilton Jun 11, 2024
c2c80a0
full width rule for feedback questions
peterdavidhamilton Jun 11, 2024
a9d27c7
Fix previous button with skipped pages and guest users
peterdavidhamilton Jun 11, 2024
4c2ab48
Export guest and registered user feedback responses
peterdavidhamilton Jun 12, 2024
5e4749c
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jun 12, 2024
9f3dbb5
Consistent spec order
peterdavidhamilton Jun 12, 2024
ad57f83
Tidy and fix UR research preference check your answers
peterdavidhamilton Jun 13, 2024
b159f4f
Refactor and simplify research_participant?
peterdavidhamilton Jun 13, 2024
4b238e7
Tweak UR redirects
peterdavidhamilton Jun 14, 2024
371222f
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jun 14, 2024
ce747eb
Port skippable answer to the main form for editing
peterdavidhamilton Jun 14, 2024
62e0be8
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jun 18, 2024
14390ec
*typo
peterdavidhamilton Jun 18, 2024
510d9f5
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jun 18, 2024
78f6cd9
Add timestamps to exported data
peterdavidhamilton Jun 18, 2024
724e67b
DPO comment reminder
peterdavidhamilton Jun 21, 2024
95c7948
Store free text response as plain text
peterdavidhamilton Jun 24, 2024
17c9b12
Progress service notes
peterdavidhamilton Jun 26, 2024
0331782
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jul 4, 2024
c861a56
Replace old variable in stylesheets
peterdavidhamilton Jul 4, 2024
f5e927c
Merge remote-tracking branch 'origin/main' into ER-793-feedback-forms
peterdavidhamilton Jul 5, 2024
b870add
Tidy
peterdavidhamilton Jul 5, 2024
3e45fb2
Adjust coverage threshold
peterdavidhamilton Jul 5, 2024
125960e
Add missing error page title
peterdavidhamilton Jul 5, 2024
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
16 changes: 4 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
DATABASE_URL: postgres://postgres:password@localhost:5432/test
DOMAIN: recovery.app
BOT_TOKEN: bot_token
CONTENTFUL_PREVIEW: true # TODO: Reminder to delete this line

services:
postgres:
Expand Down Expand Up @@ -77,21 +78,12 @@ jobs:
-
name: Compile assets
run: bundle exec rails assets:precompile
-
name: Run test suite
run: bundle exec rspec
-
name: Run rubocop
run: bundle exec rubocop
# Answer migration feature test
-
name: Run test suite
run: bundle exec rspec
env:
DISABLE_USER_ANSWER: true
# Gov One feature test
-
name: Run test suite
run: bundle exec rspec
env:
GOV_ONE_LOGIN: true
-
name: Run rubocop
run: bundle exec rubocop
4 changes: 2 additions & 2 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ def timeout_timer

private

# @return [Boolean] health check and landing page requests are exempt
# @return [Boolean]
def maintenance?
return false if %w[/maintenance /health].include?(request.path)
return false if Rails.configuration.protected_endpoints.include?(request.path)

Rails.application.maintenance?
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/concerns/learning.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ def section_bar
# @note memoization ensures validation errors work
# @return [UserAnswer, Response]
def current_user_response
@current_user_response ||= current_user.response_for(content)
@current_user_response ||= content.opinion_question? ? current_user.response_for_shared(content, mod) : current_user.response_for(content)
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
end
end
156 changes: 156 additions & 0 deletions app/controllers/feedback_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
class FeedbackController < ApplicationController
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
helper_method :previous_path, :next_path, :content, :is_checkbox?, :is_free_text?, :feedback_exists?

# @return [nil]
def show
redirect_to next_path if skip_question?
end

# @return [nil]
def index; end

# @return [nil]
def update
return if invalid_answer? || other_blank?

response_exists? ? update_response : create_response
redirect_to next_path
end

# @return [String]
def is_checkbox?
content.response_type
end

# @return [Boolean]
def is_free_text?
content.answers.empty?
end

# @return [Boolean]
def feedback_exists?
return false if current_user.nil?

Response.where(user_id: current_user.id).exists?
end

# @return [String] path to next feedback step
def next_path
return my_modules_path if action_name == 'thank_you'
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
return feedback_path(1) if params[:id].nil?
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
return thank_you_path if params[:id].to_i == questions.count
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved

feedback_path(params[:id].to_i + 1)
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
end

# @return [String] path to previous feedback step
def previous_path
return my_modules_path if params[:id].nil?
return feedback_path(1) if params[:id] == '1'

feedback_path(params[:id].to_i - 1)
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
end

private

# @return [Boolean]
def invalid_answer?
if answer.blank? || answer.all?(&:blank?)
flash[:error] = 'Please answer the question'
redirect_to current_feedback_path and return true
else
false
end
end

# @return [Boolean]
def other_blank?
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
if answer_content.include?('Other') && text_input.blank?
flash[:error] = 'Please specify'
redirect_to current_feedback_path and return true
else
false
end
end

# @return [Response]
def create_response
Response.create!(
user_id: current_user ? current_user.id : nil,
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
answers: answer_content,
question_name: content.name,
text_input: text_input,
)
end

# @return [Response]
def update_response
Response.where(user_id: current_user.id, question_name: content.name).update(
answers: answer_content,
text_input: text_input,
)
end

# @return [Boolean]
def response_exists?
return false if current_user.nil?

Response.where(user_id: current_user.id, question_name: content.name).exists?
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
end

# @return [Boolean]
def skip_question?
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
return false unless skipped_questions.include?(content.name)

true if current_user.nil?
end

# @return [Array<String>]
def skipped_questions
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
%w[main-feedback-6]
end

# @param answer [String]
# @return [String]
def answer_wording(answer)
content.answers[answer.to_i - 1].first
end

# @return [Array<Hash>]
def questions
Course.config.feedback
end

# @return [String]
def current_feedback_path
feedback_path(params[:id])
end

# @return [Hash]
def content
@content ||= questions[params[:id].to_i - 1]
end

# @return [Array<String>]
def answer
@answer ||= if is_free_text?
params[:answers]
else
Array.wrap(params[:answers])
end
end

# @return [Array<String>]
def answer_content
@answer_content ||= begin
return [] if answer.blank?
return answer if is_free_text?

answer.reject(&:blank?).map { |a| answer_wording(a) }.flatten
end
end

def text_input
@text_input ||= params[:answers_custom]
end
end
4 changes: 2 additions & 2 deletions app/controllers/training/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def index
end

def show
if content.is_question?
if content.is_question? || content.opinion_question?
redirect_to training_module_question_path(mod.name, content.name)
elsif content.assessment_results?
redirect_to training_module_assessment_path(mod.name, content.name)
Expand All @@ -38,7 +38,7 @@ def note
end

def render_page
if content.section? && !content.certificate?
if content.section? && !content.certificate? && !content.opinion_intro?
render 'section_intro'
else
render content.page_type
Expand Down
9 changes: 8 additions & 1 deletion app/controllers/training/questions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ def show; end
# @see Tracking
# @return [Event] Show action
def track_events
if track_confidence_start?
if track_feedback_start?
track('feedback_start')
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
elsif track_confidence_start?
track('confidence_check_start')
elsif track_assessment_start?
track('summative_assessment_start')
Expand All @@ -50,6 +52,11 @@ def track_confidence_start?
content.first_confidence? && confidence_start_untracked?
end

# @return [Boolean]
def track_feedback_start?
content.opinion_question? || content.opinion_intro?
end

# @return [Boolean]
def track_assessment_start?
content.first_assessment? && summative_start_untracked?
Expand Down
17 changes: 13 additions & 4 deletions app/controllers/training/responses_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ class ResponsesController < ApplicationController
layout 'hero'

def update
if save_response!
if save_response! || (content.opinion_question? && content.options.blank?)
track_question_answer
redirect
else
if content.opinion_question? && user_answer_text.blank? && content.options.present?
current_user_response.errors.clear
current_user_response.errors.add :answers, :invalid
end
render 'training/questions/show', status: :unprocessable_entity
end
end
Expand All @@ -41,10 +45,10 @@ def response_params
# @note migrate from user_answer to response
# @return [Boolean]
def save_response!
correct_answers = content.confidence_question? ? true : content.correct_answers.eql?(user_answers)
correct_answers = content.confidence_question? || content.opinion_question? ? true : content.correct_answers.eql?(user_answers)
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved

if Rails.application.migrated_answers?
current_user_response.update(answers: user_answers, correct: correct_answers)
current_user_response.update(answers: user_answers, correct: correct_answers, text_input: user_answer_text)
else
current_user_response.update(answer: user_answers, correct: correct_answers)
end
Expand All @@ -55,6 +59,10 @@ def user_answers
Array(response_params[:answers]).compact_blank.map(&:to_i)
end

def user_answer_text
response_params[:text_input]
end

def redirect
assessment.grade! if content.last_assessment?

Expand All @@ -73,7 +81,8 @@ def track_question_answer
mod_uid: mod.id,
type: content.question_type,
success: current_user_response.correct?,
answers: current_user_response.answers)
answers: current_user_response.answers,
text_input: user_answer_text)
else
track('questionnaire_answer',
uid: content.id,
Expand Down
1 change: 1 addition & 0 deletions app/decorators/module_overview_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def sections
display_line: position != mod.submodule_count,
icon: status(content_items),
subsections: subsections(submodule: submodule, items: content_items),
hide: content_items.first.opinion_intro?,
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
}
end
end
Expand Down
11 changes: 10 additions & 1 deletion app/decorators/next_page_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def text
case
when next? then label[:next]
when missing? then label[:missing]
when content.section? then label[:section]
when content_section? then label[:section]
when opinion_intro? then label[:give_feedback] # Make confidence outro
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
when test_start? then label[:start_test]
when test_finish? then label[:finish_test]
when finish? then label[:finish]
Expand Down Expand Up @@ -99,6 +100,14 @@ def missing?
content.next_item.eql?(content) && wip?
end

def opinion_intro?
content.opinion_intro?
end

def content_section?
content.section? && !content.opinion_intro?
end

# @return [Boolean]
def wip?
Rails.application.preview? || Rails.env.test?
Expand Down
18 changes: 16 additions & 2 deletions app/decorators/pagination_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ def heading

# @return [String]
def section_numbers
I18n.t(:section, scope: :pagination, current: content.submodule, total: section_total)
if content.opinion_intro? || content.opinion_question?
I18n.t(:section, scope: :pagination, current: content.submodule - 1, total: section_total - 1)
else
I18n.t(:section, scope: :pagination, current: content.submodule, total: section_total - 1)
end
end

# @return [String]
Expand All @@ -37,7 +41,17 @@ def current_page

# @return [Integer]
def page_total
content.section_content.size
size = content.section_content.size
peterdavidhamilton marked this conversation as resolved.
Show resolved Hide resolved
if content.section_content.any?(&:skippable?) # && response_for_shared.responded?
# don't count skipped page
content.section_content.each do |section_content|
if section_content.opinion_question? && section_content.always_show_question.eql?(false)
size -= 1
end
end
end

size
end

# @return [Integer]
Expand Down
10 changes: 10 additions & 0 deletions app/forms/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ def question_radio_button(option)
checked: option.checked?
end

# @param option [Training::Answer::Option]
def opinion_radio_button(option)
martikat marked this conversation as resolved.
Show resolved Hide resolved
govuk_radio_button :answers,
option.id,
label: { text: option.label },
link_errors: true,
disabled: option.disabled?,
checked: option.checked?
end

# @param option [Training::Answer::Option]
def question_check_box(option)
govuk_check_box :answers,
Expand Down
Loading
Loading