From bcb840c986a7c26db842e7e49645a2cb272fabe0 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 28 Nov 2016 15:40:25 -0600 Subject: [PATCH 01/46] completes #84 : interview request status tweaks --- app/models/candidate.rb | 5 ++++ app/views/admin/candidate/index.html.erb | 4 ++-- app/views/admin/result/_voting.html.erb | 29 +++++++++++++----------- app/views/admin/result/index.html.erb | 4 ++-- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/models/candidate.rb b/app/models/candidate.rb index f75a738..21e6ac8 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -23,6 +23,11 @@ class Candidate < ApplicationRecord declined: 2 } + def interview? + return 'yes' if approved? + 'no' if declined? + end + def build_reviews reviewers.each do |reviewer| votes.find_or_create_by(user_id: reviewer.id) diff --git a/app/views/admin/candidate/index.html.erb b/app/views/admin/candidate/index.html.erb index 67f464b..e2e2302 100644 --- a/app/views/admin/candidate/index.html.erb +++ b/app/views/admin/candidate/index.html.erb @@ -16,7 +16,7 @@ Progress Completed Reminded - Interview Request + Interview? <% @candidates.each do |candidate| %> @@ -32,7 +32,7 @@ <%= candidate.status %> <%= candidate.completed ? link_to("Submitted", admin_result_path(candidate.test_hash)) : "" %> <%= candidate.reminded ? "Yes" : "" %> - <%= candidate.review_status unless candidate.pending? %> + <%= candidate.interview? %> <% end %> diff --git a/app/views/admin/result/_voting.html.erb b/app/views/admin/result/_voting.html.erb index bc04725..3c89df9 100644 --- a/app/views/admin/result/_voting.html.erb +++ b/app/views/admin/result/_voting.html.erb @@ -1,4 +1,4 @@ -<% # TODO: This needs to be extracted into a decorator, or something. It is only a quick hack solution. %> +<% # TODO: This should be extracted into a decorator, or something. It is only a quick hack solution. %> <% if current_user.acts_as_reviewer? %>
@@ -15,19 +15,22 @@ <% if current_user.acts_as_manager? %>
- Manager Vetos: - <%= link_to admin_approve_vote_path(test_hash: @candidate.test_hash), remote: true do %> - - <%= @candidate.approved? ? "Requested" : "Request Interview" %> - - <% end %> - <%= link_to admin_decline_vote_path(test_hash: @candidate.test_hash), remote: true do %> - - <%= @candidate.declined? ? "Declined" : "Decline Interview" %> - - <% end %> -
+ Interview: + <% if !@candidate.approved? %> + <%= link_to admin_approve_vote_path(test_hash: @candidate.test_hash), remote: true do %> + Yes + <% end %> + <% else %>Yes<% end %> + + + <% if !@candidate.declined? %> + <%= link_to admin_decline_vote_path(test_hash: @candidate.test_hash), remote: true do %> + No + <% end %> + <% else %>No<% end %> + +
<% else %> Candidate Interview Status: <%= @candidate.review_status %> diff --git a/app/views/admin/result/index.html.erb b/app/views/admin/result/index.html.erb index 79ed6de..dd05923 100644 --- a/app/views/admin/result/index.html.erb +++ b/app/views/admin/result/index.html.erb @@ -8,7 +8,7 @@ Test ID Experience Recruiter - Interview Request + Interview? <% @candidates.each do |candidate| %> @@ -16,7 +16,7 @@ <%= link_to candidate.test_hash, admin_result_path(candidate.test_hash) %> <%= candidate.experience %> years <%= mail_to(candidate.recruiter.email) %> - <%= candidate.review_status unless candidate.pending? %> + <%= candidate.interview? %> <% end %> From fa27aac0833bf88408c76c2fd2ff4e3d3709cbb2 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 30 Nov 2016 18:55:27 -0600 Subject: [PATCH 02/46] recruiter notification on interview status change re: #85 --- app/controllers/admin/vote_controller.rb | 32 +++++++++++-------- app/mailers/recruiter_mailer.rb | 20 ++++++++++-- .../interview_declined.html.inky | 6 ++++ .../interview_declined.text.erb | 5 +++ .../interview_requested.html.inky | 6 ++++ .../interview_requested.text.erb | 5 +++ .../controllers/admin/vote_controller_test.rb | 21 ++++++++++-- .../previews/recruiter_mailer_preview.rb | 8 +++++ test/mailers/recruiter_mailer_test.rb | 18 +++++++++++ 9 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 app/views/recruiter_mailer/interview_declined.html.inky create mode 100644 app/views/recruiter_mailer/interview_declined.text.erb create mode 100644 app/views/recruiter_mailer/interview_requested.html.inky create mode 100644 app/views/recruiter_mailer/interview_requested.text.erb diff --git a/app/controllers/admin/vote_controller.rb b/app/controllers/admin/vote_controller.rb index 3a39bb2..45cbd4e 100644 --- a/app/controllers/admin/vote_controller.rb +++ b/app/controllers/admin/vote_controller.rb @@ -32,27 +32,31 @@ module Admin def approve @candidate = Candidate.find_by(test_hash: params[:test_hash]) authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id) - current_user.approve_candidate(@candidate) - results = { - message: "Interview requested!", - requestCopy: "Requested", - declineCopy: "Decline Interview" - } - render json: results.to_json + if current_user.approve_candidate(@candidate) + RecruiterMailer.interview_requested(@candidate).deliver_later + results = { + message: "Interview requested!", + requestCopy: "Requested", + declineCopy: "Decline Interview" + } + render json: results.to_json + end end def decline @candidate = Candidate.find_by(test_hash: params[:test_hash]) authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id) - current_user.decline_candidate(@candidate) - results = { - message: "Interview declined.", - requestCopy: "Request Interview", - declineCopy: "Declined" - } - render json: results.to_json + if current_user.decline_candidate(@candidate) + RecruiterMailer.interview_declined(@candidate).deliver_later + results = { + message: "Interview declined.", + requestCopy: "Request Interview", + declineCopy: "Declined" + } + render json: results.to_json + end end end end diff --git a/app/mailers/recruiter_mailer.rb b/app/mailers/recruiter_mailer.rb index 8ef985c..8d9e66c 100644 --- a/app/mailers/recruiter_mailer.rb +++ b/app/mailers/recruiter_mailer.rb @@ -3,12 +3,28 @@ class RecruiterMailer < ApplicationMailer def candidate_created candidate @candidate = candidate - mail to: @candidate.recruiter.email, subject: "Skills Assessment Test - #{candidate.name}" + mail to: @candidate.recruiter.email, + subject: "Skills Assessment Test - #{candidate.name}" end def candidate_submitted candidate @candidate = candidate - mail to: @candidate.recruiter.email, subject: "Skills Assessment Test - #{candidate.name}" + mail to: @candidate.recruiter.email, + subject: "Skills Assessment Test - #{candidate.name}" + end + + def interview_requested candidate + @candidate = candidate + + mail to: @candidate.recruiter.email, + subject: "Skills Assesment - Interview Request for #{candidate.name}" + end + + def interview_declined candidate + @candidate = candidate + + mail to: @candidate.recruiter.email, + subject: "Skills Assesment - Interview Declined for #{candidate.name}" end end diff --git a/app/views/recruiter_mailer/interview_declined.html.inky b/app/views/recruiter_mailer/interview_declined.html.inky new file mode 100644 index 0000000..1dd0d42 --- /dev/null +++ b/app/views/recruiter_mailer/interview_declined.html.inky @@ -0,0 +1,6 @@ + + + diff --git a/app/views/recruiter_mailer/interview_declined.text.erb b/app/views/recruiter_mailer/interview_declined.text.erb new file mode 100644 index 0000000..1ef40ae --- /dev/null +++ b/app/views/recruiter_mailer/interview_declined.text.erb @@ -0,0 +1,5 @@ +PERFICIENT/digital - Skills Assessment Test + +The team has declined an interview with <%= @candidate.name %>. + +Thank you. diff --git a/app/views/recruiter_mailer/interview_requested.html.inky b/app/views/recruiter_mailer/interview_requested.html.inky new file mode 100644 index 0000000..bbaa0df --- /dev/null +++ b/app/views/recruiter_mailer/interview_requested.html.inky @@ -0,0 +1,6 @@ + + + diff --git a/app/views/recruiter_mailer/interview_requested.text.erb b/app/views/recruiter_mailer/interview_requested.text.erb new file mode 100644 index 0000000..ca5d7f3 --- /dev/null +++ b/app/views/recruiter_mailer/interview_requested.text.erb @@ -0,0 +1,5 @@ +PERFICIENT/digital - Skills Assessment Test + +The team has requested an interview with <%= @candidate.name %>. + +Thank you. diff --git a/test/controllers/admin/vote_controller_test.rb b/test/controllers/admin/vote_controller_test.rb index f05e4e2..303f7d4 100644 --- a/test/controllers/admin/vote_controller_test.rb +++ b/test/controllers/admin/vote_controller_test.rb @@ -3,6 +3,8 @@ require 'test_helper' module Admin class VoteControllerTest < ActionDispatch::IntegrationTest + include ActiveJob::TestHelper + test "reviewer can up vote henry" do auth_user users(:reviewer) henry = candidates(:henry) @@ -42,18 +44,33 @@ module Admin get admin_approve_vote_url(henry.test_hash) assert_equal 1, henry.votes.approved.count - assert_equal 'approved', Candidate.find(henry.to_i).review_status + assert_equal 'approved', Candidate.find(henry.to_i).review_status, xhr: true assert_response :success + data = JSON.parse(response.body) + assert_match 'requested', data["message"] end test "manager can decline henry" do auth_user users(:manager) henry = candidates(:henry) - get admin_decline_vote_url(henry.test_hash) + get admin_decline_vote_url(henry.test_hash), xhr: true assert_equal 1, henry.votes.rejected.count assert_equal 'declined', Candidate.find(henry.to_i).review_status assert_response :success + data = JSON.parse(response.body) + assert_match 'declined', data["message"] + end + + test "should queue up a notification when manager approves henry" do + auth_user users(:manager) + henry = candidates(:henry) + assert_enqueued_jobs 1 do + get admin_approve_vote_url(henry.test_hash), xhr: true + end + assert_response :success + data = JSON.parse(response.body) + assert_match 'requested', data["message"] end end end diff --git a/test/mailers/previews/recruiter_mailer_preview.rb b/test/mailers/previews/recruiter_mailer_preview.rb index 9a14fb3..257f254 100644 --- a/test/mailers/previews/recruiter_mailer_preview.rb +++ b/test/mailers/previews/recruiter_mailer_preview.rb @@ -8,4 +8,12 @@ class RecruiterMailerPreview < ActionMailer::Preview def candidate_submitted RecruiterMailer.candidate_submitted Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn end + + def interview_requested + RecruiterMailer.interview_requested Candidate.find_by(test_hash: '6NjnourLE6Y') # Richard + end + + def interview_declined + RecruiterMailer.interview_declined Candidate.find_by(test_hash: 's6oFExZliYYFx') # Stacy + end end diff --git a/test/mailers/recruiter_mailer_test.rb b/test/mailers/recruiter_mailer_test.rb index 5331ca9..f51345f 100644 --- a/test/mailers/recruiter_mailer_test.rb +++ b/test/mailers/recruiter_mailer_test.rb @@ -19,4 +19,22 @@ class RecruiterMailerTest < ActionMailer::TestCase assert_equal [ENV["default_mail_from"]], mail.from assert_match candidate.name, mail.body.encoded end + + test "interview_requested" do + candidate = candidates :richard + mail = RecruiterMailer.interview_requested candidate + assert_match candidate.name, mail.subject + assert_equal [candidate.recruiter.email], mail.to + assert_equal [ENV["default_mail_from"]], mail.from + assert_match candidate.name, mail.body.encoded + end + + test "interview_declined" do + candidate = candidates :stacy + mail = RecruiterMailer.interview_declined candidate + assert_match candidate.name, mail.subject + assert_equal [candidate.recruiter.email], mail.to + assert_equal [ENV["default_mail_from"]], mail.from + assert_match candidate.name, mail.body.encoded + end end From bc31f6cf17edc118647fbcad2be8bc792dd40b9e Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 30 Nov 2016 19:00:42 -0600 Subject: [PATCH 03/46] normalise some json responses --- app/controllers/admin/candidate_controller.rb | 1 - app/controllers/admin/vote_controller.rb | 28 ------------------- .../admin/candidate/resend_welcome.json.erb | 1 + app/views/admin/vote/approve.json.erb | 5 ++++ app/views/admin/vote/decline.json.erb | 5 ++++ app/views/admin/vote/down.json.erb | 6 ++++ app/views/admin/vote/up.json.erb | 6 ++++ config/routes.rb | 10 +++---- 8 files changed, 28 insertions(+), 34 deletions(-) create mode 100644 app/views/admin/candidate/resend_welcome.json.erb create mode 100644 app/views/admin/vote/approve.json.erb create mode 100644 app/views/admin/vote/decline.json.erb create mode 100644 app/views/admin/vote/down.json.erb create mode 100644 app/views/admin/vote/up.json.erb diff --git a/app/controllers/admin/candidate_controller.rb b/app/controllers/admin/candidate_controller.rb index 5a95b68..843f762 100644 --- a/app/controllers/admin/candidate_controller.rb +++ b/app/controllers/admin/candidate_controller.rb @@ -49,7 +49,6 @@ module Admin authorize Candidate candidate = Candidate.find_by(id: params[:id]) CandidateMailer.welcome(candidate).deliver_later - render json: { message: "Email queued!" }.to_json end private diff --git a/app/controllers/admin/vote_controller.rb b/app/controllers/admin/vote_controller.rb index 45cbd4e..acf2008 100644 --- a/app/controllers/admin/vote_controller.rb +++ b/app/controllers/admin/vote_controller.rb @@ -5,28 +5,12 @@ module Admin @candidate = Candidate.find_by(test_hash: params[:test_hash]) authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id) current_user.cast_yea_on(@candidate) - - results = { - message: "Vote Counted", - upCount: @candidate.votes.yea.count, - downCount: @candidate.votes.nay.count, - myVote: "yea" - } - render json: results.to_json end def down @candidate = Candidate.find_by(test_hash: params[:test_hash]) authorize ReviewerVote.find_by(user_id: current_user.id, candidate_id: @candidate.id) current_user.cast_nay_on(@candidate) - - results = { - message: "Vote Counted", - upCount: @candidate.votes.yea.count, - downCount: @candidate.votes.nay.count, - myVote: "nay" - } - render json: results.to_json end def approve @@ -35,12 +19,6 @@ module Admin if current_user.approve_candidate(@candidate) RecruiterMailer.interview_requested(@candidate).deliver_later - results = { - message: "Interview requested!", - requestCopy: "Requested", - declineCopy: "Decline Interview" - } - render json: results.to_json end end @@ -50,12 +28,6 @@ module Admin if current_user.decline_candidate(@candidate) RecruiterMailer.interview_declined(@candidate).deliver_later - results = { - message: "Interview declined.", - requestCopy: "Request Interview", - declineCopy: "Declined" - } - render json: results.to_json end end end diff --git a/app/views/admin/candidate/resend_welcome.json.erb b/app/views/admin/candidate/resend_welcome.json.erb new file mode 100644 index 0000000..fbba8af --- /dev/null +++ b/app/views/admin/candidate/resend_welcome.json.erb @@ -0,0 +1 @@ +{ "message" : "Email queued!" } diff --git a/app/views/admin/vote/approve.json.erb b/app/views/admin/vote/approve.json.erb new file mode 100644 index 0000000..1aa0072 --- /dev/null +++ b/app/views/admin/vote/approve.json.erb @@ -0,0 +1,5 @@ +{ + "message" : "Interview requested!", + "requestCopy" : "Requested", + "declineCopy" : "Decline Interview" +} diff --git a/app/views/admin/vote/decline.json.erb b/app/views/admin/vote/decline.json.erb new file mode 100644 index 0000000..6d98faf --- /dev/null +++ b/app/views/admin/vote/decline.json.erb @@ -0,0 +1,5 @@ +{ + "message" : "Interview declined.", + "requestCopy" : "Request Interview", + "declineCopy" : "Declined" +} diff --git a/app/views/admin/vote/down.json.erb b/app/views/admin/vote/down.json.erb new file mode 100644 index 0000000..fe06c20 --- /dev/null +++ b/app/views/admin/vote/down.json.erb @@ -0,0 +1,6 @@ +{ + "message" : "Vote Counted", + "upCount" : <%= @candidate.votes.yea.count %>, + "downCount" : <%= @candidate.votes.nay.count %>, + "myVote" : "nay" +} diff --git a/app/views/admin/vote/up.json.erb b/app/views/admin/vote/up.json.erb new file mode 100644 index 0000000..605d7e1 --- /dev/null +++ b/app/views/admin/vote/up.json.erb @@ -0,0 +1,6 @@ +{ + "message" : "Vote Counted", + "upCount" : <%= @candidate.votes.yea.count %>, + "downCount" : <%= @candidate.votes.nay.count %>, + "myVote" : "yea" +} diff --git a/config/routes.rb b/config/routes.rb index 691f4c5..0fbe80c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,15 +45,15 @@ Rails.application.routes.draw do post "/admin/candidate/new", to: "admin/candidate#create", as: :admin_create_candidate get "/admin/candidate/:id", to: "admin/candidate#edit", as: :admin_edit_candidate post "/admin/candidate/:id", to: "admin/candidate#update", as: :admin_update_candidate - get "/admin/candidate/:id/resend", to: "admin/candidate#resend_welcome", as: :admin_resend_welcome + get "/admin/candidate/:id/resend", to: "admin/candidate#resend_welcome", as: :admin_resend_welcome, defaults: { format: 'json' } get "/admin/results", to: "admin/result#index", as: :admin_results get "/admin/result/:test_hash", to: "admin/result#view", as: :admin_result - get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote - get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote - get "admin/vote/:test_hash/approve", to: "admin/vote#approve", as: :admin_approve_vote - get "admin/vote/:test_hash/decline", to: "admin/vote#decline", as: :admin_decline_vote + get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote, defaults: { format: 'json' } + get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote, defaults: { format: 'json' } + get "admin/vote/:test_hash/approve", to: "admin/vote#approve", as: :admin_approve_vote, defaults: { format: 'json' } + get "admin/vote/:test_hash/decline", to: "admin/vote#decline", as: :admin_decline_vote, defaults: { format: 'json' } get "/admin", to: "admin/dashboard#show", as: :admin From 73df3d12587bbfa00337b40dd51e91ae437e607b Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Sun, 4 Dec 2016 16:19:16 -0600 Subject: [PATCH 04/46] lock voting when interview status not pending --- app/views/admin/result/_voting.html.erb | 19 ++++++++++++++----- .../20161204213828_remove_vote_locking.rb | 6 ++++++ db/schema.rb | 1 - 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20161204213828_remove_vote_locking.rb diff --git a/app/views/admin/result/_voting.html.erb b/app/views/admin/result/_voting.html.erb index 3c89df9..42316c5 100644 --- a/app/views/admin/result/_voting.html.erb +++ b/app/views/admin/result/_voting.html.erb @@ -3,12 +3,21 @@ <% if current_user.acts_as_reviewer? %>
Votes: - <%= link_to admin_up_vote_path(test_hash: @candidate.test_hash), remote: true do %> - Yea (<%= @candidate.votes.yea.count %>) - <% end %> - <%= link_to admin_down_vote_path(test_hash: @candidate.test_hash), remote: true do %> - Nay (<%= @candidate.votes.nay.count %>) + + <% if @candidate.pending? %> + <%= link_to admin_up_vote_path(test_hash: @candidate.test_hash), remote: true do %> + Yea (<%= @candidate.votes.yea.count %>) + <% end %> + + <%= link_to admin_down_vote_path(test_hash: @candidate.test_hash), remote: true do %> + Nay (<%= @candidate.votes.nay.count %>) + <% end %> + <% else %> + Voting closed - + Yea (<%= @candidate.votes.yea.count %>) - + Nay (<%= @candidate.votes.nay.count %>) <% end %> + (Your vote: <%= current_user.my_vote(@candidate) %>)
<% end %> diff --git a/db/migrate/20161204213828_remove_vote_locking.rb b/db/migrate/20161204213828_remove_vote_locking.rb new file mode 100644 index 0000000..b842e74 --- /dev/null +++ b/db/migrate/20161204213828_remove_vote_locking.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class RemoveVoteLocking < ActiveRecord::Migration[5.0] + def change + remove_column :reviewer_votes, :locked, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 2126b30..912be6d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -80,7 +80,6 @@ ActiveRecord::Schema.define(version: 20161120175737) do t.integer "vote", default: 0, null: false t.integer "veto", default: 0, null: false t.datetime "last_reminded" - t.boolean "locked", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["candidate_id", "user_id"], name: "index_reviewer_votes_on_candidate_id_and_user_id", unique: true, using: :btree From 9cf2aeb139ceaaad92df2618f530b36b4178e58f Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Sun, 4 Dec 2016 16:41:07 -0600 Subject: [PATCH 05/46] default result sorting to quiz submission descending completes #85 --- app/controllers/admin/result_controller.rb | 4 +++- app/controllers/quiz_controller.rb | 2 +- app/views/admin/result/index.html.erb | 2 ++ db/migrate/20161204220527_add_completed_at.rb | 12 ++++++++++++ db/schema.rb | 11 ++++++----- test/fixtures/candidates.yml | 4 ++++ 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20161204220527_add_completed_at.rb diff --git a/app/controllers/admin/result_controller.rb b/app/controllers/admin/result_controller.rb index e5c9564..867c922 100644 --- a/app/controllers/admin/result_controller.rb +++ b/app/controllers/admin/result_controller.rb @@ -10,7 +10,9 @@ module Admin # TODO: Limit results to the quizzes current_user has access to def index - @candidates = Candidate.where(completed: true).includes(:recruiter) + @candidates = Candidate.where(completed: true) + .includes(:recruiter) + .order(:review_status, completed_at: :desc) end def view diff --git a/app/controllers/quiz_controller.rb b/app/controllers/quiz_controller.rb index 4f4e35d..6ec5921 100644 --- a/app/controllers/quiz_controller.rb +++ b/app/controllers/quiz_controller.rb @@ -35,7 +35,7 @@ class QuizController < ApplicationController private def complete_and_email - if current_candidate.update_attributes(completed: true) + if current_candidate.update_attributes(completed: true, completed_at: DateTime.current) current_candidate.build_reviews CandidateMailer.submitted(current_candidate).deliver_later RecruiterMailer.candidate_submitted(current_candidate).deliver_later diff --git a/app/views/admin/result/index.html.erb b/app/views/admin/result/index.html.erb index dd05923..96ab9f4 100644 --- a/app/views/admin/result/index.html.erb +++ b/app/views/admin/result/index.html.erb @@ -8,6 +8,7 @@ Test ID Experience Recruiter + Submitted on Interview? @@ -16,6 +17,7 @@ <%= link_to candidate.test_hash, admin_result_path(candidate.test_hash) %> <%= candidate.experience %> years <%= mail_to(candidate.recruiter.email) %> + <%= candidate.completed_at.strftime('%D') unless candidate.completed_at.nil? %> <%= candidate.interview? %> <% end %> diff --git a/db/migrate/20161204220527_add_completed_at.rb b/db/migrate/20161204220527_add_completed_at.rb new file mode 100644 index 0000000..8315b00 --- /dev/null +++ b/db/migrate/20161204220527_add_completed_at.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +class AddCompletedAt < ActiveRecord::Migration[5.0] + def change + add_column :candidates, :completed_at, :datetime, after: :completed + + Candidate.reset_column_information + Candidate.where(completed: true).each do |candidate| + candidate.completed_at = candidate.updated_at + candidate.save + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 912be6d..dc15ecc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161120175737) do +ActiveRecord::Schema.define(version: 20161204220527) do create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| t.integer "candidate_id" @@ -32,6 +32,7 @@ ActiveRecord::Schema.define(version: 20161120175737) do t.string "experience" t.integer "recruiter_id" t.boolean "completed" + t.datetime "completed_at" t.boolean "reminded" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -77,11 +78,11 @@ ActiveRecord::Schema.define(version: 20161120175737) do create_table "reviewer_votes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| t.integer "candidate_id" t.integer "user_id" - t.integer "vote", default: 0, null: false - t.integer "veto", default: 0, null: false + t.integer "vote", default: 0, null: false + t.integer "veto", default: 0, null: false t.datetime "last_reminded" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["candidate_id", "user_id"], name: "index_reviewer_votes_on_candidate_id_and_user_id", unique: true, using: :btree end diff --git a/test/fixtures/candidates.yml b/test/fixtures/candidates.yml index 4def21b..354fc68 100644 --- a/test/fixtures/candidates.yml +++ b/test/fixtures/candidates.yml @@ -57,6 +57,7 @@ richard: # Richard has completed AND submitted the test recruiter: recruiter quiz: fed completed: true + completed_at: <%= DateTime.current %> reminded: false test_hash: 6NjnourLE6Y review_status: 1 @@ -78,6 +79,7 @@ stacy: # Stacy has completed AND submitted the test recruiter: recruiter quiz: fed completed: true + completed_at: <%= DateTime.current %> reminded: false test_hash: s6oFExZliYYFx review_status: 2 @@ -89,6 +91,7 @@ henry: # Henry has completed AND submitted the test recruiter: recruiter quiz: fed completed: true + completed_at: <%= DateTime.current %> reminded: false test_hash: egPomAuVDeCEp @@ -99,6 +102,7 @@ wade: # Wade has completed AND submitted the test recruiter: recruiter quiz: fed completed: true + completed_at: <%= DateTime.current %> reminded: false test_hash: BkSkpapJnkz2N From d75ba7ea56061bb94f8b1d1a6b45da1f36c0c929 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 12 Jan 2017 18:39:12 -0600 Subject: [PATCH 06/46] updated gems --- Gemfile | 2 +- Gemfile.lock | 152 ++++++++++++++++--------------- test/test_helper.rb | 1 + test/workers/quiz_status_test.rb | 2 +- 4 files changed, 80 insertions(+), 77 deletions(-) diff --git a/Gemfile b/Gemfile index bd5d9ab..64cb4fe 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' gem 'figaro', '~> 1.1.1' gem 'bcrypt', '~> 3.1.7' gem 'mysql2', '>= 0.3.18', '< 0.5' -gem 'rails', '~> 5.0', '>= 5.0.0.1' +gem 'rails', '~> 5.0', '>= 5.0.1' gem 'jbuilder', '~> 2.6' gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 589a2b6..fea4f78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,47 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.0.0.1) - actionpack (= 5.0.0.1) + actioncable (5.0.1) + actionpack (= 5.0.1) nio4r (~> 1.2) websocket-driver (~> 0.6.1) - actionmailer (5.0.0.1) - actionpack (= 5.0.0.1) - actionview (= 5.0.0.1) - activejob (= 5.0.0.1) + actionmailer (5.0.1) + actionpack (= 5.0.1) + actionview (= 5.0.1) + activejob (= 5.0.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.0.1) - actionview (= 5.0.0.1) - activesupport (= 5.0.0.1) + actionpack (5.0.1) + actionview (= 5.0.1) + activesupport (= 5.0.1) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.0.1) - activesupport (= 5.0.0.1) + actionview (5.0.1) + activesupport (= 5.0.1) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (5.0.0.1) - activesupport (= 5.0.0.1) + activejob (5.0.1) + activesupport (= 5.0.1) globalid (>= 0.3.6) - activemodel (5.0.0.1) - activesupport (= 5.0.0.1) - activerecord (5.0.0.1) - activemodel (= 5.0.0.1) - activesupport (= 5.0.0.1) + activemodel (5.0.1) + activesupport (= 5.0.1) + activerecord (5.0.1) + activemodel (= 5.0.1) + activesupport (= 5.0.1) arel (~> 7.0) - activesupport (5.0.0.1) + activesupport (5.0.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.4.0) + addressable (2.5.0) + public_suffix (~> 2.0, >= 2.0.2) ansi (1.5.0) - arel (7.1.1) + arel (7.1.4) ast (2.3.0) awesome_print (1.7.0) bcrypt (3.1.11) @@ -53,29 +54,29 @@ GEM bourbon (4.2.7) sass (~> 3.4) thor (~> 0.19) - brakeman (3.4.0) + brakeman (3.4.1) builder (3.2.2) - byebug (9.0.5) + byebug (9.0.6) choice (0.2.0) coderay (1.1.1) - concurrent-ruby (1.0.2) - css_parser (1.4.5) + concurrent-ruby (1.0.4) + css_parser (1.4.7) addressable debug_inspector (0.0.2) docile (1.1.5) - domain_name (0.5.20160615) + domain_name (0.5.20161129) unf (>= 0.0.5, < 1.0.0) em-websocket (0.5.1) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) erubis (2.7.0) - eventmachine (1.2.0.1) + eventmachine (1.2.1) execjs (2.7.0) ffi (1.9.14) figaro (1.1.1) thor (~> 0.14) formatador (0.2.5) - foundation_emails (2.2.0.0) + foundation_emails (2.2.1.0) globalid (0.3.7) activesupport (>= 4.1.0) guard (2.14.0) @@ -106,27 +107,28 @@ GEM guard (>= 2.0.0) guard-compat (~> 1.0) htmlentities (4.3.4) - http-cookie (1.0.2) + http-cookie (1.0.3) domain_name (~> 0.5) http_parser.rb (0.6.0) i18n (0.7.0) - inky-rb (1.3.6.1) + inky-rb (1.3.7.2) foundation_emails (~> 2) - jbuilder (2.6.0) + nokogiri + jbuilder (2.6.1) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) - jquery-rails (4.2.1) + jquery-rails (4.2.2) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.0.2) + json (2.0.3) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) loofah (2.0.3) nokogiri (>= 1.5.9) - lumberjack (1.0.10) + lumberjack (1.0.11) mail (2.6.4) mime-types (>= 1.16, < 4) mailjet (1.3.8) @@ -138,29 +140,27 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) - minitest (5.9.0) - minitest-reporters (1.1.11) + minitest (5.10.1) + minitest-reporters (1.1.13) ansi builder minitest (>= 5.0) ruby-progressbar multi_json (1.12.1) - mysql2 (0.4.4) + mysql2 (0.4.5) neat (1.8.0) sass (>= 3.3) thor (~> 0.19) nenv (0.3.0) netrc (0.11.0) nio4r (1.2.1) - nokogiri (1.6.8) + nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) - pkg-config (~> 1.1.7) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) - parser (2.3.1.2) + parser (2.3.3.1) ast (~> 2.2) - pkg-config (1.1.7) policy-assertions (0.0.3) activesupport (>= 3.0.0) pundit (>= 1.0.0) @@ -168,19 +168,20 @@ GEM premailer (1.8.7) css_parser (>= 1.4.5) htmlentities (>= 4.0.0) - premailer-rails (1.9.4) + premailer-rails (1.9.5) actionmailer (>= 3, < 6) premailer (~> 1.7, >= 1.7.9) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - pry-byebug (3.4.0) + pry-byebug (3.4.2) byebug (~> 9.0) pry (~> 0.10) pry-rails (0.3.4) pry (>= 0.9.10) - puma (3.6.0) + public_suffix (2.0.5) + puma (3.6.2) pundit (1.1.0) activesupport (>= 3.0.0) rack (2.0.1) @@ -188,25 +189,25 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (5.0.0.1) - actioncable (= 5.0.0.1) - actionmailer (= 5.0.0.1) - actionpack (= 5.0.0.1) - actionview (= 5.0.0.1) - activejob (= 5.0.0.1) - activemodel (= 5.0.0.1) - activerecord (= 5.0.0.1) - activesupport (= 5.0.0.1) + rails (5.0.1) + actioncable (= 5.0.1) + actionmailer (= 5.0.1) + actionpack (= 5.0.1) + actionview (= 5.0.1) + activejob (= 5.0.1) + activemodel (= 5.0.1) + activerecord (= 5.0.1) + activesupport (= 5.0.1) bundler (>= 1.3.0, < 2.0) - railties (= 5.0.0.1) + railties (= 5.0.1) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.1) actionpack (~> 5.x) actionview (~> 5.x) activesupport (~> 5.x) - rails-dom-testing (2.0.1) + rails-dom-testing (2.0.2) activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6) rails-erd (1.5.0) activerecord (>= 3.2) activesupport (>= 3.2) @@ -214,15 +215,15 @@ GEM ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (5.0.0.1) - actionpack (= 5.0.0.1) - activesupport (= 5.0.0.1) + railties (5.0.1) + actionpack (= 5.0.1) + activesupport (= 5.0.1) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.1.0) - rake (11.2.2) - rb-fsevent (0.9.7) + rainbow (2.2.1) + rake (12.0.0) + rb-fsevent (0.9.8) rb-inotify (0.9.7) ffi (>= 0.5.0) rest-client (2.0.0) @@ -237,8 +238,8 @@ GEM unicode-display_width (~> 1.0, >= 1.0.1) ruby-graphviz (1.2.2) ruby-progressbar (1.8.1) - ruby_dep (1.4.0) - sass (3.4.22) + ruby_dep (1.5.0) + sass (3.4.23) sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) @@ -253,18 +254,19 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slop (3.6.0) - spring (1.7.2) - spring-watcher-listen (2.0.0) + spring (2.0.0) + activesupport (>= 4.2) + spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) - spring (~> 1.2) - sprockets (3.7.0) + spring (>= 1.2, < 3.0) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.1.1) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - thor (0.19.1) + thor (0.19.4) thread_safe (0.3.5) tilt (2.0.5) turbolinks (5.0.1) @@ -272,13 +274,13 @@ GEM turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.2) + uglifier (3.0.4) execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.1) - web-console (3.3.1) + unicode-display_width (1.1.2) + web-console (3.4.0) actionview (>= 5.0) activemodel (>= 5.0) debug_inspector @@ -321,7 +323,7 @@ DEPENDENCIES puma (~> 3.0) pundit rack-livereload - rails (~> 5.0, >= 5.0.0.1) + rails (~> 5.0, >= 5.0.1) rails-controller-testing rails-erd rubocop (~> 0.42.0) @@ -336,4 +338,4 @@ DEPENDENCIES web-console BUNDLED WITH - 1.12.5 + 1.13.2 diff --git a/test/test_helper.rb b/test/test_helper.rb index 65a1a33..108c430 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -19,6 +19,7 @@ Dir[Rails.root.join("test/test_helpers/**/*.rb")].each { |f| require f } Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)] class ActiveSupport::TestCase + ActiveRecord::Migration.maintain_test_schema! ActiveRecord::Migration.check_pending! # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. diff --git a/test/workers/quiz_status_test.rb b/test/workers/quiz_status_test.rb index f79b299..ea52280 100644 --- a/test/workers/quiz_status_test.rb +++ b/test/workers/quiz_status_test.rb @@ -97,7 +97,7 @@ class QuizStatusTest < ActiveSupport::TestCase dawn = candidates :dawn status = QuizStatus.new dawn - assert_equal nil, status.current_question_id + assert_nil status.current_question_id end test "richard has no_finish_laters" do From 5c3691ebe00b7996b506e65c4beea349cc8e9a46 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 12 Jan 2017 19:03:05 -0600 Subject: [PATCH 07/46] neat routing visualizer trick --- config/routes.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 0fbe80c..e7a7558 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +# Neat routing visualizer, in irb: +# File.open('routing.html', 'w+'){|f| f.write Rails.application.routes.router.visualizer } Rails.application.routes.draw do get "/styleguide", to: "application#styleguide", as: :styleguide get "/admin/styleguide", to: "application#styleguide" From 58d3fbd7c244f0b8c10635bce339d52c76082399 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 8 Feb 2017 16:05:37 -0600 Subject: [PATCH 08/46] adds candidate recruitment project label field completes #93 --- app/controllers/admin/candidate_controller.rb | 2 +- app/models/candidate.rb | 1 + app/views/admin/candidate/_form.html.erb | 5 +++++ app/views/admin/result/index.html.erb | 2 ++ app/views/admin/result/view.html.erb | 1 + .../recruiter_mailer/candidate_created.html.inky | 3 ++- app/views/recruiter_mailer/candidate_created.text.erb | 1 + .../reviewer_mailer/candidate_submission.html.inky | 5 ++++- .../reviewer_mailer/candidate_submission.text.erb | 2 +- db/migrate/20170208212526_add_project_to_client.rb | 6 ++++++ db/schema.rb | 3 ++- .../admin/candidate_controller/new_candidate_test.rb | 9 +++++++-- test/fixtures/candidates.yml | 11 +++++++++++ test/models/candidate_test.rb | 2 ++ 14 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20170208212526_add_project_to_client.rb diff --git a/app/controllers/admin/candidate_controller.rb b/app/controllers/admin/candidate_controller.rb index 843f762..7564886 100644 --- a/app/controllers/admin/candidate_controller.rb +++ b/app/controllers/admin/candidate_controller.rb @@ -54,7 +54,7 @@ module Admin private def candidate_params - params.require(:candidate).permit(:name, :email, :experience, :quiz_id) + params.require(:candidate).permit(:name, :email, :experience, :quiz_id, :project) end def collect_quizzes diff --git a/app/models/candidate.rb b/app/models/candidate.rb index 21e6ac8..5d4df2b 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -14,6 +14,7 @@ class Candidate < ApplicationRecord validates :recruiter_id, presence: true validates :name, presence: true validates :experience, presence: true + validates :project, presence: true validates :email, uniqueness: true, presence: true, email_format: true validates :test_hash, uniqueness: true, presence: true diff --git a/app/views/admin/candidate/_form.html.erb b/app/views/admin/candidate/_form.html.erb index 0310471..6337130 100644 --- a/app/views/admin/candidate/_form.html.erb +++ b/app/views/admin/candidate/_form.html.erb @@ -16,6 +16,11 @@ <%= form.select :experience, experience_options(candidate.experience), include_blank: false %> +
+ <%= form.label :project, "Client or project" %> + <%= form.text_field :project %> +
+
<%= form.label :quiz_id, "Quiz" %> <%= form.select :quiz_id, quiz_options(quizzes, candidate.quiz_id), include_blank: (quizzes.size > 1) %> diff --git a/app/views/admin/result/index.html.erb b/app/views/admin/result/index.html.erb index 96ab9f4..6faa58f 100644 --- a/app/views/admin/result/index.html.erb +++ b/app/views/admin/result/index.html.erb @@ -7,6 +7,7 @@ Test ID Experience + Client/Project Recruiter Submitted on Interview? @@ -16,6 +17,7 @@ <%= link_to candidate.test_hash, admin_result_path(candidate.test_hash) %> <%= candidate.experience %> years + <%= candidate.project %> <%= mail_to(candidate.recruiter.email) %> <%= candidate.completed_at.strftime('%D') unless candidate.completed_at.nil? %> <%= candidate.interview? %> diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index 61863fa..d0452ca 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -9,6 +9,7 @@
Test ID: <%= @candidate.test_hash %>
Years of Experience: <%= @candidate.experience %>
+ Client/Project: <%= @candidate.project %>
Recruiter Email: <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %>
diff --git a/app/views/recruiter_mailer/candidate_created.html.inky b/app/views/recruiter_mailer/candidate_created.html.inky index 0a9cc9e..dcd04c7 100644 --- a/app/views/recruiter_mailer/candidate_created.html.inky +++ b/app/views/recruiter_mailer/candidate_created.html.inky @@ -7,8 +7,9 @@ Candidate email: <%= @candidate.email %>
Candidate ID: <%= @candidate.test_hash %>
Years of experience: <%= @candidate.experience %> Years
+ Client/Project: <%= @candidate.project %>

