Implement changing sort order

This commit is contained in:
Kovid Goyal 2015-11-18 08:54:26 +05:30
parent 50f058ed86
commit bae7f40165
4 changed files with 105 additions and 9 deletions

View File

@ -16,6 +16,7 @@ from calibre.srv.errors import HTTPNotFound, HTTPBadRequest
from calibre.srv.metadata import book_as_json from calibre.srv.metadata import book_as_json
from calibre.srv.routes import endpoint, json from calibre.srv.routes import endpoint, json
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.utils.search_query_parser import ParseException
html_cache = {} html_cache = {}
cache_lock = Lock() cache_lock = Lock()
@ -67,7 +68,9 @@ def get_basic_query_data(ctx, query):
sorts, orders = [], [] sorts, orders = [], []
for x in query.get('sort', '').split(','): for x in query.get('sort', '').split(','):
if x: if x:
s, o = x.partition('.')[::2] s, o = x.rpartition('.')[::2]
if o and not s:
s, o = o, ''
if o not in ('asc', 'desc'): if o not in ('asc', 'desc'):
o = 'asc' o = 'asc'
if s.startswith('_'): if s.startswith('_'):
@ -170,3 +173,29 @@ def set_session_data(ctx, rd):
ud = ctx.user_manager.get_session_data(rd.username) ud = ctx.user_manager.get_session_data(rd.username)
ud.update(new_data) ud.update(new_data)
ctx.user_manager.set_session_data(rd.username, ud) ctx.user_manager.set_session_data(rd.username, ud)
@endpoint('/interface-data/get-books', postprocess=json)
def get_books(ctx, rd):
'''
Get books for the specified query
Optional: ?library_id=<default library>&num=50&sort=timestamp.desc&search=''
'''
library_id, db, sorts, orders = get_basic_query_data(ctx, rd.query)
try:
num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
except Exception:
raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
searchq = rd.query.get('search', '')
db = get_library_data(ctx, rd.query)[0]
ans = {}
mdata = ans['metadata'] = {}
with db.safe_read_lock:
try:
ans['search_result'] = search_result(ctx, rd, db, searchq, num, 0, ','.join(sorts), ','.join(orders))
except ParseException as err:
raise HTTPBadRequest('Invalid search expression: %s' % as_unicode(err))
for book_id in ans['search_result']['book_ids']:
data = book_as_json(db, book_id)
mdata[book_id] = data
return ans

View File

@ -4,6 +4,7 @@
from book_list.ui import UI from book_list.ui import UI
from modals import error_dialog from modals import error_dialog
from gettext import gettext as _ from gettext import gettext as _
from book_list.globals import get_session_data
class Boss: class Boss:
@ -29,3 +30,14 @@ class Boss:
return True return True
except: except:
console.error('There was an error in the unhandled exception handler') console.error('There was an error in the unhandled exception handler')
def change_books(self, data):
data.search_result.sort = str.split(data.search_result.sort, ',')[:2].join(',')
data.search_result.sort_order = str.split(data.search_result.sort_order, ',')[:2].join(',')
sval = ''
for field, order in zip(str.split(data.search_result.sort, ','), str.split(data.search_result.sort_order, ',')):
sval += field + '.' + order + ','
get_session_data().set('sort', str.rstrip(sval, ','))
self.interface_data.metadata = data.metadata
self.interface_data.search_result = data.search_result
self.ui.books_view.refresh()

View File

