[conduit/tracker: 145/179] Sync contacts to tracker



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]