discussions: separate group discussions from group assignments

fixes CNVS-4475

pre-checkout plan:
  * create a course
  * open people->groups
  * create a student group with multiple students
  * open discussions
  * create two topics
  * make one topic a group assignment

test plan:
  * open the topics you created
  * verify that a "group discussion" option replaced "group assignment"
  * verify that your group assignment is a group discussion
  * verify that your group discussion uses the same group set
  * verify that the old behavior of presenting child topics is retained
  * verify that your non-group topic is not a group discussion
  * verify that the "group discussion" flag cannot be changed for topics
      with replies

  * create a new discussion topic
  * verify that "group discussion" is available for ungraded topics
  * verify that changes to the field persist

  * create a new graded group discussion topic
  * post to one of the subtopics as a student in its group
  * open the gradebook as the teacher
  * verify that the student who posted has a submission but the other
      group members do not
  * verify that group members can be graded individually

Change-Id: Iada30628f0abdb7d7df4267d695553baf8b952d3
Reviewed-on: https://gerrit.instructure.com/33253
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Trevor deHaan <tdehaan@instructure.com>
Reviewed-by: Jon Willesen <jonw@instructure.com>
Product-Review: Braden Anderson <banderson@instructure.com>
This commit is contained in:
Braden Anderson 2014-04-10 16:39:45 -06:00
parent b735558ff8
commit 4fafdda484
24 changed files with 269 additions and 81 deletions

View File

