zeitgeist team mailing list archive
-
zeitgeist team
-
Mailing list archive
-
Message #02087
[Merge] lp:~zeitgeist/zeitgeist/remove-datahub into lp:zeitgeist
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 <thekorn@xxxxxx>
-# Copyright © 2009 Siegfried-Angel Gevatter Pujals <rainct@xxxxxxxxxx>
-#
-# 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 <alex.graveley@xxxxxxxxxxxxxxxxxxxxx>
-# Copyright © 2009 Markus Korn <thekorn@xxxxxx>
-# Copyright © 2009 Natan Yellin <aantny@xxxxxxxxx>
-# Copyright © 2009 Seif Lotfy <seif@xxxxxxxxx>
-# Copyright © 2009 Shane Fagan <shanepatrickfagan@xxxxxxxx>
-# Copyright © 2009-2010 Siegfried-Angel Gevatter Pujals <rainct@xxxxxxxxxx>
-#
-# 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 <seif@xxxxxxxxx>
-# Copyright © 2009-2010 Siegfried-Angel Gevatter Pujals <rainct@xxxxxxxxxx>
-# Copyright © 2009 Natan Yellin <aantny@xxxxxxxxx>
-# Copyright © 2009 Alex Graveley <alex@xxxxxxxxxxxxxxxxxxx>
-# Copyright © 2009 Markus Korn <thekorn@xxxxxx>
-#
-# 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 <thekorn@xxxxxx>
-#
-# 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 <rainct@xxxxxxxxxx>
-#
-# 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()
Follow ups