From 27eca8fe7269ad5311f38065dd650d5174b8fac9 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 19 Apr 2010 17:22:47 +0100 Subject: [PATCH] Commit before more extensive testing --- resources/images/drawer.svg | 2679 ++++++++++++++++++++ src/calibre/gui2/__init__.py | 3 +- src/calibre/gui2/dialogs/tag_categories.py | 210 +- src/calibre/gui2/dialogs/tag_categories.ui | 70 +- src/calibre/gui2/main.ui | 4 +- src/calibre/gui2/tag_view.py | 101 +- src/calibre/library/caches.py | 1 + src/calibre/library/database2.py | 10 +- src/calibre/utils/date.py | 9 +- 9 files changed, 2874 insertions(+), 213 deletions(-) create mode 100644 resources/images/drawer.svg diff --git a/resources/images/drawer.svg b/resources/images/drawer.svg new file mode 100644 index 0000000000..679bca53b2 --- /dev/null +++ b/resources/images/drawer.svg @@ -0,0 +1,2679 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 12987caeb9..a78c71316f 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -96,7 +96,8 @@ def _config(): help=_('Overwrite author and title with new metadata')) c.add_opt('enforce_cpu_limit', default=True, help=_('Limit max simultaneous jobs to number of CPUs')) - c.add_opt('tag_categories', default={}, help=_('User-created tag categories')) + c.add_opt('user_categories', default={}, + help=_('User-created tag browser categories')) return ConfigProxy(c) diff --git a/src/calibre/gui2/dialogs/tag_categories.py b/src/calibre/gui2/dialogs/tag_categories.py index 5711f4794f..74afc67242 100644 --- a/src/calibre/gui2/dialogs/tag_categories.py +++ b/src/calibre/gui2/dialogs/tag_categories.py @@ -1,8 +1,12 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -from PyQt4.QtCore import SIGNAL, Qt -from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QComboBox + +from copy import copy + +from PyQt4.QtCore import SIGNAL, Qt, QVariant +from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QComboBox, \ + QIcon, QListWidgetItem from PyQt4.Qt import QString from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories @@ -11,10 +15,18 @@ from calibre.gui2 import question_dialog, error_dialog from calibre.gui2.dialogs.confirm_delete import confirm from calibre.constants import islinux -class TagCategories(QDialog, Ui_TagCategories): - category_names = [_('Authors'), _('Series'), _('Publishers'), _('Tags')] - category_labels = ['author', 'series', 'publisher', 'tag'] +class Item: + def __init__(self, name, label, index, icon, exists): + self.name = name + self.label = label + self.index = index + self.icon = icon + self.exists = exists + def __str__(self): + return 'name=%s, label=%s, index=%s, exists='%(self.name, self.label, self.index, self.exists) +class TagCategories(QDialog, Ui_TagCategories): + category_labels = ['', 'author', 'series', 'publisher', 'tag'] def __init__(self, window, db, index=None): QDialog.__init__(self, window) @@ -23,86 +35,110 @@ class TagCategories(QDialog, Ui_TagCategories): self.db = db self.index = index - self.tags = [] + self.applied_items = [] - self.all_items = {} - self.all_items['tag'] = sorted(self.db.all_tags(), cmp=lambda x,y: cmp(x.lower(), y.lower())) - self.all_items['author'] = sorted([i[1].replace('|', ',') for i in self.db.all_authors()], - cmp=lambda x,y: cmp(x.lower(), y.lower())) - self.all_items['publisher'] = sorted([i[1] for i in self.db.all_publishers()], - cmp=lambda x,y: cmp(x.lower(), y.lower())) - self.all_items['series'] = sorted([i[1] for i in self.db.all_series()], - cmp=lambda x,y: cmp(x.lower(), y.lower())) + category_icons = [None, QIcon(I('user_profile.svg')), QIcon(I('series.svg')), + QIcon(I('publisher.png')), QIcon(I('tags.svg'))] + category_values = [None, + lambda: [n for (id, n) in self.db.all_authors()], + lambda: [n for (id, n) in self.db.all_series()], + lambda: [n for (id, n) in self.db.all_publishers()], + lambda: self.db.all_tags() + ] + category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')] + + self.all_items = [] + self.all_items_dict = {} + for idx,label in enumerate(self.category_labels): + if idx == 0: + continue + for n in category_values[idx](): + t = Item(name=n, label=label, index=len(self.all_items),icon=category_icons[idx], exists=True) + self.all_items.append(t) + self.all_items_dict[label+':'+n] = t + + self.categories = dict.copy(config['user_categories']) + if self.categories is None: + self.categories = {} + for cat in self.categories: + for item,l in enumerate(self.categories[cat]): + key = ':'.join([l[1], l[0]]) + t = self.all_items_dict.get(key, None) + 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) + self.all_items.append(t) + self.all_items_dict[key] = t + l[2] = t.index + + self.all_items_sorted = sorted(self.all_items, cmp=lambda x,y: cmp(x.name.lower(), y.name.lower())) + self.display_filtered_categories(0) + + for v in category_names: + self.category_filter_box.addItem(v) self.current_cat_name = None - self.current_cat_label= None - self.category_label_to_name = {} - self.category_name_to_label = {} - for i in range(len(self.category_labels)): - self.category_label_to_name[self.category_labels[i]] = self.category_names[i] - self.category_name_to_label[self.category_names[i]] = self.category_labels[i] self.connect(self.apply_button, SIGNAL('clicked()'), self.apply_tags) self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_tags) self.connect(self.add_category_button, SIGNAL('clicked()'), self.add_category) self.connect(self.category_box, SIGNAL('currentIndexChanged(int)'), self.select_category) + self.connect(self.category_filter_box, SIGNAL('currentIndexChanged(int)'), self.display_filtered_categories) self.connect(self.delete_category_button, SIGNAL('clicked()'), self.del_category) if islinux: - self.available_tags.itemDoubleClicked.connect(self.apply_tags) + self.available_items_box.itemDoubleClicked.connect(self.apply_tags) else: - self.connect(self.available_tags, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags) - self.connect(self.applied_tags, 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.categories = dict.copy(config['tag_categories']) - if self.categories is None: - self.categories = {} self.populate_category_list() - self.category_kind_box.clear() - for i in range(len(self.category_names)): - self.category_kind_box.addItem(self.category_names[i]) + return self.select_category(0) - def apply_tags(self, item=None): - if self.current_cat_name[0] is None: + def make_list_widget(self, item): + n = item.name if item.exists else item.name + _(' (not on any book)') + w = QListWidgetItem(item.icon, n) + w.setData(Qt.UserRole, item.index) + return w + + def display_filtered_categories(self, idx): + idx = idx if idx is not None else self.category_filter_box.currentIndex() + self.available_items_box.clear() + self.applied_items_box.clear() + for item in self.all_items_sorted: + if idx == 0 or item.label == self.category_labels[idx]: + if item.index not in self.applied_items and item.exists: + self.available_items_box.addItem(self.make_list_widget(item)) + for index in self.applied_items: + self.applied_items_box.addItem(self.make_list_widget(self.all_items[index])) + + def apply_tags(self, node=None): + if self.current_cat_name is None: return - items = self.available_tags.selectedItems() if item is None else [item] - for item in items: - tag = qstring_to_unicode(item.text()) - if tag not in self.tags: - self.tags.append(tag) - self.available_tags.takeItem(self.available_tags.row(item)) - self.tags.sort() - self.applied_tags.clear() - for tag in self.tags: - self.applied_tags.addItem(tag) - def unapply_tags(self, item=None): - items = self.applied_tags.selectedItems() if item is None else [item] - for item in items: - tag = qstring_to_unicode(item.text()) - self.tags.remove(tag) - self.available_tags.addItem(tag) - self.tags.sort() - self.applied_tags.clear() - for tag in self.tags: - self.applied_tags.addItem(tag) - self.available_tags.sortItems() + nodes = self.available_items_box.selectedItems() if node is None else [node] + for node in nodes: + index = self.all_items[node.data(Qt.UserRole).toPyObject()].index + if index not in self.applied_items: + self.applied_items.append(index) + self.applied_items.sort(cmp=lambda x, y:cmp(self.all_items[x].name.lower(), self.all_items[y].name.lower())) + self.display_filtered_categories(None) + + def unapply_tags(self, node=None): + nodes = self.applied_items_box.selectedItems() if node is None else [node] + for node in nodes: + index = self.all_items[node.data(Qt.UserRole).toPyObject()].index + self.applied_items.remove(index) + self.display_filtered_categories(None) def add_category(self): self.save_category() cat_name = qstring_to_unicode(self.input_box.text()).strip() if cat_name == '': - return - cat_kind = unicode(self.category_kind_box.currentText()) - r_cat_kind = self.category_name_to_label[cat_kind] - if r_cat_kind not in self.categories: - self.categories[r_cat_kind] = {} - if cat_name not in self.categories[r_cat_kind]: + return False + if cat_name not in self.categories: self.category_box.clear() - self.category_kind_label.setText(cat_kind) self.current_cat_name = cat_name - self.current_cat_label = r_cat_kind - self.categories[r_cat_kind][cat_name] = [] - if len(self.tags): - self.clear_boxes(item_label=self.current_cat_label) + self.categories[cat_name] = [] + self.applied_items = [] self.populate_category_list() self.category_box.setCurrentIndex(self.category_box.findText(cat_name)) else: @@ -116,8 +152,8 @@ class TagCategories(QDialog, Ui_TagCategories): return if self.current_cat_name is not None: if self.current_cat_name == unicode(self.category_box.currentText()): - del self.categories[self.current_cat_label][self.current_cat_name] - self.current_category = [None, None] ## order here is important. RemoveItem will put it back + del self.categories[self.current_cat_name] + self.current_category = None self.category_box.removeItem(self.category_box.currentIndex()) def select_category(self, idx): @@ -125,47 +161,25 @@ class TagCategories(QDialog, Ui_TagCategories): s = self.category_box.itemText(idx) if s: self.current_cat_name = unicode(s) - self.current_cat_label = str(self.category_box.itemData(idx).toString()) else: self.current_cat_name = None - self.current_cat_label = None - self.clear_boxes(item_label=False) - if self.current_cat_label: - self.category_kind_label.setText(self.category_label_to_name[self.current_cat_label]) - self.tags = self.categories[self.current_cat_label].get(self.current_cat_name, []) - # Must do two loops because obsolete values can be saved - # We need to show these to the user so they can be deleted if desired - for t in self.tags: - self.applied_tags.addItem(t) - for t in self.all_items[self.current_cat_label]: - if t not in self.tags: - self.available_tags.addItem(t) - else: - self.category_kind_label.setText('') - - - def clear_boxes(self, item_label = None): - self.tags = [] - self.applied_tags.clear() - self.available_tags.clear() - if item_label: - for item in self.all_items[item_label]: - self.available_tags.addItem(item) + if self.current_cat_name: + self.applied_items = [tup[2] for tup in self.categories.get(self.current_cat_name, [])] + self.display_filtered_categories(None) def accept(self): self.save_category() - config['tag_categories'] = self.categories + config['user_categories'] = self.categories QDialog.accept(self) def save_category(self): if self.current_cat_name is not None: - self.categories[self.current_cat_label][self.current_cat_name] = self.tags + l = [] + for index in self.applied_items: + item = self.all_items[index] + l.append([item.name, item.label, item.index]) + self.categories[self.current_cat_name] = l def populate_category_list(self): - cat_list = {} - for c in self.categories: - for n in self.categories[c]: - if n.strip(): - cat_list[n] = c - for n in sorted(cat_list.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())): - self.category_box.addItem(n, cat_list[n]) \ No newline at end of file + for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())): + self.category_box.addItem(n) \ No newline at end of file diff --git a/src/calibre/gui2/dialogs/tag_categories.ui b/src/calibre/gui2/dialogs/tag_categories.ui index 9b3b58bc87..2904b2464e 100644 --- a/src/calibre/gui2/dialogs/tag_categories.ui +++ b/src/calibre/gui2/dialogs/tag_categories.ui @@ -25,10 +25,10 @@ - A&vailable values + A&vailable items - available_tags + available_items_box @@ -50,7 +50,7 @@ - + true @@ -117,10 +117,10 @@ - A&pplied values + A&pplied items - applied_tags + applied_items_box @@ -140,7 +140,7 @@ - + true @@ -311,48 +311,6 @@ - - - - Select the content kind of the new category - - - - Author - - - - - Series - - - - - Formats - - - - - Publishers - - - - - Tags - - - - - - - - Category kind: - - - category_kind_box - - - @@ -366,23 +324,23 @@ - - - - TextLabel - - - - Category kind: + Category filter: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Select the content kind of the new category + + + diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index b3ed89af93..12ce6fb2c9 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -355,10 +355,10 @@ - Manage tag categories + Manage user categories - Create, edit, and delete tag categories + Create, edit, and delete user categories diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 7b24d9d2e0..2e964e8d8a 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -126,7 +126,8 @@ class TagTreeItem(object): TAG = 1 ROOT = 2 - def __init__(self, data=None, tag=None, category_icon=None, icon_map=None, parent=None): +# def __init__(self, data=None, tag=None, category_icon=None, icon_map=None, parent=None): + def __init__(self, data=None, category_icon=None, icon_map=None, parent=None): self.parent = parent self.children = [] if self.parent is not None: @@ -142,13 +143,14 @@ class TagTreeItem(object): self.bold_font.setBold(True) self.bold_font = QVariant(self.bold_font) elif self.type == self.TAG: - self.tag, self.icon_map = data, list(map(QVariant, icon_map)) + icon_map[0] = data.icon + self.tag, self.icon_state_map = data, list(map(QVariant, icon_map)) def __str__(self): if self.type == self.ROOT: return 'ROOT' if self.type == self.CATEGORY: - return 'CATEGORY:'+self.name+':%d'%len(self.children) + return 'CATEGORY:'+str(QVariant.toString(self.name))+':%d'%len(self.children) return 'TAG:'+self.tag.name def row(self): @@ -183,7 +185,7 @@ class TagTreeItem(object): else: return QVariant('[%d] %s'%(self.tag.count, self.tag.name)) if role == Qt.DecorationRole: - return self.icon_map[self.tag.state] + return self.icon_state_map[self.tag.state] if role == Qt.ToolTipRole and self.tag.tooltip: return QVariant(self.tag.tooltip) return NONE @@ -196,16 +198,20 @@ class TagTreeItem(object): class TagsModel(QAbstractItemModel): categories_orig = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('All tags')] row_map_orig = ['author', 'series', 'format', 'publisher', 'news', 'tag'] - fixed_categories= 5 + tags_categories_start= 5 search_keys=['search', _('Searches')] def __init__(self, db, parent=None): QAbstractItemModel.__init__(self, parent) - self.cmap_orig = list(map(QIcon, [I('user_profile.svg'), + self.cat_icon_map_orig = list(map(QIcon, [I('user_profile.svg'), I('series.svg'), I('book.svg'), I('publisher.png'), - I('news.svg')])) - self.icon_map = [QIcon(), QIcon(I('plus.svg')), - QIcon(I('minus.svg'))] + I('news.svg'), I('tags.svg')])) + self.icon_state_map = [None, QIcon(I('plus.svg')), QIcon(I('minus.svg'))] + self.custcol_icon = QIcon(I('column.svg')) + self.search_icon = QIcon(I('search.svg')) + self.usercat_icon = QIcon(I('drawer.svg')) + self.label_to_icon_map = dict(map(None, self.row_map_orig, self.cat_icon_map_orig)) + self.label_to_icon_map['*custom'] = self.custcol_icon self.db = db self.search_restriction = '' self.user_categories = {} @@ -214,9 +220,9 @@ class TagsModel(QAbstractItemModel): self.root_item = TagTreeItem() for i, r in enumerate(self.row_map): c = TagTreeItem(parent=self.root_item, - data=self.categories[i], category_icon=self.cmap[i]) + data=self.categories[i], category_icon=self.cat_icon_map[i]) for tag in data[r]: - TagTreeItem(parent=c, data=tag, icon_map=self.icon_map) + TagTreeItem(parent=c, data=tag, icon_map=self.icon_state_map) def set_search_restriction(self, s): self.search_restriction = s @@ -224,65 +230,58 @@ class TagsModel(QAbstractItemModel): def get_node_tree(self, sort): self.row_map = [] self.categories = [] - self.cmap = self.cmap_orig[:] - self.user_categories = dict.copy(config['tag_categories']) + self.cat_icon_map = self.cat_icon_map_orig[:-1] # strip the tags icon. We will put it back later + self.user_categories = dict.copy(config['user_categories']) column_map = config['column_map'] - for i in range(0, self.fixed_categories): # First the standard categories + for i in range(0, self.tags_categories_start): # First the standard categories self.row_map.append(self.row_map_orig[i]) self.categories.append(self.categories_orig[i]) if len(self.search_restriction): - data = self.db.get_categories(sort_on_count=sort, + data = self.db.get_categories(sort_on_count=sort, icon_map=self.label_to_icon_map, ids=self.db.search(self.search_restriction, return_matches=True)) else: - data = self.db.get_categories(sort_on_count=sort) + data = self.db.get_categories(sort_on_count=sort, icon_map=self.label_to_icon_map) - for i in data: # now the custom columns - if i not in self.row_map_orig and i in column_map: - self.row_map.append(i) - self.categories.append(self.db.custom_column_label_map[i]['name']) - self.cmap.append(QIcon(I('column.svg'))) + for c in data: # now the custom columns + if c not in self.row_map_orig and c in column_map: + self.row_map.append(c) + self.categories.append(self.db.custom_column_label_map[c]['name']) + self.cat_icon_map.append(self.custcol_icon) - for i in self.row_map_orig: - if i not in self.user_categories: - self.user_categories[i] = {} - config['tag_categories'] = self.user_categories + # Now do the user-defined categories. There is a time/space tradeoff here. + # By converting the tags into a map, we can do the verification in the category + # loop much faster, at the cost of duplicating the categories lists. + taglist = {} + for c in self.row_map_orig: + taglist[c] = dict(map(lambda t:(t.name if c != 'author' else t.name.replace('|', ','), t), data[c])) - taglist = {} # Now the user-defined categories - for i in data: - taglist[i] = dict(map(lambda t:(t.name if i != 'author' else t.name.replace('|', ','), t), data[i])) - for k in self.row_map_orig: - if k not in self.user_categories: - continue - for i in sorted(self.user_categories[k].keys()): # now the tag categories - l = [] - for t in self.user_categories[k][i]: - if t in taglist[k]: # use same tag node as the complete category - l.append(taglist[k][t]) - # else: eliminate nodes that have zero counts - data[i+'*'] = l - self.row_map.append(i+'*') - self.categories.append(i) - if k == 'tag': # choose the icon - self.cmap.append(QIcon(I('tags.svg'))) - else: - self.cmap.append(QIcon(self.cmap[self.row_map_orig.index(k)])) + for c in self.user_categories: + l = [] + for (name,label,ign) in self.user_categories[c]: + if name in taglist[label]: # use same node as the complete category + l.append(taglist[label][name]) + # else: do nothing, to eliminate nodes that have zero counts + data[c+'*'] = sorted(l, cmp=(lambda x, y: cmp(x.name.lower(), y.name.lower()))) + self.row_map.append(c+'*') + self.categories.append(c) + self.cat_icon_map.append(self.usercat_icon) # Now the rest of the normal tag categories - for i in range(self.fixed_categories, len(self.row_map_orig)): + for i in range(self.tags_categories_start, len(self.row_map_orig)): self.row_map.append(self.row_map_orig[i]) self.categories.append(self.categories_orig[i]) - self.cmap.append(QIcon(I('tags.svg'))) - data['search'] = self.get_search_nodes() # Add the search category + self.cat_icon_map.append(self.cat_icon_map_orig[i]) + data['search'] = self.get_search_nodes(self.search_icon) # Add the search category self.row_map.append(self.search_keys[0]) self.categories.append(self.search_keys[1]) - self.cmap.append(QIcon(I('search.svg'))) + self.cat_icon_map.append(self.search_icon) return data - def get_search_nodes(self): + def get_search_nodes(self, icon): l = [] for i in saved_searches.names(): - l.append(Tag(i, tooltip=saved_searches.lookup(i))) + l.append(Tag(i, tooltip=saved_searches.lookup(i), icon=icon)) return l def refresh(self): @@ -302,7 +301,7 @@ class TagsModel(QAbstractItemModel): self.beginInsertRows(category_index, 0, len(data[r])-1) for tag in data[r]: tag.state = state_map.get(tag.name, 0) - t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_map) + t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_state_map) self.endInsertRows() def columnCount(self, parent): diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 1ce0843185..6794408ca0 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -15,6 +15,7 @@ from PyQt4.QtGui import QImage from calibre.utils.config import tweaks, prefs from calibre.utils.date import parse_date from calibre.utils.search_query_parser import SearchQueryParser +from calibre.utils.pyparsing import ParseException class CoverCache(QThread): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index f704eb68a6..55aa7520f7 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -58,12 +58,13 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile class Tag(object): - def __init__(self, name, id=None, count=0, state=0, tooltip=None): + def __init__(self, name, id=None, count=0, state=0, tooltip=None, icon=None): self.name = name self.id = id self.count = count self.state = state self.tooltip = tooltip + self.icon = icon def __unicode__(self): return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.tooltip) @@ -574,7 +575,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def get_recipe(self, id): return self.conn.get('SELECT script FROM feeds WHERE id=?', (id,), all=False) - def get_categories(self, sort_on_count=False, ids=None): + def get_categories(self, sort_on_count=False, ids=None, icon_map=None): orig_category_columns = {'tags': ['tag', 'name'], 'series': ['series', 'name'], @@ -647,11 +648,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): query += ' ORDER BY {0} ASC'.format(cn[1]) data = self.conn.get(query) category = cn[0] + icon = icon_map[category] if category in icon_map else icon_map['*custom'] if ids is None: # no filtering - categories[category] = [Tag(r[1], count=r[2], id=r[0]) + categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon) for r in data] else: # filter out zero-count tags - categories[category] = [Tag(r[1], count=r[2], id=r[0]) + categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon) for r in data if r[2] > 0] categories['format'] = [] for fmt in self.conn.get('SELECT DISTINCT format FROM data'): diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index fb9d3e90b0..e48e10d90f 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -24,6 +24,13 @@ class SafeLocalTimeZone(tzlocal): pass return False +def compute_locale_info_for_parse_date(): + dt = datetime.strptime('1/5/2000', "%x") + if dt.month == 5: + return True + return False + +parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() @@ -44,7 +51,7 @@ def parse_date(date_string, assume_utc=False, as_utc=True, default=None): func = datetime.utcnow if assume_utc else datetime.now default = func().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=_utc_tz if assume_utc else _local_tz) - dt = parse(date_string, default=default) + dt = parse(date_string, default=default, dayfirst=parse_date_day_first) if dt.tzinfo is None: dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz) return dt.astimezone(_utc_tz if as_utc else _local_tz)