Siegfried Gevatter has proposed merging lp:~zeitgeist/zeitgeist/move_logic into 
lp:zeitgeist with lp:~zeitgeist/zeitgeist/dbschema4 as a prerequisite.

Requested reviews:
  Zeitgeist Framework Team (zeitgeist)

For more details, see:
https://code.launchpad.net/~zeitgeist/zeitgeist/move_logic/+merge/54580
-- 
https://code.launchpad.net/~zeitgeist/zeitgeist/move_logic/+merge/54580
Your team Zeitgeist Framework Team is requested to review the proposed merge of 
lp:~zeitgeist/zeitgeist/move_logic into lp:zeitgeist.
=== removed file '_zeitgeist/engine/extensions/storagemonitor.py'
--- _zeitgeist/engine/extensions/storagemonitor.py	2011-03-23 18:21:08 +0000
+++ _zeitgeist/engine/extensions/storagemonitor.py	1970-01-01 00:00:00 +0000
@@ -1,385 +0,0 @@
-# -.- coding: utf-8 -.-
-
-# Zeitgeist
-#
-# Copyright © 2009 Mikkel Kamstrup Erlandsen <mikkel.kamst...@gmail.com>
-# Copyright © 2011 Canonical Ltd
-#
-# 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 2.1 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 os
-import dbus
-import dbus.service
-import sqlite3
-import gio
-import logging
-
-from zeitgeist.datamodel import Event
-from _zeitgeist.engine.extension import Extension
-from _zeitgeist.engine import constants
-
-from zeitgeist.datamodel import StorageState
-from _zeitgeist.engine.sql import get_default_cursor
-
-log = logging.getLogger("zeitgeist.storagemonitor")
-
-#
-# Storage mediums we need to handle:
-#
-# - USB drives
-# - Data CD/DVDs
-# - Audio CDs
-# - Video DVD
-# - Networked file systems
-# - Online resources
-#
-# A storage medium is  gio.Volume (since this is a physical entity for the user)
-# or a network interface - how ever NetworkManager/ConnMan model these
-#
-# We can not obtain UUIDs for all of the listed gio.Volumes, so we need a
-# fallback chain of identifiers
-#
-# DB schema: 
-# It may be handy for app authors to have the human-readable
-# description at hand. We can not currently easily do this in the
-# current db... We may be able to do this in a new table, not
-# breaking compat with the log db. We might also want a formal type
-# associated with the storage so apps can use an icon for it.
-# A new table and a new object+interface on DBus could facilitate this
-#
-# 'storage' table
-#   id
-#   name
-#   state
-#   +type
-#   +display_name
-#
-# FIXME: We can not guess what the correct ID of CDs and DVDs were when they
-#        are ejected, and also guess "unknown"
-#
-
-STORAGE_MONITOR_DBUS_OBJECT_PATH = "/org/gnome/zeitgeist/storagemonitor"
-STORAGE_MONITOR_DBUS_INTERFACE = "org.gnome.zeitgeist.StorageMonitor"
-
-class StorageMonitor(Extension, dbus.service.Object):
-	"""
-	The Storage Monitor monitors the availability of network interfaces and
-	storage devices and updates the Zeitgeist database with this information so
-	clients can efficiently query based on the storage identifier and availability
-	of the storage media the event subjects reside on.
-	
-	For storage devices the monitor will use the UUID of the partition that a
-	subject reside on as storage id. For network URIs the storage monitor will
-	use the fixed identifier :const:`net`. For subjects residing on persistent,
-	but unidentifiable, media attached to the computer the id :const:`local`
-	will be used. For URIs that can't be handled the storage id will be set
-	to :const:`unknown`. The :const:`local` and :const:`unknown` storage media
-	are considered to be always in an available state. To determine the
-	availability of the :const:`net` media the monitor will use either Connman
-	or NetworkManager - what ever is available on the host system.
-
-	For subjects being inserted into the log that doesn't have a storage id set
-	on them this extension will try and figure it out on the fly and update
-	the subject appropriately before its inserted into the log.
-	
-	The storage monitor of the Zeitgeist engine has DBus object path
-	:const:`/org/gnome/zeitgeist/storagemonitor` under the bus name
-	:const:`org.gnome.zeitgeist.Engine`.
-	"""
-	PUBLIC_METHODS = []
-	
-	def __init__ (self, engine):		
-		Extension.__init__(self, engine)
-		dbus.service.Object.__init__(self, dbus.SessionBus(),
-		                             STORAGE_MONITOR_DBUS_OBJECT_PATH)
-		
-		self._db = get_default_cursor()
-		mon = gio.VolumeMonitor()
-		
-		# Update DB with all current states
-		for vol in mon.get_volumes():
-			self.add_storage_medium(self._get_volume_id(vol), vol.get_icon().to_string(), vol.get_name())
-		
-		# React to volumes comming and going
-		mon.connect("volume-added", self._on_volume_added)
-		mon.connect("volume-removed", self._on_volume_removed)
-		
-		# Write connectivity to the DB. Dynamically decide whether to use
-		# Connman or NetworkManager
-		if dbus.SystemBus().name_has_owner ("net.connman"):
-			self._network = ConnmanNetworkMonitor(lambda: self.add_storage_medium("net", "stock_internet", "Internet"),
-			                                      lambda: self.remove_storage_medium("net"))
-		elif dbus.SystemBus().name_has_owner ("org.freedesktop.NetworkManager"):
-			self._network = NMNetworkMonitor(lambda: self.add_storage_medium("net", "stock_internet", "Internet"),
-			                                 lambda: self.remove_storage_medium("net"))
-		else:
-			log.info("No network monitoring system found (Connman or NetworkManager)."
-			         "Network monitoring disabled")
-	
-	def pre_insert_event (self, event, dbus_sender):
-		"""
-		On-the-fly add subject.storage to events if it is not set
-		"""
-		for subj in event.subjects:
-			if not subj.storage:
-				storage = self._find_storage(subj.uri)
-				#log.debug("Subject %s resides on %s" % (subj.uri, storage))
-				subj.storage = storage
-		return event
-	
-	def _find_storage (self, uri):
-		"""
-		Given a URI find the name of the storage medium it resides on
-		"""
-		uri_scheme = uri.rpartition("://")[0]
-		if uri_scheme in ["http", "https", "ftp", "sftp", "ssh", "mailto"]:
-			return "net"
-		elif uri_scheme == "file":
-			# Note: gio.File.find_enclosing_mount() does not behave
-			#       as documented, but throws errors when no
-			#       gio.Mount is found.
-			#       Cases where we have no mount often happens when
-			#       we are on a non-removable drive , and this is
-			#       the assumption here. We use the stora medium
-			#       'local' for this situation
-			try:
-				mount = gio.File(uri=uri).find_enclosing_mount()
-			except gio.Error:
-				return "local"
-			if mount is None: return "unknown"
-			return self._get_volume_id(mount.get_volume())
-	
-	def _on_volume_added (self, mon, volume):
-		icon = volume.get_icon()
-		if isinstance(icon, gio.ThemedIcon):
-			icon_name = icon.get_names()[0]
-		else:
-			icon_name = ""
-		self.add_storage_medium (self._get_volume_id(volume), icon_name, volume.get_name())
-	
-	def _on_volume_removed (self, mon, volume):
-		self.remove_storage_medium (self._get_volume_id(volume))
-
-	def _get_volume_id (self, volume):
-		"""
-		Get a string identifier for a gio.Volume. The id is constructed
-		as a "best effort" since we can not always uniquely identify
-		volumes, especially audio- and data CDs are problematic.
-		"""
-		volume_id = volume.get_uuid()
-		if volume_id : return volume_id
-		
-		volume_id = volume.get_identifier("uuid")
-		if volume_id : return volume_id
-		
-		volume_id = volume.get_identifier("label")
-		if volume_id : return volume_id
-		
-		volume_id = volume.get_name()
-		if volume_id : return volume_id
-		
-		return "unknown"
-		
-	def add_storage_medium (self, medium_name, icon, display_name):
-		"""
-		Mark storage medium as available in the Zeitgeist DB
-		"""
-		if isinstance(icon,gio.Icon):
-			icon = icon.to_string()
-		elif not isinstance(icon, basestring):
-			raise TypeError, "The 'icon' argument must be a gio.Icon or a string"
-		
-		log.debug("Setting storage medium %s '%s' as available" % (medium_name, display_name))
-		
-		try:
-			self._db.execute("INSERT INTO storage (value, state, icon, display_name) VALUES (?, ?, ?, ?)", (medium_name, StorageState.Available, icon, display_name))
-		except sqlite3.IntegrityError, e:
-			try:
-				self._db.execute("UPDATE storage SET state=?, icon=?, display_name=? WHERE value=?", (StorageState.Available, icon, display_name, medium_name))
-			except Exception, e:
-				log.warn("Error updating storage state for '%s': %s" % (medium_name, e))
-				return
-		
-		self._db.connection.commit()
-		
-		# Notify DBus that the storage is available
-		self.StorageAvailable(medium_name, { "available" : True,
-		                                     "icon" : icon or "",
-		                                     "display-name" : display_name or ""})
-		
-	def remove_storage_medium (self, medium_name):
-		"""
-		Mark storage medium  as `not` available in the Zeitgeist DB
-		"""
-		
-		log.debug("Setting storage medium %s as not available" % medium_name)
-		
-		try:
-			self._db.execute("INSERT INTO storage (value, state) VALUES (?, ?)", (medium_name, StorageState.NotAvailable))
-		except sqlite3.IntegrityError, e:
-			try:
-				self._db.execute("UPDATE storage SET state=? WHERE value=?", (StorageState.NotAvailable, medium_name))
-			except Exception, e:
-				log.warn("Error updating storage state for '%s': %s" % (medium_name, e))
-				return
-		
-		self._db.connection.commit()
-		
-		# Notify DBus that the storage is unavailable
-		self.StorageUnavailable(medium_name)
-	
-	@dbus.service.method(STORAGE_MONITOR_DBUS_INTERFACE,
-	                     out_signature="a(sa{sv})")
-	def GetStorages (self):
-		"""
-		Retrieve a list describing all storage media known by the Zeitgeist daemon.
-		A storage medium is identified by a key - as set in the subject
-		:const:`storage` field. For each storage id there is a dict of properties
-		that will minimally include the following: :const:`available` with a boolean
-		value, :const:`icon` a string with the name of the icon to use for the
-		storage medium, and :const:`display-name` a string with a human readable
-		name for the storage medium.
-		
-		The DBus signature of the return value of this method is :const:`a(sa{sv})`.
-		"""
-		storage_mediums = []
-		storage_data = self._db.execute("SELECT value, state, icon, display_name FROM storage").fetchall()
-		
-		for row in storage_data:
-			if not row[0] : continue
-			storage_mediums.append((row[0],
-			                       { "available" : bool(row[1]),
-			                         "icon" : row[2] or "",
-			                         "display-name" : row[3] or ""}))
-		
-		return storage_mediums
-	
-	@dbus.service.signal(STORAGE_MONITOR_DBUS_INTERFACE,
-	                     signature="sa{sv}")
-	def StorageAvailable (self, storage_id, storage_description):
-		"""
-		The Zeitgeist daemon emits this signal when the storage medium with id
-		:const:`storage_id` has become available.
-		
-		The second parameter for this signal is a dictionary containing string
-		keys and variant values. The keys that are guaranteed to be there are
-		:const:`available` with a boolean value, :const:`icon` a string with the
-		name of the icon to use for the storage medium, and :const:`display-name`
-		a string with a human readable name for the storage medium.
-		
-		The DBus signature of this signal is :const:`sa{sv}`.
-		"""
-		pass
-	
-	@dbus.service.signal(STORAGE_MONITOR_DBUS_INTERFACE,
-	                     signature="s")
-	def StorageUnavailable (self, storage_id):
-		"""
-		The Zeitgeist daemon emits this signal when the storage medium with id
-		:const:`storage_id` is no longer available.
-		
-		The DBus signature of this signal is :const:`s`.
-		"""
-		pass
-
-class NMNetworkMonitor:
-	"""
-	Checks whether there is a funtioning network interface via
-	NetworkManager (requires NM >= 0.8).
-	See http://projects.gnome.org/NetworkManager/developers/spec-08.html
-	"""
-	NM_BUS_NAME = "org.freedesktop.NetworkManager"
-	NM_IFACE = "org.freedesktop.NetworkManager"
-	NM_OBJECT_PATH = "/org/freedesktop/NetworkManager"
-	
-	NM_STATE_UNKNOWN = 0
-	NM_STATE_ASLEEP = 1
-	NM_STATE_CONNECTING = 2
-	NM_STATE_CONNECTED = 3
-	NM_STATE_DISCONNECTED = 4
-	
-	def __init__ (self, on_network_up, on_network_down):
-		log.debug("Creating NetworkManager network monitor")
-		if not callable(on_network_up):
-			raise TypeError((
-				"First argument to NMNetworkMonitor constructor "
-				"must be callable, found %s" % on_network_up))
-		if not callable(on_network_down):
-			raise TypeError((
-				"Second argument to NMNetworkMonitor constructor "
-				"must be callable, found %s" % on_network_up))
-		
-		self._up = on_network_up
-		self._down = on_network_down
-		
-		proxy = dbus.SystemBus().get_object(NetworkMonitor.NM_BUS_NAME,
-		                                    NetworkMonitor.NM_OBJECT_PATH)
-		self._props = dbus.Interface(proxy, dbus.PROPERTIES_IFACE)
-		self._nm = dbus.Interface(proxy, NetworkMonitor.NM_IFACE)
-		self._nm.connect_to_signal("StateChanged", self._on_state_changed)
-		
-		# Register the initial state
-		state = self._props.Get(NetworkMonitor.NM_IFACE, "State")
-		self._on_state_changed(state)
-		
-	def _on_state_changed(self, state):
-		log.debug("NetworkManager network state: %s" % state)
-		if state == NetworkMonitor.NM_STATE_CONNECTED:
-			self._up ()
-		else:
-			self._down()
-
-class ConnmanNetworkMonitor:
-	"""
-	Checks whether there is a funtioning network interface via Connman
-	"""
-	CM_BUS_NAME = "net.connman"
-	CM_IFACE = "net.connman.Manager"
-	CM_OBJECT_PATH = "/"
-	
-	def __init__ (self, on_network_up, on_network_down):
-		log.debug("Creating Connman network monitor")
-		if not callable(on_network_up):
-			raise TypeError((
-				"First argument to ConnmanNetworkMonitor constructor "
-				"must be callable, found %s" % on_network_up))
-		if not callable(on_network_down):
-			raise TypeError((
-				"Second argument to ConnmanNetworkMonitor constructor "
-				"must be callable, found %s" % on_network_up))
-		
-		self._up = on_network_up
-		self._down = on_network_down
-		
-		proxy = dbus.SystemBus().get_object(ConnmanNetworkMonitor.CM_BUS_NAME,
-		                                    ConnmanNetworkMonitor.CM_OBJECT_PATH)
-		self._cm = dbus.Interface(proxy, ConnmanNetworkMonitor.CM_IFACE)
-		self._cm.connect_to_signal("StateChanged", self._on_state_changed)
-		#
-		# ^^ There is a bug in some Connman versions causing it to not emit the
-		#    net.connman.Manager.StateChanged signal. We take our chances this
-		#    instance is working properly :-)
-		#
-
-		
-		# Register the initial state
-		state = self._cm.GetState()
-		self._on_state_changed(state)
-		
-	def _on_state_changed(self, state):
-		log.debug("Connman network state is '%s'" % state)
-		if state == "online":
-			self._up ()
-		else:
-			self._down()

