Consider only relevant discussions when evaluating CMPs
Context module progressions should only consider items that a user is able to view when evaluating a module's completion status. Ungraded discussions can be posted to only certain sections within a course, so only discussions in section(s) where the student is enrolled should be considered. This is normally the case, however, when evaluating all of the progressions within a course, we cache the item visibilities to prevent recalculating the same thing repeatedly (see ContextModule#evaluate_all_progressions). This codepath is not frequently followed, but is, for example, when a module itelf is edited. In the caching process, we get all of the sections (and associated discussions) visible to a list of users. This was an issue becuase the list of users might contain users with different section visibilities. This patchset considers section visibilities for each user in the list individually, ensuring that module progressions evaluate appropriately for all users. closes LS-3245 flag = none Test plan: - Create a course with 2 sections - Create a module in the course - Create 2 discussions in the module - both ungraded; assign one to the first section, and one to the second section - Create a page in the module - Edit module requirements - set "contribute to the page" on both discussions and "view" on the page - Enroll a student in one of the sections and publish the course - As the student, view the page and reply to the visible discussion - Expect the module to be marked as completed - As the teacher, create a new (empty) module, move it to the top of the page, then delete it - As the student, view the modules page; expect the module to still be marked as completed Change-Id: I4d9a57dae0cf521a46953f4c5217df7358b08835 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/296104 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> QA-Review: Luis Oliveira <luis.oliveira@instructure.com> Reviewed-by: Luis Oliveira <luis.oliveira@instructure.com> Product-Review: Jackson Howe <jackson.howe@instructure.com>
This commit is contained in:
parent
0eb99d975f
commit
f3aa9a9f78
|
@ -42,8 +42,9 @@ module Submittable
|
|||
|
||||
module ClassMethods
|
||||
def visible_ids_by_user(opts)
|
||||
# pluck id, assignment_id, and user_id from items joined with the SQL view
|
||||
# Items with an assignment: pluck id, assignment_id, and user_id from items joined with the SQL view
|
||||
plucked_visibilities = pluck_visibilities(opts).group_by { |_, _, user_id| user_id }
|
||||
|
||||
# Assignment-less items are *normally* visible to all -- the exception is
|
||||
# section-specific discussions, so here get the ones visible to everyone in the
|
||||
# course, and below get the ones that are visible to the right section.
|
||||
|
@ -55,18 +56,33 @@ module Submittable
|
|||
|
||||
# Now get the section-specific discussions that are in the proper sections.
|
||||
ids_visible_to_sections = if opts[:item_type] == :discussion
|
||||
user_sections = Enrollment.active.where(
|
||||
course_id: opts[:course_id], user_id: opts[:user_id]
|
||||
).pluck(:course_section_id)
|
||||
DiscussionTopicSectionVisibility.active.where(course_section_id: user_sections).pluck(:discussion_topic_id).uniq
|
||||
# build hash of user_ids to array of section ids
|
||||
sections_per_user = {}
|
||||
Enrollment.active.where(course_id: opts[:course_id], user_id: opts[:user_id])
|
||||
.pluck(:user_id, :course_section_id)
|
||||
.each { |user_id, section_id| (sections_per_user[user_id] ||= Set.new) << section_id }
|
||||
|
||||
# build hash of section_ids to array of visible topic ids
|
||||
all_section_ids = sections_per_user.values.reduce([]) { |all_ids, section_ids| all_ids.concat(section_ids.to_a) }
|
||||
topic_ids_per_section = {}
|
||||
DiscussionTopicSectionVisibility.active.where(course_section_id: all_section_ids)
|
||||
.pluck(:course_section_id, :discussion_topic_id)
|
||||
.each { |section_id, topic_id| (topic_ids_per_section[section_id] ||= Set.new) << topic_id }
|
||||
topic_ids_per_section.each { |section_id, topic_ids| topic_ids_per_section[section_id] = topic_ids.to_a }
|
||||
|
||||
# finally, build hash of user_ids to array of visible topic ids
|
||||
topic_ids_per_user = {}
|
||||
opts[:user_id].each { |user_id| topic_ids_per_user[user_id] = sections_per_user[user_id]&.map { |section_id| topic_ids_per_section[section_id] }&.flatten&.uniq&.compact }
|
||||
topic_ids_per_user
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
# build map of user_ids to array of item ids {1 => [2,3,4], 2 => [2,4]}
|
||||
opts[:user_id].index_with do |student_id|
|
||||
ids_from_pluck = (plucked_visibilities[student_id] || []).map { |id, _, _| id }
|
||||
ids_from_pluck.concat(ids_visible_to_all).concat(ids_visible_to_sections)
|
||||
assignment_item_ids = (plucked_visibilities[student_id] || []).map { |id, _, _| id }
|
||||
section_specific_ids = ids_visible_to_sections[student_id] || []
|
||||
assignment_item_ids.concat(ids_visible_to_all).concat(section_specific_ids)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -162,6 +162,28 @@ describe "section specific topic" do
|
|||
expect(vis_hash[student.id].first).to eq(section_specific_topic1.id)
|
||||
end
|
||||
|
||||
it "filters section specific topics properly for multiple users" do
|
||||
course = course_factory(active_all: true)
|
||||
section1 = course.course_sections.create!(name: "section 1")
|
||||
section2 = course.course_sections.create!(name: "section 2")
|
||||
topic1 = course.discussion_topics.create!(title: "topic 1 (for section 1)")
|
||||
topic2 = course.discussion_topics.create!(title: "topic 2 (for section 2)")
|
||||
topic3 = course.discussion_topics.create!(title: "topic 3 (for all sections)")
|
||||
topic4 = course.discussion_topics.create!(title: "topic 4 (for section 2)")
|
||||
add_section_to_topic(topic1, section1)
|
||||
add_section_to_topic(topic2, section2)
|
||||
add_section_to_topic(topic4, section2)
|
||||
student = user_factory(active_all: true)
|
||||
teacher = user_factory(active_all: true)
|
||||
course.enroll_student(student, section: section2)
|
||||
course.enroll_teacher(teacher, section: section1)
|
||||
course.reload
|
||||
|
||||
vis_hash = DiscussionTopic.visible_ids_by_user(course_id: course.id, user_id: [student.id, teacher.id], item_type: :discussion)
|
||||
expect(vis_hash[student.id]).to contain_exactly(topic2.id, topic3.id, topic4.id)
|
||||
expect(vis_hash[teacher.id]).to contain_exactly(topic1.id, topic3.id)
|
||||
end
|
||||
|
||||
it "properly filters section specific topics for deleted section visibilities" do
|
||||
course = course_factory(active_course: true)
|
||||
section1 = course.course_sections.create!(name: "section for student")
|
||||
|
@ -174,4 +196,30 @@ describe "section specific topic" do
|
|||
vis_hash = DiscussionTopic.visible_ids_by_user(course_id: course.id, user_id: [student.id], item_type: :discussion)
|
||||
expect(vis_hash[student.id].length).to eq(0)
|
||||
end
|
||||
|
||||
it "handles sections that don't have any discussion topics" do
|
||||
course = course_factory(active_all: true)
|
||||
section1 = course.course_sections.create!(name: "section 1")
|
||||
section2 = course.course_sections.create!(name: "section 2")
|
||||
topic1 = course.discussion_topics.create!(title: "topic 1 (for section 1)")
|
||||
add_section_to_topic(topic1, section1)
|
||||
student = user_factory(active_all: true)
|
||||
course.enroll_student(student, section: section2)
|
||||
course.reload
|
||||
|
||||
vis_hash = DiscussionTopic.visible_ids_by_user(course_id: course.id, user_id: [student.id], item_type: :discussion)
|
||||
expect(vis_hash[student.id].length).to be(0)
|
||||
end
|
||||
|
||||
it "handles user not enrolled in any sections" do
|
||||
course = course_factory(active_all: true)
|
||||
section1 = course.course_sections.create!(name: "section 1")
|
||||
topic1 = course.discussion_topics.create!(title: "topic 1 (for section 1)")
|
||||
add_section_to_topic(topic1, section1)
|
||||
student = user_factory(active_all: true)
|
||||
course.reload
|
||||
|
||||
vis_hash = DiscussionTopic.visible_ids_by_user(course_id: course.id, user_id: [student.id], item_type: :discussion)
|
||||
expect(vis_hash[student.id].length).to be(0)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue