From dc0f280cae9905d2d118852953da41f12e7a4982 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 25 Jan 2013 13:52:35 +0530 Subject: [PATCH] Saved search categories --- src/calibre/db/categories.py | 103 +++++++++++++++++-------------- src/calibre/db/search.py | 1 - src/calibre/db/tests/metadata.db | Bin 237568 -> 237568 bytes 3 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/calibre/db/categories.py b/src/calibre/db/categories.py index 606688f422..396bd954bd 100644 --- a/src/calibre/db/categories.py +++ b/src/calibre/db/categories.py @@ -15,6 +15,7 @@ from future_builtins import map from calibre.library.field_metadata import TagsIcons from calibre.utils.config_base import tweaks from calibre.utils.icu import sort_key +from calibre.utils.search_query_parser import saved_searches CATEGORY_SORTS = ('name', 'popularity', 'rating') # This has to be a tuple not a set @@ -158,55 +159,67 @@ def get_categories(dbcache, sort='name', book_ids=None, icon_map=None): # User categories user_categories = clean_user_categories(dbcache).copy() - # We want to use same node in the user category as in the source - # category. To do that, we need to find the original Tag node. 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 - # temporarily duplicating the categories lists. - taglist = {} - for c, items in categories.iteritems(): - taglist[c] = dict(map(lambda t:(icu_lower(t.name), t), items)) + if user_categories: + # We want to use same node in the user category as in the source + # category. To do that, we need to find the original Tag node. 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 + # temporarily duplicating the categories lists. + taglist = {} + for c, items in categories.iteritems(): + taglist[c] = dict(map(lambda t:(icu_lower(t.name), t), items)) - muc = dbcache.pref('grouped_search_make_user_categories', []) - gst = dbcache.pref('grouped_search_terms', {}) - for c in gst: - if c not in muc: - continue - user_categories[c] = [] - for sc in gst[c]: - if sc in categories.keys(): - for t in categories[sc]: - user_categories[c].append([t.name, sc, 0]) + muc = dbcache.pref('grouped_search_make_user_categories', []) + gst = dbcache.pref('grouped_search_terms', {}) + for c in gst: + if c not in muc: + continue + user_categories[c] = [] + for sc in gst[c]: + if sc in categories.keys(): + for t in categories[sc]: + user_categories[c].append([t.name, sc, 0]) - gst_icon = icon_map['gst'] if icon_map else None - for user_cat in sorted(user_categories.iterkeys(), key=sort_key): - items = [] - names_seen = {} - for name, label, ign in user_categories[user_cat]: - n = icu_lower(name) - if label in taglist and n in taglist[label]: - if user_cat in gst: - # for gst items, make copy and consolidate the tags by name. - if n in names_seen: - t = names_seen[n] - t.id_set |= taglist[label][n].id_set - t.count += taglist[label][n].count - t.tooltip = t.tooltip.replace(')', ', ' + label + ')') + gst_icon = icon_map['gst'] if icon_map else None + for user_cat in sorted(user_categories.iterkeys(), key=sort_key): + items = [] + names_seen = {} + for name, label, ign in user_categories[user_cat]: + n = icu_lower(name) + if label in taglist and n in taglist[label]: + if user_cat in gst: + # for gst items, make copy and consolidate the tags by name. + if n in names_seen: + t = names_seen[n] + t.id_set |= taglist[label][n].id_set + t.count += taglist[label][n].count + t.tooltip = t.tooltip.replace(')', ', ' + label + ')') + else: + t = copy.copy(taglist[label][n]) + t.icon = gst_icon + names_seen[t.name] = t + items.append(t) else: - t = copy.copy(taglist[label][n]) - t.icon = gst_icon - names_seen[t.name] = t - items.append(t) - else: - items.append(taglist[label][n]) - # else: do nothing, to not include nodes w zero counts - cat_name = '@' + user_cat # add the '@' to avoid name collision - # Not a problem if we accumulate entries in the icon map - if icon_map is not None: - icon_map[cat_name] = icon_map['user:'] - categories[cat_name] = sort_categories(items, sort) + items.append(taglist[label][n]) + # else: do nothing, to not include nodes w zero counts + cat_name = '@' + user_cat # add the '@' to avoid name collision + # Not a problem if we accumulate entries in the icon map + if icon_map is not None: + icon_map[cat_name] = icon_map['user:'] + categories[cat_name] = sort_categories(items, sort) - # TODO: saved searches + #### Finally, the saved searches category #### + items = [] + icon = None + if icon_map and 'search' in icon_map: + icon = icon_map['search'] + ss = saved_searches() + for srch in ss.names(): + items.append(Tag(srch, tooltip=ss.lookup(srch), + sort=srch, icon=icon, category='search', + is_editable=False)) + if len(items): + categories['search'] = items return categories diff --git a/src/calibre/db/search.py b/src/calibre/db/search.py index 7eb747da67..57039e191d 100644 --- a/src/calibre/db/search.py +++ b/src/calibre/db/search.py @@ -17,7 +17,6 @@ from calibre.utils.icu import primary_find from calibre.utils.localization import lang_map, canonicalize_lang from calibre.utils.search_query_parser import SearchQueryParser, ParseException -# TODO: Thread safety of saved searches CONTAINS_MATCH = 0 EQUALS_MATCH = 1 REGEXP_MATCH = 2 diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db index 1a14791592919dcff2176473866e1f2cbe94c82b..70237949d808826370c2e912ebff673348714b84 100644 GIT binary patch delta 889 zcmY+DT}V_x6vt=oUl*0lQVTS*u6N6_3?;Nc+DiJS#LS8{r1iRcTQ_vKcJC?`mA9av zA~qH~5m@x0(MwPZ-luXia9@J7v=2Y3m%fPTMJRe|u4d3MzhUP5&l!eu=5!=7I}(}E z{8`Ma26x+tYsXryri<@);yH*fYu&%>p^#u<_;eX zcr>qyer7-D*IK;pRxJm`Qdn)%yza23hCIz$nBBB&GrF#NRb6GbV`r1f!W+4$;J#2W za`>OWTh~IZ;S}+roHGN-a|20k4kRU~&WFixp~x(%#i&Flw_`nR+=)k);~*KQaL7Eg z7gr>z^I;J=c45L&hUl^r3&>rEbhEVs5zzHLXeyf}B+hm-F+Yi|#Ye$=!8^f(U|cZv zmvJgru=+*N9Az&x35}EgEqT7N-RRUaigVnLbgw2N(J4p6w!bwF^fmvI!Tg z*f(TE;V>PWF(FYj0voTOS|19jWCMmRhqp9z6s2Zq6^11GxejxwZy%PBb1j})@@S7! zTv^tM*_7kOcgr+JRhv+5mTPz>K`xRh8bm%R&3I?2<5cSendFCGqP}|UAy*4l*`{kt zH8E7p?4N8yv0X$=D0S0?r$QfcD7qOk`E@L(3pGfm^c~1G&$r;%ifkLd%lKXXl)vY{ z5Jj&Pie=f?#H$ngd8|6IAbu+GobyHE*E(EVjlvXG-2VT+Ixgy=R81vct_f&LrL3q^ zr*LI3pvh{$D+hwQJY&<_gG$DUsnYWwjQ>C{dMUFJgQ<*35A$hdmq?@Dg>UH>Qw>G5 V3DsF`VqU!lFQ=47oU!sT@&_Vy5yAif delta 818 zcmZ{iT}V@57{}jp{^r)4;w(uMWezjN3#|w#TnQQG$JG2tQ)8RkX?{-IX8D2W5;QbH z8Tu3!26od;PzL9sz+vDny3y1=G|etjyQ=OYf_itvi-HdC@4Scq`@GNpd7tz4`6K%L z5uf&O44UmXaxqJh42S)e88@1-8Sd$(cz18BtLCiszC;HTziZxXBkEh@ZJL zS1TknTb+(}E05gvYBVTiYQ!>pfeViii!97x04p=P6al{2fT3V08tH;ly5Tl^TFrHm z+1=^1N%r7&{nZAk*=}x;JXX8aW9{KmVsk^C%j)VhUq66U@$Ypu>>CIe5^lxdq~LJ1 zwzo-kvs<#dZ1rZ3sC4_c(}jVMt>YOlmFW#vFa89j)V zk5*w_iFr*S#rQ>sdU04zD94r}E>U7NvMJ{*UQl)w)Uvh^Ka`ba`jn3ZGMvK({jNYV zS*oFuk7Z#=RCC7_SCeF?u?~zVHuN|brS!Z4jj}Tv6H10R69wF_FCR(N*1=O}YPG94c6~?K{?bqMP|cD-&&9-4>hAJElR~qjE@G^%O=?~u~2mRblS`rlVAAH)r{G_ znhuFuzYpgxK`3HE5mw1Ng>I#I;>G8OF(y7$g|gI}chl(#??RaOw&V@l6=W_