rethinking question CRUD

This commit is contained in:
Mark Moser 2016-07-31 18:54:12 -05:00
parent ace9b864d3
commit dfd582d251
11 changed files with 172 additions and 74 deletions

View File

@ -41,7 +41,7 @@ Style/StringLiterals:
Metrics/AbcSize: Metrics/AbcSize:
Exclude: Exclude:
- db/migrate/**/* - db/migrate/**/*
Max: 27 Max: 18
Metrics/LineLength: Metrics/LineLength:
Max: 110 Max: 110
@ -54,4 +54,4 @@ Metrics/LineLength:
Metrics/MethodLength: Metrics/MethodLength:
Exclude: Exclude:
- db/migrate/* - db/migrate/*
- app/controllers/* # - app/controllers/*

View File

@ -28,4 +28,8 @@ class ApplicationController < ActionController::Base
def authorize_reviewer def authorize_reviewer
redirect_to review_login_path unless current_reviewer redirect_to review_login_path unless current_reviewer
end end
def authorize_candidate
redirect_to welcome_path unless current_candidate
end
end end

View File

@ -1,33 +1,30 @@
class CandidateController < ApplicationController class CandidateController < ApplicationController
before_action :authorize_candidate, except: [:welcome, :validate]
def welcome def welcome
end end
def saved
end
def thankyou
redirect_to root_path if session[:test_id].nil?
reset_session
end
def question def question
@status = QuizStatus.new(current_candidate) @status = QuizStatus.new(current_candidate)
qid = @status.current_question_id qid = @status.current_question_id
redirect_to :summary and return if qid.nil? redirect_to :summary and return if qid.nil?
@question = current_candidate.fetch_question(qid) prep_question qid
@answer = Answer.new @answer = Answer.new
end end
def update_answer def live_coder
answer_ids = { question_id: answer_params[:question_id], candidate_id: current_candidate.to_i } question
@answer = Answer.find_or_create_by!(answer_ids) render layout: false
@answer.answer = answer_for_type
@answer.saved = answer_params[:save]
@answer.submitted = answer_params[:next]
if @answer.save
redirect_to :summary and return if params.key?(:update)
redirect_to :saved and return if params.key?(:save)
redirect_to :question
else
flash[:error] = [answer_params[:question_id]]
@question = current_candidate.fetch_question(qid)
render :question
end
end end
def summary def summary
@ -37,50 +34,82 @@ class CandidateController < ApplicationController
redirect_to :question and return unless @status.current_question_id.nil? redirect_to :question and return unless @status.current_question_id.nil?
end end
def update_text
@answer = prep_answer
@answer.update(answer: answer_params[:text],
saved: answer_params[:save],
submitted: answer_params[:next])
validate_answer
end
def update_radio
@answer = prep_answer
@answer.update(answer: answer_params[:radio],
saved: answer_params[:save],
submitted: answer_params[:next])
validate_answer
end
def update_checkbox
@answer = prep_answer
@answer.update(answer: answer_params[:checkbox],
saved: answer_params[:save],
submitted: answer_params[:next])
validate_answer
end
def update_live_code
@answer = prep_answer
@answer.update(answer: answer_params[:live_code],
saved: answer_params[:save],
submitted: answer_params[:next])
validate_answer
end
# TODO
def update_summary def update_summary
redirect_to :summary # redirect_to :summary
end
def thankyou
redirect_to root_path if session[:test_id].nil?
reset_session
end
def saved
end end
def validate def validate
candidate = Candidate.find_by(test_hash: params['test_id']) candidate = Candidate.find_by(test_hash: params['test_id'])
redirect_to(root_path, alert: "Sorry, incorrect test id") and return if candidate.nil?
if candidate.nil? session[:test_id] = candidate.test_hash
reset_session redirect_to :thankyou and return if candidate.completed?
redirect_to root_path, alert: "Sorry, incorrect test id" redirect_to :question
else
session[:test_id] = candidate.test_hash
redirect_to :question
end
end
def live_coder
@question = Question.find(params[:question_id])
@answer = @question.answers.order("RAND()").first.answer
render layout: false
end end
private private
def prep_question qid
@question = current_candidate.fetch_question(qid)
end
def answer_params def answer_params
params.require(:answer).permit( params.require(:answer).permit(
:question_id, :answer_id, :question_id, :answer_id,
:save, :next, :summary, :save, :next, :summary,
:radio, :text, checkbox: [], live_coder: [] :radio, :text, checkbox: [], live_code: []
) )
end end
def answer_for_type def prep_answer
return answer_params[:radio] unless answer_params[:radio].nil? answer_ids = { question_id: answer_params[:question_id], candidate_id: current_candidate.to_i }
return answer_params[:text] unless answer_params[:text].nil? answer = Answer.find_or_create_by(answer_ids)
return answer_params[:checkbox] unless answer_params[:checkbox].nil? answer
return answer_params[:live_coder] unless answer_params[:live_coder].nil? end
def validate_answer
if @answer.errors.present?
flash[:error] = [answer_params[:question_id]]
prep_question answer_params[:question_id]
render :question
else
# TODO: change params.key? to submit = save/next/summary
redirect_to :summary and return if params.key?(:update)
redirect_to :saved and return if params.key?(:save)
redirect_to :question
end
end end
end end

View File

@ -5,7 +5,7 @@
content_for :footer_title, "Skills Assessment" content_for :footer_title, "Skills Assessment"
%> %>
<%= form_for(@answer, url: post_answer_path(@answer.id), html:{id: 'answer-form', data: {qid: @question.question_id}}) do |form| %> <%= form_for(@answer, url: send("post_#{@question.input_type}_path", @answer.id), html:{id: 'answer-form', data: {qid: @question.question_id}}) do |form| %>
<main class="questions_tpl"> <main class="questions_tpl">
<h2 class="question-text"><%= @question.question %></h2> <h2 class="question-text"><%= @question.question %></h2>

View File

@ -1,28 +1,31 @@
Rails.application.routes.draw do Rails.application.routes.draw do
post "/validate", to: "candidate#validate", as: :validate_candidate post "/validate", to: "candidate#validate", as: :validate_candidate
get "/welcome", to: "candidate#welcome", as: :welcome get "/welcome", to: "candidate#welcome", as: :welcome
get "/thankyou", to: "candidate#thankyou", as: :thankyou get "/thankyou", to: "candidate#thankyou", as: :thankyou
get "/saved", to: "candidate#saved", as: :saved get "/saved", to: "candidate#saved", as: :saved
post "/question(/:answer_id)", to: "candidate#update_answer", as: :post_answer post "/question/text(/:answer_id)", to: "candidate#update_text", as: :post_text
get "/question(/:question_id)", to: "candidate#question", as: :question post "/question/radio(/:answer_id)", to: "candidate#update_radio", as: :post_radio
get "/live-coder-entry/:question_id", to: "candidate#live_coder", as: :live_coder post "/question/checkbox(/:answer_id)", to: "candidate#update_checkbox", as: :post_checkbox
post "/question/live-code(/:answer_id)", to: "candidate#update_live_code", as: :post_live_code
get "/question(/:question_id)", to: "candidate#question", as: :question
get "/live-coder-entry/:question_id", to: "candidate#live_coder", as: :live_coder
post "/summary", to: "candidate#update_summary", as: :post_summary post "/summary", to: "candidate#update_summary", as: :post_summary
get "/summary", to: "candidate#summary", as: :summary get "/summary", to: "candidate#summary", as: :summary
get "/review/logout", to: "review#logout", as: :review_logout get "/review/logout", to: "review#logout", as: :review_logout
post "/review/login", to: "review#auth", as: :review_auth post "/review/login", to: "review#auth", as: :review_auth
get "/review/login", to: "review#login", as: :review_login get "/review/login", to: "review#login", as: :review_login
get "/review", to: "review#index", as: :review get "/review", to: "review#index", as: :review
get "/review/:test_hash", to: "review#view", as: :review_test get "/review/:test_hash", to: "review#view", as: :review_test
get "/recruiter", to: "recruiter#index", as: :recruiter get "/recruiter", to: "recruiter#index", as: :recruiter
get "/recruiter/new-candidate", to: "recruiter#new", as: :new_candidate get "/recruiter/new-candidate", to: "recruiter#new", as: :new_candidate
post "/recruiter/new-candidate", to: "recruiter#create", as: :create_candidate post "/recruiter/new-candidate", to: "recruiter#create", as: :create_candidate
get "/recruiter/logout", to: "recruiter#logout", as: :recruiter_logout get "/recruiter/logout", to: "recruiter#logout", as: :recruiter_logout
get "/recruiter/login", to: "recruiter#login", as: :recruiter_login get "/recruiter/login", to: "recruiter#login", as: :recruiter_login
post "/recruiter/login", to: "recruiter#auth", as: :recruiter_auth post "/recruiter/login", to: "recruiter#auth", as: :recruiter_auth
root to: "candidate#welcome" root to: "candidate#welcome"

View File

@ -6,4 +6,12 @@ select c.id candidate_id
from candidates c from candidates c
inner join questions q on q.quiz_id = c.quiz_id inner join questions q on q.quiz_id = c.quiz_id
left join answers a on a.candidate_id = c.id AND a.question_id = q.id left join answers a on a.candidate_id = c.id AND a.question_id = q.id
where c.test_hash = 'R67PmfDHGiw' -- and q.input_type = 'radio'
order by c.name, q.sort; order by c.name, q.sort;
-- delete from answers where id = 1008398109

View File

@ -0,0 +1,54 @@
require 'test_helper'
class CandidateControllerTest < ActionDispatch::IntegrationTest
def setup_auth candidate
post validate_candidate_url, params: { test_id: candidate.test_hash }
end
test "should get login" do
get welcome_path
assert_response :success
end
test "should require auth or redirect" do
get saved_path
assert_redirected_to welcome_path
get thankyou_path
assert_redirected_to welcome_path
get summary_path
assert_redirected_to welcome_path
get question_path
assert_redirected_to welcome_path
get question_path(questions(:fed1).id)
assert_redirected_to welcome_path
get live_coder_path(questions(:fed1).id)
assert_redirected_to welcome_path
end
test "should auth to question" do
setup_auth candidates(:martha)
assert_redirected_to question_path
assert session[:test_id].present?
end
test "should redirect to thankyou when completed" do
setup_auth candidates(:richard)
assert_redirected_to thankyou_path
end
test "should get summary if complete but not submitted" do
setup_auth candidates(:dawn)
get summary_url
assert_response :success
end
# should get flash message on bad question
end

View File

@ -39,7 +39,7 @@ dawn2:
dawn3: dawn3:
candidate: dawn candidate: dawn
question: fed3 question: fed3
answer: {html: "<h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''} answer: {html: "dawn3 <h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 50.minutes %> created_at: <%= DateTime.now() - 38.hours - 50.minutes %>
@ -75,7 +75,7 @@ dawn6:
dawn7: dawn7:
candidate: dawn candidate: dawn
question: fed7 question: fed7
answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} answer: {html: 'dawn7 <p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 34.minutes %> created_at: <%= DateTime.now() - 38.hours - 34.minutes %>
@ -129,7 +129,7 @@ richard2:
richard3: richard3:
candidate: richard candidate: richard
question: fed3 question: fed3
answer: {html: '<h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: ''} answer: {html: 'richard3 <h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: ''}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 36.hours - 26.minutes %> created_at: <%= DateTime.now() - 36.hours - 26.minutes %>
@ -165,7 +165,7 @@ richard6:
richard7: richard7:
candidate: richard candidate: richard
question: fed7 question: fed7
answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} answer: {html: 'richard7 <p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 36.hours - 34.minutes %> created_at: <%= DateTime.now() - 36.hours - 34.minutes %>

View File

@ -22,7 +22,7 @@ fed3:
quiz: fed quiz: fed
question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area? question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area?
category: CSS category: CSS
input_type: live-coder input_type: live_code
input_options: input_options:
sort: 2 sort: 2
active: true active: true
@ -58,7 +58,7 @@ fed7:
quiz: fed quiz: fed
question: Provide a code example to manipulate the DOM using jQuery/JavaScript to change the classname of a div 'classB' to 'classC', only if the div 'classA' exists in the page? question: Provide a code example to manipulate the DOM using jQuery/JavaScript to change the classname of a div 'classB' to 'classC', only if the div 'classA' exists in the page?
category: Javascript category: Javascript
input_type: live-coder input_type: live_code
input_options: input_options:
sort: 6 sort: 6
active: true active: true