Start work on UI for new server

This commit is contained in:
Kovid Goyal 2015-10-15 13:58:19 +05:30
parent c8ad7b203a
commit 0bbc30c07f
7 changed files with 110 additions and 25 deletions

Binary file not shown.

View File

@ -7,6 +7,8 @@ __license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
from functools import partial
from future_builtins import zip
from itertools import cycle
from calibre import force_unicode
from calibre.library.field_metadata import category_icon_map
@ -15,6 +17,7 @@ from calibre.ebooks.metadata import title_sort
from calibre.ebooks.metadata.book.json_codec import JsonCodec
from calibre.srv.errors import HTTPNotFound
from calibre.srv.routes import endpoint, json
from calibre.srv.session import defaults
from calibre.srv.content import get as get_content, icon as get_icon
from calibre.srv.utils import http_date, custom_fields_to_display, encode_name, decode_name
from calibre.utils.config import prefs, tweaks
@ -520,29 +523,20 @@ def books_in(ctx, rd, encoded_category, encoded_item, library_id):
return result
# }}}
@endpoint('/ajax/search/{library_id=None}', postprocess=json)
def search(ctx, rd, library_id):
'''
Return the books (as list of ids) matching the specified search query.
Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
'''
db = get_db(ctx, library_id)
with db.safe_read_lock:
query = rd.query.get('query')
num, offset = get_pagination(rd.query)
sort, sort_order = rd.query.get('sort', 'title'), rd.query.get('sort_order')
sort_order = ensure_val(sort_order, 'asc', 'desc')
sfield = sanitize_sort_field_name(db.field_metadata, sort)
if sfield not in db.field_metadata.sortable_field_keys():
# Search {{{
def _search(ctx, rd, db, query, num, offset, sort, sort_order):
multisort = [(sanitize_sort_field_name(db.field_metadata, s), ensure_val(o, 'asc', 'desc'))
for s, o in zip(sort.split(','), cycle(sort_order.split(',')))]
skeys = db.field_metadata.sortable_field_keys()
for sfield, sorder in multisort:
if sfield not in skeys:
raise HTTPNotFound('%s is not a valid sort field'%sort)
if not query:
ids = ctx.allowed_book_ids(rd, db)
else:
ids = ctx.search(rd, db, query)
ids = db.multisort(fields=[(sfield, sort_order == 'asc')], ids_to_sort=ids)
ids = db.multisort(fields=multisort, ids_to_sort=ids)
total_num = len(ids)
ids = ids[offset:offset+num]
return {
@ -552,3 +546,52 @@ def search(ctx, rd, library_id):
'query': query,
'book_ids':ids
}
@endpoint('/ajax/search/{library_id=None}', postprocess=json)
def search(ctx, rd, library_id):
'''
Return the books (as list of ids) matching the specified search query.
Optional: ?num=100&offset=0&sort=title&sort_order=asc&query=
'''
db = get_db(ctx, library_id)
query = rd.query.get('query')
num, offset = get_pagination(rd.query)
with db.safe_read_lock:
return _search(ctx, rd, db, query, num, offset, rd.query.get('sort', 'title'), rd.query.get('sort_order', 'asc'))
# }}}
@endpoint('/ajax/interface-data/{library_id=None}', postprocess=json)
def interface_data(ctx, rd, library_id):
'''
Return the data needed to create the server main UI
Optional: ?num=75
'''
session = rd.session
ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
sorts, orders = [], []
for x in ans['session_data']['sort'].split(','):
s, o = x.partition(':')[::2]
sorts.append(s.strip()), orders.append(o.strip())
sort, sort_order = ans['session_data']['sort'].partition(',')[0].partition(':')[::2]
try:
num = int(rd.query.get('num', 75))
except Exception:
raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
last_modified = None
db = get_db(ctx, library_id)
with db.safe_read_lock:
ans['search_result'] = _search(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
ans['field_metadata'] = db.field_metadata
# ans['categories'] = ctx.get_categories(rd, db)
mdata = ans['metadata'] = {}
for book_id in ans['search_result']['book_ids']:
data, lm = book_to_json(ctx, rd, db, book_id)
last_modified = lm if last_modified is None else max(lm, last_modified)
mdata[book_id] = data
if last_modified is not None:
rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
return ans

View File

@ -92,8 +92,8 @@ class Context(object):
return self.library_broker.get(library_id)
def allowed_book_ids(self, data, db):
# TODO: Implement this based on data.username caching result on the
# data object
# TODO: Implement this based on data.username for per-user
# restrictions. Cache result on the data object
with self.lock:
ans = data.allowed_book_ids.get(db.server_library_id)
if ans is None:

View File

@ -11,8 +11,9 @@ from threading import Lock
from calibre.utils.lru_cache import lru_cache
defaults = {
'sort': 'date:asc',
'sort': 'date:desc',
'library_id': None,
'view_mode': 'cover_grid',
}
class Session(object):

14
src/pyj/ajax.pyj Normal file
View File

@ -0,0 +1,14 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
def ajax(path, on_complete, on_progress=None, bypass_cache=True):
xhr = XMLHttpRequest()
if bypass_cache:
path += ('&' if '?' in path else '?') + Date().getTime()
if on_progress:
xhr.addEventListener('progress', on_progress)
xhr.addEventListener('abort', def(ev): on_complete('abort', this, xhr);)
xhr.addEventListener('error', def(ev): on_complete('error', this, xhr);)
xhr.addEventListener('load', def(ev): on_complete('load', this, xhr);)
return xhr

17
src/pyj/dom.pyj Normal file
View File

@ -0,0 +1,17 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
simple_vendor_prefixes = {
'transform': v"['webkit', 'ms']",
}
def set_css(elem, **kw):
s = elem.style
if s:
for prop in kw:
name, val = str.replace(prop, '_', '-'), kw[prop]
s.setProperty(name, val)
prefixes = simple_vendor_prefixes[name]
if prefixes:
for prefix in prefixes:
s.setProperty('-' + prefix + '-' + name, val)

View File

@ -1,5 +1,15 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
alert('hello world!')
from elementmaker import E
def create_page_load_progress_bar():
E.progress(id='library_load_bar')
def on_document_loaded():
alert(11111111)
# We wait for all page elements to load, since this is a single page app
# with a largely empty starting document, we can use this to preload any resources
# we know are going to be needed immediately.
window.addEventListener("load", on_document_loaded)