[conduit/tracker: 145/179] Sync contacts to tracker
- From: John Carr <johncarr src gnome org>
- To: svn-commits-list gnome org
- Subject: [conduit/tracker: 145/179] Sync contacts to tracker
- Date: Tue, 9 Jun 2009 06:47:50 -0400 (EDT)
commit 712e0aa5b49030efe40350a603aa42f4861ed505
Author: John Carr <john carr unrouted co uk>
Date: Wed May 13 17:49:39 2009 +0100
Sync contacts to tracker
---
conduit/modules/TrackerModule/TrackerModule.py | 110 ++++++++++
.../modules/TrackerModule/tralchemy/__init__.py | 5 +
conduit/modules/TrackerModule/tralchemy/core.py | 215 ++++++++++++++++++++
.../modules/TrackerModule/tralchemy/namespace.py | 69 +++++++
4 files changed, 399 insertions(+), 0 deletions(-)
diff --git a/conduit/modules/TrackerModule/TrackerModule.py b/conduit/modules/TrackerModule/TrackerModule.py
new file mode 100644
index 0000000..c9d10b8
--- /dev/null
+++ b/conduit/modules/TrackerModule/TrackerModule.py
@@ -0,0 +1,110 @@
+from gettext import gettext as _
+import logging
+log = logging.getLogger("modules.Tracker")
+
+import conduit
+import conduit.dataproviders.DataProvider as DataProvider
+import conduit.utils as Utils
+import conduit.Exceptions as Exceptions
+import conduit.datatypes.Contact as Contact
+
+Utils.dataprovider_add_dir_to_path(__file__)
+import tralchemy
+from tralchemy.nco import Contact as TrackerContact
+
+MODULES = {
+ "TrackerContacts": { "type": "dataprovider" },
+}
+
+class TrackerContacts(DataProvider.TwoWay):
+
+ _name_ = _("Tracker Contacts")
+ _description_ = _("Synchronize your contacts")
+ _category_ = conduit.dataproviders.CATEGORY_OFFICE
+ _module_type_ = "twoway"
+ _in_type_ = "contact"
+ _out_type_ = "contact"
+ _icon_ = "x-office-address-book"
+
+ def __init__(self):
+ DataProvider.TwoWay.__init__(self)
+
+ def refresh(self):
+ DataProvider.TwoWay.refresh(self)
+ self.contacts = {}
+ for contact in Contact.get():
+ self.contacts[contact.uri] = contact
+
+ def get_all(self):
+ DataProvider.TwoWay.get_all(self)
+ return self.contacts.keys()
+
+ def get(self, LUID):
+ DataProvider.TwoWay.get(self, LUID)
+ tc = self.contact[LUID]
+ c = self._tracker_to_vcard(tc)
+ c.set_UID(LUID)
+ return c
+
+ def put(self, obj, overwrite, LUID=None):
+ DataProvider.TwoWay.put(self, obj, overwrite, LUID)
+ if LUID != None:
+ self.delete(LUID)
+ c = self._vcard_to_tracker(obj)
+ c.commit()
+
+ def delete(self, LUID):
+ if LUID in self.contacts:
+ self.contacts[LUID].delete()
+
+ def finish(self, aborted, error, conflict):
+ DataProvider.TwoWay.finish(self)
+ self.contacts = None
+
+ def _vcard_to_tracker(self, data):
+ vcard = data.vcard
+
+ c = TrackerContact()
+
+ for k, v in vcard.iteritems():
+ if k == "account":
+ elif k == "tel":
+ elif k == "bday":
+ elif k == "n":
+ c.namefamily = v.value.family
+ c.namegiven = v.value.given
+ c.nameadditional = v.value.additional
+ c.namehonorificprefix = v.value.prefix
+ c.namehonorificsuffix = v.value.suffix
+ elif k == "version":
+ elif k == "org":
+ elif k == "nickname":
+ c.nickname = v
+ elif k == "email":
+ elif k == "fn":
+ c.fullname = v
+ else:
+ log.warning("Unhandled key: %s" % k)
+
+ return c
+
+ def _tracker_to_vcard(self, tracker):
+ c = Contact.Contact()
+
+ if tracker.fullname:
+ c.vcard.('fn') = tracker.fullname
+
+ if tracker.nickname:
+ c.vcard.add('nickname') = tracker.nickname
+
+ if tracker.namefamily or tracker.namegiven or tracker.nameadditional or
+ tracker.namehonorificprefix or tracker.namehonorificsuffix:
+ n = vobject.vcard.Name(family=tracker.namefamily, given=tracker.namegiven, additional=tracker.nameadditional,
+ prefix=tracker.namehonorificprefix, suffix=tracker.namehonorificsuffix)
+ c.add('n') = n
+
+ return c
+
+ def get_UID(self):
+ return ""
+
diff --git a/conduit/modules/TrackerModule/__init__.py b/conduit/modules/TrackerModule/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/conduit/modules/TrackerModule/tralchemy/__init__.py b/conduit/modules/TrackerModule/tralchemy/__init__.py
new file mode 100644
index 0000000..acbde4b
--- /dev/null
+++ b/conduit/modules/TrackerModule/tralchemy/__init__.py
@@ -0,0 +1,5 @@
+
+from .namespace import install_importhook
+install_importhook()
+del install_importhook
+
diff --git a/conduit/modules/TrackerModule/tralchemy/core.py b/conduit/modules/TrackerModule/tralchemy/core.py
new file mode 100644
index 0000000..39bb1b2
--- /dev/null
+++ b/conduit/modules/TrackerModule/tralchemy/core.py
@@ -0,0 +1,215 @@
+
+import dbus
+import uuid
+
+bus = dbus.SessionBus()
+tracker_obj = bus.get_object("org.freedesktop.Tracker", "/org/freedesktop/Tracker/Resources")
+tracker = dbus.Interface(tracker_obj, "org.freedesktop.Tracker.Resources")
+
+# Map tracker prefixes to ontology namespaces and back
+prefix_to_ns = {}
+ns_to_prefix = {}
+for prefix, namespace in tracker.SparqlQuery("SELECT ?prefix, ?ns WHERE { ?ns tracker:prefix ?prefix }"):
+ prefix_to_ns[prefix] = namespace
+ ns_to_prefix[namespace] = prefix
+
+def get_classname(classname):
+ """ Takes a classname and flattens it into tracker form """
+ if classname.startswith("http://"):
+ for ns, prefix in ns_to_prefix.iteritems():
+ if classname.startswith(ns):
+ return prefix + ":" + classname[len(ns):]
+ return classname
+
+
+class Notifications(object):
+ """ Class to allow users to attach signals to a class of object """
+ def __init__(self, uri):
+ self.uri = uri.replace(":", "/")
+
+ def connect(self, signal, callback):
+ def _(subjects):
+ subjects = [str(subject) for subject in subjects]
+ return callback(subjects)
+ bus.add_signal_receiver (_, signal_name=signal,
+ dbus_interface="org.freedesktop.Tracker.Resources.Class",
+ path="/org/freedesktop/Tracker/Resources/Classes/%s" % self.uri)
+
+
+class Resource(object):
+ """ Everything is a resource """
+
+ _type_ = "rdfs:Resource"
+
+ def __init__(self, uri):
+ self.uri = get_classname(uri)
+ self.triples = {}
+
+ @classmethod
+ def get(cls, **kwargs):
+ fragment = ""
+ for key, value in kwargs.iteritems():
+ key = getattr(cls, key).uri
+ fragment += " . ?o %s %s" % (key, value)
+
+ results = tracker.SparqlQuery("SELECT ?o WHERE { ?o rdf:type %s %s}" % (cls._type_, fragment))
+ for result in results:
+ classname = result[0]
+ classname = get_classname(classname)
+ yield cls(classname)
+
+ @classmethod
+ def create(cls, **kwargs):
+ o = cls(kwargs.get('uid', 'http://localhost/resource/%s' % str(uuid.uuid4())))
+ for k, v in kwargs.iteritems():
+ if k == "uid" or k =="commit":
+ continue
+ setattr(o, k, v)
+ if not 'commit' in kwargs or kwargs['commit'] == True:
+ o.commit()
+ return o
+
+ def delete(self):
+ tracker.SparqlUpdate("DELETE { <%s> a %s. }" % (self.uri, self._type_))
+
+ def commit(self):
+ query = "INSERT { <%s> a %s" % (self.uri, self._type_)
+ for k, v in self.triples.iteritems():
+ query += " ; %s %s" % (k, v)
+ query += " . }"
+ tracker.SparqlUpdate(query)
+ self.triples = {}
+
+
+class Property(Resource, property):
+
+ _type_ = "rdf:Property"
+
+ def __init__(self, uri, doc=""):
+ super(Property, self).__init__(uri)
+ if self.uri != 'rdfs:comment' and self.comment:
+ self.__doc__ = "%s\n\n type: %s" % (self.comment, get_classname(self.range))
+
+ def __get__(self, instance, instance_type):
+ if instance is None:
+ return self
+
+ results = tracker.SparqlQuery("SELECT ?v WHERE { %s %s ?v }" % (instance.uri, self.uri))
+ for result in results:
+ #FIXME: What to do about lists of stuff. What to do about stuff that isnt a string.
+ result = result[0]
+ if self.uri == "rdfs:range":
+ return str(result)
+ else:
+ return types.get_class(self.range)(result)
+
+ def __set__(self, instance, value):
+ if instance is None:
+ return
+ self.triples[self._type_] = value
+
+ def __delete__(self, instance):
+ pass
+
+
+# Now Resource and Property exist, monkey patch them with some properties
+Resource.comment = Property("rdfs:comment")
+Resource.label = Property("rdfs:label")
+Resource.type = Property("rdf:type")
+
+Property.domain = Property("rdfs:domain")
+Property.subpropertyof = Property("rdfs:subPropertyOf")
+Property.range = Property("rdfs:range")
+Property.indexed = Property("tracker:indexed")
+Property.fulltextindexed = Property("tracker:fulltextIndexed")
+Property.transient = Property("tracker:transient")
+
+
+class Class(Resource):
+
+ _type_ = "rdfs:Class"
+
+ subclassof = Property("rdfs:subClassOf")
+ notify = Property("tracker:notify")
+
+
+
+class WrapperFactory(object):
+
+ def __init__(self):
+ # Cache previous generated objects
+ self.wrapped = {}
+
+ # Connection to tracker
+ self.bus = dbus.SessionBus()
+ tracker_object = self.bus.get_object("org.freedesktop.Tracker", "/org/freedesktop/Tracker/Resources")
+ self.tracker = dbus.Interface(tracker_object, "org.freedesktop.Tracker.Resources")
+
+ for cls in (Class, Property, Resource):
+ self.wrapped[cls._type_] = cls
+
+ self.wrapped["xsd:boolean"] = lambda x: x=='true'
+ self.wrapped["xsd:integer"] = int
+ self.wrapped["xsd:double"] = float
+ self.wrapped["rdfs:Literal"] = str
+ self.wrapped["xsd:string"] = str
+ self.wrapped['xsd:date'] = str
+ self.wrapped['xsd:dateTime'] = str
+
+ def get_class(self, classname):
+ classname = get_classname(classname)
+
+ if classname in self.wrapped:
+ return self.wrapped[classname]
+
+ # Look up this class in tracker
+ cls = Class(classname)
+
+ attrs = {
+ "_type_": cls.uri,
+ "__doc__": cls.comment or ""
+ }
+
+ # FIXME: subclassof should return an object!!
+ baseclass = cls.subclassof
+ if baseclass:
+ baseclass = [self.get_class(baseclass.uri)]
+ else:
+ baseclass = [Resource]
+
+ # Does this class have notifications?
+ if cls.notify:
+ attrs['notifications'] = Notifications(cls.uri)
+
+ # Enumerate all properties of this class
+ for prop in Property.get(domain=cls.uri):
+ if prop.label:
+ attrs[prop.label.lower().replace(" ", "_")] = prop
+
+ # Make a new class
+ klass = type(str(classname), tuple(baseclass), attrs)
+
+ # Cache it for later
+ self.wrapped[klass._type_] = klass
+
+ return klass
+
+types = WrapperFactory()
+
+if __name__ == "__main__":
+ #help(w.get_class("rdfs:Resource"))
+ #help(w.get_class("rdfs:Class"))
+ self = __import__(__name__)
+ for cls in Class.get():
+ cls = types.get_class(cls.uri)
+ setattr(self, cls.__name__, cls)
+ help(self)
+
+ foo = {}
+ for p in Property.get():
+ foo.setdefault(p.range, 0)
+ foo[p.range] += 1
+
+ for k, v in foo.items():
+ print k, v
+
diff --git a/conduit/modules/TrackerModule/tralchemy/namespace.py b/conduit/modules/TrackerModule/tralchemy/namespace.py
new file mode 100644
index 0000000..8204a37
--- /dev/null
+++ b/conduit/modules/TrackerModule/tralchemy/namespace.py
@@ -0,0 +1,69 @@
+
+import os, sys
+
+
+class Namespace(object):
+ """ Class representing a tracker namespace """
+
+ def __init__(self, namespace, path):
+ self._namespace = namespace
+ self._path = path
+
+ @property
+ def __file__(self):
+ return self._namespace
+
+ @property
+ def __name__(self):
+ return self._namespace
+
+ @property
+ def __path__(self):
+ return [os.path.dirname(self.__file__)]
+
+ def __repr__(self):
+ return "<namespace %r from %r>" % (self._namespace, self._path)
+
+ def __getattr__(self, name):
+ from .core import WrapperFactory
+ cls = WrapperFactory().get_class("%s:%s" % (self._namespace, name))
+ if not cls:
+ raise AttributeError("%r object has no attribute %r" % (
+ self.__class__.__name__, name))
+
+ self.__dict__[name] = cls
+ return value
+
+ @property
+ def __members__(self):
+ r = []
+ #FIXME: Return names of all objects defined in this namespace
+ return r
+
+
+class Importer(object):
+ """ Class to register with python so we can dynamically generate modules """
+
+ def __init__(self, name, path):
+ self.name = name
+ self.path = path
+
+ @staticmethod
+ def find_module(name, path=None):
+ #FIXME: This function is a bit of a hack
+ if not name.startswith("tralchemy."):
+ return None
+ name = name[10:]
+ if name in ('namespace', 'core', 'dbus', 'uuid'):
+ return None
+ if '.' in name:
+ return None
+ return Importer(name, path)
+
+ def load_module(self, name):
+ return Namespace(self.name, self.path)
+
+
+def install_importhook():
+ sys.meta_path.append(Importer)
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]