Esempio n. 1
0
def search_result(ctx, rd, db, query, num, offset, sort, sort_order):
    multisort = [
        (sanitize_sort_field_name(db.field_metadata,
                                  s), ensure_val(o, 'asc', 'desc') == 'asc')
        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=multisort, ids_to_sort=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': ctx.url_for(search, library_id=db.server_library_id),
        'query': query,
        'library_id': db.server_library_id,
        'book_ids': ids
    }
Esempio n. 2
0
def search_result(ctx, rd, db, query, num, offset, sort, sort_order):
    multisort = [
        (sanitize_sort_field_name(db.field_metadata, s), ensure_val(o, "asc", "desc") == "asc")
        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=multisort, ids_to_sort=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": ctx.url_for(search, library_id=db.server_library_id),
        "query": query,
        "book_ids": ids,
    }
Esempio n. 3
0
def search_result(ctx, rd, db, query, num, offset, sort, sort_order, vl=''):
    multisort = [
        (sanitize_sort_field_name(db.field_metadata,
                                  s), ensure_val(o, 'asc', 'desc') == 'asc')
        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)

    ids, parse_error = ctx.search(rd,
                                  db,
                                  query,
                                  vl=vl,
                                  report_restriction_errors=True)
    ids = db.multisort(fields=multisort, ids_to_sort=ids)
    total_num = len(ids)
    ids = ids[offset:offset + num]
    ans = {
        'total_num': total_num,
        'sort_order': sort_order,
        'offset': offset,
        'num': len(ids),
        'sort': sort,
        'base_url': ctx.url_for(search, library_id=db.server_library_id),
        'query': query,
        'library_id': db.server_library_id,
        'book_ids': ids,
        'vl': vl,
    }
    if parse_error is not None:
        ans['bad_restriction'] = unicode_type(parse_error)
    return ans
Esempio n. 4
0
def mobile(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    try:
        start = max(1, int(rd.query.get('start', 1)))
    except ValueError:
        raise HTTPBadRequest('start is not an integer')
    try:
        num = max(0, int(rd.query.get('num', 25)))
    except ValueError:
        raise HTTPBadRequest('num is not an integer')
    search = rd.query.get('search') or ''
    with db.safe_read_lock:
        book_ids = ctx.search(rd, db, search)
        total = len(book_ids)
        ascending = rd.query.get('order', '').lower().strip() == 'ascending'
        sort_by = sanitize_sort_field_name(db.field_metadata, rd.query.get('sort') or 'date')
        try:
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        except Exception:
            sort_by = 'date'
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        books = [db.get_metadata(book_id) for book_id in book_ids[(start-1):(start-1)+num]]
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(db.last_modified()))
    order = 'ascending' if ascending else 'descending'
    q = {b'search':search.encode('utf-8'), b'order':bytes(order), b'sort':sort_by.encode('utf-8'), b'num':bytes(num), 'library_id':library_id}
    url_base = ctx.url_for('/mobile') + '?' + urlencode(q)
    lm = {k:v for k, v in library_map.iteritems() if k != library_id}
    return build_index(books, num, search, sort_by, order, start, total, url_base, db.field_metadata, ctx, lm, library_id)
