add submitted/unsubmitted quiz users endpoint to quizzes api

Test plan:
  - As a teacher, create a quiz.
  - Visit the quizzes API using JSONAPI headers. For that quiz,
    "submitted_students" should be null. You should get a URL back to
    the "unsubmitted_students" endpoint, which you can query and find
    the unsubmitted students.
  - As a student submit the quiz. As a different student, don't submit
    the quiz.
  - As a teacher, visit the quiz show endpoint for that quiz again. You
    should have "submitted_students" and "unsubmitted_students" under
    the "links" hash. Query these APIs using those URLs. The students
    returned from the link that looks like "?submitted=true"
  - As a student, you should not see "submitted_students" or
   "unsubmitted_students" under the "links" hash when querying
   the show API in JSONAPI format
  - In non-jsonapi format as any user, you should not see
   "unsubmitted_students" or "submitted_students"

closes CNVS-11687

Change-Id: I7094dfc37b0dde501e5a2c1f12ade983cd2a150a
Reviewed-on: https://gerrit.instructure.com/31223
Reviewed-by: Derek DeVries <ddevries@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
Product-Review: Stanley Stuart <stanley@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
This commit is contained in:
Stanley Stuart 2014-03-04 10:33:05 -06:00
parent 5aaf487658
commit 94f3b1bd15
13 changed files with 729 additions and 35 deletions

View File

@ -0,0 +1,178 @@
# Copyright (C) 2014 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/>.
#
module Quizzes
class QuizSubmissionUsersController < ::ApplicationController
include Filters::Quizzes
before_filter :require_context, :require_quiz
# @API List of users who have or haven't submitted for a quiz
# @beta
#
# @argument submitted [Optional, boolean]
# If true, return users who have submitted the quiz. If false, return users
# who have not submitted the quiz. If not present, returns all students for
# the course.
#
# @returns QuizSubmissionUserList
#
# @model QuizSubmissionUserList
#
# {
# "meta": {
# "$ref": "QuizSubmissionUserListMeta",
# "description": "contains meta information (such as pagination) for the list of users"
# },
# "users": {
# "$ref": "User",
# "description": "list of users that match the query"
# }
# }
#
# @model QuizSubmissionUserListMeta
#
# {
# "pagination": {
# "$ref": "JSONAPIPagination",
# "description": "contains pagination information for the list of users"
# }
# }
#
# @model JSONAPIPagination
#
# {
# "per_page": {
# "type": "integer",
# "description": "number of results per page",
# "example": 10
# },
# "page": {
# "type": "integer",
# "description": "the current page passed as the ?page= parameter",
# "example": 1
# },
# "template": {
# "type": "string",
# "description": "URL template for building out other paged URLs for this endpoint",
# "example": "https://example.instructure.com/api/v1/courses/1/quizzes/1/submission_users?page={page}"
# },
# "page_count": {
# "type": "integer",
# "description": "number of pages for this collection",
# "example": 10
# },
# "count": {
# "type": "integer",
# "description": "total number of items in this collection",
# "example": 100
# }
# }
def index
return unless user_has_teacher_level_access?
@users = if submitted_param?
@users = submitted? ? submitted_users : unsubmitted_users
else
@users = user_finder.all_students
end
@users, meta = Api.jsonapi_paginate(@users, self, index_base_url, page: params[:page])
users_json = @users.map { |user| user_json(user, @current_user, session) }
render json: { meta: meta, users: users_json }
end
# @API Send a message to unsubmitted or submitted users for the quiz
# @beta
#
# @param conversations [QuizUserConversation] - Body and recipients to send the message to.
#
# @model QuizUserConversation
#
# {
# "body": {
# "type": "string",
# "description": "message body of the conversation to be created",
# "example": "Please take the quiz."
# },
# "recipients": {
# "type": "string",
# "description": "Who to send the message to. May be either 'submitted' or 'unsubmitted'",
# "example": "submitted"
# },
# "subject": {
# "type": "string",
# "description": "Subject of the new Conversation created",
# "example": "ATTN: Quiz 101 Students"
# }
# }
def message
return unless user_has_teacher_level_access?
@conversation = Array(params[:conversations]).first
if @conversation
send_message
render json: { status: t('created', 'created') }, status: :created
else
render json: [], status: :invalid_request
end
end
private
def index_base_url
if submitted_param?
api_v1_course_quiz_submission_users_url(
@quiz.context,
@quiz,
submitted: submitted? ? 'true' : 'false'
)
else
api_v1_course_quiz_submission_users_url(@quiz.context, @quiz)
end
end
def submitted_param?
params.key?(:submitted)
end
def submitted?
::Canvas::Plugin.value_to_boolean(params[:submitted])
end
def submitted_users
user_finder.submitted_students
end
def unsubmitted_users
user_finder.unsubmitted_students
end
def user_finder
@user_finder ||= Quizzes::QuizUserFinder.new(@quiz, @current_user)
end
def send_message
Quizzes::QuizUserMessager.new(
conversation: @conversation,
root_account_id: @domain_root_account.id,
async: true,
sender: @current_user,
quiz: @quiz
).send
end
def user_has_teacher_level_access?
authorized_action(@quiz, @current_user, [:grade, :read_statistics])
end
end
end

