unity-scope-gourmet-0.1+13.10.20130723/0000755000015700001700000000000012173421372017644 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/po/0000755000015700001700000000000012173421372020262 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/po/unity-scope-gourmet.pot0000644000015700001700000000241212173420454024744 0ustar pbuserpbgroup00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #: ../src/unity_gourmet_daemon.py:34 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-02-21 01:59+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../src/unity_gourmet_daemon.py:32 msgid "Search Gourmet Recipe Manager" msgstr "" #: ../src/unity_gourmet_daemon.py:33 msgid "Sorry, there are no Gourmet recipe results that match your search." msgstr "" #: ../src/unity_gourmet_daemon.py:55 msgid "Recipes" msgstr "" #: ../src/unity_gourmet_daemon.py:69 msgid "%H hours %M minutes" msgstr "" #: ../src/unity_gourmet_daemon.py:71 msgid "%M minutes" msgstr "" #: ../data/gourmet.scope.in.h:1 msgid "gourmet;" msgstr "" #: ../data/gourmet.scope.in.h:2 msgid "Gourmet" msgstr "" #: ../data/gourmet.scope.in.h:3 msgid "Find Gourmet items" msgstr "" #: ../data/gourmet.scope.in.h:4 msgid "Search Gourmet" msgstr "" unity-scope-gourmet-0.1+13.10.20130723/po/POTFILES.in0000644000015700001700000000012712173420454022037 0ustar pbuserpbgroup00000000000000[encoding: UTF-8] src/unity_gourmet_daemon.py [type: gettext/ini]data/gourmet.scope.in unity-scope-gourmet-0.1+13.10.20130723/MANIFEST.in0000644000015700001700000000007612173420454021405 0ustar pbuserpbgroup00000000000000include MANIFEST.in include src/* include data/* include po/* unity-scope-gourmet-0.1+13.10.20130723/tests/0000755000015700001700000000000012173421372021006 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/tests/fake-gourmet.py0000755000015700001700000000010412173420454023744 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys sys.exit(1) unity-scope-gourmet-0.1+13.10.20130723/tests/__init__.py0000644000015700001700000000000012173420454023105 0ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/tests/data/0000755000015700001700000000000012173421372021717 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/tests/data/mock_gourmet_pass.db0000644000015700001700000000600012173420454025743 0ustar pbuserpbgroup00000000000000SQLite format 3@ -โ' 3ƒ ##†tableingredientsingredientsCREATE TABLE ingredients ( id INTEGER NOT NULL, recipe_id INTEGER, refid INTEGER, unit TEXT, amount FLOAT, rangeamount FLOAT, item TEXT, ingkey TEXT, optional BOOLEAN, shopoptional INTEGER, inggroup TEXT, position INTEGER, deleted BOOLEAN, PRIMARY KEY (id), FOREIGN KEY(recipe_id) REFERENCES recipe (id), FOREIGN KEY(refid) REFERENCES recipe (id) )ƒJ†otablereciperecipeCREATE TABLE recipe ( id INTEGER NOT NULL, title TEXT, instructions TEXT, modifications TEXT, cuisine TEXT, rating INTEGER, description TEXT, source TEXT, preptime INTEGER, cooktime INTEGER, servings FLOAT, yields FLOAT, yield_unit VARCHAR(32), image BLOB, thumb BLOB, deleted BOOLEAN, recipe_hash VARCHAR(32), ingredient_hash VARCHAR(32), link TEXT, last_modified INTEGER, PRIMARY KEY (id) ) zz)A#7 7Ubuntu BurgersCook them like you mean itBurgerGood eatinghttp://www.ubuntu.com„Burgerhttp://www.ubuntu.com ืื' ##g๔Minced BeefMinced Beefunity-scope-gourmet-0.1+13.10.20130723/tests/data/mock_gourmet_fail0000644000015700001700000000000612173420454025324 0ustar pbuserpbgroup00000000000000 unity-scope-gourmet-0.1+13.10.20130723/tests/test_gourmet.py0000644000015700001700000000715612173420454024112 0ustar pbuserpbgroup00000000000000#! /usr/bin/python3 # -*- coding: utf-8 -*- from gi.repository import Unity from unittest import TestCase import imp class ResultSet(Unity.ResultSet): def __init__(self): Unity.ResultSet.__init__(self) self.results = [] def do_add_result(self, result): self.results.append({'uri': result.uri, 'title': result.title, 'comment': result.comment, 'icon': result.icon_hint, 'likes': result.metadata['likes'].get_string(), 'cuisine': result.metadata['cuisine'].get_string(), 'cooktime': result.metadata['cooktime'].get_string(), 'preptime': result.metadata['preptime'].get_string(), 'ingredients': result.metadata['ingredients'].get_string(), 'yields': result.metadata['yields'].get_string(), 'instructions': result.metadata['instructions'].get_string()}) class ScopeTestCase(TestCase): def init_scope(self, scope_path): self.scope_module = imp.load_source('scope', scope_path) self.scope = self.scope_module.load_scope() def perform_query(self, query, filter_set=Unity.FilterSet.new()): result_set = ResultSet() ctx = Unity.SearchContext.create(query, 0, filter_set, None, result_set, None) s = self.scope.create_search_for_query(ctx) s.run() return result_set class TestGourmet(ScopeTestCase): def setUp(self): self.init_scope('src/unity_gourmet_daemon.py') def tearDown(self): self.scope = None self.scope_module = None def test_search(self): self.scope_module.GOURMET_DB = 'tests/data/mock_gourmet_pass.db' expected_results = ['http://www.ubuntu.com', 'Ubuntu Burgers', 'โ˜…โ˜…โ˜…โœฎโ˜†', 'Cook them like you mean it', 'Burger', '15 minutes', '30 minutes', '500 g Minced Beef', '1 Burger'] results = [] for s in ['burger']: result_set = self.perform_query(s) results.append(result_set.results[0]['uri']) results.append(result_set.results[0]['title']) results.append(result_set.results[0]['likes']) results.append(result_set.results[0]['instructions']) results.append(result_set.results[0]['cuisine']) results.append(result_set.results[0]['cooktime']) results.append(result_set.results[0]['preptime']) results.append(result_set.results[0]['ingredients']) results.append(result_set.results[0]['yields']) self.assertEqual(results, expected_results) def test_failing_search(self): self.scope_module.GOURMET_DB = 'tests/data/mock_gourmet_fail' for s in ['upnriitnyt']: result_set = self.perform_query(s) self.assertEqual(len(result_set.results), 0) def test_activation(self): result = Unity.ScopeResult() result.uri = 'http://www.ubuntu.com' self.scope_module.GOURMET_EXECUTABLE = 'tests/fake-gourmet.py' activation = self.scope.activate(result, Unity.SearchMetadata(), None) self.assertEqual(activation.props.goto_uri, None) self.assertEqual(activation.props.handled, Unity.HandledType.HIDE_DASH) if __name__ == '__main__': unittest.main() unity-scope-gourmet-0.1+13.10.20130723/src/0000755000015700001700000000000012173421372020433 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/src/unity_gourmet_daemon.py0000644000015700001700000002605712173420454025254 0ustar pbuserpbgroup00000000000000#! /usr/bin/python3 # -*- coding: utf-8 -*- # Copyright(C) 2013 Mark Tully # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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, see . from gi.repository import GLib, Gio from gi.repository import Unity import gettext import os import sqlite3 APP_NAME = 'unity-scope-gourmet' LOCAL_PATH = '/usr/share/locale/' gettext.bindtextdomain(APP_NAME, LOCAL_PATH) gettext.textdomain(APP_NAME) _ = gettext.gettext GROUP_NAME = 'com.canonical.Unity.Scope.Recipes.Gourmet' UNIQUE_PATH = '/com/canonical/unity/scope/recipes/gourmet' SEARCH_HINT = _('Search Gourmet Recipe Manager') NO_RESULTS_HINT = _('Sorry, there are no Gourmet recipe results that match your search.') PROVIDER_CREDITS = _('') SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/' PROVIDER_ICON = SVG_DIR + 'service-yelp.svg' DEFAULT_RESULT_ICON = SVG_DIR + 'result-help.svg' DEFAULT_RESULT_MIMETYPE = 'x-scheme-handler/man' DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT GOURMET_DB = os.getenv("HOME") + '/.gourmet/recipes.db' GOURMET_EXECUTABLE = '/usr/bin/gourmet' SQL_QUERY = '''SELECT recipe.id, recipe.title, recipe.instructions, recipe.modifications, recipe.cuisine, recipe.rating, recipe.preptime, recipe.cooktime, recipe.yields, recipe.yield_unit, recipe.image, recipe.link, GROUP_CONCAT(COALESCE(ingredients.amount, '') || ' ' || COALESCE(ingredients.unit, '') || ' ' || ingredients.item) FROM recipe INNER JOIN ingredients ON recipe.id = ingredients.recipe_id WHERE recipe.title LIKE '%%%s%%' AND recipe.deleted = '0' GROUP BY recipe.id''' STARS = {0: 'โ˜†โ˜†โ˜†โ˜†โ˜†', 1: 'โœฎโ˜†โ˜†โ˜†โ˜†', 2: 'โ˜…โ˜†โ˜†โ˜†โ˜†', 3: 'โ˜…โœฎโ˜†โ˜†โ˜†', 4: 'โ˜…โ˜…โ˜†โ˜†โ˜†', 5: 'โ˜…โ˜…โœฎโ˜†โ˜†', 6: 'โ˜…โ˜…โ˜…โ˜†โ˜†', 7: 'โ˜…โ˜…โ˜…โœฎโ˜†', 8: 'โ˜…โ˜…โ˜…โ˜…โ˜†', 9: 'โ˜…โ˜…โ˜…โ˜…โœฎ', 10: 'โ˜…โ˜…โ˜…โ˜…โ˜…'} c1 = {'id': 'recipes', 'name': _('Recipes'), 'icon': SVG_DIR + 'group-installed.svg', 'renderer': Unity.CategoryRenderer.VERTICAL_TILE} CATEGORIES = [c1] FILTERS = [] META0 = {'id': 'instructions', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META1 = {'id': 'likes', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META2 = {'id': 'preptime', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META3 = {'id': 'cooktime', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META4 = {'id': 'cuisine', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META5 = {'id': 'ingredients', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} META6 = {'id': 'yields', 'type': 's', 'field': Unity.SchemaFieldType.OPTIONAL} EXTRA_METADATA = [META0, META1, META2, META3, META4, META5, META6] def duration(seconds): if isinstance(seconds, int): import time if seconds > 3600: duration = time.strftime(_('%H hours %M minutes'), time.gmtime(seconds)) else: duration = time.strftime(_('%M minutes'), time.gmtime(seconds)) return duration else: return "" def gourmet_search(path, query, search): try: conn = sqlite3.connect(path) cursor = conn.cursor() print("Searching for Gourmet recipes") query = query % search cursor.execute(query) results = cursor.fetchall() cursor.close() return results except: return [] def search(search, filters): ''' Search for recipes matching the search string ''' results = [] if not os.path.exists('/tmp/unity-scope-gourmet'): os.mkdir('/tmp/unity-scope-gourmet') gourmet_results = gourmet_search(GOURMET_DB, SQL_QUERY, search) for row in gourmet_results: i = row[0] title = row[1] instructions = row[2] comments = row[3] cuisine = row[4] likes = STARS[row[5]] preptime = str(duration(row[6])) cooktime = str(duration(row[7])) yields = str(row[8]).replace('.0', ' ') + str(row[9]) uri = row[11] ingredients = row[12].replace('.0 ', ' ') ingredients = ingredients.replace(' ', ' ') # add http:// if necessary if not (uri.startswith("http://")) and not (uri == ''): uri = 'http://%s' % uri if uri == '': uri = 'gourmet' # Give each cached image file the unique ID of its recipe icon_hint = DEFAULT_RESULT_ICON if row[10]: open('/tmp/unity-scope-gourmet/icon' + str(i), 'wb').write(row[10]) icon_hint = 'file:///tmp/unity-scope-gourmet/icon%s' % str(i) else: if os.path.exists('/tmp/unity-scope-gourmet/icon' + str(i)): os.remove('/tmp/unity-scope-gourmet/icon' + str(i)) results.append({'uri': uri, 'icon': icon_hint, 'title': title, 'likes': GLib.Variant('s', likes), 'comments' : comments, 'instructions': GLib.Variant('s', instructions), 'cuisine': GLib.Variant('s', cuisine), 'preptime': GLib.Variant('s', preptime), 'cooktime': GLib.Variant('s', cooktime), 'ingredients': GLib.Variant('s', ingredients), 'yields': GLib.Variant('s', yields)}) return results def activate(result, metadata, action): parameters = [GOURMET_EXECUTABLE] GLib.spawn_async(parameters) return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None) class Preview(Unity.ResultPreviewer): ''' Creates the preview for the result ''' def do_run(self): ''' Create a preview and return it ''' preview = Unity.GenericPreview.new(self.result.title, '', None) preview.props.image_source_uri = self.result.icon_hint preview.props.subtitle = self.result.metadata['likes'].get_string() preview.props.description_markup = _('Yields:\t\t\t\t%s\n' % self.result.metadata['yields'].get_string()) preview.props.description_markup += _('Preparation Time:\t%s\n' % self.result.metadata['preptime'].get_string()) preview.props.description_markup += _('Cooking Time:\t\t%s\n' % self.result.metadata['cooktime'].get_string()) preview.props.description_markup += _('Cuisine:\t\t\t%s\n\n' % self.result.metadata['cuisine'].get_string()) preview.props.description_markup += _('Ingredients:\n%s\n\n' % self.result.metadata['ingredients'].get_string().replace(',', ', ')) preview.props.description_markup += _('Instructions\n\n%s' % self.result.metadata['instructions'].get_string()) preview.props.description_markup += _('\n\n\n%s' % self.result.comment) show_action = Unity.PreviewAction.new("open", _("Open"), None) preview.add_action(show_action) return preview # Classes below this point establish communication # with Unity, you probably shouldn't modify them. class MySearch(Unity.ScopeSearchBase): def __init__(self, search_context): super(MySearch, self).__init__() self.set_search_context(search_context) def do_run(self): ''' Adds results to the model ''' try: result_set = self.search_context.result_set for i in search(self.search_context.search_query, self.search_context.filter_state): if not 'uri' in i or not i['uri'] or i['uri'] == '': continue if not 'icon' in i or not i['icon'] or i['icon'] == '': i['icon'] = DEFAULT_RESULT_ICON if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '': i['mimetype'] = DEFAULT_RESULT_MIMETYPE if not 'result_type' in i or not i['result_type'] or i['result_type'] == '': i['result_type'] = DEFAULT_RESULT_TYPE if not 'category' in i or not i['category'] or i['category'] == '': i['category'] = 0 if not 'title' in i or not i['title']: i['title'] = '' if not 'comment' in i or not i['comment']: i['comment'] = '' if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': i['dnd_uri'] = i['uri'] i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) result_set.add_result(**i) except Exception as error: print(error) class Scope(Unity.AbstractScope): def __init__(self): Unity.AbstractScope.__init__(self) def do_get_search_hint(self): return SEARCH_HINT def do_get_schema(self): ''' Adds specific metadata fields ''' schema = Unity.Schema.new() if EXTRA_METADATA: for m in EXTRA_METADATA: schema.add_field(m['id'], m['type'], m['field']) #FIXME should be REQUIRED for credits schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL) return schema def do_get_categories(self): ''' Adds categories ''' cs = Unity.CategorySet.new() if CATEGORIES: for c in CATEGORIES: cat = Unity.Category.new(c['id'], c['name'], Gio.ThemedIcon.new(c['icon']), c['renderer']) cs.add(cat) return cs def do_get_filters(self): ''' Adds filters ''' fs = Unity.FilterSet.new() #if FILTERS: # return fs def do_get_group_name(self): return GROUP_NAME def do_get_unique_name(self): return UNIQUE_PATH def do_create_search_for_query(self, search_context): se = MySearch(search_context) return se def do_activate(self, result, metadata, action): ''' What to do when a resut is clicked ''' activation = activate(result, metadata, action) return activation def do_create_previewer(self, result, metadata): ''' Creates a preview when a resut is right-clicked ''' result_preview = Preview() result_preview.set_scope_result(result) result_preview.set_search_metadata(metadata) return result_preview def load_scope(): return Scope() unity-scope-gourmet-0.1+13.10.20130723/src/__init__.py0000644000015700001700000000000012173420454022532 0ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/setup.py0000644000015700001700000000157012173420454021361 0ustar pbuserpbgroup00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- ''' Setup file for gourmet dash plugin ''' from distutils.core import setup from DistUtilsExtra.command import build_extra from DistUtilsExtra.command import build_i18n setup(name='unity-scope-gourmet', version='0.1', author='Mark Tully', author_email='markjtully@gmail.com', url='http://launchpad.net/ubuntu-scopes', license='GNU General Public License (GPL)', data_files=[('share/dbus-1/services', ['data/unity-scope-gourmet.service']), #('share/icons/unity-icon-theme/places/svg', ['data/icons/service-gourmet.svg']), ('share/unity-scopes/gourmet', ['src/unity_gourmet_daemon.py']), ('share/unity-scopes/gourmet', ['src/__init__.py']), ], cmdclass={'build': build_extra.build_extra, 'build_i18n': build_i18n.build_i18n, }) unity-scope-gourmet-0.1+13.10.20130723/setup.cfg0000644000015700001700000000020612173420454021463 0ustar pbuserpbgroup00000000000000[build] i18n=True [build_i18n] domain=unity-scope-gourmet desktop_files=[('share/unity/scopes/recipes', ('data/gourmet.scope.in',))] unity-scope-gourmet-0.1+13.10.20130723/data/0000755000015700001700000000000012173421372020555 5ustar pbuserpbgroup00000000000000unity-scope-gourmet-0.1+13.10.20130723/data/gourmet.scope.in0000644000015700001700000000125612173420454023703 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.Recipes.Gourmet DBusPath=/com/canonical/unity/scope/recipes/gourmet Icon= QueryBinary=gourmet _Keywords=gourmet; RequiredMetadata= OptionalMetadata= Loader=/usr/share/unity-scopes/gourmet/unity_gourmet_daemon.py Module=gourmet.unity_gourmet_daemon ModuleType=Python3 RemoteContent=false Type=recipes _Name=Gourmet _Description=This is an Ubuntu search plugin that enables information from Gourmet to be searched and displayed in the Dash underneath the Recipes header. If you do not wish to search this content source, you can disable this search plugin. _SearchHint=Search Gourmet [Desktop Entry] X-Ubuntu-Gettext-Domain=unity-scope-gourmet unity-scope-gourmet-0.1+13.10.20130723/data/unity-scope-gourmet.service0000644000015700001700000000023312173420454026074 0ustar pbuserpbgroup00000000000000[D-BUS Service] Name=com.canonical.Unity.Scope.Recipes.Gourmet Exec=/usr/bin/python3 /usr/share/unity-scopes/scope-runner-dbus.py -s recipes/gourmet.scope unity-scope-gourmet-0.1+13.10.20130723/data/icons/0000755000015700001700000000000012173421372021670 5ustar pbuserpbgroup00000000000000