~unity-team/unity-lens-sample/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#! /usr/bin/python

from gi.repository import GLib, GObject, Gio
from gi.repository import Dee
from gi.repository import Unity
import urllib2, simplejson, socket

#Set the timeout (in seconds) for http calls. By design, lenses shouldn't take more than 4 seconds to give results or indicate they have failed.
socket.setdefaulttimeout(3)

# The scope dbus id
BUS_NAME = "net.launchpad.scope.information.wikipedia"

class Daemon:
	def __init__ (self):
		# Create the scope (this matches the path defined in the .scope file)
		self.scope = Unity.Scope.new ("/net/launchpad/scope/information/wikipedia")
		# Is the scope searchable from the home dash?
		self.scope.search_in_global = False
		# Connect to the search changed signal (it is fired when the lens is opened for the first time, then for each search change)
		self.scope.connect ("search-changed", self.on_search_changed)
		# Listen to the lens filters
		self.scope.connect ("filters-changed", lambda scope : scope.queue_search_changed(Unity.SearchType.DEFAULT))
		# The scope is ready
		self.scope.export()
	
	# On each search change, this method is called
	def on_search_changed (self, scope, search, search_type, cancellable):
		# Get the search string
		search_string = search.props.search_string.strip()
		print "Search changed to \"%s\"" % search_string
		# Get the Dee model (the database used by the lens to store and display search results)
		model = search.props.results_model
		# Empty the model
		model.clear()
		# Update the model
		self.update_results_model (search_string, model)
		# Signal to the lens that the search is finished (the spinning search icon stops)
		search.finished()

	# Update the model
	def update_results_model(self, search, model):
		self.wikipedia(search, model)

	# This method sends the search string to Wikipedia and brings back results
	def wikipedia_search(self,search):
		try:
			# Replace spaces by | to match the Wikipedia query format
			search = search.replace(" ", "|")
			# The Wikipedia "opensearch" API ( http://www.mediawiki.org/wiki/API:Opensearch )
			url = ("http://en.wikipedia.org/w/api.php?action=opensearch&limit=25&format=json&search=%s" % (search))

			# urllib2.urlopen(url) : send the query
			# read() : read the results
			# simplejson.loads() : we have asked Wikipedia to return the results in json format, we use simplejson to parse them
			results = simplejson.loads(urllib2.urlopen(url).read())
			print "Searching Wikipedia"

			# We return the results list (the use of [1] is specific to Wikipedia json results)
			return results[1]
		except (IOError, KeyError, urllib2.URLError, urllib2.HTTPError, simplejson.JSONDecodeError):
			print "Error : Unable to search Wikipedia"
			return []

	# This method parses the results and adds them to the model
	def wikipedia(self, search, model):
		# We iterate through each of the returned results
		for i in self.wikipedia_search(search):
			# Lens results are made of several things :
			# An uri
			uri = "http://en.wikipedia.org/wiki/%s" % i
			# A title
			title = i
			# An icon
			icon = 'http://upload.wikimedia.org/wikipedia/commons/6/63/Wikipedia-logo.png'
			# A comment (optional)
			comment = 'Wikipedia article'
			# If the uri seems valid, we add the results to the Dee model
			if (uri.startswith("http://")):
				# uri, icon, category (matching the order of the categories in the lens), mimetype, title, comment, drag and drop uri
				model.append(uri, icon, 0, "text/html", title, comment, uri)
				# We can append the same result to multiple models, just by changing the category
				model.append(uri, icon, 1, "text/html", title, comment, uri)
				
# The following lines take care of the Bus connexion.
if __name__ == "__main__":
	session_bus_connection = Gio.bus_get_sync (Gio.BusType.SESSION, None)
	session_bus = Gio.DBusProxy.new_sync (session_bus_connection, 0, None,
	                                      'org.freedesktop.DBus',
	                                      '/org/freedesktop/DBus',
	                                      'org.freedesktop.DBus', None)
	result = session_bus.call_sync('RequestName',
	                               GLib.Variant ("(su)", (BUS_NAME, 0x4)),
	                               0, -1, None)

	# Unpack variant response with signature "(u)". 1 means we got it.
	result = result.unpack()[0]
	
	if result != 1 :
		print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME
		raise SystemExit (1)
	
	daemon = Daemon()
	GObject.MainLoop().run()