From c77684a03314a93b9f8343e9ce99e2b7129098e4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 09:52:22 -0600 Subject: [PATCH 01/18] When automatically generating author sort for author name, ignore common prefixes like Mr. Dr. etc. Controllable via tweak. Also add a tweak to allow control of how a string is split up into multiple authors. Fixes #795984 ([Request] Extra author sorting options) --- resources/default_tweaks.py | 9 +++++++++ src/calibre/ebooks/metadata/__init__.py | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index b385511d56..ead9995eb3 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -70,9 +70,18 @@ author_sort_copy_method = 'comma' author_name_suffixes = ('Jr', 'Sr', 'Inc', 'Ph.D', 'Phd', 'MD', 'M.D', 'I', 'II', 'III', 'IV', 'Junior', 'Senior') +author_name_prefixes = ('Mr', 'Mrs', 'Ms', 'Dr', 'Prof') author_name_copywords = ('Corporation', 'Company', 'Co.', 'Agency', 'Council', 'Committee', 'Inc.', 'Institute', 'Society', 'Club', 'Team') +#: Splitting multiple author names +# By default, calibre splits a string containing multiple author names on +# ampersands and the words "and" and "with". You can customize the splitting +# by changing the regular expression below. Strings are split on whatever the +# specified regular expression matches. +# Default: r'(?i),?\s+(and|with)\s+' +authors_split_regex = r'(?i),?\s+(and|with)\s+' + #: Use author sort in Tag Browser # Set which author field to display in the tags pane (the list of authors, # series, publishers etc on the left hand side). The choices are author and diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index a9816db5ae..c3a229fe3c 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -10,11 +10,17 @@ import os, sys, re from urllib import unquote, quote from urlparse import urlparse -from calibre import relpath, guess_type, remove_bracketed_text +from calibre import relpath, guess_type, remove_bracketed_text, prints from calibre.utils.config import tweaks -_author_pat = re.compile(',?\s+(and|with)\s+', re.IGNORECASE) +try: + _author_pat = re.compile(tweaks['authors_split_regex']) +except: + prints ('Author split regexp:', tweaks['authors_split_regex'], + 'is invalid, using default') + _author_pat = re.compile(r'(?i),?\s+(and|with)\s+') + def string_to_authors(raw): raw = raw.replace('&&', u'\uffff') raw = _author_pat.sub('&', raw) @@ -45,6 +51,17 @@ def author_to_author_sort(author, method=None): if method == u'copy': return author + prefixes = set([x.lower() for x in tweaks['author_name_prefixes']]) + prefixes |= set([x+u'.' for x in prefixes]) + while True: + if not tokens: + return author + tok = tokens[0].lower() + if tok in prefixes: + tokens = tokens[1:] + else: + break + suffixes = set([x.lower() for x in tweaks['author_name_suffixes']]) suffixes |= set([x+u'.' for x in suffixes]) From 2ba566fa41edcabce736e0069150f2f0558eb828 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 10:22:14 -0600 Subject: [PATCH 02/18] ... --- src/calibre/gui2/preferences/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index 06a503f855..20507b4ce1 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -235,11 +235,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.highlight_index(idx) def highlight_index(self, idx): - self.plugin_view.scrollTo(idx) self.plugin_view.selectionModel().select(idx, self.plugin_view.selectionModel().ClearAndSelect) self.plugin_view.setCurrentIndex(idx) self.plugin_view.setFocus(Qt.OtherFocusReason) + self.plugin_view.scrollTo(idx, self.plugin_view.EnsureVisible) def find_next(self, *args): idx = self.plugin_view.currentIndex() From 43661588b05d21b9c95be146df29e9f3ec7bb412 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 12:56:01 -0600 Subject: [PATCH 03/18] ... --- src/calibre/gui2/actions/__init__.py | 4 +++- src/calibre/gui2/keyboard.py | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index 4f42a1d2bc..7b07cdf732 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -15,6 +15,8 @@ from calibre import prints from calibre.gui2 import Dispatcher from calibre.gui2.keyboard import NameConflict +def menu_action_unique_name(plugin, unique_name): + return u'%s : menu action : %s'%(plugin.unique_name, unique_name) class InterfaceAction(QObject): @@ -214,7 +216,7 @@ class InterfaceAction(QObject): if shortcut is not None and shortcut is not False: keys = ((shortcut,) if isinstance(shortcut, basestring) else tuple(shortcut)) - unique_name = '%s : menu action : %s'%(self.unique_name, unique_name) + unique_name = menu_action_unique_name(self, unique_name) if description is not None: ac.setToolTip(description) ac.setStatusTip(description) diff --git a/src/calibre/gui2/keyboard.py b/src/calibre/gui2/keyboard.py index 6413b216ce..9b0b1d8f69 100644 --- a/src/calibre/gui2/keyboard.py +++ b/src/calibre/gui2/keyboard.py @@ -116,6 +116,11 @@ class Manager(QObject): # {{{ done unregistering. ''' self.shortcuts.pop(unique_name, None) + for group in self.groups.itervalues(): + try: + group.remove(unique_name) + except ValueError: + pass def finalize(self): custom_keys_map = {un:tuple(keys) for un, keys in self.config.get( From dfbc4b5a67444899fc7bc866a76476bccc406374 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 15:02:31 -0600 Subject: [PATCH 04/18] Fix #841201 (EPUB language identifier not conforming to spec) --- src/calibre/ebooks/metadata/epub.py | 10 ++++++++++ src/calibre/ebooks/oeb/output.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index 27fa94e217..30fe53f1a2 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -16,6 +16,7 @@ from calibre.ebooks.metadata.opf2 import OPF from calibre.ptempfile import TemporaryDirectory, PersistentTemporaryFile from calibre import CurrentDir, walk from calibre.constants import isosx +from calibre.utils.localization import lang_as_iso639_1 class EPubException(Exception): pass @@ -231,6 +232,15 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False): for x in ('guide', 'toc', 'manifest', 'spine'): setattr(mi, x, None) + if mi.languages: + langs = [] + for lc in mi.languages: + lc2 = lang_as_iso639_1(lc) + if lc2: lc = lc2 + langs.append(lc) + mi.languages = langs + + reader.opf.smart_update(mi) if apply_null: if not getattr(mi, 'series', None): diff --git a/src/calibre/ebooks/oeb/output.py b/src/calibre/ebooks/oeb/output.py index 816fe71abb..38ac2495fd 100644 --- a/src/calibre/ebooks/oeb/output.py +++ b/src/calibre/ebooks/oeb/output.py @@ -43,6 +43,7 @@ class OEBOutput(OutputFormatPlugin): except: self.log.exception('Something went wrong while trying to' ' workaround Pocketbook cover bug, ignoring') + self.migrate_lang_code(root) raw = etree.tostring(root, pretty_print=True, encoding='utf-8', xml_declaration=True) if key == OPF_MIME: @@ -104,3 +105,12 @@ class OEBOutput(OutputFormatPlugin): p.remove(m) p.insert(0, m) # }}} + + def migrate_lang_code(self, root): # {{{ + from calibre.utils.localization import lang_as_iso639_1 + for lang in root.xpath('//*[local-name() = "language"]'): + clc = lang_as_iso639_1(lang.text) + if clc: + lang.text = clc + # }}} + From e0dc62c54479d7f8fe7c4c1b8d02c150303df28a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 18:47:52 -0600 Subject: [PATCH 05/18] Fix #841236 (Need Support for Pandigital Nova Tablet) --- src/calibre/devices/android/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 5785649739..a068590df4 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -127,7 +127,7 @@ class ANDROID(USBMS): VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS', 'TELECHIP', 'HUAWEI', 'T-MOBILE', 'SEMC', 'LGE', 'NVIDIA', - 'GENERIC-', 'ZTE', 'MID', 'QUALCOMM'] + 'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT'] WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', @@ -137,11 +137,11 @@ class ANDROID(USBMS): '7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2', 'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK', 'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612', - 'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A'] + 'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A', 'ALPANDIGITAL'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', - '__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870'] + '__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL'] OSX_MAIN_MEM = 'Android Device Main Memory' From 0d3cef5022c611de5f3a0cc23acfae877aa35518 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Sep 2011 19:50:01 -0600 Subject: [PATCH 06/18] EPUB catalog generation: Fix some entries going off the left edge of the page for unread/wishlist items --- src/calibre/library/catalog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 5ea8b4b49f..51aefe214b 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -5113,6 +5113,8 @@ Author '{0}': if catalog_source_built: recommendations = [] + recommendations.append(('remove_fake_margins', False, + OptionRecommendation.HIGH)) if DEBUG: recommendations.append(('comments', '\n'.join(line for line in build_log), OptionRecommendation.HIGH)) From 9e0d5aef2f30b317670a5e244315688501e191d9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Sep 2011 10:03:14 -0600 Subject: [PATCH 07/18] ... --- src/calibre/db/tests/metadata.db | Bin 0 -> 224256 bytes src/calibre/db/tests/reading.py | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/calibre/db/tests/metadata.db create mode 100644 src/calibre/db/tests/reading.py diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db new file mode 100644 index 0000000000000000000000000000000000000000..174345276e205e17b15923cc641588517f7d4822 GIT binary patch literal 224256 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 Date: Mon, 5 Sep 2011 10:43:03 -0600 Subject: [PATCH 08/18] Fix #841588 (Jobs shows "can't stop device tasks" for every stopped device job) --- src/calibre/gui2/jobs.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py index 076a9720a3..b7992eb319 100644 --- a/src/calibre/gui2/jobs.py +++ b/src/calibre/gui2/jobs.py @@ -21,7 +21,7 @@ from calibre.utils.ipc.job import ParallelJob from calibre.gui2 import Dispatcher, error_dialog, question_dialog, NONE, config, gprefs from calibre.gui2.device import DeviceJob from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog -from calibre import __appname__ +from calibre import __appname__, as_unicode from calibre.gui2.dialogs.job_view_ui import Ui_Dialog from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.threaded_jobs import ThreadedJobServer, ThreadedJob @@ -264,6 +264,26 @@ class JobManager(QAbstractTableModel): # {{{ _('This job cannot be stopped'), show=True) self._kill_job(job) + def kill_multiple_jobs(self, rows, view): + jobs = [self.jobs[row] for row in rows] + devjobs = [j for j in jobs is isinstance(j, DeviceJob)] + if devjobs: + error_dialog(view, _('Cannot kill job'), + _('Cannot kill jobs that communicate with the device')).exec_() + jobs = [j for j in jobs if not isinstance(j, DeviceJob)] + jobs = [j for j in jobs if j.duration is None] + unkillable = [j for j in jobs if not getattr(j, 'killable', True)] + if unkillable: + names = u'\n'.join(as_unicode(j.description) for j in unkillable) + error_dialog(view, _('Cannot kill job'), + _('Some of the jobs cannot be stopped. Click Show details' + ' to see the list of unstoppable jobs.'), det_msg=names, + show=True) + jobs = [j for j in jobs if getattr(j, 'killable', True)] + jobs = [j for j in jobs if j.duration is None] + for j in jobs: + self._kill_job(j) + def kill_all_jobs(self): for job in self.jobs: if (isinstance(job, DeviceJob) or job.duration is not None or @@ -484,8 +504,10 @@ class JobsDialog(QDialog, Ui_JobsDialog): ngettext('Do you really want to stop the selected job?', 'Do you really want to stop all the selected jobs?', len(rows))): - for row in rows: - self.model.kill_job(row, self) + if len(rows) > 1: + self.model.kill_multiple_jobs(rows, self) + else: + self.model.kill_job(rows[0], self) def kill_all_jobs(self, *args): if question_dialog(self, _('Are you sure?'), From 48363e9d69d9c3c9434298aceb175af22b8f6726 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 11:00:52 -0600 Subject: [PATCH 09/18] Allow downloading metadata from amazon.co.jp. To use it, configure the amazon metadata source to use the Japanese amazon site. Fixes #842447 ([improvement]add support for Amazon Japan metadata download) --- src/calibre/db/tests/metadata.db | Bin 224256 -> 230400 bytes src/calibre/db/tests/reading.py | 16 +++- src/calibre/ebooks/metadata/sources/amazon.py | 72 ++++++++++++++++-- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db index 174345276e205e17b15923cc641588517f7d4822..8fb89cbb8e87ff051c764bb653fef03e64094284 100644 GIT binary patch delta 8468 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 delta 1278 zcmd5*T}&KR6rMZZo!tSU>=p`0Y1~3;OTcuEAc@vef&p70U1FFsS=cNL-R2)LZfgO@?_*d|uwn4Dw5!Dm+Bu$VvU%7=@{jBnxNhs=HNVZj_VcHfu!>J{ zQmI!IyYlKqMlA-ybjmoHLlq;Lt5`3X4R2-5|ofYqQ!58U#0^4YN z0+l5Cv57JXl+a=V74$&@<+?dIW=OYYx}QKNecX>BwO*sUU8v7xuGLmnfGZ6_Gk((O zi<(9}ePX~c$1}OewW``0#+B!{98aZ^W;C8QqE>p)wBnXz>W=^N^*(6&c62d1+lg(O z-xKe3Nl~jt3!}+BsL0KI*YSj^1&=mK#hQO6!fn3tuu-sM4EiiT#w4B1e3mycc}VTa zQNX)5;xQH|;MO`WcrzFm+G*x=c(Gpdgm#lTUQ1^AGLz-q5;D8Zr&5tB=i*jph-zRG;eLQQ4&Glnef|s_McwC4B{7R#h&1(^CHTb@DQ1z2b$9 zDZPrh z4xLKVC=*BbgH(F;AnK^P8=uf{1aG*GKhj(Tj_5puhpMaJq~6ytLiStep?xvvqT~o# z4w~rF_22r zd^>i~!3pfAPzo>8MGN1$ehVTs3SAY4PvI{)rHZmh?Qf%sV&lkD%@LWq{5?!kXadWE zXK~B(QN-dTjxuU^SMu@Z`#4Xx+wqK>y_bgL2+(2=N`(BuR;Ctba(}c|Z1%~e#IN`s t;{Ujz&;G}T8diGLD3z=*oKbMb4(^&l(T&}@PidIUcTj#0;x1=5{sA`}eBl59 diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index e9c8be83b8..6149839a88 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -8,7 +8,7 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, shutil#, unittest +import os, shutil, unittest, tempfile def create_db(library_path): from calibre.library.database2 import LibraryDatabase2 @@ -17,5 +17,17 @@ def create_db(library_path): src = os.path.join(os.path.dirname(__file__), 'metadata.db') db = os.path.join(library_path, 'metadata.db') shutil.copyfile(src, db) - db = LibraryDatabase2(library_path) + return db + +class ReadingTest(unittest.TestCase): + + def setUp(self): + self.library_path = tempfile.mkdtemp() + create_db(self.library_path) + + def tearDown(self): + shutil.rmtree(self.library_path) + + def test_read(self): + pass diff --git a/src/calibre/ebooks/metadata/sources/amazon.py b/src/calibre/ebooks/metadata/sources/amazon.py index aaa13d5769..fa72766a0a 100644 --- a/src/calibre/ebooks/metadata/sources/amazon.py +++ b/src/calibre/ebooks/metadata/sources/amazon.py @@ -74,6 +74,20 @@ class Worker(Thread): # Get details {{{ 9: ['sept'], 12: ['déc'], }, + 'jp': { + 1: [u'1月'], + 2: [u'2月'], + 3: [u'3月'], + 4: [u'4月'], + 5: [u'5月'], + 6: [u'6月'], + 7: [u'7月'], + 8: [u'8月'], + 9: [u'9月'], + 10: [u'10月'], + 11: [u'11月'], + 12: [u'12月'], + }, } @@ -86,13 +100,15 @@ class Worker(Thread): # Get details {{{ text()="Produktinformation" or \ text()="Dettagli prodotto" or \ text()="Product details" or \ - text()="Détails sur le produit"]/../div[@class="content"] + text()="Détails sur le produit" or \ + text()="登録情報"]/../div[@class="content"] ''' self.publisher_xpath = ''' descendant::*[starts-with(text(), "Publisher:") or \ starts-with(text(), "Verlag:") or \ starts-with(text(), "Editore:") or \ - starts-with(text(), "Editeur")] + starts-with(text(), "Editeur") or \ + starts-with(text(), "出版社:")] ''' self.language_xpath = ''' descendant::*[ @@ -101,10 +117,11 @@ class Worker(Thread): # Get details {{{ or text() = "Sprache:" \ or text() = "Lingua:" \ or starts-with(text(), "Langue") \ + or starts-with(text(), "言語") \ ] ''' self.ratings_pat = re.compile( - r'([0-9.]+) (out of|von|su|étoiles sur) (\d+)( (stars|Sternen|stelle)){0,1}') + r'([0-9.]+) ?(out of|von|su|étoiles sur|つ星のうち) ([\d\.]+)( (stars|Sternen|stelle)){0,1}') lm = { 'eng': ('English', 'Englisch'), @@ -112,6 +129,7 @@ class Worker(Thread): # Get details {{{ 'ita': ('Italian', 'Italiano'), 'deu': ('German', 'Deutsch'), 'spa': ('Spanish', 'Espa\xf1ol', 'Espaniol'), + 'jpn': ('Japanese', u'日本語'), } self.lang_map = {} for code, names in lm.iteritems(): @@ -403,6 +421,7 @@ class Amazon(Source): 'de' : _('Germany'), 'uk' : _('UK'), 'it' : _('Italy'), + 'jp' : _('Japan'), } options = ( @@ -411,6 +430,22 @@ class Amazon(Source): 'country\'s Amazon website.'), choices=AMAZON_DOMAINS), ) + def __init__(self, *args, **kwargs): + Source.__init__(self, *args, **kwargs) + self.set_amazon_id_touched_fields() + + def save_settings(self, *args, **kwargs): + Source.save_settings(self, *args, **kwargs) + self.set_amazon_id_touched_fields() + + def set_amazon_id_touched_fields(self): + ident_name = "identifier:amazon" + if self.domain != 'com': + ident_name += '_' + self.domain + tf = [x for x in self.touched_fields if not + x.startswith('identifier:amazon')] + [ident_name] + self.touched_fields = frozenset(tf) + def get_domain_and_asin(self, identifiers): for key, val in identifiers.iteritems(): key = key.lower() @@ -488,13 +523,23 @@ class Amazon(Source): # Insufficient metadata to make an identify query return None, None - latin1q = dict([(x.encode('latin1', 'ignore'), y.encode('latin1', + # magic parameter to enable Japanese Shift_JIS encoding. + if domain == 'jp': + q['__mk_ja_JP'] = u'カタカナ' + + if domain == 'jp': + encode_to = 'Shift_JIS' + else: + encode_to = 'latin1' + encoded_q = dict([(x.encode(encode_to, 'ignore'), y.encode(encode_to, 'ignore')) for x, y in q.iteritems()]) udomain = domain if domain == 'uk': udomain = 'co.uk' - url = 'http://www.amazon.%s/s/?'%udomain + urlencode(latin1q) + elif domain == 'jp': + udomain = 'co.jp' + url = 'http://www.amazon.%s/s/?'%udomain + urlencode(encoded_q) return url, domain # }}} @@ -663,7 +708,7 @@ if __name__ == '__main__': # tests {{{ # To run these test use: calibre-debug -e # src/calibre/ebooks/metadata/sources/amazon.py from calibre.ebooks.metadata.sources.test import (test_identify_plugin, - title_test, authors_test) + isbn_test, title_test, authors_test) com_tests = [ # {{{ ( # Description has links @@ -744,6 +789,21 @@ if __name__ == '__main__': # tests {{{ ), ] # }}} + jp_tests = [ # {{{ + ( # isbn -> title, authors + {'identifiers':{'isbn': '9784101302720' }}, + [title_test(u'精霊の守り人', + exact=True), authors_test([u'上橋 菜穂子']) + ] + ), + ( # title, authors -> isbn (will use Shift_JIS encoding in query.) + {'title': u'考えない練習', + 'authors': [u'小池 龍之介']}, + [isbn_test('9784093881067'), ] + ), + ] # }}} + test_identify_plugin(Amazon.name, com_tests) + #test_identify_plugin(Amazon.name, jp_tests) # }}} From 6695f0d346f84af9ab30d095f5b321a8a86d3203 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 11:52:37 -0600 Subject: [PATCH 10/18] Fix #842882 (calibre.epub has 2 copies of content) --- src/calibre/gui2/actions/__init__.py | 28 ++++++++++---------- src/calibre/manual/epub.py | 38 +++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index 7b07cdf732..fa67130a1c 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -180,30 +180,30 @@ class InterfaceAction(QObject): description=None, triggered=None, shortcut_name=None): ''' Convenience method to easily add actions to a QMenu. + Returns the created QAction, This action has one extra attribute + calibre_shortcut_unique_name which if not None refers to the unique + name under which this action is registered with the keyboard manager. :param menu: The QMenu the newly created action will be added to :param unique_name: A unique name for this action, this must be - globally unique, so make it as descriptive as possible. If in doubt add - a uuid to it. + globally unique, so make it as descriptive as possible. If in doubt add + a uuid to it. :param text: The text of the action. :param icon: Either a QIcon or a file name. The file name is passed to - the I() builtin, so you do not need to pass the full path to the images - directory. + the I() builtin, so you do not need to pass the full path to the images + directory. :param shortcut: A string, a list of strings, None or False. If False, - no keyboard shortcut is registered for this action. If None, a keyboard - shortcut with no default keybinding is registered. String and list of - strings register a shortcut with default keybinding as specified. + no keyboard shortcut is registered for this action. If None, a keyboard + shortcut with no default keybinding is registered. String and list of + strings register a shortcut with default keybinding as specified. :param description: A description for this action. Used to set - tooltips. + tooltips. :param triggered: A callable which is connected to the triggered signal - of the created action. + of the created action. :param shortcut_name: The test displayed to the user when customizing - the keyboard shortcuts for this action. By default it is set to the - value of ``text``. + the keyboard shortcuts for this action. By default it is set to the + value of ``text``. - :return: The created QAction, This action has one extra attribute - calibre_shortcut_unique_name which if not None refers to the unique - name under which this action is registered with the keyboard manager. ''' if shortcut_name is None: shortcut_name = unicode(text) diff --git a/src/calibre/manual/epub.py b/src/calibre/manual/epub.py index a162303b09..1aadbe9f91 100644 --- a/src/calibre/manual/epub.py +++ b/src/calibre/manual/epub.py @@ -6,7 +6,9 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, time +import os, time, glob + +from lxml import etree from sphinx.builders.epub import EpubBuilder @@ -55,4 +57,38 @@ class EPUBHelpBuilder(EpubBuilder): def build_epub(self, outdir, *args, **kwargs): if self.config.epub_cover: self.add_cover(outdir, self.config.epub_cover) + self.fix_duplication_bugs(outdir) EpubBuilder.build_epub(self, outdir, *args, **kwargs) + + def fix_duplication_bugs(self, outdir): + opf = glob.glob(outdir+os.sep+'*.opf')[0] + root = etree.fromstring(open(opf, 'rb').read()) + seen = set() + for x in root.xpath( + '//*[local-name()="spine"]/*[local-name()="itemref"]'): + idref = x.get('idref') + if idref in seen: + x.getparent().remove(x) + else: + seen.add(idref) + + with open(opf, 'wb') as f: + f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True)) + + + ncx = glob.glob(outdir+os.sep+'*.ncx')[0] + root = etree.fromstring(open(ncx, 'rb').read()) + seen = set() + for x in root.xpath( + '//*[local-name()="navMap"]/*[local-name()="navPoint"]'): + text = x.xpath('descendant::*[local-name()="text"]')[0] + text = text.text + if text in seen: + x.getparent().remove(x) + else: + seen.add(text) + + with open(ncx, 'wb') as f: + f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True)) + + From fd36d1a717a5a426484218806cc60eb11659ed49 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 12:27:54 -0600 Subject: [PATCH 11/18] Driver of Motorola Ex124G --- src/calibre/customize/builtins.py | 4 ++-- src/calibre/devices/misc.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 9d9cbd1d09..1474b540ee 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -571,7 +571,7 @@ from calibre.devices.teclast.driver import (TECLAST_K3, NEWSMY, IPAPYRUS, from calibre.devices.sne.driver import SNE from calibre.devices.misc import (PALMPRE, AVANT, SWEEX, PDNOVEL, GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, LUMIREAD, ALURATEK_COLOR, - TREKSTOR, EEEREADER, NEXTBOOK, ADAM, MOOVYBOOK, COBY) + TREKSTOR, EEEREADER, NEXTBOOK, ADAM, MOOVYBOOK, COBY, EX124G) from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.kobo.driver import KOBO from calibre.devices.bambook.driver import BAMBOOK @@ -707,7 +707,7 @@ plugins += [ EEEREADER, NEXTBOOK, ADAM, - MOOVYBOOK, COBY, + MOOVYBOOK, COBY, EX124G, ITUNES, BOEYE_BEX, BOEYE_BDX, diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 90d03f073a..f25c41d073 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -377,3 +377,31 @@ class COBY(USBMS): return 'eBooks' return self.EBOOK_DIR_CARD_A +class EX124G(USBMS): + + name = 'Motorola Ex124G device interface' + gui_name = 'Ex124G' + description = _('Communicate with the Ex124G') + + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + # Ordered list of supported formats + FORMATS = ['mobi', 'prc', 'azw'] + + VENDOR_ID = [0x0e8d] + PRODUCT_ID = [0x0002] + BCD = [0x0100] + VENDOR_NAME = 'MOTOROLA' + WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '_PHONE' + + EBOOK_DIR_MAIN = 'eBooks' + + SUPPORTS_SUB_DIRS = False + + def get_carda_ebook_dir(self, for_upload=False): + if for_upload: + return 'eBooks' + return self.EBOOK_DIR_CARD_A + + From 994a31e6863823b1cc6a7c5613ea7a93c15f06fc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 20:45:03 -0600 Subject: [PATCH 12/18] ... --- recipes/ming_pao.recipe | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/recipes/ming_pao.recipe b/recipes/ming_pao.recipe index 947d85692f..7060a7cd3e 100644 --- a/recipes/ming_pao.recipe +++ b/recipes/ming_pao.recipe @@ -16,6 +16,7 @@ __UseLife__ = True ''' Change Log: +2011/09/07: disable "column" section as it is no longer offered free. 2011/06/26: add fetching Vancouver and Toronto versions of the paper, also provide captions for images using life.mingpao fetch source provide options to remove all images in the file 2011/05/12: switch the main parse source to life.mingpao.com, which has more photos on the article pages @@ -230,8 +231,9 @@ class MPRecipe(BasicNewsRecipe): (u'\u570b\u969b World', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr +'&Category=nalta', 'nal'), (u'\u7d93\u6fdf Finance', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr + '&Category=nalea', 'nal'), (u'\u9ad4\u80b2 Sport', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr + '&Category=nalsp', 'nal'), - (u'\u5f71\u8996 Film/TV', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr + '&Category=nalma', 'nal'), - (u'\u5c08\u6b04 Columns', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr +'&Category=ncolumn', 'ncl')]: + (u'\u5f71\u8996 Film/TV', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr + '&Category=nalma', 'nal') + #(u'\u5c08\u6b04 Columns', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr +'&Category=ncolumn', 'ncl') + ]: articles = self.parse_section2(url, keystr) if articles: feeds.append((title, articles)) @@ -591,4 +593,3 @@ class MPRecipe(BasicNewsRecipe): with nested(open(opf_path, 'wb'), open(ncx_path, 'wb')) as (opf_file, ncx_file): opf.render(opf_file, ncx_file) - From 940e64af0d788a77a6bc3845cffddf6e617bc39c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 21:41:36 -0600 Subject: [PATCH 13/18] Make smarten and unsmarten checkboxes in the GUI exclusive --- src/calibre/gui2/convert/look_and_feel.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/convert/look_and_feel.py b/src/calibre/gui2/convert/look_and_feel.py index 5ca7e1ea02..4785e222fc 100644 --- a/src/calibre/gui2/convert/look_and_feel.py +++ b/src/calibre/gui2/convert/look_and_feel.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import SIGNAL, QVariant +from PyQt4.Qt import SIGNAL, QVariant, Qt from calibre.gui2.convert.look_and_feel_ui import Ui_Form from calibre.gui2.convert import Widget @@ -45,6 +45,12 @@ class LookAndFeelWidget(Widget, Ui_Form): self.font_key_wizard) self.opt_remove_paragraph_spacing.toggle() self.opt_remove_paragraph_spacing.toggle() + self.opt_smarten_punctuation.stateChanged.connect( + lambda state: state != Qt.Unchecked and + self.opt_unsmarten_punctuation.setCheckState(Qt.Unchecked)) + self.opt_unsmarten_punctuation.stateChanged.connect( + lambda state: state != Qt.Unchecked and + self.opt_smarten_punctuation.setCheckState(Qt.Unchecked)) def get_value_handler(self, g): if g is self.opt_change_justification: From 7911f9ecda2f8286a16cb369fe4088858709b20d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 21:44:24 -0600 Subject: [PATCH 14/18] ... --- src/calibre/utils/unsmarten.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/utils/unsmarten.py b/src/calibre/utils/unsmarten.py index 7944f710b0..6f0c2a19e1 100644 --- a/src/calibre/utils/unsmarten.py +++ b/src/calibre/utils/unsmarten.py @@ -36,8 +36,8 @@ _mreplace = MReplace({ '‘':"'", '’':"'", '′':"'", -} -) +}) + unsmarten_text = _mreplace.mreplace From 9342cdc8773f2862feefc40a3b965bbd4d3a77de Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 21:45:25 -0600 Subject: [PATCH 15/18] ... --- src/calibre/db/tests/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/calibre/db/tests/__init__.py diff --git a/src/calibre/db/tests/__init__.py b/src/calibre/db/tests/__init__.py new file mode 100644 index 0000000000..cc6da1e995 --- /dev/null +++ b/src/calibre/db/tests/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + + From b3a0671a46d67329168bd4ecd851ad2d20450521 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 23:49:46 -0600 Subject: [PATCH 16/18] SONY driver: Print out cover upload path --- src/calibre/devices/prs505/driver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 402ca65658..9c1dc930b8 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -291,6 +291,8 @@ class PRS505(USBMS): thumbnail_dir = os.path.join(thumbnail_dir, relpath) if not os.path.exists(thumbnail_dir): os.makedirs(thumbnail_dir) - with open(os.path.join(thumbnail_dir, 'main_thumbnail.jpg'), 'wb') as f: + cpath = os.path.join(thumbnail_dir, 'main_thumbnail.jpg') + with open(cpath, 'wb') as f: f.write(metadata.thumbnail[-1]) + debug_print('Cover uploaded to: %r'%cpath) From 442d0ab26746240ebb55d8e26ecaf23f2e905a7d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 23:56:18 -0600 Subject: [PATCH 17/18] ... --- src/calibre/db/fields.py | 17 ++++-- src/calibre/db/tables.py | 2 - src/calibre/db/tests/metadata.db | Bin 230400 -> 230400 bytes src/calibre/db/tests/reading.py | 87 ++++++++++++++++++++++++++++++- src/calibre/debug.py | 1 + 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index 0a497555c4..5c0dffd383 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -164,7 +164,7 @@ class ManyToOneField(Field): def for_book(self, book_id, default_value=None): ids = self.table.book_col_map.get(book_id, None) if ids is not None: - ans = self.id_map[ids] + ans = self.table.id_map[ids] else: ans = default_value return ans @@ -182,7 +182,7 @@ class ManyToOneField(Field): return self.table.id_map.iterkeys() def sort_keys_for_books(self, get_metadata, all_book_ids): - keys = {id_ : self._sort_key(self.id_map.get(id_, '')) for id_ in + keys = {id_ : self._sort_key(self.table.id_map.get(id_, '')) for id_ in all_book_ids} return {id_ : keys.get( self.book_col_map.get(id_, None), '') for id_ in all_book_ids} @@ -196,7 +196,7 @@ class ManyToManyField(Field): def for_book(self, book_id, default_value=None): ids = self.table.book_col_map.get(book_id, ()) if ids: - ans = tuple(self.id_map[i] for i in ids) + ans = tuple(self.table.id_map[i] for i in ids) else: ans = default_value return ans @@ -211,7 +211,7 @@ class ManyToManyField(Field): return self.table.id_map.iterkeys() def sort_keys_for_books(self, get_metadata, all_book_ids): - keys = {id_ : self._sort_key(self.id_map.get(id_, '')) for id_ in + keys = {id_ : self._sort_key(self.table.id_map.get(id_, '')) for id_ in all_book_ids} def sort_key_for_book(book_id): @@ -222,6 +222,13 @@ class ManyToManyField(Field): return {id_ : sort_key_for_book(id_) for id_ in all_book_ids} +class IdentifiersField(ManyToManyField): + + def for_book(self, book_id, default_value=None): + ids = self.table.book_col_map.get(book_id, ()) + if not ids: + ids = default_value + return ids class AuthorsField(ManyToManyField): @@ -249,6 +256,8 @@ def create_field(name, table): cls = OnDeviceField elif name == 'formats': cls = FormatsField + elif name == 'identifiers': + cls = IdentifiersField elif table.metadata['datatype'] == 'composite': cls = CompositeField return cls(name, table) diff --git a/src/calibre/db/tables.py b/src/calibre/db/tables.py index 185d15d86b..6a6730c2b4 100644 --- a/src/calibre/db/tables.py +++ b/src/calibre/db/tables.py @@ -66,8 +66,6 @@ class VirtualTable(Table): self.table_type = table_type Table.__init__(self, name, metadata) - - class OneToOneTable(Table): ''' diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db index 8fb89cbb8e87ff051c764bb653fef03e64094284..812bf296ba52f6b02fb4d33ae8b76a81e50749ba 100644 GIT binary patch delta 2189 zcmc&#Yiv|S6uvXx?B1nxTb5QRZE0^=p`{eq?LJycQA(?YKy5>7BuWAW*0R_K(ru(h zq{PIa2@!MENt8dtAN(@0dy~B;#DqelCJ2`DP_RfFAyJH_1c_Kp>&$JtTORt)xY=`O z@|`p1oO{lh?{*zb={lHlIFpk6ceB@sXZO~lGiNG_Lg_Ar?n0^I&3KE<#*@l`lA9}* zi(*j}^1gn|c}oA-X%?4goDxgg%CpRHn~!2w;QAGyKt#0lQ6YeD}g;) zaC>elvy7P?mNBBM|A5MD5Qd1B-^Qe##`Q(NL%)@;8e{A4sQ-F+)6a0(E$1Yj2{T4t z5SiHT%Y+gZ-FikPOh4N+kHGk;$;IL+G>@+*B>LHyNXQsJp3G)k!=H3N{fM40(ocLv zbpOdsG*d1wk~RElHQbK31C=(V`aK^%`ZPXyqS(KpNK4YhZ^53%NVv6WL77(SnS$J% zq4w4`33=mC+|29o>fNUvU+nb;rj+fWJ>gKhuwUVyf+FFTPM!+cg;o5PqN>=|<0&nJisYJzuHo*w!3w-x=DYHN4X1f^qBg7}A&v`6Qtc z!-8nvnN(SwYgToxIoK?gf;>`hB_qcP(d)!-jV$^VjG*6e6?~2vg~Dx-R?!jRUu2PG zv21b&PSt9yO+I0dX#$!z4I=g=IrzA#;2v%YFW+FHHZirr7ZJMIPUMmVq9Tom1Zb>) z+)H&ZYjAMi5XH*L)I>d?82Bf}269Jr+7pQq7Hg2XPlG6%KB%$H zf5IEMtCyTKw1T^|TsA7<(rWH~E~)f6X;3@OSavm*A+AR9f8aQ(4TDmqk5s1{OuG(! zWVtPmGI8q!K?sJJxSoI<24GmhZy3d};qnhclWm#kvtSZAMUS%L!?`5MK`aA`n1`|q zgMIxSW>3|0a&|?P`S5x=M<@r7A_|khsX1Ir=ded>o}=SPgH6($;3)^Oc2p3BU?+a- zI8_NT$MeZ32+Yy#bX=9|OVr-S#2XEotP6h$zEhOGW9&6B?%Ici=Hk7$0@g2D&CgX} zA+Oz!bzE!1O7qhQ>Os!)RTZe>M_xt1+_xKVf?VK(omgZRHsdnLMc%z1i_N7waS7xS zcfA3(nZFeSP`}+P2&34~k3J90EZi)Nk6{y-LuuJ9nZ&~#JFt?Lmg>*+%ujajXpDp` zxJcvOO-R=}dme)~7~~@%q}K%FROF5{Hc2M(1{2g3Zr|1_pkV0~Ik`iv9W4SW@x_ri zg?YXDJ3S?4AcV7&T;_*fN4BXo;75?}c}_ht&73;i02$yfZ$!G8u>rq;T;*ML2%9Zy zp<}I66p$!f5&t2vmC`5VoREGY=Y?Dla&c1RUSd)wZKSO{GsuV5A)61@VFnkD3o*8+ z%KWe#A&?(ptHD^6ody*eq#J2?g89Xv2LoIZm=qc8!9aro>q`VtE!K5Ud@V$<3K{Ik M|NjYT6lq@h8|D%vng9R* delta 1820 zcmb_cYiv|S6uxJ^*$3P1mZhb%^tG@8g)X?ew3aOrq|cS>5gmyA2R+ zQvVnyX+%1$>6Jz4dJu~Ni zbMAL$&YW}F_PE;ixb{tD+O_wKm#Xo|rh{2oY`ezjkVc0PGy(p%r8i0P-A z3W<#0n%rF6K0}HtdZ(6H`{%@Q~I6Uf^zGHJT5^I`XxpznlhhJ@1SnC=K%woiV%&@K&b*od|V#if8miMzmjF*VeV%I zR>BPpHw<^>b%-0aEdLQ8UC&(dZauT}ap0HPE$Dw#*s=+BvScb$m5c9hIB2vYRrRFQ z?SK)JLtEL1P2A3bQ(B{J===TWiO;iWK~24@wXpVvCfHFG;44ZlD@iN4q~yXqQTn}d za$qy|kpt>mhbUL`$B9_BMSO>Pr2*Lszhw&p+GV*5)d z7QInSuw`n+J6RR2_r9+YvcFrM|NqwSDy#B)anui2>W`u=iim7ZLWlW%R&0+UPZqom z)u7Ia(m1Bd(x}Ql`boUE6vfiB4cBlOo4{=scBxhotQ1F9!XxKwzy;7H;a&`{oV6Zl zSTxmJLL#dX3!xf{zPhu-;ceWU3}+gQh&95e@9lJm!7#GdJk)TKo0}4k^`J;%OYAWy z9BXNQ3`%4g#we)o>wHF5g>i~eS{zx1*>dh$oC94Jz0YH&oU_K@870bm(FQ7`c yuEN{kJ*uL_=~m1T^~><0@HAkd{B$K6K)u4f1T*C9<+upCdYC^4RnO1jF8%@E)c?8w diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index 6149839a88..91ec2a92f6 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -8,7 +8,9 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, shutil, unittest, tempfile +import os, shutil, unittest, tempfile, datetime + +from calibre.utils.date import local_tz def create_db(library_path): from calibre.library.database2 import LibraryDatabase2 @@ -19,6 +21,14 @@ def create_db(library_path): shutil.copyfile(src, db) return db +def init_cache(library_path): + from calibre.db.backend import DB + from calibre.db.cache import Cache + backend = DB(library_path) + cache = Cache(backend) + cache.init() + return cache + class ReadingTest(unittest.TestCase): def setUp(self): @@ -29,5 +39,78 @@ class ReadingTest(unittest.TestCase): shutil.rmtree(self.library_path) def test_read(self): - pass + cache = init_cache(self.library_path) + tests = { + 2 : { + 'title': 'Title One', + 'sort': 'One', + 'authors': ('Author One',), + 'author_sort': 'One, Author', + 'series' : 'Series One', + 'series_index': 1.0, + 'tags':('Tag One',), + 'rating': 4.0, + 'identifiers': {'test':'one'}, + 'timestamp': datetime.datetime(2011, 9, 5, 15, 6, + tzinfo=local_tz), + 'pubdate': datetime.datetime(2011, 9, 5, 15, 6, + tzinfo=local_tz), + 'publisher': 'Publisher One', + 'languages': ('eng',), + 'comments': '

