~ubuntu-branches/ubuntu/precise/zeitgeist/precise-proposed

« back to all changes in this revision

Viewing changes to _zeitgeist/engine/extensions/datasource_registry.py

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-06-18 15:58:20 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20100618155820-17r271agf6nl73yc
Tags: 0.4.0-0ubuntu1
* New upstream release for dx team
* debian/rules:
  - add simple-patchsys.mk
  - add autoreconf.mk to take the following patch into account
* debian/patches/01_zeitgeist-hardwire-fts-extension.patch:
  - add temporarly FTS to zg
* debian/control.in:
  - bump Standards-Version to latest
  - build-dep on libgconf2-dev and dh-autoreconf for running autoreconf at
    build time
  - build-dep on raptor-utils

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -.- coding: utf-8 -.-
 
2
 
 
3
# Zeitgeist
 
4
#
 
5
# Copyright © 2010 Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 
6
# Copyright © 2009 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
 
7
#
 
8
# This program is free software: you can redistribute it and/or modify
 
9
# it under the terms of the GNU Lesser General Public License as published by
 
10
# the Free Software Foundation, either version 3 of the License, or
 
11
# (at your option) any later version.
 
12
#
 
13
# This program is distributed in the hope that it will be useful,
 
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
# GNU Lesser General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU Lesser General Public License
 
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
from __future__ import with_statement
 
22
import os
 
23
import cPickle as pickle
 
24
import dbus
 
25
import dbus.service
 
26
import logging
 
27
 
 
28
from zeitgeist.datamodel import get_timestamp_for_now
 
29
from _zeitgeist.engine.datamodel import Event, DataSource as OrigDataSource
 
30
from _zeitgeist.engine.extension import Extension
 
31
from _zeitgeist.engine import constants
 
32
 
 
33
logging.basicConfig(level=logging.DEBUG)
 
34
log = logging.getLogger("zeitgeist.datasource_registry")
 
35
 
 
36
DATA_FILE = os.path.join(constants.DATA_PATH, "datasources.pickle")
 
37
REGISTRY_DBUS_OBJECT_PATH = "/org/gnome/zeitgeist/data_source_registry"
 
38
REGISTRY_DBUS_INTERFACE = "org.gnome.zeitgeist.DataSourceRegistry"
 
39
SIG_FULL_DATASOURCE = "(sssa("+constants.SIG_EVENT+")bxb)"
 
40
 
 
41
class DataSource(OrigDataSource):
 
42
        @classmethod
 
43
        def from_list(cls, l):
 
44
                """
 
45
                Parse a list into a DataSource, overriding the value of Running
 
46
                to always be False.
 
47
                """
 
48
                s = cls(l[cls.UniqueId], l[cls.Name], l[cls.Description],
 
49
                        l[cls.EventTemplates], False, l[cls.LastSeen], l[cls.Enabled])
 
50
                return s
 
51
        
 
52
        def update_from_data_source(self, source):
 
53
                for prop in (self.Name, self.Description, self.EventTemplates,
 
54
                        self.LastSeen, self.Running):
 
55
                        self[prop] = source[prop]
 
56
 
 
57
class DataSourceRegistry(Extension, dbus.service.Object):
 
58
        """
 
59
        The Zeitgeist engine maintains a publicly available list of recognized
 
60
        data-sources (components inserting information into Zeitgeist). An
 
61
        option to disable such data-providers is also provided.
 
62
        
 
63
        The data-source registry of the Zeitgeist engine has DBus object path
 
64
        :const:`/org/gnome/zeitgeist/data_source_registry` under the bus name
 
65
        :const:`org.gnome.zeitgeist.DataSourceRegistry`.
 
66
        """
 
67
        PUBLIC_METHODS = ["register_data_source", "get_data_sources",
 
68
                "set_data_source_enabled"]
 
69
        
 
70
        def __init__ (self, engine):
 
71
        
 
72
                Extension.__init__(self, engine)
 
73
                dbus.service.Object.__init__(self, dbus.SessionBus(),
 
74
                        REGISTRY_DBUS_OBJECT_PATH)
 
75
                
 
76
                if os.path.exists(DATA_FILE):
 
77
                        try:
 
