add filters to submissionsConnection for enrollment state

closes EVAL-3897
flag=none

Adds the include_concluded, include_deactivated, and
apply_gradebook_enrollment_filters filters to the submissionsConnection.
These filters allow for submissions belonging to concluded/deactivated
students to be returned.

Test Plan:
- specs pass

Change-Id: Ie6b6fa213a2d31d3c60fef5ce75c28bb6107b295
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/337515
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Derek Williams <derek.williams@instructure.com>
Reviewed-by: Kai Bjorkman <kbjorkman@instructure.com>
QA-Review: Spencer Olson <solson@instructure.com>
Product-Review: Cameron Ray <cameron.ray@instructure.com>
This commit is contained in:
Spencer Olson 2024-01-12 13:37:51 -06:00
parent b98805b6c2
commit 75e5d4da31
4 changed files with 158 additions and 5 deletions

View File

@ -22,12 +22,25 @@ module Types
class SubmissionSearchFilterInputType < Types::BaseInputObject
graphql_name "SubmissionSearchFilterInput"
argument :apply_gradebook_enrollment_filters, Boolean, <<~MD, required: false
Filters submissions for deactivated and concluded users based on the calling user's
'Show -> Inactive Enrollments' and 'Show -> Concluded Enrollments' settings in the Gradebook.
When true, this filter takes precedence over the include_concluded and include_deactivated filters.
MD
argument :include_unsubmitted, Boolean, required: false
argument :states, [SubmissionStateType], required: false, default_value: DEFAULT_SUBMISSION_STATES
argument :section_ids, [ID], required: false, prepare: GraphQLHelpers.relay_or_legacy_ids_prepare_func("Section")
argument :enrollment_types, [EnrollmentTypeType], required: false
argument :include_concluded, Boolean, <<~MD, required: false
Include submissions for concluded students.
MD
argument :include_deactivated, Boolean, <<~MD, required: false
Include submissions for deactivated students.
MD
argument :user_search, String, <<~MD, required: false
The partial name or full ID of the users to match and return in the

View File

@ -3033,7 +3033,8 @@ class Course < ActiveRecord::Base
user,
visibilities,
visibility,
enrollment_state: opts[:enrollment_state])
enrollment_state: opts[:enrollment_state],
exclude_enrollment_state: opts[:exclude_enrollment_state])
end
def enrollments_visible_to(user, opts = {})
@ -3044,8 +3045,12 @@ class Course < ActiveRecord::Base
apply_enrollment_visibilities_internal(enrollment_scope.except(:preload), user, visibilities, visibility)
end
def apply_enrollment_visibilities_internal(scope, user, visibilities, visibility, enrollment_state: nil)
scope = scope.where(enrollments: { workflow_state: enrollment_state }) if enrollment_state
def apply_enrollment_visibilities_internal(scope, user, visibilities, visibility, enrollment_state: nil, exclude_enrollment_state: nil)
if enrollment_state
scope = scope.where(enrollments: { workflow_state: enrollment_state })
elsif exclude_enrollment_state
scope = scope.where.not(enrollments: { workflow_state: exclude_enrollment_state })
end
# See also MessageableUsers (same logic used to get users across multiple courses) (should refactor)
case visibility
when :full then scope

View File

