Merge lp:~jamesh/unity-scope-yelp/preview into lp:unity-scope-yelp

Proposed by James Henstridge
Status: Merged
Approved by: James Henstridge
Approved revision: 29
Merged at revision: 28
Proposed branch: lp:~jamesh/unity-scope-yelp/preview
Merge into: lp:unity-scope-yelp
Diff against target: 262 lines (+122/-70)
2 files modified
src/unity_yelp_daemon.py (+93/-59)
tests/test_yelp.py (+29/-11)
To merge this branch: bzr merge lp:~jamesh/unity-scope-yelp/preview
Reviewer Review Type Date Requested Status
David Callé Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+163470@code.launchpad.net

Commit message

Add a simple preview to the scope, and correctly discover documentation in the fallback "C" directory when there are localised manuals.

Description of the change

Add a preview to the Yelp scope.

I also updated the code that discovers installed documentation to not completely ignore the "C" fallback directory if there is any localised documentation (it now picks up any manuals from there that haven't been localised).

I also switched to using cElementTree for the parsing to speed things up a bit.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
David Callé (davidc3) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/unity_yelp_daemon.py'
--- src/unity_yelp_daemon.py 2013-04-02 11:09:28 +0000
+++ src/unity_yelp_daemon.py 2013-05-13 07:19:26 +0000
@@ -14,12 +14,14 @@
14# You should have received a copy of the GNU General Public License along14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.15# with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17from gi.repository import GLib, Gio
18from gi.repository import Unity
19import gettext17import gettext
18import html
20import locale19import locale
21import os20import os
22from xml.dom import minidom21from xml.etree import cElementTree as ET
22
23from gi.repository import GLib, Gio
24from gi.repository import Unity
2325
24APP_NAME = 'unity-scope-yelp'26APP_NAME = 'unity-scope-yelp'
25LOCAL_PATH = '/usr/share/locale/'27LOCAL_PATH = '/usr/share/locale/'
@@ -55,51 +57,71 @@
55EXTRA_METADATA = []57EXTRA_METADATA = []
5658
5759
60def _get_manuals_in_dir(dir, manuals):
61 """Yield the manuals found in the given directory, omitting those
62 that have already been discovered."""
63 if not os.path.isdir(dir):
64 return
65 for manual in os.listdir(dir):
66 if manual in manuals:
67 continue
68 manualdir = os.path.join(dir, manual)
69 if os.path.isdir(manualdir):
70 manuals.add(manual)
71 yield manualdir
72
73
74def get_manuals(helpdir):
75 """Get the manuals found in the given directory according to the
76 user's locale."""
77 language, encoding = locale.getlocale()
78 manuals = set()
79 if language is not None:
80 languagedir = os.path.join(helpdir, language)
81 yield from _get_manuals_in_dir(languagedir, manuals)
82 # If the locale includes a country, look for manuals
83 if language[:2] != language:
84 languagedir = os.path.join(helpdir, language[:2])
85 yield from _get_manuals_in_dir(languagedir, manuals)
86 # Now return untranslated versions of remaining manuals.
87 languagedir = os.path.join(helpdir, 'C')
88 yield from _get_manuals_in_dir(languagedir, manuals)
89
90
58def get_yelp_documents():91def get_yelp_documents():
59 '''92 '''
60 Parses local help files for <desc> and 1st <title> tags and associates93 Parses local help files for <desc> and 1st <title> tags and associates
61 them with the page's uri in self.data as: {uri, page description, page title}94 them with the page's uri in self.data as: {uri, page description, page title}
62 If a help file has no <desc> or <title> tag, it is excluded.95 If a help file has no <desc> or <title> tag, it is excluded.
63 '''96 '''
64 language, encoding = locale.getlocale()
65 if language is None:
66 language = 'C'
67
68 data = []97 data = []
69 help_location = '%s%s' % (HELP_DIR, language)98 namespaces = {'m': 'http://projectmallard.org/1.0/'}
70 if not os.path.exists(help_location):99 for manualdir in get_manuals(HELP_DIR):
71 help_location = '%s%s' % (HELP_DIR, language[:2])100 for filename in os.listdir(manualdir):
72 if not os.path.exists(help_location):101 filename = os.path.join(manualdir, filename)
73 help_location = '%s%s' % (HELP_DIR, "C")102 if not (filename.endswith('page') and os.path.isfile(filename)):
74 for dirname in os.listdir(help_location):103 continue
75 directory = help_location + "/" + dirname104 try:
76 for filename in os.listdir(directory):105 tree = ET.parse(filename)
77 filename = directory + "/" + filename106 except ET.ParseError:
78 if not os.path.isdir(filename):107 # Not an XML file
79 if os.path.isfile(filename):108 continue
80 xmldoc = minidom.parse(filename)109 if (tree.getroot().tag != '{http://projectmallard.org/1.0/}page' or
81 try:110 tree.getroot().get('type') != 'topic'):
82 pagenode = xmldoc.getElementsByTagName('page')[0]111 # Not a Mallard documentation file.
83 if pagenode.attributes["type"].value == "topic":112 continue
84 desc = ""113 title = desc = ""
85 nodes = xmldoc.getElementsByTagName('desc').item(0).childNodes114 node = tree.find('m:title', namespaces)
86 for node in nodes:115 if node is not None:
87 try:116 title = node.text
88 desc += str(node.wholeText)117 node = tree.find('m:info/m:desc', namespaces)
89 except:118 if node is not None:
90 desc += str(node.childNodes[0].wholeText)119 desc = ''.join(node.itertext())
91 desc = desc.strip(' \t\n\r')120 desc = desc.strip(' \t\n\r')
92 title = xmldoc.getElementsByTagName('title').item(0).childNodes[0].data121 if desc == "":
93 if desc == "":122 desc = title
94 desc = title123
95 record = []124 data.append((filename, title, desc, os.path.basename(manualdir)))
96 record.append(filename)
97 record.append(desc)
98 record.append(title)
99 record.append(dirname)
100 data.append(record)
101 except:
102 pass
103 return data125 return data
104126
105127
@@ -116,34 +138,40 @@
116 YELP_CACHE = get_yelp_documents()138 YELP_CACHE = get_yelp_documents()
117 help_data = YELP_CACHE139 help_data = YELP_CACHE
118140
119 for data in help_data:141 search = search.lower()
142
143 for (filename, title, desc, manual) in help_data:
120 if len(results) >= MAX_RESULTS:144 if len(results) >= MAX_RESULTS:
121 return results145 break
122 try:146 try:
123 if data[3] == "ubuntu-help":147 if manual == "ubuntu-help":
124 icon_hint = Gio.ThemedIcon.new("distributor-logo").to_string()148 icon_hint = Gio.ThemedIcon.new("distributor-logo").to_string()
125 else:149 else:
126 icon_hint = Gio.ThemedIcon.new(data[3]).to_string()150 icon_hint = Gio.ThemedIcon.new(manual).to_string()
127 except:151 except:
128 icon_hint = Gio.ThemedIcon.new("help").to_string()152 icon_hint = Gio.ThemedIcon.new("help").to_string()
129153
130 if search.lower() in data[1].lower():154 if (search in title.lower() or
131 results.append({'uri': data[0],155 search in desc.lower() or
132 'icon': icon_hint,156 search in manual.lower()):
133 'title': data[1]})157 results.append({'uri': filename,
134 elif search.lower() in data[2].lower():158 'icon': icon_hint,
135 results.append({'uri': data[0],159 'title': title,
136 'icon': icon_hint,160 'comment': desc})
137 'title': data[2]})
138 elif search.lower() in data[3].lower():
139 results.append({'uri': data[0],
140 'icon': icon_hint,
141 'title': data[1]})
142 else:
143 continue
144 return results161 return results
145162
146163
164class Previewer(Unity.ResultPreviewer):
165
166 def do_run(self):
167 image = Gio.ThemedIcon.new(self.result.icon_hint)
168 preview = Unity.GenericPreview.new(
169 self.result.title, html.escape(self.result.comment), image)
170 action = Unity.PreviewAction.new("open", _("Open"), None)
171 preview.add_action(action)
172 return preview
173
174
147# Classes below this point establish communication175# Classes below this point establish communication
148# with Unity, you probably shouldn't modify them.176# with Unity, you probably shouldn't modify them.
149177
@@ -245,6 +273,12 @@
245 se = MySearch(search_context)273 se = MySearch(search_context)
246 return se274 return se
247275
276 def do_create_previewer(self, result, metadata):
277 rp = Previewer()
278 rp.set_scope_result(result)
279 rp.set_search_metadata(metadata)
280 return rp
281
248 def do_activate(self, result, metadata, id):282 def do_activate(self, result, metadata, id):
249 parameters = [YELP_EXECUTABLE, result.uri]283 parameters = [YELP_EXECUTABLE, result.uri]
250 GLib.spawn_async(parameters)284 GLib.spawn_async(parameters)
251285
=== renamed file 'tests/data/C/sample_help/mock_yelp_file' => 'tests/data/C/sample_help/mock_yelp_file.page'
=== modified file 'tests/test_yelp.py'
--- tests/test_yelp.py 2013-04-02 11:09:28 +0000
+++ tests/test_yelp.py 2013-05-13 07:19:26 +0000
@@ -44,24 +44,42 @@
4444
45 def test_questions_search(self):45 def test_questions_search(self):
46 self.scope_module.HELP_DIR = 'tests/data/'46 self.scope_module.HELP_DIR = 'tests/data/'
47 expected_results = ["tests/data/C/sample_help/mock_yelp_file",47 expected_results = ["tests/data/C/sample_help/mock_yelp_file.page",
48 "A visual introduction to the Unity desktop."]48 "Welcome to Ubuntu"]
49 results = []49 results = []
50 for s in ['unity']:50 result_set = self.perform_query('unity')
51 result_set = self.perform_query(s)51 self.assertEqual(len(result_set.results), 1)
52 results.append(result_set.results[0]['uri'])52 result = result_set.results[0]
53 results.append(result_set.results[0]['title'])53 self.assertEqual(
54 self.assertEqual(results, expected_results)54 result['uri'], "tests/data/C/sample_help/mock_yelp_file.page")
55 self.assertEqual(result['title'], "Welcome to Ubuntu")
56 self.assertEqual(
57 result['comment'], "A visual introduction to the Unity desktop.")
58 self.assertEqual(result['icon'], 'sample_help')
5559
56 def test_questions_failing_search(self):60 def test_questions_failing_search(self):
57 self.scope_module.HELP_DIR = 'tests/data/'61 self.scope_module.HELP_DIR = 'tests/data/'
58 for s in ['upnriitnyt']:62 result_set = self.perform_query('upnriitnyt')
59 result_set = self.perform_query(s)63 self.assertEqual(len(result_set.results), 0)
60 self.assertEqual(len(result_set.results), 0)64
65 def test_preview(self):
66 result = Unity.ScopeResult()
67 result.uri = "tests/data/C/sample_help/mock_yelp_file.page"
68 result.title = "Welcome to Ubuntu"
69 result.comment = "A visual introduction to the Unity desktop."
70 result.icon_hint = 'sample_help'
71
72 previewer = self.scope.create_previewer(result, Unity.SearchMetadata())
73 preview = previewer.run()
74 self.assertEqual(preview.props.title, "Welcome to Ubuntu")
75 self.assertEqual(preview.props.description_markup,
76 "A visual introduction to the Unity desktop.")
77 self.assertNotEqual(preview.props.image, None)
78 self.assertEqual(preview.props.image.get_names(), ['sample_help'])
6179
62 def test_activation(self):80 def test_activation(self):
63 result = Unity.ScopeResult()81 result = Unity.ScopeResult()
64 result.uri = "tests/data/C/sample_help/mock_yelp_file"82 result.uri = "tests/data/C/sample_help/mock_yelp_file.page"
65 activation = self.scope.activate(result, Unity.SearchMetadata(), None)83 activation = self.scope.activate(result, Unity.SearchMetadata(), None)
66 self.assertEqual(activation.props.goto_uri, None)84 self.assertEqual(activation.props.goto_uri, None)
67 self.assertEqual(activation.props.handled, Unity.HandledType.HIDE_DASH)85 self.assertEqual(activation.props.handled, Unity.HandledType.HIDE_DASH)

Subscribers

People subscribed via source and target branches

to all changes: