270 lines
11 KiB
Ruby
270 lines
11 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/>.
|
|
#
|
|
|
|
# @API Assignment Groups
|
|
#
|
|
# API for accessing Assignment Group and Assignment information.
|
|
#
|
|
# @model GradingRules
|
|
# {
|
|
# "id": "GradingRules",
|
|
# "description": "",
|
|
# "properties": {
|
|
# "drop_lowest": {
|
|
# "description": "Number of lowest scores to be dropped for each user.",
|
|
# "example": 1,
|
|
# "type": "integer"
|
|
# },
|
|
# "drop_highest": {
|
|
# "description": "Number of highest scores to be dropped for each user.",
|
|
# "example": 1,
|
|
# "type": "integer"
|
|
# },
|
|
# "never_drop": {
|
|
# "description": "Assignment IDs that should never be dropped.",
|
|
# "example": "[33, 17, 24]",
|
|
# "type": "array",
|
|
# "items": {"type": "integer"}
|
|
# }
|
|
# }
|
|
# }
|
|
# @model AssignmentGroup
|
|
# {
|
|
# "id": "AssignmentGroup",
|
|
# "description": "",
|
|
# "properties": {
|
|
# "id": {
|
|
# "description": "the id of the Assignment Group",
|
|
# "example": 1,
|
|
# "type": "integer"
|
|
# },
|
|
# "name": {
|
|
# "description": "the name of the Assignment Group",
|
|
# "example": "group2",
|
|
# "type": "string"
|
|
# },
|
|
# "position": {
|
|
# "description": "the position of the Assignment Group",
|
|
# "example": 7,
|
|
# "type": "integer"
|
|
# },
|
|
# "group_weight": {
|
|
# "description": "the weight of the Assignment Group",
|
|
# "example": 20,
|
|
# "type": "integer"
|
|
# },
|
|
# "assignments": {
|
|
# "description": "the assignments in this Assignment Group (see the Assignment API for a detailed list of fields)",
|
|
# "example": "[]",
|
|
# "type": "array",
|
|
# "items": {"type": "integer"}
|
|
# },
|
|
# "rules": {
|
|
# "description": "the grading rules that this Assignment Group has",
|
|
# "$ref": "GradingRules"
|
|
# }
|
|
# }
|
|
# }
|
|
#
|
|
class AssignmentGroupsController < ApplicationController
|
|
before_filter :require_context
|
|
|
|
include Api::V1::AssignmentGroup
|
|
|
|
# @API List assignment groups
|
|
#
|
|
# Returns the list of assignment groups for the current context. The returned
|
|
# groups are sorted by their position field.
|
|
#
|
|
# @argument include[] [String, "assignments"|"discussion_topic"|"all_dates"|"assignment_visibility"]
|
|
# Associations to include with the group. "discussion_topic", "all_dates"
|
|
# "assignment_visibility" are only valid are only valid if "assignments" is also included.
|
|
# The "assignment_visibility" option additionally requires that the Differentiated Assignments course feature be turned on.
|
|
#
|
|
# @argument override_assignment_dates [Boolean]
|
|
# Apply assignment overrides for each assignment, defaults to true.
|
|
#
|
|
# @returns [AssignmentGroup]
|
|
def index
|
|
if authorized_action(@context.assignment_groups.scoped.new, @current_user, :read)
|
|
@groups = @context.assignment_groups.active
|
|
|
|
params[:include] = Array(params[:include])
|
|
assignments_by_group = {}
|
|
if params[:include].include? 'assignments'
|
|
assignment_includes = [:rubric, :quiz, :external_tool_tag, :rubric_association]
|
|
assignment_includes.concat(params[:include] & ["discussion_topic"])
|
|
assignment_includes.concat([:assignment_overrides]) if params[:include].include?("all_dates")
|
|
|
|
all_visible_assignments = AssignmentGroup.visible_assignments(@current_user, @context, @groups, assignment_includes)
|
|
.with_student_submission_count
|
|
|
|
da_enabled = @context.feature_enabled?(:differentiated_assignments)
|
|
include_visibility = Array(params[:include]).include?('assignment_visibility') && @context.grants_any_right?(@current_user, :read_as_admin, :manage_grades, :manage_assignments)
|
|
if include_visibility && da_enabled
|
|
assignment_visibilities = AssignmentStudentVisibility.users_with_visibility_by_assignment(course_id: @context.id, assignment_id: all_visible_assignments.map(&:id))
|
|
else
|
|
params[:include].delete('assignment_visibility')
|
|
end
|
|
|
|
# because of a bug with including content_tags, we are preloading here rather than in
|
|
# visible_assignments with multiple associations referencing content_tags table and therefore
|
|
# aliased table names the conditons on has_many :context_module_tags will break
|
|
if params[:include].include? "module_ids"
|
|
module_includes = [:context_module_tags,{:discussion_topic => :context_module_tags},{:quiz => :context_module_tags}]
|
|
ActiveRecord::Associations::Preloader.new(all_visible_assignments, module_includes).run
|
|
end
|
|
|
|
assignment_descriptions = all_visible_assignments.map(&:description)
|
|
assignments_by_group = all_visible_assignments.group_by(&:assignment_group_id)
|
|
user_content_attachments = api_bulk_load_user_content_attachments(
|
|
assignment_descriptions, @context, @current_user
|
|
)
|
|
|
|
override_param = params[:override_assignment_dates] || true
|
|
override_dates = value_to_boolean(override_param)
|
|
if override_dates
|
|
assignments_with_overrides = @context.assignments.active.except(:order)
|
|
.joins(:assignment_overrides)
|
|
.select("assignments.id")
|
|
.uniq
|
|
assignments_without_overrides = all_visible_assignments - assignments_with_overrides
|
|
assignments_without_overrides.each { |a| a.has_no_overrides = true }
|
|
end
|
|
end
|
|
|
|
respond_to do |format|
|
|
format.json {
|
|
json = @groups.map { |g|
|
|
g.context = @context
|
|
assignment_group_json(g, @current_user, session, params[:include],
|
|
stringify_json_ids: stringify_json_ids?,
|
|
override_assignment_dates: override_dates,
|
|
preloaded_user_content_attachments: user_content_attachments,
|
|
assignments: assignments_by_group[g.id],
|
|
assignment_visibilities: assignment_visibilities,
|
|
differentiated_assignments_enabled: da_enabled
|
|
)
|
|
}
|
|
render :json => json
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
def reorder
|
|
if authorized_action(@context.assignment_groups.scoped.new, @current_user, :update)
|
|
order = params[:order].split(',')
|
|
@context.assignment_groups.first.update_order(order)
|
|
new_order = @context.assignment_groups.pluck(:id)
|
|
render :json => {:reorder => true, :order => new_order}, :status => :ok
|
|
end
|
|
end
|
|
|
|
def reorder_assignments
|
|
@group = @context.assignment_groups.find(params[:assignment_group_id])
|
|
if authorized_action(@group, @current_user, :update)
|
|
order = params[:order].split(',').map{|id| id.to_i }
|
|
group_ids = ([@group.id] + (order.empty? ? [] : @context.assignments.where(id: order).uniq.except(:order).pluck(:assignment_group_id)))
|
|
Assignment.where(:id => order, :context_id => @context, :context_type => @context.class.to_s).update_all(:assignment_group_id => @group)
|
|
@group.assignments.first.update_order(order) unless @group.assignments.empty?
|
|
AssignmentGroup.where(:id => group_ids).update_all(:updated_at => Time.now.utc)
|
|
ids = @group.active_assignments.map(&:id)
|
|
@context.recompute_student_scores rescue nil
|
|
respond_to do |format|
|
|
format.json { render :json => {:reorder => true, :order => ids}, :status => :ok }
|
|
end
|
|
end
|
|
end
|
|
|
|
def show
|
|
@assignment_group = @context.assignment_groups.find(params[:id])
|
|
if @assignment_group.deleted?
|
|
respond_to do |format|
|
|
flash[:notice] = t 'notices.deleted', "This group has been deleted"
|
|
format.html { redirect_to named_context_url(@context, :assignments_url) }
|
|
end
|
|
return
|
|
end
|
|
if authorized_action(@assignment_group, @current_user, :read)
|
|
respond_to do |format|
|
|
format.html { redirect_to(named_context_url(@context, :context_assignments_url, @assignment_group.context_id)) }
|
|
format.json { render :json => @assignment_group.as_json(:permissions => {:user => @current_user, :session => session}) }
|
|
end
|
|
end
|
|
end
|
|
|
|
def create
|
|
@assignment_group = @context.assignment_groups.scoped.new(params[:assignment_group])
|
|
if authorized_action(@assignment_group, @current_user, :create)
|
|
respond_to do |format|
|
|
if @assignment_group.save
|
|
@assignment_group.insert_at(1)
|
|
flash[:notice] = t 'notices.created', 'Assignment Group was successfully created.'
|
|
format.html { redirect_to named_context_url(@context, :context_assignments_url) }
|
|
format.json { render :json => @assignment_group.as_json(:permissions => {:user => @current_user, :session => session}), :status => :created}
|
|
else
|
|
format.json { render :json => @assignment_group.errors, :status => :bad_request }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
@assignment_group = @context.assignment_groups.find(params[:id])
|
|
if authorized_action(@assignment_group, @current_user, :update)
|
|
respond_to do |format|
|
|
if @assignment_group.update_attributes(params[:assignment_group])
|
|
flash[:notice] = t 'notices.updated', 'Assignment Group was successfully updated.'
|
|
format.html { redirect_to named_context_url(@context, :context_assignments_url) }
|
|
format.json { render :json => @assignment_group.as_json(:permissions => {:user => @current_user, :session => session}), :status => :ok }
|
|
else
|
|
format.json { render :json => @assignment_group.errors, :status => :bad_request }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
@assignment_group = AssignmentGroup.find(params[:id])
|
|
if authorized_action(@assignment_group, @current_user, :delete)
|
|
if @assignment_group.has_frozen_assignments?(@current_user)
|
|
@assignment_group.errors.add('workflow_state', t('errors.cannot_delete_group', "You can not delete a group with a locked assignment.", :att_name => 'workflow_state'))
|
|
respond_to do |format|
|
|
format.html { redirect_to named_context_url(@context, :context_assignments_url) }
|
|
format.json { render :json => @assignment_group.errors, :status => :bad_request }
|
|
end
|
|
return
|
|
end
|
|
|
|
if params[:move_assignments_to]
|
|
@assignment_group.move_assignments_to params[:move_assignments_to]
|
|
end
|
|
@assignment_group.destroy
|
|
|
|
respond_to do |format|
|
|
format.html { redirect_to(named_context_url(@context, :context_assignments_url)) }
|
|
format.json { render :json => {
|
|
assignment_group: @assignment_group.as_json(include_root: false, include: :active_assignments),
|
|
new_assignment_group: @new_group.as_json(include_root: false, include: :active_assignments)
|
|
}}
|
|
end
|
|
end
|
|
end
|
|
end
|