@ -68,8 +68,7 @@ class SubmissionSearch
search_scope = if @course.grants_any_right?(@searcher, @session, :manage_grades, :view_all_grades) || @course.participating_observers.map(&:id).include?(@searcher.id)
# a user with manage_grades, view_all_grades, or an observer can see other users' submissions
# TODO: may want to add a preloader for this
allowed_user_ids = @course.users_visible_to(@searcher)
search_scope.where(user_id: allowed_user_ids)
search_scope.where(user_id: allowed_users)
elsif @course.grants_right?(@searcher, @session, :read_grades)
# a user can see their own submission
search_scope.where(user_id: @searcher.id)
@ -125,4 +124,36 @@ class SubmissionSearch
end
search_scope.order(:user_id)
end
private
def allowed_users
if @options[:apply_gradebook_enrollment_filters]
@course.users_visible_to(@searcher, true, exclude_enrollment_state: excluded_enrollment_states_from_gradebook_settings)
elsif @options[:include_concluded] || @options[:include_deactivated]
@course.users_visible_to(@searcher, true, exclude_enrollment_state: excluded_enrollment_states_from_filters)
else
@course.users_visible_to(@searcher)
end
end
def excluded_enrollment_states_from_gradebook_settings
settings = @searcher.get_preference(:gradebook_settings, @course.global_id) || {}
excluded_enrollment_states(
completed: settings["show_concluded_enrollments"] != "true",
inactive: settings["show_inactive_enrollments"] != "true"
)
end
def excluded_enrollment_states_from_filters
excluded_enrollment_states(
completed: !@options[:include_concluded],
inactive: !@options[:include_deactivated]
)
end
def excluded_enrollment_states(states)
excluded_states = states.filter_map { |state, excluded| state if excluded }
excluded_states << :rejected
end
end

View File

@ -57,6 +57,110 @@ describe SubmissionSearch do
expect(results.preload(:user).map(&:user)).to eq students
end
it "excludes rejected students by default" do
course.enrollments.find_by(user: jonah).reject
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }]).search
expect(results.where(user: jonah).exists?).to be false
end
it "excludes deactivated students by default" do
course.enrollments.find_by(user: jonah).deactivate
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }]).search
expect(results.where(user: jonah).exists?).to be false
end
it "optionally includes deactivated students" do
course.enrollments.find_by(user: jonah).deactivate
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }], include_deactivated: true).search
expect(results.where(user: jonah).exists?).to be true
end
it "excludes rejected students when including deactivated students" do
course.enrollments.find_by(user: jonah).reject
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }], include_deactivated: true).search
expect(results.where(user: jonah).exists?).to be false
end
it "excludes concluded students by default" do
course.enrollments.find_by(user: jonah).conclude
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }]).search
expect(results.where(user: jonah).exists?).to be false
end
it "optionally includes concluded students" do
course.enrollments.find_by(user: jonah).conclude
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }], include_concluded: true).search
expect(results.where(user: jonah).exists?).to be true
end
it "excludes rejected students when including concluded students" do
course.enrollments.find_by(user: jonah).reject
results = SubmissionSearch.new(assignment, teacher, nil, order_by: [{ field: "username" }], include_concluded: true).search
expect(results.where(user: jonah).exists?).to be false
end
it "optionally includes deactivated students via gradebook settings" do
course.enrollments.find_by(user: jonah).deactivate
teacher.preferences[:gradebook_settings] = {
course.global_id => {
"show_inactive_enrollments" => "true"
}
}
teacher.save!
results = SubmissionSearch.new(
assignment,
teacher,
nil,
order_by: [{ field: "username" }],
apply_gradebook_enrollment_filters: true
).search
expect(results.where(user: jonah).exists?).to be true
end
it "optionally includes concluded students via gradebook settings" do
course.enrollments.find_by(user: jonah).conclude
teacher.preferences[:gradebook_settings] = {
course.global_id => {
"show_concluded_enrollments" => "true"
}
}
teacher.save!
results = SubmissionSearch.new(
assignment,
teacher,
nil,
order_by: [{ field: "username" }],
apply_gradebook_enrollment_filters: true
).search
expect(results.where(user: jonah).exists?).to be true
end
it "ignores include_concluded and include_deactivated when apply_gradebook_enrollment_filters is true" do
course.enrollments.find_by(user: amanda).deactivate
course.enrollments.find_by(user: jonah).conclude
teacher.preferences[:gradebook_settings] = {
course.global_id => {
"show_concluded_enrollments" => "false",
"show_inactive_enrollments" => "false"
}
}
teacher.save!
results = SubmissionSearch.new(
assignment,
teacher,
nil,
order_by: [{ field: "username" }],
apply_gradebook_enrollment_filters: true,
include_concluded: true,
include_deactivated: true
).search
aggregate_failures do
expect(results.where(user: amanda).exists?).to be false
expect(results.where(user: jonah).exists?).to be false
end
end
it "finds submissions with user name search" do
results = SubmissionSearch.new(assignment,
teacher,