Esempio n. 5
0
def get_library_init_data(ctx, rd, db, num, sorts, orders, vl):
    ans = {}
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db,
                                                 rd.query.get('search', ''),
                                                 num, 0, ','.join(sorts),
                                                 ','.join(orders), vl)
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0,
                                                 ','.join(sorts),
                                                 ','.join(orders), vl)
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(
            ((sanitize_sort_field_name(db.field_metadata, k), v)
             for k, v in sf.iteritems()),
            key=lambda field_name: sort_key(field_name[1]))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['virtual_libraries'] = db._pref('virtual_libraries', {})
        ans['book_display_fields'] = get_field_list(db)
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(
                int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data
    return ans
Esempio n. 6
0
def mobile(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    try:
        start = max(1, int(rd.query.get('start', 1)))
    except ValueError:
        raise HTTPBadRequest('start is not an integer')
    try:
        num = max(0, int(rd.query.get('num', 25)))
    except ValueError:
        raise HTTPBadRequest('num is not an integer')
    search = rd.query.get('search') or ''
    with db.safe_read_lock:
        book_ids = ctx.search(rd, db, search)
        total = len(book_ids)
        ascending = rd.query.get('order', '').lower().strip() == 'ascending'
        sort_by = sanitize_sort_field_name(db.field_metadata, rd.query.get('sort') or 'date')
        try:
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        except Exception:
            sort_by = 'date'
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        books = [db.get_metadata(book_id) for book_id in book_ids[(start-1):(start-1)+num]]
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(db.last_modified()))
    order = 'ascending' if ascending else 'descending'
    q = {b'search':search.encode('utf-8'), b'order':bytes(order), b'sort':sort_by.encode('utf-8'), b'num':bytes(num), 'library_id':library_id}
    url_base = ctx.url_for('/mobile') + '?' + urlencode(q)
    lm = {k:v for k, v in iteritems(library_map) if k != library_id}
    return build_index(rd, books, num, search, sort_by, order, start, total, url_base, db.field_metadata, ctx, lm, library_id)
Esempio n. 7
0
def get_library_init_data(ctx, rd, db, num, sorts, orders):
    ans = {}
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data
    return ans
Esempio n. 8
0
def interface_data(ctx, rd):
    '''
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &search=''&extra_books=''
    '''
    ans = {
        'username':rd.username,
        'output_format':prefs['output_format'].upper(),
        'input_formats':{x.upper():True for x in available_input_formats()},
        'gui_pubdate_display_format':tweaks['gui_pubdate_display_format'],
        'gui_timestamp_display_format':tweaks['gui_timestamp_display_format'],
        'gui_last_modified_display_format':tweaks['gui_last_modified_display_format'],
        'use_roman_numerals_for_series_number': get_use_roman(),
    }
    ans['library_map'], ans['default_library'] = ctx.library_info(rd)
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get('library_id')
        if lid and lid in ans['library_map']:
            rd.query.set('library_id', lid)
        usort = ud.get('sort')
        if usort:
            rd.query.set('sort', usort)
    ans['library_id'], db, sorts, orders = get_basic_query_data(ctx, rd)
    ans['user_session_data'] = ud
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['icon_map'] = icon_map()
        ans['icon_path'] = ctx.url_for('/icon', which='')
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data

    return ans
Esempio n. 9
0
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():
            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)
        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': ctx.url_for(search, library_id=db.server_library_id),
            'query': query,
            'book_ids': ids
        }
Esempio n. 10
0
def get_acquisition_feed(rc,
                         ids,
                         offset,
                         page_url,
                         up_url,
                         id_,
                         sort_by='title',
                         ascending=True,
                         feed_title=None):
    if not ids:
        raise HTTPNotFound('No books found')
    with rc.db.safe_read_lock:
        sort_by = sanitize_sort_field_name(rc.db.field_metadata, sort_by)
        items = rc.db.multisort([(sort_by, ascending)], ids)
        max_items = rc.opts.max_opds_items
        offsets = Offsets(offset, max_items, len(items))
        items = items[offsets.offset:offsets.offset + max_items]
        lm = rc.last_modified()
        rc.outheaders['Last-Modified'] = http_date(timestampfromdt(lm))
        return AcquisitionFeed(id_,
                               lm,
                               rc,
                               items,
                               offsets,
                               page_url,
                               up_url,
                               title=feed_title).root
Esempio n. 11
0
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():
            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)
        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':ctx.url_for(search, library_id=db.server_library_id),
                'query': query,
                'book_ids':ids
        }
Esempio n. 12
0
def interface_data(ctx, rd):
    '''
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &search=''&extra_books=''
    '''
    ans = {
        'username':rd.username,
        'output_format':prefs['output_format'].upper(),
        'input_formats':{x.upper():True for x in available_input_formats()},
        'gui_pubdate_display_format':tweaks['gui_pubdate_display_format'],
        'gui_timestamp_display_format':tweaks['gui_timestamp_display_format'],
        'gui_last_modified_display_format':tweaks['gui_last_modified_display_format'],
        'use_roman_numerals_for_series_number': get_use_roman(),
    }
    ans['library_map'], ans['default_library'] = ctx.library_map
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get('library_id')
        if lid and lid in ans['library_map']:
            rd.query.set('library_id', lid)
        usort = ud.get('sort')
        if usort:
            rd.query.set('sort', usort)
    ans['library_id'], db, sorts, orders = get_basic_query_data(ctx, rd.query)
    ans['user_session_data'] = ud
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['icon_map'] = icon_map()
        ans['icon_path'] = ctx.url_for('/icon', which='')
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data

    return ans
