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>
This commit is contained in:
parent
7e2ad3caf0
commit
6edaff4a7d
|
@ -241,11 +241,17 @@ class AssignmentsApiController < ApplicationController
|
|||
return if overrides && !overrides.is_a?(Array)
|
||||
|
||||
# do the updating
|
||||
update_api_assignment(assignment, assignment_params)
|
||||
assignment.transaction do
|
||||
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!
|
||||
batch_update_assignment_overrides(assignment, overrides) if overrides
|
||||
end
|
||||
|
||||
return true
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
return false
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
<%= asset.title %>
|
||||
|
||||
<% if asset.due_at %>
|
||||
<% if asset.multiple_due_dates_apply_to(user) %>
|
||||
<%= t('multiple_due_dates', 'due: Multiple Dates') %>
|
||||
<% elsif asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
<br/><br/>
|
||||
<b><a href="<%= content :link %>"><%= asset.title %></a></b>
|
||||
<br/>
|
||||
<% if asset.due_at %>
|
||||
<% if asset.multiple_due_dates_apply_to(user) %>
|
||||
<%= t('multiple_due_dates', 'due: Multiple Dates') %>
|
||||
<% elsif asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<%= t('new_assignment', 'New assignment for %{course_name}', :course_name => asset.context.name) %>
|
||||
<%= asset.title %>
|
||||
|
||||
<% if asset.due_at %>
|
||||
<% if asset.multiple_due_dates_apply_to(user) %>
|
||||
<%= t('multiple_due_dates', 'due: Multiple Dates') %>
|
||||
<% elsif asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
<% end %>
|
||||
|
||||
<%= t('more_info', 'More info at %{course_name}', :course_name => HostUrl.context_host(asset.context)) %>
|
||||
<%= t('more_info', 'More info at %{course_name}', :course_name => HostUrl.context_host(asset.context)) %>
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
<%= t('assignment_created', 'Assignment Created - %{assignment_name}, %{course_name}', :assignment_name => asset.title, :course_name => asset.context.name) %>
|
||||
<% end %>
|
||||
|
||||
<% if asset.due_at %>
|
||||
<% if asset.multiple_due_dates_apply_to(user) %>
|
||||
<%= t('multiple_due_dates', 'due: Multiple Dates') %>
|
||||
<% elsif asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
http://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/assignments/<%= asset.id %>
|
||||
<% end %>
|
||||
<%= t('assignment_change', 'Canvas Alert - Change: %{assignment_name}, %{course_name}', :assignment_name => asset.title, :course_name => asset.context.name) %>
|
||||
<% if asset.due_at %>
|
||||
<% if asset.multiple_due_dates_apply_to(user) %>
|
||||
<%= t('multiple_due_dates', 'due: Multiple Dates') %>
|
||||
<% elsif asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
<%= t('no_due_date', 'No Due Date') %>
|
||||
<% end %>
|
||||
|
||||
<%= t('more_info_at_url', 'More info at %{web_address}', :web_address => HostUrl.context_host(asset.context)) %>
|
||||
<%= t('more_info_at_url', 'More info at %{web_address}', :web_address => HostUrl.context_host(asset.context)) %>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<% define_content :subject do %>
|
||||
<%= t('assignment_due_date_changed', 'Assignment Due Date Changed: %{assignment_name}, %{course_name}', :assignment_name => asset.title, :course_name => asset.context.name) %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<% if asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.assignment.context) %>/<%= asset.assignment.context.class.to_s.downcase.pluralize %>/<%= asset.assignment.context_id %>/assignments/<%= asset.assignment.id %>
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
<%= t('assignment_due_date_changed', 'Assignment Due Date Changed: %{assignment_name}, %{course_name} (%{override})', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title) %>
|
||||
<% end %>
|
||||
|
||||
<%= t('assignment_due_date_changed_sentence', 'The due date for the assignment, %{assignment_name}, for the course, %{course_name} (%{override}), has changed to:', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title) %>
|
||||
|
||||
<% if asset.due_at %>
|
||||
<%= datetime_string(force_zone(asset.due_at)) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'No Due Date') %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= before_label('click_to_see_assignment', 'Click here to view the assignment') %>
|
||||
<%= content :link %>
|
|
@ -0,0 +1,13 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.assignment.context) %>/<%= asset.assignment.context.class.to_s.downcase.pluralize %>/<%= asset.assignment.context_id %>/assignments/<%= asset.assignment.id %>
|
||||
<% end %>
|
||||
|
||||
<%= t('assignment_due_date_changed_sentence', 'The due date for the assignment *%{assignment_name}* for %{course_name} (%{override}) has changed to:', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title, :wrapper => "<b><a href=\"#{content :link}\">\\1</a></b>") %>
|
||||
|
||||
<b>
|
||||
<% if asset.due_at %>
|
||||
<%= datetime_string(force_zone(asset.due_at)) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'No Due Date') %>
|
||||
<% end %>
|
||||
</b>
|
|
@ -0,0 +1,8 @@
|
|||
<%= t('assignment_due_date_changed', '%{assignment_name}, %{course_name} (%{override}) is now due:', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title) %>
|
||||
<% if asset.due_at %>
|
||||
<%= datetime_string(force_zone(asset.due_at)) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'No Due Date') %>
|
||||
<% end %>
|
||||
|
||||
<%= t('more_info_at_url', 'More info at %{web_address}', :web_address => HostUrl.context_host(asset.assignment.context)) %>
|
|
@ -0,0 +1,13 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.assignment.context) %>/<%= asset.assignment.context.class.to_s.downcase.pluralize %>/<%= asset.assignment.context_id %>/assignments/<%= asset.assignment.id %>
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
<%= t('assignment_due_date_changed', 'Assignment Due Date Changed: %{assignment_name}, %{course_name} (%{override})', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title) %>
|
||||
<% end %>
|
||||
|
||||
<% if asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
<% end %>
|
|
@ -0,0 +1,9 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.assignment.context) %>/<%= asset.assignment.context.class.to_s.downcase.pluralize %>/<%= asset.assignment.context_id %>/assignments/<%= asset.assignment.id %>
|
||||
<% end %>
|
||||
<%= t('assignment_change', 'Canvas Alert - Date Change: %{assignment_name}, %{course_name} (%{override})', :assignment_name => asset.assignment.title, :course_name => asset.assignment.context.name, :override => asset.title) %>
|
||||
<% if asset.due_at %>
|
||||
<%= t('due_at', 'due: %{assignment_due_date_time}', :assignment_due_date_time => datetime_string(force_zone(asset.due_at))) %>
|
||||
<% else %>
|
||||
<%= t('no_due_date', 'due: No Due Date') %>
|
||||
<% end %>
|
|
@ -228,6 +228,21 @@ class Assignment < ActiveRecord::Base
|
|||
:all_day_date => all_day_date }
|
||||
end
|
||||
|
||||
def self.due_date_compare_value(date)
|
||||
# due dates are considered equal if they're the same up to the minute
|
||||
date.to_i / 60
|
||||
end
|
||||
|
||||
def self.due_dates_equal?(date1, date2)
|
||||
due_date_compare_value(date1) == due_date_compare_value(date2)
|
||||
end
|
||||
|
||||
def multiple_due_dates_apply_to(user)
|
||||
as_instructor = self.due_dates_for(user).second
|
||||
as_instructor && as_instructor.map{ |hash|
|
||||
Assignment.due_date_compare_value(hash[:due_at]) }.uniq.size > 1
|
||||
end
|
||||
|
||||
# like due_dates_for, but for unlock_at values instead. for consistency, each
|
||||
# unlock_at is still represented by a hash, even though the "as a student"
|
||||
# value will only have one key.
|
||||
|
@ -515,13 +530,26 @@ class Assignment < ActiveRecord::Base
|
|||
tags_to_update.each { |tag| tag.context_module_action(user, action, points) }
|
||||
end
|
||||
|
||||
# call this to perform notifications on an Assignment that is not being saved
|
||||
# (useful when a batch of overrides associated with a new assignment have been saved)
|
||||
def do_notifications!
|
||||
@broadcasted = false
|
||||
self.prior_version = self.versions.previous(self.current_version.number).try(:model)
|
||||
self.just_created = self.prior_version.nil?
|
||||
broadcast_notifications
|
||||
end
|
||||
|
||||
set_broadcast_policy do |p|
|
||||
p.dispatch :assignment_due_date_changed
|
||||
p.to { participants }
|
||||
p.to {
|
||||
# everyone who is _not_ covered by an assignment override affecting due_at
|
||||
# (the AssignmentOverride records will take care of notifying those users)
|
||||
participants - participants_with_overridden_due_at
|
||||
}
|
||||
p.whenever { |record|
|
||||
!self.suppress_broadcast and
|
||||
record.context.state == :available and record.changed_in_states([:available,:published], :fields => :due_at) and
|
||||
record.prior_version && (record.due_at.to_i.divmod(60)[0]) != (record.prior_version.due_at.to_i.divmod(60)[0]) and
|
||||
record.prior_version && !Assignment.due_dates_equal?(record.due_at, record.prior_version.due_at) and
|
||||
record.created_at < 3.hours.ago
|
||||
}
|
||||
|
||||
|
@ -541,6 +569,9 @@ class Assignment < ActiveRecord::Base
|
|||
!self.suppress_broadcast and
|
||||
record.context.state == :available and record.just_created
|
||||
}
|
||||
p.filter_asset_by_recipient { |record, user|
|
||||
record.overridden_for(user)
|
||||
}
|
||||
|
||||
p.dispatch :assignment_graded
|
||||
p.to { @students_whose_grade_just_changed }
|
||||
|
@ -640,6 +671,12 @@ class Assignment < ActiveRecord::Base
|
|||
self.context.participants
|
||||
end
|
||||
|
||||
def participants_with_overridden_due_at
|
||||
assignment_overrides.active.overriding_due_at.inject([]) do |overridden_users, o|
|
||||
overridden_users.concat(o.applies_to_students)
|
||||
end
|
||||
end
|
||||
|
||||
def infer_state_from_course
|
||||
self.workflow_state = "published" if (self.context.publish_grades_immediately rescue false)
|
||||
if self.assignment_group_id.nil?
|
||||
|
@ -1161,14 +1198,21 @@ class Assignment < ActiveRecord::Base
|
|||
homework.submitted_at = Time.now
|
||||
|
||||
homework.with_versioning(:explicit => true) do
|
||||
group ? homework.save_without_broadcast : homework.save!
|
||||
if group
|
||||
if student == original_student
|
||||
homework.broadcast_group_submission
|
||||
else
|
||||
homework.save_without_broadcasting!
|
||||
end
|
||||
else
|
||||
homework.save!
|
||||
end
|
||||
end
|
||||
homeworks << homework
|
||||
primary_homework = homework if student == original_student
|
||||
end
|
||||
end
|
||||
end
|
||||
primary_homework.broadcast_group_submission if group
|
||||
homeworks.each do |homework|
|
||||
context_module_action(homework.student, :submitted)
|
||||
homework.add_comment({:comment => comment, :author => original_student}) if comment && (group_comment || homework == primary_homework)
|
||||
|
|
|
@ -139,6 +139,7 @@ class AssignmentOverride < ActiveRecord::Base
|
|||
write_attribute(:all_day_date, new_all_day_date)
|
||||
end
|
||||
|
||||
|
||||
def as_hash
|
||||
{ :title => title,
|
||||
:due_at => due_at,
|
||||
|
@ -147,6 +148,54 @@ class AssignmentOverride < ActiveRecord::Base
|
|||
:override => self }
|
||||
end
|
||||
|
||||
def applies_to_students
|
||||
# FIXME: exclude students for whom this override does not apply
|
||||
# because a higher-priority override exists
|
||||
case set_type
|
||||
when 'ADHOC'
|
||||
set
|
||||
when 'CourseSection'
|
||||
set.participating_students
|
||||
when 'Group'
|
||||
set.participants
|
||||
end
|
||||
end
|
||||
|
||||
def applies_to_admins
|
||||
case set_type
|
||||
when 'CourseSection'
|
||||
set.participating_admins
|
||||
else
|
||||
assignment.context.participating_admins
|
||||
end
|
||||
end
|
||||
|
||||
def notify_change?
|
||||
self.assignment and
|
||||
self.assignment.context.state == :available and
|
||||
(self.assignment.workflow_state == 'available' || self.assignment.workflow_state == 'published') and
|
||||
self.assignment.created_at < 3.hours.ago and
|
||||
(!self.prior_version ||
|
||||
self.workflow_state != self.prior_version.workflow_state ||
|
||||
self.due_at_overridden != self.prior_version.due_at_overridden ||
|
||||
self.due_at_overridden && !Assignment.due_dates_equal?(self.due_at, self.prior_version.due_at))
|
||||
end
|
||||
|
||||
has_a_broadcast_policy
|
||||
set_broadcast_policy do |p|
|
||||
p.dispatch :assignment_due_date_changed
|
||||
p.to { applies_to_students }
|
||||
p.whenever { |record| record.notify_change? }
|
||||
p.filter_asset_by_recipient { |record, user|
|
||||
# note that our asset for this message is an Assignment, not an AssignmentOverride
|
||||
record.assignment.overridden_for(user)
|
||||
}
|
||||
|
||||
p.dispatch :assignment_due_date_override_changed
|
||||
p.to { applies_to_admins }
|
||||
p.whenever { |record| record.notify_change? }
|
||||
end
|
||||
|
||||
named_scope :visible_to, lambda{ |admin, course|
|
||||
scopes = []
|
||||
|
||||
|
|
|
@ -838,15 +838,21 @@ class Attachment < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def save_without_broadcasting
|
||||
@skip_broadcasts = true
|
||||
save
|
||||
@skip_broadcasts = false
|
||||
begin
|
||||
@skip_broadcasts = true
|
||||
save
|
||||
ensure
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
end
|
||||
|
||||
def save_without_broadcasting!
|
||||
@skip_broadcasts = true
|
||||
save!
|
||||
@skip_broadcasts = false
|
||||
begin
|
||||
@skip_broadcasts = true
|
||||
save!
|
||||
ensure
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
end
|
||||
|
||||
# called before save
|
||||
|
|
|
@ -54,11 +54,14 @@ class CourseSection < ActiveRecord::Base
|
|||
course.participating_students.scoped(:conditions => ["enrollments.course_section_id = ?", id])
|
||||
end
|
||||
|
||||
def participants
|
||||
participating_students +
|
||||
def participating_admins
|
||||
course.participating_admins.scoped(:conditions => ["enrollments.course_section_id = ? OR NOT COALESCE(enrollments.limit_privileges_to_course_section, ?)", id, false])
|
||||
end
|
||||
|
||||
def participants
|
||||
participating_students + participating_admins
|
||||
end
|
||||
|
||||
def available?
|
||||
course.available?
|
||||
end
|
||||
|
|
|
@ -26,6 +26,7 @@ class Notification < ActiveRecord::Base
|
|||
"Assignment Created",
|
||||
"Assignment Changed",
|
||||
"Assignment Due Date Changed",
|
||||
"Assignment Due Date Override Changed",
|
||||
|
||||
# Submissions / Grading
|
||||
"Assignment Graded",
|
||||
|
@ -182,15 +183,20 @@ class Notification < ActiveRecord::Base
|
|||
user = recipient
|
||||
cc = user.email_channel
|
||||
end
|
||||
|
||||
user_asset = asset.respond_to?(:filter_asset_by_recipient) ?
|
||||
asset.filter_asset_by_recipient(self, user) : asset
|
||||
next unless user_asset
|
||||
|
||||
I18n.locale = infer_locale(:user => user,
|
||||
:context => asset.is_a?(Context) ? asset : asset.try_rescue(:context))
|
||||
:context => user_asset.is_a?(Context) ? user_asset : user_asset.try_rescue(:context))
|
||||
|
||||
# For non-essential messages, check if too many have gone out, and if so
|
||||
# send this message as a daily summary message instead of immediate.
|
||||
should_summarize = user && self.summarizable? && too_many_messages?(user)
|
||||
channels = CommunicationChannel.find_all_for(user, self, cc)
|
||||
fallback_channel = channels.sort_by{|c| c.path_type }.first
|
||||
record_delayed_messages((options || {}).merge(:user => user, :communication_channel => cc, :asset => asset, :fallback_channel => should_summarize ? channels.first : nil))
|
||||
record_delayed_messages((options || {}).merge(:user => user, :communication_channel => cc, :asset => user_asset, :fallback_channel => should_summarize ? channels.first : nil))
|
||||
if should_summarize
|
||||
channels = channels.select{|cc| cc.path_type != 'email' && cc.path_type != 'sms' }
|
||||
end
|
||||
|
@ -212,8 +218,8 @@ class Notification < ActiveRecord::Base
|
|||
message.communication_channel = c if c.is_a?(CommunicationChannel)
|
||||
message.dispatch_at = nil
|
||||
message.user = user
|
||||
message.context = asset
|
||||
message.asset_context = options[:asset_context] || asset.context(user) rescue asset
|
||||
message.context = user_asset
|
||||
message.asset_context = options[:asset_context] || user_asset.context(user) rescue user_asset
|
||||
message.notification_category = self.category
|
||||
message.delay_for = self.delay_for if self.delay_for
|
||||
message.data = data if data
|
||||
|
@ -444,6 +450,7 @@ class Notification < ActiveRecord::Base
|
|||
t 'names.assignment_changed', 'Assignment Changed'
|
||||
t 'names.assignment_created', 'Assignment Created'
|
||||
t 'names.assignment_due_date_changed', 'Assignment Due Date Changed'
|
||||
t 'names.assignment_due_date_override_changed', 'Assignment Due Date Override Changed'
|
||||
t 'names.assignment_graded', 'Assignment Graded'
|
||||
t 'names.assignment_resubmitted', 'Assignment Resubmitted'
|
||||
t 'names.assignment_submitted', 'Assignment Submitted'
|
||||
|
|
|
@ -563,6 +563,10 @@ class Submission < ActiveRecord::Base
|
|||
def <=>(other)
|
||||
self.updated_at <=> other.updated_at
|
||||
end
|
||||
|
||||
def submitted_late?
|
||||
self.assignment.overridden_for(self.user).due_at <= Time.now.localtime
|
||||
end
|
||||
|
||||
# Submission:
|
||||
# Online submission submitted AFTER the due date (notify the teacher) - "Grade Changes"
|
||||
|
@ -573,45 +577,48 @@ class Submission < ActiveRecord::Base
|
|||
p.to { assignment.context.instructors_in_charge_of(user_id) }
|
||||
p.whenever {|record|
|
||||
!record.suppress_broadcast and
|
||||
!record.group_broadcast_submission and
|
||||
record.assignment.context.state == :available and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted)) and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted) || record.prior_version.try(:submitted_at) != record.submitted_at) and
|
||||
record.state == :submitted and
|
||||
record.has_submission? and
|
||||
record.assignment.due_at <= Time.now.localtime
|
||||
record.submitted_late?
|
||||
}
|
||||
|
||||
|
||||
p.dispatch :assignment_submitted
|
||||
p.to { assignment.context.instructors_in_charge_of(user_id) }
|
||||
p.whenever {|record|
|
||||
!record.suppress_broadcast and
|
||||
record.assignment.context.state == :available and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted) || record.prior_version.submitted_at != record.submitted_at) and
|
||||
record.assignment.context.state == :available and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted)) and
|
||||
record.state == :submitted and
|
||||
record.has_submission?
|
||||
record.has_submission? and
|
||||
# don't send a submitted message because we already sent an :assignment_submitted_late message
|
||||
!record.submitted_late?
|
||||
}
|
||||
|
||||
p.dispatch :assignment_resubmitted
|
||||
p.to { assignment.context.instructors_in_charge_of(user_id) }
|
||||
p.whenever {|record|
|
||||
!record.suppress_broadcast and
|
||||
record.assignment.context.state == :available and
|
||||
record.assignment.context.state == :available and
|
||||
record.submitted? and
|
||||
record.prior_version.submitted_at and
|
||||
record.prior_version.submitted_at != record.submitted_at and
|
||||
record.has_submission? and
|
||||
# don't send a resubmitted message because we already sent a :assignment_submitted_late message.
|
||||
record.assignment.due_at > Time.now.localtime
|
||||
!record.submitted_late?
|
||||
}
|
||||
|
||||
p.dispatch :group_assignment_submitted_late
|
||||
p.to { assignment.context.instructors_in_charge_of(user_id) }
|
||||
p.whenever {|record|
|
||||
!record.suppress_broadcast and
|
||||
record.group_submission_broadcast and
|
||||
record.group_broadcast_submission and
|
||||
record.assignment.context.state == :available and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted)) and
|
||||
((record.just_created && record.submitted?) || record.changed_state_to(:submitted) || record.prior_version.try(:submitted_at) != record.submitted_at) and
|
||||
record.state == :submitted and
|
||||
record.assignment.due_at <= Time.now.localtime
|
||||
record.submitted_late?
|
||||
}
|
||||
|
||||
p.dispatch :submission_graded
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class AddAssignmentDueDateOverrideNotifications < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
return unless Shard.current.default?
|
||||
Notification.create!(:name => "Assignment Due Date Override Changed", :category => "Due Date")
|
||||
end
|
||||
|
||||
def self.down
|
||||
return unless Shard.current.default?
|
||||
Notification.find_by_name("Assignment Due Date Override Changed").try(:destroy)
|
||||
end
|
||||
end
|
|
@ -80,13 +80,17 @@ module Api::V1::Assignment
|
|||
|
||||
API_ALLOWED_ASSIGNMENT_FIELDS = %w(name position points_possible grading_type due_at description)
|
||||
|
||||
def update_api_assignment(assignment, assignment_params)
|
||||
def update_api_assignment(assignment, assignment_params, save = true)
|
||||
return nil unless assignment_params.is_a?(Hash)
|
||||
update_params = assignment_params.slice(*API_ALLOWED_ASSIGNMENT_FIELDS)
|
||||
update_params["time_zone_edited"] = Time.zone.name if update_params["due_at"]
|
||||
|
||||
assignment.assignment_group = assignment.context.assignment_groups.find(assignment_params[:assignment_group_id]) if assignment_params[:assignment_group_id]
|
||||
assignment.update_attributes(update_params)
|
||||
if save
|
||||
assignment.update_attributes(update_params)
|
||||
else
|
||||
assignment.attributes = update_params
|
||||
end
|
||||
assignment.infer_due_at
|
||||
# TODO: allow rubric creation
|
||||
|
||||
|
|
|
@ -256,8 +256,26 @@ namespace :db do
|
|||
<%= asset.title %>, <%= asset.context.name %>, is now due:
|
||||
<%= asset.due_at.strftime("%b %d at %I:%M") rescue "No Due Date" %><%= asset.due_at.strftime("%p").downcase rescue "" %>
|
||||
}
|
||||
|
||||
create_notification 'Assignment', 'Course Content', 30*60,
|
||||
|
||||
create_notification 'AssignmentOverride', 'Due Date', 5*60,
|
||||
'http://<%= HostUrl.context_host(asset.assignment.context) %>/<%= asset.assignment.context.class.to_s.downcase.pluralize %>/<%= asset.assignment.context_id %>/assignments/<%= asset.assignment.id %>', %{
|
||||
Assignment Due Date Override Changed
|
||||
|
||||
Assignment Due Date Changed: <%= asset.assignment.title %>, <%= asset.assignment.context.name %> (<%= asset.title %>)
|
||||
|
||||
The due date for the assignment, <%= asset.assignment.title %>, for the course, <%= asset.assignment.context.name %> (<%= asset.title %>) has changed to:
|
||||
|
||||
<%= asset.due_at.strftime("%b %d at %I:%M") rescue "No Due Date" %><%= asset.due_at.strftime("%p").downcase rescue "" %>
|
||||
|
||||
|
||||
Click here to view the assignment:
|
||||
<%= main_link %>
|
||||
}, %{
|
||||
<%= asset.assignment.title %>, <%= asset.assignment.context.name %> (<%= asset.title %>, is now due:
|
||||
<%= asset.due_at.strftime("%b %d at %I:%M") rescue "No Due Date" %><%= asset.due_at.strftime("%p").downcase rescue "" %>
|
||||
}
|
||||
|
||||
create_notification 'Assignment', 'Course Content', 30*60,
|
||||
'http://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/assignments/<%= asset.id %>', %{
|
||||
Assignment Changed
|
||||
|
||||
|
|
|
@ -212,6 +212,35 @@ describe AssignmentsApiController, :type => :integration do
|
|||
@section_override.due_at.to_i.should == @section_due_at.to_i
|
||||
end
|
||||
|
||||
it "should take overrides into account in the assignment-created notification for assignments created with overrides" do
|
||||
course_with_teacher(:active_all => true)
|
||||
student_in_course(:course => @course, :active_enrollment => true)
|
||||
course_with_ta(:course => @course, :active_enrollment => true)
|
||||
|
||||
notification = Notification.create! :name => "Assignment Created"
|
||||
|
||||
@student.register!
|
||||
@student.communication_channels.create(:path => "student@instructure.com").confirm!
|
||||
@student.email_channel.notification_policies.find_or_create_by_notification_id(notification.id).update_attribute(:frequency, 'immediately')
|
||||
|
||||
@ta.register!
|
||||
@ta.communication_channels.create(:path => "ta@instructure.com").confirm!
|
||||
@ta.email_channel.notification_policies.find_or_create_by_notification_id(notification.id).update_attribute(:frequency, 'immediately')
|
||||
|
||||
@override_due_at = Time.parse('2002 Jun 22 12:00:00')
|
||||
|
||||
@user = @teacher
|
||||
api_call(:post, "/api/v1/courses/#{@course.id}/assignments.json",
|
||||
{ :controller => 'assignments_api', :action => 'create', :format => 'json', :course_id => @course.id.to_s },
|
||||
{ :assignment => {
|
||||
'name' => 'some assignment',
|
||||
'assignment_overrides' => {
|
||||
'0' => { 'course_section_id' => [ @course.default_section.id ], 'due_at' => @override_due_at.iso8601 }}}})
|
||||
|
||||
@student.messages.detect{|m| m.notification_id == notification.id}.body.should be_include 'Jun 22'
|
||||
@ta.messages.detect{|m| m.notification_id == notification.id}.body.should be_include 'Multiple Dates'
|
||||
end
|
||||
|
||||
it "should allow updating an assignment via the API" do
|
||||
course_with_teacher(:active_all => true)
|
||||
@start_group = @course.assignment_groups.create!({:name => "start group"})
|
||||
|
|
|
@ -37,3 +37,11 @@ def assignment_valid_attributes
|
|||
:points_possible => "1.5"
|
||||
}
|
||||
end
|
||||
|
||||
def assignment_with_override(opts={})
|
||||
assignment_model(opts)
|
||||
@override = @a.assignment_overrides.build
|
||||
@override.set = @c.default_section
|
||||
@override.save!
|
||||
@override
|
||||
end
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_changed.twitter' do
|
||||
it "should render" do
|
||||
assignment_model(:title => "Quiz 1")
|
||||
@object = @assignment
|
||||
generate_message(:assignment_due_date_changed, :twitter, @object)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_override_changed.email' do
|
||||
it "should render" do
|
||||
assignment_with_override
|
||||
generate_message(:assignment_due_date_override_changed, :email, @override)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_override_changed.facebook' do
|
||||
it "should render" do
|
||||
assignment_with_override
|
||||
generate_message(:assignment_due_date_override_changed, :facebook, @override)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_override_changed.sms' do
|
||||
it "should render" do
|
||||
assignment_with_override
|
||||
generate_message(:assignment_due_date_override_changed, :sms, @override)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_override_changed.summary' do
|
||||
it "should render" do
|
||||
assignment_with_override
|
||||
generate_message(:assignment_due_date_override_changed, :summary, @override)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
||||
|
||||
describe 'assignment_due_date_override_changed.twitter' do
|
||||
it "should render" do
|
||||
assignment_with_override
|
||||
generate_message(:assignment_due_date_override_changed, :twitter, @override)
|
||||
end
|
||||
end
|
|
@ -1862,8 +1862,150 @@ describe Assignment do
|
|||
end
|
||||
end
|
||||
|
||||
context "varied due date notifications" do
|
||||
before do
|
||||
course_with_teacher(:active_all => true)
|
||||
@teacher.communication_channels.create(:path => "teacher@instructure.com").confirm!
|
||||
|
||||
@studentA = user_with_pseudonym(:active_all => true, :name => 'StudentA', :username => 'studentA@instructure.com')
|
||||
@studentA.communication_channels.create(:path => "studentA@instructure.com").confirm!
|
||||
@ta = user_with_pseudonym(:active_all => true, :name => 'TA1', :username => 'ta1@instructure.com')
|
||||
@ta.communication_channels.create(:path => "ta1@instructure.com").confirm!
|
||||
@course.enroll_student(@studentA).update_attribute(:workflow_state, 'active')
|
||||
@course.enroll_user(@ta, 'TaEnrollment', :enrollment_state => 'active', :limit_privileges_to_course_section => true)
|
||||
|
||||
@section2 = @course.course_sections.create!(:name => 'section 2')
|
||||
@studentB = user_with_pseudonym(:active_all => true, :name => 'StudentB', :username => 'studentB@instructure.com')
|
||||
@studentB.communication_channels.create(:path => "studentB@instructure.com").confirm!
|
||||
@ta2 = user_with_pseudonym(:active_all => true, :name => 'TA2', :username => 'ta2@instructure.com')
|
||||
@ta2.communication_channels.create(:path => "ta2@instructure.com").confirm!
|
||||
@section2.enroll_user(@studentB, 'StudentEnrollment', 'active')
|
||||
@course.enroll_user(@ta2, 'TaEnrollment', :section => @section2, :enrollment_state => 'active', :limit_privileges_to_course_section => true)
|
||||
|
||||
Time.zone = 'Alaska'
|
||||
default_due = DateTime.parse("01 Jan 2011 14:00 AKST")
|
||||
section_2_due = DateTime.parse("02 Jan 2011 14:00 AKST")
|
||||
@assignment = @course.assignments.build(:title => "some assignment", :due_at => default_due, :submission_types => ['online_text_entry'])
|
||||
@assignment.save_without_broadcasting!
|
||||
override = @assignment.assignment_overrides.build
|
||||
override.set = @section2
|
||||
override.override_due_at(section_2_due)
|
||||
override.save!
|
||||
end
|
||||
|
||||
context "assignment created" do
|
||||
before do
|
||||
Notification.create(:name => 'Assignment Created')
|
||||
end
|
||||
|
||||
it "should notify of the correct due date for the recipient, or 'multiple'" do
|
||||
@assignment.do_notifications!
|
||||
|
||||
messages_sent = @assignment.messages_sent['Assignment Created']
|
||||
messages_sent.detect{|m|m.user_id == @teacher.id}.body.should be_include "Multiple Dates"
|
||||
messages_sent.detect{|m|m.user_id == @studentA.id}.body.should be_include "Jan 1, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @ta.id}.body.should be_include "Jan 1, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @studentB.id}.body.should be_include "Jan 2, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @ta2.id}.body.should be_include "Multiple Dates"
|
||||
end
|
||||
|
||||
it "should collapse identical instructor due dates" do
|
||||
# change the override to match the default due date
|
||||
override = @assignment.assignment_overrides.first
|
||||
override.override_due_at(@assignment.due_at)
|
||||
override.save!
|
||||
@assignment.do_notifications!
|
||||
|
||||
# when the override matches the default, show the default and not "Multiple"
|
||||
messages_sent = @assignment.messages_sent['Assignment Created']
|
||||
messages_sent.each{|m| m.body.should be_include "Jan 1, 2011"}
|
||||
end
|
||||
end
|
||||
|
||||
context "assignment due date changed" do
|
||||
before do
|
||||
Notification.create(:name => 'Assignment Due Date Changed')
|
||||
Notification.create(:name => 'Assignment Due Date Override Changed')
|
||||
end
|
||||
|
||||
it "should notify appropriate parties when the default due date changes" do
|
||||
@assignment.update_attribute(:created_at, 1.day.ago)
|
||||
|
||||
@assignment.due_at = DateTime.parse("09 Jan 2011 14:00 AKST")
|
||||
@assignment.save!
|
||||
|
||||
messages_sent = @assignment.messages_sent['Assignment Due Date Changed']
|
||||
messages_sent.detect{|m|m.user_id == @teacher.id}.body.should be_include "Jan 9, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @studentA.id}.body.should be_include "Jan 9, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @ta.id}.body.should be_include "Jan 9, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @studentB.id}.should be_nil
|
||||
messages_sent.detect{|m|m.user_id == @ta2.id}.body.should be_include "Jan 9, 2011"
|
||||
end
|
||||
|
||||
it "should notify appropriate parties when an override due date changes" do
|
||||
@assignment.update_attribute(:created_at, 1.day.ago)
|
||||
|
||||
override = @assignment.assignment_overrides.first.reload
|
||||
override.override_due_at(DateTime.parse("11 Jan 2011 11:11 AKST"))
|
||||
override.save!
|
||||
|
||||
messages_sent = override.messages_sent['Assignment Due Date Changed']
|
||||
messages_sent.detect{|m|m.user_id == @studentA.id}.should be_nil
|
||||
messages_sent.detect{|m|m.user_id == @studentB.id}.body.should be_include "Jan 11, 2011"
|
||||
|
||||
messages_sent = override.messages_sent['Assignment Due Date Override Changed']
|
||||
messages_sent.detect{|m|m.user_id == @ta.id}.should be_nil
|
||||
messages_sent.detect{|m|m.user_id == @teacher.id}.body.should be_include "Jan 11, 2011"
|
||||
messages_sent.detect{|m|m.user_id == @ta2.id}.body.should be_include "Jan 11, 2011"
|
||||
end
|
||||
end
|
||||
|
||||
context "assignment submitted late" do
|
||||
before do
|
||||
Notification.create(:name => 'Assignment Submitted')
|
||||
Notification.create(:name => 'Assignment Submitted Late')
|
||||
end
|
||||
|
||||
it "should send a late submission notification iff the submit date is late for the submitter" do
|
||||
fake_submission_time = Time.parse "Jan 01 17:00:00 -0900 2011"
|
||||
Time.stubs(:now).returns(fake_submission_time)
|
||||
subA = @assignment.submit_homework @studentA, :submission_type => "online_text_entry", :body => "ooga"
|
||||
subB = @assignment.submit_homework @studentB, :submission_type => "online_text_entry", :body => "booga"
|
||||
Time.unstub(:now)
|
||||
|
||||
subA.messages_sent["Assignment Submitted Late"].should_not be_nil
|
||||
subB.messages_sent["Assignment Submitted Late"].should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "group assignment submitted late" do
|
||||
before do
|
||||
Notification.create(:name => 'Group Assignment Submitted Late')
|
||||
end
|
||||
|
||||
it "should send a late submission notification iff the submit date is late for the group" do
|
||||
@a = assignment_model(:course => @course, :group_category => "Study Groups", :due_at => Time.parse("Jan 01 17:00:00 -0900 2011"), :submission_types => ["online_text_entry"])
|
||||
@group1 = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
|
||||
@group1.add_user(@studentA)
|
||||
@group2 = @a.context.groups.create!(:name => "Study Group 2", :group_category => @a.group_category)
|
||||
@group2.add_user(@studentB)
|
||||
override = @a.assignment_overrides.new
|
||||
override.set = @group2
|
||||
override.override_due_at(Time.parse("Jan 03 17:00:00 -0900 2011"))
|
||||
override.save!
|
||||
fake_submission_time = Time.parse("Jan 02 17:00:00 -0900 2011")
|
||||
Time.stubs(:now).returns(fake_submission_time)
|
||||
subA = @assignment.submit_homework @studentA, :submission_type => "online_text_entry", :body => "eenie"
|
||||
subB = @assignment.submit_homework @studentB, :submission_type => "online_text_entry", :body => "meenie"
|
||||
Time.unstub(:now)
|
||||
|
||||
subA.messages_sent["Group Assignment Submitted Late"].should_not be_nil
|
||||
subB.messages_sent["Group Assignment Submitted Late"].should be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "group assignment" do
|
||||
it "should submit the homework for all students in the same group" do
|
||||
setup_assignment_with_group
|
||||
|
|
|
@ -131,25 +131,65 @@ describe Submission do
|
|||
end
|
||||
|
||||
context "broadcast policy" do
|
||||
context "Assignment Submitted Late" do
|
||||
it "should create a message when the assignment is turned in late" do
|
||||
context "Submission Notifications" do
|
||||
before do
|
||||
Notification.create(:name => 'Assignment Submitted')
|
||||
Notification.create(:name => 'Assignment Resubmitted')
|
||||
Notification.create(:name => 'Assignment Submitted Late')
|
||||
t = User.create(:name => "some teacher")
|
||||
s = User.create(:name => "late student")
|
||||
@context.enroll_teacher(t)
|
||||
@context.enroll_student(s)
|
||||
# @context.stubs(:teachers).returns([@user])
|
||||
Notification.create(:name => 'Group Assignment Submitted Late')
|
||||
|
||||
@teacher = User.create(:name => "some teacher")
|
||||
@student = User.create(:name => "a student")
|
||||
@context.enroll_teacher(@teacher)
|
||||
@context.enroll_student(@student)
|
||||
end
|
||||
|
||||
it "should send the correct message when an assignment is turned in on-time" do
|
||||
@assignment.workflow_state = "published"
|
||||
@assignment.update_attributes(:due_at => Time.now + 1000)
|
||||
|
||||
submission_spec_model(:user => @student)
|
||||
@submission.messages_sent.keys.should == ['Assignment Submitted']
|
||||
end
|
||||
|
||||
it "should send the correct message when an assignment is turned in late" do
|
||||
@assignment.workflow_state = "published"
|
||||
@assignment.update_attributes(:due_at => Time.now - 1000)
|
||||
# @assignment.stubs(:due_at).returns(Time.now - 100)
|
||||
submission_spec_model(:user => s)
|
||||
|
||||
# @submission.stubs(:validate_enrollment).returns(true)
|
||||
# @submission.save
|
||||
@submission.messages_sent.should be_include('Assignment Submitted Late')
|
||||
|
||||
submission_spec_model(:user => @student)
|
||||
@submission.messages_sent.keys.should == ['Assignment Submitted Late']
|
||||
end
|
||||
|
||||
it "should send the correct message when an assignment is resubmitted on-time" do
|
||||
@assignment.submission_types = ['online_text_entry']
|
||||
@assignment.due_at = Time.now + 1000
|
||||
@assignment.save!
|
||||
|
||||
@assignment.submit_homework(@student, :body => "lol")
|
||||
resubmission = @assignment.submit_homework(@student, :body => "frd")
|
||||
resubmission.messages_sent.keys.should == ['Assignment Resubmitted']
|
||||
end
|
||||
|
||||
it "should send the correct message when an assignment is resubmitted late" do
|
||||
@assignment.submission_types = ['online_text_entry']
|
||||
@assignment.due_at = Time.now - 1000
|
||||
@assignment.save!
|
||||
|
||||
@assignment.submit_homework(@student, :body => "lol")
|
||||
resubmission = @assignment.submit_homework(@student, :body => "frd")
|
||||
resubmission.messages_sent.keys.should == ['Assignment Submitted Late']
|
||||
end
|
||||
|
||||
it "should send the correct message when a group assignment is submitted late" do
|
||||
@a = assignment_model(:course => @context, :group_category => "Study Groups", :due_at => Time.now - 1000, :submission_types => ["online_text_entry"])
|
||||
@group1 = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
|
||||
@group1.add_user(@student)
|
||||
submission = @a.submit_homework @student, :submission_type => "online_text_entry", :body => "blah"
|
||||
|
||||
submission.messages_sent.keys.should == ['Group Assignment Submitted Late']
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "Submission Graded" do
|
||||
it "should create a message when the assignment has been graded and published" do
|
||||
Notification.create(:name => 'Submission Graded')
|
||||
|
|
|
@ -96,13 +96,21 @@ module Instructure #:nodoc:
|
|||
self.current_notification.data = block
|
||||
end
|
||||
|
||||
def filter_asset_by_recipient(&block)
|
||||
self.current_notification.recipient_filter = block
|
||||
end
|
||||
|
||||
def find_policy_for(notification)
|
||||
@notifications.detect{|policy| policy.dispatch == notification.name}
|
||||
end
|
||||
end
|
||||
|
||||
class NotificationPolicy
|
||||
attr_accessor :dispatch, :to, :whenever, :context, :data
|
||||
attr_accessor :dispatch, :to, :whenever, :context, :data, :recipient_filter
|
||||
|
||||
def initialize(dispatch)
|
||||
self.dispatch = dispatch
|
||||
self.recipient_filter = lambda { |record, user| record }
|
||||
end
|
||||
|
||||
# This should be called for an instance. It can only be sent out if the
|
||||
|
@ -283,15 +291,21 @@ module Instructure #:nodoc:
|
|||
attr_accessor :skip_broadcasts
|
||||
|
||||
def save_without_broadcasting
|
||||
@skip_broadcasts = true
|
||||
self.save
|
||||
@skip_broadcasts = false
|
||||
begin
|
||||
@skip_broadcasts = true
|
||||
self.save
|
||||
ensure
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
end
|
||||
|
||||
def save_without_broadcasting!
|
||||
@skip_broadcasts = true
|
||||
self.save!
|
||||
@skip_broadcasts = false
|
||||
begin
|
||||
@skip_broadcasts = true
|
||||
self.save!
|
||||
ensure
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
end
|
||||
|
||||
# The rest of the methods here should just be helper methods to make
|
||||
|
@ -350,6 +364,10 @@ module Instructure #:nodoc:
|
|||
end
|
||||
alias :changed_state_to :changed_state
|
||||
|
||||
def filter_asset_by_recipient(notification, recipient)
|
||||
policy = self.class.broadcast_policy_list.find_policy_for(notification)
|
||||
policy ? policy.recipient_filter.call(self, recipient) : self
|
||||
end
|
||||
|
||||
end # InstanceMethods
|
||||
end # BroadcastPolicy
|
||||
|
|
Loading…
Reference in New Issue