~markjtully/+junk/medicines-lens

« back to all changes in this revision

Viewing changes to unity-medicines-daemon

  • Committer: Mark Tully
  • Date: 2012-12-26 16:50:40 UTC
  • Revision ID: markjtully@gmail.com-20121226165040-hwu6puzy6uuqzjt0
Version 0.4:
  * Back to python2 again (due to Image library not being supported 
    in python3)
  * Implemented previews using chemical data from Wikipedia
  * Implemented privacy setting support
  * Now directly open PILs in evince directly rather than going to the download page

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python3
 
1
#!/usr/bin/python
2
2
# -*- coding: utf-8 -*-
3
3
 
4
4
"""
5
 
#    Copyright (C) 2011  Stefano Palazzo <stefano.palazzo@gmail.com>
6
 
#                  2011  Mark Tully <markjtully@gmail.com>
 
5
#    Copyright (C) 2011  Mark Tully <markjtully@gmail.com>
7
6
 
8
7
#    This program is free software: you can redistribute it and/or modify
9
8
#    it under the terms of the GNU General Public License as published by
20
19
"""
21
20
 
22
21
import sys
23
 
from urllib.request import urlopen
 
22
import urllib2
24
23
import re
25
24
import lxml.html
 
25
import json
 
26
import subprocess
 
27
from urllib import FancyURLopener
 
28
import Image
 
29
import os
26
30
 
27
31
# Unity imports
28
32
from gi.repository import GLib
32
36
 
33
37
BUS_NAME = "net.launchpad.lens.medicines"
34
38
 
35
 
PIL = 0
36
 
SPC = 1
 
39
PIL = 1
 
40
SPC = 2
 
41
 
 
42
 
 
43
class MyOpener(FancyURLopener):
 
44
    version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11'
37
45
 
38
46
 
39
47
class Daemon (object):
52
60
        is also a filter for searching different websites
53
61
        """
54
62
        self.location = self.get_location()
55
 
 
 
63
        if not os.path.isdir('/tmp/unity-lens-medicines'):
 
64
            os.makedirs('/tmp/unity-lens-medicines')
56
65
        self.lens = Unity.Lens.new("/net/launchpad/lens/medicines", "medicines")
57
66
        self.scope = Unity.Scope.new("/net/launchpad/lens/medicines/scope/ie")
58
67
 
64
73
        # Set up categories. Categories for all scopes must be set up here.
65
74
        # They can not be done from within the scopes themselves
66
75
        cats = []
 
76
        cats.append(Unity.Category.new("Messages",
 
77
                                        Gio.ThemedIcon.new("/usr/lib/unity-lens-medicines/icons/Rx.svg"),
 
78
                                        Unity.CategoryRenderer.HORIZONTAL_TILE))
67
79
        cats.append(Unity.Category.new("Patient Information Leaflets",
68
80
                                        Gio.ThemedIcon.new("/usr/lib/unity-lens-medicines/icons/Rx.svg"),
69
81
                                        Unity.CategoryRenderer.HORIZONTAL_TILE))
85
97
 
86
98
        # Listen for changes and requests
87
99
        self.scope.connect("search-changed", self.on_search_changed)
88
 
        self.scope.connect("filters-changed", self.on_filters_changed)
 
100
        self.scope.connect("filters-changed", self.on_filters_or_preferences_changed)
89
101
        self.scope.connect("notify::active", self.on_lens_active)
90
 
        #self.scope.connect("activate-uri", self.activate_uri)
 
102
        self.scope.connect("preview-uri", self.preview_uri)
 
103
        self.scope.connect("activate-uri", self.activate_uri)
 
104
 
 
105
        self.preferences = Unity.PreferencesManager.get_default()
 
106
        self.preferences.connect("notify::remote-content-search", self.on_filters_or_preferences_changed)
91
107
 
92
108
        self.lens.add_local_scope(self.scope)
93
109
        self.lens.export()
99
115
            The country the user's IP address is in as a 2 letter code
100
116
        """
