mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
...
This commit is contained in:
parent
ed0366b343
commit
fb5b21d057
@ -971,7 +971,14 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
def sort(self, field, ascending, subsort=False):
|
def sort(self, field, ascending, subsort=False):
|
||||||
self.multisort([(field, ascending)])
|
self.multisort([(field, ascending)])
|
||||||
|
|
||||||
def multisort(self, fields=[], subsort=False):
|
def multisort(self, fields=[], subsort=False, only_ids=None):
|
||||||
|
'''
|
||||||
|
fields is a list of 2-tuple, each tuple is of the form
|
||||||
|
(field_name, is_ascending)
|
||||||
|
|
||||||
|
If only_ids is a list of ids, this function will sort that list instead
|
||||||
|
of the internal mapping of ids.
|
||||||
|
'''
|
||||||
fields = [(self.sanitize_sort_field_name(x), bool(y)) for x, y in fields]
|
fields = [(self.sanitize_sort_field_name(x), bool(y)) for x, y in fields]
|
||||||
keys = self.field_metadata.sortable_field_keys()
|
keys = self.field_metadata.sortable_field_keys()
|
||||||
fields = [x for x in fields if x[0] in keys]
|
fields = [x for x in fields if x[0] in keys]
|
||||||
@ -981,13 +988,15 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
fields = [('timestamp', False)]
|
fields = [('timestamp', False)]
|
||||||
|
|
||||||
keyg = SortKeyGenerator(fields, self.field_metadata, self._data, self.db_prefs)
|
keyg = SortKeyGenerator(fields, self.field_metadata, self._data, self.db_prefs)
|
||||||
self._map.sort(key=keyg)
|
if only_ids is None:
|
||||||
|
self._map.sort(key=keyg)
|
||||||
tmap = list(itertools.repeat(False, len(self._data)))
|
|
||||||
for x in self._map_filtered:
|
|
||||||
tmap[x] = True
|
|
||||||
self._map_filtered = [x for x in self._map if tmap[x]]
|
|
||||||
|
|
||||||
|
tmap = list(itertools.repeat(False, len(self._data)))
|
||||||
|
for x in self._map_filtered:
|
||||||
|
tmap[x] = True
|
||||||
|
self._map_filtered = [x for x in self._map if tmap[x]]
|
||||||
|
else:
|
||||||
|
only_ids.sort(key=keyg)
|
||||||
|
|
||||||
class SortKey(object):
|
class SortKey(object):
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
from __future__ import (unicode_literals, division, absolute_import,
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
print_function)
|
print_function)
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
@ -9,6 +10,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
@ -64,11 +66,19 @@ def category_icon(category, meta): # {{{
|
|||||||
return icon
|
return icon
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
def encode_name(name):
|
||||||
|
if isinstance(name, unicode):
|
||||||
|
name = name.encode('utf-8')
|
||||||
|
return hexlify(name)
|
||||||
|
|
||||||
|
def decode_name(name):
|
||||||
|
return unhexlify(name).decode('utf-8')
|
||||||
|
|
||||||
def absurl(prefix, url):
|
def absurl(prefix, url):
|
||||||
return prefix + url
|
return prefix + url
|
||||||
|
|
||||||
def category_url(prefix, cid):
|
def category_url(prefix, cid):
|
||||||
return absurl(prefix, '/ajax/category/'+cid)
|
return absurl(prefix, '/ajax/category/'+encode_name(cid))
|
||||||
|
|
||||||
def icon_url(prefix, name):
|
def icon_url(prefix, name):
|
||||||
return absurl(prefix, '/browse/icon/'+name)
|
return absurl(prefix, '/browse/icon/'+name)
|
||||||
@ -93,7 +103,7 @@ class AjaxServer(object):
|
|||||||
self.ajax_category)
|
self.ajax_category)
|
||||||
|
|
||||||
# List of books in specified category
|
# List of books in specified category
|
||||||
connect('ajax_books_in', base_href+'/books_in/{category}/{name}',
|
connect('ajax_books_in', base_href+'/books_in/{category}/{item}',
|
||||||
self.ajax_books_in)
|
self.ajax_books_in)
|
||||||
|
|
||||||
|
|
||||||
@ -190,7 +200,7 @@ class AjaxServer(object):
|
|||||||
if category.startswith('@'):
|
if category.startswith('@'):
|
||||||
category = category.partition('.')[0]
|
category = category.partition('.')[0]
|
||||||
display_name = category[1:]
|
display_name = category[1:]
|
||||||
url = force_unicode(category).encode('utf-8')
|
url = force_unicode(category)
|
||||||
icon = category_icon(category, meta)
|
icon = category_icon(category, meta)
|
||||||
ans[url] = (display_name, icon)
|
ans[url] = (display_name, icon)
|
||||||
|
|
||||||
@ -264,31 +274,33 @@ class AjaxServer(object):
|
|||||||
|
|
||||||
base_url = absurl(self.opts.url_prefix, '/ajax/category/'+name)
|
base_url = absurl(self.opts.url_prefix, '/ajax/category/'+name)
|
||||||
|
|
||||||
if name in ('newest', 'allbooks'):
|
|
||||||
if name == 'newest':
|
|
||||||
sort, sort_order = 'timestamp', 'desc'
|
|
||||||
raise cherrypy.InternalRedirect(
|
|
||||||
'/ajax/books_in/%s/dummy?num=%s&offset=%s&sort=%s&sort_order=%s'%(
|
|
||||||
name, num, offset, sort, sort_order))
|
|
||||||
|
|
||||||
if sort not in ('rating', 'name', 'popularity'):
|
if sort not in ('rating', 'name', 'popularity'):
|
||||||
sort = 'name'
|
sort = 'name'
|
||||||
|
|
||||||
if sort_order not in ('asc', 'desc'):
|
if sort_order not in ('asc', 'desc'):
|
||||||
sort_order = 'asc'
|
sort_order = 'asc'
|
||||||
|
|
||||||
|
try:
|
||||||
|
dname = decode_name(name)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'Invalid encoding of category name'
|
||||||
|
' %r'%name)
|
||||||
|
|
||||||
|
if dname in ('newest', 'allbooks'):
|
||||||
|
if dname == 'newest':
|
||||||
|
sort, sort_order = 'timestamp', 'desc'
|
||||||
|
raise cherrypy.InternalRedirect(
|
||||||
|
'/ajax/books_in/%s/%s?sort=%s&sort_order=%s'%(
|
||||||
|
encode_name(dname), encode_name('0'), sort, sort_order))
|
||||||
|
|
||||||
fm = self.db.field_metadata
|
fm = self.db.field_metadata
|
||||||
categories = self.categories_cache()
|
categories = self.categories_cache()
|
||||||
hierarchical_categories = self.db.prefs['categories_using_hierarchy']
|
hierarchical_categories = self.db.prefs['categories_using_hierarchy']
|
||||||
|
|
||||||
if name in categories:
|
subcategory = dname
|
||||||
toplevel = name
|
toplevel = subcategory.partition('.')[0]
|
||||||
|
if toplevel == subcategory:
|
||||||
subcategory = None
|
subcategory = None
|
||||||
else:
|
|
||||||
subcategory = name
|
|
||||||
toplevel = subcategory.partition('.')[0]
|
|
||||||
if toplevel == subcategory:
|
|
||||||
subcategory = None
|
|
||||||
if toplevel not in categories or toplevel not in fm:
|
if toplevel not in categories or toplevel not in fm:
|
||||||
raise cherrypy.HTTPError(404, 'Category %r not found'%toplevel)
|
raise cherrypy.HTTPError(404, 'Category %r not found'%toplevel)
|
||||||
|
|
||||||
@ -371,8 +383,8 @@ class AjaxServer(object):
|
|||||||
'average_rating': x.avg_rating,
|
'average_rating': x.avg_rating,
|
||||||
'count': x.count,
|
'count': x.count,
|
||||||
'url': absurl(self.opts.url_prefix, '/ajax/books_in/%s/%s'%(
|
'url': absurl(self.opts.url_prefix, '/ajax/books_in/%s/%s'%(
|
||||||
x.category if x.category else toplevel,
|
encode_name(x.category if x.category else toplevel),
|
||||||
x.original_name if x.id is None else x.id)),
|
encode_name(x.original_name if x.id is None else unicode(x.id)))),
|
||||||
'has_children': x.original_name in children,
|
'has_children': x.original_name in children,
|
||||||
} for x in items]
|
} for x in items]
|
||||||
|
|
||||||
@ -391,8 +403,61 @@ class AjaxServer(object):
|
|||||||
|
|
||||||
# Books in the specified category {{{
|
# Books in the specified category {{{
|
||||||
@Endpoint()
|
@Endpoint()
|
||||||
def ajax_books_in(self, name, item, sort='title', num=100, offset=0,
|
def ajax_books_in(self, category, item, sort='title', num=25, offset=0,
|
||||||
sort_order='asc'):
|
sort_order='asc'):
|
||||||
pass
|
try:
|
||||||
|
dname, ditem = map(decode_name, (category, item))
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'Invalid encoded param: %r'%category)
|
||||||
|
|
||||||
|
try:
|
||||||
|
num = int(num)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, "Invalid num: %r"%num)
|
||||||
|
try:
|
||||||
|
offset = int(offset)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, "Invalid offset: %r"%offset)
|
||||||
|
|
||||||
|
if sort_order not in ('asc', 'desc'):
|
||||||
|
sort_order = 'asc'
|
||||||
|
|
||||||
|
if dname in ('allbooks', 'newest'):
|
||||||
|
ids = self.search_cache('')
|
||||||
|
elif dname == 'search':
|
||||||
|
try:
|
||||||
|
ids = self.search_cache('search:"%s"'%ditem)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'Search: %r not understood'%ditem)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
cid = int(ditem)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404,
|
||||||
|
'Category id %r not an integer'%ditem)
|
||||||
|
|
||||||
|
if dname == 'news':
|
||||||
|
dname = 'tags'
|
||||||
|
ids = self.db.get_books_for_category(dname, cid)
|
||||||
|
all_ids = set(self.search_cache(''))
|
||||||
|
# Implement restriction
|
||||||
|
ids = ids.intersection(all_ids)
|
||||||
|
|
||||||
|
ids = list(ids)
|
||||||
|
sfield = self.db.data.sanitize_sort_field_name(sort)
|
||||||
|
if sfield not in self.db.field_metadata.sortable_field_keys():
|
||||||
|
raise cherrypy.HTTPError(404, '%s is not a valid sort field'%sort)
|
||||||
|
self.db.data.multisort(fields=[(sfield, sort_order == 'asc')], subsort=True,
|
||||||
|
only_ids=ids)
|
||||||
|
total_num = len(ids)
|
||||||
|
ids = ids[offset:offset+num]
|
||||||
|
return {
|
||||||
|
'total_num': total_num, 'sort_order':sort_order,
|
||||||
|
'offset':offset, 'num':len(ids), 'sort':sort,
|
||||||
|
'base_url':'/ajax/books_in/%s/%s'%(category, item),
|
||||||
|
'book_ids':ids
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user