You will be notified when the candidate has finished taking the test.

- \ No newline at end of file + diff --git a/app/views/recruiter_mailer/candidate_created.text.erb b/app/views/recruiter_mailer/candidate_created.text.erb index 56b001b..c16e184 100644 --- a/app/views/recruiter_mailer/candidate_created.text.erb +++ b/app/views/recruiter_mailer/candidate_created.text.erb @@ -6,5 +6,6 @@ Candidate name: <%= @candidate.name %> Candidate email: <%= @candidate.email %> Candidate ID: <%= @candidate.test_hash %> Years of experience: <%= @candidate.experience %> Years +Client/Project: <%= @candidate.project %> You will be notified when the candidate has finished taking the test. diff --git a/app/views/reviewer_mailer/candidate_submission.html.inky b/app/views/reviewer_mailer/candidate_submission.html.inky index 1c5a20f..eeaed3d 100644 --- a/app/views/reviewer_mailer/candidate_submission.html.inky +++ b/app/views/reviewer_mailer/candidate_submission.html.inky @@ -1,6 +1,9 @@ diff --git a/app/views/reviewer_mailer/candidate_submission.text.erb b/app/views/reviewer_mailer/candidate_submission.text.erb index e81acc2..7120c2e 100644 --- a/app/views/reviewer_mailer/candidate_submission.text.erb +++ b/app/views/reviewer_mailer/candidate_submission.text.erb @@ -1,5 +1,5 @@ PERFICIENT/digital SKILLS ASSESSMENT RESULTS -Candidate <%= @candidate.test_hash %> has completed the Skills Assessment Test. +Candidate <%= @candidate.test_hash %> has completed the Skills Assessment Test for client/project <%= @candidate.project %>. You can view the results here: <%= admin_result_url(@candidate.test_hash) %>. diff --git a/db/migrate/20170208212526_add_project_to_client.rb b/db/migrate/20170208212526_add_project_to_client.rb new file mode 100644 index 0000000..e3adda1 --- /dev/null +++ b/db/migrate/20170208212526_add_project_to_client.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class AddProjectToClient < ActiveRecord::Migration[5.0] + def change + add_column :candidates, :project, :string, after: :experience + end +end diff --git a/db/schema.rb b/db/schema.rb index dc15ecc..5e5c4cc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161204220527) do +ActiveRecord::Schema.define(version: 20170208212526) do create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| t.integer "candidate_id" @@ -30,6 +30,7 @@ ActiveRecord::Schema.define(version: 20161204220527) do t.string "name" t.string "email" t.string "experience" + t.string "project" t.integer "recruiter_id" t.boolean "completed" t.datetime "completed_at" diff --git a/test/controllers/admin/candidate_controller/new_candidate_test.rb b/test/controllers/admin/candidate_controller/new_candidate_test.rb index c59f58c..891e039 100644 --- a/test/controllers/admin/candidate_controller/new_candidate_test.rb +++ b/test/controllers/admin/candidate_controller/new_candidate_test.rb @@ -23,8 +23,13 @@ module Admin assert_enqueued_jobs 2 do assert_difference("Candidate.count") do - post admin_create_candidate_path, params: { candidate: - { name: 'new name', email: 'test@mailinator.com', experience: '0-3', quiz_id: quizzes(:fed).id } } + post admin_create_candidate_path, params: { candidate: { + name: 'new name', + email: 'test@mailinator.com', + experience: '0-3', + project: 'client project', + quiz_id: quizzes(:fed).id + } } end end assert_redirected_to admin_candidates_path diff --git a/test/fixtures/candidates.yml b/test/fixtures/candidates.yml index 354fc68..f5cf861 100644 --- a/test/fixtures/candidates.yml +++ b/test/fixtures/candidates.yml @@ -4,6 +4,7 @@ roy: # Roy should have started, and is ready for a reminder name: Roy Cruz email: <%= CryptSerializer.dump 'roy.cruz@mailinator.com' %> experience: 0-3 + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -14,6 +15,7 @@ gillian: # Gillian has not begun the test name: Gillian Anderson email: <%= CryptSerializer.dump 'gillian.anderson@mailinator.com' %> experience: 4-6 + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -24,6 +26,7 @@ martha: # Martha has not begun the test name: Martha Watts email: <%= CryptSerializer.dump 'martha.watts@mailinator.com' %> experience: 4-6 + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -34,6 +37,7 @@ dawn: # Dawn has completed, and been reminded, but not submitted the test name: Dawn Hopkins email: <%= CryptSerializer.dump 'dawn.hopkins@mailinator.com' %> experience: 0-2 + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -44,6 +48,7 @@ peggy: # Peggy has completed, and been reminded, but not submitted the test name: Peggy Blisters email: <%= CryptSerializer.dump 'peggy.blisters@mailinator.com' %> experience: 0-2 + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -54,6 +59,7 @@ richard: # Richard has completed AND submitted the test name: Richard Burns email: <%= CryptSerializer.dump 'richard.burns@mailinator.com' %> experience: 15+ + project: Client/Project recruiter: recruiter quiz: fed completed: true @@ -66,6 +72,7 @@ juan: # Juan has chosen "finish later" for live coders name: Juan Campbell email: <%= CryptSerializer.dump 'juan.campbell@mailinator.com' %> experience: 15+ + project: Client/Project recruiter: recruiter quiz: fed completed: false @@ -76,6 +83,7 @@ stacy: # Stacy has completed AND submitted the test name: Stacy Scott email: <%= CryptSerializer.dump 'stacy.scott@mailinator.com' %> experience: 7-9 + project: Client/Project recruiter: recruiter quiz: fed completed: true @@ -88,6 +96,7 @@ henry: # Henry has completed AND submitted the test name: Henry Butler email: <%= CryptSerializer.dump 'henry.butler@mailinator.com' %> experience: 4-6 + project: Client/Project recruiter: recruiter quiz: fed completed: true @@ -99,6 +108,7 @@ wade: # Wade has completed AND submitted the test name: Wade Armstrong email: <%= CryptSerializer.dump 'wade.armstrong@mailinator.com' %> experience: 0-3 + project: Client/Project recruiter: recruiter quiz: fed completed: true @@ -110,6 +120,7 @@ gustov: # Gustov is NOT for FED name: Gustov email: <%= CryptSerializer.dump 'gustov@mailinator.com' %> experience: 0-3 + project: Client/Project recruiter: recruiter quiz: admin completed: false diff --git a/test/models/candidate_test.rb b/test/models/candidate_test.rb index 393c7e8..1fef679 100644 --- a/test/models/candidate_test.rb +++ b/test/models/candidate_test.rb @@ -6,6 +6,7 @@ class CandidateTest < ActiveSupport::TestCase candidate = Candidate.create(name: 'new name', email: 'test@mailinator.com', experience: '0-3', + project: 'Client', quiz_id: quizzes(:fed).id) assert candidate.test_hash.present? @@ -16,6 +17,7 @@ class CandidateTest < ActiveSupport::TestCase candidate = Candidate.create(name: 'new name', email: email, experience: '0-3', + project: 'Client', recruiter_id: users(:recruiter).id, quiz_id: quizzes(:fed).id) From 5f3dfb5c96691c364a056b138927f207f1c22234 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 8 Feb 2017 16:30:54 -0600 Subject: [PATCH 09/46] reminder name refactor --- app/workers/{reminder.rb => candidate_reminder.rb} | 2 +- lib/tasks/reminders.rake | 2 +- .../{reminder_test.rb => candidate_reminder_test.rb} | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) rename app/workers/{reminder.rb => candidate_reminder.rb} (97%) rename test/workers/{reminder_test.rb => candidate_reminder_test.rb} (81%) diff --git a/app/workers/reminder.rb b/app/workers/candidate_reminder.rb similarity index 97% rename from app/workers/reminder.rb rename to app/workers/candidate_reminder.rb index 7437f04..a804e8e 100644 --- a/app/workers/reminder.rb +++ b/app/workers/candidate_reminder.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class Reminder +class CandidateReminder def initialize @collection = reminder_collection end diff --git a/lib/tasks/reminders.rake b/lib/tasks/reminders.rake index 2695aa6..086a570 100644 --- a/lib/tasks/reminders.rake +++ b/lib/tasks/reminders.rake @@ -2,7 +2,7 @@ namespace :reminders do desc "send reminders to stagnate quizes" task send_all: :environment do - reminders = Reminder.new + reminders = CandidateReminder.new reminders.send_all end end diff --git a/test/workers/reminder_test.rb b/test/workers/candidate_reminder_test.rb similarity index 81% rename from test/workers/reminder_test.rb rename to test/workers/candidate_reminder_test.rb index 32dabbb..fbe83a5 100644 --- a/test/workers/reminder_test.rb +++ b/test/workers/candidate_reminder_test.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true require 'test_helper' -class ReminderTest < ActiveSupport::TestCase +class CandidateReminderTest < ActiveSupport::TestCase test "collection is created with one result" do - reminders = Reminder.new + reminders = CandidateReminder.new assert_equal 1, reminders.size end test "each candidate has needed attributes" do - reminders = Reminder.new + reminders = CandidateReminder.new assert_instance_of String, reminders.candidates.first.name assert_instance_of String, reminders.candidates.first.test_hash @@ -16,7 +16,7 @@ class ReminderTest < ActiveSupport::TestCase end test "send reminders sends email, and flags reminded" do - reminders = Reminder.new + reminders = CandidateReminder.new pre_reminded = Candidate.find(reminders.candidates.first.id).reminded assert_difference("ActionMailer::Base.deliveries.size", reminders.count) do From bf6c686e2e2f3db886279fbef1425495390732f6 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 8 Feb 2017 17:34:05 -0600 Subject: [PATCH 10/46] reviewer reminder worker --- app/workers/reviewer_reminder.rb | 32 ++++++++++++++++++++++++++ lib/tasks/reminders.rake | 6 +++++ test/workers/reviewer_reminder_test.rb | 25 ++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 app/workers/reviewer_reminder.rb create mode 100644 test/workers/reviewer_reminder_test.rb diff --git a/app/workers/reviewer_reminder.rb b/app/workers/reviewer_reminder.rb new file mode 100644 index 0000000..58fbdb6 --- /dev/null +++ b/app/workers/reviewer_reminder.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +class ReviewerReminder + def initialize + @collection = reminder_collection + end + + def count + @collection.count + end + alias size count + + def reminders + @reminders ||= @collection.to_hash.map { |r| OpenStruct.new(r) } + end + + def send_all + reminders.each do |reminder| + ReviewerMailer.reminder(reminder).deliver_now + end + end + + private + + def reminder_collection + sql = "select u.name, u.email, c.test_hash, c.project + from reviewer_votes rev + inner join users u on u.id = rev.user_id + inner join candidates c on c.id = rev.candidate_id + where rev.vote = 0 and rev.veto = 0 and u.active is not false;" + ActiveRecord::Base.connection.exec_query(sql) + end +end diff --git a/lib/tasks/reminders.rake b/lib/tasks/reminders.rake index 086a570..680dfde 100644 --- a/lib/tasks/reminders.rake +++ b/lib/tasks/reminders.rake @@ -5,4 +5,10 @@ namespace :reminders do reminders = CandidateReminder.new reminders.send_all end + + desc "send reminders to reviewers" + task reviewers: :environment do + reminders = ReviewerReminder.new + reminders.send_all + end end diff --git a/test/workers/reviewer_reminder_test.rb b/test/workers/reviewer_reminder_test.rb new file mode 100644 index 0000000..08101d3 --- /dev/null +++ b/test/workers/reviewer_reminder_test.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +require 'test_helper' + +class ReviewerReminderTest < ActiveSupport::TestCase + test "collection is created with four results" do + reminders = ReviewerReminder.new + assert_equal 3, reminders.size + end + + test "each reminder has needed attributes" do + collection = ReviewerReminder.new + + assert_instance_of String, collection.reminders.first.name + assert_instance_of String, collection.reminders.first.email + assert_instance_of String, collection.reminders.first.test_hash + end + + test "send_all sends emails for each reviewer and test" do + collection = ReviewerReminder.new + + assert_difference("ActionMailer::Base.deliveries.size", collection.count) do + collection.send_all + end + end +end From 117b795a0cf24df6c77666f0fbce43ed8bc8d6cd Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Wed, 8 Feb 2017 17:57:25 -0600 Subject: [PATCH 11/46] reviewer reminders --- app/mailers/reviewer_mailer.rb | 6 ++++++ app/views/reviewer_mailer/reminder.html.inky | 9 +++++++++ app/views/reviewer_mailer/reminder.text.erb | 5 +++++ test/mailers/previews/reviewer_mailer_preview.rb | 6 ++++++ test/mailers/reviewer_mailer_test.rb | 10 ++++++++++ 5 files changed, 36 insertions(+) create mode 100644 app/views/reviewer_mailer/reminder.html.inky create mode 100644 app/views/reviewer_mailer/reminder.text.erb diff --git a/app/mailers/reviewer_mailer.rb b/app/mailers/reviewer_mailer.rb index 9d57d89..7b467b4 100644 --- a/app/mailers/reviewer_mailer.rb +++ b/app/mailers/reviewer_mailer.rb @@ -6,4 +6,10 @@ class ReviewerMailer < ApplicationMailer mail to: recipients, subject: "Skills Assessment Results - #{@candidate.test_hash}" end + + def reminder reminder + @reminder = reminder + + mail to: reminder.email, subject: "Review Reminder" + end end diff --git a/app/views/reviewer_mailer/reminder.html.inky b/app/views/reviewer_mailer/reminder.html.inky new file mode 100644 index 0000000..af7cfc8 --- /dev/null +++ b/app/views/reviewer_mailer/reminder.html.inky @@ -0,0 +1,9 @@ + + + + diff --git a/app/views/reviewer_mailer/reminder.text.erb b/app/views/reviewer_mailer/reminder.text.erb new file mode 100644 index 0000000..ef651e0 --- /dev/null +++ b/app/views/reviewer_mailer/reminder.text.erb @@ -0,0 +1,5 @@ +PERFICIENT/digital SKILLS ASSESSMENT RESULTS + +Hello <%= @reminder.name %>, + +Please review and vote on the results for <%= admin_result_url(@reminder.test_hash) %>. diff --git a/test/mailers/previews/reviewer_mailer_preview.rb b/test/mailers/previews/reviewer_mailer_preview.rb index df8b1cb..bc7f97e 100644 --- a/test/mailers/previews/reviewer_mailer_preview.rb +++ b/test/mailers/previews/reviewer_mailer_preview.rb @@ -4,4 +4,10 @@ class ReviewerMailerPreview < ActionMailer::Preview def candidate_submission ReviewerMailer.candidate_submission Candidate.find_by(test_hash: 'OvP0ZqGKwJ0') # Dawn end + + def reminder + reminders = ReviewerReminder.new + reminder = reminders.reminders.first + ReviewerMailer.reminder reminder + end end diff --git a/test/mailers/reviewer_mailer_test.rb b/test/mailers/reviewer_mailer_test.rb index 35673eb..f02c068 100644 --- a/test/mailers/reviewer_mailer_test.rb +++ b/test/mailers/reviewer_mailer_test.rb @@ -11,4 +11,14 @@ class ReviewerMailerTest < ActionMailer::TestCase assert_equal [ENV["default_mail_from"]], mail.from assert_match candidate.test_hash, mail.body.encoded end + + test "reminder" do + reminders = ReviewerReminder.new + reminder = reminders.reminders.first + mail = ReviewerMailer.reminder reminder + assert_match "Review Reminder", mail.subject + assert_equal [reminder.email], mail.to + assert_equal [ENV["default_mail_from"]], mail.from + assert_match reminder.test_hash, mail.body.encoded + end end From 82563438518630873008e8a495bad97077514248 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 9 Feb 2017 15:38:38 -0600 Subject: [PATCH 12/46] notify manager email --- app/mailers/reviewer_mailer.rb | 7 +++++++ app/models/candidate.rb | 7 +++++++ test/mailers/previews/reviewer_mailer_preview.rb | 4 ++++ test/mailers/reviewer_mailer_test.rb | 9 +++++++++ 4 files changed, 27 insertions(+) diff --git a/app/mailers/reviewer_mailer.rb b/app/mailers/reviewer_mailer.rb index 7b467b4..3955ef9 100644 --- a/app/mailers/reviewer_mailer.rb +++ b/app/mailers/reviewer_mailer.rb @@ -12,4 +12,11 @@ class ReviewerMailer < ApplicationMailer mail to: reminder.email, subject: "Review Reminder" end + + def notify_manager candidate_id + @candidate = Candidate.find_by(id: candidate_id) + @manager = @candidate.manager + + mail to: @manager.email, subject: "Voting Complete" + end end diff --git a/app/models/candidate.rb b/app/models/candidate.rb index 5d4df2b..7d5b287 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -35,6 +35,13 @@ class Candidate < ApplicationRecord end end + def manager + manager_votes = votes.joins(:user).where("users.role = 'manager'") + return nil if manager_votes.empty? + + manager_votes.first.user + end + def submitted_answers answers.where(submitted: true) end diff --git a/test/mailers/previews/reviewer_mailer_preview.rb b/test/mailers/previews/reviewer_mailer_preview.rb index bc7f97e..1f67524 100644 --- a/test/mailers/previews/reviewer_mailer_preview.rb +++ b/test/mailers/previews/reviewer_mailer_preview.rb @@ -10,4 +10,8 @@ class ReviewerMailerPreview < ActionMailer::Preview reminder = reminders.reminders.first ReviewerMailer.reminder reminder end + + def notify_manager + ReviewerMailer.notify_manager Candidate.find_by(test_hash: 'OvP0ZqGKwJ0').id # Dawn + end end diff --git a/test/mailers/reviewer_mailer_test.rb b/test/mailers/reviewer_mailer_test.rb index f02c068..aa2e04f 100644 --- a/test/mailers/reviewer_mailer_test.rb +++ b/test/mailers/reviewer_mailer_test.rb @@ -21,4 +21,13 @@ class ReviewerMailerTest < ActionMailer::TestCase assert_equal [ENV["default_mail_from"]], mail.from assert_match reminder.test_hash, mail.body.encoded end + + test "notify_manager" do + candidate = candidates(:richard) + mail = ReviewerMailer.notify_manager candidate.id + assert_match "Voting Complete", mail.subject + assert_equal [candidate.manager.email], mail.to + assert_equal [ENV["default_mail_from"]], mail.from + assert_match candidate.test_hash, mail.body.encoded + end end From cb6dccded8c2830dc7122ea5926f133f23be0749 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 9 Feb 2017 16:26:33 -0600 Subject: [PATCH 13/46] manager notifications --- app/models/reviewer_vote.rb | 36 ++++ app/models/user.rb | 2 + .../reviewer_mailer/notify_manager.html.inky | 10 + .../reviewer_mailer/notify_manager.text.erb | 6 + test/fixtures/answers.yml | 194 ++++++++++++++++++ test/fixtures/candidates.yml | 24 +++ test/fixtures/reviewer_votes.yml | 27 +++ test/models/reviewer_vote_test.rb | 23 ++- 8 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 app/views/reviewer_mailer/notify_manager.html.inky create mode 100644 app/views/reviewer_mailer/notify_manager.text.erb diff --git a/app/models/reviewer_vote.rb b/app/models/reviewer_vote.rb index 6ed8fce..2dce2f4 100644 --- a/app/models/reviewer_vote.rb +++ b/app/models/reviewer_vote.rb @@ -5,6 +5,8 @@ class ReviewerVote < ApplicationRecord validates :user_id, uniqueness: { scope: :candidate_id } + after_save :notify_manager + enum vote: { undecided: 0, yea: 1, @@ -15,4 +17,38 @@ class ReviewerVote < ApplicationRecord approved: 1, rejected: 2 } + + private + + def notify_manager + ReviewerMailer.notify_manager(candidate_id).deliver_now if all_reviewers_voted? + end + + def all_reviewers_voted? # rubocop:disable Metrics/MethodLength + sql = " select distinct rev.candidate_id + , full_votes.voters, full_votes.vote_count + , c.test_hash + from reviewer_votes rev + inner join users u on u.id = rev.user_id + inner join candidates c on c.id = rev.candidate_id + left join ( + select candidate_id + from reviewer_votes + where veto > 0 or veto is null + ) as vetos on vetos.candidate_id = rev.candidate_id + left join ( + select candidate_id + , count(vote) voters + , sum(case when vote != 0 then 1 else 0 end) vote_count + from reviewer_votes + left join users on users.id = reviewer_votes.user_id + where users.role != 'manager' + group by candidate_id + ) as full_votes on full_votes.candidate_id = rev.candidate_id + where vetos.candidate_id is null + and full_votes.voters = full_votes.vote_count + and rev.candidate_id = #{candidate_id};" + result = ActiveRecord::Base.connection.exec_query(sql) + result.count == 1 + end # rubocop:enable Metrics/MethodLength end diff --git a/app/models/user.rb b/app/models/user.rb index b7dfbdd..773cb1f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -19,6 +19,8 @@ class User < ApplicationRecord end # Voting + # TODO: Refactor this out of User, belongs on ReviewerVote + # ie: cast_yea(candidate, user) def cast_yea_on candidate vote = votes.find_by(candidate_id: candidate.to_i) vote.vote = :yea diff --git a/app/views/reviewer_mailer/notify_manager.html.inky b/app/views/reviewer_mailer/notify_manager.html.inky new file mode 100644 index 0000000..c11d797 --- /dev/null +++ b/app/views/reviewer_mailer/notify_manager.html.inky @@ -0,0 +1,10 @@ + + + + diff --git a/app/views/reviewer_mailer/notify_manager.text.erb b/app/views/reviewer_mailer/notify_manager.text.erb new file mode 100644 index 0000000..dacc2bb --- /dev/null +++ b/app/views/reviewer_mailer/notify_manager.text.erb @@ -0,0 +1,6 @@ +PERFICIENT/digital SKILLS ASSESSMENT RESULTS + +Hello <%= @manager.name %>, + +Everyone has voted and you need to request or decline an interview for +<%= admin_result_url(@candidate.test_hash) %> diff --git a/test/fixtures/answers.yml b/test/fixtures/answers.yml index de70e0c..69019e2 100644 --- a/test/fixtures/answers.yml +++ b/test/fixtures/answers.yml @@ -696,3 +696,197 @@ wade10: created_at: <%= DateTime.now() - 36.hours - 40.minutes %> updated_at: <%= DateTime.now() - 36.hours - 20.minutes %> +jorge1: + candidate: jorge + question: Cras justo odio, dapibus ac facilisis in, egestas eget quam. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. + answer: option 3 + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 22.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 22.minutes %> + +jorge2: + candidate: jorge + question: fed2 + answer: 'indexOf()' + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 24.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 4.minutes %> + +jorge3: + candidate: jorge + question: fed3 + answer: {html: '

