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 @@
+ Hello <%= @manager.name %>,
+ Everyone has voted and you need to request or decline an interview for
+ <%= link_to nil, admin_result_url(@candidate.test_hash) %>
+
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: '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