canvas-lms/lib/notification_message_creato...

390 lines
16 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
#
# Copyright (C) 2013 - present 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/>.
#
# This file creates notification messages. I hope that was already known.
# Users have multiple communication_channels and notification preferences at
# different levels. This file accounts for these details.
#
# There are three main types of messages that are created here:
# immediate_message, delayed_messages, and dashboard_messages.
#
class NotificationMessageCreator
include LocaleSelection
attr_accessor :notification, :asset, :to_user_channels, :message_data
# Options can include:
# :to_list - A list of Users, User IDs, and CommunicationChannels to send to
# :data - Options merged with Message options
def initialize(notification, asset, options={})
@notification = notification
@asset = asset
@to_user_channels = user_channels(options[:to_list])
@user_counts = recent_messages_for_users(@to_user_channels.keys)
@message_data = options.delete(:data)
respect notification preference overrides Allows course context notification preference overrides to actually take effect when creating notifications for a user. fixes KNO-402 flag=notification_granular_course_preferences / ---- ---- \ | Test Plan | \ ---- ---- / - Run the following migration bundle exec rake db:migrate:up VERSION=20200420211742 - Create two courses and add a student and a teacher to it - configure a communication channel for the student - As the student create a notification preference override for announcments with an immediate frequency for the first course - This can be done through graphiql using the following mutation ``` mutation MyMutation { __typename updateNotificationPreferences( input: { contextType: Course, communicationChannelId: <communication_channel_id>, courseId: <course_id>, frequency: immediate, notificationCategory: Announcement } ) { course { _id notificationPreferences { channels { _id path pathType notificationPolicies { communicationChannelId frequency notification { category categoryDisplayName name } } notificationPolicyOverrides( contextType: Course, courseId: <course_id> ) { communicationChannelId frequency notification { category categoryDisplayName name } } } } notificationPreferencesEnabled } } } ``` - As the student navigate to /profile/communication and set all your Announcement policies to 'weekly' - As the teacher navigate to the SECOND course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should not exist - In a rails console check that the DelayedMessage was created n = Notification.where(category: 'Announcement').first delayed_messages = DelayedMessage.where( notification_id: n.id, communication_channel_id: <channel_id> ) - The delayed_messages array should contain the notification for the announcment with a 'weekly' frequency - As the teacher navigate to the FIRST course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should exist - In a rails console validate that the DelayedMessage was not created using similar steps as detailed above - Now as the student set your override policy to 'daily' using the same mutation provided above but changing the frequency - navigate to /profile/communication and set all your Announcement policies to 'immediately' - Run the same tests as above but now validate that an immediate message is created for the second course when an announcment is created and no delayed message is created - Also verify that a delayed message with a 'daily' frequency is created for the first course when an announcement is created and no immediate message is created - phew, that was a doozy of a test plan! Change-Id: Idb5e95bf13762472c3fdd7aceef200a17f5cd9a0 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/234804 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: Ahmad Amireh <ahmad@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2020-04-22 02:06:01 +08:00
course_id = @message_data&.dig(:course_id)
root_account_id = @message_data&.dig(:root_account_id)
if course_id && root_account_id
@account = Account.new(id: root_account_id)
@course = Course.new(id: course_id)
end
end
# Public: create (and dispatch, and queue delayed) a message
# for this notification, associated with the given asset, sent to the given recipients
#
# asset - what the message applies to. An assignment, a discussion, etc.
# to_list - a list of who to send the message to. the list can contain Users, User ids, or CommunicationChannels
# options - a hash of extra options to merge with the options used to build the Message
#
# Returns a list of the messages dispatched immediately
def create_message
dashboard_messages = []
immediate_messages = []
delayed_messages = []
@to_user_channels.each do |user, channels|
# asset_applied_to is used for the asset (ie assignment, announcement)
# to filter users out that do not apply to the notification like when a due
# date is different for a specific user when using variable due dates.
next unless (asset = asset_applied_to(user))
user_locale = infer_locale(user: user, context: user_asset_context(asset), ignore_browser_locale: true)
I18n.with_locale(user_locale) do
# the channels in this method are all the users active channels or the
# channels that were provided in the to_list.
#
# If the notification has an immediate_policy, it will create an
# immediate_message or a delayed_message via build_fallback_for.
# otherwise it will create a delayed_message. Any message can create a
# dashboard message in addition to itself.
channels.each do |channel|
channel.set_root_account_ids(persist_changes: true, log: true)
next unless notifications_enabled_for_context?(user, @course)
if immediate_policy?(user, channel)
immediate_messages << build_immediate_message_for(user, channel)
delayed_messages << build_fallback_for(user, channel)
else
delayed_messages << build_delayed_message_for(user, channel)
end
end
dashboard_messages << build_dashboard_message_for(user)
end
end
[delayed_messages, dashboard_messages, immediate_messages].each(&:compact!)
delayed_messages.each(&:save!)
dispatch_immediate_messages(immediate_messages) + dispatch_dashboard_messages(dashboard_messages)
end
private
# Notifications are enabled for a user in a course by default, but can be
# disabled for notifications. The broadcast_policy needs to pass both the
# course_id and the root_account_id to the set_broadcast_policy block for us
# to be able to look up if it should be disabled. root_account_id is used
# right now to look up the feature flag, but it can also be used to set
# root_account_id on the message, or look up policy overrides in the future.
# A user can disable notifications for a course with a notification policy
# override.
respect notification preference overrides Allows course context notification preference overrides to actually take effect when creating notifications for a user. fixes KNO-402 flag=notification_granular_course_preferences / ---- ---- \ | Test Plan | \ ---- ---- / - Run the following migration bundle exec rake db:migrate:up VERSION=20200420211742 - Create two courses and add a student and a teacher to it - configure a communication channel for the student - As the student create a notification preference override for announcments with an immediate frequency for the first course - This can be done through graphiql using the following mutation ``` mutation MyMutation { __typename updateNotificationPreferences( input: { contextType: Course, communicationChannelId: <communication_channel_id>, courseId: <course_id>, frequency: immediate, notificationCategory: Announcement } ) { course { _id notificationPreferences { channels { _id path pathType notificationPolicies { communicationChannelId frequency notification { category categoryDisplayName name } } notificationPolicyOverrides( contextType: Course, courseId: <course_id> ) { communicationChannelId frequency notification { category categoryDisplayName name } } } } notificationPreferencesEnabled } } } ``` - As the student navigate to /profile/communication and set all your Announcement policies to 'weekly' - As the teacher navigate to the SECOND course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should not exist - In a rails console check that the DelayedMessage was created n = Notification.where(category: 'Announcement').first delayed_messages = DelayedMessage.where( notification_id: n.id, communication_channel_id: <channel_id> ) - The delayed_messages array should contain the notification for the announcment with a 'weekly' frequency - As the teacher navigate to the FIRST course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should exist - In a rails console validate that the DelayedMessage was not created using similar steps as detailed above - Now as the student set your override policy to 'daily' using the same mutation provided above but changing the frequency - navigate to /profile/communication and set all your Announcement policies to 'immediately' - Run the same tests as above but now validate that an immediate message is created for the second course when an announcment is created and no delayed message is created - Also verify that a delayed message with a 'daily' frequency is created for the first course when an announcement is created and no immediate message is created - phew, that was a doozy of a test plan! Change-Id: Idb5e95bf13762472c3fdd7aceef200a17f5cd9a0 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/234804 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: Ahmad Amireh <ahmad@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2020-04-22 02:06:01 +08:00
def notifications_enabled_for_context?(user, context)
# if the message is not summarizable?, it is in a context that notifications
# cannot be disabled, so return true before checking.
return true unless @notification.summarizable?
return NotificationPolicyOverride.enabled_for(user, context) if context
true
end
# fallback message is a summary message for email only that we create when we
# have sent too many immediate messages to a user in a day.
# returns delayed_message or nil
def build_fallback_for(user, channel)
# delayed_messages are only sent to email channels.
# some types of notifications are only for immediate.
return unless @notification.summarizable? && channel.path_type == 'email'
# we only send fallback when we did not send an immediate message, ie.
# when the channel is bouncing or there have been too_many_messages
return unless channel.bouncing? || too_many_messages_for?(user)
fallback_policy = nil
NotificationPolicy.unique_constraint_retry do
fallback_policy = channel.notification_policies.by_frequency('daily').where(:notification_id => nil).first
fallback_policy ||= channel.notification_policies.create!(frequency: 'daily')
end
InstStatsd::Statsd.increment("message.fall_back_used", short_stat: 'message.fall_back_used')
build_summary_for(user, fallback_policy)
end
# returns delayed_message or nil
def build_delayed_message_for(user, channel)
# delayed_messages are only sent to email channels.
# some types of notifications are only for immediate.
return unless @notification.summarizable? && channel.path_type == 'email'
policy = effective_policy_for(user, channel)
# if the policy is not daily or weekly, it is either immediate which was
# picked up before in build_immediate_message_for, or it's never.
return unless %w(daily weekly).include?(policy&.frequency)
build_summary_for(user, policy) if policy
end
def build_summary_for(user, policy)
user.shard.activate do
message = user.messages.build(message_options_for(user))
message.parse!('summary')
delayed_message = policy.delayed_messages.build(:notification => @notification,
:frequency => policy.frequency,
remove repeat query in delayed message creation DelayedMessage#set_send_at is called as a before save, and calls both: - self.communication_channel - self.communication_channel.user Meaning that each time a delayed message is saved, it generates a query for a communication channel and a user. One common place delayed messages are created is in the NotificationMessageCreator. at the time they are created the communication channel and user are already in memory, we just need to coax rails into using them. Rails tries to set inverse_of associations automatically whenever possible, but it appears that something (maybe the order argument) on user.communication_channels is preventing that from happening. Once we have inverse associations working properly, we can pass the object instead of the id so everything is preloaded. As noted in the inline comment, this is safe because notification_policy is always loaded from a communication_channel association, whose automatic inverse_of association is working correctly. test plan: - send a delayed notification to many people - it should work, and not over-query communication_channels or users Change-Id: Ia24282c9a24746701227781b4782b22b6c9f70f7 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/231352 Reviewed-by: Cody Cutrer <cody@instructure.com> Reviewed-by: Ethan Vizitei <evizitei@instructure.com> QA-Review: Cody Cutrer <cody@instructure.com> QA-Review: Simon Williams <simon@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com> Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
2020-03-25 05:04:42 +08:00
# policy.communication_channel should
# already be loaded in memory as the
# inverse association of loading the
# policy from the channel. passing the
# object through here lets the delayed
# message use it without having to re-query.
:communication_channel => policy.communication_channel,
:root_account_id => message.context_root_account.try(:id),
:name_of_topic => message.subject,
:link => message.url,
:summary => message.body)
delayed_message.context = @asset
delayed_message.save! if Rails.env.test?
delayed_message
end
end
def build_immediate_message_for(user, channel)
# if we have already created a fallback message, we don't want to make an
# immediate message.
return if @notification.summarizable? && too_many_messages_for?(user) && ['email', 'sms'].include?(channel.path_type)
message_options = message_options_for(user)
message = user.messages.build(message_options.merge(communication_channel: channel, to: channel.path))
message&.parse!
message.workflow_state = 'bounced' if channel.bouncing?
message
end
def dispatch_immediate_messages(messages)
Message.transaction do
# Cancel any that haven't been sent out for the same purpose
cancel_pending_duplicate_messages if Rails.env.production?
messages.each do |message|
message.stage_without_dispatch!
message.save!
end
end
# we filter out bounced messages now that they have been saved.
messages = messages.select(&:staged?)
MessageDispatcher.batch_dispatch(messages)
messages
end
# returns a message or nil
def build_dashboard_message_for(user)
# Dashboard messages are only built if the user has finished registration,
# if a user has never logged in, let's not spam the dashboard for no reason.
return unless @notification.dashboard? && @notification.show_in_feed? && !user.pre_registered?
message = user.messages.build(message_options_for(user).merge(:to => 'dashboard'))
message.parse!
message
end
def dispatch_dashboard_messages(messages)
messages.each do |message|
message.infer_defaults
message.create_stream_items
end
messages
end
def effective_policy_for(user, channel)
# a user can override the notification preference for a context, the context
# needs to be provided in the notification from broadcast_policy, the lowest
# level override is the one that should be respected.
policy = override_policy_for(channel, @message_data&.dig(:course_id), 'Course')
policy ||= override_policy_for(channel, @message_data&.dig(:root_account_id), 'Account')
if !policy && should_use_default_policy?(user, channel)
policy ||= channel.notification_policies.new(notification_id: @notification.id, frequency: @notification.default_frequency(user))
end
policy ||= channel.notification_policies.find { |np| np.notification_id == @notification.id }
policy
end
def should_use_default_policy?(user, channel)
# only use new policies for default channel when there are no other policies for the notification and user.
# If another policy exists then it means the notification preferences page has been visited and null values
# show as never policies in the UI.
default_email?(user, channel) && (user.notification_policies.find { |np| np.notification_id == @notification.id }).nil?
end
def default_email?(user, channel)
user.email_channel == channel
end
def override_policy_for(channel, context_id, context_type)
# NotificationPolicyOverrides are already loaded and this find block is on
# an array and can only have one for a given context and channel.
respect notification preference overrides Allows course context notification preference overrides to actually take effect when creating notifications for a user. fixes KNO-402 flag=notification_granular_course_preferences / ---- ---- \ | Test Plan | \ ---- ---- / - Run the following migration bundle exec rake db:migrate:up VERSION=20200420211742 - Create two courses and add a student and a teacher to it - configure a communication channel for the student - As the student create a notification preference override for announcments with an immediate frequency for the first course - This can be done through graphiql using the following mutation ``` mutation MyMutation { __typename updateNotificationPreferences( input: { contextType: Course, communicationChannelId: <communication_channel_id>, courseId: <course_id>, frequency: immediate, notificationCategory: Announcement } ) { course { _id notificationPreferences { channels { _id path pathType notificationPolicies { communicationChannelId frequency notification { category categoryDisplayName name } } notificationPolicyOverrides( contextType: Course, courseId: <course_id> ) { communicationChannelId frequency notification { category categoryDisplayName name } } } } notificationPreferencesEnabled } } } ``` - As the student navigate to /profile/communication and set all your Announcement policies to 'weekly' - As the teacher navigate to the SECOND course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should not exist - In a rails console check that the DelayedMessage was created n = Notification.where(category: 'Announcement').first delayed_messages = DelayedMessage.where( notification_id: n.id, communication_channel_id: <channel_id> ) - The delayed_messages array should contain the notification for the announcment with a 'weekly' frequency - As the teacher navigate to the FIRST course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should exist - In a rails console validate that the DelayedMessage was not created using similar steps as detailed above - Now as the student set your override policy to 'daily' using the same mutation provided above but changing the frequency - navigate to /profile/communication and set all your Announcement policies to 'immediately' - Run the same tests as above but now validate that an immediate message is created for the second course when an announcment is created and no delayed message is created - Also verify that a delayed message with a 'daily' frequency is created for the first course when an announcement is created and no immediate message is created - phew, that was a doozy of a test plan! Change-Id: Idb5e95bf13762472c3fdd7aceef200a17f5cd9a0 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/234804 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: Ahmad Amireh <ahmad@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2020-04-22 02:06:01 +08:00
if context_id
channel.notification_policy_overrides.find do |np|
np.notification_id == @notification.id &&
np.context_id == context_id &&
np.context_type == context_type
respect notification preference overrides Allows course context notification preference overrides to actually take effect when creating notifications for a user. fixes KNO-402 flag=notification_granular_course_preferences / ---- ---- \ | Test Plan | \ ---- ---- / - Run the following migration bundle exec rake db:migrate:up VERSION=20200420211742 - Create two courses and add a student and a teacher to it - configure a communication channel for the student - As the student create a notification preference override for announcments with an immediate frequency for the first course - This can be done through graphiql using the following mutation ``` mutation MyMutation { __typename updateNotificationPreferences( input: { contextType: Course, communicationChannelId: <communication_channel_id>, courseId: <course_id>, frequency: immediate, notificationCategory: Announcement } ) { course { _id notificationPreferences { channels { _id path pathType notificationPolicies { communicationChannelId frequency notification { category categoryDisplayName name } } notificationPolicyOverrides( contextType: Course, courseId: <course_id> ) { communicationChannelId frequency notification { category categoryDisplayName name } } } } notificationPreferencesEnabled } } } ``` - As the student navigate to /profile/communication and set all your Announcement policies to 'weekly' - As the teacher navigate to the SECOND course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should not exist - In a rails console check that the DelayedMessage was created n = Notification.where(category: 'Announcement').first delayed_messages = DelayedMessage.where( notification_id: n.id, communication_channel_id: <channel_id> ) - The delayed_messages array should contain the notification for the announcment with a 'weekly' frequency - As the teacher navigate to the FIRST course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should exist - In a rails console validate that the DelayedMessage was not created using similar steps as detailed above - Now as the student set your override policy to 'daily' using the same mutation provided above but changing the frequency - navigate to /profile/communication and set all your Announcement policies to 'immediately' - Run the same tests as above but now validate that an immediate message is created for the second course when an announcment is created and no delayed message is created - Also verify that a delayed message with a 'daily' frequency is created for the first course when an announcement is created and no immediate message is created - phew, that was a doozy of a test plan! Change-Id: Idb5e95bf13762472c3fdd7aceef200a17f5cd9a0 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/234804 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: Ahmad Amireh <ahmad@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2020-04-22 02:06:01 +08:00
end
end
end
def user_channels(to_list)
to_user_channels = Hash.new([])
# if this method is given users we preload communication channels and they
# are already loaded so we are using the select :active? to not do another
# query to load them again.
users_from_to_list(to_list).each do |user|
to_user_channels[user] += user.communication_channels.select{ |cc| add_channel?(user, cc) }
end
# if the method gets communication channels, the user is loaded, and this
# allows all the methods in this file to behave the same as if it were users.
communication_channels_from_to_list(to_list).each do |channel|
to_user_channels[channel.user] += [channel]
end
to_user_channels.each_value(&:uniq!)
to_user_channels
end
respect notification preference overrides Allows course context notification preference overrides to actually take effect when creating notifications for a user. fixes KNO-402 flag=notification_granular_course_preferences / ---- ---- \ | Test Plan | \ ---- ---- / - Run the following migration bundle exec rake db:migrate:up VERSION=20200420211742 - Create two courses and add a student and a teacher to it - configure a communication channel for the student - As the student create a notification preference override for announcments with an immediate frequency for the first course - This can be done through graphiql using the following mutation ``` mutation MyMutation { __typename updateNotificationPreferences( input: { contextType: Course, communicationChannelId: <communication_channel_id>, courseId: <course_id>, frequency: immediate, notificationCategory: Announcement } ) { course { _id notificationPreferences { channels { _id path pathType notificationPolicies { communicationChannelId frequency notification { category categoryDisplayName name } } notificationPolicyOverrides( contextType: Course, courseId: <course_id> ) { communicationChannelId frequency notification { category categoryDisplayName name } } } } notificationPreferencesEnabled } } } ``` - As the student navigate to /profile/communication and set all your Announcement policies to 'weekly' - As the teacher navigate to the SECOND course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should not exist - In a rails console check that the DelayedMessage was created n = Notification.where(category: 'Announcement').first delayed_messages = DelayedMessage.where( notification_id: n.id, communication_channel_id: <channel_id> ) - The delayed_messages array should contain the notification for the announcment with a 'weekly' frequency - As the teacher navigate to the FIRST course and create an announcement - Navigate to /users/<student_id>/messages and note that the announcement notification should exist - In a rails console validate that the DelayedMessage was not created using similar steps as detailed above - Now as the student set your override policy to 'daily' using the same mutation provided above but changing the frequency - navigate to /profile/communication and set all your Announcement policies to 'immediately' - Run the same tests as above but now validate that an immediate message is created for the second course when an announcment is created and no delayed message is created - Also verify that a delayed message with a 'daily' frequency is created for the first course when an announcement is created and no immediate message is created - phew, that was a doozy of a test plan! Change-Id: Idb5e95bf13762472c3fdd7aceef200a17f5cd9a0 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/234804 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: Ahmad Amireh <ahmad@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2020-04-22 02:06:01 +08:00
# only send emails to active channels or registration notifications to default users' channel
def add_channel?(user, channel)
channel.active? || (@notification.registration? && default_email?(user, channel))
end
def users_from_to_list(to_list)
to_list = [to_list] unless to_list.is_a? Enumerable
to_users = []
to_users += User.find(to_list.select{ |to| to.is_a? Numeric }.uniq)
to_users += to_list.select{ |to| to.is_a? User }
to_users.uniq!
to_users
end
def communication_channels_from_to_list(to_list)
to_list = [to_list] unless to_list.is_a? Enumerable
to_list.select{ |to| to.is_a? CommunicationChannel }.uniq
end
def asset_applied_to(user)
if asset.respond_to?(:filter_asset_by_recipient)
asset.filter_asset_by_recipient(@notification, user)
else
asset
end
end
def message_options_for(user)
user_asset = asset_applied_to(user)
message_options = {
:subject => @notification.subject,
:notification => @notification,
:notification_name => @notification.name,
:user => user,
:context => user_asset,
}
# can't just merge these because nil values need to be overwritten in a later merge
message_options[:delay_for] = @notification.delay_for if @notification.delay_for
message_options[:data] = @message_data if @message_data
message_options
end
def user_asset_context(user_asset)
if user_asset.is_a?(Context)
user_asset
elsif user_asset.respond_to?(:context)
user_asset.context
end
end
def immediate_policy?(user, channel)
# we want to ignore unconfirmed channels unless the notification is
# registration because that is how the user can confirm the channel
return true if @notification.registration?
# pre_registered users should only get registration emails.
return false if user.pre_registered?
return false if channel.unconfirmed?
return true if @notification.migration?
policy = effective_policy_for(user, channel)
policy&.frequency == 'immediately'
end
def cancel_pending_duplicate_messages
first_start_time = start_time = Setting.get("pending_duplicate_message_window_hours", "6").to_i.hours.ago
final_end_time = Time.now.utc
first_partition = Message.infer_partition_table_name('created_at' => first_start_time)
loop do
end_time = start_time + 7.days
end_time = final_end_time if end_time > final_end_time
scope = Message.
in_partition('created_at' => start_time).
where(:notification_id => @notification).
for(@asset).
by_name(@notification.name).
for_user(@to_user_channels.keys).
cancellable
start_partition = Message.infer_partition_table_name('created_at' => start_time)
end_partition = Message.infer_partition_table_name('created_at' => end_time)
if first_partition == start_partition &&
start_partition == end_partition
Message.infer_partition_table_name('created_at' => end_time)
scope = scope.where(created_at: start_time..end_time)
break_this_loop = true
elsif start_time == first_start_time
scope = scope.where("created_at>=?", start_time)
elsif start_partition == end_partition
scope = scope.where("created_at<=?", end_time)
break_this_loop = true
# else <no conditions; we're addressing the entire partition>
end
scope.update_all(:workflow_state => 'cancelled')
break if break_this_loop
start_time = end_time
end
end
def too_many_messages_for?(user)
if @user_counts[user.id] >= user.max_messages_per_day
InstStatsd::Statsd.increment("message.too_many_messages_for_was_true", short_stat: 'message.too_many_messages_for_was_true')
true
end
end
# Cache the count for number of messages sent to a user/user-with-category,
# it can also be manually re-set to reflect new rows added... this cache
# data can get out of sync if messages are cancelled for being repeats...
# not sure if we care about that...
def recent_messages_for_users(users)
GuardRail.activate(:secondary) do
Hash.new(0).merge(Message.more_recent_than(24.hours.ago).where(user_id: users, to_email: true).group(:user_id).count)
end
end
end