From 5063efaca745509575e73a4d218201e0a9f14060 Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 27 Jan 2010 06:32:07 -0700 Subject: [PATCH 1/2] Revisions to support 'numbers_as_text', minor cleanup --- src/calibre/ebooks/metadata/mobi.py | 1 - src/calibre/gui2/catalog/catalog_epub_mobi.py | 26 ++++--- src/calibre/gui2/catalog/catalog_epub_mobi.ui | 15 ++-- src/calibre/gui2/ui.py | 2 +- src/calibre/library/catalog.py | 68 ++++++++++++------- src/calibre/library/cli.py | 2 - 6 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index ff3dfe5809..4fab134937 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -275,7 +275,6 @@ class MetadataUpdater(object): return StreamSlicer(self.stream, start, stop) def update(self, mi): - print "self.type: %s" % self.type if self.type != "BOOKMOBI": raise MobiError("Setting metadata only supported for MOBI files of type 'BOOK'.\n" "\tThis is a '%s' file of type '%s'" % (self.type[0:4], self.type[4:8])) diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.py b/src/calibre/gui2/catalog/catalog_epub_mobi.py index a22ef3ae9e..2037545bb4 100644 --- a/src/calibre/gui2/catalog/catalog_epub_mobi.py +++ b/src/calibre/gui2/catalog/catalog_epub_mobi.py @@ -18,8 +18,9 @@ class PluginWidget(QWidget,Ui_Form): HELP = _('Options specific to')+' EPUB/MOBI '+_('output') OPTION_FIELDS = [('exclude_genre','\[[\w ]*\]'), ('exclude_tags','~,'+_('Catalog')), - ('read_tag','+'), - ('note_tag','*')] + ('note_tag','*'), + ('numbers_as_text', False), + ('read_tag','+')] # Output synced to the connected device? sync_enabled = True @@ -33,21 +34,30 @@ class PluginWidget(QWidget,Ui_Form): def initialize(self, name): self.name = name - # Restore options from last use here + # Update dialog fields from stored options for opt in self.OPTION_FIELDS: opt_value = gprefs.get(self.name + '_' + opt[0], opt[1]) - getattr(self, opt[0]).setText(opt_value) + if opt[0] == 'numbers_as_text': + getattr(self, opt[0]).setChecked(opt_value) + else: + getattr(self, opt[0]).setText(opt_value) def options(self): # Save/return the current options + # exclude_genre stores literally + # numbers_as_text stores as True/False + # others store as lists opts_dict = {} for opt in self.OPTION_FIELDS: - opt_value = unicode(getattr(self, opt[0]).text()) + if opt[0] == 'numbers_as_text': + opt_value = getattr(self,opt[0]).isChecked() + else: + opt_value = unicode(getattr(self, opt[0]).text()) gprefs.set(self.name + '_' + opt[0], opt_value) - if opt[0] != 'exclude_genre': + if opt[0] == 'exclude_genre' or 'numbers_as_text': + opts_dict[opt[0]] = opt_value + else: opt_value = opt_value.split(',') - opts_dict[opt[0]] = opt_value - opts_dict['output_profile'] = [load_defaults('page_setup')['output_profile']] diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.ui b/src/calibre/gui2/catalog/catalog_epub_mobi.ui index e683c479bf..858aec429f 100644 --- a/src/calibre/gui2/catalog/catalog_epub_mobi.ui +++ b/src/calibre/gui2/catalog/catalog_epub_mobi.ui @@ -69,6 +69,13 @@ + + + + + + + @@ -82,10 +89,10 @@ - - - - + + + + Sort numbers as text diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 714b2c3a27..157a7eabf8 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -357,7 +357,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): cm.addAction(_('Bulk convert')) cm.addSeparator() ac = cm.addAction( - _('Create catalog of books in your calibre library')) + _('Create catalog')) ac.triggered.connect(self.generate_catalog) self.action_convert.setMenu(cm) self._convert_single_hook = partial(self.convert_ebook, bulk=False) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 9ed2406dbc..d31c68f485 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -87,7 +87,12 @@ class CSV_XML(CatalogPlugin): outstr = '' for (x, field) in enumerate(fields): item = entry[field] - if field in ['authors','tags','formats']: + if field == 'formats': + fmt_list = [] + for format in item: + fmt_list.append(format.partition('.')[2]) + item = ', '.join(fmt_list) + elif field in ['authors','tags']: item = ', '.join(item) if x < len(fields) - 1: if item is not None: @@ -259,23 +264,29 @@ class EPUB_MOBI(CatalogPlugin): "--exclude-tags=skip will match 'skip this book' and 'Skip will like this'.\n" "Default: '%default'\n" "Applies to: ePub, MOBI output formats")), - Option('--read-tag', - default='+', - dest='read_tag', - help=_("Tag indicating book has been read.\n" "Default: '%default'\n" - "Applies to: ePub, MOBI output formats")), Option('--note-tag', default='*', dest='note_tag', help=_("Tag prefix for user notes, e.g. '*Jeff might enjoy reading this'.\n" "Default: '%default'\n" "Applies to: ePub, MOBI output formats")), + Option('--numbers-as-text', + default=False, + dest='numbers_as_text', + help=_("Sort titles with leading numbers as text, e.g.,\n'2001: A Space Odyssey' sorts as \n'Two Thousand One: A Space Odyssey'.\n" + "Default: '%default'\n" + "Applies to: ePub, MOBI output formats")), Option('--output-profile', default=None, dest='output_profile', help=_("Specifies the output profile. In some cases, an output profile is required to optimize the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured Table of Contents with Sections and Articles.\n" "Default: '%default'\n" - "Applies to: ePub, MOBI output formats")) + "Applies to: ePub, MOBI output formats")), + Option('--read-tag', + default='+', + dest='read_tag', + help=_("Tag indicating book has been read.\n" "Default: '%default'\n" + "Applies to: ePub, MOBI output formats")), ] class NumberToText(object): @@ -798,7 +809,6 @@ class EPUB_MOBI(CatalogPlugin): os.path.join(self.catalogPath, file[0])) def fetchBooksByTitle(self): - self.opts.log.info(self.updateProgressFullStep("fetchBooksByTitle()")) # Get the database as a dictionary @@ -1104,15 +1114,15 @@ class EPUB_MOBI(CatalogPlugin): # Loop through the books by title for book in self.booksByTitle: - if book['title_sort'][0].upper() != current_letter : + if self.letter_or_symbol(book['title_sort'][0]) != current_letter : # Start a new letter - current_letter = book['title_sort'][0].upper() + current_letter = self.letter_or_symbol(book['title_sort'][0]) pIndexTag = Tag(soup, "p") pIndexTag['class'] = "letter_index" aTag = Tag(soup, "a") - aTag['name'] = "%stitles" % book['title_sort'][0].upper() + aTag['name'] = "%s" % self.letter_or_symbol(book['title_sort'][0]) pIndexTag.insert(0,aTag) - pIndexTag.insert(1,NavigableString(book['title_sort'][0].upper())) + pIndexTag.insert(1,NavigableString(self.letter_or_symbol(book['title_sort'][0]))) divTag.insert(dtc,pIndexTag) dtc += 1 @@ -1716,19 +1726,19 @@ class EPUB_MOBI(CatalogPlugin): books_by_letter = [] # Loop over the titles, find start of each letter, add description_preview_count books - current_letter = self.booksByTitle[0]['title_sort'][0].upper() + current_letter = self.letter_or_symbol(self.booksByTitle[0]['title_sort'][0]) title_letters = [current_letter] current_book_list = [] current_book = "" for book in self.booksByTitle: - if book['title_sort'][0].upper() != current_letter: + if self.letter_or_symbol(book['title_sort'][0]) != current_letter: # Save the old list book_list = " • ".join(current_book_list) short_description = self.generateShortDescription(self.formatNCXText(book_list)) books_by_letter.append(short_description) # Start the new list - current_letter = book['title_sort'][0].upper() + current_letter = self.letter_or_symbol(book['title_sort'][0]) title_letters.append(current_letter) current_book = book['title'] current_book_list = [book['title']] @@ -1743,7 +1753,6 @@ class EPUB_MOBI(CatalogPlugin): short_description = self.generateShortDescription(self.formatNCXText(book_list)) books_by_letter.append(short_description) - # Add *article* entries for each populated title letter for (i,books) in enumerate(books_by_letter): navPointByLetterTag = Tag(soup, 'navPoint') @@ -1753,11 +1762,11 @@ class EPUB_MOBI(CatalogPlugin): self.playOrder += 1 navLabelTag = Tag(soup, 'navLabel') textTag = Tag(soup, 'text') - textTag.insert(0, NavigableString("Books beginning with '%s'" % (title_letters[i].upper()))) + textTag.insert(0, NavigableString("Titles beginning with %s" % (title_letters[i]))) navLabelTag.insert(0, textTag) navPointByLetterTag.insert(0,navLabelTag) contentTag = Tag(soup, 'content') - contentTag['src'] = "content/%s.html#%stitles" % (output, title_letters[i].upper()) + contentTag['src'] = "content/%s.html#%s" % (output, title_letters[i]) navPointByLetterTag.insert(1,contentTag) if self.generateForKindle: @@ -1990,7 +1999,6 @@ class EPUB_MOBI(CatalogPlugin): tokens[0] += ',' return ' '.join(tokens) - def convertHTMLEntities(self, s): matches = re.findall("&#\d+;", s) if len(matches) > 0: @@ -2294,22 +2302,24 @@ class EPUB_MOBI(CatalogPlugin): def generateSortTitle(self, title): # Convert the actual title to a string suitable for sorting. - # Convert numbers to strings, ignore leading stop words - # The 21-Day Consciousness Cleanse - # Scan for numbers in each word clump. + # Ignore leading stop words + # Optionally convert leading numbers to strings from calibre.ebooks.metadata import title_sort title_words = title_sort(title).split() translated = [] for (i,word) in enumerate(title_words): - # Initial numbers translated to text equivalent - if i==0 and re.search('[0-9]+',word): - translated.append(EPUB_MOBI.NumberToText(word).text) + # Leading numbers optionally translated to text equivalent + if i==0: + if self.opts.numbers_as_text and re.search('[0-9]+',word): + translated.append(EPUB_MOBI.NumberToText(word).text.capitalize()) + else: + translated.append(word.capitalize()) else: if re.search('[0-9]+',word): # Coerce standard-width strings for numbers - word = '%03d' % int(re.sub('\D','',word)) + word = '%10.2f' % float(re.sub('[^\d\.]','',word)) translated.append(word) return ' '.join(translated) @@ -2338,6 +2348,12 @@ class EPUB_MOBI(CatalogPlugin): except RuntimeError: self.opts.log.error("generateThumbnail(): RuntimeError with %s" % title['title']) + def letter_or_symbol(self,char): + if not re.search('[a-zA-Z]',char): + return 'Symbols' + else: + return char + def processSpecialTags(self, tags, this_title, opts): tag_list = [] for tag in tags: diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index cf29106684..0d9665de8a 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -670,8 +670,6 @@ def command_catalog(args, dbpath): print print >>sys.stderr, _('Error: You must specify a catalog output file') return 1 - if opts.verbose: - log("library.cli:command_catalog dispatching to plugin %s" % plugin.name) if opts.ids: opts.ids = [int(id) for id in opts.ids.split(',')] From 2253c81d32335398276c6c752d16dca4dd69de2a Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 27 Jan 2010 07:50:29 -0700 Subject: [PATCH 2/2] Revisions to support 'numbers_as_text', minor cleanup --- src/calibre/library/catalog.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index d31c68f485..246eb570fe 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -2315,7 +2315,10 @@ class EPUB_MOBI(CatalogPlugin): if self.opts.numbers_as_text and re.search('[0-9]+',word): translated.append(EPUB_MOBI.NumberToText(word).text.capitalize()) else: - translated.append(word.capitalize()) + if re.search('[0-9]+',word): + # Coerce standard-width strings for numbers for value sorting + word = '%10.2f' % float(re.sub('[^\d\.]','',word)) + translated.append(word) else: if re.search('[0-9]+',word): # Coerce standard-width strings for numbers