Add option to show composite columns in the tag browser

This commit is contained in:
Kovid Goyal 2011-03-18 10:28:00 -06:00
commit 001242d21e
9 changed files with 107 additions and 43 deletions

View File

@ -59,14 +59,24 @@ class TagCategories(QDialog, Ui_TagCategories):
]
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')]
cc_map = self.db.custom_column_label_map
for cc in cc_map:
if cc_map[cc]['datatype'] in ['text', 'series']:
self.category_labels.append(db.field_metadata.label_to_key(cc))
cvals = {}
for key,cc in self.db.custom_field_metadata().iteritems():
if cc['datatype'] in ['text', 'series', 'enumeration']:
self.category_labels.append(key)
category_icons.append(cc_icon)
category_values.append(lambda col=cc: self.db.all_custom(label=col))
category_names.append(cc_map[cc]['name'])
category_values.append(lambda col=cc['label']: self.db.all_custom(label=col))
category_names.append(cc['name'])
elif cc['datatype'] == 'composite' and \
cc['display'].get('make_category', False):
self.category_labels.append(key)
category_icons.append(cc_icon)
category_names.append(cc['name'])
dex = cc['rec_index']
cvals = set()
for book in db.data.iterall():
if book[dex]:
cvals.add(book[dex])
category_values.append(lambda s=list(cvals): s)
self.all_items = []
self.all_items_dict = {}
for idx,label in enumerate(self.category_labels):
@ -88,7 +98,8 @@ class TagCategories(QDialog, Ui_TagCategories):
if l[1] in self.category_labels:
if t is None:
t = Item(name=l[0], label=l[1], index=len(self.all_items),
icon=category_icons[self.category_labels.index(l[1])], exists=False)
icon=category_icons[self.category_labels.index(l[1])],
exists=False)
self.all_items.append(t)
self.all_items_dict[key] = t
l[2] = t.index
@ -108,13 +119,16 @@ class TagCategories(QDialog, Ui_TagCategories):
self.add_category_button.clicked.connect(self.add_category)
self.rename_category_button.clicked.connect(self.rename_category)
self.category_box.currentIndexChanged[int].connect(self.select_category)
self.category_filter_box.currentIndexChanged[int].connect(self.display_filtered_categories)
self.category_filter_box.currentIndexChanged[int].connect(
self.display_filtered_categories)
self.delete_category_button.clicked.connect(self.del_category)
if islinux:
self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
else:
self.connect(self.available_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
self.connect(self.applied_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
self.connect(self.available_items_box,
SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
self.connect(self.applied_items_box,
SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
self.populate_category_list()
if on_category is not None:
@ -129,6 +143,7 @@ class TagCategories(QDialog, Ui_TagCategories):
n = item.name if item.exists else item.name + _(' (not on any book)')
w = QListWidgetItem(item.icon, n)
w.setData(Qt.UserRole, item.index)
w.setToolTip(_('Category lookup name: ') + item.label)
return w
def display_filtered_categories(self, idx):

View File

@ -118,6 +118,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
else:
sb = 0
self.composite_sort_by.setCurrentIndex(sb)
self.composite_make_category.setChecked(
c['display'].get('make_category', False))
elif ct == 'enumeration':
self.enum_box.setText(','.join(c['display'].get('enum_values', [])))
self.datatype_changed()
@ -159,7 +161,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
col_type = None
for x in ('box', 'default_label', 'label'):
getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime')
for x in ('box', 'default_label', 'label', 'sort_by', 'sort_by_label'):
for x in ('box', 'default_label', 'label', 'sort_by', 'sort_by_label',
'make_category'):
getattr(self, 'composite_'+x).setVisible(col_type == 'composite')
for x in ('box', 'default_label', 'label'):
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
@ -222,7 +225,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
' composite columns'))
display_dict = {'composite_template':unicode(self.composite_box.text()).strip(),
'composite_sort': ['text', 'number', 'date', 'bool']
[self.composite_sort_by.currentIndex()]
[self.composite_sort_by.currentIndex()],
'make_category': self.composite_make_category.isChecked(),
}
elif col_type == 'enumeration':
if not unicode(self.enum_box.text()).strip():

View File

@ -220,7 +220,9 @@ Everything else will show nothing.</string>
</item>
</layout>
</item>
<item row="6" column="0">
<item row="6" column="2">
<layout class="QHBoxLayout" name="composite_layout">
<item>
<widget class="QLabel" name="composite_sort_by_label">
<property name="text">
<string>&amp;Sort/search column by</string>
@ -230,8 +232,6 @@ Everything else will show nothing.</string>
</property>
</widget>
</item>
<item row="6" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="composite_sort_by">
<property name="toolTip">
@ -239,6 +239,16 @@ Everything else will show nothing.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="composite_make_category">
<property name="text">
<string>Show in tags browser</string>
</property>
<property name="toolTip">
<string>If checked, this column will appear in the tags browser as a category</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_24">
<property name="orientation">

View File

@ -510,9 +510,11 @@ class TagsView(QTreeView): # {{{
if hasattr(md, 'column_name'):
fm_src = self.db.metadata_for_field(md.column_name)
if md.column_name in ['authors', 'publisher', 'series'] or \
(fm_src['is_custom'] and
fm_src['datatype'] in ['series', 'text'] and
not fm_src['is_multiple']):
(fm_src['is_custom'] and (
(fm_src['datatype'] in ['series', 'text', 'enumeration'] and
not fm_src['is_multiple']) or
(fm_src['datatype'] == 'composite' and
fm_src['display'].get('make_category', False)))):
self.setDropIndicatorShown(True)
def clear(self):
@ -976,8 +978,11 @@ class TagsModel(QAbstractItemModel): # {{{
fm = self.db.metadata_for_field(node.tag.category)
if node.tag.category in \
('tags', 'series', 'authors', 'rating', 'publisher') or \
(fm['is_custom'] and \
fm['datatype'] in ['text', 'rating', 'series']):
(fm['is_custom'] and (
fm['datatype'] in ['text', 'rating', 'series',
'enumeration'] or
(fm['datatype'] == 'composite' and
fm['display'].get('make_category', False)))):
mime = 'application/calibre+from_library'
ids = list(map(int, str(md.data(mime)).split()))
self.handle_drop(node, ids)
@ -987,9 +992,11 @@ class TagsModel(QAbstractItemModel): # {{{
if fm_dest['kind'] == 'user':
fm_src = self.db.metadata_for_field(md.column_name)
if md.column_name in ['authors', 'publisher', 'series'] or \
(fm_src['is_custom'] and
fm_src['datatype'] in ['series', 'text'] and
not fm_src['is_multiple']):
(fm_src['is_custom'] and (
(fm_src['datatype'] in ['series', 'text', 'enumeration'] and
not fm_src['is_multiple']))or
(fm_src['datatype'] == 'composite' and
fm_src['display'].get('make_category', False))):
mime = 'application/calibre+from_library'
ids = list(map(int, str(md.data(mime)).split()))
self.handle_user_category_drop(node, ids, md.column_name)
@ -1003,7 +1010,6 @@ class TagsModel(QAbstractItemModel): # {{{
return
fm_src = self.db.metadata_for_field(column)
for id in ids:
vmap = {}
label = fm_src['label']
if not fm_src['is_custom']:
if label == 'authors':
@ -1019,19 +1025,21 @@ class TagsModel(QAbstractItemModel): # {{{
value = self.db.series(id, index_is_id=True)
else:
items = self.db.get_custom_items_with_ids(label=label)
if fm_src['datatype'] != 'composite':
value = self.db.get_custom(id, label=label, index_is_id=True)
else:
value = self.db.get_property(id, loc=fm_src['rec_index'],
index_is_id=True)
if value is None:
return
if not isinstance(value, list):
value = [value]
for v in items:
vmap[v[1]] = v[0]
for val in value:
for (v, c, id) in category:
if v == val and c == column:
break
else:
category.append([val, column, vmap[val]])
category.append([val, column, 0])
categories[on_node.category_key[1:]] = category
self.db.prefs.set('user_categories', categories)
self.tags_view.recount()

View File

@ -1210,6 +1210,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return ans
field = self.field_metadata[category]
if field['datatype'] == 'composite':
dex = field['rec_index']
for book in self.data.iterall():
if book[dex] == id_:
ans.add(book[0])
return ans
ans = self.conn.get(
'SELECT book FROM books_{tn}_link WHERE {col}=?'.format(
tn=field['table'], col=field['link_column']), (id_,))
@ -1281,7 +1288,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# First, build the maps. We need a category->items map and an
# item -> (item_id, sort_val) map to use in the books loop
for category in tb_cats.keys():
for category in tb_cats.iterkeys():
cat = tb_cats[category]
if not cat['is_category'] or cat['kind'] in ['user', 'search'] \
or category in ['news', 'formats'] or cat.get('is_csp',
@ -1324,8 +1331,15 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
tcategories[category] = {}
# create a list of category/field_index for the books scan to use.
# This saves iterating through field_metadata for each book
md.append((category, cat['rec_index'], cat['is_multiple']))
md.append((category, cat['rec_index'], cat['is_multiple'], False))
for category in tb_cats.iterkeys():
cat = tb_cats[category]
if cat['datatype'] == 'composite' and \
cat['display'].get('make_category', False):
tcategories[category] = {}
md.append((category, cat['rec_index'], cat['is_multiple'],
cat['datatype'] == 'composite'))
#print 'end phase "collection":', time.clock() - last, 'seconds'
#last = time.clock()
@ -1339,11 +1353,22 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
continue
rating = book[rating_dex]
# We kept track of all possible category field_map positions above
for (cat, dex, mult) in md:
if book[dex] is None:
for (cat, dex, mult, is_comp) in md:
if not book[dex]:
continue
if not mult:
val = book[dex]
if is_comp:
item = tcategories[cat].get(val, None)
if not item:
item = tag_class(val, val)
tcategories[cat][val] = item
item.c += 1
item.id = val
if rating > 0:
item.rt += rating
item.rc += 1
continue
try:
(item_id, sort_val) = tids[cat][val] # let exceptions fly
item = tcategories[cat].get(val, None)
@ -1405,7 +1430,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# and building the Tag instances.
categories = {}
tag_class = Tag
for category in tb_cats.keys():
for category in tb_cats.iterkeys():
if category not in tcategories:
continue
cat = tb_cats[category]

View File

@ -623,6 +623,8 @@ class BrowseServer(object):
except:
raise cherrypy.HTTPError(404, 'Search: %r not understood'%which)
else:
if fm[category]['datatype'] == 'composite':
cid = cid.decode('utf-8')
all_ids = self.search_cache('')
if category == 'newest':
ids = all_ids

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -70,7 +70,7 @@ Then after restarting |app|, you must tell |app| that the column is to be treate
At the point there are no genres in the column. We are left with the last step: how to apply a genre to a book. A genre does not exist in |app| until it appears on at least one book. To learn how to apply a genre for the first time, we must go into some detail about what a genre looks like in the metadata for a book.
A hierarchy of 'things' is built by creating an item consisting of phrases separated by periods. Continuing the genre example, these items would "History.Military", "Mysteries.Vampire", "Science Fiction.Space Opera", etc. Thus to create a new genre, you pick a book that should have that genre, edit its metadata, and enter the new genre into the column you created. Continuing our example, if you want to assign a new genre "Comics" with a sub-genre "Superheros" to a book, you would 'edit metadata' for that (comic) book, choose the Custom metadata tab, and then enter "Comics.Superheros" as shown in the following (ignore the other custom columns):
A hierarchy of 'things' is built by creating an item consisting of phrases separated by periods. Continuing the genre example, these items would "History.Military", "Mysteries.Vampire", "Science Fiction.Space Opera", etc. Thus to create a new genre, you pick a book that should have that genre, edit its metadata, and enter the new genre into the column you created. Continuing our example, if you want to assign a new genre "Comics" with a sub-genre "Superheroes" to a book, you would 'edit metadata' for that (comic) book, choose the Custom metadata tab, and then enter "Comics.Superheroes" as shown in the following (ignore the other custom columns):
.. image:: images/sg_genre.jpg
:align: center