Comments One

', + '#enum':'One', + '#authors':('Custom One', 'Custom Two'), + '#date':datetime.datetime(2011, 9, 5, 0, 0, + tzinfo=local_tz), + '#rating':2.0, + '#series':'My Series One', + '#series_index': 1.0, + '#tags':('My Tag One', 'My Tag Two'), + '#yesno':True, + '#comments': '
My Comments One

', + }, + 1 : { + 'title': 'Title Two', + 'sort': 'Title Two', + 'authors': ('Author Two', 'Author One'), + 'author_sort': 'Two, Author & One, Author', + 'series' : 'Series Two', + 'series_index': 2.0, + 'rating': 6.0, + 'tags': ('Tag Two',), + 'identifiers': {'test':'two'}, + 'timestamp': datetime.datetime(2011, 9, 6, 0, 0, + tzinfo=local_tz), + 'pubdate': datetime.datetime(2011, 8, 5, 0, 0, + tzinfo=local_tz), + 'publisher': 'Publisher Two', + 'languages': ('deu',), + 'comments': '

Comments Two

', + '#enum':'Two', + '#authors':('My Author Two',), + '#date':datetime.datetime(2011, 9, 1, 0, 0, + tzinfo=local_tz), + '#rating':4.0, + '#series':'My Series Two', + '#series_index': 3.0, + '#tags':('My Tag Two',), + '#yesno':False, + '#comments': '
My Comments Two

