app/models/alert.rb
# == Schema Information
#
# Table name: alerts
#
# id :integer(4) not null, primary key
# title :string(255)
# message :text
# severity :string(255)
# status :string(255)
# acknowledge :boolean(1)
# author_id :integer(4)
# created_at :datetime
# updated_at :datetime
# sensitive :boolean(1)
# delivery_time :integer(4)
# sent_at :datetime
# message_type :string(255)
# program_type :string(255)
# from_organization_id :integer(4)
# from_organization_name :string(255)
# from_organization_oid :string(255)
# identifier :string(255)
# scope :string(255)
# category :string(255)
# program :string(255)
# urgency :string(255)
# certainty :string(255)
# jurisdiction_level :string(255)
# references :string(255)
# from_jurisdiction_id :integer(4)
# original_alert_id :integer(4)
# short_message :string(255) default("")
# message_recording_file_name :string(255)
# message_recording_content_type :string(255)
# message_recording_file_size :integer(4)
# distribution_reference :string(255)
# caller_id :string(255)
# ack_distribution_reference :string(255)
# distribution_id :string(255)
# reference :string(255)
# sender_id :string(255)
# call_down_messages :text
# not_cross_jurisdictional :boolean(1) default(true)
#
class Alert < ActiveRecord::Base
self.inheritance_column = 'alert_type'
serialize :call_down_messages, Hash
belongs_to :author, :class_name => 'User'
has_many :targets, :as => :item, :foreign_key => :item_id, :conditions => proc{"targets.item_type = \'#{self.class.to_s}\'"}, :include => :users
has_many :audiences, :through => :targets, :include => [:roles, :jurisdictions, :users]
has_many :alert_device_types, :foreign_key => :alert_id, :dependent => :delete_all
has_many :alert_attempts, :foreign_key => :alert_id, :dependent => :destroy, :include => [:user, :acknowledged_alert_device_type, :jurisdiction, :organization, :devices]
has_many :deliveries, :through => :alert_attempts
has_many :attempted_users, :through => :alert_attempts, :source => :user, :uniq => true
has_many :acknowledged_users,
:source => :user,
:through => :alert_attempts,
:uniq => true,
:conditions => ["alert_attempts.acknowledged_at IS NOT NULL"]
has_many :unacknowledged_users,
:source => :user,
:through => :alert_attempts,
:uniq => true,
:conditions => ["alert_attempts.acknowledged_at IS NULL"]
has_many :ack_logs, :class_name => 'AlertAckLog'
has_many :recipients, :class_name => "User", :finder_sql => proc{"SELECT users.* FROM users, targets, targets_users WHERE targets.item_id=#{id} AND targets_users.target_id=targets.id AND targets_users.user_id=users.id"}
has_paper_trail :meta => { :item_desc => Proc.new { |x| x.to_s } }
after_create :create_console_alert_device_type
after_create :batch_deliver
scope :acknowledged, :join => :alert_attempts, :conditions => "alert_attempts.acknowledged IS NOT NULL"
scope :devices, {
:select => "DISTINCT devices.type",
:joins => "INNER JOIN alert_attempts ON alerts.id=alert_attempts.alert_id INNER JOIN deliveries ON deliveries.alert_attempt_id=alert_attempts.id INNER JOIN devices ON deliveries.device_id=devices.id",
:conditions => "alerts.id=#{object_id}"
}
scope :has_acknowledge, :conditions => ['acknowledge = ?', true]
def self.default_alert
title = "Example Alert - please click More to see the alert contents"
message = "This is an example of ah alert. You can see the title above and this is the alert body.\n\nThe status lets you know if this is an actual alert or just a test alert."
Alert.new(:title => title, :message => message, :created_at => Time.zone.now)
end
def self.child_classes
Module.constants.map{|constant_name| constant_name.constantize if !(defined? constant_name.constantize.superclass).nil? && constant_name.constantize.superclass == ::Alert}.compact
end
# def superclass
# self.class.superclass
# end
def alert_identifier
id
end
def audiences_attributes=(attrs={})
attrs.each do |key, value|
audiences << Audience.new(value)
end
end
def device_types=(types)
alert_device_types.clear
types.each do |type|
alert_device_types.build :device => type
end unless types.nil?
end
def device_types
alert_device_types.map(&:device)
end
def acknowledgments
alert_attempts.all(:conditions => "acknowledged_at IS NOT NULL")
end
def acknowledged_percent
total = ack_logs.find_by_item_type("total")
if total
total.acknowledged_percent
else
0
end
end
def to_s
(alert_type.nil? || alert_type == "Alert") ? self.title : alert_type + ':' + alert_type.constantize.find(id).to_s
end
def to_xml(options={})
options={} if options.blank?
builder=::Builder::XmlMarkup.new( :indent => 2)
builder.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
builder.TMAPI(:messageId => options[:messageId].blank? ? "#{self.class}-#{self.id}" : options[:messageId]) do |tmapi|
xml_build_author tmapi, options[:Author]
xml_build_behavior tmapi, options[:Behavior]
xml_build_messages tmapi, options[:Messages]
xml_build_ivrtree tmapi, options[:IVRTree]
xml_build_recipients tmapi, options[:Recipients]
end
end
#
# def to_json
#
# end
def batch_deliver &block
recipients.each do |user|
alert_attempts.create!(:user => user).batch_deliver
end
yield if block_given?
::MessageApi.deliver(self)
self.initialize_statistics
end
def initialize_statistics
self.reload
aa_size = alert_attempts(true).size.to_f
types = (alert_device_types.map(&:device) << "Device::ConsoleDevice").uniq
types.each do |type|
ack_logs.create(:item_type => "device", :item => type, :acks => 0, :total => aa_size)
end
ack_logs.create(:item_type => "total", :acks => 0, :total => aa_size)
end
def update_statistics(options)
aa_size = nil
return unless respond_to? :call_down_messages
if options[:response] && options[:response].to_i > 0
response = options[:response]
ack = ack_logs.find_by_item_type_and_item("alert_response", call_down_messages[options[:response]])
ack.update_attribute(:acks, ack[:acks] + 1) unless ack.nil?
end
if options[:device]
ack = ack_logs.find_by_item_type_and_item("device",options[:device])
ack.update_attribute(:acks, ack[:acks] + 1) unless ack.nil?
end
ack = ack_logs.find_by_item_type("total")
ack.update_attribute(:acks, ack[:acks] + 1) unless ack.nil?
end
private
def create_console_alert_device_type
AlertDeviceType.create!(:alert_id => self.id, :device => "Device::ConsoleDevice") unless alert_device_types.map(&:device).include?("Device::ConsoleDevice")
end
def xml_build_author builder, options={}
options={} if options.blank?
unless self.author.blank?
builder.Author(:givenName => self.author.first_name, :surname => self.author.last_name, :display_name => self.author.display_name) do |a|
if options[:override]
options[:override].call(a)
else
a.Contact(:device_type => "E-mail") do |contact|
contact.Value self.author.email
end
options[:supplement].call(a) if options[:supplement]
end
end
end
end
def xml_build_behavior builder, options={}
options={ :Delivery => {:Providers => {} } } if options.blank?
builder.Behavior do |behavior|
if options[:override]
options[:override].call(behavior)
else
if options[:Delivery]
behavior.Delivery do |delivery|
if options[:Delivery][:override]
options[:Delivery][:override].call(delivery)
else
if options[:Delivery][:Providers]
delivery.Providers do |providers|
if options[:Delivery][:Providers][:override]
options[:Delivery][:Providers][:override].call(providers)
else
(self.alert_device_types.map{|device| device.device_type.display_name} || Service::Swn::Message::SUPPORTED_DEVICES.keys).each do |device|
device_options = {:name => "swn", :device => device}
device_options[:ivr] = "alert_responses" if self.acknowledge?
providers.Provider(device_options)
end
options[:Delivery][:Providers][:supplement].call(providers) if options[:Delivery][:Providers][:supplement]
end
end
end
options[:Delivery][:supplement].call(delivery) if options[:Delivery][:supplement]
end
end
end
options[:supplement].call(behavior) if options[:supplement]
end
end
end
def xml_build_messages builder, options={}
options={} if options.blank?
builder.Messages do |messages|
if options[:override]
options[:override].call(messages)
else
messages.Message(:name => "title", :lang => "en/us", :encoding => "utf8", :content_type => "text/plain") do |message|
message.Value self.title
end
messages.Message(:name => "message", :lang => "en/us", :encoding => "utf8", :content_type => "text/plain") do |message|
message.Value self.message
end
options[:supplement].call(messages) if options[:supplement]
end
end
end
def xml_build_ivrtree builder, options={}
options={} if options.blank?
builder.IVRTree do |ivrtree|
if options[:override]
options[:override].call(ivrtree)
elsif self.acknowledge?
ivrtree.IVR(:name => "alert_responses") do |ivr|
ivr.RootNode(:operation => "start") do |rootnode|
sorted_messages = self.call_down_messages.sort {|a, b| a[0]<=>b[0]}
sorted_messages.each do |key, call_down|
rootnode.ContextNode do |node|
node.label key
node.operation "TTS"
node.response call_down
end
end
rootnode.ContextNode do |response_node|
response_node.label "Prompt"
response_node.operation "Prompt"
end
end
end
options[:supplement].call(ivrtree) if options[:supplement]
end
end
end
def xml_build_recipients builder, options={}
options={} if options.blank?
builder.Recipients do |rcpts|
if options[:override]
options[:override].call(rcpts)
else
# Can't use recipients association since find_each doesn't append the LIMIT to it properly
User.find_each(:joins => "INNER JOIN targets_users ON targets_users.user_id=users.id INNER JOIN targets ON targets_users.target_id=targets.id AND targets.item_type='#{self.class.to_s}'", :conditions => ['targets.item_id = ?', self.id]) do |recipient|
rcpts.Recipient(:id => recipient.id, :givenName => recipient.first_name, :surname => recipient.last_name, :display_name => recipient.display_name) do |rcpt|
(recipient.devices.find_all_by_type(self.alert_device_types.map(&:device))).each do |device|
rcpt.Device(:id => device.id, :device_type => device.class.display_name) do |d|
d.URN device.URN
end
end
end
end
options[:supplement].call(rcpts) if options[:supplement]
end
end
end
end