summaryrefslogtreecommitdiffstats
path: root/pyfirstaidkit/reporting.py
blob: 9f85ed17b67ad8237bac0edb297be311a99cc5ed (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# First Aid Kit - diagnostic and repair tool for Linux
# Copyright (C) 2007 Martin Sivak <msivak@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import Queue
import logging
import thread
import weakref

Logger = logging.getLogger("firstaidkit")

#semantics values
#first the "task" levels for START and STOP
FIRSTAIDKIT = 100
TASKER = 90
PLUGINSYSTEM = 80
PLUGIN = 70
FLOW = 60
TASK = 50

#semantics
START = 0
STOP = 1
PROGRESS = 2
INFO = 3
ALERT = 4
EXCEPTION = 5
TABLE = 6 #types for arbitrary table-like organized iterables
TREE = 7  #nested iterables organized as tree
ISSUE = 8  #New issue object was created or changed
QUESTION = 999 #type of message which contains respond-to field
END = 1000 #End of operations, final message

class Origin(object):
    """Class which defines mandatory interface for origin,
    when using the reporting system"""
    name = "Origin:Unknown"

    def __init__(self, name):
        self.name = name

class Reports(object):
    """Instances of this class are used as reporting mechanism by which the
    plugins can comminucate back to whatever frontend we are using.

    Message has four parts:
    origin - who sent the message (instance of the plugin, Pluginsystem, ...)
    level - which level of First Aid Kit sent the message (PLUGIN, TASKER, ..)
    action - what action does the message describe
                (INFO, ALERT, PROGRESS, START, STOP, DATA, END)
    importance - how is that message important (debug, info, error, ...)
                 this must be number, possibly the same as in logging module
    message - the message itself
              for INFO and ALERT semantics, this is an arbitrary  text
              for PROGRESS, this is  (x,y) pair denoting progress
                (on step x from y steps) or None to hide the progress
              for START and STOP, there is no mandatory message and the
                importance specifies the level
    reply - the instance of Queue.Queue, which should receive the replies
    title - title of the message
    """

    def __init__(self, maxsize=-1, round = False, parent  = None, name = None):
        """round - is this a round buffer?
        maxsize - size of the buffer"""
        self._queue = Queue.Queue(maxsize = maxsize)
        self._queue_lock = thread.allocate_lock()
        self._round = round
        self._mailboxes = []
        self._notify = []
        self._notify_all = []
        self._parent  = parent
        if not name:
            self._name = "Reporting"
        else:
            self._name = name


    def notify(self, cb, *args, **kwargs):
        """When putting anything new into the Queue, run notifications
        callbacks. Usefull for Gui and single-thread reporting.
        The notification function has parameters: reporting object,
        message recorded to the queue, any parameters provided
        when registering"""
        return self._notify.append((cb, args, kwargs))
    
    def notify_all(self, cb, *args, **kwargs):
        """When putting anything new into the Queue or mailboxes
        belonging to this Reporting object, run notifications
        callbacks. Usefull for logging.
        The notification function has parameters: reporting object,
        message recorded to the queue, any parameters provided
        when registering"""
        return self._notify_all.append((cb, args, kwargs))

    def put(self, message, origin, level, action, importance = logging.INFO,
            reply = None, inreplyto = None, title = "", destination = None):
        """destination hold reference to another Reporting object"""

        if destination is not None:
            return destination.put(message = message, origin = origin, level = level, action = action, importance = importance, reply = reply, title = title, inreplyto = inreplyto)
        
        data = {"level": level, "origin": origin, "action": action,
                "importance": importance, "message": message,
                "reply": reply, "inreplyto": inreplyto, "title": title}

        destination = self._queue
        try:
            self._queue_lock.acquire()
            ret=destination.put(data, block = False)
        except Queue.Full, e:
            if not self._round:
                raise
            #Queue is full and it is a round buffer.. remove the oldest item
            #and use the free space to put the new item
            destination.get()
            ret=destination.put(data)
        finally:
            self._queue_lock.release()

        #call all the notify callbacks
        for func, args, kwargs in self._notify:
            func(self, data, *args, **kwargs)
        
        #call all the notify-all callbacks
        self.notifyAll(self, data)

        return ret

    def get(self, mailbox = None, *args, **kwargs):
        if mailbox is not None:
            return mailbox.get(*args, **kwargs)
        
        try:
            self._queue_lock.acquire()
            ret = self._queue.get(*args, **kwargs)
        finally:
            self._queue_lock.release()

        return ret

    def openMailbox(self, maxsize=-1):
        """Allocate new mailbox for replies"""

        mb = None
        try:
            self._queue_lock.acquire()
            mb = Reports(maxsize = maxsize, parent = self)
            self._mailboxes.append(mb)
        finally:
            self._queue_lock.release()

        return mb

    def removeMailbox(self, mb):
        """Remove mailbox from the mailbox list"""
        try:
            self._queue_lock.acquire()
            self._mailboxes.remove(mb)
            mb._parent = None
        finally:
            self._queue_lock.release()

    def closeMailbox(self):
        """Close mailbox when not needed anymore"""
        self._parent.removeMailbox(self)

    def notifyAll(self, data, sender=None):
        if sender is None:
            sender = self

        #call all the notify-all callbacks
        for func, args, kwargs in self._notify_all:
            func(sender, data, *args, **kwargs)

        if self._parent:
            self._parent.notifyAll(data, sender, *args, **kwargs)

    #There will be helper methods inspired by logging module
    def end(self, level = PLUGIN):
        return self.put(None, level, None, END, importance = 1000)

    def error(self, message, origin, inreplyto = None, level = PLUGIN, action = INFO):
        Logger.error(origin.name+": "+message)
        return self.put(message, origin, level, action,
                importance = logging.ERROR, inreplyto = inreplyto)

    def start(self, origin, inreplyto = None, level = PLUGIN, message = ""):
        return self.put(message, origin, level, START,
                importance = logging.DEBUG, inreplyto = inreplyto)
    def stop(self, origin, inreplyto = None, level = PLUGIN, message = ""):
        return self.put(message, origin, level, STOP,
                importance = logging.DEBUG, inreplyto = inreplyto)

    def progress(self, position, maximum, origin, inreplyto = None, level = PLUGIN,
            importance = logging.INFO):
        return self.put((position, maximum), origin, level, PROGRESS,
                importance = importance, inreplyto = inreplyto)

    def issue(self, issue, origin, inreplyto = None, level = PLUGIN, importance = logging.INFO):
        Logger.debug(origin.name+": issue changed state to "+str(issue))
        return self.put(issue, origin, level, ISSUE, importance = importance, inreplyto = inreplyto)

    def info(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.INFO):
        Logger.info(origin.name+": "+message)
        return self.put(message, origin, level, INFO, importance = importance, inreplyto = inreplyto)

    def debug(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.DEBUG):
        Logger.debug(origin.name+": "+message)
        return self.put(message, origin, level, INFO, importance = importance, inreplyto = inreplyto)

    def tree(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.INFO,
            title = ""):
        return self.put(message, origin, level, TREE, importance = importance,
                title = title, inreplyto = inreplyto)

    def table(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.INFO,
            title = ""):
        return self.put(message, origin, level, TABLE,
                importance = importance, title = title, inreplyto = inreplyto)

    def alert(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.WARNING):
        return self.put(message, origin, level, ALERT, importance = importance, inreplyto = inreplyto)

    def exception(self, message, origin, inreplyto = None, level = PLUGIN, importance = logging.ERROR):
        Logger.error(origin.name+": "+message)
        return self.put(message, origin, level, EXCEPTION,
                importance = importance, inreplyto = inreplyto)