=== modified file '_zeitgeist/engine/main.py'
--- _zeitgeist/engine/main.py	2011-03-23 18:21:08 +0000
+++ _zeitgeist/engine/main.py	2011-03-23 18:21:09 +0000
@@ -640,7 +640,7 @@
 						None, # event origin
 						payload_id,
 						subject.uri,
-						subject.uri,
+						subject.uri_current,
 						self._interpretation[subject.interpretation],
 						self._manifestation[subject.manifestation],
 						subject.origin,
@@ -664,6 +664,12 @@
 					self._actor[event.actor]))
 			return self._cursor.fetchone()[0]
 		
+		if event.interpretation == Interpretation.MOVE_EVENT:
+			for subject in event.subjects:
+				self._cursor.execute("""
+					UPDATE event SET subject_id_current=? WHERE subject_id_current=?
+					""", (subject.uri_current, subject.uri))
+		
 		return id
 	
 	def _store_payload (self, event):

=== modified file '_zeitgeist/engine/sql.py'
--- _zeitgeist/engine/sql.py	2011-03-23 18:21:08 +0000
+++ _zeitgeist/engine/sql.py	2011-03-23 18:21:09 +0000
@@ -392,11 +392,15 @@
 				event.interpretation,
 				event.manifestation,
 				event.actor,