Salmon

', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'} + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 26.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 6.minutes %> + +jorge4: + candidate: jorge + question: fed4 + answer: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 28.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 28.minutes %> + +jorge5: + candidate: jorge + question: fed5 + answer: 'Dynamic listeners' + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 30.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 30.minutes %> + +jorge6: + candidate: jorge + question: fed6 + answer: Integer posuere erat a ante venenatis dapibus posuere velit aliquet. + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 32.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 12.minutes %> + +jorge7: + candidate: jorge + question: fed7 + answer: {html: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 34.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 14.minutes %> + +jorge8: + candidate: jorge + question: fed8 + answer: + other: Some generic user input + options: + - other + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 36.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 16.minutes %> + +jorge9: + candidate: jorge + question: fed9 + answer: + other: Brunch + options: + - Neither + - other + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 38.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 18.minutes %> + +jorge10: + candidate: jorge + question: fed10 + answer: ["Live long and prosper", "Who you calling Scruffy?"] + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 40.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 20.minutes %> + +elsie1: + candidate: elsie + question: Cras justo odio, dapibus ac facilisis in, egestas eget quam. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. + answer: option 3 + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 22.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 22.minutes %> + +elsie2: + candidate: elsie + question: fed2 + answer: 'indexOf()' + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 24.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 4.minutes %> + +elsie3: + candidate: elsie + question: fed3 + answer: {html: '

Salmon

', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'} + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 26.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 6.minutes %> + +elsie4: + candidate: elsie + question: fed4 + answer: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 28.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 28.minutes %> + +elsie5: + candidate: elsie + question: fed5 + answer: 'Dynamic listeners' + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 30.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 30.minutes %> + +elsie6: + candidate: elsie + question: fed6 + answer: Integer posuere erat a ante venenatis dapibus posuere velit aliquet. + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 32.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 12.minutes %> + +elsie7: + candidate: elsie + question: fed7 + answer: {html: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 34.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 14.minutes %> + +elsie8: + candidate: elsie + question: fed8 + answer: + other: Some generic user input + options: + - other + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 36.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 16.minutes %> + +elsie9: + candidate: elsie + question: fed9 + answer: + other: Brunch + options: + - Neither + - other + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 38.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 18.minutes %> + +elsie10: + candidate: elsie + question: fed10 + answer: ["Live long and prosper", "Who you calling Scruffy?"] + saved: 0 + submitted: true + created_at: <%= DateTime.now() - 36.hours - 40.minutes %> + updated_at: <%= DateTime.now() - 36.hours - 20.minutes %> + diff --git a/test/fixtures/candidates.yml b/test/fixtures/candidates.yml index f5cf861..9554347 100644 --- a/test/fixtures/candidates.yml +++ b/test/fixtures/candidates.yml @@ -116,6 +116,30 @@ wade: # Wade has completed AND submitted the test reminded: false test_hash: BkSkpapJnkz2N +jorge: # Jorge has completed AND submitted the test + name: Jorge Holmes + email: <%= CryptSerializer.dump 'jorge.holmes@mailinator.com' %> + experience: 0-3 + project: Client/Project + recruiter: recruiter + quiz: fed + completed: true + completed_at: <%= DateTime.current %> + reminded: false + test_hash: iC5FdWJxcyySBmpOpU + +elsie: # Elsie has completed AND submitted the test + name: Elsie Lowe + email: <%= CryptSerializer.dump 'elsie.lowe@mailinator.com' %> + experience: 0-3 + project: Client/Project + recruiter: recruiter + quiz: fed + completed: true + completed_at: <%= DateTime.current %> + reminded: false + test_hash: rLSoizA3ATMNSCx + gustov: # Gustov is NOT for FED name: Gustov email: <%= CryptSerializer.dump 'gustov@mailinator.com' %> diff --git a/test/fixtures/reviewer_votes.yml b/test/fixtures/reviewer_votes.yml index 2669604..a8ab4c1 100644 --- a/test/fixtures/reviewer_votes.yml +++ b/test/fixtures/reviewer_votes.yml @@ -66,3 +66,30 @@ reviewer2_wade: candidate: wade user: reviewer2 + +manager_jorge: + candidate: jorge + user: manager + +reviewer_jorge: + candidate: jorge + user: reviewer + vote: 1 + +reviewer2_jorge: + candidate: jorge + user: reviewer2 + + +manager_elsie: + candidate: elsie + user: manager + +reviewer_elsie: + candidate: elsie + user: reviewer + +reviewer2_elsie: + candidate: elsie + user: reviewer2 + diff --git a/test/models/reviewer_vote_test.rb b/test/models/reviewer_vote_test.rb index 48db0fe..5f73233 100644 --- a/test/models/reviewer_vote_test.rb +++ b/test/models/reviewer_vote_test.rb @@ -12,10 +12,11 @@ class ReviewerVoteTest < ActiveSupport::TestCase assert_equal 3, richard.votes.size end - test "manager has 4 votes" do + test "manager has a vote for every completed quiz" do manager = users(:manager) + completed_count = Candidate.where(completed: true).count - assert_equal 4, manager.votes.size + assert_equal completed_count, manager.votes.size end test "richard has been approved" do @@ -31,4 +32,22 @@ class ReviewerVoteTest < ActiveSupport::TestCase assert stacy.declined? refute stacy.approved? end + + test "mailer is queued on last vote" do + reviewer = users(:reviewer2) + candidate = candidates(:jorge) + + assert_difference("ActionMailer::Base.deliveries.size", 1) do + reviewer.cast_yea_on(candidate) + end + end + + test "mailer is NOT queued on first vote" do + reviewer = users(:reviewer2) + candidate = candidates(:elsie) + + assert_difference("ActionMailer::Base.deliveries.size", 0) do + reviewer.cast_yea_on(candidate) + end + end end From 3c0627e9ea534ed4e22ce19829fd398a0c448931 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 9 Feb 2017 16:27:00 -0600 Subject: [PATCH 14/46] no voting reminders for managers --- app/workers/reviewer_reminder.rb | 3 ++- test/workers/reviewer_reminder_test.rb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/workers/reviewer_reminder.rb b/app/workers/reviewer_reminder.rb index 58fbdb6..7e67da6 100644 --- a/app/workers/reviewer_reminder.rb +++ b/app/workers/reviewer_reminder.rb @@ -26,7 +26,8 @@ class ReviewerReminder from reviewer_votes rev inner join users u on u.id = rev.user_id inner join candidates c on c.id = rev.candidate_id - where rev.vote = 0 and rev.veto = 0 and u.active is not false;" + where rev.vote = 0 and rev.veto = 0 + and u.role != 'manager' and u.active is not false;" ActiveRecord::Base.connection.exec_query(sql) end end diff --git a/test/workers/reviewer_reminder_test.rb b/test/workers/reviewer_reminder_test.rb index 08101d3..c4399dd 100644 --- a/test/workers/reviewer_reminder_test.rb +++ b/test/workers/reviewer_reminder_test.rb @@ -2,9 +2,9 @@ require 'test_helper' class ReviewerReminderTest < ActiveSupport::TestCase - test "collection is created with four results" do + test "collection is created with results" do reminders = ReviewerReminder.new - assert_equal 3, reminders.size + assert_equal 6, reminders.size end test "each reminder has needed attributes" do From 9c2b53aa79fca87b8e1a2fa258d8d34ec293cabd Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Fri, 10 Feb 2017 12:03:50 -0600 Subject: [PATCH 15/46] init comments --- app/models/candidate.rb | 1 + app/models/quiz_comment.rb | 7 ++ app/models/user.rb | 1 + .../20170210165110_create_quiz_comments.rb | 12 ++++ db/schema.rb | 10 ++- test/fixtures/quiz_comments.yml | 72 +++++++++++++++++++ test/models/quiz_comment_test.rb | 27 +++++++ test/models/reviewer_vote_test.rb | 2 +- 8 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 app/models/quiz_comment.rb create mode 100644 db/migrate/20170210165110_create_quiz_comments.rb create mode 100644 test/fixtures/quiz_comments.yml create mode 100644 test/models/quiz_comment_test.rb diff --git a/app/models/candidate.rb b/app/models/candidate.rb index 7d5b287..af91ff7 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -6,6 +6,7 @@ class Candidate < ApplicationRecord belongs_to :recruiter, class_name: "User" has_many :votes, class_name: "ReviewerVote" has_many :reviewers, through: :quiz + has_many :quiz_comments, foreign_key: :test_hash, primary_key: :test_hash serialize :email, CryptSerializer diff --git a/app/models/quiz_comment.rb b/app/models/quiz_comment.rb new file mode 100644 index 0000000..8819997 --- /dev/null +++ b/app/models/quiz_comment.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +class QuizComment < ApplicationRecord + belongs_to :user + belongs_to :candidate, foreign_key: :test_hash, primary_key: :test_hash + + validates :message, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 773cb1f..6179a94 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ApplicationRecord has_many :reviewer_to_quizzes has_many :quizzes, through: :reviewer_to_quizzes has_many :votes, class_name: 'ReviewerVote' + has_many :quiz_comments has_many :reviewees, through: :quizzes, source: :candidates diff --git a/db/migrate/20170210165110_create_quiz_comments.rb b/db/migrate/20170210165110_create_quiz_comments.rb new file mode 100644 index 0000000..caae162 --- /dev/null +++ b/db/migrate/20170210165110_create_quiz_comments.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +class CreateQuizComments < ActiveRecord::Migration[5.0] + def change + create_table :quiz_comments do |t| + t.integer :user_id + t.string :test_hash + t.text :message + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 5e5c4cc..ae6704d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170208212526) do +ActiveRecord::Schema.define(version: 20170210165110) do create_table "answers", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| t.integer "candidate_id" @@ -60,6 +60,14 @@ ActiveRecord::Schema.define(version: 20170208212526) do t.index ["sort"], name: "index_questions_on_sort", using: :btree end + create_table "quiz_comments", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| + t.integer "user_id" + t.string "test_hash" + t.text "message", limit: 65535 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "quizzes", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| t.string "unit" t.string "dept" diff --git a/test/fixtures/quiz_comments.yml b/test/fixtures/quiz_comments.yml new file mode 100644 index 0000000..68a7938 --- /dev/null +++ b/test/fixtures/quiz_comments.yml @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +com1: + test_hash: BkSkpapJnkz2N #wade + user: reviewer + message: Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Etiam porta sem malesuada magna mollis euismod. Aenean lacinia bibendum nulla sed consectetur. Maecenas faucibus mollis interdum. + +com2: + test_hash: BkSkpapJnkz2N #wade + user: reviewer + message: Donec sed odio dui. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Curabitur blandit tempus porttitor. Nullam quis risus eget urna mollis ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +com3: + test_hash: BkSkpapJnkz2N #wade + user: reviewer2 + message: Cras mattis consectetur purus sit amet fermentum. Donec sed odio dui. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. + +com4: + test_hash: iC5FdWJxcyySBmpOpU #jorge + user: manager + message: Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed posuere consectetur est at lobortis. + +com5: + test_hash: egPomAuVDeCEp #henry + user: manager + message: no. + +com6: + test_hash: egPomAuVDeCEp #henry + user: reviewer2 + message: fine. + +com7: + test_hash: iC5FdWJxcyySBmpOpU #jorge + user: reviewer + message: Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. + +com8: + test_hash: egPomAuVDeCEp #henry + user: manager + message: no. + +com9: + test_hash: rLSoizA3ATMNSCx #elsie + user: reviewer + message: Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Donec ullamcorper nulla non metus auctor fringilla. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Etiam porta sem malesuada magna mollis euismod. Vestibulum id ligula porta felis euismod semper. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Maecenas faucibus mollis interdum. + +com10: + test_hash: rLSoizA3ATMNSCx #elsie + user: reviewer2 + message: Ornare Tellus Nullam Mattis + +com11: + test_hash: rLSoizA3ATMNSCx #elsie + user: reviewer2 + message: Nibh Ultricies Purus + +com12: + test_hash: rLSoizA3ATMNSCx #elsie + user: reviewer + message: Donec id elit non mi porta gravida at eget metus. + +com13: + test_hash: rLSoizA3ATMNSCx #elsie + user: manager + message: Donec id elit non mi porta gravida at eget metus. + +com14: + test_hash: rLSoizA3ATMNSCx #elsie + user: reviewer2 + message: Ultricies Vulputate Bibendum Parturient + diff --git a/test/models/quiz_comment_test.rb b/test/models/quiz_comment_test.rb new file mode 100644 index 0000000..90e3ec8 --- /dev/null +++ b/test/models/quiz_comment_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +require 'test_helper' + +class QuizCommentTest < ActiveSupport::TestCase + test "the truth" do + assert QuizComment + end + + test "user to comments association" do + manager = users(:manager) + + assert_equal 4, manager.quiz_comments.size + end + + test "candidate to comments association" do + candidate = candidates(:elsie) + + assert_equal 6, candidate.quiz_comments.size + end + + test 'comment to user' do + comment = quiz_comments(:com1) + + assert_match 'Wade', comment.candidate.name + assert_match 'Tina', comment.user.name + end +end diff --git a/test/models/reviewer_vote_test.rb b/test/models/reviewer_vote_test.rb index 5f73233..d5a9ef3 100644 --- a/test/models/reviewer_vote_test.rb +++ b/test/models/reviewer_vote_test.rb @@ -3,7 +3,7 @@ require 'test_helper' class ReviewerVoteTest < ActiveSupport::TestCase test "the truth" do - assert ReviewerVoteTest + assert ReviewerVote end test "richard has 3 votes" do From 17d62131c91616868c4f9342353c1dc86d9251a7 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Fri, 10 Feb 2017 15:46:43 -0600 Subject: [PATCH 16/46] scratched in view partials --- .../stylesheets/molecules/_admin_review.scss | 25 +++++- app/controllers/admin/result_controller.rb | 2 + app/views/admin/result/_comment.html.erb | 2 + app/views/admin/result/_comment_form.html.erb | 10 +++ app/views/admin/result/view.html.erb | 76 ++++++++++--------- 5 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 app/views/admin/result/_comment.html.erb create mode 100644 app/views/admin/result/_comment_form.html.erb diff --git a/app/assets/stylesheets/molecules/_admin_review.scss b/app/assets/stylesheets/molecules/_admin_review.scss index 586367d..8cbab59 100644 --- a/app/assets/stylesheets/molecules/_admin_review.scss +++ b/app/assets/stylesheets/molecules/_admin_review.scss @@ -1,12 +1,14 @@ .admin-review { counter-reset: question; + float: left; + width: 66%; form { margin-left: 2.3em; position: relative; &::before { - content: counter(question) ") "; + content: counter(question) ') '; counter-increment: question; font-size: 1.25em; left: -1.8em; @@ -17,6 +19,27 @@ } +.review-comments { + float: right; + padding-left: 30px; + width: 33%; + + .comment-message { + margin-right: 5px; + } + + .comment-author { + font-size: 0.85em; + font-weight: 700; + margin-bottom: 30px; + text-align: right; + + &:before { + content: '- '; + } + } +} + .review_meta { @media screen and (min-width: 768px) { diff --git a/app/controllers/admin/result_controller.rb b/app/controllers/admin/result_controller.rb index 867c922..67be27a 100644 --- a/app/controllers/admin/result_controller.rb +++ b/app/controllers/admin/result_controller.rb @@ -19,6 +19,8 @@ module Admin @candidate = Candidate.find_by(test_hash: params[:test_hash]) @quiz = @candidate.my_quiz @status = QuizStatus.new(@candidate) + @comments = QuizComment.includes(:user).where(test_hash: @candidate.test_hash).order(:created_at) + @comment = QuizComment.new end end end diff --git a/app/views/admin/result/_comment.html.erb b/app/views/admin/result/_comment.html.erb new file mode 100644 index 0000000..b260eca --- /dev/null +++ b/app/views/admin/result/_comment.html.erb @@ -0,0 +1,2 @@ +
<%= comment.message %>
+
<%= comment.user.name %>
diff --git a/app/views/admin/result/_comment_form.html.erb b/app/views/admin/result/_comment_form.html.erb new file mode 100644 index 0000000..8867b8e --- /dev/null +++ b/app/views/admin/result/_comment_form.html.erb @@ -0,0 +1,10 @@ +<%= render partial: 'shared/form_model_errors', locals: { obj: @comment } %> + +<%= form_for @comment, url: "#", method: :post do |form| %> +
+ <%= form.label :message, "Comment" %> + <%= form.text_area :message %> +
+ + <%= submit_tag "Save Comment" %> +<% end %> diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index d0452ca..3b1abaa 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -2,42 +2,50 @@ content_for :title, "Quiz Review - Skills Assessment Admin" %> -
-

Quiz Review

+
+
+

Quiz Review

-
-
- Test ID: <%= @candidate.test_hash %>
- Years of Experience: <%= @candidate.experience %>
- Client/Project: <%= @candidate.project %>
- Recruiter Email: <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %>
+
+
+ Test ID: <%= @candidate.test_hash %>
+ Years of Experience: <%= @candidate.experience %>
+ Client/Project: <%= @candidate.project %>
+ Recruiter Email: <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %>
+
+ +
<%= render partial: 'voting' %>
-
<%= render partial: 'voting' %>
+ <% @quiz.each do |question| %> + <%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %> +
+
+
+

<%= question.question %>

+
+
+ +
+ <% if question.attachment.present? %> + <%= image_tag question.attachment %> + <% end %> +
+ <%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %> +
+
+
+ <% end #form_tag %> + <% end #questions loop %> + + <%= link_to(admin_results_path, { class: 'secondary-btn' }) do %> + + <% end %>
- <% @quiz.each do |question| %> - <%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %> -
-
-
-

<%= question.question %>

-
-
- -
- <% if question.attachment.present? %> - <%= image_tag question.attachment %> - <% end %> -
- <%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %> -
-
-
- <% end #form_tag %> - <% end #questions loop %> - - <%= link_to(admin_results_path, { class: 'secondary-btn' }) do %> - - <% end %> -
+
+

Comments

+ <%= render partial: 'comment', collection: @comments %> +
<%= render partial: 'comment_form' %>
+
+
From 43c77677f600dadca3010ea731cd167d7772ff87 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 15:03:08 -0600 Subject: [PATCH 17/46] rubocop autofix! --- Guardfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guardfile b/Guardfile index 1027353..305fd8e 100644 --- a/Guardfile +++ b/Guardfile @@ -79,7 +79,7 @@ guard :shell, all_on_start: true do end end -guard :rubocop, cli: %w(-D -S) do +guard :rubocop, cli: %w(-D -S -a) do watch(/.rubocop.yml/) watch(/.+\.rb$/) watch(/Rakefile/) From d3a28707478d44f94134d5dface3ff9f04500a7d Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 15:04:47 -0600 Subject: [PATCH 18/46] a simple comment policy --- app/policies/quiz_comment_policy.rb | 21 ++++++++++++++++++ test/policies/quiz_comment_policy_test.rb | 27 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 app/policies/quiz_comment_policy.rb create mode 100644 test/policies/quiz_comment_policy_test.rb diff --git a/app/policies/quiz_comment_policy.rb b/app/policies/quiz_comment_policy.rb new file mode 100644 index 0000000..49b850e --- /dev/null +++ b/app/policies/quiz_comment_policy.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +class QuizCommentPolicy < ApplicationPolicy + # Quiz Comment Policy + # + # Anyone with access to the results can comment + # Only Comment owner can edit + + def create? + user.acts_as_reviewer? + end + + def update? + user.acts_as_reviewer? && user.id == record.user_id + end + + class Scope < Scope + def resolve + true + end + end +end diff --git a/test/policies/quiz_comment_policy_test.rb b/test/policies/quiz_comment_policy_test.rb new file mode 100644 index 0000000..518681f --- /dev/null +++ b/test/policies/quiz_comment_policy_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +require 'test_helper' + +class QuizCommentPolicyTest < PolicyAssertions::Test + test 'should require current_user' do + assert_raise Pundit::NotAuthorizedError do + QuizCommentPolicy.new(nil, User.first).create? + end + end + + def test_create + assert_permit users(:admin), QuizComment + assert_permit users(:manager), QuizComment + assert_permit users(:reviewer), QuizComment + + refute_permit users(:recruiter), QuizComment + end + + def test_update + assert_permit users(:reviewer2), quiz_comments(:com6) + + refute_permit users(:reviewer), quiz_comments(:com6) + refute_permit users(:manager), quiz_comments(:com6) + refute_permit users(:admin), quiz_comments(:com6) + refute_permit users(:recruiter), quiz_comments(:com6) + end +end From 567b4a409de0d076c656400b5a7e04ecc00d45db Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 15:52:01 -0600 Subject: [PATCH 19/46] Don't auto corect debug statements --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index ddbc53a..2f5dff1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,9 @@ AllCops: - bin/**/* - vendor/assets/**/* +Lint/Debugger: + AutoCorrect: False + Style/AndOr: Enabled: false From da5dc4bd94a44a53616e7aeae2e98785c617519e Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 15:54:57 -0600 Subject: [PATCH 20/46] comment creation --- app/controllers/admin/comment_controller.rb | 26 +++++++++++++++++++ app/views/admin/result/_comment_form.html.erb | 2 +- app/views/admin/result/view.html.erb | 4 +-- config/routes.rb | 2 ++ .../admin/comment_controller_test.rb | 19 ++++++++++++++ 5 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 app/controllers/admin/comment_controller.rb create mode 100644 test/controllers/admin/comment_controller_test.rb diff --git a/app/controllers/admin/comment_controller.rb b/app/controllers/admin/comment_controller.rb new file mode 100644 index 0000000..d02ee24 --- /dev/null +++ b/app/controllers/admin/comment_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +module Admin + class CommentController < AdminController + before_action { authorize QuizComment } + + def create + comment = QuizComment.create( + comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash]) + ) + + if comment.persisted? + redirect_to admin_result_path(params[:test_hash]), + flash: { success: "Sucessfully created comment" } + else + redirect_to admin_result_path(params[:test_hash]), + flash: { error: "Failed to save comment" } + end + end + + private + + def comment_params + params.require(:quiz_comment).permit(:message, :id) + end + end +end diff --git a/app/views/admin/result/_comment_form.html.erb b/app/views/admin/result/_comment_form.html.erb index 8867b8e..d4c1b55 100644 --- a/app/views/admin/result/_comment_form.html.erb +++ b/app/views/admin/result/_comment_form.html.erb @@ -1,6 +1,6 @@ <%= render partial: 'shared/form_model_errors', locals: { obj: @comment } %> -<%= form_for @comment, url: "#", method: :post do |form| %> +<%= form_for @comment, url: admin_create_comment_path(test_hash), method: :post do |form| %>
<%= form.label :message, "Comment" %> <%= form.text_area :message %> diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index 3b1abaa..b07c69a 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -45,7 +45,7 @@

Comments

- <%= render partial: 'comment', collection: @comments %> -
<%= render partial: 'comment_form' %>
+ <%= render partial: 'comment', collection: @comments, locals: { test_hash: @candidate.test_hash } %> +
<%= render partial: 'comment_form', locals: { test_hash: @candidate.test_hash } %>
diff --git a/config/routes.rb b/config/routes.rb index e7a7558..edff83f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,8 @@ Rails.application.routes.draw do get "/admin/results", to: "admin/result#index", as: :admin_results get "/admin/result/:test_hash", to: "admin/result#view", as: :admin_result + post "/admin/comment/:test_hash", to: "admin/comment#create", as: :admin_create_comment + get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote, defaults: { format: 'json' } get "admin/vote/:test_hash/down", to: "admin/vote#down", as: :admin_down_vote, defaults: { format: 'json' } get "admin/vote/:test_hash/approve", to: "admin/vote#approve", as: :admin_approve_vote, defaults: { format: 'json' } diff --git a/test/controllers/admin/comment_controller_test.rb b/test/controllers/admin/comment_controller_test.rb new file mode 100644 index 0000000..d900269 --- /dev/null +++ b/test/controllers/admin/comment_controller_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +require 'test_helper' + +module Admin + class CommentControllerTest < ActionDispatch::IntegrationTest + test "should post create" do + auth_reviewer + candidate = candidates(:stacy) + + assert_difference("QuizComment.count") do + post admin_create_comment_url(test_hash: candidate.test_hash), params: { quiz_comment: { + message: 'this is a test comment' + } } + end + assert_redirected_to admin_result_url(test_hash: candidate.test_hash) + assert flash[:success] + end + end +end From 39ba1a8369fbfef8d4c72dff8b8a508279b98fe5 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 16:12:59 -0600 Subject: [PATCH 21/46] comment updates --- app/controllers/admin/comment_controller.rb | 29 +++++++++++++------ app/views/admin/result/_comment_form.html.erb | 1 + config/routes.rb | 1 + .../admin/comment_controller_test.rb | 17 +++++++++-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/app/controllers/admin/comment_controller.rb b/app/controllers/admin/comment_controller.rb index d02ee24..cef5cff 100644 --- a/app/controllers/admin/comment_controller.rb +++ b/app/controllers/admin/comment_controller.rb @@ -1,26 +1,37 @@ # frozen_string_literal: true module Admin class CommentController < AdminController - before_action { authorize QuizComment } + def update + comment = QuizComment.find_by(id: params[:id], test_hash: params[:test_hash]) + authorize comment + + comment.update(comment_params) + flash_message = if comment.save + { success: "Sucessfully updated comment" } + else + { error: "Failed to update comment" } + end + redirect_to admin_result_path(params[:test_hash]), flash: flash_message + end def create + authorize QuizComment comment = QuizComment.create( comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash]) ) - if comment.persisted? - redirect_to admin_result_path(params[:test_hash]), - flash: { success: "Sucessfully created comment" } - else - redirect_to admin_result_path(params[:test_hash]), - flash: { error: "Failed to save comment" } - end + flash_message = if comment.persisted? + { success: "Sucessfully created comment" } + else + { error: "Failed to save comment" } + end + redirect_to admin_result_path(params[:test_hash]), flash: flash_message end private def comment_params - params.require(:quiz_comment).permit(:message, :id) + params.require(:quiz_comment).permit(:message) end end end diff --git a/app/views/admin/result/_comment_form.html.erb b/app/views/admin/result/_comment_form.html.erb index d4c1b55..c2ab89c 100644 --- a/app/views/admin/result/_comment_form.html.erb +++ b/app/views/admin/result/_comment_form.html.erb @@ -1,5 +1,6 @@ <%= render partial: 'shared/form_model_errors', locals: { obj: @comment } %> +<% # admin_update_comment_path # admin_create_comment_path %> <%= form_for @comment, url: admin_create_comment_path(test_hash), method: :post do |form| %>
<%= form.label :message, "Comment" %> diff --git a/config/routes.rb b/config/routes.rb index edff83f..c806362 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,7 @@ Rails.application.routes.draw do get "/admin/results", to: "admin/result#index", as: :admin_results get "/admin/result/:test_hash", to: "admin/result#view", as: :admin_result + post "/admin/comment/:test_hash/:id", to: "admin/comment#update", as: :admin_update_comment post "/admin/comment/:test_hash", to: "admin/comment#create", as: :admin_create_comment get "admin/vote/:test_hash/up", to: "admin/vote#up", as: :admin_up_vote, defaults: { format: 'json' } diff --git a/test/controllers/admin/comment_controller_test.rb b/test/controllers/admin/comment_controller_test.rb index d900269..d864127 100644 --- a/test/controllers/admin/comment_controller_test.rb +++ b/test/controllers/admin/comment_controller_test.rb @@ -3,15 +3,26 @@ require 'test_helper' module Admin class CommentControllerTest < ActionDispatch::IntegrationTest + test "should post update" do + auth_manager + comment = quiz_comments(:com5) + post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id), + params: { quiz_comment: { message: 'updated comment' } } + + assert_redirected_to admin_result_url(test_hash: comment.test_hash) + assert flash[:success] + refute_equal comment.message, QuizComment.find_by(id: comment.id).message + end + test "should post create" do auth_reviewer candidate = candidates(:stacy) assert_difference("QuizComment.count") do - post admin_create_comment_url(test_hash: candidate.test_hash), params: { quiz_comment: { - message: 'this is a test comment' - } } + post admin_create_comment_url(test_hash: candidate.test_hash), + params: { quiz_comment: { message: 'this is a test comment' } } end + assert_redirected_to admin_result_url(test_hash: candidate.test_hash) assert flash[:success] end From 906b62247bc2d52e27ae0d5d24d3584579ac5b76 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Mon, 13 Feb 2017 18:08:51 -0600 Subject: [PATCH 22/46] comment updates --- .sass-lint.yml | 28 +++++++++++++++ .../stylesheets/molecules/_admin_review.scss | 34 ++++++++++++++++++- app/models/quiz_comment.rb | 4 +++ app/views/admin/result/_comment.html.erb | 21 +++++++++++- app/views/admin/result/_comment_form.html.erb | 30 +++++++++++----- app/views/admin/result/view.html.erb | 4 ++- 6 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 .sass-lint.yml diff --git a/.sass-lint.yml b/.sass-lint.yml new file mode 100644 index 0000000..84ba9b0 --- /dev/null +++ b/.sass-lint.yml @@ -0,0 +1,28 @@ +# https://github.com/sasstools/sass-lint/tree/master/docs/rules +files: + include: app/assets/stylesheets/**/*.scss + +options: + formatter: stylish + merge-default-rules: true + +rules: + class-name-format: 0 + id-name-format: 0 + leading-zero: + - 1 + - include: true + no-duplicate-properties: + - 1 + - + exclude: + - src # for @font mixins + no-qualifying-elements: + - 1 + - allow-element-with-attribute: true # input[type='email'] but not div.class-name + quotes: 0 + no-vendor-prefixes: + - + excluded-identifiers: + - -moz-osx-font-smoothing + - -webkit-font-smoothing diff --git a/app/assets/stylesheets/molecules/_admin_review.scss b/app/assets/stylesheets/molecules/_admin_review.scss index 8cbab59..9077423 100644 --- a/app/assets/stylesheets/molecules/_admin_review.scss +++ b/app/assets/stylesheets/molecules/_admin_review.scss @@ -34,10 +34,42 @@ margin-bottom: 30px; text-align: right; - &:before { + &::before { content: '- '; } } + + .comment-edit-stamp { + color: rgba($gray-dark, 0.65); + font-size: 0.75em; + text-align: right; + } + + .comment-edit-btn { + cursor: pointer; + display: inline-block; + font-size: 0.85em; + font-weight: bold; + padding: 2px 5px; + + &:hover { + background-color: $gray-base; + color: $gray-lighter; + } + } + + .comment-edit-form { + display: none; + margin: 30px 0; + padding: 10px 0; + + } + + [type="checkbox"] { + &:checked + .comment-edit-form { + display: block; + } + } } .review_meta { diff --git a/app/models/quiz_comment.rb b/app/models/quiz_comment.rb index 8819997..a918447 100644 --- a/app/models/quiz_comment.rb +++ b/app/models/quiz_comment.rb @@ -4,4 +4,8 @@ class QuizComment < ApplicationRecord belongs_to :candidate, foreign_key: :test_hash, primary_key: :test_hash validates :message, presence: true + + def edits? + updated_at > (created_at + 5.seconds) + end end diff --git a/app/views/admin/result/_comment.html.erb b/app/views/admin/result/_comment.html.erb index b260eca..d47d8be 100644 --- a/app/views/admin/result/_comment.html.erb +++ b/app/views/admin/result/_comment.html.erb @@ -1,2 +1,21 @@ -
<%= comment.message %>
+
+ <%= comment.message %> + + <% if policy(comment).update? %> + + <% end %> + + <% if comment.edits? %> +
Updated <%= time_ago_in_words(comment.updated_at) %> ago
+ <% end %> +
+
<%= comment.user.name %>
+ + +<% if policy(comment).update? %> + +
+ <%= render partial: 'comment_form', locals: {comment: comment, test_hash: comment.test_hash } %> +
+<% end %> diff --git a/app/views/admin/result/_comment_form.html.erb b/app/views/admin/result/_comment_form.html.erb index c2ab89c..701cb7a 100644 --- a/app/views/admin/result/_comment_form.html.erb +++ b/app/views/admin/result/_comment_form.html.erb @@ -1,11 +1,25 @@ -<%= render partial: 'shared/form_model_errors', locals: { obj: @comment } %> +<%= render partial: 'shared/form_model_errors', locals: { obj: comment } %> -<% # admin_update_comment_path # admin_create_comment_path %> -<%= form_for @comment, url: admin_create_comment_path(test_hash), method: :post do |form| %> -
- <%= form.label :message, "Comment" %> - <%= form.text_area :message %> -
+<% if comment.id.nil? %> + + <%= form_for comment, url: admin_create_comment_path(test_hash: test_hash), method: :post do |form| %> +
+ <%= form.label :message, "New Comment" %> + <%= form.text_area :message %> +
+ + <%= submit_tag "Save Comment" %> + <% end %> + +<% else %> + + <%= form_for comment, url: admin_update_comment_path(test_hash: test_hash, id: comment.id), method: :post do |form| %> +
+ <%= form.label :message, "Update Comment" %> + <%= form.text_area :message %> +
+ + <%= submit_tag "Update" %> + <% end %> - <%= submit_tag "Save Comment" %> <% end %> diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index b07c69a..bcc3a8b 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -46,6 +46,8 @@

Comments

<%= render partial: 'comment', collection: @comments, locals: { test_hash: @candidate.test_hash } %> -
<%= render partial: 'comment_form', locals: { test_hash: @candidate.test_hash } %>
+ <% if policy(QuizComment).create? %> + <%= render partial: 'comment_form', locals: {comment: @comment, test_hash: @candidate.test_hash } %> + <% end %>
From a8c42af3de44a2f60446edd2200acb99410ba81a Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Tue, 14 Feb 2017 10:17:42 -0600 Subject: [PATCH 23/46] comment policy and test updates --- app/controllers/admin/comment_controller.rb | 8 ++-- app/policies/quiz_comment_policy.rb | 16 +++---- app/views/admin/result/view.html.erb | 2 +- .../admin/comment_controller_test.rb | 46 +++++++++++++++++++ test/policies/quiz_comment_policy_test.rb | 11 +++-- 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/app/controllers/admin/comment_controller.rb b/app/controllers/admin/comment_controller.rb index cef5cff..9a12d50 100644 --- a/app/controllers/admin/comment_controller.rb +++ b/app/controllers/admin/comment_controller.rb @@ -15,12 +15,10 @@ module Admin end def create - authorize QuizComment - comment = QuizComment.create( - comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash]) - ) + comment = QuizComment.new(comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash])) + authorize comment - flash_message = if comment.persisted? + flash_message = if comment.save { success: "Sucessfully created comment" } else { error: "Failed to save comment" } diff --git a/app/policies/quiz_comment_policy.rb b/app/policies/quiz_comment_policy.rb index 49b850e..f4128f9 100644 --- a/app/policies/quiz_comment_policy.rb +++ b/app/policies/quiz_comment_policy.rb @@ -2,20 +2,18 @@ class QuizCommentPolicy < ApplicationPolicy # Quiz Comment Policy # - # Anyone with access to the results can comment - # Only Comment owner can edit + # Anyone who can vote on results, can comment + # Only comment owner can edit her comment + + def new? + user.acts_as_reviewer? + end def create? - user.acts_as_reviewer? + user.acts_as_reviewer? && record.candidate.reviewers.where(id: user.id).count.positive? end def update? user.acts_as_reviewer? && user.id == record.user_id end - - class Scope < Scope - def resolve - true - end - end end diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index bcc3a8b..0372f3d 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -46,7 +46,7 @@

