From 3b557de4c724384587f99ff2ad2f496e3db46011 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 23 May 2010 21:34:19 +0100 Subject: [PATCH] First pass at converting db2.get_categories to return a complete dict --- src/calibre/gui2/tag_view.py | 8 +-- src/calibre/library/custom_columns.py | 4 +- src/calibre/library/database2.py | 61 +++++++++------- src/calibre/utils/ordered_dict.py | 100 ++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 32 deletions(-) create mode 100644 src/calibre/utils/ordered_dict.py diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 8a01b6ad27..0fb72e071b 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -199,8 +199,8 @@ class TagsModel(QAbstractItemModel): # {{{ categories_orig = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('Ratings'), _('News'), _('Tags')] - row_map_orig = ['author', 'series', 'format', 'publisher', 'rating', - 'news', 'tag'] + row_map_orig = ['authors', 'series', 'formats', 'publishers', 'ratings', + 'news', 'tags'] tags_categories_start= 7 search_keys=['search', _('Searches')] @@ -264,8 +264,8 @@ class TagsModel(QAbstractItemModel): # {{{ self.cat_icon_map.append(self.cat_icon_map_orig[i]) # Clean up the author's tags, getting rid of the '|' characters - if data['author'] is not None: - for t in data['author']: + if data['authors'] is not None: + for t in data['authors']: t.name = t.name.replace('|', ',') # Now do the user-defined categories. There is a time/space tradeoff here. diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index a8375c6b5c..b6ada01b8c 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -144,8 +144,8 @@ class CustomColumns(object): for i, v in self.custom_column_num_map.items(): if v['normalized']: tn = 'custom_column_{0}'.format(i) - self.tag_browser_categories[tn] = [v['label'], 'value'] - self.tag_browser_datatype[v['label']] = v['datatype'] + self.tag_browser_categories[v['label']] = {'table':tn, 'column':'value', 'type':v['datatype'], 'name':v['name']} + #self.tag_browser_datatype[v['label']] = v['datatype'] def get_custom(self, idx, label=None, num=None, index_is_id=False): if label is not None: diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index ed56d35bdc..12398de918 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -33,6 +33,7 @@ from calibre.customize.ui import run_plugins_on_import from calibre.utils.filenames import ascii_filename from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp +from calibre.utils.ordered_dict import OrderedDict from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format if iswindows: @@ -123,22 +124,25 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if isinstance(self.dbpath, unicode): self.dbpath = self.dbpath.encode(filesystem_encoding) - self.tag_browser_categories = { - 'tags' : ['tag', 'name'], - 'series' : ['series', 'name'], - 'publishers': ['publisher', 'name'], - 'authors' : ['author', 'name'], - 'news' : ['news', 'name'], - 'ratings' : ['rating', 'rating'] - } - self.tag_browser_datatype = { - 'tag' : 'textmult', - 'series' : None, - 'publisher' : 'text', - 'author' : 'text', - 'news' : None, - 'rating' : 'rating', - } + # Order as has been customary in the tags pane. + self.tag_browser_categories = OrderedDict([ + ('authors', {'table':'authors', 'column':'name', 'type':'text', 'name':_('Authors')}), + ('series', {'table':'series', 'column':'name', 'type':None, 'name':_('Series')}), + ('formats', {'table':None, 'column':None, 'type':None, 'name':_('Formats')}), + ('publishers',{'table':'publishers', 'column':'name', 'type':'text', 'name':_('Publishers')}), + ('ratings', {'table':'ratings', 'column':'rating', 'type':'rating', 'name':_('Ratings')}), + ('news', {'table':'news', 'column':'name', 'type':None, 'name':_('News')}), + ('tags', {'table':'tags', 'column':'name', 'type':'textmult', 'name':_('Tags')}), + ]) + +# self.tag_browser_datatype = { +# 'tag' : 'textmult', +# 'series' : None, +# 'publisher' : 'text', +# 'author' : 'text', +# 'news' : None, +# 'rating' : 'rating', +# } self.tag_browser_formatters = {'rating': lambda x:u'\u2605'*int(round(x/2.))} @@ -653,17 +657,22 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.books_list_filter.change([] if not ids else ids) categories = {} - for tn, cn in self.tag_browser_categories.items(): + for category in self.tag_browser_categories.keys(): + tn = self.tag_browser_categories[category]['table'] + categories[category] = [] #reserve the position in the ordered list + if tn is None: + continue + cn = self.tag_browser_categories[category]['column'] if ids is None: - query = 'SELECT id, {0}, count FROM tag_browser_{1}'.format(cn[1], tn) + query = 'SELECT id, {0}, count FROM tag_browser_{1}'.format(cn, tn) else: - query = 'SELECT id, {0}, count FROM tag_browser_filtered_{1}'.format(cn[1], tn) + query = 'SELECT id, {0}, count FROM tag_browser_filtered_{1}'.format(cn, tn) if sort_on_count: query += ' ORDER BY count DESC' else: - query += ' ORDER BY {0} ASC'.format(cn[1]) + query += ' ORDER BY {0} ASC'.format(cn) data = self.conn.get(query) - category = cn[0] + # category = cn[0] icon, tooltip = None, '' if icon_map: if category in icon_map: @@ -671,14 +680,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): else: icon = icon_map['*custom'] tooltip = self.custom_column_label_map[category]['name'] - datatype = self.tag_browser_datatype[category] + datatype = self.tag_browser_categories[category]['type'] formatter = self.tag_browser_formatters.get(datatype, lambda x: x) categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0], icon=icon, tooltip = tooltip) for r in data if r[2] > 0 and (datatype != 'rating' or len(formatter(r[1])) > 0)] - categories['format'] = [] + categories['formats'] = [] for fmt in self.conn.get('SELECT DISTINCT format FROM data'): fmt = fmt[0] if ids is not None: @@ -693,13 +702,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): WHERE format="%s"'''%fmt, all=False) if count > 0: - categories['format'].append(Tag(fmt, count=count)) + categories['formats'].append(Tag(fmt, count=count)) if sort_on_count: - categories['format'].sort(cmp=lambda x,y:cmp(x.count, y.count), + categories['formats'].sort(cmp=lambda x,y:cmp(x.count, y.count), reverse=True) else: - categories['format'].sort(cmp=lambda x,y:cmp(x.name, y.name)) + categories['formats'].sort(cmp=lambda x,y:cmp(x.name, y.name)) return categories def tags_older_than(self, tag, delta): diff --git a/src/calibre/utils/ordered_dict.py b/src/calibre/utils/ordered_dict.py new file mode 100644 index 0000000000..95a0af9e76 --- /dev/null +++ b/src/calibre/utils/ordered_dict.py @@ -0,0 +1,100 @@ +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other