+				event.origin,
 				(SELECT value FROM payload WHERE payload.id=event.payload)
 					AS payload,
 				(SELECT value FROM uri WHERE uri.id=event.subj_id)
 					AS subj_uri,
 				event.subj_id, -- #this directly points to an id in the uri table
+				(SELECT value FROM uri WHERE uri.id=event.subj_id_current)
+					AS subj_uri_current,
+				event.subj_id_current, -- #this directly points to an id in the uri table
 				event.subj_interpretation,
 				event.subj_manifestation,
 				event.subj_origin,

=== modified file 'extra/ontology/zg.trig'
--- extra/ontology/zg.trig	2011-02-09 11:18:39 +0000
+++ extra/ontology/zg.trig	2011-03-23 18:21:09 +0000
@@ -47,7 +47,7 @@
         a rdfs:Class ;
         rdfs:comment "Event triggered because a resource has been deleted or otherwise made permanently unavailable. Fx. when deleting a file. FIXME: How about when moving to trash?" ;
         rdfs:subClassOf zg:EventInterpretation .
-        
+    
     zg:ReceiveEvent
         a rdfs:Class ;
         rdfs:comment "Event triggered when something is received from an external party. The event manifestation must be set according to the world view of the receiving party. Most often the item that is being received will be some sort of message - an email, instant message, or broadcasted media such as micro blogging" ;