View File

@ -1295,4 +1295,5 @@ class Quizzes::Quiz < ActiveRecord::Base
def self.reflection_type_name
'quizzes:quiz'
end
end

View File

@ -784,7 +784,7 @@ class Quizzes::QuizSubmission < ActiveRecord::Base
}
scope :for_user_ids, lambda { |user_ids| where(:user_id => user_ids) }
scope :logged_out, where("temporary_user_code is not null")
scope :not_settings_only, where("workflow_state<>'settings_only'")
scope :not_settings_only, where("quiz_submissions.workflow_state<>'settings_only'")
scope :completed, where(:workflow_state => %w(complete pending_review))
has_a_broadcast_policy

View File

@ -0,0 +1,53 @@
#
# Copyright (C) 2014 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/>.
#
module Quizzes
class QuizUserFinder
extend Forwardable
attr_reader :quiz, :user
def_delegators :@quiz, :context, :quiz_submissions
def initialize(quiz, user)
@quiz = quiz
@user = user
end
def submitted_students
all_students.where(id: non_preview_user_ids)
end
def unsubmitted_students
all_students.where('users.id NOT IN (?)', non_preview_user_ids)
end
def all_students
context.students_visible_to(user).order_by_sortable_name.group('users.id')
end
def non_preview_quiz_submissions
# This could optionally check for temporary_user_code<>NULL, but
# that's not indexed and we're checking user_id anyway in the queries above.
quiz_submissions.where('quiz_submissions.user_id IS NOT NULL')
end
private
def non_preview_user_ids
non_preview_quiz_submissions.not_settings_only.select(:user_id)
end
end
end

View File

@ -0,0 +1,81 @@
# Copyright (C) 2014 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/>.
#
module Quizzes
class QuizUserMessager
extend Forwardable
attr_reader :sender, :async, :recipient_list, :conversation, :quiz
attr_reader :root_account_id, :context_id
def_delegators :@user_finder,
:submitted_students,
:all_students,
:unsubmitted_students
def initialize(options)
@quiz = options.fetch(:quiz)
@sender = options.fetch(:sender)
@async = options.fetch(:async, true) ? :async : :sync
@conversation = options.fetch(:conversation)
@root_account_id = options.fetch(:root_account_id)
@context_id = quiz.context_id
@user_finder = Quizzes::QuizUserFinder.new(quiz, sender)
end
def send
ConversationBatch.generate(
message,
recipients,
async,
subject: subject,
context_id: context_id,
group: false
)
end
private
def message
@message ||= (
Conversation.build_message(
sender,
body,
root_account_id: root_account_id
)
)
end
def body
conversation[:body]
end
def subject
conversation[:subject]
end
def recipients
list = conversation.fetch(:recipients, 'all')
recipients = case list.to_s
when 'unsubmitted' then unsubmitted_students
when 'submitted' then submitted_students
else all_students
end
sender.load_messageable_users(recipients.pluck(:id))
end
end
end

View File

