canvas-lms/app/controllers/assignments_api_controller.rb

260 lines
9.0 KiB
Ruby
Raw Normal View History

2011-02-01 09:57:29 +08:00
#
# 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/>.
#
# @API Assignments
#
# API for accessing assignment information.
#
# @object Assignment
# {
# // the ID of the assignment
# id: 4,
#
# // the name of the assignment
# name: "some assignment",
#
# // the assignment description, in an HTML fragment
# description: '<p>Do the following:</p>...',
#
# // the due date
# due_at: '2012-07-01T23:59:00-06:00',
#
# // the ID of the course the assignment belongs to
# course_id: 123,
#
# // the URL to the assignment's web page
# html_url: 'http://canvas.example.com/courses/123/assignments/4'
#
# // the ID of the assignment's group
# assignment_group_id: 2,
#
# // the ID of the assignments group set (if this is a group assignment)
# group_category_id: 1
#
# // if the requesting user has grading rights, the number of submissions that need grading.
# needs_grading_count: 17,
#
# // the sorting order of the assignment in the group
# position: 1,
#
# // the URL to the Canvas web UI page for the assignment
# html_url: "https://...",
#
# // whether the assignment is muted
# muted: false,
#
# // (Optional) explanation of lock status
# lock_explanation: "This assignment is locked until September 1 at 12:00am",
#
# // (Optional) whether anonymous submissions are accepted (applies only to quiz assignments)
# anonymous_submissions: false,
#
# // (Optional) list of file extensions allowed for submissions
# allowed_extensions: ["doc","xls"],
#
# // (Optional) the DiscussionTopic associated with the assignment, if applicable
# discussion_topic: { ... },
#
# // the maximum points possible for the assignment
# points_possible: 12,
#
# // the types of submissions allowed for this assignment
# // list containing one or more of the following:
# // "online_text_entry", "online_url", "online_upload", "media_recording"
# submission_types: ["online_text_entry"]
#
# // (Optional) the type of grading the assignment receives;
# // one of 'pass_fail', 'percent', 'letter_grade', 'points'
# grading_type: "points",
#
# // if true, the rubric is directly tied to grading the assignment.
# // Otherwise, it is only advisory.
# use_rubric_for_grading: true,
#
# // an object describing the basic attributes of the rubric, including the point total
# rubric_settings: {
# points_possible: 12
# },
#
# // a list of scoring criteria and ratings for each
# rubric: [
# {
# "points": 10,
# "id": "crit1",
# "description": "Criterion 1",
# "ratings": [
# {
# "points": 10,
# "id": "rat1",
# "description": "Full marks"
# },
# {
# "points": 7,
# "id": "rat2",
# "description": "Partial answer"
# },
# {
# "points": 0,
# "id": "rat3",
# "description": "No marks"
# }
# ]
# },
# {
# "points": 2,
# "id": "crit2",
# "description": "Criterion 2",
# "ratings": [
# {
# "points": 2,
# "id": "rat1",
# "description": "Pass"
# },
# {
# "points": 0,
# "id": "rat2",
# "description": "Fail"
# }
# ]
# }
# ]
# }
#
2011-02-01 09:57:29 +08:00
class AssignmentsApiController < ApplicationController
before_filter :require_context
include Api::V1::Assignment
include Api::V1::AssignmentOverride
2011-02-01 09:57:29 +08:00
# @API List assignments
# Returns the list of assignments for the current context.
# @returns [Assignment]
2011-02-01 09:57:29 +08:00
def index
if authorized_action(@context, @current_user, :read)
@assignments = @context.active_assignments.find(:all,
:include => [:assignment_group, :rubric_association, :rubric],
:order => 'assignment_groups.position, assignments.position')
hashes = @assignments.map { |assignment|
assignment_json(assignment, @current_user, session) }
2011-02-01 09:57:29 +08:00
render :json => hashes.to_json
end
end
# @API Get a single assignment
# Returns the assignment with the given id.
# @returns Assignment
2011-02-01 09:57:29 +08:00
def show
if authorized_action(@context, @current_user, :read)
@assignment = @context.active_assignments.find(params[:id],
:include => [:assignment_group, :rubric_association, :rubric])
modules api, closes #10404 also modifies the discussion topic and assignment API controllers to make sure "must_view" requirements are fulfilled test plan: * check the API documentation; ensure it looks okay * create a course with module items of each supported type * set completion criteria of each supported type * create another module, so you can set prerequisites * use the list modules API and verify its output matches the course and the documentation * as a teacher, "state" should be missing * as a student, "state" should be "locked", "unlocked", "started", or "completed" * use the show module API and verify the correct information is returned for a single module * use the list module items API and verify the output * as a teacher, the "completion_requirement" omits the "completed" flag * as a student, "completed" should be true or false, depending on whether the requirement was met * use the show module API and verify the correct information is returned for a single module item * last but not least, verify "must view" requirements can be fulfilled through the api_data_endpoints supplied for files, pages, discussions, and assignments * files are viewed when downloading their content * pages are viewed by the show action (where content is returned) * discussions are viewed when marked read via the mark_topic_read or mark_all_read actions * assignments are viewed by the show action (where description is returned). they are not viewed if the assignment is locked and the user does not have access to the content yet. Change-Id: I0cbbbc542f69215e7b396a501d4d86ff2f76c149 Reviewed-on: https://gerrit.instructure.com/13626 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Simon Williams <simon@instructure.com>
2012-09-12 01:16:48 +08:00
@assignment.context_module_action(@current_user, :read) unless @assignment.locked_for?(@current_user, :check_policies => true)
render :json => assignment_json(@assignment, @current_user, session)
2011-02-01 09:57:29 +08:00
end
end
# @API Create an assignment
2011-02-01 09:57:29 +08:00
# Create a new assignment for this course. The assignment is created in the
# active state.
#
# @argument assignment[name] The assignment name.
# @argument assignment[position] [Integer] The position of this assignment in the
# group when displaying assignment lists.
# @argument assignment[points_possible] [Float] The maximum points possible on
# the assignment.
# @argument assignment[grading_type] [Optional, "pass_fail"|"percent"|"letter_grade"|"points"] The strategy used for grading the assignment. The assignment is ungraded if this field is omitted.
# @argument assignment[due_at] [Timestamp] The day/time the assignment is due. Accepts
# times in ISO 8601 format, e.g. 2011-10-21T18:48Z.
# @argument assignment[description] [String] The assignment's description, supports HTML.
# @argument assignment[assignment_group_id] [Integer] The assignment group id to put the assignment in. Defaults to the top assignment group in the course.
# @argument assignment[assignment_overrides] [Optional, [AssignmentOverride]]
# List of overrides for the assignment.
# @returns Assignment
2011-02-01 09:57:29 +08:00
def create
@assignment = @context.assignments.build
2011-02-01 09:57:29 +08:00
if authorized_action(@assignment, @current_user, :create)
if update_and_save_assignment(@assignment, params[:assignment])
render :json => assignment_json(@assignment, @current_user, session).to_json, :status => 201
2011-02-01 09:57:29 +08:00
else
# TODO: we don't really have a strategy in the API yet for returning
# errors.
render :json => 'error'.to_json, :status => 400
end
end
end
# @API Edit an assignment
2011-02-01 09:57:29 +08:00
# Modify an existing assignment. See the documentation for assignment
# creation.
#
# If the assignment[assignment_overrides] key is absent, any existing
# overrides are kept as is. If the assignment[assignment_overrides] key is
# present, existing overrides are updated or deleted (and new ones created,
# as necessary) to match the provided list.
#
# @returns Assignment
2011-02-01 09:57:29 +08:00
def update
@assignment = @context.assignments.find(params[:id])
if authorized_action(@assignment, @current_user, :update_content)
if @assignment.frozen?
render :json => {:message => t('errors.no_edit_frozen', "You cannot edit a frozen assignment.")}.to_json, :status => 400
elsif update_and_save_assignment(@assignment, params[:assignment])
render :json => assignment_json(@assignment, @current_user, session).to_json, :status => 201
2011-02-01 09:57:29 +08:00
else
# TODO: we don't really have a strategy in the API yet for returning
# errors.
render :json => 'error'.to_json, :status => 400
2011-02-01 09:57:29 +08:00
end
end
end
protected
def update_and_save_assignment(assignment, assignment_params)
# convert hashes like {0 => x, 1 => y} into arrays like [x, y]
overrides = assignment_params[:assignment_overrides]
if overrides.is_a?(Hash)
return unless overrides.keys.all?{ |k| k.to_i.to_s == k.to_s }
indices = overrides.keys.sort_by(&:to_i)
return unless indices.map(&:to_i) == (0...indices.size).to_a
overrides = indices.map{ |index| overrides[index] }
end
# require it to be formatted as an array if it's present
return if overrides && !overrides.is_a?(Array)
# do the updating
VDD: notifications; closes #10896 The following changes have been made: - Assignment Created - students see the due date that applies to them - admins see "Multiple Dates" - Assignment Due Date Changed - students see the due date that applies to them; they receive no notification if their date doesn't change - admins receive a separate notification for each due date that changes, that they have access to; the message indicates which section or group applies (section-limited TAs will not get messages about due dates in sections they can't see) - Assignment Submitted Late - the message text does not change, but the student's overridden due date is checked - Group Assignment Submitted Late - same as previous There were some bugs fixed along the way: - no longer send duplicate Assignment Submitted and Assignment Resubmitted notifications when an assignment is resubmitted - Group Assignment Submitted Late actually goes out (there was a typo in the whenever clause) Test plan: - Create a course with two sections and a teacher - Enroll a student in each section - Enroll a section-limited TA in each section - Make sure everybody involved is signed up for "Due Date" notifications, ASAP - Using the API, Create an assignment with a default due date (in the past) and an overridden due date for section 2 (in the future). the assignment and override must be created in the same request (use the "Create an assignment" API and supply assignment[assignment_overrides]; it may be easier to use a JSON request body) - Verify that everybody got an "Assignment Created" message (use /users/X/messages) - the teacher should see "Multiple Dates", as should the TA in section 2 (because the default date is still visible to him) - the student and the TA in section 1 should see the default due date - the student in section 2 should see the overridden due date - "Due Date Changed" messages will not go out for assignments that were created less than 3 hours ago (by design, and not new with this changeset), so for the remaining items, you either need to wait 3 hours, or falsify created_at for the assignment you just made... - Change the default due date for the assignment, leaving it in the past - Everybody except the student in section 2 should get a notification with the new date - Change the overridden due date for section 2, leaving it in the future - everybody except the teacher and TA in section 1 should get a notification about the new date - the teacher and section-2 TA's notifications should indicate that they apply to section 2 (the student's should not) - submit the assignment as each student - the teacher should get one notification about each submission: the one about the student in section 1 should say it's late; the one about the student in section 2 should not - submit again - the teacher should get one notification about each submission: the one about the student in section 1 should say it's late; the one about the student in section 2 should not, and should be identified as a resubmission (there is no late-re-submission notification) Change-Id: I26e57807ea0c83b69e2b532ec8822f6570ba1701 Reviewed-on: https://gerrit.instructure.com/14662 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
2012-10-27 04:10:08 +08:00
update_api_assignment(assignment, assignment_params, false)
if overrides
assignment.transaction do
assignment.save_without_broadcasting!
batch_update_assignment_overrides(assignment, overrides)
end
assignment.do_notifications!
else
assignment.save!
end
VDD: notifications; closes #10896 The following changes have been made: - Assignment Created - students see the due date that applies to them - admins see "Multiple Dates" - Assignment Due Date Changed - students see the due date that applies to them; they receive no notification if their date doesn't change - admins receive a separate notification for each due date that changes, that they have access to; the message indicates which section or group applies (section-limited TAs will not get messages about due dates in sections they can't see) - Assignment Submitted Late - the message text does not change, but the student's overridden due date is checked - Group Assignment Submitted Late - same as previous There were some bugs fixed along the way: - no longer send duplicate Assignment Submitted and Assignment Resubmitted notifications when an assignment is resubmitted - Group Assignment Submitted Late actually goes out (there was a typo in the whenever clause) Test plan: - Create a course with two sections and a teacher - Enroll a student in each section - Enroll a section-limited TA in each section - Make sure everybody involved is signed up for "Due Date" notifications, ASAP - Using the API, Create an assignment with a default due date (in the past) and an overridden due date for section 2 (in the future). the assignment and override must be created in the same request (use the "Create an assignment" API and supply assignment[assignment_overrides]; it may be easier to use a JSON request body) - Verify that everybody got an "Assignment Created" message (use /users/X/messages) - the teacher should see "Multiple Dates", as should the TA in section 2 (because the default date is still visible to him) - the student and the TA in section 1 should see the default due date - the student in section 2 should see the overridden due date - "Due Date Changed" messages will not go out for assignments that were created less than 3 hours ago (by design, and not new with this changeset), so for the remaining items, you either need to wait 3 hours, or falsify created_at for the assignment you just made... - Change the default due date for the assignment, leaving it in the past - Everybody except the student in section 2 should get a notification with the new date - Change the overridden due date for section 2, leaving it in the future - everybody except the teacher and TA in section 1 should get a notification about the new date - the teacher and section-2 TA's notifications should indicate that they apply to section 2 (the student's should not) - submit the assignment as each student - the teacher should get one notification about each submission: the one about the student in section 1 should say it's late; the one about the student in section 2 should not - submit again - the teacher should get one notification about each submission: the one about the student in section 1 should say it's late; the one about the student in section 2 should not, and should be identified as a resubmission (there is no late-re-submission notification) Change-Id: I26e57807ea0c83b69e2b532ec8822f6570ba1701 Reviewed-on: https://gerrit.instructure.com/14662 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
2012-10-27 04:10:08 +08:00
return true
rescue ActiveRecord::RecordInvalid
return false
end
2011-02-01 09:57:29 +08:00
end