diff --git a/src/calibre/gui2/convert/gui_conversion.py b/src/calibre/gui2/convert/gui_conversion.py
index d035a0ff93..148dc328ad 100644
--- a/src/calibre/gui2/convert/gui_conversion.py
+++ b/src/calibre/gui2/convert/gui_conversion.py
@@ -23,7 +23,7 @@ def gui_convert(input, output, recommendations, notification=DummyReporter(),
plumber.run()
-def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options,
+def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, connected_device,
notification=DummyReporter(), log=None):
if log is None:
log = Log()
@@ -44,6 +44,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options,
# Populate opts
# opts.gui_search_text = something
opts.catalog_title = title
+ opts.connected_device = connected_device
opts.ids = ids
opts.search_text = None
opts.sort_by = None
diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py
index bd34f84821..38b0a6d663 100644
--- a/src/calibre/gui2/tools.py
+++ b/src/calibre/gui2/tools.py
@@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
Logic for setting up conversion jobs
'''
-import cPickle
+import cPickle, os
from PyQt4.Qt import QDialog, QProgressDialog, QString, QTimer, SIGNAL
@@ -236,7 +236,7 @@ def fetch_scheduled_recipe(arg):
return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt]
-def generate_catalog(parent, dbspec, ids):
+def generate_catalog(parent, dbspec, ids, device):
from calibre.gui2.dialogs.catalog import Catalog
# Build the Catalog dialog in gui2.dialogs.catalog
@@ -248,6 +248,21 @@ def generate_catalog(parent, dbspec, ids):
# Create the output file
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
+ # Profile the connected device
+ # Parallel initialization in calibre.library.cli:command_catalog()
+ connected_device = { 'storage':None,'serial':None,'name':None}
+ if device:
+ storage = []
+ if device._main_prefix:
+ storage.append(os.path.join(device._main_prefix, device.EBOOK_DIR_MAIN))
+ if device._card_a_prefix:
+ storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A))
+ if device._card_b_prefix:
+ storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B))
+ connected_device = {'storage': storage,'serial':device.detected_device.serial,
+ 'name':device.gui_name}
+
+ # These args are passed inline to gui2.convert.gui_conversion:gui_catalog
args = [
d.catalog_format,
d.catalog_title,
@@ -255,12 +270,13 @@ def generate_catalog(parent, dbspec, ids):
ids,
out.name,
d.catalog_sync,
- d.fmt_options
+ d.fmt_options,
+ connected_device
]
out.close()
# This returns to gui2.ui:generate_catalog()
- # Which then calls gui2.convert.gui_conversion:gui_catalog()
+ # Which then calls gui2.convert.gui_conversion:gui_catalog() with the args inline
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
d.catalog_title
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index f3b5e439a3..c16b868b34 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -1379,7 +1379,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
show=True)
# Calling gui2.tools:generate_catalog()
- ret = generate_catalog(self, dbspec, ids)
+ ret = generate_catalog(self, dbspec, ids, self.device_manager.device)
if ret is None:
return
diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py
index 540ba65cc5..77e1e51e17 100644
--- a/src/calibre/library/catalog.py
+++ b/src/calibre/library/catalog.py
@@ -1,7 +1,8 @@
-import os, re, shutil, htmlentitydefs
+import datetime, htmlentitydefs, os, re, shutil, time, traceback
from collections import namedtuple
-from datetime import date
+from copy import deepcopy
+
from xml.sax.saxutils import escape
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
@@ -533,10 +534,11 @@ class EPUB_MOBI(CatalogPlugin):
catalog.copyResources()
catalog.buildSources()
'''
-
- # Number of discrete steps to catalog creation
-# current_step = 0.0
-# total_steps = 10.0
+ # A single number creates 'Last x days' only.
+ # Multiple numbers create 'Last x days', 'x to y days ago' ...
+ # e.g, [7,15,30,60], [30]
+ # [] = No date ranges added
+ DATE_RANGE=[30]
# basename output file basename
# creator dc:creator in OPF metadata
@@ -551,10 +553,12 @@ class EPUB_MOBI(CatalogPlugin):
report_progress=DummyReporter(),
stylesheet="content/stylesheet.css"):
self.__opts = opts
+ self.__authorClip = opts.authorClip
self.__authors = None
self.__basename = opts.basename
self.__booksByAuthor = None
self.__booksByTitle = None
+ self.__booksByTitle_noSeriesPrefix = None
self.__catalogPath = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='')
self.__contentDir = os.path.join(self.catalogPath, "content")
self.__currentStep = 0.0
@@ -581,6 +585,7 @@ class EPUB_MOBI(CatalogPlugin):
self.__thumbHeight = 0
self.__title = opts.catalog_title
self.__totalSteps = 11.0
+ self.__useSeriesPrefixInTitlesSection = False
self.__verbose = opts.verbose
# Tweak build steps based on optional sections. 1 call for HTML, 1 for NCX
@@ -600,6 +605,13 @@ class EPUB_MOBI(CatalogPlugin):
return property(fget=fget, fset=fset)
'''
+ @dynamic_property
+ def authorClip(self):
+ def fget(self):
+ return self.__authorClip
+ def fset(self, val):
+ self.__authorClip = val
+ return property(fget=fget, fset=fset)
@dynamic_property
def authors(self):
def fget(self):
@@ -629,6 +641,13 @@ class EPUB_MOBI(CatalogPlugin):
self.__booksByTitle = val
return property(fget=fget, fset=fset)
@dynamic_property
+ def booksByTitle_noSeriesPrefix(self):
+ def fget(self):
+ return self.__booksByTitle_noSeriesPrefix
+ def fset(self, val):
+ self.__booksByTitle_noSeriesPrefix = val
+ return property(fget=fget, fset=fset)
+ @dynamic_property
def catalogPath(self):
def fget(self):
return self.__catalogPath
@@ -799,6 +818,13 @@ class EPUB_MOBI(CatalogPlugin):
return self.__totalSteps
return property(fget=fget)
@dynamic_property
+ def useSeriesPrefixInTitlesSection(self):
+ def fget(self):
+ return self.__useSeriesPrefixInTitlesSection
+ def fset(self, val):
+ self.__useSeriesPrefixInTitlesSection = val
+ return property(fget=fget, fset=fset)
+ @dynamic_property
def verbose(self):
def fget(self):
return self.__verbose
@@ -971,7 +997,7 @@ class EPUB_MOBI(CatalogPlugin):
for token in p.contents:
if token.string is not None:
tokens.append(token.string)
- this_title['short_description'] = self.generateShortDescription(' '.join(tokens))
+ this_title['short_description'] = self.generateShortDescription(' '.join(tokens), dest="description")
else:
this_title['description'] = None
this_title['short_description'] = None
@@ -1109,10 +1135,10 @@ class EPUB_MOBI(CatalogPlugin):
# title
series series_index
brTag = Tag(soup,'br')
title_tokens = title['title'].split(': ')
- emTag.insert(0, NavigableString(title_tokens[1]))
+ emTag.insert(0, escape(NavigableString(title_tokens[1])))
emTag.insert(1, brTag)
smallTag = Tag(soup,'small')
- smallTag.insert(0,NavigableString(title_tokens[0]))
+ smallTag.insert(0, escape(NavigableString(title_tokens[0])))
emTag.insert(2, smallTag)
else:
emTag.insert(0, NavigableString(escape(title['title'])))
@@ -1287,8 +1313,29 @@ class EPUB_MOBI(CatalogPlugin):
dtc = 0
current_letter = ""
+ # 2/14/10 7:11 AM Experimental: re-sort title list without leading series/series_index
+ if not self.useSeriesPrefixInTitlesSection:
+ nspt = deepcopy(self.booksByTitle)
+ for book in nspt:
+ if book['series']:
+ tokens = book['title'].split(': ')
+ book['title'] = '%s (%s)' % (tokens[1], tokens[0])
+ book['title_sort'] = self.generateSortTitle(book['title'])
+ nspt = sorted(nspt,
+ key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper()))
+ self.booksByTitle_noSeriesPrefix = nspt
+ if False and self.verbose:
+ self.opts.log.info("no_series_prefix_titles: %d books" % len(nspt))
+ self.opts.log.info(" %-40s %-40s" % ('title', 'title_sort'))
+ for title in nspt:
+ self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
+ title['title_sort'][0:40])).encode('utf-8'))
+
# Loop through the books by title
- for book in self.booksByTitle:
+ title_list = self.booksByTitle
+ if not self.useSeriesPrefixInTitlesSection:
+ title_list = self.booksByTitle_noSeriesPrefix
+ for book in title_list:
if self.letter_or_symbol(book['title_sort'][0]) != current_letter :
# Start a new letter
current_letter = self.letter_or_symbol(book['title_sort'][0])
@@ -1513,7 +1560,7 @@ class EPUB_MOBI(CatalogPlugin):
# Write books by reverse chronological order
self.updateProgressFullStep("'Recently Added'")
- def add_books_to_HTML(this_months_list, dtc):
+ def add_books_to_HTML_by_month(this_months_list, dtc):
if len(this_months_list):
this_months_list.sort(self.author_compare)
@@ -1598,10 +1645,56 @@ class EPUB_MOBI(CatalogPlugin):
dtc += 1
return dtc
+ def add_books_to_HTML_by_date_range(date_range_list, date_range, dtc):
+ if len(date_range_list):
+ pIndexTag = Tag(soup, "p")
+ pIndexTag['class'] = "date_index"
+ aTag = Tag(soup, "a")
+ aTag['name'] = date_range.replace(' ','')
+ pIndexTag.insert(0,aTag)
+ pIndexTag.insert(1,NavigableString(date_range))
+ divTag.insert(dtc,pIndexTag)
+ dtc += 1
- # Sort titles case-insensitive
- self.booksByDate = sorted(self.booksByTitle,
- key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
+ for new_entry in date_range_list:
+ # Add books
+ pBookTag = Tag(soup, "p")
+ ptc = 0
+
+ # Prefix book with read/unread symbol
+ if new_entry['read']:
+ # check mark
+ pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
+ pBookTag['class'] = "read_book"
+ ptc += 1
+ else:
+ # hidden check mark
+ pBookTag['class'] = "unread_book"
+ pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
+ ptc += 1
+
+ aTag = Tag(soup, "a")
+ aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
+ aTag.insert(0,escape(new_entry['title']))
+ pBookTag.insert(ptc, aTag)
+ ptc += 1
+
+ # Dot
+ pBookTag.insert(ptc, NavigableString(" · "))
+ ptc += 1
+
+ # Link to author
+ emTag = Tag(soup, "em")
+ aTag = Tag(soup, "a")
+ aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(new_entry['author']))
+ aTag.insert(0, NavigableString(new_entry['author']))
+ emTag.insert(0,aTag)
+ pBookTag.insert(ptc, emTag)
+ ptc += 1
+
+ divTag.insert(dtc, pBookTag)
+ dtc += 1
+ return dtc
friendly_name = "Recently Added"
@@ -1640,20 +1733,63 @@ class EPUB_MOBI(CatalogPlugin):
divTag = Tag(soup, "div")
dtc = 0
- current_date = date.fromordinal(1)
+ # Add books by date range
+ if self.useSeriesPrefixInTitlesSection:
+ self.booksByDateRange = sorted(self.booksByTitle,
+ key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
+ else:
+ nspt = deepcopy(self.booksByTitle)
+ for book in nspt:
+ if book['series']:
+ tokens = book['title'].split(': ')
+ book['title'] = '%s (%s)' % (tokens[1], tokens[0])
+ book['title_sort'] = self.generateSortTitle(book['title'])
+ self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
+
+ today = datetime.datetime.now()
+ date_range_list = []
+ today_time = datetime.datetime(today.year, today.month, today.day)
+ books_added_in_date_range = False
+ for (i, date) in enumerate(self.DATE_RANGE):
+ date_range_limit = self.DATE_RANGE[i]
+ if i:
+ date_range = '%d to %d days ago' % (self.DATE_RANGE[i-1], self.DATE_RANGE[i])
+ else:
+ date_range = 'Last %d days' % (self.DATE_RANGE[i])
+ for book in self.booksByDateRange:
+ book_time = datetime.datetime(book['timestamp'].year, book['timestamp'].month, book['timestamp'].day)
+ if (today_time-book_time).days <= date_range_limit:
+ #print "generateHTMLByDateAdded: %s added %d days ago" % (book['title'], (today_time-book_time).days)
+ date_range_list.append(book)
+ books_added_in_date_range = True
+ else:
+ break
+ dtc = add_books_to_HTML_by_date_range(date_range_list, date_range, dtc)
+ date_range_list = [book]
+
+ if books_added_in_date_range:
+ # Add an