Comments

<%= render partial: 'comment', collection: @comments, locals: { test_hash: @candidate.test_hash } %> - <% if policy(QuizComment).create? %> + <% if policy(QuizComment).new? %> <%= render partial: 'comment_form', locals: {comment: @comment, test_hash: @candidate.test_hash } %> <% end %>
diff --git a/test/controllers/admin/comment_controller_test.rb b/test/controllers/admin/comment_controller_test.rb index d864127..0c2bf00 100644 --- a/test/controllers/admin/comment_controller_test.rb +++ b/test/controllers/admin/comment_controller_test.rb @@ -14,6 +14,17 @@ module Admin refute_equal comment.message, QuizComment.find_by(id: comment.id).message end + test "should require message to update" do + auth_manager + comment = quiz_comments(:com5) + post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id), + params: { quiz_comment: { message: '' } } + + assert_redirected_to admin_result_url(test_hash: comment.test_hash) + assert flash[:error] + assert_equal comment.message, QuizComment.find_by(id: comment.id).message + end + test "should post create" do auth_reviewer candidate = candidates(:stacy) @@ -26,5 +37,40 @@ module Admin assert_redirected_to admin_result_url(test_hash: candidate.test_hash) assert flash[:success] end + + test "should require comment to create" do + auth_reviewer + candidate = candidates(:stacy) + + assert_difference("QuizComment.count", 0) do + post admin_create_comment_url(test_hash: candidate.test_hash), + params: { quiz_comment: { message: '' } } + end + + assert_redirected_to admin_result_url(test_hash: candidate.test_hash) + assert flash[:error] + end + + test "should not edit others comments" do + auth_reviewer + comment = quiz_comments(:com5) + post admin_update_comment_url(test_hash: comment.test_hash, id: comment.id), + params: { quiz_comment: { message: 'updated comment' } } + + assert_redirected_to admin_login_url + assert_equal comment.message, QuizComment.find_by(id: comment.id).message + end + + test "can not comment on Gustov" do + auth_reviewer + candidate = candidates(:gustov) + + assert_difference("QuizComment.count", 0) do + post admin_create_comment_url(test_hash: candidate.test_hash), + params: { quiz_comment: { message: 'this is a test comment' } } + end + + assert_redirected_to admin_login_url + end end end diff --git a/test/policies/quiz_comment_policy_test.rb b/test/policies/quiz_comment_policy_test.rb index 518681f..6298093 100644 --- a/test/policies/quiz_comment_policy_test.rb +++ b/test/policies/quiz_comment_policy_test.rb @@ -9,11 +9,14 @@ class QuizCommentPolicyTest < PolicyAssertions::Test end def test_create - assert_permit users(:admin), QuizComment - assert_permit users(:manager), QuizComment - assert_permit users(:reviewer), QuizComment + candidate = candidates(:stacy) + comment = QuizComment.new(test_hash: candidate.test_hash) - refute_permit users(:recruiter), QuizComment + assert_permit users(:manager), comment + assert_permit users(:reviewer), comment + + refute_permit users(:admin), comment + refute_permit users(:recruiter), comment end def test_update From 2424fa021fcd98a14ed593121fd476e1dbacffc2 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Tue, 14 Feb 2017 11:32:39 -0600 Subject: [PATCH 24/46] sends emails to all reviewers on new comment --- app/controllers/admin/comment_controller.rb | 2 ++ app/mailers/reviewer_mailer.rb | 7 +++++++ app/views/layouts/mailer.html.inky | 2 +- app/views/reviewer_mailer/new_comment.html.inky | 13 +++++++++++++ app/views/reviewer_mailer/new_comment.text.erb | 9 +++++++++ test/controllers/admin/comment_controller_test.rb | 14 ++++++++++++++ test/mailers/previews/reviewer_mailer_preview.rb | 4 ++++ test/mailers/reviewer_mailer_test.rb | 10 ++++++++++ 8 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 app/views/reviewer_mailer/new_comment.html.inky create mode 100644 app/views/reviewer_mailer/new_comment.text.erb diff --git a/app/controllers/admin/comment_controller.rb b/app/controllers/admin/comment_controller.rb index 9a12d50..6da6843 100644 --- a/app/controllers/admin/comment_controller.rb +++ b/app/controllers/admin/comment_controller.rb @@ -23,6 +23,8 @@ module Admin else { error: "Failed to save comment" } end + + ReviewerMailer.new_comment(comment).deliver_later if comment.persisted? redirect_to admin_result_path(params[:test_hash]), flash: flash_message end diff --git a/app/mailers/reviewer_mailer.rb b/app/mailers/reviewer_mailer.rb index 3955ef9..b640169 100644 --- a/app/mailers/reviewer_mailer.rb +++ b/app/mailers/reviewer_mailer.rb @@ -19,4 +19,11 @@ class ReviewerMailer < ApplicationMailer mail to: @manager.email, subject: "Voting Complete" end + + def new_comment comment + @comment = comment + recipients = comment.candidate.reviewers.map(&:email) + + mail to: recipients, subject: "Skills Assessment Review Comment - #{@comment.test_hash}" + end end diff --git a/app/views/layouts/mailer.html.inky b/app/views/layouts/mailer.html.inky index 12f03c3..9761da3 100644 --- a/app/views/layouts/mailer.html.inky +++ b/app/views/layouts/mailer.html.inky @@ -23,7 +23,7 @@