[tracker] Added gtk-sparql python program



commit 31eb994bea4c211fdc3b9dba71c651e4de8c8015
Author: Ivan Frade <ivan frade nokia com>
Date:   Thu Jul 30 21:02:57 2009 +0300

    Added gtk-sparql python program
    
    Very first version of a gtk ui to run sparql queries in tracker.
    Basically a UI version of tracker-sparql that allows to save queries.

 python/gtk-sparql/Completion.py         |  285 +++++++++++++++++++++++++++++++
 python/gtk-sparql/gtk-sparql.py         |  213 +++++++++++++++++++++++
 python/gtk-sparql/gtk-sparql.ui         |  201 ++++++++++++++++++++++
 python/gtk-sparql/store.py              |   68 ++++++++
 python/gtk-sparql/tracker_completion.py |  123 +++++++++++++
 5 files changed, 890 insertions(+), 0 deletions(-)
---
diff --git a/python/gtk-sparql/Completion.py b/python/gtk-sparql/Completion.py
new file mode 100644
index 0000000..973d104
--- /dev/null
+++ b/python/gtk-sparql/Completion.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+
+#
+# Code taken from http://code.google.com/p/emesene-plugins/
+# Specifically http://emesene-plugins.googlecode.com/svn/trunk/Completion.py
+#
+# The original file doesn't contain License header, but
+# the project is under license GPL v2
+
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, emesene-plugins project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+
+VERSION = '0.1'
+import os
+import re
+
+import gobject
+import gtk
+
+class CompletionEngine:
+    def __init__(self):
+        self.complete_functions = []
+
+    def add_complete_function(self, function):
+        self.complete_functions.append(function)
+
+    def remove_complete_function(self, function):
+        self.complete_functions.remove(function)
+
+    def complete_word(self, buffer):
+        complete = []
+        for func in self.complete_functions:
+            new = func(buffer)
+            new.reverse()
+            complete.extend(new)
+        return complete
+
+class CompletionWindow(gtk.Window):
+    """Window for displaying a list of completions."""
+ 
+    def __init__(self, parent, callback):
+        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+        self.set_decorated(False)
+        self.store = None
+        self.view = None
+        self.completions = None
+        self.complete_callback = callback
+        self.set_transient_for(parent)
+        self.set_border_width(1)
+        self.text = gtk.TextView()
+        self.text.set_wrap_mode (gtk.WRAP_WORD)
+        self.text_buffer = gtk.TextBuffer()
+        self.text.set_buffer(self.text_buffer)
+        #self.text.set_size_request(130, 100)
+        self.text.set_sensitive(False)
+        self.init_tree_view()
+        self.init_frame()
+        self.cb_ids = {}
+        self.cb_ids['focus-out'] = self.connect('focus-out-event', self.focus_out_event)
+        self.cb_ids['key-press'] = self.connect('key-press-event', self.key_press_event)
+        self.grab_focus()
+        self.custom_widget = None
+
+    
+    def clear(self):
+        self.text_buffer.set_text('')
+        if self.custom_widget:
+            self.custom_widget.hide()
+            self.hbox.remove(self.custom_widget)
+            self.custom_widget = None
+
+    def key_press_event(self, widget, event):
+        if event.keyval == gtk.keysyms.Escape:
+            self.clear()
+            self.hide()
+            return True
+        if event.keyval == gtk.keysyms.BackSpace:
+            self.clear()
+            self.hide()
+            return True
+        if event.keyval in (gtk.keysyms.Return, gtk.keysyms.Right):
+            self.complete()
+            return True
+        if event.keyval == gtk.keysyms.Up:
+            self.select_previous()
+            return True
+        if event.keyval == gtk.keysyms.Down:
+            self.select_next()
+            return True
+
+        char = gtk.gdk.keyval_to_unicode(event.keyval)
+        if char:
+            self.complete_callback(chr(char))
+        return False
+ 
+    def complete(self):
+        self.complete_callback(self.completions[self.get_selected()]['completion'])
+ 
+    def focus_out_event(self, *args):
+        self.hide()
+    
+    def get_selected(self):
+        """Get the selected row."""
+ 
+        selection = self.view.get_selection()
+        return selection.get_selected_rows()[1][0][0]
+ 
+    def init_frame(self):
+        """Initialize the frame and scroller around the tree view."""
+ 
+        scroller = gtk.ScrolledWindow()
+        scroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        scroller.add(self.view)
+        frame = gtk.Frame()
+        frame.set_shadow_type(gtk.SHADOW_OUT)
+        self.hbox = hbox = gtk.HBox()
+        hbox.pack_start(scroller, True, True)
+ 
+        scroller_text = gtk.ScrolledWindow()
+        scroller_text.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
+        scroller_text.add(self.text)
+        hbox.pack_start (scroller_text, True, True)
+        frame.add(hbox)
+        self.add(frame)
+
+        #self.set_geometry_hints(scroller, min_width=200, min_height=200)
+        #self.set_geometry_hints(scroller_text, min_width=200, min_height=200)
+        self.set_geometry_hints (scroller_text, min_width=2500)
+        self.set_geometry_hints (None, min_width=400, min_height=200)
+        
+        #self.set_geometry_hints(scroller, max_height=450)
+        #self.set_geometry_hints(frame, max_height=2500)
+        #self.set_geometry_hints(None, max_height=2500)
+ 
+ 
+    def init_tree_view(self):
+        """Initialize the tree view listing the completions."""
+ 
+        self.store = gtk.ListStore(gobject.TYPE_STRING)
+        self.view = gtk.TreeView(self.store)
+        renderer = gtk.CellRendererText()
+        column = gtk.TreeViewColumn("", renderer, text=0)
+        self.view.append_column(column)
+        self.view.set_enable_search(False)
+        self.view.set_headers_visible(False)
+        self.view.set_rules_hint(True)
+        selection = self.view.get_selection()
+        selection.set_mode(gtk.SELECTION_SINGLE)
+        #self.view.set_size_request(100, 60)
+        self.view.connect('row-activated', self.row_activated)
+ 
+ 
+    def row_activated(self, tree, path, view_column, data = None):
+        self.complete()
+ 
+ 
+    def select_next(self):
+        """Select the next completion."""
+ 
+        self.clear()
+        row = min(self.get_selected() + 1, len(self.store) - 1)
+        selection = self.view.get_selection()
+        selection.unselect_all()
+        selection.select_path(row)
+        self.view.scroll_to_cell(row)
+        self.show_completions()
+ 
+    def select_previous(self):
+        """Select the previous completion."""
+ 
+        self.clear()
+        row = max(self.get_selected() - 1, 0)
+        selection = self.view.get_selection()
+        selection.unselect_all()
+        selection.select_path(row)
+        self.view.scroll_to_cell(row)
+        self.show_completions()
+ 
+    def set_completions(self, completions):
+        """Set the completions to display."""
+ 
+        self.completions = completions
+        #self.completions.reverse()
+        #self.resize(1, 1)
+        self.store.clear()
+        for completion in completions:
+            self.store.append([unicode(completion['abbr'])])
+        self.view.columns_autosize()
+        self.view.get_selection().select_path(0)
+        #self.text_buffer.set_text(self.completions[self.get_selected()]['info'])
+        self.show_completions()
+
+    def show_completions(self):
+        self.clear()
+        completion = self.completions[self.get_selected()]
+        info = completion['info']
+        if not info:
+            self.text.hide()
+        else:
+            self.text_buffer.set_text(info)
+            self.text.show()
+ 
+    def set_font_description(self, font_desc):
+        """Set the label's font description."""
+ 
+        self.view.modify_font(font_desc)
+
+class Completer:
+    '''This class provides completion feature for ONE conversation'''
+    def __init__(self, engine, view, window):
+        self.engine = engine
+        self.view = view
+        self.window = window
+        self.popup = CompletionWindow(None, self.complete)
+
+        self.key_press_id = view.connect("key-press-event", self.on_key_pressed)
+
+    def disconnect(self):
+        self.view.disconnect(self.key_press_id)
+
+    def complete(self, completion):
+        """Complete the current word."""
+
+        doc = self.view.get_buffer()
+        doc.insert_at_cursor(completion)
+        self.hide_popup()
+
+    def cancel(self):
+        self.hide_popup()
+        return False
+
+    def hide_popup(self):
+        """Hide the completion window."""
+
+        self.popup.hide()
+        self.completes = None
+
+    def show_popup(self, completions, x, y):
+        """Show the completion window."""
+ 
+        root_x, root_y = self.window.get_position()
+        print 'root', root_x, root_y
+        self.popup.move(root_x + x + 24, root_y + y + 44)
+        self.popup.set_completions(completions)
+        self.popup.show_all()
+
+    def display_completions(self, view, event):
+        doc = view.get_buffer()
+        insert = doc.get_iter_at_mark(doc.get_insert())
+
+        window = gtk.TEXT_WINDOW_TEXT
+        rect = view.get_iter_location(insert)
+        x, y = view.buffer_to_window_coords(window, rect.x, rect.y)
+        x, y = view.translate_coordinates(self.window, x, y)
+        completes = self.engine.complete_word(doc)
+        if completes:
+            self.show_popup(completes, x, y)
+            return True
+        else:
+            return False
+
+    def on_key_pressed(self, view, event):
+        if event.keyval == gtk.keysyms.Tab:
+        #if event.state & gtk.gdk.CONTROL_MASK and event.state & gtk.gdk.MOD1_MASK and event.keyval == gtk.keysyms.Enter:
+            return self.display_completions(view, event)
+        if event.state & gtk.gdk.CONTROL_MASK:
+            return self.cancel()
+        if event.state & gtk.gdk.MOD1_MASK:
+            return self.cancel()
+        return self.cancel()
diff --git a/python/gtk-sparql/gtk-sparql.py b/python/gtk-sparql/gtk-sparql.py
new file mode 100755
index 0000000..c8ba6a9
--- /dev/null
+++ b/python/gtk-sparql/gtk-sparql.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python2.5
+
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+
+
+import gtk
+import dbus
+import store
+import time
+import Completion
+import tracker_completion
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+class GtkSparql:
+
+    def __init__ (self, ui):
+        self.ui = ui
+        self.store = store.QueriesStore ()
+        self.saved_queries_model = self.load_saved_queries ()
+
+        combo = ui.get_object ("queries_combo")
+        combo.set_model (self.saved_queries_model)
+        render = gtk.CellRendererText ()
+        combo.pack_start (render, True)
+        combo.add_attribute (render, "text", 0)
+        combo.connect ("changed", self.select_store_query_cb)
+
+        self.textbuffer = gtk.TextBuffer ()
+        self.textbuffer.set_text ("SELECT \nWHERE { \n\n}")
+        ui.get_object ("query_text").set_buffer (self.textbuffer)
+
+        self.completer = Completion.Completer (tracker_completion.TrackerCompletionEngine (),
+                                               ui.get_object ("query_text"),
+                                               ui.get_object ("main_window"))
+
+
+        ui.get_object ("save_button").connect ("clicked", self.save_query_cb)
+        ui.get_object ("run_button").connect ("clicked", self.execute_query_cb)
+        ui.get_object ("delete_button").connect ("clicked", self.delete_store_query_cb)
+
+        # Clean treeview columns
+        treeview = ui.get_object ("results_tv")
+        columns = treeview.get_columns ()
+        for c in columns:
+            treeview.remove_column (c)
+
+        self.connect_tracker ()
+
+    def run (self):
+        w = builder.get_object ("main_window")
+        w.connect ("destroy", gtk.main_quit)
+        w.show_all ()
+        gtk.main ()
+
+    def connect_tracker (self):
+        # TODO Check if the connection is valid
+        bus = dbus.SessionBus ()
+        tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+        self.resources = dbus.Interface (tracker,
+                                         dbus_interface="org.freedesktop.Tracker.Resources");
+
+
+    def execute_query_cb (self, widget, call=0):
+        label = self.ui.get_object ("info_label")
+        
+        query = self.textbuffer.get_text (self.textbuffer.get_start_iter (),
+                                          self.textbuffer.get_end_iter ())
+        try:
+            start = time.time ()
+            result = self.resources.SparqlQuery (query)
+            end = time.time ()
+            
+            label.set_text ("Query took %f sec." % (end-start))
+            
+            self.set_results_in_treeview (result)
+            
+        except dbus.exceptions.DBusException, e:
+            if ("org.freedesktop.DBus.Error.ServiceUnknown" in str(e) and call == 0):
+                self.connect_tracker ()
+                self.execute_query_cb (widget, 1)
+            else:
+                label.set_text (str(e).partition(':')[2])
+
+        
+    def set_results_in_treeview (self, result_set):
+        if (len (result_set) < 1):
+            return None
+
+        columns = len (result_set[0])
+        params = tuple (columns * [str])
+        store = gtk.ListStore (*params)
+        self.configure_tree_view (columns)
+        
+        counter = 0
+        for r in result_set:
+            if (counter < 500):
+                counter += 1
+                store.append (r)
+            else:
+                label = self.ui.get_object ("info_label")
+                current_text = label.get_text ()
+                label.set_text (current_text +
+                                           "(More than 500 results. Showing only first 500)")
+                break
+
+        self.ui.get_object ("results_tv").set_model (store)
+
+    def configure_tree_view (self, columns):
+        """
+        Add of remove columns in the current treeview to have exactly 'columns'
+        """
+        treeview = self.ui.get_object ("results_tv")
+        current_columns = treeview.get_columns ()
+        if (len (current_columns) == columns):
+            return
+
+        if (len (current_columns) > columns):
+            for i in range (columns, len (current_columns)):
+                treeview.remove_column (current_columns [i])
+
+        
+        # Add columns. Is the only chance at this moment
+        for i in range (len (current_columns), columns):
+            render = gtk.CellRendererText() 
+            column = gtk.TreeViewColumn ("%d" % (i),
+                                         render,
+                                         text=i) # primera columna de datos
+            treeview.append_column (column)
+
+
+    def save_query_cb (self, widget):
+        dialog = gtk.Dialog ("Save query...",
+                             None,
+                             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                             (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+                              gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        vbox = dialog.get_content_area ()
+
+        query = self.textbuffer.get_text (self.textbuffer.get_start_iter (),
+                                          self.textbuffer.get_end_iter ())
+        dialog_query_label = gtk.Label (query)
+        vbox.pack_start (dialog_query_label)
+        dialog_name_entry = gtk.Entry ()
+        vbox.pack_start (dialog_name_entry)
+        vbox.show_all ()
+        
+        # Disable 'ok' button unless there is a name
+        dialog.set_response_sensitive (gtk.RESPONSE_ACCEPT, False)
+        def validate_entry (entry, dialog):
+            dialog.set_response_sensitive (gtk.RESPONSE_ACCEPT, len (entry.get_text ()) > 0)
+        dialog_name_entry.connect ("changed", validate_entry, dialog)
+        
+        response = dialog.run ()
+        if (response == gtk.RESPONSE_ACCEPT):
+            name = dialog_name_entry.get_text ()
+            self.save_query (name, query)
+        dialog.destroy ()
+
+
+    def select_store_query_cb (self, combobox):
+        it = combobox.get_active_iter ()
+        selected = combobox.get_model ().get (it, 1)[0]
+        self.textbuffer.set_text (selected)
+
+
+    def delete_store_query_cb (self, button):
+        combo = self.ui.get_object ("queries_combo")
+        it = combo.get_active_iter ()
+        name = self.saved_queries_model.get (it, 0)[0]
+        self.store.delete_query (name)
+        self.saved_queries_model.remove (it)
+        combo.set_active (0)
+
+
+    def load_saved_queries (self):
+        queries_model = gtk.ListStore (str, str)
+        queries = self.store.get_all_queries ()
+        for row in queries:
+            queries_model.append (row)
+        return queries_model
+
+
+    def save_query (self, name, value):
+        self.store.save_query (name, value)
+        it = self.saved_queries_model.append ((name, value))
+        self.ui.get_object ("queries_combo").set_active_iter (it)
+
+        
+if __name__ == "__main__":
+
+    builder = gtk.Builder ()
+    builder.add_from_file ("gtk-sparql.ui")
+
+    app = GtkSparql (builder)
+    app.run ()
diff --git a/python/gtk-sparql/gtk-sparql.ui b/python/gtk-sparql/gtk-sparql.ui
new file mode 100644
index 0000000..6f94518
--- /dev/null
+++ b/python/gtk-sparql/gtk-sparql.ui
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="liststore1"/>
+  <object class="GtkListStore" id="liststore2"/>
+  <object class="GtkWindow" id="main_window">
+    <property name="border_width">6</property>
+    <property name="icon_name">applications-development</property>
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;b&gt;Saved queries&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">6</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="queries_combo">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="delete_button">
+                <property name="label" translatable="yes">gtk-delete</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVPaned" id="vpaned1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkTextView" id="query_text">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkButton" id="save_button">
+                        <property name="label" translatable="yes">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVSeparator" id="vseparator1">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="previous_button">
+                        <property name="label" translatable="yes">gtk-go-back</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="next_button">
+                        <property name="label" translatable="yes">gtk-go-forward</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVSeparator" id="vseparator2">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                      </object>
+                      <packing>
+                        <property name="position">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="run_button">
+                        <property name="label" translatable="yes">gtk-execute</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">5</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">6</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="info_label">
+                    <property name="visible">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <object class="GtkTreeView" id="results_tv">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/python/gtk-sparql/store.py b/python/gtk-sparql/store.py
new file mode 100644
index 0000000..538f4c6
--- /dev/null
+++ b/python/gtk-sparql/store.py
@@ -0,0 +1,68 @@
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+import sqlite3
+import os
+
+DEFAULT_EXAMPLE = "SELECT ?u \nWHERE { ?u a nie:InformationElement. }\n\n"
+EMPTY_QUERY = "SELECT \nWHERE { \n\n}"
+
+class QueriesStore ():
+
+    def __init__ (self):
+        create = False
+        self.db_dir, self.db_path =  self.get_db_dir_file ()
+
+        if (not os.path.exists (self.db_dir)):
+            os.makedirs (self.db_dir)
+            create = True
+
+        if (create or not os.path.exists (self.db_path)):
+            conn = sqlite3.connect (self.db_path)
+            c = conn.cursor ()
+            c.execute ("CREATE TABLE saved_queries ( Name Text not null, Query Text );")
+            c.execute ("INSERT INTO saved_queries VALUES ('', '%s')" % (EMPTY_QUERY)) 
+            c.execute ("INSERT INTO saved_queries VALUES ('Example', '%s')" % (DEFAULT_EXAMPLE)) 
+            conn.commit ()
+            c.close ()
+
+    def get_db_dir_file (self):
+        return (os.path.join (os.getenv ("HOME"), ".local", "share", "tracker-query"),
+                os.path.join (os.getenv ("HOME"), ".local", "share", "tracker-query", "queries.db"))
+
+    def save_query (self, name, value):
+        conn = sqlite3.connect (self.db_path)
+        c = conn.cursor ()
+        c.execute ("INSERT INTO saved_queries VALUES ('%s', '%s');" % (name, value))
+        conn.commit ()
+        c.close ()
+
+    def delete_query (self, name):
+        conn = sqlite3.connect (self.db_path)
+        c = conn.cursor ()
+        c.execute ("DELETE FROM saved_queries WHERE Name='%s';" % (name))
+        conn.commit ()
+        c.close ()
+        
+
+    def get_all_queries (self):
+        conn = sqlite3.connect (self.db_path)
+        c = conn.cursor ()
+        c.execute ("SELECT * FROM saved_queries;")
+        results =  c.fetchall ()
+        c.close ()
+        return results
diff --git a/python/gtk-sparql/tracker_completion.py b/python/gtk-sparql/tracker_completion.py
new file mode 100644
index 0000000..8725055
--- /dev/null
+++ b/python/gtk-sparql/tracker_completion.py
@@ -0,0 +1,123 @@
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+
+import dbus
+import re
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+ALL_NAMESPACES = """
+SELECT ?u ?prefix WHERE { ?u a tracker:Namespace; tracker:prefix ?prefix. }
+"""
+
+ALL_PROPERTIES = """
+SELECT ?u ?comment
+WHERE { ?u a rdf:Property. OPTIONAL { ?u rdfs:comment ?comment.} }
+"""
+
+ALL_CLASSES = """
+SELECT ?u ?comment WHERE { ?u a rdfs:Class. OPTIONAL { ?u rdfs:comment ?comment.} }
+"""
+
+class TrackerCompletionEngine:
+
+    def __init__ (self):
+        self.connect_tracker ()
+
+        self.namespaces = {}
+        for row in self.resources.SparqlQuery (ALL_NAMESPACES):
+            self.namespaces [row[0]] = row[1]
+
+        self.properties = {}
+        for (uri, comment) in self.resources.SparqlQuery (ALL_PROPERTIES):
+            self.properties [str(self.qname_to_short (uri))] = str(comment)
+
+        self.classes = {}
+        for (uri, comment) in self.resources.SparqlQuery (ALL_CLASSES):
+            self.classes [str(self.qname_to_short (uri))] = str(comment)
+
+    def qname_to_short (self, uri):
+        ns, classname = self.split_ns_uri (uri)
+        prefix = self.namespaces [ns]
+        return prefix + ":" + classname
+
+    def split_ns_uri (self, uri):
+        pieces = uri.split ("#")
+        if ( len (pieces) > 1):
+            classname = pieces [1]
+            namespace = pieces [0] + "#"
+        else:
+            classname = uri[uri.rindex ('/')+1:]
+            namespace = uri[:uri.rindex ('/')+1]
+        return namespace, classname
+
+
+    def connect_tracker (self):
+        # TODO Check if the connection is valid
+        bus = dbus.SessionBus ()
+        tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+        self.resources = dbus.Interface (tracker,
+                                         dbus_interface="org.freedesktop.Tracker.Resources");
+
+    def complete_word (self, textbuffer):
+        """
+        Return a list of dictionaries with the following keys:
+          completion: text to set in the buffer if selected the option
+          abbr: text visible for the user in the list of options
+          info: test description showed near to the option
+        """
+        completions = []
+        uncomplete_word = self.get_last_word (textbuffer)
+        for k in self.classes.iterkeys ():
+            if (k.startswith (uncomplete_word)):
+                c = {'completion':k[len (uncomplete_word):],
+                     'abbr':k,
+                     'info':self.classes [k]}
+                completions.insert (0, c)
+                
+        for k in self.properties.iterkeys ():
+            if (k.startswith (uncomplete_word)):
+                c = {'completion':k[len (uncomplete_word):],
+                     'abbr':k,
+                     'info':self.properties [k]}
+                completions.insert (0, c)
+
+        return completions
+        
+
+    def get_last_word (self, textbuffer):
+        insert = textbuffer.get_iter_at_mark(textbuffer.get_insert())
+        lineno = insert.get_line ()
+        linestart = textbuffer.get_iter_at_line (lineno)
+        regex = re.compile (r"\S+$", re.MULTILINE)
+        line = textbuffer.get_slice (linestart, insert, False)
+        match = re.search (regex, line)
+        if (not match):
+            return []
+        uncomplete_word = match.group (0)
+        return uncomplete_word
+
+if __name__ == "__main__":
+
+    import gtk
+    engine = TrackerCompletionEngine ()
+    buf = gtk.TextBuffer ()
+    buf.set_text ("SELECT ?u WHERE { ?u a nie:InformationElement;\n nie:t")
+    print "Last word: ", engine.get_last_word (buf)
+    print engine.complete_word (buf)



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