Esempio n. 13
0
def books_in(ctx, rd, encoded_category, encoded_item, library_id):
    '''
    Return the books (as list of ids) present in the specified category.

    Optional: ?num=100&offset=0&sort=title&sort_order=asc&get_additional_fields=
    '''
    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        try:
            dname, ditem = map(decode_name, (encoded_category, encoded_item))
        except:
            raise HTTPNotFound('Invalid encoded param: %r' % (encoded_category, encoded_item))
        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():
            raise HTTPNotFound('%s is not a valid sort field'%sort)

        if dname in ('allbooks', 'newest'):
            ids = ctx.allowed_book_ids(rd, db)
        elif dname == 'search':
            try:
                ids = ctx.search(rd, db, 'search:"%s"'%ditem)
            except Exception:
                raise HTTPNotFound('Search: %r not understood'%ditem)
        else:
            try:
                cid = int(ditem)
            except Exception:
                raise HTTPNotFound('Category id %r not an integer'%ditem)

            if dname == 'news':
                dname = 'tags'
            ids = db.get_books_for_category(dname, cid).intersection(ctx.allowed_book_ids(rd, db))

        ids = db.multisort(fields=[(sfield, sort_order == 'asc')], ids_to_sort=ids)
        total_num = len(ids)
        ids = ids[offset:offset+num]

        result = {
                'total_num': total_num, 'sort_order':sort_order,
                'offset':offset, 'num':len(ids), 'sort':sort,
                'base_url':ctx.url_for(books_in, encoded_category=encoded_category, encoded_item=encoded_item, library_id=db.server_library_id),
                'book_ids':ids
        }

        get_additional_fields = rd.query.get('get_additional_fields')
        if get_additional_fields:
            additional_fields = {}
            for field in get_additional_fields.split(','):
                field = field.strip()
                if field:
                    flist = additional_fields[field] = []
                    for id_ in ids:
                        flist.append(db.field_for(field, id_, default_value=None))
            if additional_fields:
                result['additional_fields'] = additional_fields
        return result
Esempio n. 14
0
def build_search_box(num, search, sort, order, ctx, field_metadata,
                     library_id):  # {{{
    div = E.div(id='search_box')
    form = E.form(_('Show '), method='get', action=ctx.url_for('/mobile'))
    form.set('accept-charset', 'UTF-8')

    div.append(form)

    num_select = E.select(name='num')
    for option in (5, 10, 25, 100):
        kwargs = {'value': unicode_type(option)}
        if option == num:
            kwargs['SELECTED'] = 'SELECTED'
        num_select.append(E.option(unicode_type(option), **kwargs))
    num_select.tail = ' books matching '
    form.append(num_select)

    searchf = E.input(name='search', id='s', value=search if search else '')
    searchf.tail = _(' sorted by ')
    form.append(searchf)

    sort_select = E.select(name='sort')
    for option in ('date', 'author', 'title', 'rating', 'size', 'tags',
                   'series'):
        q = sanitize_sort_field_name(field_metadata, option)
        kwargs = {'value': option}
        if q == sanitize_sort_field_name(field_metadata, sort):
            kwargs['SELECTED'] = 'SELECTED'
        sort_select.append(E.option(option, **kwargs))
    form.append(sort_select)

    order_select = E.select(name='order')
    for option in ('ascending', 'descending'):
        kwargs = {'value': option}
        if option == order:
            kwargs['SELECTED'] = 'SELECTED'
        order_select.append(E.option(option, **kwargs))
    form.append(order_select)

    if library_id:
        form.append(E.input(name='library_id', type='hidden',
                            value=library_id))

    form.append(E.input(id='go', type='submit', value=_('Search')))

    return div
Esempio n. 15
0
def build_search_box(num, search, sort, order, ctx, field_metadata, library_id):  # {{{
    div = E.div(id='search_box')
    form = E.form(_('Show '), method='get', action=ctx.url_for('/mobile'))
    form.set('accept-charset', 'UTF-8')

    div.append(form)

    num_select = E.select(name='num')
    for option in (5, 10, 25, 100):
        kwargs = {'value':str(option)}
        if option == num:
            kwargs['SELECTED'] = 'SELECTED'
        num_select.append(E.option(str(option), **kwargs))
    num_select.tail = ' books matching '
    form.append(num_select)

    searchf = E.input(name='search', id='s', value=search if search else '')
    searchf.tail = _(' sorted by ')
    form.append(searchf)

    sort_select = E.select(name='sort')
    for option in ('date','author','title','rating','size','tags','series'):
        q = sanitize_sort_field_name(field_metadata, option)
        kwargs = {'value':option}
        if q == sanitize_sort_field_name(field_metadata, sort):
            kwargs['SELECTED'] = 'SELECTED'
        sort_select.append(E.option(option, **kwargs))
    form.append(sort_select)

    order_select = E.select(name='order')
    for option in ('ascending','descending'):
        kwargs = {'value':option}
        if option == order:
            kwargs['SELECTED'] = 'SELECTED'
        order_select.append(E.option(option, **kwargs))
    form.append(order_select)

    if library_id:
        form.append(E.input(name='library_id', type='hidden', value=library_id))

    form.append(E.input(id='go', type='submit', value=_('Search')))

    return div
