updating view access for multi departments

This commit is contained in:
Mark Moser 2017-05-04 14:02:41 -05:00
commit b6cc08ecf9
24 changed files with 329 additions and 27 deletions

View File

@ -54,6 +54,7 @@ group :development, :test do
gem 'byebug', platform: :mri gem 'byebug', platform: :mri
gem 'pry-byebug' gem 'pry-byebug'
gem 'pry-rails' gem 'pry-rails'
gem 'table_print'
gem 'faker' gem 'faker'
gem 'brakeman' gem 'brakeman'

View File

@ -280,6 +280,7 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
table_print (1.5.6)
thor (0.19.4) thor (0.19.4)
thread_safe (0.3.5) thread_safe (0.3.5)
tilt (2.0.5) tilt (2.0.5)
@ -348,6 +349,7 @@ DEPENDENCIES
simplecov simplecov
spring spring
spring-watcher-listen (~> 2.0.0) spring-watcher-listen (~> 2.0.0)
table_print
turbolinks (~> 5) turbolinks (~> 5)
tzinfo-data tzinfo-data
uglifier (>= 1.3.0) uglifier (>= 1.3.0)

View File

@ -1,18 +1,14 @@
# frozen_string_literal: true # frozen_string_literal: true
module Admin module Admin
class ResultController < AdminController class ResultController < AdminController
# TODO: change context from Candidate to Quiz # TODO: bypass pundit authorization until a result wrapper class if sorted
# bypass pundit lockdowns until completed
after_action :skip_policy_scope
after_action :skip_authorization after_action :skip_authorization
# # needed for :view
# TODO: Limit results to the quizzes current_user has access to
def index def index
sort_case = "(case when review_status = 0 then '' else name end)" sort_case = "(case when review_status = 0 then '' else name end)"
sort_with_case = sort_column == 'name' ? sort_case : sort_column sort_with_case = sort_column == 'name' ? sort_case : sort_column
@candidates = Candidate.where(completed: true) @candidates = policy_scope(:result).includes(:recruiter)
.includes(:recruiter)
.order("#{sort_with_case} #{sort_direction}") .order("#{sort_with_case} #{sort_direction}")
.page(params[:page]) .page(params[:page])
end end

View File

@ -45,6 +45,7 @@ class CandidateController < ApplicationController
end end
def send_to_oops def send_to_oops
redirect_to welcome_path and return if current_candidate && current_candidate.stale?
redirect_to oops_path if current_candidate redirect_to oops_path if current_candidate
end end
end end

View File

@ -48,6 +48,17 @@ class Candidate < ApplicationRecord
answers.where(submitted: true) answers.where(submitted: true)
end end
def last_answered_at
return Time.current unless submitted_answers.count.positive?
submitted_answers.order(updated_at: :desc).first.updated_at
end
def stale?
return true unless answers.count.positive?
minutes_since_answered = (Time.current.minus_with_coercion(last_answered_at) / 60).round
minutes_since_answered > 45
end
def answered_questions def answered_questions
answers.where.not(answer: nil) answers.where.not(answer: nil)
.where("answers.answer not like '%later:%'") .where("answers.answer not like '%later:%'")

View File

@ -25,10 +25,10 @@ class QuizPolicy < ApplicationPolicy
class Scope < Scope class Scope < Scope
def resolve def resolve
if user.reviewer? if user.acts_as_recruiter?
scope.joins(:reviewers).where('reviewer_to_quizzes.user_id = ?', user.id)
else
scope scope
else
scope.joins(:reviewers).where('reviewer_to_quizzes.user_id = ?', user.id)
end end
end end
end end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
class ResultPolicy < Struct.new(:user, :result)
# Result Access Policy
#
# Only Admins and Recruiters can view all results
# Managers and Reviewers can view any completed quiz they are linked to
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "Must be logged in." unless user
@user = user
@record = record
end
def index?
true
end
# def view?
# return true if user.acts_as_recruiter?
# user.reviewees.include? record
# end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user.acts_as_recruiter?
Candidate.where(completed: true)
else
user.reviewees.where(completed: true)
end
end
end
end

View File

@ -8,13 +8,13 @@
User.create( User.create(
name: 'admin', name: 'admin',
email: 'pdr.admin@mailinator.com', email: 'pda.admin@mailinator.com',
password_digest: BCrypt::Password.create("this is the admin password"), password_digest: BCrypt::Password.create("this is the admin password"),
role: 'admin' role: 'admin'
) )
Quiz.create( Quiz.create(
name: 'PDR Standard FED Screening', name: 'PDA Standard FED Screening',
unit: 'FED', unit: 'FED',
dept: 'PDR' dept: 'PDA'
) )

BIN
erd.pdf

Binary file not shown.

View File

@ -47,7 +47,7 @@ module Admin
test "recruiter should auth to dashboard" do test "recruiter should auth to dashboard" do
post admin_auth_url, params: { auth: post admin_auth_url, params: { auth:
{ email: 'pdr.recruiter@mailinator.com', password: 'password' } } { email: 'pda.recruiter@mailinator.com', password: 'password' } }
assert_redirected_to admin_url assert_redirected_to admin_url
end end