@ -15,7 +15,8 @@ module Quizzes
:hide_correct_answers_at, :all_dates, :can_unpublish, :can_update,
:require_lockdown_browser, :require_lockdown_browser_for_results,
:require_lockdown_browser_monitor, :lockdown_browser_monitor_data,
:speed_grader_url, :permissions, :quiz_reports_url, :quiz_statistics_url
:speed_grader_url, :permissions, :quiz_reports_url, :quiz_statistics_url,
:message_students_url
def_delegators :@controller,
:api_v1_course_assignment_group_url,
@ -23,29 +24,58 @@ module Quizzes
:api_v1_course_quiz_submission_url,
:api_v1_course_quiz_submissions_url,
:api_v1_course_quiz_reports_url,
:api_v1_course_quiz_statistics_url
:api_v1_course_quiz_statistics_url,
:api_v1_course_quiz_submission_users_url,
:api_v1_course_quiz_submission_users_message_url
def_delegators :@object,
:context,
:submitted_students_visible_to,
:unsubmitted_students_visible_to
has_one :assignment_group, embed: :ids, root: :assignment_group
has_many :quiz_submissions, embed: :ids, root: :quiz_submissions
has_many :submitted_students, embed: :ids, root: :submitted_students
has_many :unsubmitted_students, embed: :ids, root: :unsubmitted_students
def submitted_students
user_finder.submitted_students
end
def unsubmitted_students
user_finder.unsubmitted_students
end
def message_students_url
api_v1_course_quiz_submission_users_message_url(context, quiz)
end
def speed_grader_url
return nil unless show_speedgrader?
speed_grader_course_gradebook_url(quiz.context, assignment_id: quiz.assignment.id)
speed_grader_course_gradebook_url(context, assignment_id: quiz.assignment.id)
end
def quiz_submissions_url
if user_may_grade?
api_v1_course_quiz_submissions_url(quiz.context, quiz)
api_v1_course_quiz_submissions_url(context, quiz)
else
quiz_submission = quiz.quiz_submissions.where(user_id: current_user).first
if quiz_submission
api_v1_course_quiz_submission_url(quiz.context, quiz, quiz_submission)
api_v1_course_quiz_submission_url(context, quiz, quiz_submission)
else
nil
end
end
end
def unsubmitted_students_url
api_v1_course_quiz_submission_users_url(context, quiz, submitted: 'false')
end
def submitted_students_url
api_v1_course_quiz_submission_users_url(context, quiz, submitted: true)
end
def html_url
controller.send(:course_quiz_url, context, quiz)
end
@ -73,8 +103,9 @@ module Quizzes
super(keys).select do |key|
case key
when :all_dates then include_all_dates?
when :access_code, :speed_grader_url then user_may_grade?
when :access_code, :speed_grader_url, :message_students_url then user_may_grade?
when :unpublishable then include_unpublishable?
when :submitted_students, :unsubmitted_students then user_may_grade?
else true
end
end
@ -91,19 +122,18 @@ module Quizzes
def question_count
quiz.available_question_count
end
def require_lockdown_browser
quiz.require_lockdown_browser?
end
def require_lockdown_browser_for_results
quiz.require_lockdown_browser_for_results?
end
def require_lockdown_browser_monitor
quiz.require_lockdown_browser_monitor?
end
def lockdown_browser_monitor_data
quiz.lockdown_browser_monitor_data
end
@ -126,7 +156,7 @@ module Quizzes
end
def assignment_group_url
api_v1_course_assignment_group_url(quiz.context, quiz.assignment_group.id)
api_v1_course_assignment_group_url(context, quiz.assignment_group.id)
end
def quiz_reports_url
@ -144,7 +174,7 @@ module Quizzes
private
def show_speedgrader?
quiz.assignment.present? && quiz.published? && quiz.context.allows_speed_grader?
quiz.assignment.present? && quiz.published? && context.allows_speed_grader?
end
def due_dates
@ -176,5 +206,8 @@ module Quizzes
quiz.grants_right?(current_user, session, :grade)
end
def user_finder
@user_finder ||= Quizzes::QuizUserFinder.new(quiz, current_user)
end
end
end

View File

@ -1330,6 +1330,11 @@ routes.draw do
post "courses/:course_id/quizzes/:id/reorder", :action => :reorder, :path_name => 'course_quiz_reorder'
end
scope(:controller => 'quizzes/quiz_submission_users') do
get "courses/:course_id/quizzes/:id/submission_users", :action => :index, :path_name => 'course_quiz_submission_users'
post "courses/:course_id/quizzes/:id/submission_users/message", :action => :message, :path_name => 'course_quiz_submission_users_message'
end
scope(:controller => 'quizzes/quiz_groups') do
post "courses/:course_id/quizzes/:quiz_id/groups", :action => :create, :path_name => 'course_quiz_group_create'
put "courses/:course_id/quizzes/:quiz_id/groups/:id", :action => :update, :path_name => 'course_quiz_group_update'

