Skip to content

Commit 718c3d0

Browse files
committed
support section ids in the submissions api, refs #5835
Change-Id: Ibbe8f3852e8674c963e735b3f0fe8982d5ad7244 Reviewed-on: https://gerrit.instructure.com/6014 Reviewed-by: Cody Cutrer <cody@instructure.com> Tested-by: Hudson <hudson@instructure.com>
1 parent 0393f8d commit 718c3d0

7 files changed

Lines changed: 123 additions & 11 deletions

File tree

app/controllers/submissions_api_controller.rb

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121
# API for accessing and updating submissions for an assignment. The submission
2222
# id in these URLs is the id of the student in the course, there is no separate
2323
# submission id exposed in these APIs.
24+
#
25+
# All submission actions can be performed with either the course id, or the
26+
# course section id. SIS ids can be used, prefixed by "sis_course_id:" or
27+
# "sis_section_id:" as described in the API documentation on SIS IDs.
2428
class SubmissionsApiController < ApplicationController
25-
before_filter :require_context
29+
before_filter :get_course_from_section, :require_context
2630

2731
# @API
2832
#
@@ -33,7 +37,7 @@ def index
3337
if authorized_action(@context, @current_user, :manage_grades)
3438
@assignment = @context.assignments.active.find(params[:assignment_id])
3539
@submissions = @assignment.submissions.all(
36-
:conditions => { :user_id => @context.student_ids })
40+
:conditions => { :user_id => (@section || @context).student_ids })
3741

3842
includes = Array(params[:include])
3943

