1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# encoding: UTF-8
require 'mauve/datamapper'
require 'log4r'
module Mauve
class AlertChanged
include DataMapper::Resource
# so .first always returns the most recent update
default_scope(:default).update(:order => [:at.desc, :id.desc])
property :id, Serial
property :alert_id, Integer, :required => true
property :person, String, :required => true
property :at, DateTime, :required => true
property :was_relevant, Boolean, :required => true, :default => true
property :level, String, :required => true
property :update_type, String, :required => true
property :remind_at, DateTime
property :updated_at, DateTime
def inspect
"#<AlertChanged #{id}: alert_id #{alert_id}, for #{person}, update_type #{update_type}>"
end
alias to_s inspect
belongs_to :alert
# There is a bug there. You could have two reminders for the same
# person if that person has two different notify clauses.
#
# See the test cases test_Bug_reminders_get_trashed() in ./test/
after :create do
old_changed = AlertChanged.first(
:alert_id => alert_id,
:person => person,
:id.not => id,
:remind_at.not => nil
)
if old_changed
if !old_changed.update(:remind_at => nil)
logger.info "Couldn't save #{old_changed}, will get duplicate reminders"
end
end
end
def was_relevant=(value)
attribute_set(:was_relevant, value)
end
def logger
Log4r::Logger.new self.class.to_s
end
## Checks to see if a raise was send to the person.
#
# @TODO: Recurence is broken in ruby, change this so that it does not
# use it.
#
# @author Matthew Bloch
# @return [Boolean] true if it was relevant, false otherwise.
def was_relevant_when_raised?
if "acknowledged" == update_type and true == was_relevant
return true
end
return was_relevant if update_type == "raised"
previous = AlertChanged.first(:id.lt => id,
:alert_id => alert_id,
:person => person)
if previous
previous.was_relevant_when_raised?
else
# a bug, but hardly inconceivable :)
logger.info("Could not see that #{alert} was raised with #{person} "+
"but further updates exist (e.g. #{self}) "+
"- you may see spurious notifications as a result")
true
end
end
# Sends a reminder about this alert state change, or forget about it if
# the alert has been acknowledged
#
def remind
unless alert.is_a?(Alert)
logger.info "#{self.inspect} lost alert #{alert_id}. Killing self."
destroy!
return false
end
alert_group = AlertGroup.matches(alert)[0]
if !alert_group || alert.acknowledged?
logger.info((alert_group ?
"Alert already acknowledged" :
"No alert group matches any more"
) + " => no reminder due for #{self.alert.inspect}"
)
self.remind_at = nil
save
else
logger.info "Sending a reminder for #{self.alert.inspect}"
saved = false
unless alert_group.notifications.nil?
alert_group.notifications.each do |notification|
#
# Build an array of people that could/should be notified.
#
notification_people = []
notification.people.each do |np|
case np
when Person
notification_people << np.username
when PeopleList
notification_people += np.list
end
end
#
# For each person, send a notification
#
notification_people.sort.uniq.each do |np|
if np == self.person
#
# Only remind if the time is right.
#
if DuringRunner.new(Time.now, alert, ¬ification.during).now?
Configuration.current.people[np].send_alert(level, alert)
end
self.remind_at = notification.remind_at_next(alert)
save
saved = true
end
end
end
end
if !saved
logger.warn("#{self.inspect} did not match any people, maybe configuration has changed but I'm going to delete this and not try to remind anyone again")
destroy!
end
end
end
def due_at # mimic interface from Alert
remind_at ? remind_at.to_time : nil
end
def poll # mimic interface from Alert
remind if remind_at.to_time <= Time.now
end
class << self
def next_reminder
first(:remind_at.not => nil, :order => [:remind_at])
end
def find_next_with_event # mimic interface from Alert
next_reminder
end
def all_overdue(at = Time.now)
all(:remind_at.not => nil, :remind_at.lt => at, :order => [:remind_at]).to_a
end
end
end
end
|