View File

@ -0,0 +1,127 @@
# Copyright (C) 2014 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/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../../api_spec_helper')
require File.expand_path(File.dirname(__FILE__) + '/../../../models/quizzes/quiz_user_messager_spec_helper')
describe Quizzes::QuizSubmissionUsersController, type: :request do
before do
course_with_teacher_logged_in(active_all: true)
course_quiz(true)
end
def controller_options(options)
options.reverse_merge!({
controller: "quizzes/quiz_submission_users",
action: "message",
format: "json",
course_id: @course.id,
id: @quiz.id
})
end
describe "POST message" do
include Quizzes::QuizUserMessagerSpecHelper
before do
@finder = Quizzes::QuizUserFinder.new(@quiz, @teacher)
course_with_student(active_all: true, course: @course)
@user = @teacher
end
def send_message(target_group)
raw_api_call(
:post,
"/api/v1/courses/#{@course.id}/quizzes/#{@quiz.id}/submission_users/message",
controller_options(
action: 'message',
conversations: [
{ body: 'Ohi!', recipients: target_group.to_s }
]
)
)
run_jobs
end
it "sends a message to unsubmitted users" do
expect { send_message(:unsubmitted) }.to change { recipient_messages(:unsubmitted) }.by 1
recipient_messages(:submitted).should == 0
end
it "sends a message to submitted users" do
sub = @quiz.generate_submission(@student)
sub.mark_completed
sub.grade_submission
expect { send_message(:submitted) }.to change { recipient_messages(:submitted) }.by 1
recipient_messages(:unsubmitted).should == 0
end
end
describe "GET submission_users" do
def get_submitted_users(options={})
options = controller_options(options.reverse_merge!(action: 'index'))
raw_api_call(
:get,
"/api/v1/courses/#{@course.id}/quizzes/#{@quiz.id}/submission_users",
options,
{ 'Accept' => 'application/json'}
)
JSON.parse(response.body) if response.success?
end
it "does not allow students to view information at the endpoint" do
course_with_student_logged_in(course: @course, active_all: true)
get_submitted_users
response.should_not be_success
end
it "allows teachers to see submitted students with ?submitted=true" do
course_with_student(active_all: true, course: @course)
quiz_with_graded_submission([], course: @course, user: @student)
@user = @teacher
json = get_submitted_users(submitted: true)
response.should be_success
json['users'].first['id'].should == @student.id
end
it "allows teachers to see unsubmitted students with ?submitted=false" do
course_with_student(active_all: true, course: @course)
@student_frd = @student
quiz_with_graded_submission([], course: @course, user: @student_frd)
course_with_student(active_all: true, course: @course)
@user = @teacher
json = get_submitted_users(submitted: false)
response.should be_success
user_ids = json['users'].map { |h| h['id'] }
user_ids.should_not include @student_frd.id
user_ids.should include @student.id
end
it "allows teachers to see all students for quiz when submitted parameter not passed" do
course_with_student(active_all: true, course: @course)
@student_frd = @student
quiz_with_graded_submission([], course: @course, user: @student_frd)
course_with_student(active_all: true, course: @course)
@user = @teacher
json = get_submitted_users
response.should be_success
user_ids = json['users'].map { |h| h['id'] }
user_ids.should include @student_frd.id
user_ids.should include @student.id
end
end
end

View File

@ -0,0 +1,67 @@
#
# Copyright (C) 2014 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/>.
#
require 'spec_helper'
describe Quizzes::QuizUserFinder do
before do
course_with_teacher_logged_in(course: @course, active_all: true)
course_quiz(true)
course_with_student(active_all: true, course: @course)
@submitted_student = @student
course_with_student(active_all: true, course: @course)
@unsubmitted_student = @student
sub = @quiz.generate_submission(@submitted_student)
sub.mark_completed
sub.grade_submission
@finder = Quizzes::QuizUserFinder.new(@quiz, @teacher)
end
def students
[ @unsubmitted_student, @submitted_student ]
end
it "(#all_students) finds all students" do
@finder.all_students.should =~ students
end
it "(#unsubmitted_students) finds unsubmitted students" do
@finder.unsubmitted_students.should == [ @unsubmitted_student ]
end
it "(#submitted_student) finds submitted students" do
@finder.submitted_students.should == [ @submitted_student ]
end
it "doesn't find submissions from teachers for preview submissions" do
sub = @quiz.generate_submission(@teacher, preview=true)
sub.grade_submission
sub.save!
@finder.submitted_students.should_not include @teacher
@finder.unsubmitted_students.should_not include @teacher
@finder.unsubmitted_students.should_not be_empty
@finder.all_students.should_not include @teacher
end
it "doesn't duplicate the same user found in multiple sections" do
add_section('The Mother We Share')
student_in_section(@course_section, user: @submitted_student)
@finder.all_students.should =~ students
end
end