@@ -91,7 +95,7 @@ def for_students
9195
Api.assignment_ids_for_students_api = assignments.map(&:id)
9296
sql_includes = { :user => [] }
9397
sql_includes[:user] << :submissions_for_given_assignments unless assignments.empty?
94-
scope = @context.student_enrollments.scoped(
98+
scope = (@section || @context).student_enrollments.scoped(
9599
:include => sql_includes,
96100
:conditions => { 'users.id' => student_ids })
97101

@@ -127,8 +131,8 @@ def for_students
127131
# @argument include[] ["submission_history"|"submission_comments"|"rubric_assessment"] Associations to include with the group.
128132
def show
129133
@assignment = @context.assignments.active.find(params[:assignment_id])
130-
params[:id] = map_user_ids([params[:id]]).first
131-
@submission = @assignment.submissions.find_by_user_id(params[:id]) or raise ActiveRecord::RecordNotFound
134+
@user = get_user_considering_section(params[:id])
135+
@submission = @assignment.submissions.find_by_user_id(@user.id) or raise ActiveRecord::RecordNotFound
132136
if authorized_action(@submission, @current_user, :read)
133137
includes = Array(params[:include])
134138
render :json => submission_json(@submission, @assignment, includes).to_json
@@ -191,7 +195,7 @@ def show
191195
def update
192196
if authorized_action(@context, @current_user, :manage_grades)
193197
@assignment = @context.assignments.active.find(params[:assignment_id])
194-
@user = api_find(@context.students_visible_to(@current_user), params[:id])
198+
@user = get_user_considering_section(params[:id])
195199

196200
submission = {}
197201
if params[:submission].is_a?(Hash)
@@ -354,7 +358,21 @@ def media_comment_json(media_comment_id, media_comment_type)
354358
end
355359

356360
def map_user_ids(user_ids)
357-
Api.map_ids(user_ids, User).compact
361+
Api.map_ids(user_ids, User).compact unless user_ids.blank?
362+
end
363+
364+
def get_course_from_section
365+
if params[:section_id]
366+
@section = api_find(CourseSection, params.delete(:section_id))
367+
params[:course_id] = @section.course_id
368+
end
358369
end
359370

371+
def get_user_considering_section(user_id)
372+
scope = @context.students_visible_to(@current_user)
373+
if @section
374+
scope = scope.scoped(:conditions => { 'enrollments.course_section_id' => @section.id })
375+
end
376+
api_find(scope, user_id)
377+
end
360378
end

app/models/course_section.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class CourseSection < ActiveRecord::Base
2727
belongs_to :sis_cross_listed_section
2828
belongs_to :account
2929
has_many :enrollments, :include => :user, :conditions => ['enrollments.workflow_state != ?', 'deleted'], :dependent => :destroy
30+
has_many :students, :through => :student_enrollments, :source => :user, :order => :sortable_name
3031
has_many :student_enrollments, :class_name => 'StudentEnrollment', :conditions => ['enrollments.workflow_state != ? AND enrollments.workflow_state != ? AND enrollments.workflow_state != ? AND enrollments.workflow_state != ?', 'deleted', 'completed', 'rejected', 'inactive'], :include => :user
3132
has_many :admin_enrollments, :class_name => 'Enrollment', :conditions => "(enrollments.type = 'TaEnrollment' or enrollments.type = 'TeacherEnrollment')"
3233
has_many :users, :through => :enrollments

config/routes.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,6 @@
647647
courses.get 'courses/:id', :action => :show
648648
courses.get 'courses/:course_id/sections', :action => :sections, :path_name => 'course_sections'
649649
courses.get 'courses/:course_id/students', :action => :students
650-
courses.get 'courses/:course_id/students/submissions', :controller => :submissions_api, :action => :for_students, :path_name => 'course_student_submissions'
651650
courses.get 'courses/:course_id/activity_stream', :action => :activity_stream
652651
end
653652

@@ -660,8 +659,16 @@
660659

661660
api.with_options(:controller => :submissions_api) do |submissions|
662661
submissions.get 'courses/:course_id/assignments/:assignment_id/submissions', :action => :index, :path_name => 'course_assignment_submissions'
662+
submissions.get 'sections/:section_id/assignments/:assignment_id/submissions', :action => :index, :path_name => 'section_assignment_submissions'
663+
664+
submissions.get 'courses/:course_id/students/submissions', :controller => :submissions_api, :action => :for_students, :path_name => 'course_student_submissions'
665+
submissions.get 'sections/:section_id/students/submissions', :controller => :submissions_api, :action => :for_students, :path_name => 'section_student_submissions'
666+
663667
submissions.get 'courses/:course_id/assignments/:assignment_id/submissions/:id', :action => :show
668+
submissions.get 'sections/:section_id/assignments/:assignment_id/submissions/:id', :action => :show
669+
664670
submissions.put 'courses/:course_id/assignments/:assignment_id/submissions/:id', :action => :update, :path_name => 'course_assignment_submission'
671+
submissions.put 'sections/:section_id/assignments/:assignment_id/submissions/:id', :action => :update, :path_name => 'section_assignment_submission'
665672
end
666673

667674
api.get 'courses/:course_id/assignment_groups', :controller => :assignment_groups, :action => :index, :path_name => 'course_assignment_groups'
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
<% @routes.each do |route| %>
2+
<% route_path = route.segments.inject("") { |str,s| str << s.to_s }.sub(/\(\.:format\)\?/, '').sub(/\/$/, '')
3+
next if route_path =~ /\.json$/ %>
14
<h3>
2-
<%= @route.conditions[:method].to_s.upcase %> <%= @route.segments.inject("") { |str,s| str << s.to_s }.sub(/\(\.:format\)\?/, '').sub(/\/$/, '') %>
3-
<span class='defined-in'><a href="https://github.com/instructure/canvas-lms/blob/master/app/controllers/<%= @route.requirements[:controller] %>_controller.rb"><%= "#{@route.requirements[:controller].camelize}Controller\##{@route.requirements[:action]}" %></a></span>
5+
<%= route.conditions[:method].to_s.upcase %> <%= route_path %>
6+
<span class='defined-in'><a href="https://github.com/instructure/canvas-lms/blob/master/app/controllers/<%= route.requirements[:controller] %>_controller.rb"><%= "#{route.requirements[:controller].camelize}Controller\##{route.requirements[:action]}" %></a></span>
47
</h3>
8+
<% end %>

doc/templates/rest/method_details/html/setup.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def init
2020
sections :header, [:method_signature, T('docstring')]
2121
@controller = object.parent.path.underscore.sub("_controller", '')
2222
@action = object.path.sub(/^.*#/, '')
23-
@route = ApiRouteSet.apis.first.api_methods_for_controller_and_action(@controller, @action).first
23+
@routes = ApiRouteSet.apis.first.api_methods_for_controller_and_action(@controller, @action)
2424
end
2525

2626
def header

lib/api.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ def self.map_ids(ids, collection)
6060
{ 'sis_user_id' => 'pseudonyms.sis_user_id', 'sis_login_id' => 'pseudonyms.sis_source_id' },
6161
Account.table_name =>
6262
{ 'sis_account_id' => 'sis_source_id' },
63+
CourseSection.table_name =>
64+
{ 'sis_section_id' => 'sis_source_id' },
6365
}
6466

6567
def self.sis_find_params_for_collection(collection, id, sis_find_params = nil)

spec/apis/v1/submissions_api_spec.rb

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,86 @@ def submit_homework(assignment, student, opts = {:body => "test!"})
5050
response.status.should match /404/
5151
end
5252

53+
describe "using section ids" do
54+
before do
55+
@student1 = user(:active_all => true)
56+
course_with_teacher(:active_all => true)
57+
@default_section = @course.default_section
58+
@section = factory_with_protected_attributes(@course.course_sections, :sis_source_id => 'my-section-sis-id', :name => 'section2')
59+
@course.enroll_user(@student1, 'StudentEnrollment', :section => @section).accept!
60+
end
61+
62+
it "should list submissions" do
63+
quiz = Quiz.create!(:title => 'quiz1', :context => @course)
64+
quiz.did_edit!
65+
quiz.offer!
66+
a1 = quiz.assignment
67+
sub = a1.find_or_create_submission(@student1)
68+
sub.submission_type = 'online_quiz'
69+
sub.workflow_state = 'submitted'
70+
sub.save!
71+
72+
json = api_call(:get,
73+
"/api/v1/sections/#{@default_section.id}/assignments/#{a1.id}/submissions.json",
74+
{ :controller => 'submissions_api', :action => 'index',
75+
:format => 'json', :section_id => @default_section.id.to_s,
76+
:assignment_id => a1.id.to_s },
77+
{ :include => %w(submission_history submission_comments rubric_assessment) })
78+
json.size.should == 0
79+
80+
json = api_call(:get,
81+
"/api/v1/sections/sis_section_id:my-section-sis-id/assignments/#{a1.id}/submissions.json",
82+
{ :controller => 'submissions_api', :action => 'index',
83+
:format => 'json', :section_id => 'sis_section_id:my-section-sis-id',
84+
:assignment_id => a1.id.to_s },
85+
{ :include => %w(submission_history submission_comments rubric_assessment) })
86+
json.size.should == 1
87+
json.first['user_id'].should == @student1.id
88+
89+
json = api_call(:get,
90+
"/api/v1/sections/#{@default_section.id}/students/submissions",
91+
{ :controller => 'submissions_api', :action => 'for_students',
92+
:format => 'json', :section_id => @default_section.id.to_s },
93+
:student_ids => [@student1.id])
94+
json.size.should == 0
95+
96+
json = api_call(:get,
97+
"/api/v1/sections/sis_section_id:my-section-sis-id/students/submissions",
98+
{ :controller => 'submissions_api', :action => 'for_students',
99+
:format => 'json', :section_id => 'sis_section_id:my-section-sis-id' },
100+
:student_ids => [@student1.id])
101+
json.size.should == 1
102+
end
103+
104+
it "should post to submissions" do
105+
a1 = @course.assignments.create!({:title => 'assignment1', :grading_type => 'percent', :points_possible => 10})
106+
107+
raw_api_call(:put,
108+
"/api/v1/sections/#{@default_section.id}/assignments/#{a1.id}/submissions/#{@student1.id}",
109+
{ :controller => 'submissions_api', :action => 'update',
110+
:format => 'json', :section_id => @default_section.id.to_s,
111+
:assignment_id => a1.id.to_s, :id => @student1.id.to_s },
112+
{ :submission => { :posted_grade => '75%' } })
113+
response.status.should == "404 Not Found"
114+
115+
json = api_call(:put,
116+
"/api/v1/sections/sis_section_id:my-section-sis-id/assignments/#{a1.id}/submissions/#{@student1.id}",
117+
{ :controller => 'submissions_api', :action => 'update',
118+
:format => 'json', :section_id => 'sis_section_id:my-section-sis-id',
119+
:assignment_id => a1.id.to_s, :id => @student1.id.to_s },
120+
{ :submission => { :posted_grade => '75%' } })
121+
122+
Submission.count.should == 1
123+
@submission = Submission.first
124+
125+
json['score'].should == 7.5
126+
json['grade'].should == '75%'
127+
end
128+
129+
it "should return submissions for a section" do
130+
end
131+
end
132+
53133
it "should return student discussion entries for discussion_topic assignments" do
54134
@student = user(:active_all => true)
55135
course_with_teacher(:active_all => true)

0 commit comments

Comments
 (0)