[tracker] Adding demo app



commit dfa334ec453f355989965bf91bcb9d5976ba0e98
Author: Ivan Frade <ifrade src gnome org>
Date:   Thu Apr 9 09:52:43 2009 +0200

    Adding demo app
---
 python/rss_reader/README             |   25 ++++
 python/rss_reader/initial-data.ttl   |   19 +++
 python/rss_reader/main.py            |  244 ++++++++++++++++++++++++++++++++++
 python/rss_reader/signaler.py        |  151 +++++++++++++++++++++
 python/rss_reader/tracker_backend.py |  153 +++++++++++++++++++++
 5 files changed, 592 insertions(+), 0 deletions(-)

diff --git a/python/rss_reader/README b/python/rss_reader/README
new file mode 100644
index 0000000..9082447
--- /dev/null
+++ b/python/rss_reader/README
@@ -0,0 +1,25 @@
+RSS reader application
+======================
+
+Example of an application using the tracker SparQL interface to query
+data and set other data.
+
+To simulate the incoming of new RSS entries, there is a helper utility
+to generate a random RSS Message and saving it into tracker.
+
+
+The programs
+============
+
+./main.py : A tree view that loads the data from tracker. It connects
+to the signal "Change in RSS Messages", so can act to it.
+
+./signaller.py: Small app that generates a random post and insert it
+into tracker (using the sparql interface).
+
+**** The feed channel "planet-maemo" need to be registered in tracker
+     before using signaller. So the first time you initialize tracker
+     with -r or remove the DBs, you need to load the file
+     initial-data.ttl. For instance with this command:
+
+dbus-send --print-reply --dest=org.freedesktop.Tracker /org/freedesktop/Tracker/Metadata org.freedesktop.Tracker.Metadata.Import string:"/x/y/z/initial-data.ttl"
diff --git a/python/rss_reader/initial-data.ttl b/python/rss_reader/initial-data.ttl
new file mode 100644
index 0000000..cde3e9d
--- /dev/null
+++ b/python/rss_reader/initial-data.ttl
@@ -0,0 +1,19 @@
+ prefix rdf: <http://www.w3.org/2000/01/rdf-schema#>.
+ prefix nrl: <http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#>.
+ prefix nid3: <http://www.semanticdesktop.org/ontologies/2007/05/10/nid3#>.
+ prefix nao: <http://www.semanticdesktop.org/ontologies/2007/08/15/nao#>.
+ prefix nco: <http://www.semanticdesktop.org/ontologies/2007/03/22/nco#>.
+ prefix nmo: <http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#>.
+ prefix nfo: <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#>.
+ prefix nie: <http://www.semanticdesktop.org/ontologies/2007/01/19/nie#>.
+ prefix ncal: <http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#>.
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
+
+<http://maemo.org/news/planet-maemo/atom.xml> a nmo:FeedChannel;
+        nie:title "Planet Maemo";
+        nie:description "Blog entries from Maemo community";
+        nie:contentLastModified "2009-03-11T20:20:55+00:00".
+
+<http://planet.gnome.org/atom.xml> a nmo:FeedChannel;
+        nie:title "Planet GNOME";
+        nie:contentLastModified "2009-03-11T18:29:08+00:00".
diff --git a/python/rss_reader/main.py b/python/rss_reader/main.py
new file mode 100755
index 0000000..c38247a
--- /dev/null
+++ b/python/rss_reader/main.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python2.5
+#
+# Demo RSS client using tracker as backend
+# Copyright (C) 2009 Nokia <urho konttori nokia com>
+#
+# This library 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 library 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 library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+#
+import gtk
+import gobject
+import pango
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+from tracker_backend import TrackerRSS
+
+# uri, title, date, isread, pango.Weight
+posts_model = gtk.ListStore (str, str, str, bool, int)
+
+# uri, title, entries, enabled
+channels_model = gtk.ListStore (str, str, str, bool)
+
+posts_treeview = None
+channels_treeview = None
+
+sources_dialog = None
+
+# Convenience defines
+FALSE = "false"
+TRUE = "true"
+NORMAL = pango.WEIGHT_NORMAL
+BOLD = pango.WEIGHT_BOLD
+
+tracker = TrackerRSS ()
+
+def populate_initial_posts ():
+    results = tracker.get_post_sorted_by_date (10)
+    
+    for result in results:
+        if (result[3] == FALSE):
+            row = (result[0], result[1], result[2], False, BOLD)
+        else:
+            row = (result[0], result[1], result[2], True, NORMAL)
+            
+        posts_model.append (row)
+
+def create_posts_tree_view ():
+    treeview = gtk.TreeView ()
+    column_title = gtk.TreeViewColumn ("Title",
+                                       gtk.CellRendererText (),
+                                       text=1, weight=4)
+    column_date = gtk.TreeViewColumn ("Date",
+                                       gtk.CellRendererText (),
+                                       text=2)
+
+    treeview.append_column (column_title)
+    treeview.append_column (column_date)
+
+    return treeview
+
+def create_channels_tree_view ():
+    treeview = gtk.TreeView ()
+    toggle = gtk.CellRendererToggle ()
+    toggle.set_property ("activatable", True)
+    def toggled (widget, path):
+        it = channels_model.get_iter_from_string (path)
+        uri, old_value = channels_model.get (it, 0, 3)
+        if (old_value == True):
+            tracker.mark_as_invisible (uri)
+        else:
+            tracker.mark_as_visible (uri)
+        channels_model.set (it, 3, not old_value)
+    toggle.connect ("toggled",
+                    toggled)
+    column_enable = gtk.TreeViewColumn ("Enabled",
+                                        toggle,
+                                        active=3)
+    column_title = gtk.TreeViewColumn ("Title",
+                                       gtk.CellRendererText (),
+                                       text=1)
+    column_entries = gtk.TreeViewColumn ("Entries",
+                                         gtk.CellRendererText (),
+                                         text=2)
+    treeview.append_column (column_enable)
+    treeview.append_column (column_title)
+    treeview.append_column (column_entries)
+    return treeview
+    
+def toggle_row_foreach (treemodel, path, iter):
+    uri, readed, text = treemodel.get (iter, 0, 3 ,1)
+    if (readed):
+        #Mark as unread
+        treemodel.set_value (iter, 3, False)
+        treemodel.set_value (iter, 4, BOLD)
+        tracker.set_is_read (uri, False)
+    else:
+        #Mark as readed
+        treemodel.set_value (iter, 3, True)
+        treemodel.set_value (iter, 4, NORMAL)
+        tracker.set_is_read (uri, True)
+
+def clicked_toggle_cb (widget):
+    selected = posts_treeview.get_selection ()
+    if (selected.count_selected_rows () == 0):
+        return
+
+    selected.selected_foreach (toggle_row_foreach)
+
+def clicked_sources_cb (widget, dialog):
+    # Dont do this all the time!
+    if (len (channels_model) == 0):
+        feeds = tracker.get_all_subscribed_feeds ()
+        for (uri, name, value, visible) in feeds:
+            channels_model.append ((uri, name, value, visible))
+
+    channels_treeview.show_all ()
+    retval = dialog.run ()
+    if (retval == gtk.RESPONSE_ACCEPT):
+        pass
+    
+    dialog.hide ()
+
+def notification_addition (added_uris):
+    print "Received signal"
+    print "%d add: %s" % (len(added_uris), [str(n) for n in added_uris])
+    for uri in added_uris:
+        details = tracker.get_info_for_entry (uri)
+        if (details):
+            if (details[2]):
+                model_row = (uri, details[0], details[1],
+                             details[2], NORMAL)
+            else:
+                model_row = (uri, details[0], details[1],
+                             details[2], BOLD)
+            posts_model.prepend (model_row)
+
+def remove_uris (model, path, iter, user_data):
+    uri = model.get (iter, 0)
+    if (uri[0] in user_data):
+        model.remove (iter)
+        return True
+
+def notification_removal (removed_uris):
+    print "Received signal"
+    print "%d remove: %s" % (len(removed_uris), [str(n) for n in removed_uris])
+    posts_model.foreach (remove_uris, removed_uris)
+
+def update_uri (model, path, iter, user_data):
+    updated_uri = user_data [0]
+    uri = model.get (iter, 0)
+    print "Comparing ", uri[0], "with", updated_uri
+    if (uri[0] == updated_uri):
+        print "Setting new values", user_data
+        model.set(iter,
+                  1, user_data[1],
+                  2, user_data[2],
+                  3, user_data[3],
+                  4, user_data[4])
+        return True
+    
+
+def notification_update (updated_uris):
+    print "Received signal"
+    print "%d update: %s" % (len(updated_uris), [str(n) for n in updated_uris])
+    for uri in updated_uris:
+        details = tracker.get_info_for_entry (uri)
+        if (details):
+            if (details[2]):
+                model_row = (uri, details[0], details[1],
+                             details[2], NORMAL)
+            else:
+                model_row = (uri, details[0], details[1],
+                             details[2], BOLD)
+            posts_model.foreach (update_uri, model_row)
+
+if __name__ == "__main__":
+
+    dbus_loop = DBusGMainLoop(set_as_default=True)
+
+    bus = dbus.SessionBus (dbus_loop)
+    bus.add_signal_receiver (notification_addition,
+                             signal_name="SubjectsAdded",
+                             dbus_interface="org.freedesktop.Tracker.Resources.Class",
+                             path="/org/freedesktop/Tracker/Resources/Classes/nmo/FeedMessage")
+
+    bus.add_signal_receiver (notification_removal,
+                             signal_name="SubjectsRemoved",
+                             dbus_interface="org.freedesktop.Tracker.Resources.Class",
+                             path="/org/freedesktop/Tracker/Resources/Classes/nmo/FeedMessage")
+
+    bus.add_signal_receiver (notification_update,
+                             signal_name="SubjectsChanged",
+                             dbus_interface="org.freedesktop.Tracker.Resources.Class",
+                             path="/org/freedesktop/Tracker/Resources/Classes/nmo/FeedMessage")
+
+    gobject.set_application_name ("Rss/tracker")
+    window = gtk.Window ()
+    
+    vbox = gtk.VBox ()
+    posts_treeview = create_posts_tree_view ()
+    posts_treeview.set_model (posts_model)
+    vbox.add (posts_treeview)
+
+    channels_treeview = create_channels_tree_view ()
+    channels_treeview.set_model (channels_model)
+    
+    dialog = gtk.Dialog ("Sources", window,
+                         gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+                         (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+                          gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+    dialog.vbox.add (channels_treeview)
+
+
+    button_box = gtk.HBox ()
+    button_toggle = gtk.Button (label="Read/Unread")
+    button_toggle.connect ("clicked", clicked_toggle_cb)
+    button_box.add (button_toggle)
+    button_sources = gtk.Button (stock=gtk.STOCK_HOME)
+    button_sources.connect ("clicked", clicked_sources_cb, dialog)
+    button_box.add (button_sources)
+    
+    vbox.add (button_box)
+    window.add (vbox)
+
+    populate_initial_posts ()
+
+    window.show_all ()
+    def destroy (window):
+        tracker.flush_to_file ()
+        gtk.main_quit ()
+    
+    window.connect ("destroy", destroy)
+    gtk.main ()
diff --git a/python/rss_reader/signaler.py b/python/rss_reader/signaler.py
new file mode 100755
index 0000000..3ce53ea
--- /dev/null
+++ b/python/rss_reader/signaler.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python2.5
+#
+# Demo RSS provider simulator 
+# Copyright (C) 2009 Nokia <urho konttori nokia com>
+#
+# This library 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 library 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 library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+#
+import gobject
+import gtk
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+import datetime, random
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+# We are not inserting content, nor contributor!
+INSERT_SPARQL = """
+INSERT {
+<%s> a nmo:FeedMessage ;
+ nie:contentLastModified "%s" ;
+ nmo:communicationChannel <http://maemo.org/news/planet-maemo/atom.xml>;
+ nie:title "%s".
+ }
+"""
+
+DELETE_SPARQL = """
+  DELETE { <%s> a nmo:FeedMessage. }
+"""
+
+class SignalerUI (gtk.Window):
+
+    def __init__ (self):
+        gtk.Window.__init__ (self)
+
+        bus = dbus.SessionBus ()
+        tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+        self.iface = dbus.Interface (tracker,
+                                     "org.freedesktop.Tracker.Resources")
+
+        vbox = gtk.VBox ()
+    
+        # Post frame
+        post_frame = gtk.Frame ("Post")
+        post_frame_vbox = gtk.VBox ()
+        post_frame.add (post_frame_vbox)
+        # Title
+        title_label = gtk.Label ("Title")
+        self.title_entry = gtk.Entry()
+        title_hbox = gtk.HBox ()
+        title_hbox.add (title_label)
+        title_hbox.add (self.title_entry)
+        post_frame_vbox.add (title_hbox)
+
+        uri_label = gtk.Label ("Uri")
+        self.uri_entry = gtk.Entry ()
+        hbox_uri = gtk.HBox ()
+        hbox_uri.add (uri_label)
+        hbox_uri.add (self.uri_entry)
+        #self.uri_entry.set_property ("editable", False)
+        post_frame_vbox.add (hbox_uri)
+        date_label = gtk.Label ("Date")
+        self.date_entry = gtk.Entry ()
+        self.date_entry.set_property ("editable", False)
+        date_hbox = gtk.HBox ()
+        date_hbox.add (date_label)
+        date_hbox.add (self.date_entry)
+        post_frame_vbox.add (date_hbox)
+        button_gen = gtk.Button (stock=gtk.STOCK_NEW)
+        button_gen.connect ("clicked", self.gen_new_post_cb)
+        post_frame_vbox.add (button_gen)
+        
+        vbox.add (post_frame)
+
+
+        button_new = gtk.Button (stock=gtk.STOCK_ADD)
+        button_new.connect ("clicked", self.clicked_add_cb)
+        vbox.add (button_new)
+
+        button_remove = gtk.Button (stock=gtk.STOCK_REMOVE)
+        button_remove.connect ("clicked", self.clicked_remove_cb)
+        vbox.add (button_remove)
+
+        self.add (vbox)
+        self.connect ("destroy", gtk.main_quit)
+        self.show_all ()
+
+        self.last_inserted = None
+
+        gtk.main ()
+
+
+    def clicked_add_cb (self, widget):
+        date = self.date_entry.get_text ()
+        uri = self.uri_entry.get_text ()
+        title = self.title_entry.get_text ()
+
+        if (not date or (len(date) == 0)):
+            pass
+        if (not uri or (len(uri) == 0)):
+            pass
+        if (not title or (len(title) == 0)):
+            pass
+
+        if (uri == self.last_inserted):
+            print "Generate a new URI!"
+            return
+        
+        sparql_insert = INSERT_SPARQL % (uri, date, title)
+        print sparql_insert
+
+        self.iface.SparqlUpdate (sparql_insert)
+        self.last_inserted = uri
+
+    def clicked_remove_cb (self, widget):
+        uri = self.uri_entry.get_text ()
+        if (not uri or (len(uri) == 0)):
+            pass
+        sparql_delete = DELETE_SPARQL % (uri)
+        print sparql_delete
+        
+        self.iface.SparqlUpdate (sparql_delete)
+
+    def gen_new_post_cb (self, widget):
+        today = datetime.datetime.today ()
+        self.date_entry.set_text (today.isoformat ().split('.')[0] + "+00:00")
+        post_no = str(random.randint (100, 1000000))
+        self.uri_entry.set_text ("http://test.maemo.org/feed/"; + post_no)
+        self.title_entry.set_text ("Title %s" % (post_no))
+
+if __name__ == "__main__":
+
+    DBusGMainLoop(set_as_default=True)
+    gobject.set_application_name ("Feeds engine/signals simulator")
+
+    SignalerUI ()
+    
diff --git a/python/rss_reader/tracker_backend.py b/python/rss_reader/tracker_backend.py
new file mode 100644
index 0000000..23d0af8
--- /dev/null
+++ b/python/rss_reader/tracker_backend.py
@@ -0,0 +1,153 @@
+#
+# Demo RSS client using tracker as backend
+# Copyright (C) 2009 Nokia <urho konttori nokia com>
+#
+# This library 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 library 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 library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+#
+import dbus
+import os
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+FALSE = "false"
+TRUE = "true"
+
+QUERY_FIRST_ENTRIES = """
+    SELECT ?entry ?title ?date ?isRead WHERE {
+      ?entry a nmo:FeedMessage ;
+         nie:title ?title ;
+         nie:contentLastModified ?date .
+    OPTIONAL {
+       ?entry nmo:isRead ?isRead.
+    }
+    } ORDER BY DESC(?date) LIMIT %s
+    """
+
+SET_URI_AS_READED = """
+    INSERT {<%s> nmo:isRead "%s".}
+    """
+
+QUERY_ALL_SUBSCRIBED_FEEDS ="""
+    SELECT ?feeduri ?title COUNT (?entries) AS e WHERE {
+       ?feeduri a nmo:FeedChannel ;
+                nie:title ?title.
+       ?entries a nmo:FeedMessage ;
+                nmo:communicationChannel ?feeduri.
+    } GROUP BY ?feeduri
+"""
+
+QUERY_FOR_URI = """
+    SELECT ?title ?date ?isRead ?channel WHERE {
+      <%s> a nmo:FeedMessage ;
+             nie:title ?title ;
+             nie:contentLastModified ?date ;
+             nmo:communicationChannel ?channel .
+      OPTIONAL {
+      <%s> nmo:isRead ?isRead.
+      }
+    }
+"""
+
+CONF_FILE = os.path.expanduser ("~/.config/rss_tracker/rss.conf")
+
+class TrackerRSS:
+
+    def __init__ (self):
+        bus = dbus.SessionBus ()
+        self.tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+        self.iface = dbus.Interface (self.tracker,
+                                     "org.freedesktop.Tracker.Resources")
+        self.invisible_feeds = []
+        self.load_config ()
+        
+
+    def load_config (self):
+        if (os.path.exists (CONF_FILE)):
+            print "Loading %s" % (CONF_FILE)
+            for line in open (CONF_FILE):
+                line = line.replace ('\n','')
+                if (len (line) > 0):
+                    self.invisible_feeds.append (line)
+            print "Hiding feeds from:", self.invisible_feeds
+        else:
+            if (not os.path.exists (os.path.dirname (CONF_FILE))):
+                os.makedirs (os.path.dirname (CONF_FILE))
+            f = open (CONF_FILE, 'w')
+            f.close ()
+        
+    def get_post_sorted_by_date (self, amount):
+        results = self.iface.Query (QUERY_FIRST_ENTRIES % (amount))
+        return results
+    
+    def set_is_read (self, uri, value):
+        if (value):
+            dbus_value = TRUE
+        else:
+            dbus_value = FALSE
+
+        print "Sending ", SET_URI_AS_READED % (uri, dbus_value)
+        self.iface.SparqlUpdate (SET_URI_AS_READED % (uri, dbus_value))
+
+    def get_all_subscribed_feeds (self):
+        """ Returns [(uri, feed channel name, entries, visible)]
+        """
+        componed = []
+        results = self.iface.Query (QUERY_ALL_SUBSCRIBED_FEEDS)
+        for result in results:
+            print "Looking for", result[0]
+            if (result[0] in self.invisible_feeds):
+                visible = False
+            else:
+                visible = True
+            componed.insert (0, result + [visible])
+
+        componed.reverse ()
+        return componed
+
+    def get_info_for_entry (self, uri):
+        """  Returns (?title ?date ?isRead)
+        """
+        details = self.iface.Query (QUERY_FOR_URI % (uri, uri))
+        if (len (details) < 1):
+            print "No details !??!!"
+            return None
+        if (len (details) > 1):
+            print "OMG what are you asking for?!?!?!"
+            return None
+
+        info = details [0]
+        if (info[3] in self.invisible_feeds):
+            print "That feed is not visible"
+            return None
+        else:
+            if (info[2] == TRUE):
+                return (info[0], info[1], True)
+            else:
+                return (info[0], info[1], False)
+
+
+    def mark_as_invisible (self, uri):
+        self.invisible_feeds.append (uri)
+
+    def mark_as_visible (self, uri):
+        self.invisible_feeds.remove (uri)
+
+    def flush_to_file (self):
+        f = open (CONF_FILE, 'w')
+        for line in self.invisible_feeds:
+            f.write (line + "\n")
+        f.close ()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]