View File

@ -0,0 +1,53 @@
# Copyright (C) 2014 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/>.
#
require 'spec_helper'
require File.expand_path(File.dirname(__FILE__) + "/quiz_user_messager_spec_helper.rb")
describe Quizzes::QuizUserMessager do
include Quizzes::QuizUserMessagerSpecHelper
before do
course_with_teacher_logged_in(active_all: true)
course_quiz(true)
course_with_student(active_all: true, course: @course)
@unsubmitted = @student
course_with_student(active_all: true, course: @course)
@submitted = @student
submission = @quiz.generate_submission(@submitted)
submission.mark_completed
submission.grade_submission
@finder = Quizzes::QuizUserFinder.new(@quiz, @teacher)
end
describe "#send" do
it "sends to all students" do
expect { send_message }.to change { recipient_messages('all') }.by 2
end
it "can send to either submitted or unsubmitted students" do
expect {
send_message('submitted')
}.to change { recipient_messages('submitted') }.by 1
expect {
send_message('unsubmitted')
}.to change { recipient_messages('unsubmitted') }.by 1
end
end
end

View File

@ -0,0 +1,45 @@
# Copyright (C) 2014 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/>.
#
module Quizzes
module QuizUserMessagerSpecHelper
def conversation(recipients)
{
subject: "Do you want ants?",
body: "Because that's how you get ants",
recipients: recipients
}
end
def send_message(recipients='all')
options = {
quiz: @quiz,
sender: @teacher,
conversation: conversation(recipients),
root_account_id: Account.default.id
}
Quizzes::QuizUserMessager.new(options).send
run_jobs
end
def recipient_messages(target_group)
recipients = @finder.send("#{target_group}_students")
recipients.map(&:all_conversations).map(&:size).reduce(:+) || 0
end
end
end

View File