78
                                self._registry = [DataSource.from_list(
 
79
                                        datasource) for datasource in pickle.load(open(DATA_FILE))]
 
80
                                log.debug("Loaded data-source data from %s" % DATA_FILE)
 
81
                        except Exception, e:
 
82
                                log.warn("Failed to load data file %s: %s" % (DATA_FILE, e))
 
83
                                self._registry = []
 
84
                else:
 
85
                        log.debug("No existing data-source data found.")
 
86
                        self._registry = []
 
87
                self._running = {}
 
88
                
 
89
                # Connect to client disconnection signals
 
90
                dbus.SessionBus().add_signal_receiver(self._name_owner_changed,
 
91
                    signal_name="NameOwnerChanged",
 
92
                    dbus_interface=dbus.BUS_DAEMON_IFACE,
 
93
                    arg2="", # only match services with no new owner
 
94
            )
 
95
        
 
96
        def _write_to_disk(self):
 
97
                data = [DataSource.get_plain(datasource) for datasource in self._registry]
 
98
                with open(DATA_FILE, "w") as data_file:
 
99
                        pickle.dump(data, data_file)
 
100
                #log.debug("Data-source registry update written to disk.")
 
101
        
 
102
        def _get_data_source(self, unique_id):
 
103
                for datasource in self._registry:
 
104
                        if datasource[DataSource.UniqueId] == unique_id:
 
105
                                return datasource
 
106
        
 
107
        def insert_event_hook(self, event, sender):
 
108
                for (unique_id, bus_names) in self._running.iteritems():
 
109
                        if sender in bus_names:
 
110
                                datasource = self._get_data_source(unique_id)
 
111
                                # Update LastSeen time
 
112
                                datasource[DataSource.LastSeen] = get_timestamp_for_now()
 
113
                                self._write_to_disk()
 
114
                                # Check whether the data-source is allowed to insert events
 
115
                                if not [DataSource.Enabled]:
 
116
                                        return None
 
117
                return event
 
118
        
 
119
        # PUBLIC
 
120
        def register_data_source(self, unique_id, name, description, templates):
 
121
                source = DataSource(str(unique_id), unicode(name), unicode(description),
 
122
                        map(Event.new_for_struct, templates))
 
123
                for datasource in self._registry:
 
124
                        if datasource == source:
 
125
                                datasource.update_from_data_source(source)
 
126
                                self.DataSourceRegistered(datasource)
 
127
                                return datasource[DataSource.Enabled]
 
128
                self._registry.append(source)
 
129
                self._write_to_disk()
 
130
                self.DataSourceRegistered(source)
 
131
                return True
 
132
        
 
133
        # PUBLIC
 
134
        def get_data_sources(self):
 
135
                return self._registry
 
136
        
 
137
        # PUBLIC
 
138
        def set_data_source_enabled(self, unique_id, enabled):
 
139
                datasource = self._get_data_source(unique_id)
 
140
                if not datasource:
 
141
                        return False
 
142
                if datasource[DataSource.Enabled] != enabled:
 
143
                        datasource[DataSource.Enabled] = enabled
 
144
                        self.DataSourceEnabled(datasource[DataSource.UniqueId], enabled)
 
145
                return True
 
146
        
 
147
        @dbus.service.method(REGISTRY_DBUS_INTERFACE,
 
148
                                                 in_signature="sssa("+constants.SIG_EVENT+")",
 
149
                                                 out_signature="b",
 
150
                                                 sender_keyword="sender")
 
151
        def RegisterDataSource(self, unique_id, name, description, event_templates,
 
152
            sender):
 
153
                """
 
154
                Register a data-source as currently running. If the data-source was
 
155
                already in the database, its metadata (name, description and
 
156
                event_templates) are updated.
 
157
                
 
158
                The optional event_templates is purely informational and serves to
 
159
                let data-source management applications and other data-sources know
 
160
                what sort of information you log.
 
161
                
 
162
                :param unique_id: unique ASCII string identifying the data-source
 
163
                :param name: data-source name (may be translated)
 
164
                :param description: data-source description (may be translated)
 
165
                :param event_templates: list of
 
166
                        :class:`Event <zeitgeist.datamodel.Event>` templates.
 
167
                """
 
168
                if not unique_id in self._running:
 
169
                    self._running[unique_id] = [sender]
 