101
117
        try:
102
 
            f = urlopen('http://geoip.ubuntu.com/lookup')
 
118
            f = urllib2.urlopen('http://geoip.ubuntu.com/lookup')
103
119
            content = f.read()
 
120
            content = content.decode("utf-8")
104
121
            country = re.findall('''\<CountryCode\>(.*)\</CountryCode\>''', content)[0].lower()
105
122
        except:
106
123
            country = "us"
107
124
        return country
108
125
 
109
 
    def on_filters_changed(self, *_):
 
126
    def on_filters_or_preferences_changed(self, *_):
110
127
        """ Called when a filter is clicked.  Queue's a new search
111
128
        """
112
129
        self.scope.queue_search_changed(Unity.SearchType.DEFAULT)
160
177
            if source.props.active:
161
178
                active_filters.append(source.props.id)
162
179
 
163
 
        if not search == "":
164
 
 
165
 
            if "ie" in active_filters:
166
 
                url = "http://www.medicines.ie/searchresults.aspx?term=%s&searchtype=AdvancedSearch" % search
167
 
                self.append_results(url, model)
168
 
 
169
 
            if "uk" in active_filters:
170
 
                url = "http://www.medicines.org.uk/EMC/searchresults.aspx?term=%s&searchtype=QuickSearch" % search
171
 
                self.append_results(url, model)
172
 
 
 
180
        if self.preferences.props.remote_content_search != Unity.PreferencesManagerRemoteContent.ALL:
 
181
            icon_hint = Gio.ThemedIcon.new("error").to_string()
 
182
            model.append("open-privacy-settings", 
 
183
                          icon_hint, 
 
184
                          0, 
 
185
                          "text/html", 
 
186
                          ("Online search results disabled"), 
 
187
                          ("Click here to change options"), 
 
188
                          "open-privacy-settings")
 
189
        else:
 
190
            if not search == "":
 
191
                if "ie" in active_filters:
 
192
                    url = "http://www.medicines.ie/searchresults.aspx?term=%s&searchtype=AdvancedSearch" % search
 
193
                    self.append_results(url, model)
 
194
    
 
195
                if "uk" in active_filters:
 
196
                    url = "http://www.medicines.org.uk/EMC/searchresults.aspx?term=%s&searchtype=QuickSearch" % search
 
197
                    self.append_results(url, model)
 
198
    
173
199
    def append_results(self, url, model):
