Seif Lotfy has proposed merging lp:~zeitgeist/zeitgeist/remove-datahub into lp:zeitgeist.
Requested reviews: Zeitgeist Framework Team (zeitgeist) Related bugs: #630593 Replace old datahub with vala port https://bugs.launchpad.net/bugs/630593 initial fix for bzr bug #655164 removed zeitgeist-datahub.py and loggers -- https://code.launchpad.net/~zeitgeist/zeitgeist/remove-datahub/+merge/38339 Your team Zeitgeist Framework Team is requested to review the proposed merge of lp:~zeitgeist/zeitgeist/remove-datahub into lp:zeitgeist.
=== modified file 'Makefile.am' --- Makefile.am 2010-09-03 10:16:51 +0000 +++ Makefile.am 2010-10-13 18:39:30 +0000 @@ -6,8 +6,7 @@ intltool-update.in bin_SCRIPTS = \ - zeitgeist-daemon \ - zeitgeist-datahub + zeitgeist-daemon EXTRA_DIST = \ $(bin_SCRIPTS) \ @@ -16,7 +15,6 @@ COPYRIGHT \ NEWS \ zeitgeist-daemon.py \ - zeitgeist-datahub.py \ zeitgeist-daemon.pc.in DISTCLEANFILES = \ @@ -25,8 +23,7 @@ intltool-update CLEANFILES = \ - zeitgeist-daemon \ - zeitgeist-datahub + zeitgeist-daemon pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = zeitgeist-daemon.pc @@ -34,19 +31,12 @@ zeitgeist-daemon: zeitgeist-daemon.py sed \ -e "s!\/usr\/bin\/env python!$(PYTHON)!" \ - -e "s!zeitgeist-datahub\.py!zeitgeist-datahub!" \ < $< > $@ chmod +x zeitgeist-daemon zeitgeist-daemon: Makefile -zeitgeist-datahub: zeitgeist-datahub.py - sed \ - -e "s!\/usr\/bin\/env python!$(PYTHON)!" \ - < $< > $@ - chmod +x zeitgeist-datahub -zeitgeist-datahub: Makefile -all-local: zeitgeist-daemon zeitgeist-datahub +all-local: zeitgeist-daemon # Generate ChangeLog dist-hook: === modified file '_zeitgeist/Makefile.am' --- _zeitgeist/Makefile.am 2010-06-22 19:53:09 +0000 +++ _zeitgeist/Makefile.am 2010-10-13 18:39:30 +0000 @@ -1,4 +1,4 @@ -SUBDIRS = engine loggers +SUBDIRS = engine appdir = $(datadir)/zeitgeist/_zeitgeist/ === removed directory '_zeitgeist/loggers' === removed file '_zeitgeist/loggers/Makefile.am' --- _zeitgeist/loggers/Makefile.am 2010-06-17 20:43:34 +0000 +++ _zeitgeist/loggers/Makefile.am 1970-01-01 00:00:00 +0000 @@ -1,9 +0,0 @@ -SUBDIRS = datasources - -appdir = $(datadir)/zeitgeist/_zeitgeist/loggers/ - -app_PYTHON = \ - __init__.py \ - iso_strptime.py \ - zeitgeist_setup_service.py \ - zeitgeist_base.py === removed file '_zeitgeist/loggers/__init__.py' === removed directory '_zeitgeist/loggers/datasources' === removed file '_zeitgeist/loggers/datasources/Makefile.am' --- _zeitgeist/loggers/datasources/Makefile.am 2010-06-17 20:43:34 +0000 +++ _zeitgeist/loggers/datasources/Makefile.am 1970-01-01 00:00:00 +0000 @@ -1,6 +0,0 @@ -appdir = $(datadir)/zeitgeist/_zeitgeist/loggers/datasources - -app_PYTHON = \ - __init__.py \ - _recentmanager.py \ - recent.py === removed file '_zeitgeist/loggers/datasources/__init__.py' === removed file '_zeitgeist/loggers/datasources/_recentmanager.py' --- _zeitgeist/loggers/datasources/_recentmanager.py 2010-01-16 18:21:11 +0000 +++ _zeitgeist/loggers/datasources/_recentmanager.py 1970-01-01 00:00:00 +0000 @@ -1,146 +0,0 @@ -# -.- coding: utf-8 -.- - -# Zeitgeist -# -# Copyright © 2009 Markus Korn <[email protected]> -# Copyright © 2009 Siegfried-Angel Gevatter Pujals <[email protected]> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import urllib -import gobject -import gio -import os.path -import time -import logging -from xml.dom.minidom import parse as minidom_parse - -from _zeitgeist.loggers.iso_strptime import iso_strptime - -DST = bool(time.mktime(time.gmtime(0))) -log = logging.getLogger("zeitgeist.logger._recentmanager") - -class FileInfo(object): - - @staticmethod - def convert_timestring(time_str): - # My observation is that all times in self.RECENTFILE are in UTC (I might be wrong here) - # so we need to parse the time string into a timestamp - # and correct the result by the timezone difference - try: - timetuple = time.strptime(time_str, "%Y-%m-%dT%H:%M:%SZ") - except ValueError: - timetuple = iso_strptime(time_str.rstrip("Z")).timetuple() - result = int(time.mktime(timetuple)) - if DST: - result -= time.altzone - return result - - def __init__(self, node): - self._uri = node.getAttribute("href") - self._path = "/%s" % self._uri.split("///", 1)[-1] - self._added = self.convert_timestring(node.getAttribute("added")) - self._modified = self.convert_timestring(node.getAttribute("modified")) - self._visited = self.convert_timestring(node.getAttribute("visited")) - - mimetype = node.getElementsByTagNameNS( - "http://www.freedesktop.org/standards/shared-mime-info", - "mime-type") - if not mimetype: - raise ValueError, "Could not find mimetype for item: %s" % self._uri - self._mimetype = mimetype[-1].getAttribute("type") - - applications = node.getElementsByTagNameNS( - "http://www.freedesktop.org/standards/desktop-bookmarks", - "applications") - assert applications - application = applications[0].getElementsByTagNameNS( - "http://www.freedesktop.org/standards/desktop-bookmarks", - "application") - if not application: - raise ValueError, "Could not find application for item: %s" % self._uri - self._application = application[-1].getAttribute("exec").strip("'") - - def get_mime_type(self): - return self._mimetype - - def get_visited(self): - return self._visited - - def get_added(self): - return self._added - - def get_modified(self): - return self._modified - - def get_uri_display(self): - return self._path - - def get_uri(self): - return self._uri - - def get_display_name(self): - return unicode(os.path.basename(urllib.unquote(str(self._path)))) - - def exists(self): - if not self._uri.startswith("file:///"): - return True # Don't check online resources - return gio.File(self._path).get_path() is not None - - def get_private_hint(self): - return False # FIXME: How to get this? - - def last_application(self): - # Not necessary, our get_application_info always returns the info of - # the last application - return "" - - def get_application_info(self, app): - return (self._application, None, None) - -class RecentManager(gobject.GObject): - - RECENTFILE = os.path.expanduser("~/.recently-used.xbel") - - def __init__(self): - super(RecentManager, self).__init__() - if not os.path.exists(self.RECENTFILE): - raise OSError("Can't use alternative RecentManager, '%s' not found" % self.RECENTFILE) - - self._fetching_items = None - file_object = gio.File(self.RECENTFILE) - self.file_monitor = file_object.monitor_file() - self.file_monitor.set_rate_limit(1600) # for to high rates RecentManager - # gets hickup, not sure what's optimal here - self.file_monitor.connect("changed", self._content_changed) - - def _content_changed(self, monitor, fileobj, _, event): - # Only emit the signal if we aren't already parsing RECENTFILE - if not self._fetching_items: - self.emit("changed") - - def get_items(self): - self._fetching_items = True - xml = minidom_parse(self.RECENTFILE) - for bookmark in xml.getElementsByTagName("bookmark"): - yield FileInfo(bookmark) - self._fetching_items = False - - def set_limit(self, limit): - pass - -gobject.type_register(RecentManager) - -gobject.signal_new("changed", RecentManager, - gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) === removed file '_zeitgeist/loggers/datasources/recent.py' --- _zeitgeist/loggers/datasources/recent.py 2010-09-25 13:19:51 +0000 +++ _zeitgeist/loggers/datasources/recent.py 1970-01-01 00:00:00 +0000 @@ -1,371 +0,0 @@ -# -.- coding: utf-8 -.- - -# Zeitgeist -# -# Copyright © 2009 Alex Graveley <[email protected]> -# Copyright © 2009 Markus Korn <[email protected]> -# Copyright © 2009 Natan Yellin <[email protected]> -# Copyright © 2009 Seif Lotfy <[email protected]> -# Copyright © 2009 Shane Fagan <[email protected]> -# Copyright © 2009-2010 Siegfried-Angel Gevatter Pujals <[email protected]> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -from __future__ import with_statement -import os -import re -import fnmatch -import urllib -import time -import logging -from xdg import BaseDirectory - -from zeitgeist import _config -from zeitgeist.datamodel import Event, Subject, Interpretation, Manifestation, \ - DataSource, get_timestamp_for_now -from _zeitgeist.loggers.zeitgeist_base import DataProvider - -log = logging.getLogger("zeitgeist.logger.datasources.recent") - -try: - import gtk - if gtk.pygtk_version >= (2, 15, 2): - recent_manager = gtk.recent_manager_get_default - else: - from _recentmanager import RecentManager - recent_manager = RecentManager -except ImportError: - log.exception(_("Could not import GTK; data source disabled.")) - enabled = False -else: - enabled = True - -class SimpleMatch(object): - """ Wrapper around fnmatch.fnmatch which allows to define mimetype - patterns by using shell-style wildcards. - """ - - def __init__(self, pattern): - self.__pattern = pattern - - def match(self, text): - return fnmatch.fnmatch(text, self.__pattern) - - def __repr__(self): - return "%s(%r)" %(self.__class__.__name__, self.__pattern) - -DOCUMENT_MIMETYPES = [ - # Covers: - # vnd.corel-draw - # vnd.ms-powerpoint - # vnd.ms-excel - # vnd.oasis.opendocument.* - # vnd.stardivision.* - # vnd.sun.xml.* - SimpleMatch(u"application/vnd.*"), - # Covers: x-applix-word, x-applix-spreadsheet, x-applix-presents - SimpleMatch(u"application/x-applix-*"), - # Covers: x-kword, x-kspread, x-kpresenter, x-killustrator - re.compile(u"application/x-k(word|spread|presenter|illustrator)"), - u"application/ms-powerpoint", - u"application/msword", - u"application/pdf", - u"application/postscript", - u"application/ps", - u"application/rtf", - u"application/x-abiword", - u"application/x-gnucash", - u"application/x-gnumeric", - SimpleMatch(u"application/x-java*"), - SimpleMatch(u"*/x-tex"), - SimpleMatch(u"*/x-latex"), - SimpleMatch(u"*/x-dvi"), - u"text/plain" -] - -IMAGE_MIMETYPES = [ - # Covers: - # vnd.corel-draw - u"application/vnd.corel-draw", - # Covers: x-kword, x-kspread, x-kpresenter, x-killustrator - re.compile(u"application/x-k(word|spread|presenter|illustrator)"), - SimpleMatch(u"image/*"), -] - -AUDIO_MIMETYPES = [ - SimpleMatch(u"audio/*"), - u"application/ogg" -] - -VIDEO_MIMETYPES = [ - SimpleMatch(u"video/*"), - u"application/ogg" -] - -DEVELOPMENT_MIMETYPES = [ - u"application/ecmascript", - u"application/javascript", - u"application/x-csh", - u"application/x-designer", - u"application/x-desktop", - u"application/x-dia-diagram", - u"application/x-fluid", - u"application/x-glade", - u"application/xhtml+xml", - u"application/x-java-archive", - u"application/x-m4", - u"application/xml", - u"application/x-object", - u"application/x-perl", - u"application/x-php", - u"application/x-ruby", - u"application/x-shellscript", - u"application/x-sql", - u"text/css", - u"text/html", - u"text/x-c", - u"text/x-c++", - u"text/x-chdr", - u"text/x-copying", - u"text/x-credits", - u"text/x-csharp", - u"text/x-c++src", - u"text/x-csrc", - u"text/x-dsrc", - u"text/x-eiffel", - u"text/x-gettext-translation", - u"text/x-gettext-translation-template", - u"text/x-haskell", - u"text/x-idl", - u"text/x-java", - u"text/x-lisp", - u"text/x-lua", - u"text/x-makefile", - u"text/x-objcsrc", - u"text/x-ocaml", - u"text/x-pascal", - u"text/x-patch", - u"text/x-python", - u"text/x-sql", - u"text/x-tcl", - u"text/x-troff", - u"text/x-vala", - u"text/x-vhdl", -] - -ALL_MIMETYPES = DOCUMENT_MIMETYPES + IMAGE_MIMETYPES + AUDIO_MIMETYPES + \ - VIDEO_MIMETYPES + DEVELOPMENT_MIMETYPES - -class MimeTypeSet(set): - """ Set which allows to match against a string or an object with a - match() method. - """ - - def __init__(self, *items): - super(MimeTypeSet, self).__init__() - self.__pattern = set() - for item in items: - if isinstance(item, (str, unicode)): - self.add(item) - elif hasattr(item, "match"): - self.__pattern.add(item) - else: - raise ValueError("Bad mimetype '%s'" %item) - - def __contains__(self, mimetype): - result = super(MimeTypeSet, self).__contains__(mimetype) - if not result: - for pattern in self.__pattern: - if pattern.match(mimetype): - return True - return result - - def __len__(self): - return super(MimeTypeSet, self).__len__() + len(self.__pattern) - - def __repr__(self): - items = ", ".join(sorted(map(repr, self | self.__pattern))) - return "%s(%s)" %(self.__class__.__name__, items) - - -class RecentlyUsedManagerGtk(DataProvider): - - FILTERS = { - # dict of name as key and the matching mimetypes as value - # if the value is None this filter matches all mimetypes - "DOCUMENT": MimeTypeSet(*DOCUMENT_MIMETYPES), - "IMAGE": MimeTypeSet(*IMAGE_MIMETYPES), - "AUDIO": MimeTypeSet(*AUDIO_MIMETYPES), - "VIDEO": MimeTypeSet(*VIDEO_MIMETYPES), - "SOURCE_CODE": MimeTypeSet(*DEVELOPMENT_MIMETYPES), - } - - def __init__(self, client): - DataProvider.__init__(self, - unique_id="com.zeitgeist-project,datahub,recent", - name="Recently Used Documents", - description="Logs events from GtkRecentlyUsed", - event_templates=[Event.new_for_values(interpretation=i) for i in ( - Interpretation.CREATE_EVENT, - Interpretation.ACCESS_EVENT, - Interpretation.MODIFY_EVENT - )], - client=client) - self._load_data_sources_registry() - self.recent_manager = recent_manager() - self.recent_manager.set_limit(-1) - self.recent_manager.connect("changed", lambda m: self.emit("reload")) - self.config.connect("configured", lambda m: self.emit("reload")) - - def _load_data_sources_registry(self): - self._ignore_apps = {} - def _data_source_registered(datasource): - for tmpl in datasource[DataSource.EventTemplates]: - actor = tmpl[0][Event.Actor] - if actor: - if not actor in self._ignore_apps: - self._ignore_apps[actor] = set() - interp = tmpl[0][Event.Interpretation] - if interp: - self._ignore_apps[actor].add(interp) - for datasource in self._registry.GetDataSources(): - _data_source_registered(datasource) - self._registry.connect("DataSourceRegistered", _data_source_registered) - - @staticmethod - def _desktop_file_matches_app(filename, application, mimetype): - """ Checks whether the given .desktop file represents the indicated - application. - - If a mimetype is also given, only .desktop files listing it as - supported will be considered. This is needed to differentiate - between the different OpenOffice.org components, for instance. - """ - - try: - with open(filename) as desktopfile: - _exec = False - _mime = False - for line in desktopfile: - if line.startswith("Exec=") and \ - line.split("=", 1)[-1].strip().split()[0] == application: - if mimetype is None or _mime: - return True - else: - _exec = True - elif line.startswith("MimeType=") and mimetype in \ - line.split("=", 1)[-1].split(";"): - if _exec: - return True - else: - _mime = True - - except IOError: - pass # file may be a broken symlink (LP: #523761) - except Exception, e: - log.warning('Corrupt .desktop file: %s', filename) - - return False - - @staticmethod - def _find_desktop_file_for_application(application, mimetype): - """ Searches for a .desktop file for the given application in - $XDG_DATADIRS and returns the path to the found file. If no file - is found, returns None. - """ - desktopfiles = \ - list(BaseDirectory.load_data_paths("applications", "%s.desktop" % application)) - if desktopfiles: - return unicode(desktopfiles[0]) - else: - is_match = RecentlyUsedManagerGtk._desktop_file_matches_app # local stuff is accessed faster - for path in BaseDirectory.load_data_paths("applications"): - for filename in (name for name in os.listdir(path) if name.endswith(".desktop")): - fullname = os.path.join(path, filename) - if (is_match(fullname, application, mimetype)): - return unicode(fullname) - - return None - - def _get_interpretation_for_mimetype(self, mimetype): - matching_filter = None - for filter_name, mimetypes in self.FILTERS.iteritems(): - if mimetype and mimetype in mimetypes: - matching_filter = filter_name - break - if matching_filter: - return getattr(Interpretation, matching_filter).uri - return "" - - def _get_items(self): - # We save the start timestamp to avoid race conditions - last_seen = get_timestamp_for_now() - - events = [] - - for (num, info) in enumerate(self.recent_manager.get_items()): - uri = info.get_uri() - if info.exists() and not info.get_private_hint() and not uri.startswith("file:///tmp/"): - last_application = info.last_application().strip() - application = info.get_application_info(last_application)[0].split()[0] - mimetype = unicode(info.get_mime_type()) - if application in ("ooffice", "soffice"): - # Special case OpenOffice.org *sigh* - desktopfile = self._find_desktop_file_for_application("ooffice", mimetype) - else: - desktopfile = self._find_desktop_file_for_application(application, None) - if not desktopfile: - continue - actor = u"application://%s" % os.path.basename(desktopfile) - - subject = Subject.new_for_values( - uri = unicode(uri), - interpretation = self._get_interpretation_for_mimetype( - unicode(info.get_mime_type())), - manifestation = Manifestation.FILE_DATA_OBJECT.uri, - text = info.get_display_name(), - mimetype = mimetype, - origin = uri.rpartition("/")[0] - ) - - times = set() - for meth, interp in ( - (info.get_added, Interpretation.CREATE_EVENT.uri), - (info.get_visited, Interpretation.ACCESS_EVENT.uri), - (info.get_modified, Interpretation.MODIFY_EVENT.uri) - ): - if actor not in self._ignore_apps or \ - (self._ignore_apps[actor] and - interp not in self._ignore_apps[actor]): - times.add((meth() * 1000, interp)) - - is_new = False - for timestamp, use in times: - if timestamp <= self._last_seen: - continue - is_new = True - events.append(Event.new_for_values( - timestamp = timestamp, - interpretation = use, - manifestation = Manifestation.USER_ACTIVITY.uri, - actor = actor, - subjects = [subject] - )) - if num % 50 == 0: - self._process_gobject_events() - self._last_seen = last_seen - return events - -if enabled: - __datasource__ = RecentlyUsedManagerGtk === removed file '_zeitgeist/loggers/iso_strptime.py' --- _zeitgeist/loggers/iso_strptime.py 2009-07-20 17:10:41 +0000 +++ _zeitgeist/loggers/iso_strptime.py 1970-01-01 00:00:00 +0000 @@ -1,86 +0,0 @@ -# -.- coding: utf-8 -.- - -# This file is part of wadllib. -# -# Copyright © 2009 Canonical Ltd. -# -# wadllib is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, version 3 of the License. -# -# wadllib 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with wadllib. If not, see <http://www.gnu.org/licenses/>. - -""" -Parser for ISO 8601 time strings -================================ - ->>> d = iso_strptime("2008-01-07T05:30:30.345323+03:00") ->>> d -datetime.datetime(2008, 1, 7, 5, 30, 30, 345323, tzinfo=TimeZone(10800)) ->>> d.timetuple() -(2008, 1, 7, 5, 30, 30, 0, 7, 0) ->>> d.utctimetuple() -(2008, 1, 7, 2, 30, 30, 0, 7, 0) ->>> iso_strptime("2008-01-07T05:30:30.345323-03:00") -datetime.datetime(2008, 1, 7, 5, 30, 30, 345323, tzinfo=TimeZone(-10800)) ->>> iso_strptime("2008-01-07T05:30:30.345323") -datetime.datetime(2008, 1, 7, 5, 30, 30, 345323) ->>> iso_strptime("2008-01-07T05:30:30") -datetime.datetime(2008, 1, 7, 5, 30, 30) ->>> iso_strptime("2008-01-07T05:30:30+02:00") -datetime.datetime(2008, 1, 7, 5, 30, 30, tzinfo=TimeZone(7200)) -""" - -import re -import datetime - -RE_TIME = re.compile(r"""^ - # pattern matching date - (?P<year>\d{4})\-(?P<month>\d{2})\-(?P<day>\d{2}) - # separator - T - # pattern matching time - (?P<hour>\d{2})\:(?P<minutes>\d{2})\:(?P<seconds>\d{2}) - # pattern matching optional microseconds - (\.(?P<microseconds>\d{6})\d*)? - # pattern matching optional timezone offset - (?P<tz_offset>[\-\+]\d{2}\:\d{2})? - $""", re.VERBOSE) - -class TimeZone(datetime.tzinfo): - - def __init__(self, tz_string): - hours, minutes = tz_string.lstrip("-+").split(":") - self.stdoffset = datetime.timedelta(hours=int(hours), - minutes=int(minutes)) - if tz_string.startswith("-"): - self.stdoffset *= -1 - - def __repr__(self): - return "TimeZone(%s)" % ( - self.stdoffset.days*24*60*60 + self.stdoffset.seconds) - - def utcoffset(self, dt): - return self.stdoffset - - def dst(self, dt): - return datetime.timedelta(0) - -def iso_strptime(time_str): - x = RE_TIME.match(time_str) - if not x: - raise ValueError("unable to parse time '%s'" %time_str) - d = datetime.datetime(int(x.group("year")), int(x.group("month")), - int(x.group("day")), int(x.group("hour")), int(x.group("minutes")), - int(x.group("seconds"))) - if x.group("microseconds"): - d = d.replace(microsecond=int(x.group("microseconds"))) - if x.group("tz_offset"): - d = d.replace(tzinfo=TimeZone(x.group("tz_offset"))) - return d === removed file '_zeitgeist/loggers/zeitgeist_base.py' --- _zeitgeist/loggers/zeitgeist_base.py 2010-04-22 18:28:40 +0000 +++ _zeitgeist/loggers/zeitgeist_base.py 1970-01-01 00:00:00 +0000 @@ -1,91 +0,0 @@ -# -.- coding: utf-8 -.- - -# Zeitgeist -# -# Copyright © 2009 Seif Lotfy <[email protected]> -# Copyright © 2009-2010 Siegfried-Angel Gevatter Pujals <[email protected]> -# Copyright © 2009 Natan Yellin <[email protected]> -# Copyright © 2009 Alex Graveley <[email protected]> -# Copyright © 2009 Markus Korn <[email protected]> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -from threading import Thread -import gobject -import logging - -from zeitgeist.datamodel import DataSource -from _zeitgeist.loggers.zeitgeist_setup_service import _Configuration, DefaultConfiguration - -class DataProvider(gobject.GObject, Thread): - - __gsignals__ = { - "reload" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), - } - - def __init__(self, unique_id, name, description="", event_templates=[], - client=None, config=None): - - # Initialize superclasses - Thread.__init__(self) - gobject.GObject.__init__(self) - - self._name = name - self._client = client - self._ctx = gobject.main_context_default() - - if client: - self._registry = self._client.get_extension("DataSourceRegistry", - "data_source_registry") - try: - self._last_seen = [ds[DataSource.LastSeen] for ds in \ - self._registry.GetDataSources() if \ - ds[DataSource.UniqueId] == unique_id][0] - 1800000 - # We substract 30 minutes to make sure no events get missed. - except IndexError: - self._last_seen = 0 - self._enabled = self._registry.RegisterDataSource(unique_id, name, - description, event_templates) - - if not config: - self.config = DefaultConfiguration(self._name) - else: - if not isinstance(config, _Configuration): - raise TypeError - self.config = config - - def get_name(self): - return self._name - - def get_items(self): - if not self._enabled: - return [] - # FIXME: We need to figure out what to do with this configuration stuff - # Maybe merge it into the DataSource registry so that everyone - # can benefit from it, or just throw it out. - if not self.config.isConfigured() or not self.config.enabled: - logging.warning("'%s' isn't enabled or configured." % \ - self.config.get_internal_name()) - return [] - return self._get_items() - - def _get_items(self): - """ Subclasses should override this to return data. """ - raise NotImplementedError - - def _process_gobject_events(self): - """ Check for pending gobject events. This should be called in some - meaningful place in _get_items on long running updates. """ - while self._ctx.pending(): - self._ctx.iteration() === removed file '_zeitgeist/loggers/zeitgeist_setup_service.py' --- _zeitgeist/loggers/zeitgeist_setup_service.py 2009-09-04 15:13:13 +0000 +++ _zeitgeist/loggers/zeitgeist_setup_service.py 1970-01-01 00:00:00 +0000 @@ -1,243 +0,0 @@ -# -.- coding: utf-8 -.- - -# Zeitgeist -# -# Copyright © 2009 Markus Korn <[email protected]> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import dbus -import dbus.service -import gobject -import gconf -import glib -import dbus.mainloop.glib -from ConfigParser import SafeConfigParser -from xdg import BaseDirectory -from StringIO import StringIO - -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - -class DataProviderService(dbus.service.Object): - - def __init__(self, datasources, mainloop=None): - bus_name = dbus.service.BusName("org.gnome.zeitgeist.datahub", dbus.SessionBus()) - dbus.service.Object.__init__(self, bus_name, "/org/gnome/zeitgeist/datahub") - self._mainloop = mainloop - self.__datasources = datasources - - @dbus.service.method("org.gnome.zeitgeist.DataHub", - out_signature="as") - def GetDataProviders(self): - return [i.config.get_internal_name() for i in self.__datasources if i.config.has_dbus_service()] - - def needs_setup(self): - return not self.__configuration.isConfigured() - - -class SetupService(dbus.service.Object): - - def __init__(self, datasource, root_config, mainloop=None): - bus_name = dbus.service.BusName("org.gnome.zeitgeist.datahub", dbus.SessionBus()) - dbus.service.Object.__init__(self, - bus_name, "/org/gnome/zeitgeist/datahub/dataprovider/%s" %datasource) - self._mainloop = mainloop - self.__configuration = root_config - if not isinstance(self.__configuration, _Configuration): - raise TypeError - self.__setup_is_running = None - - @dbus.service.method("org.gnome.zeitgeist.DataHub", - in_signature="iss") - def SetConfiguration(self, token, option, value): - if token != self.__setup_is_running: - raise RuntimeError("wrong client") - self.__configuration.set_attribute(option, value) - - @dbus.service.signal("org.gnome.zeitgeist.DataHub") - def NeedsSetup(self): - pass - - @dbus.service.method("org.gnome.zeitgeist.DataHub", - in_signature="i", out_signature="b") - def RequestSetupRun(self, token): - if self.__setup_is_running is None: - self.__setup_is_running = token - return True - else: - raise False - - @dbus.service.method("org.gnome.zeitgeist.DataHub", - out_signature="a(sb)") - def GetOptions(self, token): - if token != self.__setup_is_running: - raise RuntimeError("wrong client") - return self.__configuration.get_options() - - def needs_setup(self): - return not self.__configuration.isConfigured() - - -class _Configuration(gobject.GObject): - - @staticmethod - def like_bool(value): - if isinstance(value, bool): - return value - elif value.lower() in ("true", "1", "on"): - return True - elif value.lower() in ("false", "0", "off"): - return False - else: - raise ValueError - - def __init__(self, internal_name, use_dbus=True, mainloop=None): - gobject.GObject.__init__(self) - self.__required = set() - self.__items = dict() - self.__internal_name = internal_name.replace(" ", "_").lower() - if use_dbus: - self.__dbus_service = SetupService(self.__internal_name, self, mainloop) - else: - self.__dbus_service = None - - def has_dbus_service(self): - return self.__dbus_service is not None - - def get_internal_name(self): - return self.__internal_name - - def add_option(self, name, to_type=str, to_string=str, default=None, - required=True, secret=False): - if name in self.__items: - raise ValueError - if required: - self.__required.add(name) - if to_type is None: - to_type = lambda x: x - self.__items[name] = (to_type(default), (to_type, to_string), secret) - - def __getattr__(self, name): - if not self.isConfigured(): - raise RuntimeError - return self.__items[name][0] - - def get_as_string(self, name): - if not self.isConfigured(): - raise RuntimeError - try: - value, (_, to_string), _ = self.__items[name] - except KeyError: - raise AttributeError - return str(to_string(value)) - - def set_attribute(self, name, value, check_configured=True): - if name not in self.__items: - raise ValueError - _, (to_type, to_string), secret = self.__items[name] - self.__items[name] = (to_type(value), (to_type, to_string), secret) - if name in self.__required: - self.remove_requirement(name) - if check_configured and self.isConfigured(): - glib.idle_add(self.emit, "configured") - - def remove_requirement(self, name): - self.__required.remove(name) - - def add_requirement(self, name): - if not name in self.__items: - raise ValueError - self.__required.add(name) - - def isConfigured(self): - return not self.__required - - def read_config(self, filename, section): - config = SafeConfigParser() - config.readfp(open(filename)) - if config.has_section(section): - for name, value in config.items(section): - self.set_attribute(name, value) - - def dump_config(self, config=None): - section = self.get_internal_name() - if config is None: - config = SafeConfigParser() - try: - config.add_section(section) - except ConfigParser.DuplicateSectionError: - pass - for key, value in self.__items.iteritems(): - value, _, secret = value - if not secret: - config.set(section, key, str(value)) - f = StringIO() - config.write(f) - return f.getvalue() - - def get_requirements(self): - return self.__required - - def get_options(self): - return [(str(key), key in self.__required) for key in self.__items] - - -gobject.signal_new("configured", _Configuration, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, - tuple()) - - -class DefaultConfiguration(_Configuration): - - CONFIGFILE = BaseDirectory.load_first_config("zeitgeist", "dataprovider.conf") - DEFAULTS = [ - ("enabled", _Configuration.like_bool, str, True, False), - ] - - def __init__(self, dataprovider): - super(DefaultConfiguration, self).__init__(dataprovider) - for default in self.DEFAULTS: - self.add_option(*default) - if self.CONFIGFILE: - self.read_config(self.CONFIGFILE, self.get_internal_name()) - - def save_config(self): - if self.CONFIGFILE: - config = SafeConfigParser() - config.readfp(open(self.CONFIGFILE)) - self.dump_config(config) - f = StringIO() - config.write(f) - configfile = open(self.CONFIGFILE, "w") - try: - config.write(configfile) - finally: - configfile.close() - -if __name__ == "__main__": - - # TODO: Move this to test/. - - def test(config): - for option, required in config.get_options(): - print option, getattr(config, option) - - dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - mainloop = gobject.MainLoop() - - config = _Configuration("test", True, mainloop) - config.add_option("enabled", _Configuration.like_bool, default=False) - config.connect("configured", test) - mainloop.run() === modified file 'configure.ac' --- configure.ac 2010-09-26 18:16:01 +0000 +++ configure.ac 2010-10-13 18:39:30 +0000 @@ -26,8 +26,6 @@ zeitgeist-daemon.pc zeitgeist/Makefile _zeitgeist/Makefile - _zeitgeist/loggers/Makefile - _zeitgeist/loggers/datasources/Makefile _zeitgeist/engine/Makefile _zeitgeist/engine/extensions/Makefile _zeitgeist/engine/upgrades/Makefile === modified file 'po/POTFILES.in' --- po/POTFILES.in 2009-11-27 20:32:54 +0000 +++ po/POTFILES.in 2010-10-13 18:39:30 +0000 @@ -1,8 +1,4 @@ zeitgeist-daemon.py -zeitgeist-datahub.py zeitgeist/datamodel.py _zeitgeist/singleton.py -_zeitgeist/loggers/zeitgeist_base.py -_zeitgeist/loggers/zeitgeist_setup_service.py -_zeitgeist/loggers/datasources/recent.py _zeitgeist/engine/remote.py === removed file 'test/loggers-datasources-recent-test.py' --- test/loggers-datasources-recent-test.py 2010-09-21 09:17:41 +0000 +++ test/loggers-datasources-recent-test.py 1970-01-01 00:00:00 +0000 @@ -1,49 +0,0 @@ -#!/usr/bin/python - -# Update python path to use local zeitgeist module -import sys -import os -import re -import unittest - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) - -SimpleMatch = None -MimeTypeSet = None - -class BaseTestCase(unittest.TestCase): - - def setUp(self): - global SimpleMatch - global MimeTypeSet - if None in (SimpleMatch, MimeTypeSet): - from _zeitgeist.loggers.datasources.recent import SimpleMatch as _SM, MimeTypeSet as _MTS - SimpleMatch = _SM - MimeTypeSet = _MTS - -class SimpleMatchTest(BaseTestCase): - - def testmatch(self): - self.assertTrue(SimpleMatch("boo/*").match("boo/bar")) - self.assertTrue(SimpleMatch("boo/bar.*").match("boo/bar.foo")) - self.assertFalse(SimpleMatch("boo/bar.*").match("boo/barfoo")) - -class MimeTypeSetTest(BaseTestCase): - - def testinit(self): - self.assertEquals(repr(MimeTypeSet("boo", "bar", "foo")), "MimeTypeSet('bar', 'boo', 'foo')") - self.assertEquals(repr(MimeTypeSet("boo", "foo", "foo")), "MimeTypeSet('boo', 'foo')") - m = MimeTypeSet("boo", SimpleMatch("bar/*"), re.compile("test.*")) - self.assertEquals(len(m), 3) - self.assertRaises(ValueError, MimeTypeSet, 1) - - def testcontains(self): - m = MimeTypeSet("boo", SimpleMatch("bar/*"), re.compile("test.*")) - self.assertTrue("boo" in m) - self.assertTrue("bar/boo" in m) - self.assertTrue("testboo" in m) - self.assertFalse("boobar" in m) - self.assertFalse("bar" in m) - -if __name__ == '__main__': - unittest.main() === modified file 'zeitgeist-daemon.py' --- zeitgeist-daemon.py 2010-09-15 12:56:45 +0000 +++ zeitgeist-daemon.py 2010-10-13 18:39:30 +0000 @@ -100,14 +100,13 @@ logging.error(unicode(e)) sys.exit(1) -passive_loggers = os.path.join(_config.bindir, "zeitgeist-datahub.py") if _config.options.start_datahub: - if os.path.isfile(passive_loggers): + try: devnull = open(os.devnull, 'w') - subprocess.Popen(passive_loggers, stdin=devnull, stdout=devnull, + subprocess.Popen(zeitgeist-datahub, stdin=devnull, stdout=devnull, stderr=devnull) del devnull - else: + except: logging.warning( _("File \"%s\" not found, not starting datahub") % passive_loggers) === removed file 'zeitgeist-datahub.py' --- zeitgeist-datahub.py 2010-01-21 14:57:23 +0000 +++ zeitgeist-datahub.py 1970-01-01 00:00:00 +0000 @@ -1,135 +0,0 @@ -#! /usr/bin/env python -# -.- coding: utf-8 -.- - -# Zeitgeist -# -# Copyright © 2009 Siegfried-Angel Gevatter Pujals <[email protected]> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import sys -import os -import glob -import gettext -import logging -import gobject -import dbus.exceptions - -from zeitgeist import _config -_config.setup_path() - -from zeitgeist.client import ZeitgeistDBusInterface -from _zeitgeist.loggers.zeitgeist_setup_service import DataProviderService - -gettext.install("zeitgeist", _config.localedir, unicode=1) -logging.basicConfig(level=logging.DEBUG) - -sys.path.insert(0, _config.datasourcedir) - -class DataHub(gobject.GObject): - - __gsignals__ = { - "reload" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), - } - - def __init__(self): - - gobject.GObject.__init__(self) - - self._client = ZeitgeistDBusInterface() - self._client.connect_exit(self._daemon_exit) - - # Load the data sources - self._sources = [] - for datasource_file in glob.glob(_config.datasourcedir + '/*.py'): - if not datasource_file.startswith('_'): - self._load_datasource_file(os.path.basename(datasource_file)) - - # Start by fetch new items from all sources - self._sources_queue = list(self._sources) - if not self._sources_queue: - logging.warning(_("No passive loggers found, bye.")) - sys.exit(1) # Mainloop doesn't exist yet, exit directly - self._db_update_in_progress = True - gobject.idle_add(self._update_db_async) - - for source in self._sources: - source.connect("reload", self._update_db_with_source) - - self._mainloop = gobject.MainLoop() - self.dbus_service = DataProviderService(self._sources, None) - self._mainloop.run() - - def _daemon_exit(self): - self._mainloop.quit() - - def _load_datasource_file(self, datasource_file): - - try: - datasource_object = __import__(datasource_file[:-3]) - except ImportError, err: - logging.exception(_("Could not load file: %s" % datasource_file)) - return False - - if hasattr(datasource_object, "__datasource__"): - objs = datasource_object.__datasource__ - for obj in objs if hasattr(objs, "__iter__") else (objs,): - self._sources.append(obj(self._client)) - - def _update_db_with_source(self, source): - """ - Add new items into the database. This funcion should not be - called directly, but instead activated through the "reload" - signal. - """ - - if not source in self._sources_queue: - self._sources_queue.append(source) - if not self._db_update_in_progress: - self._db_update_in_progress = True - gobject.idle_add(self._update_db_async) - - def _update_db_async(self): - - logging.debug(_("Updating database with new %s items") % \ - self._sources_queue[0].get_name()) - - events = self._sources_queue[0].get_items() - if events: - self._insert_events(self._sources_queue[0].get_name(), events) - - del self._sources_queue[0] - - if len(self._sources_queue) == 0: - self._db_update_in_progress = False - return False # Return False to stop this callback - - # Otherwise, if there are more items in the queue return True so - # that GTK+ will continue to call this function in idle CPU time - return True - - def _insert_events(self, source_name, events): - try: - self._client.InsertEvents(events) - except dbus.exceptions.DBusException, error: - error = error.get_dbus_name() - if error == "org.freedesktop.DBus.Error.ServiceUnknown": - logging.warning( - _("Lost connection to zeitgeist-daemon, terminating.")) - self._daemon_exit() - else: - logging.exception(_("Error logging item from \"%s\": %s" % \ - (source_name, error))) - -datahub = DataHub()
_______________________________________________ Mailing list: https://launchpad.net/~zeitgeist Post to : [email protected] Unsubscribe : https://launchpad.net/~zeitgeist More help : https://help.launchpad.net/ListHelp

