diff --git a/resources/recipes/science_based_medicine.recipe b/resources/recipes/science_based_medicine.recipe new file mode 100644 index 0000000000..7aa28cb170 --- /dev/null +++ b/resources/recipes/science_based_medicine.recipe @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import re +from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import Tag + +class SBM(BasicNewsRecipe): + title = 'Science Based Medicine' + __author__ = 'BuzzKill' + description = 'Exploring issues and controversies in the relationship between science and medicine' + oldest_article = 5 + max_articles_per_feed = 15 + no_stylesheets = True + use_embedded_content = False + encoding = 'utf-8' + publisher = 'SBM' + category = 'science, sbm, ebm, blog, pseudoscience' + language = 'en' + + lang = 'en-US' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } + + keep_only_tags = [ + dict(name='a', attrs={'title':re.compile(r'Posts by.*', re.DOTALL|re.IGNORECASE)}), + dict(name='div', attrs={'class':'entry'}) + ] + + feeds = [(u'Science Based Medicine', u'http://www.sciencebasedmedicine.org/?feed=rss2')] + + def preprocess_html(self, soup): + mtag = Tag(soup,'meta',[('http-equiv','Content-Type'),('context','text/html; charset=utf-8')]) + soup.head.insert(0,mtag) + soup.html['lang'] = self.lang + return self.adeify_images(soup) + diff --git a/resources/recipes/zeitde.recipe b/resources/recipes/zeitde.recipe index 64345ea675..389bdec670 100644 --- a/resources/recipes/zeitde.recipe +++ b/resources/recipes/zeitde.recipe @@ -60,8 +60,8 @@ class ZeitDe(BasicNewsRecipe): for tag in soup.findAll(name=['ul','li']): tag.name = 'div' - soup.html['xml:lang'] = self.lang - soup.html['lang'] = self.lang + soup.html['xml:lang'] = self.language.replace('_', '-') + soup.html['lang'] = self.language.replace('_', '-') mtag = '' soup.head.insert(0,mtag) return soup diff --git a/src/calibre/devices/errors.py b/src/calibre/devices/errors.py index 7464d6635e..3d88eb741f 100644 --- a/src/calibre/devices/errors.py +++ b/src/calibre/devices/errors.py @@ -36,6 +36,11 @@ class UserFeedback(DeviceError): self.details = details self.msg = msg +class OpenFeedback(DeviceError): + def __init__(self, msg): + self.feedback_msg = msg + DeviceError.__init__(self, msg) + class DeviceBusy(ProtocolError): """ Raised when device is busy """ def __init__(self, uerr=""): diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 48d751fc29..2a92f46e8d 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -216,6 +216,9 @@ class DevicePlugin(Plugin): an implementation of this function that should serve as a good example for USB Mass storage devices. + + This method can raise an OpenFeedback exception to display a message to + the user. ''' raise NotImplementedError() diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 008649f534..07bfeccc4f 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -12,7 +12,7 @@ from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \ from calibre.customize.ui import available_input_formats, available_output_formats, \ device_plugins from calibre.devices.interface import DevicePlugin -from calibre.devices.errors import UserFeedback +from calibre.devices.errors import UserFeedback, OpenFeedback from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner @@ -122,7 +122,8 @@ def device_name_for_plugboards(device_class): class DeviceManager(Thread): # {{{ - def __init__(self, connected_slot, job_manager, open_feedback_slot, sleep_time=2): + def __init__(self, connected_slot, job_manager, open_feedback_slot, + open_feedback_msg, sleep_time=2): ''' :sleep_time: Time to sleep between device probes in secs ''' @@ -143,6 +144,7 @@ class DeviceManager(Thread): # {{{ self.ejected_devices = set([]) self.mount_connection_requests = Queue.Queue(0) self.open_feedback_slot = open_feedback_slot + self.open_feedback_msg = open_feedback_msg def report_progress(self, *args): pass @@ -163,6 +165,9 @@ class DeviceManager(Thread): # {{{ dev.reset(detected_device=detected_device, report_progress=self.report_progress) dev.open() + except OpenFeedback, e: + self.open_feedback_msg(dev.get_gui_name(), e.feedback_msg) + continue except: tb = traceback.format_exc() if DEBUG or tb not in self.reported_errors: @@ -594,11 +599,16 @@ class DeviceMixin(object): # {{{ _('Error communicating with device'), ' ') self.device_error_dialog.setModal(Qt.NonModal) self.device_manager = DeviceManager(Dispatcher(self.device_detected), - self.job_manager, Dispatcher(self.status_bar.show_message)) + self.job_manager, Dispatcher(self.status_bar.show_message), + Dispatcher(self.show_open_feedback)) self.device_manager.start() if tweaks['auto_connect_to_folder']: self.connect_to_folder_named(tweaks['auto_connect_to_folder']) + def show_open_feedback(self, devname, msg): + self.__of_dev_mem__ = d = info_dialog(self, devname, msg) + d.show() + def auto_convert_question(self, msg, autos): autos = u'\n'.join(map(unicode, map(force_unicode, autos))) return self.ask_a_yes_no_question( diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 0d301ccaff..64d143ef3c 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -10,7 +10,6 @@ import os, sys, shutil, cStringIO, glob, time, functools, traceback, re from itertools import repeat from math import floor from Queue import Queue -from operator import itemgetter from PyQt4.QtGui import QImage @@ -1068,7 +1067,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def get_categories(self, sort='name', ids=None, icon_map=None): - start = time.time() + start = last = time.time() if icon_map is not None and type(icon_map) != TagsIcons: raise TypeError('icon_map passed to get_categories must be of type TagIcons') if sort not in self.CATEGORY_SORTS: @@ -1120,12 +1119,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # This saves iterating through field_metadata for each book md.append((category, cat['rec_index'], cat['is_multiple'])) - print 'end phase "collection":', time.time() - start, 'seconds' + print 'end phase "collection":', time.time() - last, 'seconds' + last = time.time() # Now scan every book looking for category items. # Code below is duplicated because it shaves off 10% of the loop time id_dex = self.FIELD_MAP['id'] rating_dex = self.FIELD_MAP['rating'] + tag_class = LibraryDatabase2.TCat_Tag for book in self.data.iterall(): if id_filter and book[id_dex] not in id_filter: continue @@ -1140,7 +1141,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): (item_id, sort_val) = tids[cat][val] # let exceptions fly item = tcategories[cat].get(val, None) if not item: - item = LibraryDatabase2.TCat_Tag(val, sort_val) + item = tag_class(val, sort_val) tcategories[cat][val] = item item.c += 1 item.id = item_id @@ -1156,7 +1157,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): (item_id, sort_val) = tids[cat][val] # let exceptions fly item = tcategories[cat].get(val, None) if not item: - item = LibraryDatabase2.TCat_Tag(val, sort_val) + item = tag_class(val, sort_val) tcategories[cat][val] = item item.c += 1 item.id = item_id @@ -1166,7 +1167,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): except: prints('get_categories: item', val, 'is not in', cat, 'list!') - print 'end phase "books":', time.time() - start, 'seconds' + print 'end phase "books":', time.time() - last, 'seconds' + last = time.time() # Now do news tcategories['news'] = {} @@ -1186,11 +1188,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): item.set_all(c=r[2], rt=r[2]*r[3], rc=r[2], id=r[0]) tcategories['news'][r[1]] = item - print 'end phase "news":', time.time() - start, 'seconds' + print 'end phase "news":', time.time() - last, 'seconds' + last = time.time() # Build the real category list by iterating over the temporary copy # and building the Tag instances. categories = {} + tag_class = Tag for category in tb_cats.keys(): if category not in tcategories: continue @@ -1232,7 +1236,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # sort the list if sort == 'name': - kf = lambda x: sort_key(x.s) if isinstance(x.s, unicode) else x.s + def get_sort_key(x): + sk = x.s + if isinstance(sk, unicode): + sk = sort_key(sk) + return sk + kf = get_sort_key reverse=False elif sort == 'popularity': kf = lambda x: x.c @@ -1242,12 +1251,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): reverse=True items.sort(key=kf, reverse=reverse) - categories[category] = [Tag(formatter(r.n), count=r.c, id=r.id, + categories[category] = [tag_class(formatter(r.n), count=r.c, id=r.id, avg=avgr(r), sort=r.s, icon=icon, tooltip=tooltip, category=category) for r in items] - print 'end phase "tags list":', time.time() - start, 'seconds' + print 'end phase "tags list":', time.time() - last, 'seconds' + last = time.time() # Needed for legacy databases that have multiple ratings that # map to n stars @@ -1333,8 +1343,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): icon_map['search'] = icon_map['search'] categories['search'] = items - t = time.time() - start - print 'get_categories ran in:', t, 'seconds' + print 'last phase ran in:', time.time() - last, 'seconds' + print 'get_categories ran in:', time.time() - start, 'seconds' return categories