@ -30,10 +30,11 @@ class ClosePanelBar(BarState):
class UIState: class UIState:
def __init__(self, top_bar_state=None, main_panel=None, panel_data=None): def __init__(self, top_bar_state=None, main_panel=None, panel_data=None, is_cacheable=True):
self.top_bar_state = top_bar_state self.top_bar_state = top_bar_state
self.main_panel = main_panel or get_boss().ui.items_view self.main_panel = main_panel or get_boss().ui.items_view
self.panel_data = panel_data self.panel_data = panel_data
self.is_cacheable = is_cacheable
def add_button(self, **kw): def add_button(self, **kw):
self.top_bar_state.add_button(**kw) self.top_bar_state.add_button(**kw)
@ -43,7 +44,9 @@ panels = {}
def panel(key): def panel(key):
ans = panels[key] ans = panels[key]
if not ans: if not ans:
ans = panels[key] = create_panel[key]() ans = create_panel[key]()
if ans.is_cacheable:
panels[key] = ans
return ans return ans
def close_panel(): def close_panel():
@ -65,8 +68,12 @@ create_panel = {
, ,
'booklist-mode-menu': def booklist_mode_menu(): 'booklist-mode-menu': def booklist_mode_menu():
return UIState(ClosePanelBar(_('Book List Mode')), panel_data=[ return UIState(ClosePanelBar(_('Book List Mode')), panel_data=[])
]) ,
'booklist-sort-menu': def change_booklist_sort():
data = get_boss().ui.books_view.sort_panel_data(create_item)
return UIState(ClosePanelBar(_('Sort books')), panel_data=data, is_cacheable=False)
, ,
} }
@ -82,7 +89,7 @@ class UI:
self.books_view = BooksView(interface_data) self.books_view = BooksView(interface_data)
self.items_view = ItemsView(interface_data) self.items_view = ItemsView(interface_data)
ibs = BarState(run_animation=True) ibs = BarState(run_animation=True)
ibs.add_button(icon_name='sort-alpha-asc', tooltip=_('Sort books')) ibs.add_button(icon_name='sort-amount-desc', tooltip=_('Sort books'), action=show_panel_action('booklist-sort-menu'))
ibs.add_button(icon_name='search', tooltip=_('Search for books')) ibs.add_button(icon_name='search', tooltip=_('Search for books'))
ibs.add_button(icon_name='ellipsis-v', tooltip=_('More actions'), action=show_panel_action('more-actions-menu')) ibs.add_button(icon_name='ellipsis-v', tooltip=_('More actions'), action=show_panel_action('more-actions-menu'))
self.states.append(UIState(ibs, self.books_view)) self.states.append(UIState(ibs, self.books_view))

View File

@ -5,9 +5,9 @@ from ajax import ajax_send
from dom import set_css, build_rule, clear from dom import set_css, build_rule, clear
from elementmaker import E from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
from modals import error_dialog from modals import error_dialog, ajax_progress_dialog
from book_list.globals import get_session_data from book_list.globals import get_session_data, get_boss
from widgets import create_button, create_spinner from widgets import create_button, create_spinner
THUMBNAIL_MAX_WIDTH = 300 THUMBNAIL_MAX_WIDTH = 300
@ -148,7 +148,7 @@ class BooksView:
except Exception as err: except Exception as err:
error_dialog(_('Could not get more books'), _('Server returned an invalid response'), err.stack or err.toString()) error_dialog(_('Could not get more books'), _('Server returned an invalid response'), err.stack or err.toString())
elif end_type != 'abort': elif end_type != 'abort':
error_dialog(_('Could not get more books'), xhr.error_string) error_dialog(_('Could not get more books'), xhr.error_html)
# Cover grid {{{ # Cover grid {{{
@ -186,3 +186,51 @@ class BooksView:
) )
# }}} # }}}
def sort_panel_data(self, create_item):
current_sorted_field = str.partition(self.interface_data.search_result.sort, ',')[0]
current_sorted_field_order = str.partition(self.interface_data.search_result.sort_order, ',')[0]
new_sort_order = 'desc' if current_sorted_field_order == 'asc' else 'asc'
if current_sorted_field == 'date':
current_sorted_field = 'timestamp'
ans = []
ans.subtitle = _('Change how the list of books is sorted')
for field, name in self.interface_data.sortable_fields:
subtitle = icon_name = None
if field == current_sorted_field:
subtitle = _('Reverse current sort order')
icon_name = 'sort-amount-asc' if current_sorted_field_order == 'asc' else 'sort-amount-desc'
action = self.change_sort.bind(self, field, new_sort_order)
else:
action = self.change_sort.bind(self, field, None)
ans.push(create_item(name, subtitle=subtitle, icon_name=icon_name, action=action))
return ans
def change_sort(self, field, order):
key = 'sort-order-for-' + field
sd = get_session_data()
order = order or sd.get(key, 'asc')
order = 'asc' if order == 'asc' else 'desc'
sd.set(key, order)
sr = self.interface_data.search_result
sort = field + '.' + order + ',' + sr.sort + '.' + sr.order
data = {'search':sr.query or '', 'sort':sort, 'num':self.shown_book_ids.length, 'library_id':self.interface_data.library_id}
ajax_progress_dialog('interface-data/get-books', self.sort_change_completed.bind(self), _(
'Fetching data from server, please wait') + '…', query=data)
def sort_change_completed(self, end_type, xhr, ev):
if end_type == 'load':
boss = get_boss()
try:
data = JSON.parse(xhr.responseText)
boss.change_books(data)
except Exception as err:
return error_dialog(_('Could not change sort order'), err + '', details=err.stack)
boss.ui.close_panel()
window.scrollTo(0, 0)
elif end_type != 'abort':
error_dialog(_('Could not change sort order'), xhr.error_html)
def refresh(self):
self.clear()
self.render_ids()