@ -1,6 +1,15 @@
require 'spec_helper'
describe Quizzes::QuizSerializer do
def quiz_serializer(options={})
options.reverse_merge!({
controller: controller,
scope: @user,
session: @session
})
Quizzes::QuizSerializer.new(@quiz, options)
end
let(:quiz) { @quiz }
let(:context ) { @context }
let(:serializer) { @serializer }
@ -24,10 +33,7 @@ describe Quizzes::QuizSerializer do
controller.stubs(:session).returns session
controller.stubs(:context).returns context
@quiz.stubs(:grants_right?).at_least_once.returns true
@serializer = Quizzes::QuizSerializer.new(@quiz,
controller: controller,
scope: @user,
session: @session)
@serializer = quiz_serializer
@json = @serializer.as_json[:quiz]
end
@ -95,6 +101,15 @@ describe Quizzes::QuizSerializer do
serializer.as_json[:quiz][:question_count].should == 5
end
it "sends the message_students_url when user can grade" do
quiz.expects(:grants_right?).at_least_once.returns true
serializer.as_json[:quiz][:message_students_url].should ==
controller.send(:api_v1_course_quiz_submission_users_message_url, quiz, quiz.context)
quiz.expects(:grants_right?).at_least_once.returns false
serializer.as_json[:quiz].should_not have_key :message_students_url
end
describe "id" do
it "stringifys when stringify_json_ids? is true" do
@ -116,8 +131,7 @@ describe Quizzes::QuizSerializer do
quiz.expects(:locked_for?).
with(@user, check_policies: true, context: @context).
returns({due_at: true})
json = Quizzes::QuizSerializer.new(quiz, scope: @user, controller: controller).
as_json[:quiz]
json = quiz_serializer.as_json[:quiz]
json.should have_key :lock_info
json.should have_key :lock_explanation
json[:locked_for_user].should == true
@ -125,8 +139,7 @@ describe Quizzes::QuizSerializer do
quiz.expects(:locked_for?).
with(@user, check_policies: true, context: @context).
returns false
json = Quizzes::QuizSerializer.new(quiz, scope: @user, controller: controller).
as_json[:quiz]
json = quiz_serializer.as_json[:quiz]
json.should_not have_key :lock_info
json.should_not have_key :lock_explanation
json[:locked_for_user].should == false
@ -184,9 +197,7 @@ describe Quizzes::QuizSerializer do
it "sends the url for all submissions when user may grade" do
course_with_teacher_logged_in(active_all: true)
quiz_with_graded_submission([], course: @course)
serializer = Quizzes::QuizSerializer.new(@quiz,
controller: controller,
scope: @teacher)
serializer = quiz_serializer(scope: @teacher)
serializer.as_json[:quiz]['links']['quiz_submissions'].should ==
controller.send(:api_v1_course_quiz_submissions_url, @quiz.context.id, @quiz.id)
end
@ -194,22 +205,17 @@ describe Quizzes::QuizSerializer do
it "sends the url to a student's submission for students" do
course_with_student_logged_in(active_all: true)
quiz_with_graded_submission([], user: @student, course: @course)
serializer = Quizzes::QuizSerializer.new(@quiz,
controller: controller,
scope: @student)
serializer = quiz_serializer(scope: @student)
serializer.as_json[:quiz]['links']['quiz_submissions'].should ==
controller.send(:api_v1_course_quiz_submission_url,
@quiz.context.id,
@quiz.id,
@quiz.quiz_submissions.where(user_id: @student).first.id)
end
it "sends nil if user can't grade and doesn't have a submission" do
course_with_student_logged_in(active_all: true)
serializer = Quizzes::QuizSerializer.new(@quiz,
controller: controller,
scope: @student)
serializer = quiz_serializer(scope: @student)
serializer.as_json[:quiz]['links']['quiz_submissions'].should be_nil
end
end
@ -243,6 +249,51 @@ describe Quizzes::QuizSerializer do
controller.send(:api_v1_course_quiz_statistics_url, 3, quiz.id)
end
end
describe "submitted_students" do
it "sends nil if user can't grade" do
course_with_student_logged_in(active_all: true)
@quiz.unstub(:grants_right?)
serializer = quiz_serializer(scope: @student)
serializer.as_json[:quiz]['links'].should_not have_key 'unsubmitted_students'
end
it "sends a url if there are submissions and user can grade" do
course_with_teacher_logged_in(active_all: true)
course_with_student_logged_in(active_all: true, course: @course)
quiz_with_graded_submission([], user: @student, course: @course)
serializer = quiz_serializer(scope: @teacher)
serializer.as_json[:quiz]['links']['submitted_students'].
should == controller.send(:api_v1_course_quiz_submission_users_url,
@quiz.context,
@quiz,
submitted: true)
end
end
describe "unsubmitted_students" do
it "sends nil if user can't grade" do
@quiz.unstub(:grants_right?)
course_with_student_logged_in(active_all: true)
serializer = quiz_serializer(scope: @student)
serializer.as_json[:quiz]['links'].should_not have_key 'unsubmitted_students'
end
it "sends a url if there are submissions and user can grade" do
course_with_teacher_logged_in(active_all: true)
course_with_student_logged_in(active_all: true, course: @course)
course_with_student_logged_in(active_all: true, course: @course)
quiz_with_graded_submission([], user: @student, course: @course)
serializer = quiz_serializer(scope: @teacher)
serializer.as_json[:quiz]['links']['unsubmitted_students'].
should == controller.send(:api_v1_course_quiz_submission_users_url,
@quiz.context,
@quiz,
submitted: 'false')
end
end
end
describe "permissions" do

View File

@ -638,12 +638,12 @@ end
end
def student_in_section(section, opts={})
user
enrollment = section.course.enroll_user(@user, 'StudentEnrollment', :section => section)
@user.save!
student = opts.fetch(:user) { user }
enrollment = section.course.enroll_user(student, 'StudentEnrollment', :section => section)
student.save!
enrollment.workflow_state = 'active'
enrollment.save!
@user
student
end
def teacher_in_course(opts={})