~jconti/recent-notifications/trunk

« back to all changes in this revision

Viewing changes to Notifications.py

  • Committer: Jason Conti
  • Date: 2010-02-20 13:55:51 UTC
  • Revision ID: jason.conti@gmail.com-20100220135551-lwjf8ef1vdoccr91
Moved LogWatcher and Notifications out of the main project because they are no longer needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Notifications.py
3
 
by Jason Conti
4
 
February 15, 2010
5
 
 
6
 
Reads notifications in the notify-osd.log format specified at:
7
 
  https://wiki.ubuntu.com/NotifyOSD#Logging notifications
8
 
"""
9
 
 
10
 
import re
11
 
import time
12
 
 
13
 
from LogWatcher import LogWatcher
14
 
 
15
 
message_header = re.compile("""
16
 
    ^
17
 
    \[                            # [timestamp, process-name, status?]
18
 
    (
19
 
    \d{4}-\d{2}-\d{2}             # date YYYY-mm-dd
20
 
    [Tt]
21
 
    \d{2}:\d{2}:\d{2}             # time HH-MM-SS
22
 
    ([Zz]|[+-]\d{2}:\d{2})        # time offset
23
 
    )
24
 
    ,
25
 
    ([^,\]]+)                     # process name
26
 
    (,([^\]]+))?                  # optional status
27
 
    \]
28
 
    (.*)                          # title
29
 
    $
30
 
    """, re.VERBOSE)
31
 
 
32
 
class Message(object):
33
 
  """A notification message"""
34
 
  def __init__(self, timestamp, process_name, status, title):
35
 
    self.timestamp = timestamp
36
 
    self.process_name = process_name
37
 
    self.status = status
38
 
    self.title = title
39
 
    self.body = ""
40
 
 
41
 
  def append_body(self, body):
42
 
    """Appends a body line to the message."""
43
 
    self.body += body
44
 
 
45
 
  def formatted_time(self):
46
 
    """Returned the time in a different format."""
47
 
    t = time.strptime(self.timestamp.upper(), "%Y-%m-%dT%H:%M:%S-00:00")
48
 
    return time.strftime("%B %d, %Y at %I:%M:%S %p", t)
49
 
 
50
 
class Notifications(object):
51
 
  """Reads notifications from a log file and sends them to the listeners."""
52
 
  def __init__(self, path):
53
 
    self._path = path
54
 
    self._listeners = set()
55
 
 
56
 
  def add_listener(self, f):
57
 
    """A listener is a function of a single argument, the messages 
58
 
    received."""
59
 
    self._listeners.add(f)
60
 
 
61
 
  def remove_listener(self, f):
62
 
    """Removes f from the set of listeners."""
63
 
    self._listeners.remove(f)
64
 
 
65
 
  def start(self):
66
 
    """Reads the previous messages, sends them to the listeners and starts
67
 
    watching for more messages."""
68
 
    # Get the previous messages
69
 
    f = open(self._path, "r")
70
 
    messages = f.read()
71
 
    f.close()
72
 
    self._message_received(None, messages)
73
 
 
74
 
    self._watcher = LogWatcher(self._path)
75
 
    self._watcher.connect("changed", self._message_received)
76
 
 
77
 
    self._watcher.start()
78
 
 
79
 
  def stop(self):
80
 
    """Stops watching for more messages."""
81
 
    self._watcher.stop()
82
 
 
83
 
  def _notify_listeners(self, messages):
84
 
    """Notifies the listeners of new messages."""
85
 
    for f in self._listeners:
86
 
      f(messages)
87
 
 
88
 
  def _message_received(self, watcher, messages):
89
 
    """Callback that parses messages and adds them to the queue."""
90
 
    queue = self._parse_messages(messages)
91
 
    if len(queue) > 0:
92
 
      self._notify_listeners(queue)
93
 
 
94
 
  def _parse_messages(self, messages):
95
 
    """Returns a list of parsed messages."""
96
 
    lines = messages.splitlines()
97
 
    queue = []
98
 
    message = None
99
 
 
100
 
    for line in lines:
101
 
      # Try to parse a new message, discard the line if the header
102
 
      # doesn't match
103
 
      if message == None:
104
 
        m = message_header.match(line)
105
 
        if m != None:
106
 
          header = m.groups()
107
 
          timestamp = header[0].strip()
108
 
          process_name = header[2].strip()
109
 
          status = header[4]
110
 
          # Status may not exist
111
 
          if status == None:
112
 
            status = ""
113
 
          else:
114
 
            status = status.strip()
115
 
          title = header[5].strip()
116
 
          message = Message(timestamp, process_name, status, title)
117
 
      # Messages are delimited by a blank line
118
 
      else:
119
 
        if line.strip() == "":
120
 
          queue.append(message)
121
 
          message = None
122
 
        else:
123
 
          message.append_body(line)
124
 
 
125
 
    # Don't forget the last message (should probably never happen)
126
 
    if message != None:
127
 
      queue.append(message)
128
 
 
129
 
    return queue
130