← Back to team overview

zeitgeist team mailing list archive

[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