', + + }, + } + for book_id, test in tests.iteritems(): + for field, expected_val in test.iteritems(): + self.assertEqual(expected_val, + cache.field_for(field, book_id)) + break + +def tests(): + return unittest.TestLoader().loadTestsFromTestCase(ReadingTest) + +def run(): + unittest.TextTestRunner(verbosity=2).run(tests()) + +if __name__ == '__main__': + run() diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 79110d9585..20f8617396 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -180,6 +180,7 @@ def main(args=sys.argv): sys.path.insert(0, base) g = globals() g['__name__'] = '__main__' + g['__file__'] = ef execfile(ef, g) return From fe27e08a835be9f13b859d487f727fdd685f2b68 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Sep 2011 23:57:56 -0600 Subject: [PATCH 18/18] ... --- src/calibre/db/tests/reading.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index 91ec2a92f6..f25b308f79 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -38,7 +38,7 @@ class ReadingTest(unittest.TestCase): def tearDown(self): shutil.rmtree(self.library_path) - def test_read(self): + def test_read(self): # {{{ cache = init_cache(self.library_path) tests = { 2 : { @@ -48,7 +48,7 @@ class ReadingTest(unittest.TestCase): 'author_sort': 'One, Author', 'series' : 'Series One', 'series_index': 1.0, - 'tags':('Tag One',), + 'tags':('Tag Two', 'Tag One'), 'rating': 4.0, 'identifiers': {'test':'one'}, 'timestamp': datetime.datetime(2011, 9, 5, 15, 6, @@ -103,7 +103,7 @@ class ReadingTest(unittest.TestCase): for field, expected_val in test.iteritems(): self.assertEqual(expected_val, cache.field_for(field, book_id)) - break + # }}} def tests(): return unittest.TestLoader().loadTestsFromTestCase(ReadingTest)