mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Rationalize use of metadata field labels across tag browser and search
This commit is contained in:
commit
3f49d4c483
@ -89,11 +89,18 @@ CALIBRE_METADATA_FIELDS = frozenset([
|
||||
)
|
||||
|
||||
CALIBRE_RESERVED_LABELS = frozenset([
|
||||
'search', # reserved for saved searches
|
||||
'date',
|
||||
'all',
|
||||
'ondevice',
|
||||
'inlibrary',
|
||||
'all', # search term
|
||||
'author_sort', # can appear in device collection customization
|
||||
'date', # search term
|
||||
'formats', # search term
|
||||
'inlibrary', # search term
|
||||
'news', # search term
|
||||
'ondevice', # search term
|
||||
'search', # search term
|
||||
'format', # The next four are here for backwards compatibility
|
||||
'tag', # with searching. The terms can be used without the
|
||||
'author', # trailing 's'.
|
||||
'comment', # Sigh ...
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -631,7 +631,10 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
if section >= len(self.column_map): # same problem as in data, the column_map can be wrong
|
||||
return None
|
||||
if role == Qt.ToolTipRole:
|
||||
return QVariant(_('The lookup/search name is "{0}"').format(self.column_map[section]))
|
||||
ht = self.column_map[section]
|
||||
if ht == 'timestamp': # change help text because users know this field as 'date'
|
||||
ht = 'date'
|
||||
return QVariant(_('The lookup/search name is "{0}"').format(ht))
|
||||
if role == Qt.DisplayRole:
|
||||
return QVariant(self.headers[self.column_map[section]])
|
||||
return NONE
|
||||
@ -730,11 +733,13 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
class OnDeviceSearch(SearchQueryParser): # {{{
|
||||
|
||||
USABLE_LOCATIONS = [
|
||||
'collections',
|
||||
'title',
|
||||
'author',
|
||||
'format',
|
||||
'all',
|
||||
'author',
|
||||
'authors',
|
||||
'collections',
|
||||
'format',
|
||||
'formats',
|
||||
'title',
|
||||
]
|
||||
|
||||
|
||||
|
@ -14,8 +14,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
|
||||
QAbstractItemModel, QVariant, QModelIndex
|
||||
from calibre.gui2 import config, NONE
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
from calibre.library.database2 import Tag
|
||||
from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS
|
||||
|
||||
class TagsView(QTreeView): # {{{
|
||||
|
||||
@ -204,17 +203,22 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
QAbstractItemModel.__init__(self, parent)
|
||||
|
||||
# must do this here because 'QPixmap: Must construct a QApplication
|
||||
# before a QPaintDevice'
|
||||
self.category_icon_map = {'authors': QIcon(I('user_profile.svg')),
|
||||
# before a QPaintDevice'. The ':' in front avoids polluting either the
|
||||
# user-defined categories (':' at end) or columns namespaces (no ':').
|
||||
self.category_icon_map = {
|
||||
'authors' : QIcon(I('user_profile.svg')),
|
||||
'series' : QIcon(I('series.svg')),
|
||||
'formats' : QIcon(I('book.svg')),
|
||||
'publishers': QIcon(I('publisher.png')),
|
||||
'ratings':QIcon(I('star.png')),
|
||||
'publisher' : QIcon(I('publisher.png')),
|
||||
'rating' : QIcon(I('star.png')),
|
||||
'news' : QIcon(I('news.svg')),
|
||||
'tags' : QIcon(I('tags.svg')),
|
||||
'*custom':QIcon(I('column.svg')),
|
||||
'*user':QIcon(I('drawer.svg')),
|
||||
':custom' : QIcon(I('column.svg')),
|
||||
':user' : QIcon(I('drawer.svg')),
|
||||
'search' : QIcon(I('search.svg'))}
|
||||
for k in self.category_icon_map.keys():
|
||||
if not k.startswith(':') and k not in RESERVED_METADATA_FIELDS:
|
||||
raise ValueError('Tag category [%s] is not a reserved word.' %(k))
|
||||
self.icon_state_map = [None, QIcon(I('plus.svg')), QIcon(I('minus.svg'))]
|
||||
self.db = db
|
||||
self.search_restriction = ''
|
||||
@ -381,9 +385,9 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
|
||||
def tokens(self):
|
||||
ans = []
|
||||
tags_seen = []
|
||||
tags_seen = set()
|
||||
for i, key in enumerate(self.row_map):
|
||||
if key.endswith('*'): # User category, so skip it. The tag will be marked in its real category
|
||||
if key.endswith(':'): # User category, so skip it. The tag will be marked in its real category
|
||||
continue
|
||||
category_item = self.root_item.children[i]
|
||||
for tag_item in category_item.children:
|
||||
@ -394,10 +398,10 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
if tag.name[0] == u'\u2605': # char is a star. Assume rating
|
||||
ans.append('%s%s:%s'%(prefix, category, len(tag.name)))
|
||||
else:
|
||||
if category == 'tag':
|
||||
if category == 'tags':
|
||||
if tag.name in tags_seen:
|
||||
continue
|
||||
tags_seen.append(tag.name)
|
||||
tags_seen.add(tag.name)
|
||||
ans.append('%s%s:"=%s"'%(prefix, category, tag.name))
|
||||
return ans
|
||||
|
||||
|
@ -37,6 +37,7 @@ from calibre.utils.ordered_dict import OrderedDict
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
||||
from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS
|
||||
|
||||
if iswindows:
|
||||
import calibre.utils.winshell as winshell
|
||||
@ -137,10 +138,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
('formats', {'table':None, 'column':None,
|
||||
'type':None, 'is_multiple':False,
|
||||
'kind':'standard', 'name':_('Formats')}),
|
||||
('publishers',{'table':'publishers', 'column':'name',
|
||||
('publisher', {'table':'publishers', 'column':'name',
|
||||
'type':'text', 'is_multiple':False,
|
||||
'kind':'standard', 'name':_('Publishers')}),
|
||||
('ratings', {'table':'ratings', 'column':'rating',
|
||||
('rating', {'table':'ratings', 'column':'rating',
|
||||
'type':'rating', 'is_multiple':False,
|
||||
'kind':'standard', 'name':_('Ratings')}),
|
||||
('news', {'table':'news', 'column':'name',
|
||||
@ -152,6 +153,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
]
|
||||
self.tag_browser_categories = OrderedDict()
|
||||
for k,v in tag_browser_categories_items:
|
||||
if k not in RESERVED_METADATA_FIELDS:
|
||||
raise ValueError('Tag category [%s] is not a reserved word.' %(k))
|
||||
self.tag_browser_categories[k] = v
|
||||
|
||||
self.connect()
|
||||
@ -694,25 +697,25 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
if category in icon_map:
|
||||
icon = icon_map[category]
|
||||
elif self.tag_browser_categories[category]['kind'] == 'custom':
|
||||
icon = icon_map['*custom']
|
||||
icon_map[category] = icon_map['*custom']
|
||||
icon = icon_map[':custom']
|
||||
icon_map[category] = icon
|
||||
tooltip = self.custom_column_label_map[category]['name']
|
||||
|
||||
datatype = self.tag_browser_categories[category]['type']
|
||||
if datatype == 'rating':
|
||||
item_zero_func = (lambda x: len(formatter(r[1])) > 0)
|
||||
item_not_zero_func = (lambda x: x[1] > 0 and x[2] > 0)
|
||||
formatter = (lambda x:u'\u2605'*int(round(x/2.)))
|
||||
elif category == 'authors':
|
||||
item_zero_func = (lambda x: x[2] > 0)
|
||||
item_not_zero_func = (lambda x: x[2] > 0)
|
||||
# Clean up the authors strings to human-readable form
|
||||
formatter = (lambda x: x.replace('|', ','))
|
||||
else:
|
||||
item_zero_func = (lambda x: x[2] > 0)
|
||||
item_not_zero_func = (lambda x: x[2] > 0)
|
||||
formatter = (lambda x:x)
|
||||
|
||||
categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0],
|
||||
icon=icon, tooltip = tooltip)
|
||||
for r in data if item_zero_func(r)]
|
||||
for r in data if item_not_zero_func(r)]
|
||||
|
||||
# We delayed computing the standard formats category because it does not
|
||||
# use a view, but is computed dynamically
|
||||
@ -767,14 +770,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
items.append(taglist[label][name])
|
||||
# else: do nothing, to not include nodes w zero counts
|
||||
if len(items):
|
||||
cat_name = user_cat+'*' # add the * to avoid name collision
|
||||
cat_name = user_cat+':' # add the ':' to avoid name collision
|
||||
self.tag_browser_categories[cat_name] = {
|
||||
'table':None, 'column':None,
|
||||
'type':None, 'is_multiple':False,
|
||||
'kind':'user', 'name':user_cat}
|
||||
# Not a problem if we accumulate entries in the icon map
|
||||
if icon_map is not None:
|
||||
icon_map[cat_name] = icon_map['*user']
|
||||
icon_map[cat_name] = icon_map[':user']
|
||||
if sort_on_count:
|
||||
categories[cat_name] = \
|
||||
sorted(items, cmp=(lambda x, y: cmp(y.count, x.count)))
|
||||
|
@ -22,7 +22,7 @@ from calibre.utils.pyparsing import Keyword, Group, Forward, CharsNotIn, Suppres
|
||||
OneOrMore, oneOf, CaselessLiteral, Optional, NoMatch, ParseException
|
||||
from calibre.constants import preferred_encoding
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
from calibre.ebooks.metadata.book import RESERVED_METADATA_FIELDS
|
||||
|
||||
'''
|
||||
This class manages access to the preference holding the saved search queries.
|
||||
@ -87,21 +87,25 @@ class SearchQueryParser(object):
|
||||
'''
|
||||
|
||||
DEFAULT_LOCATIONS = [
|
||||
'tag',
|
||||
'title',
|
||||
'author',
|
||||
'all',
|
||||
'author', # compatibility
|
||||
'authors',
|
||||
'comment', # compatibility
|
||||
'comments',
|
||||
'cover',
|
||||
'date',
|
||||
'format', # compatibility
|
||||
'formats',
|
||||
'isbn',
|
||||
'ondevice',
|
||||
'pubdate',
|
||||
'publisher',
|
||||
'search',
|
||||
'series',
|
||||
'rating',
|
||||
'cover',
|
||||
'comments',
|
||||
'format',
|
||||
'isbn',
|
||||
'search',
|
||||
'date',
|
||||
'pubdate',
|
||||
'ondevice',
|
||||
'all',
|
||||
'tag', # compatibility
|
||||
'tags',
|
||||
'title',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
@ -118,6 +122,9 @@ class SearchQueryParser(object):
|
||||
return failed
|
||||
|
||||
def __init__(self, locations=None, test=False):
|
||||
for k in self.DEFAULT_LOCATIONS:
|
||||
if k not in RESERVED_METADATA_FIELDS:
|
||||
raise ValueError('Search location [%s] is not a reserved word.' %(k))
|
||||
if locations is None:
|
||||
locations = self.DEFAULT_LOCATIONS
|
||||
self._tests_failed = False
|
||||
|
Loading…
x
Reference in New Issue
Block a user