174
200
        """ Parses the html of the page of the url and adds results to
175
201
        the model
188
214
                itemtype = item.xpath("td/table/tr/td/img/@alt")[0]
189
215
                uri = item.xpath("td/table/tr/td/a/@href")[0]
190
216
                company = item.xpath("td/a")[0]
191
 
                group = 1
 
217
                group = 2
192
218
                if itemtype == "PIL":
193
 
                    group = 0
 
219
                    group = 1
194
220
                    icon_hint = Gio.ThemedIcon.new("application-pdf").to_string()
195
221
                model.append(str(uri), icon_hint, group, "text/html", title.text_content().strip(), drug.text_content().strip() + "\n" + company.text_content().strip(), str(uri))
196
222
 
 
223
    def activate_uri(self, scope, uri):
 
224
        if re.search("PIL", uri):
 
225
            if uri.startswith("http://www.medicines.ie"):
 
226
                number = uri[33:]
 
227
                end = re.search("PIL", number)
 
228
                number = (number[:end.start() - 1])
 
229
                url = "http://www.medicines.ie/pdfviewer.aspx?isAttachment=true&documentid=%s" % number
 
230
            else:
 
231
                number = uri[41:]
 
232
                if re.search("XPIL", number):
 
233
                    end = re.search("XPIL", number)
 
234
                else:
 
235
                    end = re.search("PIL", number)
 
236
                number = (number[:end.start() - 1])
 
237
                url = "http://www.medicines.org.uk/EMC/pdfviewer.aspx?isAttachment=true&documentid=%s" % number
 
238
            command = []
 
239
            command.append("evince")
 
240
            command.append(url)
 
241
            subprocess.Popen(command)
 
242
            return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='')
 
243
        else:
 
244
            return Unity.ActivationResponse(handled=Unity.HandledType.NOT_HANDLED)
 
245
 
 
246
    def preview_uri(self, scope, uri):
 
247
        """Preview request handler"""
 
248
        albumtracks = []
 
249
        model = scope.props.results_model
 
250
        iteration = model.get_first_iter()
 
251
        end_iter = model.get_last_iter()
 
252
        self.preview = None
 
253
        while iteration != end_iter:
 
254
            if model.get_value(iteration, 0) == uri:
 
255
                title = model.get_value(iteration, 4)
 
256
                description = model.get_value(iteration, 5).title() + "\n"
 
257
 
 
258
                if model.get_value(iteration, 2) == 1:
 
259
                    document_type = "PIL"
 
260
                    url = "http://www.medicines.ie/pdfviewer.aspx?isAttachment=true&documentid=7703"
 
261
                else:
 
262
                    document_type = "SPC"
 
263
                    url = uri
 
264
                # Get the appropriate wikipedia page for the drug in the product
 
265
                word = description.split()[0]
 
266
                if word[-1] == ",":
 
267
                    word = word[0:len(word) - 1]
 
268
 
 
269
                wikipage = "http://en.wikipedia.org/wiki/%s" % word.title()
 
270
                tree = lxml.html.parse(wikipage)
 
271
                tree.getroot().make_links_absolute()
 
272
 
 
273
                # Get details fro the infobox on the wiki page for the preview description
 
274
                link = tree.xpath("//table[@class='infobox']")[0]
 
275
                infobox = link.text_content()
 
276
                infobox = infobox.replace("\n", "\t")
 
277
 
 
278
                try:
 
279
                    match1 = re.search("Bioavailability", infobox)
 
280
                    match2 = re.search("Metabolism", infobox)
 
281
                    description = description + "\n<b>Bioavailability:</b>\t" + infobox[match1.end():match2.start()]
 
282
                except:
 
283
                    print("Bioavailability unavailable")
 
284
 
 
285
                try:
 
286
                    match1 = re.search("Metabolism", infobox)
 
287
                    match2 = re.search("Half-life", infobox)
 
288
                    description = description + "\n<b>Metabolism:</b>\t" + infobox[match1.end():match2.start()]
 
289
                except:
 
290
                    print("Metabolism unavailable")
 
291
 
 
292
                try:
 
293
                    match1 = re.search("Half-life", infobox)
 
294
                    match2 = re.search("Excretion", infobox)
 
295
                    description = description + "\n<b>Half-life:</b>\t\t" + infobox[match1.end():match2.start()]
 
296
                except:
 
297
                    print("Half Life unavailable")
 
298
 
 
299
                try:
 
300
                    match1 = re.search("Excretion", infobox)
 
301
                    match2 = re.search("Identifiers", infobox)
 
302
                    description = description + "\n<b>Excretion:</b>\t\t" + infobox[match1.end():match2.start()]
 
303
                except:
 
304
                    print("Excretion unavailable")
 
305
 
 
306
                try:
 
307
                    match1 = re.search("Formula", infobox)
 
308
                    match2 = re.search("Mol", infobox)
 
309
                    description = description + "\n<b>Formula:</b>\t\t" + infobox[match1.end():match2.start()]
 
310
                except:
 
311
                    print("Formula unavailable")
 
312
 
 
313
                try:
 
314
                    match1 = re.search("Mol", infobox)
 
315
                    match2 = re.search("g/mol", infobox)
 
316
                    description = description + "\n<b>Molecular Mass:</b>" + infobox[match1.end() + 6:match1.end()+ 14] + " g/mol"
 
317
                except:
 
318
                    print("Molecular Mass unavailable")
 
319
 
 
320
                try:
 
321
                    match1 = re.search("ATC code", infobox)
 
322
                    match2 = re.search("PubChem", infobox)
 
323
                    description = description + "\n<b>ATC Code:</b>\t\t" + infobox[match1.end():match2.start()]
 
324
                except:
 
325
                    print("ATC Code unavailable")
 
326
 
 
327
                try:
 
328
                    match1 = re.search("Pregnancy cat.", infobox)
 
329
                    match2 = re.search("Legal status", infobox)
 
330
                    description = description + "\n<b>Pregnancy Category:</b>" + infobox[match1.end():match2.start()]
 
331
                except:
 
332
                    print("Pregnancy Category unavailable")
 
333
 
 
334
                try:
 
335
                    match1 = re.search(" name", infobox)
 
336
                    match2 = re.search("Clinical data", infobox)
 
337
                    description = description + "\n\n<b>Systematic (IUPAC) name:</b>\n" + infobox[match1.end():match2.start()].replace("\t", "")
 
338
                except:
 
339
                    print("Systematic (IUPAC) name unavailable")
 
340
                    
 
341
                description += "\n\n<small>Chemical entity information from Wikipedia</small>"
 
342
 
 
343
                if not os.path.exists('/tmp/unity-lens-medicines/%s.png' % word.title()):
 
344
                    # Get the preview image from Wikipedia
 
345
                    link = tree.xpath("//a[@class='image']/img/@src")[0]
 
346
                    image = link
 
347
                    # Change the image size to something better for previews
 
348
                    match = re.search(r"px-", link)
 
349
                    if match:
 
350
                        size = link[match.start()-3:match.start()]
 
351
                        if not re.search("png/", link):
 
352
                            image = link.replace(size, "500")
 
353
    
 
354
                    myopener = MyOpener()
 
355
                    myopener.retrieve(image, "/tmp/unity-lens-medicines/image.png")
 
356
        
 
357
                    try:
 
358
                        img = Image.open('/tmp/unity-lens-medicines/image.png','r')
 
359
                        img_w,img_h=img.size
 
360
                        background = Image.new('RGBA', (img_w,img_h), (255, 255, 255, 255))
 
361
                        bg_w,bg_h=background.size
 
362
                        offset=((bg_w-img_w)/2,(bg_h-img_h)/2)
 
363
                        background.paste(img,offset, img)
 
364
                        background.save('/tmp/unity-lens-medicines/%s.png' % word.title())
 
365
                    except:
 
366
                        myopener.retrieve(image, '/tmp/unity-lens-medicines/%s.png' % word.title())
 
367
                        print("No image transformation performed")
 
368
                
 
369
                # Assemble the preview
 
370
                self.preview = Unity.GenericPreview.new(title, description, None)
 
371
                self.preview.props.image_source_uri = 'file:///tmp/unity-lens-medicines/%s.png' % word.title()
 
372
                
 
373
                # Add the "View" action
 
374
                view_action = Unity.PreviewAction.new("activate_uri", "View %s" % (document_type), None)
 
375
                view_action.connect("activated", self.activate_uri)
 
376
                self.preview.add_action(view_action)
 
377
                break
 
378
 
 
379
            iteration = model.next(iteration)
 
380
        if self.preview is None:
 
381
            print ("Couldn't find model row for requested preview uri: '%s'", uri)
 
382
        return self.preview
 
383
 
197
384
if __name__ == "__main__":
198
385
    session_bus_connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
199
386
    session_bus = Gio.DBusProxy.new_sync(session_bus_connection, 0, None,
204
391
    result = result.unpack()[0]
205
392
    # We could try to do some automated rescue when this happens:
206
393
    if result != 1:
207
 
        print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME
208
 
        print >> sys.stderr, "Do you have another instance running?"
 
394
        print("Failed to own name %s. Bailing out." % BUS_NAME)
 
395
        print("Do you have another instance running?")
209
396
        raise SystemExit(1)
210
397
    daemon = Daemon()
211
398
    print("entering the main loop")