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 'pry-byebug'
gem 'pry-rails'
gem 'table_print'
gem 'faker'
gem 'brakeman'

View File

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

View File

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

View File

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

View File

@ -48,6 +48,17 @@ class Candidate < ApplicationRecord
answers.where(submitted: true)
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
answers.where.not(answer: nil)
.where("answers.answer not like '%later:%'")

View File

@ -25,10 +25,10 @@ class QuizPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.reviewer?
scope.joins(:reviewers).where('reviewer_to_quizzes.user_id = ?', user.id)
else
if user.acts_as_recruiter?
scope
else
scope.joins(:reviewers).where('reviewer_to_quizzes.user_id = ?', user.id)
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(
name: 'admin',
email: 'pdr.admin@mailinator.com',
email: 'pda.admin@mailinator.com',
password_digest: BCrypt::Password.create("this is the admin password"),
role: 'admin'
)
Quiz.create(
name: 'PDR Standard FED Screening',
name: 'PDA Standard FED Screening',
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
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
end

View File

@ -25,7 +25,7 @@ module Admin
auth_recruiter
get admin_candidates_url
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

View File

@ -698,8 +698,8 @@ wade10:
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
question: fed1
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 %>
@ -890,3 +890,87 @@ elsie10:
created_at: <%= DateTime.now() - 36.hours - 40.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
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
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
fed:
name: PDR Standard FED Screening
unit: PDR
name: PDA Standard FED Screening
unit: PD Agency
dept: FED
admin:
name: An extra quiz not assigned to anyone
unit: PDR
unit: PD
dept: NOPE
studio:
name: Studio Screening One
unit: PD Studio
dept: FED

View File

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

View File

@ -93,3 +93,29 @@ reviewer2_elsie:
candidate: elsie
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:
name: Sam Recruiter
email: pdr.recruiter@mailinator.com
email: pda.recruiter@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
role: recruiter
@ -29,3 +29,16 @@ admin:
email: alan.admin@mailinator.com
password_digest: <%= BCrypt::Password.create("password", cost: 4) %>
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
assert_equal 3, candidate.votes.count
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

View File

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

View File

@ -15,7 +15,7 @@ class QuizPolicyTest < PolicyAssertions::Test
test 'should allow manager to scope' do
scope = QuizPolicy::Scope.new(users(:manager), Quiz).resolve
assert_equal Quiz.count, scope.count
assert_equal users(:manager).quizzes.count, scope.count
end
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
post admin_auth_url, params: { auth:
{ email: 'pdr.recruiter@mailinator.com', password: 'password' } }
{ email: 'pda.recruiter@mailinator.com', password: 'password' } }
end
def auth_reviewer

View File

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