move SQL from UngradedDiscussionStudentVisibility view to rails
moving logic from a view (that has become too complex to properly push down WHERE clause predicates) to a service. The SQL is existing view definition SQL, from MigrationHelpers::StudentVisibilities::StudentVisibilitiesV7 with the improvements recommended by DBAs (don't use a view; put the WHERE clause on each SELECT in the UNION or EXCEPT) New tests call this service, but no application code is calling it yet. flag = differentiated_modules refs LX-1727 Test Plan: - tests pass Change-Id: Ia6f3fd3f6edc4f90f5d0686ce8f84e38c722d6fb Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/348646 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Jen Smith <jen.smith@instructure.com> QA-Review: Jen Smith <jen.smith@instructure.com> Product-Review: Jen Smith <jen.smith@instructure.com>
This commit is contained in:
parent
7718f4ed6d
commit
08d9ceb884
|
@ -0,0 +1,58 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module UngradedDiscussionVisibility
|
||||
module Entities
|
||||
# When a discussion topic is visible to a (student) user
|
||||
class UngradedDiscussionVisibleToStudent
|
||||
attr_reader :course_id,
|
||||
:user_id,
|
||||
:discussion_topic_id
|
||||
|
||||
def initialize(course_id:,
|
||||
user_id:,
|
||||
discussion_topic_id:)
|
||||
raise ArgumentError, "course_id cannot be nil" if course_id.nil?
|
||||
raise ArgumentError, "user_id cannot be nil" if user_id.nil?
|
||||
raise ArgumentError, "discussion_topic_id cannot be nil" if discussion_topic_id.nil?
|
||||
|
||||
@course_id = course_id
|
||||
@user_id = user_id
|
||||
@discussion_topic_id = discussion_topic_id
|
||||
end
|
||||
|
||||
# two UngradedDiscussionVisibleToStudent DTOs are equal if all of their attributes are equal
|
||||
def ==(other)
|
||||
return false unless other.is_a?(UngradedDiscussionVisibleToStudent)
|
||||
|
||||
course_id == other.course_id &&
|
||||
user_id == other.user_id &&
|
||||
discussion_topic_id == other.discussion_topic_id
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
self == other
|
||||
end
|
||||
|
||||
def hash
|
||||
[course_id, user_id, discussion_topic_id].hash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,230 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module UngradedDiscussionVisibility
|
||||
module Repositories
|
||||
class UngradedDiscussionVisibleToStudentRepository
|
||||
class << self
|
||||
# NOTE: context module has a pretty different function for a few of the functions implemented here
|
||||
|
||||
# if only_visible_to_overrides is false, or there's related modules with no overrides, then everyone can see it
|
||||
def find_discussion_topics_visible_to_everyone(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join context modules */
|
||||
#{VisibilitySqlHelper.module_items_join_sql(content_tag_type: "DiscussionTopic")}
|
||||
|
||||
/* join assignment override */
|
||||
#{VisibilitySqlHelper.assignment_override_everyone_join_sql}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.assignment_override_everyone_filter_sql(filter_condition_sql:)}
|
||||
|
||||
SQL
|
||||
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
# section overrides and related module section overrides
|
||||
def find_discussion_topics_visible_to_sections(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join context modules */
|
||||
#{VisibilitySqlHelper.module_items_join_sql(content_tag_type: "DiscussionTopic")}
|
||||
|
||||
/* join assignment overrides (assignment or related context module) for CourseSection */
|
||||
#{VisibilitySqlHelper.assignment_override_section_join_sql(id_column_name: "discussion_topic_id")}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.section_override_filter_sql(filter_condition_sql:)}
|
||||
SQL
|
||||
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
# students with unassigned section overrides
|
||||
def find_discussion_topics_with_unassigned_section_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join assignment override for 'CourseSection' (no module check) */
|
||||
#{VisibilitySqlHelper.assignment_override_unassign_section_join_sql(id_column_name: "discussion_topic_id")}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.assignment_override_unassign_section_filter_sql(filter_condition_sql:)}
|
||||
SQL
|
||||
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
# students with unassigned adhoc overrides
|
||||
def find_discussion_topics_with_unassigned_adhoc_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join assignment overrides for 'ADHOC' (no module check) */
|
||||
#{VisibilitySqlHelper.assignment_override_unassign_adhoc_join_sql(id_column_name: "discussion_topic_id")}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.assignment_override_unassign_adhoc_filter_sql(filter_condition_sql:)}
|
||||
SQL
|
||||
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
# ADHOC overrides and related module ADHOC overrides
|
||||
def find_discussion_topics_visible_to_adhoc_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join context modules */
|
||||
#{VisibilitySqlHelper.module_items_join_sql(content_tag_type: "DiscussionTopic")}
|
||||
|
||||
/* join assignment override for 'ADHOC' */
|
||||
#{VisibilitySqlHelper.assignment_override_adhoc_join_sql(id_column_name: "discussion_topic_id")}
|
||||
|
||||
/* join AssignmentOverrideStudent */
|
||||
#{VisibilitySqlHelper.assignment_override_student_join_sql}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.adhoc_override_filter_sql(filter_condition_sql:)}
|
||||
SQL
|
||||
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
# course overrides
|
||||
def find_discussion_topics_visible_to_course_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
filter_condition_sql = filter_condition_sql(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_sql = <<~SQL.squish
|
||||
#{discussion_topic_select_sql}
|
||||
|
||||
/* join active student enrollments */
|
||||
#{VisibilitySqlHelper.enrollment_join_sql}
|
||||
|
||||
/* join assignment override for 'Course' */
|
||||
#{VisibilitySqlHelper.assignment_override_course_join_sql(id_column_name: "discussion_topic_id")}
|
||||
|
||||
/* filtered to course_id, user_id, discussion_topic_id, and additional conditions */
|
||||
#{VisibilitySqlHelper.course_override_filter_sql(filter_condition_sql:)}
|
||||
|
||||
SQL
|
||||
query_params = query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def exec_find_discussion_topic_visibility_query(query_sql:, query_params:)
|
||||
# safely replace parameters in the filter clause
|
||||
sanitized_sql = ActiveRecord::Base.sanitize_sql_array([query_sql, query_params])
|
||||
|
||||
# Execute the query
|
||||
query_results = ActiveRecord::Base.connection.exec_query(sanitized_sql)
|
||||
|
||||
# map the results to an array of AssignmentVisibleToStudent (DTO / PORO) and return it
|
||||
query_results.map do |row|
|
||||
UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(course_id: row["course_id"], discussion_topic_id: row["discussion_topic_id"], user_id: row["user_id"])
|
||||
end
|
||||
end
|
||||
|
||||
def query_params(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
query_params = {}
|
||||
query_params[:course_id] = course_id_params unless course_id_params.nil?
|
||||
query_params[:user_id] = user_id_params unless user_id_params.nil?
|
||||
query_params[:discussion_topic_id] = discussion_topic_id_params unless discussion_topic_id_params.nil?
|
||||
query_params
|
||||
end
|
||||
|
||||
# Create a filter clause SQL from the params - something like: e.user_id IN ['1', '2'] AND course_id = '20'
|
||||
# Note that at least one of the params must be non nil
|
||||
def filter_condition_sql(course_id_params: nil, user_id_params: nil, discussion_topic_id_params: nil)
|
||||
query_conditions = []
|
||||
|
||||
if discussion_topic_id_params
|
||||
query_conditions << if discussion_topic_id_params.is_a?(Array)
|
||||
"o.id IN (:discussion_topic_id)"
|
||||
else
|
||||
"o.id = :discussion_topic_id"
|
||||
end
|
||||
end
|
||||
|
||||
if user_id_params
|
||||
query_conditions << if user_id_params.is_a?(Array)
|
||||
"e.user_id IN (:user_id)"
|
||||
else
|
||||
"e.user_id = :user_id"
|
||||
end
|
||||
end
|
||||
|
||||
if course_id_params
|
||||
query_conditions << if course_id_params.is_a?(Array)
|
||||
"e.course_id IN (:course_id)"
|
||||
else
|
||||
"e.course_id = :course_id"
|
||||
end
|
||||
end
|
||||
|
||||
if query_conditions.empty?
|
||||
raise ArgumentError, "UngradedDiscussionsVisibleToStudents must have a limiting where clause of at least one course_id, user_id, or discussion_topic_id (for performance reasons)"
|
||||
end
|
||||
|
||||
query_conditions.join(" AND ")
|
||||
end
|
||||
|
||||
def discussion_topic_select_sql
|
||||
<<~SQL.squish
|
||||
SELECT DISTINCT o.id as discussion_topic_id,
|
||||
e.user_id as user_id,
|
||||
e.course_id as course_id
|
||||
FROM #{DiscussionTopic.quoted_table_name} o
|
||||
SQL
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,126 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module UngradedDiscussionVisibility
|
||||
class UngradedDiscussionVisibilityService
|
||||
class << self
|
||||
def discussion_topics_visible_to_student(course_id:, user_id:)
|
||||
raise ArgumentError, "course_id cannot be nil" if course_id.nil?
|
||||
raise ArgumentError, "course_id must not be an array" if course_id.is_a?(Array)
|
||||
raise ArgumentError, "user_id cannot be nil" if user_id.nil?
|
||||
raise ArgumentError, "user_id must not be an array" if user_id.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(course_id_params: course_id, user_id_params: user_id)
|
||||
end
|
||||
|
||||
def discussion_topics_visible_to_students_in_courses(course_ids:, user_ids:)
|
||||
raise ArgumentError, "course_ids cannot be nil" if course_ids.nil?
|
||||
raise ArgumentError, "course_ids must be an array" unless course_ids.is_a?(Array)
|
||||
raise ArgumentError, "user_ids cannot be nil" if user_ids.nil?
|
||||
raise ArgumentError, "user_ids must be an array" unless user_ids.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(course_id_params: course_ids, user_id_params: user_ids)
|
||||
end
|
||||
|
||||
def discussion_topic_visible_to_student(discussion_topic_id:, user_id:)
|
||||
raise ArgumentError, "discussion_topic_id cannot be nil" if discussion_topic_id.nil?
|
||||
raise ArgumentError, "discussion_topic_id must not be an array" if discussion_topic_id.is_a?(Array)
|
||||
raise ArgumentError, "user_id cannot be nil" if user_id.nil?
|
||||
raise ArgumentError, "user_id must not be an array" if user_id.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(discussion_topic_id_params: discussion_topic_id, user_id_params: user_id)
|
||||
end
|
||||
|
||||
def discussion_topic_visible_to_students(discussion_topic_id:, user_ids:)
|
||||
raise ArgumentError, "discussion_topic_id cannot be nil" if discussion_topic_id.nil?
|
||||
raise ArgumentError, "discussion_topic_id must not be an array" if discussion_topic_id.is_a?(Array)
|
||||
raise ArgumentError, "user_ids cannot be nil" if user_ids.nil?
|
||||
raise ArgumentError, "user_ids must be an array" unless user_ids.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(discussion_topic_id_params: discussion_topic_id, user_id_params: user_ids)
|
||||
end
|
||||
|
||||
def discussion_topic_visible_to_students_in_course(discussion_topic_id:, user_ids:, course_id:)
|
||||
raise ArgumentError, "course_id cannot be nil" if course_id.nil?
|
||||
raise ArgumentError, "course_id must not be an array" if course_id.is_a?(Array)
|
||||
raise ArgumentError, "discussion_topic_id cannot be nil" if discussion_topic_id.nil?
|
||||
raise ArgumentError, "discussion_topic_id must not be an array" if discussion_topic_id.is_a?(Array)
|
||||
raise ArgumentError, "user_ids cannot be nil" if user_ids.nil?
|
||||
raise ArgumentError, "user_ids must be an array" unless user_ids.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(course_id_params: course_id, discussion_topic_id_params: discussion_topic_id, user_id_params: user_ids)
|
||||
end
|
||||
|
||||
def discussion_topic_visible_in_course(discussion_topic_id:, course_id:)
|
||||
raise ArgumentError, "course_id cannot be nil" if course_id.nil?
|
||||
raise ArgumentError, "course_id must not be an array" if course_id.is_a?(Array)
|
||||
raise ArgumentError, "discussion_topic_id cannot be nil" if discussion_topic_id.nil?
|
||||
raise ArgumentError, "discussion_topic_id must not be an array" if discussion_topic_id.is_a?(Array)
|
||||
|
||||
discussion_topics_visible_to_students(course_id_params: course_id, discussion_topic_id_params: discussion_topic_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def discussion_topics_visible_to_students(course_id_params: nil, user_id_params: nil, discussion_topic_id_params: nil)
|
||||
if course_id_params.nil? && user_id_params.nil? && discussion_topic_id_params.nil?
|
||||
raise ArgumentError, "at least one non nil course_id, user_id, or discussion_topic_id_params is required (for query performance reasons)"
|
||||
end
|
||||
|
||||
visible_discussion_topics = []
|
||||
|
||||
# add discussion topics visible to everyone
|
||||
discussion_topics_visible_to_all = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_visible_to_everyone(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
visible_discussion_topics |= discussion_topics_visible_to_all
|
||||
|
||||
# add discussion topics visible to sections (and related module section overrides)
|
||||
discussion_topics_visible_to_sections = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_visible_to_sections(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
visible_discussion_topics |= discussion_topics_visible_to_sections
|
||||
|
||||
# remove discussion topics for students with unassigned section overrides
|
||||
discussion_topics_with_unassigned_section_overrides = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_with_unassigned_section_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
visible_discussion_topics -= discussion_topics_with_unassigned_section_overrides
|
||||
|
||||
# add discussion topics visible due to ADHOC overrides (and related module ADHOC overrides)
|
||||
discussion_topics_visible_to_adhoc_overrides = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_visible_to_adhoc_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
visible_discussion_topics |= discussion_topics_visible_to_adhoc_overrides
|
||||
|
||||
# remove discussion topics for students with unassigned ADHOC overrides
|
||||
discussion_topics_with_unassigned_adhoc_overrides = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_with_unassigned_adhoc_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
visible_discussion_topics -= discussion_topics_with_unassigned_adhoc_overrides
|
||||
|
||||
# add discussion topics visible due to course overrides
|
||||
discussion_topics_visible_to_course_overrides = UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_visible_to_course_overrides(course_id_params:, user_id_params:, discussion_topic_id_params:)
|
||||
|
||||
visible_discussion_topics | discussion_topics_visible_to_course_overrides
|
||||
end
|
||||
|
||||
def empty_id_hash(ids)
|
||||
# [1,2,3] => {1:[],2:[],3:[]}
|
||||
ids.zip(ids.map { [] }).to_h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,7 +21,7 @@ module StudentVisibilityCommon
|
|||
def ids_visible_to_user(user, learning_object_type)
|
||||
case learning_object_type
|
||||
when "discussion_topic"
|
||||
UngradedDiscussionStudentVisibility.where(course_id: @course.id, user_id: user.id).pluck(:discussion_topic_id)
|
||||
UngradedDiscussionVisibility::UngradedDiscussionVisibilityService.discussion_topics_visible_to_student(course_id: @course.id, user_id: user.id).map(&:discussion_topic_id)
|
||||
when "wiki_page"
|
||||
WikiPageVisibility::WikiPageVisibilityService.wiki_pages_visible_to_student(course_id: @course.id, user_id: user.id).map(&:wiki_page_id)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_relative "../../../spec_helper"
|
||||
|
||||
describe UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent do
|
||||
describe "testing DTO" do
|
||||
it "can be initialized" do
|
||||
discussion_topic_visible_to_student = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student.discussion_topic_id).to eq 7
|
||||
end
|
||||
|
||||
it "raises error if passed nil course_id" do
|
||||
expect { UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: nil, discussion_topic_id: 7) }.to raise_error(ArgumentError, "course_id cannot be nil")
|
||||
end
|
||||
|
||||
it "raises error if passed nil user_id" do
|
||||
expect { UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: nil, course_id: 6, discussion_topic_id: 7) }.to raise_error(ArgumentError, "user_id cannot be nil")
|
||||
end
|
||||
|
||||
it "raises error if passed nil discussion_topic_id" do
|
||||
expect { UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: nil) }.to raise_error(ArgumentError, "discussion_topic_id cannot be nil")
|
||||
end
|
||||
|
||||
it "equality is attribute based" do
|
||||
discussion_topic_visible_to_student = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_2 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student).to eq discussion_topic_visible_to_student_2
|
||||
|
||||
# with different user_ids
|
||||
discussion_topic_visible_to_student_3 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 4, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_4 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_3).not_to eq discussion_topic_visible_to_student_4
|
||||
|
||||
# with different discussion_topic_ids
|
||||
discussion_topic_visible_to_student_5 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 3)
|
||||
discussion_topic_visible_to_student_6 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_5).not_to eq discussion_topic_visible_to_student_6
|
||||
|
||||
# with different course_ids
|
||||
discussion_topic_visible_to_student_7 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 8, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_8 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_7).not_to eq discussion_topic_visible_to_student_8
|
||||
end
|
||||
|
||||
it "hashcode is attribute based" do
|
||||
discussion_topic_visible_to_student = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_2 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student.hash).to eq discussion_topic_visible_to_student_2.hash
|
||||
|
||||
# with different user_ids
|
||||
discussion_topic_visible_to_student_3 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 4, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_4 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_3.hash).not_to eq discussion_topic_visible_to_student_4.hash
|
||||
|
||||
# with different discussion_topic_ids
|
||||
discussion_topic_visible_to_student_5 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 3)
|
||||
discussion_topic_visible_to_student_6 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_5.hash).not_to eq discussion_topic_visible_to_student_6.hash
|
||||
|
||||
# with different course_ids
|
||||
discussion_topic_visible_to_student_7 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 8, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_8 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
expect(discussion_topic_visible_to_student_7.hash).not_to eq discussion_topic_visible_to_student_8.hash
|
||||
end
|
||||
|
||||
it "can be unioned in array (set operation)" do
|
||||
# 4 discussion_topic_visible_to_student, one is a duplicate of another
|
||||
discussion_topic_visible_to_student = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_duplicate = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
|
||||
discussion_topic_visible_to_student_2 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 3, course_id: 4, discussion_topic_id: 5)
|
||||
discussion_topic_visible_to_student_3 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 8)
|
||||
|
||||
array_1 = [discussion_topic_visible_to_student, discussion_topic_visible_to_student_2]
|
||||
array_2 = [discussion_topic_visible_to_student_duplicate, discussion_topic_visible_to_student_3]
|
||||
|
||||
# union the two arrays
|
||||
union_array = array_1 | array_2
|
||||
|
||||
# the duplicate should be removed
|
||||
expect(union_array.length).to eq 3
|
||||
expect(union_array).to include(discussion_topic_visible_to_student)
|
||||
expect(union_array).to include(discussion_topic_visible_to_student_2)
|
||||
expect(union_array).to include(discussion_topic_visible_to_student_3)
|
||||
end
|
||||
|
||||
it "can be removed from array (set operation)" do
|
||||
# 4 discussion_topic_visible_to_student, one is a duplicate of another
|
||||
discussion_topic_visible_to_student = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
discussion_topic_visible_to_student_duplicate = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 7)
|
||||
|
||||
discussion_topic_visible_to_student_2 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 3, course_id: 4, discussion_topic_id: 5)
|
||||
discussion_topic_visible_to_student_3 = UngradedDiscussionVisibility::Entities::UngradedDiscussionVisibleToStudent.new(user_id: 5, course_id: 6, discussion_topic_id: 8)
|
||||
|
||||
array_1 = [discussion_topic_visible_to_student, discussion_topic_visible_to_student_2, discussion_topic_visible_to_student_3]
|
||||
array_2 = [discussion_topic_visible_to_student_duplicate]
|
||||
|
||||
# remove all elements in array_2 from array_1
|
||||
array_with_removal = array_1 - array_2
|
||||
|
||||
expect(array_with_removal.length).to eq 2
|
||||
|
||||
expect(array_with_removal).not_to include(discussion_topic_visible_to_student)
|
||||
expect(array_with_removal).not_to include(discussion_topic_visible_to_student_duplicate)
|
||||
expect(array_with_removal).to include(discussion_topic_visible_to_student_2)
|
||||
expect(array_with_removal).to include(discussion_topic_visible_to_student_3)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_relative "../../../spec_helper"
|
||||
|
||||
# See discussion_topic_visibility_service_spec for more (integration) tests that exercise this repository
|
||||
describe UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository do
|
||||
describe "testing things" do
|
||||
it "raises error if called with no filter parameters" do
|
||||
expect do
|
||||
UngradedDiscussionVisibility::Repositories::UngradedDiscussionVisibleToStudentRepository
|
||||
.find_discussion_topics_visible_to_everyone(course_id_params: nil, user_id_params: nil, discussion_topic_id_params: nil)
|
||||
end.to raise_error(ArgumentError, "UngradedDiscussionsVisibleToStudents must have a limiting where clause of at least one course_id, user_id, or discussion_topic_id (for performance reasons)")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_relative "../../models/student_visibility/student_visibility_common"
|
||||
|
||||
describe "UngradedDiscussionVisibility" do
|
||||
include StudentVisibilityCommon
|
||||
|
||||
def assignment_ids_visible_to_user(user)
|
||||
AssignmentVisibility::AssignmentVisibilityService.assignments_visible_to_student(course_id: @course.id, user_id: user.id).map(&:assignment_id)
|
||||
end
|
||||
|
||||
before :once do
|
||||
course_factory(active_all: true)
|
||||
@section1 = @course.default_section
|
||||
@section2 = @course.course_sections.create!(name: "Section 2")
|
||||
@student1 = student_in_course(active_all: true, section: @section1).user
|
||||
@student2 = student_in_course(active_all: true, section: @section2).user
|
||||
@discussion1 = DiscussionTopic.create!(context: @course, title: "Page 1")
|
||||
@discussion2 = DiscussionTopic.create!(context: @course, title: "Page 2")
|
||||
end
|
||||
|
||||
context "discussion topic visibility" do
|
||||
let(:learning_object1) { @discussion1 }
|
||||
let(:learning_object2) { @discussion2 }
|
||||
let(:learning_object_type) { "discussion_topic" }
|
||||
|
||||
it_behaves_like "learning object visiblities with modules"
|
||||
it_behaves_like "learning object visiblities"
|
||||
|
||||
it "does not include unpublished discussion topics" do
|
||||
@discussion1.workflow_state = "unpublished"
|
||||
@discussion1.save!
|
||||
expect(ids_visible_to_user(@student1, "discussion_topic")).to contain_exactly(@discussion2.id)
|
||||
end
|
||||
end
|
||||
|
||||
context "graded discussion topic visibility" do
|
||||
# graded discussion topics must use assignment_student_visibility as their
|
||||
# assignment overrides are on the assignment, not the discussion topic
|
||||
before :once do
|
||||
@discussion1_assignment = @course.assignments.create!
|
||||
@discussion2_assignment = @course.assignments.create!
|
||||
|
||||
@discussion1.update!(assignment: @discussion1_assignment)
|
||||
@discussion2.update!(assignment: @discussion2_assignment)
|
||||
end
|
||||
|
||||
it "ungraded_discussion_student_visibilities does not include graded discussion's assignment overrides" do
|
||||
@discussion1_assignment.only_visible_to_overrides = true
|
||||
@discussion1_assignment.save!
|
||||
|
||||
@discussion1.assignment.assignment_overrides.create!(set_type: "CourseSection", set_id: @section2.id)
|
||||
|
||||
# The overrides are on the assignment, not the discussion topic, so discussion visibilities will not be affected
|
||||
expect(ids_visible_to_user(@student1, "discussion_topic")).to contain_exactly(@discussion1.id, @discussion2.id)
|
||||
end
|
||||
|
||||
it "assignment_student_visibilities shows correct visibilities for graded discussion topic's assignment" do
|
||||
@discussion1_assignment.only_visible_to_overrides = true
|
||||
@discussion1_assignment.save!
|
||||
|
||||
@discussion1.assignment.assignment_overrides.create!(set_type: "CourseSection", set_id: @section2.id)
|
||||
|
||||
expect(assignment_ids_visible_to_user(@student1)).to contain_exactly(@discussion2.assignment.id)
|
||||
expect(assignment_ids_visible_to_user(@student2)).to contain_exactly(@discussion1.assignment.id, @discussion2.assignment.id)
|
||||
end
|
||||
|
||||
it "gets module overrides from graded discussion topic's assignment" do
|
||||
@module1 = @course.context_modules.create!(name: "Module 1")
|
||||
@module2 = @course.context_modules.create!(name: "Module 2")
|
||||
@discussion1.context_module_tags.create! context_module: @module1, context: @course, tag_type: "context_module"
|
||||
|
||||
override = @module1.assignment_overrides.create!
|
||||
override.assignment_override_students.create!(user: @student1)
|
||||
expect(ids_visible_to_user(@student1, "discussion_topic")).to contain_exactly(@discussion1.id, @discussion2.id)
|
||||
expect(ids_visible_to_user(@student2, "discussion_topic")).to contain_exactly(@discussion2.id)
|
||||
|
||||
expect(assignment_ids_visible_to_user(@student1)).to contain_exactly(@discussion1.assignment.id, @discussion2.assignment.id)
|
||||
expect(assignment_ids_visible_to_user(@student2)).to contain_exactly(@discussion2.assignment.id)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue