summaryrefslogtreecommitdiffstats
path: root/pyfirstaidkit/reporting.py
blob: 8a19a07b0a39d3b6e0073624f653c39d9599480a (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
# 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):
        """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 = []

    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: message recorded to the queue, any parameters provided when registering"""
        return self._notify.append((cb, args, kwargs))

    def put(self, message, level, origin, action, importance = logging.INFO, reply = None, title = "", destination = None):
        data = {"level": level, "origin": origin, "action": action, "importance": importance, "message": message, "reply": reply, "title": title}

        if destination is not None:
            return destination.put(data)

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

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

        return ret

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

        return ret

    def openMailbox(self, maxsize=-1):
        """Allocate new mailbox for replies"""
        mb = Queue.Queue(maxsize = maxsize)
        self._mailboxes.append(mb)
        return mb

    def closeMailbox(self, mb):
        """Close mailbox when not needed anymore"""
        self._mailboxes.remove(mb)

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

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

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

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

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

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

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

    def alert(self, message, level, origin, importance = logging.WARNING):
        return self.put(message, level, origin, ALERT, importance = importance)
    def exception(self, message, level, origin, importance = logging.ERROR):
        Logger.error(origin.name+": "+message)
        return self.put(message, level, origin, EXCEPTION, importance = importance)