canvas-lms/app/controllers/outcomes_controller.rb

289 lines
13 KiB
Ruby

#
# Copyright (C) 2011 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 OutcomesController < ApplicationController
include Api::V1::Outcome
before_filter :require_context, :except => [:build_outcomes]
add_crumb(proc { t "#crumbs.outcomes", "Outcomes" }, :except => [:destroy, :build_outcomes]) { |c| c.send :named_context_url, c.instance_variable_get("@context"), :context_outcomes_path }
before_filter { |c| c.active_tab = "outcomes" }
def index
return unless authorized_action(@context, @current_user, :read)
return unless tab_enabled?(@context.class::TAB_OUTCOMES)
log_asset_access([ "outcomes", @context ], "outcomes", "other")
@root_outcome_group = @context.root_outcome_group
if (common_core_group_id = Setting.get(AcademicBenchmark.common_core_setting_key, nil))
common_core_group_id = common_core_group_id.to_i
common_core_group_url = polymorphic_path([:api_v1, :global, :outcome_group], :id => common_core_group_id)
end
js_env(:ROOT_OUTCOME_GROUP => outcome_group_json(@root_outcome_group, @current_user, session),
:CONTEXT_URL_ROOT => polymorphic_path([@context]),
:ACCOUNT_CHAIN_URL => polymorphic_path([:api_v1, @context, :account_chain]),
# Don't display state standards if in the context of a Course. Only at Account level.
:STATE_STANDARDS_URL => @context.is_a?(Course) ? nil : api_v1_global_redirect_path,
:COMMON_CORE_GROUP_ID => common_core_group_id,
:COMMON_CORE_GROUP_URL => common_core_group_url,
:PERMISSIONS => {
:manage_outcomes => @context.grants_right?(@current_user, session, :manage_outcomes),
:manage_rubrics => @context.grants_right?(@current_user, session, :manage_rubrics)
})
end
def show
if @context.respond_to?(:large_roster?) && @context.large_roster?
flash[:notice] = t "#application.notices.page_disabled_for_course", "That page has been disabled for this course"
redirect_to named_context_url(@context, :context_outcomes_path)
return
end
@outcome = @context.linked_learning_outcomes.find(params[:id])
return unless authorized_action(@context, @current_user, :manage_outcomes)
log_asset_access(@outcome, "outcomes", "outcomes")
codes = [@context].map(&:asset_string)
if @context.is_a?(Account)
if @context == @outcome.context
codes = "all"
else
codes = @context.all_courses.select(:id).map(&:asset_string)
end
end
@alignments = @outcome.alignments.active.for_context(@context)
add_crumb(@outcome.short_description, named_context_url(@context, :context_outcome_url, @outcome.id))
@results = @outcome.learning_outcome_results.for_context_codes(codes).custom_ordering(params[:sort]).paginate(:page => params[:page], :per_page => 10)
js_env({
:PERMISSIONS => {
:manage_outcomes => @context.grants_right?(@current_user, session, :manage_outcomes)
}
})
end
def details
@outcome = @context.linked_learning_outcomes.find(params[:outcome_id])
return unless authorized_action(@context, @current_user, :read)
@outcome.tie_to(@context)
render :json => @outcome.as_json(
:methods => :artifacts_count_for_tied_context,
:user_content => %w(description))
end
def outcome_results
@outcome = @context.linked_learning_outcomes.find(params[:outcome_id])
return unless authorized_action(@context, @current_user, :read)
codes = [@context].map(&:asset_string)
if @context.is_a?(Account)
if @context == @outcome.context
codes = "all"
else
codes = @context.all_courses.select(:id).map(&:asset_string)
end
end
@results = @outcome.learning_outcome_results.for_context_codes(codes).custom_ordering(params[:sort])
render :json => Api.paginate(@results, self, polymorphic_url([@context, :outcome_results]))
end
def user_outcome_results
user_id = params[:user_id]
if @context.is_a?(User)
@user = @context
elsif @context.is_a?(Course)
@user = @context.users.find(user_id)
else
@user = @context.all_users.find(user_id)
end
return unless authorized_action(@context, @current_user, :manage)
if @user == @context
@outcomes = LearningOutcome.has_result_for(@user).active
else
@outcomes = @context.available_outcomes
end
@results = LearningOutcomeResult.for_user(@user).for_outcome_ids(@outcomes.map(&:id)) #.for_context_codes(@codes)
@results_for_outcome = @results.group_by(&:learning_outcome_id)
end
def list
return unless authorized_action(@context, @current_user, :manage_outcomes)
@account_contexts = @context.associated_accounts rescue []
@current_outcomes = @context.linked_learning_outcomes
@outcomes = Canvas::ICU.collate_by(@context.available_outcomes, &:title)
if params[:unused]
@outcomes -= @current_outcomes
end
render :json => @outcomes.map{ |o| o.as_json(methods: :cached_context_short_name) }
end
# as in, add existing outcome from another context to this context
def add_outcome
return unless authorized_action(@context, @current_user, :manage_outcomes)
@account_contexts = @context.associated_accounts.uniq rescue []
codes = @account_contexts.map(&:asset_string)
@outcome = LearningOutcome.for_context_codes(codes).find(params[:learning_outcome_id])
@group = @context.learning_outcome_groups.find(params[:learning_outcome_group_id])
# this is silly. there should be different actions for moving a link
# (adopt_outcome_link) and adding a new link (add_outcome). as is, you
# can't add a second link to the same outcome under a new group. but just
# refactoring the model layer for now...
if outcome_link = @group.child_outcome_links.where(content_id: @outcome.id).first
@group.adopt_outcome_link(outcome_link)
else
@group.add_outcome(@outcome)
end
render :json => @outcome.as_json(:methods => :cached_context_short_name, :permissions => {:user => @current_user, :session => session})
end
def align
return unless authorized_action(@context, @current_user, :manage_outcomes)
@outcome = @context.linked_learning_outcomes.find(params[:outcome_id])
@asset = @context.find_asset(params[:asset_string])
mastery_type = @asset.is_a?(Assignment) ? "points" : "none"
@alignment = @outcome.align(@asset, @context, :mastery_type => mastery_type) if @asset
render :json => @alignment.as_json(:include => :learning_outcome)
end
def alignment_redirect
return unless authorized_action(@context, @current_user, :read)
@outcome = @context.available_outcome(params[:outcome_id].to_i)
@alignment = @outcome.alignments.find(params[:id])
content_tag_redirect(@context, @alignment, :context_outcomes_url)
end
def remove_alignment
return unless authorized_action(@context, @current_user, :manage_outcomes)
@outcome = @context.available_outcome(params[:outcome_id].to_i)
@outcome.remove_alignment(params[:id], @context)
render :json => @alignment.as_json(:include => :learning_outcome)
end
def outcome_result
return unless authorized_action(@context, @current_user, :manage_outcomes)
@outcome = @context.linked_learning_outcomes.find(params[:outcome_id])
@result = @outcome.learning_outcome_results.find(params[:id])
return unless authorized_action(@result.context, @current_user, :manage_outcomes)
if @result.artifact.is_a?(Submission)
@submission = @result.artifact
redirect_to named_context_url(@result.context, :context_assignment_submission_url, @submission.assignment_id, @submission.user_id)
elsif @result.artifact.is_a?(RubricAssessment) && @result.artifact.artifact && @result.artifact.artifact.is_a?(Submission)
@submission = @result.artifact.artifact
redirect_to named_context_url(@result.context, :context_assignment_submission_url, @submission.assignment_id, @submission.user_id)
elsif @result.artifact.is_a?(Quizzes::QuizSubmission) && @result.associated_asset
@submission = @result.artifact
@question = @result.associated_asset
if @submission.attempt <= @result.attempt
@submission_version = @submission
else
@submission_version = @submission.submitted_attempts.detect{|s| s.attempt >= @result.attempt }
end
question = @submission.quiz_data.detect{|q| q['assessment_question_id'] == @question.data[:id] }
question_id = (question && question['id']) || @question.data[:id]
redirect_to named_context_url(@result.context, :context_quiz_history_url, @submission.quiz_id, :quiz_submission_id => @submission.id, :version => @submission_version.version_number, :anchor => "question_#{question_id}")
else
flash[:error] = "Unrecognized artifact type: #{@result.try(:artifact_type) || 'nil'}"
redirect_to named_context_url(@context, :context_outcome_url, @outcome.id)
end
end
def reorder_alignments
return unless authorized_action(@context, @current_user, :manage_outcomes)
@outcome = @context.linked_learning_outcomes.find(params[:outcome_id])
@alignments = @outcome.reorder_alignments(@context, params[:order].split(","))
render :json => @alignments.map{ |a| a.as_json(include: :learning_outcome) }
end
def create
return unless authorized_action(@context, @current_user, :manage_outcomes)
if params[:learning_outcome_group_id].present?
@outcome_group = @context.learning_outcome_groups.find(params[:learning_outcome_group_id])
end
@outcome_group ||= @context.root_outcome_group
@outcome = @context.created_learning_outcomes.build(params[:learning_outcome])
respond_to do |format|
if @outcome.save
@outcome_group.add_outcome(@outcome)
flash[:notice] = t :successful_outcome_creation, "Outcome successfully created!"
format.html { redirect_to named_context_url(@context, :context_outcomes_url) }
format.json { render :json => @outcome }
else
flash[:error] = t :failed_outcome_creation, "Outcome creation failed"
format.html { redirect_to named_context_url(@context, :context_outcomes_url) }
format.json { render :json => @outcome.errors, :status => :bad_request }
end
end
end
def update
return unless authorized_action(@context, @current_user, :manage_outcomes)
@outcome = @context.created_learning_outcomes.find(params[:id])
respond_to do |format|
if @outcome.update_attributes(params[:learning_outcome])
flash[:notice] = t :successful_outcome_update, "Outcome successfully updated!"
format.html { redirect_to named_context_url(@context, :context_outcomes_url) }
format.json { render :json => @outcome }
else
flash[:error] = t :failed_outcome_update, "Outcome update failed"
format.html { redirect_to named_context_url(@context, :context_outcomes_url) }
format.json { render :json => @outcome.errors, :statue => :bad_request }
end
end
end
def destroy
return unless authorized_action(@context, @current_user, :manage_outcomes)
respond_to do |format|
# TODO: params[:id] is overloaded to be either a native outcome id or
# the id of a link to a foreign outcome. therefore it's possible to
# intend to delete a link to a foreign but accidentally delete a
# completely unrelated native outcome. needs to be decoupled.
if params[:id].present? && @outcome = @context.created_learning_outcomes.where(id: params[:id]).first
@outcome.destroy
flash[:notice] = t :successful_outcome_delete, "Outcome successfully deleted"
format.json { render :json => @outcome }
elsif params[:id].present? && @link = @context.learning_outcome_links.where(id: params[:id]).first
@link.destroy
flash[:notice] = t :successful_outcome_removal, "Outcome successfully removed"
format.json { render :json => @link.learning_outcome }
else
flash[:notice] = t :missing_outcome, "Couldn't find that learning outcome"
format.json { render :json => {:errors => {:base => t(:missing_outcome, "Couldn't find that learning outcome")}}, :status => :bad_request }
end
format.html { redirect_to named_context_url(@context, :context_outcomes_url) }
end
end
end