@@ -72,6 +72,12 @@
         a rdfs:Class ;
         rdfs:comment "Event triggered when something expires or times out. These types of events are normally not triggered by the user, but by the operating system or some external party. Examples are a recurring calendar item or task deadline that expires or a when the user fails to respond to an external request such as a phone call" ;
         rdfs:subClassOf zg:EventInterpretation .
+        
+    zg:MoveEvent
+        a rdfs:Class ;
+        rdfs:comment "Event triggered when a resource has been moved from a location to another. Fx. moving a file from a folder to another.";
+        rdfs:subClassOf zg:EventInterpretation.
+        
     
 # manifestations
     

=== modified file 'zeitgeist/datamodel.py'
--- zeitgeist/datamodel.py	2011-01-17 15:54:47 +0000
+++ zeitgeist/datamodel.py	2011-03-23 18:21:09 +0000
@@ -440,15 +440,16 @@
 	create new subjects.
 	"""
 	Fields = (Uri,
+		UriCurrent,
 		Interpretation,
 		Manifestation,
 		Origin,
 		Mimetype,
 		Text,
-		Storage) = range(7)
+		Storage) = range(8)
 		
-	SUPPORTS_NEGATION = (Uri, Interpretation, Manifestation, Origin, Mimetype)
-	SUPPORTS_WILDCARDS = (Uri, Origin, Mimetype)
+	SUPPORTS_NEGATION = (Uri, UriCurrent, Interpretation, Manifestation, Origin, Mimetype)
+	SUPPORTS_WILDCARDS = (Uri, UriCurrent, Origin, Mimetype)
 	
 	def __init__(self, data=None):
 		super(Subject, self).__init__([""]*len(Subject.Fields))
@@ -473,6 +474,7 @@
 		to the keyword arguments passed to this method.
 		
 		:param uri: The URI of the subject. Eg. *file:///tmp/ratpie.txt*
+		:param uri_current: The current URI of the subject. Eg. *file:///tmp/ratpie.txt*
 		:param interpretation: The interpretation type of the subject, given either as a string URI or as a :class:`Interpretation` instance
 		:param manifestation: The manifestation type of the subject, given either as a string URI or as a :class:`Manifestation` instance
 		:param origin: The URI of the location where subject resides or can be found
@@ -482,7 +484,7 @@
 		"""
 		self = Subject()
 		for key, value in values.iteritems():