170
                elif sender not in self._running[unique_id]:
 
171
                    self._running[unique_id].append(sender)
 
172
                return self.register_data_source(unique_id, name, description,
 
173
                    event_templates)
 
174
        
 
175
        @dbus.service.method(REGISTRY_DBUS_INTERFACE,
 
176
                                                 in_signature="",
 
177
                                                 out_signature="a"+SIG_FULL_DATASOURCE)
 
178
        def GetDataSources(self):
 
179
                """
 
180
                Get the list of known data-sources.
 
181
                
 
182
                :returns: A list of
 
183
                        :class:`DataSource <zeitgeist.datamodel.DataSource>` objects.
 
184
                """
 
185
                return self.get_data_sources()
 
186
 
 
187
        @dbus.service.method(REGISTRY_DBUS_INTERFACE,
 
188
                                                 in_signature="sb",)
 
189
        def SetDataSourceEnabled(self, unique_id, enabled):
 
190
                """
 
191
                Get a list of data-sources.
 
192
                
 
193
                :param unique_id: unique string identifying a data-source (it's a good
 
194
                        idea to use a domain name / email address / etc. as part of the
 
195
                        name, to avoid name clashes).
 
196
                :type unique_id: string
 
197
                :param enabled: whether the data-source is to be enabled or disabled
 
198
                :type enabled: Bool
 
199
                :returns: True on success, False if there is no known data-source
 
200
                        matching the given ID.
 
201
                :rtype: Bool
 
202
                """
 
203
                return self.set_data_source_enabled(unique_id, enabled)
 
204
 
 
205
        @dbus.service.signal(REGISTRY_DBUS_INTERFACE,
 
206
                                                signature="sb")
 
207
        def DataSourceEnabled(self, value, enabled):
 
208
                """This signal is emitted whenever a data-source is enabled or
 
209
                disabled.
 
210
                
 
211
                :returns: unique string identifier of a data-source and a bool which
 
212
                        is True if it was enabled False if it was disabled.
 
213
                :rtype: struct containing a string and a bool
 
214
                """
 
215
                return (value, enabled)
 
216
 
 
217
        @dbus.service.signal(REGISTRY_DBUS_INTERFACE,
 
218
                                                signature=SIG_FULL_DATASOURCE)
 
219
        def DataSourceRegistered(self, datasource):
 
220
                """This signal is emitted whenever a data-source registers itself.
 
221
                
 
222
                :returns: the registered data-source
 
223
                :rtype: :class:`DataSource <zeitgeist.datamodel.DataSource>`
 
224
                """
 
225
                return datasource
 
226
 
 
227
        @dbus.service.signal(REGISTRY_DBUS_INTERFACE,
 
228
                                                signature=SIG_FULL_DATASOURCE)
 
229
        def DataSourceDisconnected(self, datasource):
 
230
                """This signal is emitted whenever the last running instance of a
 
231
                data-source disconnects.
 
232
                
 
233
                :returns: the disconnected data-source
 
234
                :rtype: :class:`DataSource <zeitgeist.datamodel.DataSource>`
 
235
                """
 
236
                return datasource
 
237
 
 
238
        def _name_owner_changed(self, owner, old, new):
 
239
                """
 
240
                Cleanup disconnected clients and mark data-sources as not running
 
241
                when no client remains.
 
242
                """
 
243
                uid = [uid for uid, ids in self._running.iteritems() if owner in ids]
 
244
                if not uid:
 
245
                        return
 
246
                uid = uid[0]
 
247
 
 
248
                datasource = self._get_data_source(uid)
 
249
                
 
250
                # Update LastSeen time
 
251
                datasource[DataSource.LastSeen] = get_timestamp_for_now()
 
252
                self._write_to_disk()
 
253
                
 
254
                strid = "%s (%s)" % (uid, datasource[DataSource.Name])
 
255
                log.debug("Client disconnected: %s" % strid)
 
256
                if len(self._running[uid]) == 1:
 
257
                        log.debug("No remaining client running: %s" % strid)
 
258
                        del self._running[uid]
 
259
                        datasource[DataSource.Running] = False
 
260
                        self.DataSourceDisconnected(datasource)
 
261
                else:
 
262
                        self._running[uid].remove(owner)