Skip to content

Commit ca87014

Browse files
committed
Outcome API
Change-Id: I6c33d2ab364522930b908d8edd162b20634e54b5 Reviewed-on: https://gerrit.instructure.com/12762 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
1 parent 8fa01db commit ca87014

19 files changed

Lines changed: 2038 additions & 92 deletions
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#
2+
# Copyright (C) 2012 Instructure, Inc.
3+
#
4+
# This file is part of Canvas.
5+
#
6+
# Canvas is free software: you can redistribute it and/or modify it under
7+
# the terms of the GNU Affero General Public License as published by the Free
8+
# Software Foundation, version 3 of the License.
9+
#
10+
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12+
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13+
# details.
14+
#
15+
# You should have received a copy of the GNU Affero General Public License along
16+
# with this program. If not, see <http://www.gnu.org/licenses/>.
17+
#
18+
19+
# @API Outcome Groups
20+
#
21+
# API for accessing learning outcome group information.
22+
class OutcomeGroupsApiController < ApplicationController
23+
include Api::V1::Outcome
24+
25+
before_filter :get_context, :except => :global_redirect
26+
before_filter :require_context, :only => :context_redirect
27+
28+
# @API Redirect for global outcomes
29+
# Convenience redirect to find the root outcome group for global outcomes.
30+
def redirect
31+
if can_read_outcomes
32+
@outcome_group = @context ?
33+
@context.root_outcome_group :
34+
LearningOutcomeGroup.global_root_outcome_group
35+
redirect_to polymorphic_path [:api_v1, @context || :global, :outcome_group], :id => @outcome_group.id
36+
end
37+
end
38+
39+
# @API Retrieve an outcome group's details.
40+
def show
41+
if can_read_outcomes
42+
@outcome_group = context_outcome_groups.find(params[:id])
43+
render :json => outcome_group_json(@outcome_group, @current_user, session)
44+
end
45+
end
46+
47+
# @API Update an outcome group.
48+
def update
49+
if can_manage_outcomes
50+
@outcome_group = context_outcome_groups.find(params[:id])
51+
if @outcome_group.learning_outcome_group_id.nil?
52+
render :json => 'error'.to_json, :status => :bad_request
53+
return
54+
end
55+
@outcome_group.update_attributes(params.slice(:title, :description))
56+
if params[:parent_outcome_group_id] && params[:parent_outcome_group_id] != @outcome_group.learning_outcome_group_id
57+
new_parent = context_outcome_groups.find(params[:parent_outcome_group_id])
58+
unless new_parent.adopt_outcome_group(@outcome_group)
59+
render :json => 'error'.to_json, :status => :bad_request
60+
return
61+
end
62+
end
63+
if @outcome_group.save
64+
render :json => outcome_group_json(@outcome_group, @current_user, session)
65+
else
66+
render :json => @outcome_group.errors, :status => :bad_request
67+
end
68+
end
69+
end
70+
71+
# @API Delete an outcome group.
72+
def destroy
73+
if can_manage_outcomes
74+
@outcome_group = context_outcome_groups.find(params[:id])
75+
if @outcome_group.learning_outcome_group_id.nil?
76+
render :json => 'error'.to_json, :status => :bad_request
77+
return
78+
end
79+
begin
80+
@outcome_group.destroy
81+
render :json => outcome_group_json(@outcome_group, @current_user, session)
82+
rescue ActiveRecord::RecordNotSaved
83+
render :json => 'error'.to_json, :status => :bad_request
84+
end
85+
end
86+
end
87+
88+
# @API List the outcomes in a group.
89+
def outcomes
90+
if can_read_outcomes
91+
@outcome_group = context_outcome_groups.find(params[:id])
92+
93+
# get and paginate links from group
94+
link_scope = @outcome_group.child_outcome_links.active.order_by_outcome_title
95+
path = polymorphic_path [:api_v1, @context, :outcome_group_outcomes], :id => @outcome_group.id
96+
@links = Api.paginate(link_scope, self, path)
97+
98+
# pre-populate the links' groups and contexts to prevent
99+
# extraneous loads
100+
@links.each do |link|
101+
link.associated_asset = @outcome_group
102+
link.context = @outcome_group.context
103+
end
104+
105+
# preload the links' outcomes' contexts.
106+
ContentTag.send(:preload_associations, @links, :learning_outcome_content => :context)
107+
108+
# render to json and serve
109+
render :json => @links.map{ |link| outcome_link_json(link, @current_user, session) }
110+
end
111+
end
112+
113+
# @API Link an outcome into a group.
114+
def link
115+
if can_manage_outcomes
116+
@outcome_group = context_outcome_groups.find(params[:id])
117+
if params[:outcome_id]
118+
@outcome = context_available_outcome(params[:outcome_id])
119+
unless @outcome
120+
render :json => 'error'.to_json, :status => :bad_request
121+
return
122+
end
123+
else
124+
@outcome = context_create_outcome(params.slice(:title, :description, :ratings, :mastery_points))
125+
unless @outcome.valid?
126+
render :json => @outcome.errors, :status => :bad_request
127+
return
128+
end
129+
end
130+
@outcome_link = @outcome_group.add_outcome(@outcome)
131+
render :json => outcome_link_json(@outcome_link, @current_user, session)
132+
end
133+
end
134+
135+
# @API Unlink an outcome link in a group.
136+
def unlink
137+
if can_manage_outcomes
138+
@outcome_group = context_outcome_groups.find(params[:id])
139+
@outcome_link = @outcome_group.child_outcome_links.active.find_by_content_id(params[:outcome_id])
140+
raise ActiveRecord::RecordNotFound unless @outcome_link
141+
begin
142+
@outcome_link.destroy
143+
render :json => outcome_link_json(@outcome_link, @current_user, session)
144+
rescue ActiveRecord::RecordNotSaved
145+
render :json => 'error'.to_json, :status => :bad_request
146+
end
147+
end
148+
end
149+
150+
# @API List the outcomes in a group.
151+
def subgroups
152+
if can_read_outcomes
153+
@outcome_group = context_outcome_groups.find(params[:id])
154+
155+
# get and paginate subgroups from group
156+
subgroup_scope = @outcome_group.child_outcome_groups.active.order_by_title
157+
path = polymorphic_path [:api_v1, @context, :outcome_group_subgroups], :id => @outcome_group.id
158+
@subgroups = Api.paginate(subgroup_scope, self, path)
159+
160+
# pre-populate the subgroups' parent groups to prevent extraneous
161+
# loads
162+
@subgroups.each{ |group| group.context = @outcome_group.context }
163+
164+
# render to json and serve
165+
render :json => @subgroups.map{ |group| outcome_group_json(group, @current_user, session, :abbrev) }
166+
end
167+
end
168+
169+
# @API Create a new subgroup of a group.
170+
def create
171+
if can_manage_outcomes
172+
@outcome_group = context_outcome_groups.find(params[:id])
173+
@child_outcome_group = @outcome_group.child_outcome_groups.build(params.slice(:title, :description))
174+
if @child_outcome_group.save
175+
render :json => outcome_group_json(@child_outcome_group, @current_user, session)
176+
else
177+
render :json => 'error'.to_json, :status => :bad_request
178+
end
179+
end
180+
end
181+
182+
# @API Import an existing non-root outcome group into a group.
183+
def import
184+
if can_manage_outcomes
185+
@outcome_group = context_outcome_groups.find(params[:id])
186+
187+
# source has to exist
188+
@source_outcome_group = LearningOutcomeGroup.active.find_by_id(params[:source_outcome_group_id])
189+
unless @source_outcome_group
190+
render :json => 'error'.to_json, :status => :bad_request
191+
return
192+
end
193+
194+
# source has to be global, in same context, or in an associated
195+
# account
196+
source_context = @source_outcome_group.context
197+
unless !source_context || source_context == @context || @context.associated_accounts.include?(source_context)
198+
render :json => 'error'.to_json, :status => :bad_request
199+
return
200+
end
201+
202+
# source can't be a root group
203+
unless @source_outcome_group.learning_outcome_group_id
204+
render :json => 'error'.to_json, :status => :bad_request
205+
return
206+
end
207+
208+
# import the validated source
209+
@child_outcome_group = @outcome_group.add_outcome_group(@source_outcome_group)
210+
render :json => outcome_group_json(@child_outcome_group, @current_user, session)
211+
end
212+
end
213+
214+
protected
215+
216+
def can_read_outcomes
217+
if @context
218+
authorized_action(@context, @current_user, :manage_outcomes)
219+
else
220+
# anyone (that's logged in) can read global outcomes
221+
true
222+
end
223+
end
224+
225+
def can_manage_outcomes
226+
if @context
227+
authorized_action(@context, @current_user, :manage_outcomes)
228+
else
229+
authorized_action(Account.site_admin, @current_user, :manage_global_outcomes)
230+
end
231+
end
232+
233+
# get the active outcome groups in the context/global
234+
def context_outcome_groups
235+
LearningOutcomeGroup.for_context(@context).active
236+
end
237+
238+
# verify the outcome is eligible to be linked into the context,
239+
# returning the outcome if so
240+
def context_available_outcome(outcome_id)
241+
if @context
242+
@context.available_outcome(outcome_id, :allow_global => true)
243+
else
244+
LearningOutcome.global.find_by_id(outcome_id)
245+
end
246+
end
247+
248+
def context_create_outcome(data)
249+
scope = @context ? @context.created_learning_outcomes : LearningOutcome.global
250+
outcome = scope.build(data.slice(:title, :description))
251+
if data[:ratings]
252+
outcome.rubric_criterion = data.slice(:ratings, :mastery_points)
253+
end
254+
outcome.save
255+
outcome
256+
end
257+
end
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#
2+
# Copyright (C) 2012 Instructure, Inc.
3+
#
4+
# This file is part of Canvas.
5+
#
6+
# Canvas is free software: you can redistribute it and/or modify it under
7+
# the terms of the GNU Affero General Public License as published by the Free
8+
# Software Foundation, version 3 of the License.
9+
#
10+
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12+
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13+
# details.
14+
#
15+
# You should have received a copy of the GNU Affero General Public License along
16+
# with this program. If not, see <http://www.gnu.org/licenses/>.
17+
#
18+
19+
# @API Outcomes
20+
#
21+
# API for accessing learning outcome information.
22+
class OutcomesApiController < ApplicationController
23+
include Api::V1::Outcome
24+
25+
before_filter :get_outcome
26+
27+
# @API Retrieve an outcome's details.
28+
def show
29+
if can_read_outcome
30+
render :json => outcome_json(@outcome, @current_user, session)
31+
end
32+
end
33+
34+
# @API Update an outcome.
35+
def update
36+
if can_manage_outcome
37+
@outcome.update_attributes(params.slice(:title, :description))
38+
if params[:mastery_points] || params[:ratings]
39+
criterion = @outcome.data && @outcome.data[:rubric_criterion]
40+
criterion ||= {}
41+
if params[:mastery_points]
42+
criterion[:mastery_points] = params[:mastery_points]
43+
else
44+
criterion.delete(:mastery_points)
45+
end
46+
if params[:ratings]
47+
criterion[:ratings] = params[:ratings]
48+
end
49+
@outcome.rubric_criterion = criterion
50+
end
51+
if @outcome.save
52+
render :json => outcome_json(@outcome, @current_user, session)
53+
else
54+
render :json => @outcome.errors, :status => :bad_request
55+
end
56+
end
57+
end
58+
59+
protected
60+
61+
def get_outcome
62+
@outcome = LearningOutcome.active.find(params[:id])
63+
end
64+
65+
def can_read_outcome
66+
if @outcome.context_id
67+
authorized_action(@outcome.context, @current_user, :manage_outcomes)
68+
else
69+
authorized_action(Account.site_admin, @current_user, [:read_global_outcomes, :manage_global_outcomes])
70+
end
71+
end
72+
73+
def can_manage_outcome
74+
if @outcome.context_id
75+
authorized_action(@outcome.context, @current_user, :manage_outcomes)
76+
else
77+
authorized_action(Account.site_admin, @current_user, :manage_global_outcomes)
78+
end
79+
end
80+
end

app/controllers/outcomes_controller.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def index
2525
if authorized_action(@context, @current_user, :read)
2626
return unless tab_enabled?(@context.class::TAB_OUTCOMES)
2727
@root_outcome_group = @context.root_outcome_group
28-
@outcomes = @context.linked_learning_outcomes
2928
end
3029
end
3130

0 commit comments

Comments
 (0)