~submarine/unity-scope-reddit/trunk

« back to all changes in this revision

Viewing changes to src/unity_reddit_daemon.py

  • Committer: David Callé
  • Date: 2013-02-21 11:50:58 UTC
  • Revision ID: davidc@framli.eu-20130221115058-n2aipwxksedw3dze
* New API scope
* Packaging

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python3
 
2
# -*- coding: utf-8 -*-
 
3
 
 
4
# Copyright (C) 2013 David Callé <davidc@framli.eu>
 
5
# This program is free software: you can redistribute it and/or modify it 
 
6
# under the terms of the GNU General Public License version 3, as published 
 
7
# by the Free Software Foundation.
 
8
 
9
# This program is distributed in the hope that it will be useful, but 
 
10
# WITHOUT ANY WARRANTY; without even the implied warranties of 
 
11
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
 
12
# PURPOSE.  See the GNU General Public License for more details.
 
13
 
14
# 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/>.
 
16
 
 
17
from gi.repository import Unity, UnityExtras
 
18
from gi.repository import Gio, GLib
 
19
import urllib.parse
 
20
import urllib.request
 
21
import json
 
22
import gettext
 
23
 
 
24
APP_NAME = 'unity-scope-reddit'
 
25
LOCAL_PATH = '/usr/share/locale/'
 
26
gettext.bindtextdomain(APP_NAME, LOCAL_PATH)
 
27
gettext.textdomain(APP_NAME)
 
28
_ = gettext.gettext
 
29
 
 
30
GROUP_NAME = 'com.canonical.Unity.Scope.Info.Reddit'
 
31
UNIQUE_PATH = '/com/canonical/unity/scope/info/reddit'
 
32
SEARCH_URI = 'https://reddit.com/'
 
33
SEARCH_HINT = _('Search Reddit')
 
34
NO_RESULTS_HINT = _('Sorry, there are no subreddits that match your search.')
 
35
PROVIDER_CREDITS = _('Powered by Reddit')
 
36
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
 
37
PROVIDER_ICON = SVG_DIR+'service-reddit.svg'
 
38
DEFAULT_RESULT_ICON = SVG_DIR+'result-info.svg'
 
39
DEFAULT_RESULT_MIMETYPE = 'text/html'
 
40
DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT
 
