aboutsummaryrefslogtreecommitdiff
path: root/lib/mauve/alert_changed.rb
blob: ebcbbda99226d2e73c146bea9ff75cd2040c2f8d (plain)
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
# 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 to_s
      "#<AlertChanged:#{id} of #{alert_id} for #{person} update_type #{update_type}>"
    end
    
    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.error "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.to_sym and true == was_relevant
        return true 
      end

      return was_relevant if update_type.to_sym == :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.warn("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.debug "#{self.inspect} lost alert #{alert_id}.  Killing self."
        destroy!
        return false
      end


      logger.debug "Reminding someone about #{self.inspect}"
      
      alert_group = AlertGroup.matches(alert)[0]
      
      if !alert_group || alert.acknowledged?
        logger.debug((alert_group ? 
          "Alert already acknowledged" : 
          "No alert group matches any more"
          ) + ", no reminder due"
        )
        self.remind_at = nil
        save
      else
        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, &notification.during).now?
                  Configuration.current.people[np].remind(alert, level)
                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 <= MauveTime.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 = MauveTime.now)
        all(:remind_at.not => nil, :remind_at.lt => at, :order => [:remind_at]).to_a
      end
    end
  end
end