~jconti/recent-notifications/trunk

« back to all changes in this revision

Viewing changes to Notifications.py

  • Committer: Jason Conti
  • Date: 2010-02-18 00:04:49 UTC
  • Revision ID: jason.conti@gmail.com-20100218000449-o1qon3657afk5ghn
Updated Notifications.py to use the new LogWatcher class. Currently still uses the listeners to notify when new messages arrive, should probably update to use glib signals.

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("%A, %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