@ -183,6 +183,8 @@ define [
return @get( 'group_category_id' ) unless arguments.length > 0
@set 'group_category_id', id
canGroup: -> !@get('has_submitted_submissions')
gradingStandardId: (id) =>
return @get('grading_standard_id') unless arguments.length > 0
@set 'grading_standard_id', id

View File

@ -166,3 +166,9 @@ define [
locked: @get('locked')
@set('position', null)
@updatePartial(data)
groupCategoryId: (id) =>
return @get( 'group_category_id' ) unless arguments.length > 0
@set 'group_category_id', id
canGroup: -> @get('can_group')

View File

@ -42,6 +42,11 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
'change #use_for_grading' : 'toggleAvailabilityOptions'
)
messages:
group_category_section_label: I18n.t('group_discussion_title', 'Group Discussion')
group_category_field_label: I18n.t('this_is_a_group_discussion', 'This is a Group Discussion')
group_locked_message: I18n.t('group_discussion_locked', 'Students have already submitted to this discussion, so group settings cannot be changed.')
@optionProperty 'permissions'
initialize: (options) ->
@ -126,9 +131,12 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
renderGroupCategoryOptions: =>
@groupCategorySelector = new GroupCategorySelector
el: '#group_category_options'
parentModel: @assignment
parentModel: @model
groupCategories: ENV.GROUP_CATEGORIES
nested: true
hideGradeIndividually: true
sectionLabel: @messages.group_category_section_label
fieldLabel: @messages.group_category_field_label
lockedMessage: @messages.group_locked_message
@groupCategorySelector.render()
@ -145,6 +153,8 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
data.title ||= I18n.t 'default_discussion_title', 'No Title'
data.discussion_type = if data.threaded is '1' then 'threaded' else 'side_comment'
data.podcast_has_student_posts = false unless data.podcast_enabled is '1'
unless ENV?.IS_LARGE_ROSTER
data = @groupCategorySelector.filterFormData data
assign_data = data.assignment
delete data.assignment
@ -169,8 +179,6 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
data
updateAssignment: (data) =>
unless ENV?.IS_LARGE_ROSTER
data = @groupCategorySelector.filterFormData data
@dueDateOverrideView.updateOverrides()
defaultDate = @dueDateOverrideView.getDefaultDueDate()
data.lock_at = defaultDate?.get('lock_at') or null

View File

@ -31,6 +31,10 @@ define [
@optionProperty 'parentModel'
@optionProperty 'groupCategories'
@optionProperty 'nested'
@optionProperty 'hideGradeIndividually'
@optionProperty 'sectionLabel'
@optionProperty 'fieldLabel'
@optionProperty 'lockedMessage'
showGroupCategoryCreateDialog: =>
if @$groupCategoryID.val() == 'new'
@ -52,19 +56,24 @@ define [
@showGroupCategoryCreateDialog()
toJSON: =>
frozenAttributes = @parentModel.frozenAttributes()
frozenAttributes = @parentModel.frozenAttributes?() || []
groupCategoryFrozen = _.include frozenAttributes, 'group_category_id'
groupCategoryLocked = @parentModel.attributes.has_submitted_submissions
groupCategoryLocked = !@parentModel.canGroup()
groupCategoryId: @parentModel.groupCategoryId()
groupCategories: @groupCategories
gradeGroupStudentsIndividually: @parentModel.gradeGroupStudentsIndividually()
hideGradeIndividually: @hideGradeIndividually
gradeGroupStudentsIndividually: !@hideGradeIndividually && @parentModel.gradeGroupStudentsIndividually()
groupCategoryLocked: groupCategoryLocked
hasGroupCategoryDisabled: groupCategoryFrozen || groupCategoryLocked
gradeIndividuallyDisabled: groupCategoryFrozen
groupCategoryIdDisabled: groupCategoryFrozen || groupCategoryLocked
sectionLabel: @sectionLabel
fieldLabel: @fieldLabel
lockedMessage: @lockedMessage
nested: @nested
prefix: 'assignment' if @nested

View File

@ -554,8 +554,8 @@ class DiscussionTopicsApiController < ApplicationController
def visible_topics(topic)
# conflate entries from all child topics for groups the user can access
topics = [topic]
if topic.for_group_assignment? && !topic.child_topics.empty?
groups = topic.assignment.group_category.groups.active.select do |group|
if topic.for_group_discussion? && !topic.child_topics.empty?
groups = topic.group_category.groups.active.select do |group|
group.grants_right?(@current_user, session, :read)
end
topic.child_topics.each{ |t| topics << t if groups.include?(t.context) }

View File

@ -197,6 +197,10 @@
# ]
# }
# },
# "group_category_id": {
# "description": "The unique identifier of the group category if the topic is a group discussion, otherwise null.",
# "type": "integer"
# },
# "attachments": {
# "description": "Array of file attachments.",
# "type": "array",
@ -348,6 +352,7 @@ class DiscussionTopicsController < ApplicationController
hash[:ATTRIBUTES] = discussion_topic_api_json(@topic, @context, @current_user, session, override_dates: false)
end
(hash[:ATTRIBUTES] ||= {})[:is_announcement] = @topic.is_announcement
hash[:ATTRIBUTES][:can_group] = @topic.can_group?
handle_assignment_edit_params(hash[:ATTRIBUTES])
if @topic.assignment.present?
@ -395,8 +400,8 @@ class DiscussionTopicsController < ApplicationController
@locked = @topic.locked_for?(@current_user, :check_policies => true, :deep_check_if_needed => true) || @topic.locked?
@unlock_at = @topic.available_from_for(@current_user)
@topic.change_read_state('read', @current_user)
if @topic.for_group_assignment?
@groups = @topic.assignment.group_category.groups.active.select{ |g| g.grants_right?(@current_user, session, :read) }
if @topic.for_group_discussion?
@groups = @topic.group_category.groups.active.select{ |g| g.grants_right?(@current_user, session, :read) }
topics = @topic.child_topics.to_a
topics = topics.select{|t| @groups.include?(t.context) } unless @topic.grants_right?(@current_user, session, :update)
@group_topics = @groups.map do |group|
@ -430,7 +435,7 @@ class DiscussionTopicsController < ApplicationController
},
:PERMISSIONS => {
:CAN_REPLY => @locked ? false : !(@topic.for_group_assignment? || @topic.locked_for?(@current_user)), # Can reply
:CAN_REPLY => @locked ? false : !(@topic.for_group_discussion? || @topic.locked_for?(@current_user)), # Can reply
:CAN_ATTACH => @locked ? false : @topic.grants_right?(@current_user, session, :attach), # Can attach files on replies
:CAN_MANAGE_OWN => @context.user_can_manage_own_discussion_posts?(@current_user), # Can moderate their own topics
:MODERATE => user_can_moderate # Can moderate any topic
@ -528,6 +533,10 @@ class DiscussionTopicsController < ApplicationController
# can pass the id of another topic to have this one show up after the other
# when they are listed.
#
# @argument group_category_id [Optional, Integer]
# If present, the topic will become a group discussion assigned
# to the group.
#
# @example_request
# curl https://<canvas>/api/v1/courses/<course_id>/discussion_topics \
# -F title='my topic' \
@ -648,7 +657,8 @@ class DiscussionTopicsController < ApplicationController
end
API_ALLOWED_TOPIC_FIELDS = %w(title message discussion_type delayed_post_at lock_at podcast_enabled
podcast_has_student_posts require_initial_post is_announcement pinned)
podcast_has_student_posts require_initial_post is_announcement pinned
group_category_id)
def process_discussion_topic(is_new = false)
@errors = {}
discussion_topic_hash = params.slice(*API_ALLOWED_TOPIC_FIELDS)
@ -675,6 +685,7 @@ class DiscussionTopicsController < ApplicationController
process_lock_parameters(discussion_topic_hash)
end
process_published_parameters(discussion_topic_hash)
process_group_parameters(discussion_topic_hash)
process_pin_parameters(discussion_topic_hash)
if @errors.present?
@ -767,6 +778,21 @@ class DiscussionTopicsController < ApplicationController
end
end
def process_group_parameters(discussion_topic_hash)
if params[:assignment] && params[:assignment].has_key?(:group_category_id)
id = params[:assignment].delete(:group_category_id)
discussion_topic_hash[:group_category_id] ||= id
end
return unless params[:group_category_id].to_s != @topic.group_category_id.to_s
if @topic.is_announcement
@errors[:group] = t(:error_group_announcement, "You cannot use grouped discussion on an announcement.")
return
end
if !@topic.can_group?
@errors[:group] = t(:error_group_change, "You cannot change grouping on a discussion with replies.")
end
end
# TODO: upgrade acts_as_list after rails3
# check_scope will probably handle this
def process_pin_parameters(discussion_topic_hash)
@ -828,6 +854,7 @@ class DiscussionTopicsController < ApplicationController
elsif (@assignment = @topic.assignment || @topic.restore_old_assignment || (@topic.assignment = @context.assignments.build)) &&
@assignment.grants_right?(@current_user, session, :update)
params[:assignment][:group_category_id] = nil unless @topic.group_category_id || @assignment.has_submitted_submissions?
update_api_assignment(@assignment, params[:assignment].merge(@topic.attributes.slice('title')))
@assignment.submission_types = 'discussion_topic'
@assignment.saved_by = :discussion_topic

View File

@ -30,7 +30,8 @@ class DiscussionTopic < ActiveRecord::Base
attr_accessible :title, :message, :user, :delayed_post_at, :lock_at, :assignment,
:plaintext_message, :podcast_enabled, :podcast_has_student_posts,
:require_initial_post, :threaded, :discussion_type, :context, :pinned, :locked
:require_initial_post, :threaded, :discussion_type, :context, :pinned, :locked,
:group_category, :group_category_id
module DiscussionTypes
SIDE_COMMENT = 'side_comment'
@ -51,6 +52,7 @@ class DiscussionTopic < ActiveRecord::Base
belongs_to :editor, :class_name => 'User'
belongs_to :old_assignment, :class_name => 'Assignment'
belongs_to :root_topic, :class_name => 'DiscussionTopic'
belongs_to :group_category
has_many :child_topics, :class_name => 'DiscussionTopic', :foreign_key => :root_topic_id, :dependent => :destroy
has_many :discussion_topic_participants, :dependent => :destroy
has_many :discussion_entry_participants, :through => :discussion_entries
@ -117,13 +119,28 @@ class DiscussionTopic < ActiveRecord::Base
if self.assignment_id
self.assignment_id = nil unless (self.assignment && self.assignment.context == self.context) || (self.root_topic && self.root_topic.assignment_id == self.assignment_id)
self.old_assignment_id = self.assignment_id if self.assignment_id
if self.assignment && self.assignment.submission_types == 'discussion_topic' && self.assignment.has_group_category?
self.subtopics_refreshed_at ||= Time.parse("Jan 1 2000")
end
end
if self.has_group_category?
self.subtopics_refreshed_at ||= Time.parse("Jan 1 2000")
end
end
protected :default_values
# TODO: These overrides for assignment's group_category can be removed after the migration.
alias_method :raw_group_category, :group_category
def group_category
return raw_group_category || (self.assignment && self.assignment.group_category)
end
def legacy_group_category_id
return self.group_category.id if self.group_category
end
def has_group_category?
self.group_category.present?
end
def set_schedule_delayed_transitions
@should_schedule_delayed_post = self.delayed_post_at? && self.delayed_post_at_changed?
@should_schedule_lock_at = self.lock_at && self.lock_at_changed?
@ -141,14 +158,14 @@ class DiscussionTopic < ActiveRecord::Base
end
def update_subtopics
if !self.deleted? && self.assignment && self.assignment.submission_types == 'discussion_topic' && self.assignment.has_group_category?
if !self.deleted? && self.has_group_category?
send_later_if_production :refresh_subtopics
end
end
def refresh_subtopics
return if self.deleted?
category = self.assignment.try(:group_category)
category = self.group_category
return unless category && self.root_topic_id.blank?
category.groups.active.each do |group|
group.shard.activate do
@ -158,6 +175,7 @@ class DiscussionTopic < ActiveRecord::Base
topic.message = self.message
topic.title = "#{self.title} - #{group.name}"
topic.assignment_id = self.assignment_id
topic.group_category_id = self.group_category_id
topic.user_id = self.user_id
topic.discussion_type = self.discussion_type
topic.workflow_state = self.workflow_state
@ -214,7 +232,7 @@ class DiscussionTopic < ActiveRecord::Base
def is_announcement; false end
def root_topic?
!self.root_topic_id && self.assignment_id && self.assignment.has_group_category?
!self.root_topic_id && self.has_group_category?
end
# only the root level entries
@ -231,8 +249,8 @@ class DiscussionTopic < ActiveRecord::Base
self.assignment && self.assignment.submission_types =~ /discussion_topic/
end
def for_group_assignment?
self.for_assignment? && self.context == self.assignment.context && self.assignment.has_group_category?
def for_group_discussion?
self.has_group_category? && self.root_topic?
end
def plaintext_message=(val)
@ -552,17 +570,25 @@ class DiscussionTopic < ActiveRecord::Base
def can_unpublish?
if self.assignment
!self.assignment.has_student_submissions?
elsif self.for_group_discussion?
self.child_topics.all? do |child|
child.discussion_subentry_count == 0
end
else
self.discussion_subentry_count == 0
end
end
def can_group?
can_unpublish?
end
def should_send_to_stream
if self.not_available_yet?
false
elsif self.cloned_item_id
false
elsif self.assignment && self.root_topic_id && self.assignment.has_group_category?
elsif self.root_topic_id && self.has_group_category?
false
elsif self.assignment && self.assignment.submission_types == 'discussion_topic' && (!self.assignment.due_at || self.assignment.due_at > 1.week.from_now) # TODO: vdd
false

View File

@ -23,22 +23,6 @@
</div>
</div>
<% if @topic.for_group_assignment? %>
<p>
<%= t :separated_conversation_notice, "Since this is a group assignment, each group has its own conversation for this topic. Here are the ones you have access to:" %>
<ul>
<% @group_topics.each do |group_and_topic| %>
<li>
<a href="<%= context_url(group_and_topic[:group], :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group_and_topic[:group].name %></a>
<%= render :partial => 'new_and_total_badge', :locals => {
:unread_count => group_and_topic[:topic].unread_count(@current_user),
:reply_count => group_and_topic[:topic].discussion_entries.active.size } if group_and_topic[:topic] %>
</li>
<% end %>
</ul>
</p>
<% end %>
<% if @presenter.can_grade? %>
<div class="row-fluid">
<div class="span12 hidden due_date_wrapper">

View File

@ -0,0 +1,15 @@
<div class="discussion-section alert alert-info">
<p>
<%= t :separated_conversation_notice, "Since this is a group discussion, each group has its own conversation for this topic. Here are the ones you have access to:" %>
<ul>
<% @group_topics.each do |group_and_topic| %>
<li>
<a href="<%= context_url(group_and_topic[:group], :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group_and_topic[:group].name %></a>
<%= render :partial => 'new_and_total_badge', :locals => {
:unread_count => group_and_topic[:topic].unread_count(@current_user),
:reply_count => group_and_topic[:topic].discussion_entries.active.size } if group_and_topic[:topic] %>
</li>
<% end %>
</ul>
</p>
</div>

View File

@ -142,6 +142,9 @@
<% if @topic.for_assignment? %>
<%= render :partial => 'assignment_details' %>
<% end %>
<% if @topic.for_group_discussion? %>
<%= render :partial => 'group_discussion' %>
<% end %>
<header class="discussion-section clearfix">
<%= avatar((@topic.user), context_code: @context.asset_string) if @topic.user %>
<div class="discussion-header-content right-of-avatar">

View File

@ -134,6 +134,10 @@
</fieldset>
{{#if isTopic}}
{{#unless isLargeRoster}}
<div id="group_category_options" class="control-group"></div>
{{/unless}}
{{#if contextIsCourse}}
<div id="availability_options" style="{{hiddenIf set_assignment}}">
<fieldset>
@ -185,10 +189,6 @@
<div id="assignment_group_options" class="control-group"></div>
{{#unless isLargeRoster}}
<div id="group_category_options" class="control-group"></div>
{{/unless}}
{{#unless isLargeRoster}}
<div id="peer_review_options" class="control-group"></div>
{{/unless}}

View File

@ -1,5 +1,5 @@
<div class="form-column-left">
{{#t 'group_assignment_header'}}Group Assignment{{/t}}
{{#if sectionLabel}}{{sectionLabel}}{{else}}{{#t 'group_assignment_header'}}Group Assignment{{/t}}{{/if}}
</div>
<div class="form-column-right">
@ -11,12 +11,13 @@
prefix=prefix
checked=groupCategoryId
disabled=hasGroupCategoryDisabled}}
{{#t "is_group_assignment"}}This is a Group Assignment{{/t}}
{{#if fieldLabel}}{{fieldLabel}}{{else}}{{#t "is_group_assignment"}}This is a Group Assignment{{/t}}{{/if}}
</label>
{{!-- Individual grading? --}}
<div class="nested">
<div id="group_category_options" style="{{hiddenUnless groupCategoryId}}">
{{#unless hideGradeIndividually}}
<label class="checkbox" for="assignment_grade_students_individually">
{{checkbox "grade_group_students_individually"
id="assignment_grade_students_individually"
@ -27,6 +28,7 @@
Assign Grades to Each Student Individually
{{/t}}
</label>
{{/unless}}
{{!-- Group selection --}}
<label for="group_category_id">
@ -50,9 +52,11 @@
{{#if groupCategoryLocked}}
<div class="group_category_locked_explanation alert"
style="margin: 15px 20px 0">
{{#t "group_category_locked_explanation"}}Students have already
submitted homework on this assignment, so group settings cannot be
changed.{{/t}}
{{#if lockedMessage}}{{lockedMessage}}{{else}}
{{#t "group_category_locked_explanation"}}Students have already
submitted homework on this assignment, so group settings cannot be
changed.{{/t}}
{{/if}}
</div>
{{/if}}
</div>

View File

@ -3,7 +3,7 @@
<% if @assignment.submission_types == 'discussion_topic' && @assignment.discussion_topic %>
<div style="width: 600px; margin: 10px auto;">
<p>
<% if @assignment.discussion_topic.for_group_assignment? && (group = @assignment.group_students(@submission.user)[0]) %>
<% if @assignment.discussion_topic.for_group_discussion? && (group = @assignment.discussion_topic.group_category.group_for(@user)) %>
<%= t('group_discussion_submission_description',
"The submissions for this assignment are posts in the assignment's discussion for this group. Below are the discussion posts for %{user}, or you can *view the full group discussion*.",
:user => context_user_name(@context, @submission.user),
@ -17,7 +17,7 @@
</p>
<% @entries = @assignment.discussion_topic.discussion_entries.active.for_user(@user) %>
<% if @assignment.has_group_category? %>
<% if @assignment.discussion_topic.for_group_discussion? %>
<% @entries = @assignment.discussion_topic.child_topics.map{|t| t.discussion_entries.active.for_user(@user) }.flatten.sort_by{|e| e.created_at} %>
<% end %>
<% @entries.each do |entry| %>

View File

@ -0,0 +1,13 @@
class AddGroupCategoryIdToDiscussionTopic < ActiveRecord::Migration
tag :predeploy
def self.up
add_column :discussion_topics, :group_category_id, :integer, :limit => 8
add_foreign_key :discussion_topics, :group_categories
end
def self.down
remove_foreign_key :discussion_topics, :group_categories
remove_column :discussion_topics, :group_category_id
end
end

View File

@ -0,0 +1,7 @@
class PopulateGroupCategoryOnDiscussionTopics < ActiveRecord::Migration
tag :postdeploy
def up
DataFixup::PopulateGroupCategoryOnDiscussionTopics.send_later_if_production_enqueue_args(:run, :priority => Delayed::LOW_PRIORITY, :max_attempts => 1)
end
end

View File

@ -105,7 +105,8 @@ module Api::V1::DiscussionTopics
subscribed: topic.subscribed?(user), topic_children: topic.child_topics.pluck(:id),
attachments: attachments, published: topic.published?, can_unpublish: topic.can_unpublish?,
locked: topic.locked?, author: user_display_json(topic.user, topic.context),
html_url: html_url, url: html_url, pinned: !!topic.pinned }
html_url: html_url, url: html_url, pinned: !!topic.pinned,
group_category_id: topic.legacy_group_category_id, can_group: topic.can_group? }
end
# Public: Serialize discussion entries for returning a JSON response. This method,

View File

@ -0,0 +1,7 @@
module DataFixup::PopulateGroupCategoryOnDiscussionTopics
def self.run
Assignment.where('submission_types = ? AND group_category_id IS NOT NULL', 'discussion_topic').find_ids_in_ranges do |min_id, max_id|
DiscussionTopic.where(assignment_id: min_id..max_id).joins(:assignment).update_all('group_category_id = assignments.group_category_id')
end
end
end

View File

@ -1383,6 +1383,8 @@ describe AssignmentsApiController, type: :request do
'attachments' => [],
'permissions' => {'delete' => true, 'attach' => true, 'update' => true},
'discussion_type' => 'side_comment',
'group_category_id' => nil,
'can_group' => true,
}
end

View File

@ -290,7 +290,10 @@ describe DiscussionTopicsController, type: :request do
"locked"=>false,
"locked_for_user"=>false,
"author" => user_display_json(@topic.user, @topic.context).stringify_keys!,
"permissions" => { "delete"=>true, "attach"=>true, "update"=>true }}
"permissions" => { "delete"=>true, "attach"=>true, "update"=>true },
"group_category_id" => nil,
"can_group" => true,
}
end
describe "GET 'index'" do
@ -718,6 +721,21 @@ describe DiscussionTopicsController, type: :request do
@assignment.should be_deleted
end
it "should transfer assignment group category to the discussion" do
group_category = @course.group_categories.create(:name => 'watup')
group = group_category.groups.create!(:name => "group1", :context => @course)
group.add_user(@user)
api_call(:put, "/api/v1/courses/#{@course.id}/discussion_topics/#{@topic.id}",
{ :controller => "discussion_topics", :action => "update", :format => "json", :course_id => @course.to_param, :topic_id => @topic.to_param },
{ :assignment => { :group_category_id => group_category.id } })
@topic.reload
@topic.title.should == "Topic 1"
@topic.group_category.should == group_category
@topic.assignment.should be_present
@topic.assignment.group_category.should be_nil
end
it "should allow pinning a topic" do
api_call(:put, "/api/v1/courses/#{@course.id}/discussion_topics/#{@topic.id}",
{ controller: 'discussion_topics', action: 'update', format: 'json', course_id: @course.to_param, topic_id: @topic.to_param },
@ -892,7 +910,9 @@ describe DiscussionTopicsController, type: :request do
"permissions" => {"delete"=>true, "attach"=>true, "update"=>true},
"locked" => false,
"locked_for_user" => false,
"author" => user_display_json(gtopic.user, gtopic.context).stringify_keys!
"author" => user_display_json(gtopic.user, gtopic.context).stringify_keys!,
"group_category_id" => nil,
"can_group" => true,
}
json.should == expected
end

View File

@ -169,7 +169,7 @@ describe DiscussionTopicsController do
response.should be_success
end
it "should assign groups from the topic's assignment's category if the topic is for a group assignment" do
it "should assign groups from the topic's category if the topic is a group discussion" do
course_with_teacher_logged_in(:active_all => true)
course_topic(:with_assignment => true)
@ -179,8 +179,8 @@ describe DiscussionTopicsController do
@course.groups.create!(:group_category => group_category1)
@course.groups.create!(:group_category => group_category1)
@course.groups.create!(:group_category => group_category2)
@topic.assignment.group_category = group_category1
@topic.assignment.save!
@topic.group_category = group_category1
@topic.save!
get 'show', :course_id => @course.id, :id => @topic.id
assigns[:groups].size.should eql(2)

View File

@ -0,0 +1,51 @@
#
# Copyright (C) 2012 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__) + '/../spec_helper.rb')
describe DataFixup::PopulateGroupCategoryOnDiscussionTopics do
it 'should copy assignment.group_category onto discussion topics' do
# set up data
course(:active_all => true, :name => 'Test course')
group_category = @course.group_categories.create(:name => "category")
@group = @course.groups.create(:name => "group", :group_category => group_category)
assignment1 = course.assignments.build(:submission_types => 'discussion_topic', :title => '')
assignment1.group_category = group_category
assignment1.save
topic1 = @course.discussion_topics.create(:title => "topic 1")
topic1.assignment = assignment1
topic1.save
assignment2 = course.assignments.build(:submission_types => 'discussion_topic', :title => '')
topic2 = @course.discussion_topics.create(:title => "topic 2")
topic2.assignment = assignment2
topic2.save
topic3 = @course.discussion_topics.create(:title => "topic 1")
# run the fix
DataFixup::PopulateGroupCategoryOnDiscussionTopics.run
# verify the results
topic1.reload.group_category.should == group_category
topic1.assignment.reload.group_category.should == group_category
topic2.reload.group_category.should be_nil
topic3.reload.group_category.should be_nil
end
end

View File

@ -163,9 +163,9 @@ describe DiscussionTopic do
it "should not be visible when no delayed_post but assignment unlock date in future" do
@topic.delayed_post_at = nil
group_category = @course.group_categories.create(:name => "category")
@topic.group_category = group_category
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic',
:title => @topic.title,
:group_category => group_category,
:unlock_at => 10.days.from_now,
:lock_at => 30.days.from_now)
@topic.assignment.infer_times
@ -372,16 +372,14 @@ describe DiscussionTopic do
end
context "sub-topics" do
it "should default subtopics_refreshed_at on save if a group assignment" do
it "should default subtopics_refreshed_at on save if a group discussion" do
course_with_student(:active_all => true)
group_category = @course.group_categories.create(:name => "category")
@group = @course.groups.create(:name => "group", :group_category => group_category)
@topic = @course.discussion_topics.create(:title => "topic")
@topic.subtopics_refreshed_at.should be_nil
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => @group.group_category)
@topic.assignment.infer_times
@topic.assignment.saved_by = :discussion_topic
@topic.group_category = group_category
@topic.save
@topic.subtopics_refreshed_at.should_not be_nil
end
@ -405,7 +403,7 @@ describe DiscussionTopic do
end
context "refresh_subtopics" do
it "should be a no-op unless there's an assignment and it has a group_category" do
it "should be a no-op unless it has a group_category" do
course_with_student(:active_all => true)
@topic = @course.discussion_topics.create(:title => "topic")
@topic.refresh_subtopics
@ -453,11 +451,13 @@ describe DiscussionTopic do
course_with_student(:active_all => true)
group_category = @course.group_categories.create(:name => "category")
@parent_topic = @course.discussion_topics.create(:title => "parent topic")
@parent_topic.group_category = group_category
@subtopic = @parent_topic.child_topics.build(:title => "subtopic")
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :group_category => group_category)
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title)
@assignment.infer_times
@assignment.saved_by = :discussion_topic
@subtopic.assignment = @assignment
@subtopic.group_category = group_category
@subtopic.save
@subtopic.should_not be_root_topic
@ -470,7 +470,7 @@ describe DiscussionTopic do
@topic.should_not be_root_topic
end
it "should be false unless the topic's assignment has a group_category" do
it "should be false unless the topic has a group_category" do
# topic has no root topic and has an assignment, but the assignment has no group_category
course_with_student(:active_all => true)
@topic = @course.discussion_topics.create(:title => "topic")
@ -488,7 +488,8 @@ describe DiscussionTopic do
course_with_student(:active_all => true)
group_category = @course.group_categories.create(:name => "category")
@topic = @course.discussion_topics.create(:title => "topic")
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => group_category)
@topic.group_category = group_category
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
@assignment.infer_times
@assignment.saved_by = :discussion_topic
@topic.assignment = @assignment
@ -512,23 +513,22 @@ describe DiscussionTopic do
end
end
context "for_assignment?/for_group_assignment?" do
it "should not be for_assignment?/for_group_assignment? unless it has an assignment" do
context "for_assignment?" do
it "should not be for_assignment? unless it has an assignment" do
course_with_student(:active_all => true)
@topic = @course.discussion_topics.create(:title => "topic")
@topic.should_not be_for_assignment
@topic.should_not be_for_group_assignment
group_category = @course.group_categories.build(:name => "category")
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => group_category)
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
@topic.assignment.infer_times
@topic.assignment.saved_by = :discussion_topic
@topic.save
@topic.should be_for_assignment
@topic.should be_for_group_assignment
end
end
it "should not be for_group_assignment? unless the assignment has a group_category" do
context "for_group_discussion?" do
it "should not be for_group_discussion? unless it has a group_category" do
course_with_student(:active_all => true)
@topic = @course.discussion_topics.build(:title => "topic")
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
@ -536,12 +536,11 @@ describe DiscussionTopic do
@assignment.saved_by = :discussion_topic
@topic.assignment = @assignment
@topic.save
@topic.should be_for_assignment
@topic.should_not be_for_group_assignment
@topic.should_not be_for_group_discussion
@assignment.group_category = @course.group_categories.create(:name => "category")
@assignment.save
@topic.reload.should be_for_group_assignment
@topic.group_category = @course.group_categories.create(:name => "category")
@topic.save
@topic.should be_for_group_discussion
end
end
@ -562,12 +561,15 @@ describe DiscussionTopic do
@topic.should_send_to_stream.should be_true
end
it "should be true for the parent topic only in group discussion assignments, not the subtopics" do
it "should be true for the parent topic only in group discussions, not the subtopics" do
course_with_student(:active_all => true)
group_category = @course.group_categories.create(:name => "category")
@parent_topic = @course.discussion_topics.create(:title => "parent topic")
@parent_topic.group_category = group_category
@parent_topic.save
@subtopic = @parent_topic.child_topics.build(:title => "subtopic")
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :group_category => group_category, :due_at => 1.day.from_now)
@subtopic.group_category = group_category
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :due_at => 1.day.from_now)
@assignment.saved_by = :discussion_topic
@subtopic.assignment = @assignment
@subtopic.save

View File

@ -1113,7 +1113,7 @@ describe "discussions" do
click_option("#assignment_group_category_id", group_cat.name)
expect_new_page_load { f('.form-actions button[type=submit]').click }
topic.reload.assignment.group_category_id.should == group_cat.id
topic.reload.group_category_id.should == group_cat.id
end
it "should allow editing the peer review" do

View File

@ -857,7 +857,8 @@ end
@group2 = course.groups.create!(:name => "group 2", :group_category => group_category)
@topic = course.discussion_topics.build(:title => "topic")
@assignment = course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => @group1.group_category)
@topic.group_category = group_category
@assignment = course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
@assignment.infer_times
@assignment.saved_by = :discussion_topic
@topic.assignment = @assignment