115 lines
3.0 KiB
Ruby
115 lines
3.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#
|
|
# Copyright (C) 2016 - 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/>.
|
|
#
|
|
|
|
class MustViewModuleProgressor
|
|
def initialize(user, course)
|
|
@user = user
|
|
@course = course
|
|
end
|
|
attr_reader :user, :course
|
|
|
|
def modules
|
|
course.context_modules
|
|
end
|
|
|
|
def make_progress
|
|
modules.each do |mod|
|
|
mod.evaluate_for(user)
|
|
if mod.require_sequential_progress
|
|
progress_sequential_module(mod)
|
|
else
|
|
progress_random_access_module(mod)
|
|
end
|
|
end
|
|
end
|
|
|
|
def current_progress
|
|
progress = {}
|
|
modules.each do |mod|
|
|
progress[mod.id] =
|
|
if (progression = mod.find_or_create_progression(user)&.evaluate)
|
|
{ status: progression.workflow_state }
|
|
elsif mod.grants_right?(user, :read)
|
|
{ status: 'unlocked' }
|
|
else
|
|
{ status: 'locked' }
|
|
end
|
|
progress[mod.id][:items] = items_current_progress(mod, progression)
|
|
end
|
|
progress
|
|
end
|
|
|
|
private
|
|
|
|
def always_skippable?(item)
|
|
return true unless item.visible_to_user?(user)
|
|
|
|
progression = item.context_module.find_or_create_progression(user)
|
|
# unenrolled users don't have progressions and can always skip
|
|
return true if progression.nil?
|
|
return true if progression.finished_item?(item)
|
|
|
|
content = item.content
|
|
return true if content.respond_to?(:published?) && !content.published?
|
|
|
|
false
|
|
end
|
|
|
|
def random_access_should_skip?(item)
|
|
always_skippable?(item) || sequential_access_should_stop?(item)
|
|
end
|
|
|
|
def sequential_access_should_stop?(item)
|
|
content = item.content
|
|
return true if content.respond_to?(:locked_for?) && content.locked_for?(user, deep_check_if_needed: true)
|
|
return true unless item.context_module.completion_requirement_for(:read, item)
|
|
false
|
|
end
|
|
|
|
def progress_random_access_module(mod)
|
|
items = mod.content_tags
|
|
items.each do |item|
|
|
next if random_access_should_skip?(item)
|
|
progress_item(item)
|
|
end
|
|
end
|
|
|
|
def progress_sequential_module(mod)
|
|
mod.content_tags.each do |item|
|
|
next if always_skippable?(item)
|
|
break if sequential_access_should_stop?(item)
|
|
progress_item(item)
|
|
end
|
|
end
|
|
|
|
def progress_item(item)
|
|
item.context_module_action(user, :read)
|
|
end
|
|
|
|
def items_current_progress(mod, progression)
|
|
progress = {}
|
|
mod.content_tags.each do |item|
|
|
progress[item.id] = (progression&.finished_item?(item) || false)
|
|
end
|
|
progress
|
|
end
|
|
|
|
end
|