[tracker] Adding demo app
- From: Jürg Billeter <juergbi src gnome org>
- To: svn-commits-list gnome org
- Subject: [tracker] Adding demo app
- Date: Thu, 16 Apr 2009 11:25:26 -0400 (EDT)
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]