-			if not key in ("uri", "interpretation", "manifestation", "origin",
+			if not key in ("uri", "uri_current", "interpretation", "manifestation", "origin",
 						"mimetype", "text", "storage"):
 				raise ValueError("Subject parameter '%s' is not supported" %key)
 			setattr(self, key, value)
@@ -495,6 +497,14 @@
 		self[Subject.Uri] = value
 	uri = property(get_uri, set_uri,
 	doc="Read/write property with the URI of the subject encoded as a string")
+	
+	def get_uri_current(self):
+		return self[Subject.Uri]
+		
+	def set_uri_current(self, value):
+		self[Subject.UriCurrent] = value
+	uri_current = property(get_uri_current, set_uri_current,
+	doc="Read/write property with the UriCurrent of the subject encoded as a string")
 		
 	def get_interpretation(self):
 		return self[Subject.Interpretation]
@@ -602,9 +612,10 @@
 		Timestamp,
 		Interpretation,
 		Manifestation,
-		Actor) = range(5)
+		Actor,
+		Origin) = range(6)
 		
-	SUPPORTS_NEGATION = (Interpretation, Manifestation, Actor)
+	SUPPORTS_NEGATION = (Interpretation, Manifestation, Actor, Origin)
 	SUPPORTS_WILDCARDS = (Actor,)
 	
 	def __init__(self, struct = None):