View File

@ -25,7 +25,7 @@ module Admin
auth_recruiter auth_recruiter
get admin_candidates_url get admin_candidates_url
assert_response :success assert_response :success
assert_select "a[href='#{admin_edit_candidate_path(candidates(:martha))}']" assert_select "a[href='#{admin_edit_candidate_path(candidates(:gillian))}']"
end end
end end
end end

View File

@ -698,8 +698,8 @@ wade10:
jorge1: jorge1:
candidate: jorge 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. question: fed1
answer: option 3 answer: 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.
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 36.hours - 22.minutes %> created_at: <%= DateTime.now() - 36.hours - 22.minutes %>
@ -890,3 +890,87 @@ elsie10:
created_at: <%= DateTime.now() - 36.hours - 40.minutes %> created_at: <%= DateTime.now() - 36.hours - 40.minutes %>
updated_at: <%= DateTime.now() - 36.hours - 20.minutes %> updated_at: <%= DateTime.now() - 36.hours - 20.minutes %>
##########################
############# Studio Quiz
ethan1:
candidate: ethan
question: studio1
answer: 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.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 36.hours - 22.minutes %>
updated_at: <%= DateTime.now() - 36.hours - 22.minutes %>
ethan2:
candidate: ethan
question: studio2
answer: Vestibulum id ligula porta felis euismod semper.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 36.hours - 24.minutes %>
updated_at: <%= DateTime.now() - 36.hours - 4.minutes %>
ethan3:
candidate: ethan
question: studio3
answer: Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nulla vitae elit libero, a pharetra augue.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 36.hours - 26.minutes %>
updated_at: <%= DateTime.now() - 36.hours - 6.minutes %>
adele1:
candidate: adele
question: studio1
answer: 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.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 16.hours - 22.minutes %>
updated_at: <%= DateTime.now() - 16.hours - 22.minutes %>
adele2:
candidate: adele
question: studio2
answer: Vestibulum id ligula porta felis euismod semper.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 16.hours - 24.minutes %>
updated_at: <%= DateTime.now() - 16.hours - 4.minutes %>
adele3:
candidate: adele
question: studio3
answer: Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nulla vitae elit libero, a pharetra augue.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 16.hours - 26.minutes %>
updated_at: <%= DateTime.now() - 16.hours - 6.minutes %>
carl1:
candidate: carl
question: studio1
answer: 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.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 22.minutes %>
updated_at: <%= DateTime.now() - 22.minutes %>
carl2:
candidate: carl
question: studio2
answer: Vestibulum id ligula porta felis euismod semper.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 24.minutes %>
updated_at: <%= DateTime.now() - 4.minutes %>
carl3:
candidate: carl
question: studio3
answer: Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nulla vitae elit libero, a pharetra augue.
saved: 0
submitted: true
created_at: <%= DateTime.now() - 26.minutes %>
updated_at: <%= DateTime.now() - 6.minutes %>

View File

@ -179,3 +179,42 @@ gustov: # Gustov is NOT for FED
reminded: false reminded: false
test_hash: kp6tfghjyapJnkz2N test_hash: kp6tfghjyapJnkz2N
ethan: # Completed quiz for studio
name: Ethan Woodward
email: <%= CryptSerializer.dump 'ethan.woodward@mailinator.com' %>
experience: 0-3
project: Studio Client
position: 'full-time'
skill_needs: 'Angular, HTML'
recruiter: recruiter
quiz: studio
completed: true
reminded: false
test_hash: vNgQo2c5/HZL2CN
adele: # Completed quiz for studio
name: Adele Kearney
email: <%= CryptSerializer.dump 'adele.kearney@mailinator.com' %>
experience: 0-3
project: Studio Client
position: 'full-time'
skill_needs: 'Angular, HTML'
recruiter: recruiter
quiz: studio
completed: true
reminded: false
test_hash: 37GmHL0Odjwv
carl: # Completed quiz for studio
name: Carl Mitchell
email: <%= CryptSerializer.dump 'carle.mitchell@mailinator.com' %>
experience: 0-3
project: Studio Client
position: 'full-time'
skill_needs: 'Angular, HTML'
recruiter: recruiter
quiz: studio
completed: true
reminded: false
test_hash: hANPsTL1XHcmi

View File

@ -150,3 +150,32 @@ admin1:
sort: 0 sort: 0
active: true active: true
studio1:
quiz: studio
question: 'Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas faucibus mollis interdum.'
category: Ipsum
input_type: text
input_options:
sort: 0
active: true
studio2:
quiz: studio
question: 'Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas faucibus mollis interdum.'
category: Magna
input_type: text
input_options:
sort: 0
active: true
studio3:
quiz: studio
question: 'Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Maecenas faucibus mollis interdum.'
category: Commodo
input_type: text
input_options:
sort: 0
active: true

View File