Esempio n. 16
0
def get_acquisition_feed(rc, ids, offset, page_url, up_url, id_, sort_by="title", ascending=True, feed_title=None):
    if not ids:
        raise HTTPNotFound("No books found")
    with rc.db.safe_read_lock:
        sort_by = sanitize_sort_field_name(rc.db.field_metadata, sort_by)
        items = rc.db.multisort([(sort_by, ascending)], ids)
        max_items = rc.opts.max_opds_items
        offsets = Offsets(offset, max_items, len(items))
        items = items[offsets.offset : offsets.offset + max_items]
        lm = rc.last_modified()
        rc.outheaders["Last-Modified"] = http_date(timestampfromdt(lm))
        return AcquisitionFeed(id_, lm, rc, items, offsets, page_url, up_url, title=feed_title).root
Esempio n. 17
0
def get_basic_query_data(ctx, query):
    db, library_id, library_map, default_library = get_library_data(ctx, query)
    skeys = db.field_metadata.sortable_field_keys()
    sorts, orders = [], []
    for x in query.get('sort', '').split(','):
        if x:
            s, o = x.partition('.')[::2]
            if o not in ('asc', 'desc'):
                o = 'asc'
            if s.startswith('_'):
                s = '#' + s[1:]
            s = sanitize_sort_field_name(db.field_metadata, s)
            if s in skeys:
                sorts.append(s), orders.append(o)
    if not sorts:
        sorts, orders = ['date'], ['desc']
    return library_id, db, sorts, orders
Esempio n. 18
0
def get_basic_query_data(ctx, query):
    db, library_id, library_map, default_library = get_library_data(ctx, query)
    skeys = db.field_metadata.sortable_field_keys()
    sorts, orders = [], []
    for x in query.get("sort", "").split(","):
        if x:
            s, o = x.rpartition(".")[::2]
            if o and not s:
                s, o = o, ""
            if o not in ("asc", "desc"):
                o = "asc"
            if s.startswith("_"):
                s = "#" + s[1:]
            s = sanitize_sort_field_name(db.field_metadata, s)
            if s in skeys:
                sorts.append(s), orders.append(o)
    if not sorts:
        sorts, orders = ["timestamp"], ["desc"]
    return library_id, db, sorts, orders
Esempio n. 19
0
def get_basic_query_data(ctx, query):
    db, library_id, library_map, default_library = get_library_data(ctx, query)
    skeys = db.field_metadata.sortable_field_keys()
    sorts, orders = [], []
    for x in query.get('sort', '').split(','):
        if x:
            s, o = x.rpartition('.')[::2]
            if o and not s:
                s, o = o, ''
            if o not in ('asc', 'desc'):
                o = 'asc'
            if s.startswith('_'):
                s = '#' + s[1:]
            s = sanitize_sort_field_name(db.field_metadata, s)
            if s in skeys:
                sorts.append(s), orders.append(o)
    if not sorts:
        sorts, orders = ['timestamp'], ['desc']
    return library_id, db, sorts, orders
Esempio n. 20
0
def interface_data(ctx, rd):
    '''
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &sort_tags_by=name&partition_method=first letter&collapse_at=25&
              &dont_collapse=
    '''
    ans = {'username':rd.username}
    ans['library_map'], ans['default_library'] = ctx.library_map
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get('library_id')
        if lid and lid in ans['library_map']:
            rd.query.set('library_id', lid)
        usort = ud.get('sort')
        if usort:
            rd.query.set('sort', usort)
    ans['library_id'], db, sorts, orders = get_basic_query_data(ctx, rd.query)
    ans['user_session_data'] = ud
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    with db.safe_read_lock:
        ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['categories'] = categories_as_json(ctx, rd, db)
        mdata = ans['metadata'] = {}
        for book_id in ans['search_result']['book_ids']:
            data = book_as_json(db, book_id)
            mdata[book_id] = data

    return ans
