From dc4e43371c4f36cc226b1d76a6888c1705e44f45 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 25 Jan 2013 14:37:57 +0530 Subject: [PATCH] Composite column based categories --- src/calibre/db/categories.py | 10 +++++++++- src/calibre/db/fields.py | 19 +++++++++++++++++++ src/calibre/db/tables.py | 8 ++++---- src/calibre/db/tests/metadata.db | Bin 237568 -> 245760 bytes 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/calibre/db/categories.py b/src/calibre/db/categories.py index 396bd954bd..6af9b35148 100644 --- a/src/calibre/db/categories.py +++ b/src/calibre/db/categories.py @@ -135,11 +135,19 @@ def get_categories(dbcache, sort='name', book_ids=None, icon_map=None): categories = {} book_ids = frozenset(book_ids) if book_ids else book_ids + get_metadata = partial(dbcache._get_metadata, get_user_categories=False) + bids = None + for category, is_multiple, is_composite in find_categories(fm): tag_class = create_tag_class(category, fm, icon_map) # TODO: Handle composite column based categories (both is_multiple and # not is_multiple) - if category == 'news': + if is_composite: + if bids is None: + bids = dbcache._all_book_ids() if book_ids is None else book_ids + cats = dbcache.fields[category].get_composite_categories( + tag_class, book_rating_map, bids, is_multiple, get_metadata) + elif category == 'news': cats = dbcache.fields['tags'].get_news_category(tag_class, book_ids) else: cats = dbcache.fields[category].get_categories( diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index 5816880576..bd3af5d518 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -186,6 +186,25 @@ class CompositeField(OneToOneField): for val, book_ids in val_map.iteritems(): yield val, book_ids + def get_composite_categories(self, tag_class, book_rating_map, book_ids, + is_multiple, get_metadata): + ans = [] + id_map = defaultdict(set) + for book_id in book_ids: + val = self.get_value_with_cache(book_id, get_metadata) + vals = [x.strip() for x in val.split(is_multiple)] if is_multiple else [val] + for val in vals: + if val: + id_map[val].add(book_id) + for item_id, item_book_ids in id_map.iteritems(): + ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for + book_id in item_book_ids) if r > 0) + avg = sum(ratings)/len(ratings) if ratings else 0 + c = tag_class(item_id, id=item_id, sort=item_id, avg=avg, + id_set=item_book_ids, count=len(item_book_ids)) + ans.append(c) + return ans + class OnDeviceField(OneToOneField): def __init__(self, name, table): diff --git a/src/calibre/db/tables.py b/src/calibre/db/tables.py index 4a8b4492fd..90f5db9ac5 100644 --- a/src/calibre/db/tables.py +++ b/src/calibre/db/tables.py @@ -98,10 +98,10 @@ class CompositeTable(OneToOneTable): self.book_col_map = {} d = self.metadata['display'] self.composite_template = ['composite_template'] - self.contains_html = d['contains_html'] - self.make_category = d['make_category'] - self.composite_sort = d['composite_sort'] - self.use_decorations = d['use_decorations'] + self.contains_html = d.get('contains_html', False) + self.make_category = d.get('make_category', False) + self.composite_sort = d.get('composite_sort', False) + self.use_decorations = d.get('use_decorations', False) class ManyToOneTable(Table): diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db index 70237949d808826370c2e912ebff673348714b84..88a76ac41091f0ca305d89add74c4f6ac9138a60 100644 GIT binary patch delta 1688 zcmaJ?drVVj6z})_S{|hWok75Yl!^j^rmZ?fK@nU)q`r74ib8=3Xt4!~YfyyFMKd=I z-35-XO=mKr+fb7x+%4NOqGn{tRJN(vLlNhe#Z9MX`%9N?->p-ZnfWF6=H@(q=l7j+ zZr5OBS65WGK_5`5RCmSm==A$DrwifK?^88Gw34SNiaUPyJ8C|Krv&u6a29^sS1N=* z_?89C{6aj_9Z9H+RIC{uDAqsYK_Jq58)L=y%?Pb{>(Hsf75s+IfNpCuBF7+!)w-J8 zRUUhNt1hs+b6f71ySR_vIp@W^MRh|e9!!+a?87-m0|!uqiM|wR>FLQKlQpV>UYnFS zlo;51+Rj);;QpBfVDC!VLyOpZ(vBe$J03VbwC14p*(rDKu4nS@dcOH0`;z@CVfZOhApp#|TO!@QiG3BljKy}4mI}f5D z>M^;(%VIXmdCQ>Yk^h!jr&G=fT%z6*xM`~knex3Qh+-7miShD;QXCD&{8~O+t{}=@ z2rZdA5HF{$f`_qLl)MN=`J)PiF$MWI^4Zmj-Doi)X*dVT^7Kmhd5BPb+hnKQwU|iN zIndFI#VDklDx8se+x2oxHBJWed`E0O4Dz)aBr~cmM;N`aMNoRU6ec;V4zC7{u26KN zAm&1^;8|UdKWNm3Q0iPQ!n$9;#)tS0G$5Uaio92iMiYJBg#`J=1|%}Asl;J#scmqy znn<4u3(a?8F%9<#=g%aOYMF#nbax>%iegHTTAJH~b+pTg5v9ZtxuFSrxvX}hE?Dz5 zxuIDIh$KOt*@M|Z=_u)YF-`7k!v;>#eo@JY3rTW@7db)wkF@-CjH7E60{WaBQO4cxZ+ZImGT&0lup*6IbMPFk-Gi~@KE_1pRz%7D zuVE#akxq3WM&8zempHxYMFTmzkwqs9kW7n95Kb4mu}`7p3%jS4{9e(tkBD1DhqFZ= zjrvi+v=blqh+;H}Mw+2zR(h)j^TPAR#JXA=Jq|iAA(rN~Ac5ZQMsmnQC$jecx!Ut#gZ&28)q5PHATiJEvmj*iEkC6EL96yi_!pq)hx?CfNoPp3MG| zFfPWBC-QvK9fVP=8JtL!R~F(H6I&r&Y=x9Ci!Vd$g}#h`_Ch72=0ljJT-v20UbR+9 z$ly@H5zwctL5eR<7okc@Ph!kh#h&b;$K@GI?e)?f?E0{cpzwEnE@E|7V_J>NwNYt{(qLOe{}%(X zP#*K7Ma2DG@8+UIcJa7ilx}XI^M5is-RWl*DK{&cj3-!;Jp2=%p$=g@NyU=_mbe%m zv3;$tEYy%cmF*~@X_r~@|704Rjd2f?nQ47J$-c}GRr*WE M$(XqhBU|iZ|9dRfhE`FrTiebThu;ExU+?Z0KDcFd|_Qzb3N!yP>O=oW#iBfGhJ0+{SOxhw^?oD1EP6Ej#FP425~? zp-83H9#7cY`go7m6KrYo_Vhi&Z^m$$!LDr}^#Zn-Wvw7hsy$n$7tI#&_6ulYCh^h@ z&6c=5Qw=t*J7@Q>*Yq>9HuiGj;_R`w`9D=WH{xS_#3zRr*aCgz#8!S{4_=Zf;zpVh z@Pyi^+lep@?Z+iW+&xdP?!soOKZZK~_HMXkT0Mm|Jo8~xF@yh%G(WcR&IrPw?dc*B4|D5Pl14enZ8&fiUx(^nf(}?oaHQ$h@ z8+P9EC_;=@Yf!>(9fg_EU;W7DxlJfbsjgB<2>Eoyi4EL;98sA}XHm%2W~eFtTki50 zb&frWbb2p@^}M(hB`N9}X^ps-hrAe;>Cbc6N>{2;Ks9an!)X2@MLi(?bvsIBi1yH2 zL|km|L{TbxiCiafhHQQ;b0vTmWg};q$Af5P^l>k8d434brlLvG2I1f{VeDk|gb#P~ z%BRthqE=|?G#r%dKpwx^3kRcJdyz#e5tzxp7iHAk2=_$1A1U>9J=&Yk69cGYTp1H@ zPDWs{$g)F>`u)m~TRTX1Rbm_YAH+I3K8jolyvfpOuu^pF-C-O7MGuH(1%~hlDLNjY z=g*08dQ8W->