@ -1,11 +1,16 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
fed: fed:
name: PDR Standard FED Screening name: PDA Standard FED Screening
unit: PDR unit: PD Agency
dept: FED dept: FED
admin: admin:
name: An extra quiz not assigned to anyone name: An extra quiz not assigned to anyone
unit: PDR unit: PD
dept: NOPE dept: NOPE
studio:
name: Studio Screening One
unit: PD Studio
dept: FED

View File

@ -11,3 +11,11 @@ two:
three: three:
user: manager user: manager
quiz: fed quiz: fed
four:
user: studio_manager
quiz: studio
five:
user: studio_reviewer
quiz: studio

View File

@ -93,3 +93,29 @@ reviewer2_elsie:
candidate: elsie candidate: elsie
user: reviewer2 user: reviewer2
studio_reviewer_carle:
candidate: carle
user: studio_reviewer
studio_reviewer_ethan:
candidate: ethan
user: studio_reviewer
studio_reviewer_adele:
candidate: adele
user: studio_reviewer
studio_manager_carle:
candidate: carle
user: studio_manager
studio_manager_ethan:
candidate: ethan
user: studio_manager
studio_manager_adele:
user: studio_manager
candidate: adele

View File

@ -2,7 +2,7 @@
recruiter: recruiter:
name: Sam Recruiter name: Sam Recruiter
email: pdr.recruiter@mailinator.com email: pda.recruiter@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %> password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
role: recruiter role: recruiter
@ -29,3 +29,16 @@ admin:
email: alan.admin@mailinator.com email: alan.admin@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %> password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
role: admin role: admin
studio_manager:
name: Studio Manager
email: studio.manager@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
role: manager
studio_reviewer:
name: Studio Reviewer
email: studio.reviewer@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
role: reviewer

View File

@ -34,4 +34,23 @@ class CandidateTest < ActiveSupport::TestCase
candidate.build_reviews candidate.build_reviews
assert_equal 3, candidate.votes.count assert_equal 3, candidate.votes.count
end end
test 'can get last answer timestamp' do
candidate = candidates(:roy)
roy_last = answers(:roy2).updated_at
assert_equal roy_last, candidate.last_answered_at
end
test 'gillian is stale with no answers' do
candidate = candidates(:gillian)
assert candidate.stale?
end
test 'roy is stale with answers' do
candidate = candidates(:roy)
assert candidate.stale?
end
end end

View File

@ -14,7 +14,7 @@ class ReviewerVoteTest < ActiveSupport::TestCase
test "manager has a vote for every completed quiz" do test "manager has a vote for every completed quiz" do
manager = users(:manager) manager = users(:manager)
completed_count = Candidate.where(completed: true).count completed_count = 6
assert_equal completed_count, manager.votes.size assert_equal completed_count, manager.votes.size
end end

View File

@ -15,7 +15,7 @@ class QuizPolicyTest < PolicyAssertions::Test
test 'should allow manager to scope' do test 'should allow manager to scope' do
scope = QuizPolicy::Scope.new(users(:manager), Quiz).resolve scope = QuizPolicy::Scope.new(users(:manager), Quiz).resolve
assert_equal Quiz.count, scope.count assert_equal users(:manager).quizzes.count, scope.count
end end
test 'should allow reviewer to scope' do test 'should allow reviewer to scope' do

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'test_helper'
class ResultPolicyTest < PolicyAssertions::Test
def test_index
assert_permit users(:admin), :result
assert_permit users(:recruiter), :result
assert_permit users(:manager), :result
assert_permit users(:reviewer), :result
end
test 'should allow admin to scope' do
scope = ResultPolicy::Scope.new(users(:admin), Candidate).resolve
assert_equal Candidate.where(completed: true).count, scope.count
end
test 'should allow recruiter to scope' do
scope = ResultPolicy::Scope.new(users(:recruiter), Candidate).resolve
assert_equal Candidate.where(completed: true).count, scope.count
end
test 'should not allow fed.reviewer to scope studio results' do
reviewer = users(:reviewer)
scope = ResultPolicy::Scope.new(reviewer, Candidate).resolve
assert_equal reviewer.reviewees.where(completed: true).count, scope.count
end
end

View File

@ -21,7 +21,7 @@ module AuthTestHelper
def auth_recruiter def auth_recruiter
post admin_auth_url, params: { auth: post admin_auth_url, params: { auth:
{ email: 'pdr.recruiter@mailinator.com', password: 'password' } } { email: 'pda.recruiter@mailinator.com', password: 'password' } }
end end
def auth_reviewer def auth_reviewer

View File

@ -4,7 +4,7 @@ require 'test_helper'
class ReviewerReminderTest < ActiveSupport::TestCase class ReviewerReminderTest < ActiveSupport::TestCase
test "collection is created with results" do test "collection is created with results" do
reminders = ReviewerReminder.new reminders = ReviewerReminder.new
assert_equal 6, reminders.size assert_equal 8, reminders.size
end end
test "each reminder has needed attributes" do test "each reminder has needed attributes" do