41
 
 
42
 
 
43
c1 = {'id'      :'hot',
 
44
      'name'    :_('Hot'),
 
45
      'icon'    :SVG_DIR+'group-installed.svg',
 
46
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
 
47
c2 = {'id'      :'new', 
 
48
      'name'    :_('New'),
 
49
      'icon'    :SVG_DIR+'group-installed.svg',
 
50
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
 
51
c2 = {'id'      :'controversial', 
 
52
      'name'    :_('Controversial'),
 
53
      'icon'    :SVG_DIR+'group-installed.svg',
 
54
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
 
55
c2 = {'id'      :'top', 
 
56
      'name'    :_('Top'),
 
57
      'icon'    :SVG_DIR+'group-installed.svg',
 
58
      'renderer':Unity.CategoryRenderer.VERTICAL_TILE}
 
59
CATEGORIES = [c1, c2]
 
60
 
 
61
FILTERS = []
 
62
 
 
63
m1 = {'id'   :'target',
 
64
      'type' :'s',
 
65
      'field':Unity.SchemaFieldType.OPTIONAL}
 
66
m2 = {'id'   :'author',
 
67
      'type' :'s',
 
68
      'field':Unity.SchemaFieldType.OPTIONAL}
 
69
EXTRA_METADATA = [m1, m2]
 
70
 
 
71
def search(search, filters):
 
72
    '''
 
73
    Any search method returning results as a list of tuples.
 
74
    Available tuple fields:
 
75
    uri (string)
 
76
    icon (string)
 
77
    title (string)
 
78
    comment (string)
 
79
    dnd_uri (string)
 
80
    mimetype (string)
 
81
    category (int)
 
82
    result_type (Unity ResultType)
 
83
    extras metadata fields (variant)
 
84
    '''
 
85
    results = []
 
86
    if not search:
 
87
        return results
 
88
    search = urllib.parse.quote(search)
 
89
    cats = ['hot', 'new', 'controversial', 'top']
 
90
    for cat in cats:
 
91
        uri = '%sr/%s/%s.json?limit=25' % (SEARCH_URI, search, cat)
 
92
        print(uri)
 
93
        data = None
 
94
        try:
 
95
            response = urllib.request.urlopen(uri).read()
 
96
            data = json.loads(response.decode('utf-8'))
 
97
        except Exception as error:
 
98
            print(error)
 
99
            if cat == 'hot':
 
100
                return results
 
101
            else:
 
102
                continue
 
103
        if not data or not 'data' in data:
 
104
            return results
 
105
        for d in data ['data']['children']:
 
106
            if d['data']['over_18'] is True:
 
107
                icon = None
 
108
            else:
 
109
                icon = d['data']['thumbnail']
 
110
            results.append({'uri':'%s%s' % (SEARCH_URI, d['data']['permalink']),
 
111
                            'title':d['data']['title'],
 
112
                            'icon':icon,
 
113
                            'target':GLib.Variant('s', d['data']['url']),
 
114
                            'author':GLib.Variant('s', d['data']['author']),
 
115
                            'category':cats.index(cat)})
 
116
    return results
 
117
 
 
118
 
 
119
# Classes below this point establish communication
 
120
# with Unity, you probably shouldn't modify them.
 
121
 
 
122
 
 
123
class MySearch (Unity.ScopeSearchBase):
 
124
    def __init__(self, search_context):
 
125
        super (MySearch, self).__init__()
 
126
        self.set_search_context (search_context)
 
127
 
 
128
    def do_run (self):
 
129
        '''
 
130
        Adds results to the model
 
131
        '''
 
132
        try:
 
133
            result_set = self.search_context.result_set
 
134
            for i in search(self.search_context.search_query,
 
135
                            self.search_context.filter_state):
 
136
                if not 'uri' in i or not i['uri'] or i['uri'] == '':
 
137
                    continue
 
138
                if not 'icon' in i or not i['icon'] or i['icon'] == '':
 
139
                    i['icon'] = DEFAULT_RESULT_ICON
 
140
                if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '':
 
141
                    i['mimetype'] = DEFAULT_RESULT_MIMETYPE
 
142
                if not 'result_type' in i or not i['result_type'] or i['result_type'] == '':
 
143
                    i['result_type'] = DEFAULT_RESULT_TYPE
 
144
                if not 'category' in i or not i['category'] or i['category'] == '':
 
145
                    i['category'] = 0
 
146
                if not 'title' in i or not i['title']:
 
147
                    i['title'] = ''
 
148
                if not 'comment' in i or not i['comment']:
 
149
                    i['comment'] = ''
 
150
                if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
 
151
                    i['dnd_uri'] = i['uri']
 
152
                i['metadata'] = {}
 
153
                if EXTRA_METADATA:
 
154
                    for e in i:
 
155
                        for m in EXTRA_METADATA:
 
156
                            if m['id'] == e:
 
157
                                i['metadata'][e] = i[e]
 
158
                i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
 
159
                result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']),
 
160
                                                  i['category'], i['result_type'],
 
161
                                                  str(i['mimetype']), str(i['title']),
 
162
                                                  str(i['comment']), str(i['dnd_uri']),
 
163
                                                  i['metadata'])
 
164
                result_set.add_result(result)
 
165
        except Exception as error:
 
166
            print (error)
 
167
 
 
168
class Scope (Unity.AbstractScope):
 
169
    def __init__(self):
 
170
        Unity.AbstractScope.__init__(self)
 
171
 
 
172
    def do_get_search_hint (self):
 
173
        return SEARCH_HINT
 
174
 
 
175
    def do_get_schema (self):
 
176
        '''
 
177
        Adds specific metadata fields
 
178
        '''
 
179
        schema = Unity.Schema.new ()
 
180
        if EXTRA_METADATA:
 
181
            for m in EXTRA_METADATA:
 
182
                schema.add_field(m['id'], m['type'], m['field'])
 
183
        #FIXME should be REQUIRED for credits
 
184
        schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL)
 
185
        return schema
 
186
 
 
187
    def do_get_categories (self):
 
188
        '''
 
189
        Adds categories
 
190
        '''
 
191
        cs = Unity.CategorySet.new ()
 
192
        if CATEGORIES:
 
193
            for c in CATEGORIES:
 
194
                cat = Unity.Category.new (c['id'], c['name'],
 
195
                                          Gio.ThemedIcon.new(c['icon']),
 
196
                                          c['renderer'])
 
197
                cs.add (cat)
 
198
        return cs
 
199
 
 
200
    def do_get_filters (self):
 
201
        '''
 
202
        Adds filters
 
203
        '''
 
204
        fs = Unity.FilterSet.new ()
 
205
#        if FILTERS:
 
206
#            
 
207
        return fs
 
208
 
 
209
    def do_get_group_name (self):
 
210
        return GROUP_NAME
 
211
 
 
212
    def do_get_unique_name (self):
 
213
        return UNIQUE_PATH
 
214
 
 
215
    def do_create_search_for_query (self, search_context):
 
216
        se = MySearch (search_context)
 
217
        return se
 
218
 
 
219
def load_scope():
 
220
    return Scope()