Esempio n. 21
0
def search_result(ctx, rd, db, query, num, offset, sort, sort_order, vl=''):
    multisort = [(sanitize_sort_field_name(db.field_metadata, s), ensure_val(o, 'asc', 'desc') == 'asc')
                 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)

    ids = ctx.search(rd, db, query, vl=vl)
    ids = db.multisort(fields=multisort, ids_to_sort=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':ctx.url_for(search, library_id=db.server_library_id),
        'query': query,
        'library_id': db.server_library_id,
        'book_ids':ids,
        'vl': vl,
    }
Esempio n. 22
0
def books_in(ctx, rd, encoded_category, encoded_item, library_id):
    """
    Return the books (as list of ids) present in the specified category.

    Optional: ?num=100&offset=0&sort=title&sort_order=asc&get_additional_fields=
    """
    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        try:
            dname, ditem = map(decode_name, (encoded_category, encoded_item))
        except:
            raise HTTPNotFound("Invalid encoded param: %r" % (encoded_category, encoded_item))
        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():
            raise HTTPNotFound("%s is not a valid sort field" % sort)

        if dname in ("allbooks", "newest"):
            ids = ctx.allowed_book_ids(rd, db)
        elif dname == "search":
            try:
                ids = ctx.search(rd, db, 'search:"%s"' % ditem)
            except Exception:
                raise HTTPNotFound("Search: %r not understood" % ditem)
        else:
            try:
                cid = int(ditem)
            except Exception:
                raise HTTPNotFound("Category id %r not an integer" % ditem)

            if dname == "news":
                dname = "tags"
            ids = db.get_books_for_category(dname, cid).intersection(ctx.allowed_book_ids(rd, db))

        ids = db.multisort(fields=[(sfield, sort_order == "asc")], ids_to_sort=ids)
        total_num = len(ids)
        ids = ids[offset : offset + num]

        result = {
            "total_num": total_num,
            "sort_order": sort_order,
            "offset": offset,
            "num": len(ids),
            "sort": sort,
            "base_url": ctx.url_for(
                books_in, encoded_category=encoded_category, encoded_item=encoded_item, library_id=db.server_library_id
            ),
            "book_ids": ids,
        }

        get_additional_fields = rd.query.get("get_additional_fields")
        if get_additional_fields:
            additional_fields = {}
            for field in get_additional_fields.split(","):
                field = field.strip()
                if field:
                    flist = additional_fields[field] = []
                    for id_ in ids:
                        flist.append(db.field_for(field, id_, default_value=None))
            if additional_fields:
                result["additional_fields"] = additional_fields
        return result
Esempio n. 23
0
def interface_data(ctx, rd):
    """
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &search=''&extra_books=''
    """
    ans = {
        "username": rd.username,
        "output_format": prefs["output_format"].upper(),
        "input_formats": {x.upper(): True for x in available_input_formats()},
        "gui_pubdate_display_format": tweaks["gui_pubdate_display_format"],
        "gui_timestamp_display_format": tweaks["gui_timestamp_display_format"],
        "gui_last_modified_display_format": tweaks["gui_last_modified_display_format"],
        "use_roman_numerals_for_series_number": get_use_roman(),
    }
    ans["library_map"], ans["default_library"] = ctx.library_map
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get("library_id")
        if lid and lid in ans["library_map"]:
            rd.query.set("library_id", lid)
        usort = ud.get("sort")
        if usort:
            rd.query.set("sort", usort)
    ans["library_id"], db, sorts, orders = get_basic_query_data(ctx, rd.query)
    ans["user_session_data"] = ud
    try:
        num = int(rd.query.get("num", DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound("Invalid number of books: %r" % rd.query.get("num"))
    with db.safe_read_lock:
        try:
            ans["search_result"] = search_result(
                ctx, rd, db, rd.query.get("search", ""), num, 0, ",".join(sorts), ",".join(orders)
            )
        except ParseException:
            ans["search_result"] = search_result(ctx, rd, db, "", num, 0, ",".join(sorts), ",".join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop("ondevice", None)
        ans["sortable_fields"] = sorted(
            ((sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
            key=lambda (field, name): sort_key(name),
        )
        ans["field_metadata"] = db.field_metadata.all_metadata()
        ans["icon_map"] = icon_map()
        ans["icon_path"] = ctx.url_for("/icon", which="")
        mdata = ans["metadata"] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get("extra_books", "").split(","))
        except Exception:
            extra_books = ()
        for coll in (ans["search_result"]["book_ids"], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data

    return ans