@@ -685,7 +696,8 @@
 		:param timestamp: Event timestamp in milliseconds since the Unix Epoch 
 		:param interpretaion: The Interpretation type of the event
 		:param manifestation: Manifestation type of the event
-		:param actor: The actor (application) that triggered the event
+		:param actor: The actor (application) that triggered the event		
+		:param origin: The origin (domain) where the event was triggered
 		:param subjects: A list of :class:`Subject` instances
 		
 		Instead of setting the *subjects* argument one may use a more
@@ -706,7 +718,7 @@
 		self = cls()
 		for key in values:
 			if not key in ("timestamp", "interpretation", "manifestation",
-				"actor", "subjects", "subject_uri", "subject_interpretation",
+				"actor", "origin", "subjects", "subject_uri", "subject_interpretation",
 				"subject_manifestation", "subject_origin", "subject_mimetype",
 				"subject_text", "subject_storage"):
 				raise ValueError("Event parameter '%s' is not supported" % key)
@@ -715,6 +727,7 @@
 		self.interpretation = values.get("interpretation", "")
 		self.manifestation = values.get("manifestation", "")
 		self.actor = values.get("actor", "")
+		self.actor = values.get("origin", "")
 		self.subjects = values.get("subjects", self.subjects)
 		
 		if self._dict_contains_subject_keys(values):
@@ -803,6 +816,14 @@
 	actor = property(get_actor, set_actor,
 	doc="Read/write property defining the application or entity responsible for emitting the event. For applications the format of this field is base filename of the corresponding .desktop file with an `app://` URI scheme. For example `/usr/share/applications/firefox.desktop` is encoded as `app://firefox.desktop`")
 	
+	def get_origin(self):
+		return self[0][Event.Origin]
+	
+	def set_origin(self, value):
+		self[0][Event.Origin] = value
+	origin = property(get_origin, set_origin,
+	doc="Read/write property defining the origin where the event was emitted.")
+	
 	def get_payload(self):
 		return self[2]
 	

_______________________________________________
Mailing list: https://launchpad.net/~zeitgeist
Post to     : zeitgeist@lists.launchpad.net
Unsubscribe : https://launchpad.net/~zeitgeist
More help   : https://help.launchpad.net/ListHelp

Reply via email to