From 7a9dc00e0ff62d24428607b935781f4729f2e0b5 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 2 Sep 2010 09:29:14 +0100 Subject: [PATCH 1/3] Tweak to automatically add a tag to newly added books. --- resources/default_tweaks.py | 7 ++++++- src/calibre/library/database2.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index e03b0680be..43edd1c690 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -90,4 +90,9 @@ save_template_title_series_sorting = 'library_order' # Examples: # auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib' # auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library' -auto_connect_to_folder = '' \ No newline at end of file +auto_connect_to_folder = '' + + +# Specify a tag to be automatically applied when a book is added to the library. +# Example: add_tag_to_new_books='ToDo' +add_tag_to_new_books = '' diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index ba4095fbc5..e406fb8766 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1681,7 +1681,16 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): path = path_or_stream return run_plugins_on_import(path, format) + def _add_newbook_tag(self, mi): + tag = tweaks['add_tag_to_new_books'] + if tag: + if mi.tags is None: + mi.tags = [tag] + else: + mi.tags.append(tag) + def create_book_entry(self, mi, cover=None, add_duplicates=True): + self._add_newbook_tag(mi) if not add_duplicates and self.has_book(mi): return None series_index = 1.0 if mi.series_index is None else mi.series_index @@ -1720,6 +1729,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): ids = [] for path in paths: mi = metadata.next() + self._add_newbook_tag(mi) format = formats.next() if not add_duplicates and self.has_book(mi): duplicates.append((path, format, mi)) @@ -1760,6 +1770,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def import_book(self, mi, formats, notify=True, import_hooks=True): series_index = 1.0 if mi.series_index is None else mi.series_index + self._add_newbook_tag(mi) if not mi.title: mi.title = _('Unknown') if not mi.authors: From 7fa99ffb5769e9fca78cadfcc2ea6547b88aa259 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 2 Sep 2010 11:12:40 +0100 Subject: [PATCH 2/3] 1) Add tweak to make user-defined search terms for searching across multiple fields. 2) Make parse exceptions visible as the tooltip in the GUI --- resources/default_tweaks.py | 14 ++++++++++++++ src/calibre/gui2/library/models.py | 4 ++-- src/calibre/gui2/search_box.py | 6 ++++++ src/calibre/library/caches.py | 8 +++++++- src/calibre/library/database2.py | 7 +++++++ src/calibre/library/field_metadata.py | 7 ++++--- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 43edd1c690..fb4fff9dc7 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -96,3 +96,17 @@ auto_connect_to_folder = '' # Specify a tag to be automatically applied when a book is added to the library. # Example: add_tag_to_new_books='ToDo' add_tag_to_new_books = '' + + +# Create search terms to apply a query across several built-in search terms. +# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...} +# Example: create the term 'myseries' that when used as myseries:foo would +# search all of the search categories 'series', '#myseries', and '#myseries2': +# grouped_search_terms={'myseries':['series','#myseries', '#myseries2']} +# Example: two search terms 'a' and 'b' both that search 'tags' and '#mytags': +# grouped_search_terms={'a':['tags','#mytags'], 'b':['tags','#mytags']} +# Note: You cannot create a search term that is a duplicate of an existing term. +# Such duplicates will be silently ignored. Also note that search terms ignore +# case. 'MySearch' and 'mysearch' are the same term. +grouped_search_terms = {} + diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 89008735fe..e442007c9a 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -236,8 +236,8 @@ class BooksModel(QAbstractTableModel): # {{{ def search(self, text, reset=True): try: self.db.search(text) - except ParseException: - self.searched.emit(False) + except ParseException as e: + self.searched.emit(e.msg) return self.last_search = text if reset: diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 8986346dbc..d8f59b8db7 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -90,6 +90,7 @@ class SearchBox2(QComboBox): self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon) self.setMinimumContentsLength(25) self._in_a_search = False + self.tool_tip_text = self.toolTip() def initialize(self, opt_name, colorize=False, help_text=_('Search')): self.as_you_type = config['search_as_you_type'] @@ -100,6 +101,7 @@ class SearchBox2(QComboBox): self.clear_to_help() def normalize_state(self): + self.setToolTip(self.tool_tip_text) if self.help_state: self.setEditText('') self.line_edit.setStyleSheet( @@ -112,6 +114,7 @@ class SearchBox2(QComboBox): self.normal_background) def clear_to_help(self): + self.setToolTip(self.tool_tip_text) if self.help_state: return self.help_state = True @@ -131,6 +134,9 @@ class SearchBox2(QComboBox): self.clear_to_help() def search_done(self, ok): + if isinstance(ok, basestring): + self.setToolTip(ok) + ok = False if not unicode(self.currentText()).strip(): return self.clear_to_help() self._in_a_search = ok diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index ca66d28ddb..b9c1211c7f 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -319,12 +319,18 @@ class ResultCache(SearchQueryParser): matches.add(item[0]) return matches - def get_matches(self, location, query): + def get_matches(self, location, query, allow_recursion=True): matches = set([]) if query and query.strip(): # get metadata key associated with the search term. Eliminates # dealing with plurals and other aliases location = self.field_metadata.search_term_to_key(location.lower().strip()) + if isinstance(location, list): + if allow_recursion: + for loc in location: + matches |= self.get_matches(loc, query, allow_recursion=False) + return matches + raise ParseException(query, len(query), 'Recursive query group detected', self) # take care of dates special case if location in self.field_metadata and \ diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 02141b8f91..91eafbaff0 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -297,6 +297,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) + gst = tweaks['grouped_search_terms'] + for t in gst: + try: + self.field_metadata._add_search_terms_to_map(gst[t], [t]) + except ValueError: + traceback.print_exc() + self.book_on_device_func = None self.data = ResultCache(self.FIELD_MAP, self.field_metadata) self.search = self.data.search diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 2a79c3a75b..e28b6d422a 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -475,9 +475,7 @@ class FieldMetadata(dict): # ]) def get_search_terms(self): - s_keys = [] - for v in self._tb_cats.itervalues(): - map((lambda x:s_keys.append(x)), v['search_terms']) + s_keys = sorted(self._search_term_map.keys()) for v in self.search_items: s_keys.append(v) # if set(s_keys) != self.DEFAULT_LOCATIONS: @@ -488,6 +486,9 @@ class FieldMetadata(dict): def _add_search_terms_to_map(self, key, terms): if terms is not None: for t in terms: + t = t.lower() + if t in self._search_term_map: + raise ValueError('Attempt to add duplicate search term "%s"'%t) self._search_term_map[t] = key def search_term_to_key(self, term): From e957291d37dc783afda42d7e37119dff60ae6053 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 2 Sep 2010 14:41:08 +0100 Subject: [PATCH 3/3] Put new_book_tags into the GUI, remove from tweaks. --- resources/default_tweaks.py | 5 ---- src/calibre/gui2/dialogs/config/__init__.py | 2 ++ src/calibre/gui2/dialogs/config/config.ui | 33 ++++++++++++++++----- src/calibre/library/database2.py | 13 ++++---- src/calibre/utils/config.py | 1 + 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index fb4fff9dc7..8d853deaa9 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -93,11 +93,6 @@ save_template_title_series_sorting = 'library_order' auto_connect_to_folder = '' -# Specify a tag to be automatically applied when a book is added to the library. -# Example: add_tag_to_new_books='ToDo' -add_tag_to_new_books = '' - - # Create search terms to apply a query across several built-in search terms. # Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...} # Example: create the term 'myseries' that when used as myseries:foo would diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index 1f04c50e19..1dca3e28ff 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -457,6 +457,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.priority.setCurrentIndex(p) self.priority.setVisible(iswindows) self.priority_label.setVisible(iswindows) + self.new_book_tags.setText(prefs['new_book_tags']) self._plugin_model = PluginModel() self.plugin_view.setModel(self._plugin_model) self.plugin_view.setStyleSheet( @@ -906,6 +907,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): config['disable_tray_notification'] = not self.systray_notifications.isChecked() p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] prefs['worker_process_priority'] = p + prefs['new_book_tags'] = unicode(self.new_book_tags.text()).strip() prefs['output_format'] = unicode(self.output_format.currentText()).upper() config['cover_flow_queue_length'] = self.cover_browse.value() prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString()) diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index 988960615d..5a2bf805da 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -136,7 +136,7 @@ - + Default network &timeout: @@ -146,7 +146,7 @@ - + Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information) @@ -165,10 +165,10 @@ - + - + Choose &language (requires restart): @@ -178,7 +178,7 @@ - + @@ -197,7 +197,7 @@ - + Job &priority: @@ -207,7 +207,7 @@ - + Preferred &output format: @@ -217,9 +217,26 @@ - + + + + + Tags to apply when adding a book: + + + new_book_tags + + + + + + + A comma-separated list of tags that will be applied to books added to the library + + + diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 91eafbaff0..76801a9893 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1726,12 +1726,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return run_plugins_on_import(path, format) def _add_newbook_tag(self, mi): - tag = tweaks['add_tag_to_new_books'] - if tag: - if mi.tags is None: - mi.tags = [tag] - else: - mi.tags.append(tag) + tags = prefs['new_book_tags'].strip() + if tags: + for tag in [t.strip() for t in tags.split(',')]: + if mi.tags is None: + mi.tags = [tag] + else: + mi.tags.append(tag) def create_book_entry(self, mi, cover=None, add_duplicates=True): self._add_newbook_tag(mi) diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index a890d653b6..5682cab5e1 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -717,6 +717,7 @@ def _prefs(): c.add_opt('add_formats_to_existing', default=False, help=_('Add new formats to existing book records')) c.add_opt('installation_uuid', default=None, help='Installation UUID') + c.add_opt('new_book_tags', default='', help=_('Tags to apply to books added to the library')) # these are here instead of the gui preferences because calibredb and # calibre server can execute searches