Esempio n. 1
0
 def __init__(self, start_dbus=True, mainloop=None):
     SingletonApplication.__init__(self)
     self._mainloop = mainloop
     self._engine = get_engine()
     self._notifications = MonitorManager()
Esempio n. 2
0
class RemoteInterface(SingletonApplication):
    """
	Primary interface to the Zeitgeist engine. Used to update and query
	the log. It also provides means to listen for events matching certain
	criteria. All querying is heavily based around an
	"event template"-concept.
	
	The main log of the Zeitgeist engine has DBus object path
	:const:`/org/gnome/zeitgeist/log/activity` under the bus name
	:const:`org.gnome.zeitgeist.Engine`.
	"""

    _dbus_properties = {
        "version": DBUSProperty(lambda self: (0, 8, 2), out_signature="iii"),
        "extensions": DBUSProperty(
            lambda self: dbus.Array(self._engine.extensions.iter_names(), "s"), out_signature="as"
        ),
    }

    # Initialization

    def __init__(self, start_dbus=True, mainloop=None):
        SingletonApplication.__init__(self)
        self._mainloop = mainloop
        self._engine = get_engine()
        self._notifications = MonitorManager()

        # Private methods

    def _make_events_sendable(self, events):
        for event in events:
            if event is not None:
                event._make_dbus_sendable()
        return tuple(NULL_EVENT if event is None else event for event in events)

        # Reading stuff

    @dbus.service.method(
        constants.DBUS_INTERFACE,
        in_signature="au",
        out_signature="a(" + constants.SIG_EVENT + ")",
        sender_keyword="sender",
    )
    def GetEvents(self, event_ids, sender):
        """Get full event data for a set of event IDs
		
		Each event which is not found in the event log is represented
		by the `NULL_EVENT` struct in the resulting array.
		
		:param event_ids: An array of event IDs. Fx. obtained by calling
			:meth:`FindEventIds`
		:type event_ids: Array of unsigned 32 bit integers.
			DBus signature au
		:returns: Full event data for all the requested IDs. The
		   event data can be conveniently converted into a list of
		   :class:`Event` instances by calling *events = map(Event.new_for_struct, result)*
		:rtype: A list of serialized events. DBus signature a(asaasay).
		"""
        return self._make_events_sendable(self._engine.get_events(ids=event_ids, sender=sender))

    @dbus.service.method(
        constants.DBUS_INTERFACE,
        in_signature="(xx)a(" + constants.SIG_EVENT + ")a(" + constants.SIG_EVENT + ")uuu",
        out_signature="as",
    )
    def FindRelatedUris(
        self, time_range, event_templates, result_event_templates, storage_state, num_events, result_type
    ):
        """Warning: This API is EXPERIMENTAL and is not fully supported yet.
		
		Get a list of URIs of subjects which frequently occur together
		with events matching `event_templates` within `time_range`.
		The resulting URIs must occur as subjects of events matching
		`result_event_templates` and have storage state
		`storage_state`.
		
		:param time_range: two timestamps defining the timerange for
			the query. When using the Python bindings for Zeitgeist you
			may pass a :class:`TimeRange <zeitgeist.datamodel.TimeRange>`
			instance directly to this method.
		:type time_range: tuple of 64 bit integers,
			DBus signature :const:`(xx)`
		:param event_templates: An array of event templates
			which you want URIs that relate to.
			When using the Python bindings for Zeitgeist you may pass
			a list of  :class:`Event <zeitgeist.datamodel.Event>`
			instances directly to this method.
		:type event_templates: array of events,
			DBus signature :const:`a(asaasay)`
		:param result_event_templates: An array of event templates which
			the returned URIs must occur as subjects of.
			When using the Python bindings for Zeitgeist you may pass
			a list of  :class:`Event <zeitgeist.datamodel.Event>`
			instances directly to this method.
		:type result_event_templates: array of events,
			DBus signature :const:`a(asaasay)`
		:param storage_state: whether the item is currently known to be
		   available. The list of possible values is enumerated in the
		   :class:`StorageState <zeitgeist.datamodel.StorageState>` class
		:type storage_state: unsigned 32 bit integer, DBus signature :const:`u`
		:param num_events: maximal amount of returned events
		:type num_events: unsigned integer
		:param result_type: unsigned integer 0 for relevancy 1 for recency
		:type order: unsigned integer
		:returns: A list of URIs matching the described criteria
		:rtype: An array of strings, DBus signature :const:`as`.
		"""
        event_templates = map(Event, event_templates)
        return self._engine.find_related_uris(
            time_range, event_templates, result_event_templates, storage_state, num_events, result_type
        )

    @dbus.service.method(
        constants.DBUS_INTERFACE, in_signature="(xx)a(" + constants.SIG_EVENT + ")uuu", out_signature="au"
    )
    def FindEventIds(self, time_range, event_templates, storage_state, num_events, result_type):
        """Search for events matching a given set of templates and return
		the IDs of matching events.
		
		Use :meth:`GetEvents` passing in the returned IDs to look up
		the full event data.
		
		The matching is done where unset fields in the templates
		are treated as wildcards. If a template has more than one
		subject then events will match the template if any one of their
		subjects match any one of the subject templates.
		
		The fields uri, interpretation, manifestation, origin, and mimetype
		can be prepended with an exclamation mark '!' in order to negate
		the matching.
		
		The fields uri, origin, and mimetype can be prepended with an
		asterisk '*' in order to do truncated matching.
		
		This method is intended for queries potentially returning a
		large result set. It is especially useful in cases where only
		a portion of the results are to be displayed at the same time
		(eg., by using paging or dynamic scrollbars), as by holding a
		list of IDs you keep a stable ordering and you can ask for the
		details associated to them in batches, when you need them. For queries
		yielding a small amount of results, or where you need the information
		about all results at once no matter how many of them there are,
		see :meth:`FindEvents`.
		
		:param time_range: two timestamps defining the timerange for
			the query. When using the Python bindings for Zeitgeist you
			may pass a :class:`TimeRange <zeitgeist.datamodel.TimeRange>`
			instance directly to this method
		:type time_range: tuple of 64 bit integers. DBus signature (xx)
		:param event_templates: An array of event templates which the
			returned events should match at least one of.
			When using the Python bindings for Zeitgeist you may pass
			a list of  :class:`Event <zeitgeist.datamodel.Event>`
			instances directly to this method.
		:type event_templates: array of events. DBus signature a(asaasay)
		:param storage_state: whether the item is currently known to be
			available. The list of possible values is enumerated in
			:class:`StorageState <zeitgeist.datamodel.StorageState>` class
		:type storage_state: unsigned integer
		:param num_events: maximal amount of returned events
		:type num_events: unsigned integer
		:param order: unsigned integer representing
			a :class:`result type <zeitgeist.datamodel.ResultType>`
		:type order: unsigned integer
		:returns: An array containing the IDs of all matching events,
			up to a maximum of *num_events* events. Sorted and grouped
			as defined by the *result_type* parameter.
		:rtype: Array of unsigned 32 bit integers
		"""
        time_range = TimeRange(time_range[0], time_range[1])
        event_templates = map(Event, event_templates)
        return self._engine.find_eventids(time_range, event_templates, storage_state, num_events, result_type)

    @dbus.service.method(
        constants.DBUS_INTERFACE,
        in_signature="(xx)a(" + constants.SIG_EVENT + ")uuu",
        out_signature="a(" + constants.SIG_EVENT + ")",
        sender_keyword="sender",
    )
    def FindEvents(self, time_range, event_templates, storage_state, num_events, result_type, sender):
        """Get events matching a given set of templates.
		
		The matching is done where unset fields in the templates
		are treated as wildcards. If a template has more than one
		subject then events will match the template if any one of their
		subjects match any one of the subject templates.
		
		The fields uri, interpretation, manifestation, origin, and mimetype
		can be prepended with an exclamation mark '!' in order to negate
		the matching.
		
		The fields uri, origin, and mimetype can be prepended with an
		asterisk '*' in order to do truncated matching.
		
		In case you need to do a query yielding a large (or unpredictable)
		result set and you only want to show some of the results at the
		same time (eg., by paging them), use :meth:`FindEventIds`.
		
		:param time_range: two timestamps defining the timerange for
			the query. When using the Python bindings for Zeitgeist you
			may pass a :class:`TimeRange <zeitgeist.datamodel.TimeRange>`
			instance directly to this method
		:type time_range: tuple of 64 bit integers. DBus signature (xx)
		:param event_templates: An array of event templates which the
			returned events should match at least one of.
			When using the Python bindings for Zeitgeist you may pass
			a list of  :class:`Event <zeitgeist.datamodel.Event>`
			instances directly to this method.
		:type event_templates: array of events. DBus signature a(asaasay)
		:param storage_state: whether the item is currently known to be
			available. The list of possible values is enumerated in
			:class:`StorageState <zeitgeist.datamodel.StorageState>` class
		:type storage_state: unsigned integer
		:param num_events: maximal amount of returned events
		:type num_events: unsigned integer
		:param order: unsigned integer representing
			a :class:`result type <zeitgeist.datamodel.ResultType>`
		:type order: unsigned integer
		:returns: Full event data for all the requested IDs, up to a maximum
			of *num_events* events, sorted and grouped as defined by the
			*result_type* parameter. The event data can be conveniently
			converted into a list of :class:`Event` instances by calling
			*events = map(Event.new_for_struct, result)*
		:rtype: A list of serialized events. DBus signature a(asaasay).
		"""
        time_range = TimeRange(time_range[0], time_range[1])
        event_templates = map(Event, event_templates)
        return self._make_events_sendable(
            self._engine.find_events(time_range, event_templates, storage_state, num_events, result_type, sender)
        )

        # Writing stuff

    @dbus.service.method(
        constants.DBUS_INTERFACE,
        in_signature="a(" + constants.SIG_EVENT + ")",
        out_signature="au",
        sender_keyword="sender",
    )
    def InsertEvents(self, events, sender):
        """Inserts events into the log. Returns an array containing the IDs
		of the inserted events
		
		Each event which failed to be inserted into the log (either by
		being blocked or because of an error) will be represented by `0`
		in the resulting array.
		
		One way events may end up being blocked is if they match any
		of the :ref:`blacklist templates <org_gnome_zeitgeist_Blacklist>`.
		
		Any monitors with matching templates will get notified about
		the insertion. Note that the monitors are notified *after* the
		events have been inserted.
		
		:param events: List of events to be inserted in the log.
			If you are using the Python bindings you may pass
			:class:`Event <zeitgeist.datamodel.Event>` instances
			directly to this method
		:returns: An array containing the event IDs of the inserted
			events. In case any of the events where already logged,
			the ID of the existing event will be returned. `0` as ID
			indicates a failed insert into the log.
		:rtype: Array of unsigned 32 bits integers. DBus signature au.
		"""
        if not events:
            return []
        events = map(Event, events)
        event_ids = self._engine.insert_events(events, sender)

        _events = []
        min_stamp = events[0].timestamp
        max_stamp = min_stamp
        for ev, ev_id in zip(events, event_ids):
            if not ev_id:
                # event has not been inserted because of an error or
                # because of being blocked by an extension
                # this is why we do not notify clients about this event
                continue
            _ev = Event(ev)
            _ev[0][Event.Id] = ev_id
            _events.append(_ev)
            min_stamp = min(min_stamp, _ev.timestamp)
            max_stamp = max(max_stamp, _ev.timestamp)
        self._notifications.notify_insert(TimeRange(min_stamp, max_stamp), _events)

        return event_ids

    @dbus.service.method(constants.DBUS_INTERFACE, in_signature="au", out_signature="(xx)", sender_keyword="sender")
    def DeleteEvents(self, event_ids, sender):
        """Delete a set of events from the log given their IDs
		
		:param event_ids: list of event IDs obtained, for example, by calling
			:meth:`FindEventIds`
		:type event_ids: list of integers
		"""
        timestamps = self._engine.delete_events(event_ids, sender=sender)
        if timestamps:
            # We need to check the return value, as the events could already
            # have been deleted before or the IDs might even have been invalid.
            self._notifications.notify_delete(TimeRange(timestamps[0], timestamps[1]), event_ids)
        if timestamps is None:
            # unknown event id, see doc of delete_events()
            return (-1, -1)
        timestamp_start, timestamp_end = timestamps
        timestamp_start = timestamp_start if timestamp_start is not None else -1
        timestamp_end = timestamp_end if timestamp_end is not None else -1
        return (timestamp_start, timestamp_end)

    @dbus.service.method(constants.DBUS_INTERFACE, in_signature="", out_signature="")
    def DeleteLog(self):
        """Delete the log file and all its content
		
		This method is used to delete the entire log file and all its
		content in one go. To delete specific subsets use
		:meth:`FindEventIds` combined with :meth:`DeleteEvents`.
		"""
        self._engine.delete_log()

    @dbus.service.method(constants.DBUS_INTERFACE)
    def Quit(self):
        """Terminate the running Zeitgeist engine process; use with caution,
		this action must only be triggered with the user's explicit consent,
		as it will affect all applications using Zeitgeist"""
        self._engine.close()
        if self._mainloop:
            self._mainloop.quit()
            # remove the interface from all busses (in our case from the session bus)
        self._safe_quit()

        # Properties interface

    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature="ss", out_signature="v")
    def Get(self, interface_name, property_name):
        if interface_name != constants.DBUS_INTERFACE:
            raise ValueError(
                "'%s' doesn't know anything about the '%s' interface" % (constants.DBUS_INTERFACE, interface_name)
            )
        try:
            return self._dbus_properties[property_name].fget(self)
        except KeyError, e:
            raise AttributeError(property_name)