Пример #1
0
def categories(ctx, rd, library_id):
    '''
    Return the list of top-level categories as a list of dictionaries. Each
    dictionary is of the form::
        {
        'name': Display Name,
        'url':URL that gives the JSON object corresponding to all entries in this category,
        'icon': URL to icon of this category,
        'is_category': False for the All Books and Newest categories, True for everything else
        }

    '''
    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        ans = {}
        categories = ctx.get_categories(rd, db)
        category_meta = db.field_metadata
        library_id = db.server_library_id

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{'url':k, 'name':v[0], 'icon':v[1], 'is_category':True}
                for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in [
                (_('All books'), 'allbooks', 'book.png'),
                (_('Newest'), 'newest', 'forward.png'),
                ]:
            ans.insert(0, {'name':name, 'url':url, 'icon':icon,
                'is_category':False})

        for c in ans:
            c['url'] = ctx.url_for(globals()['category'], encoded_name=encode_name(c['url']), library_id=library_id)
            c['icon'] = ctx.url_for(get_icon, which=c['icon'])

        return ans
Пример #2
0
def categories(ctx, rd, library_id):
    """
    Return the list of top-level categories as a list of dictionaries. Each
    dictionary is of the form::
        {
        'name': Display Name,
        'url':URL that gives the JSON object corresponding to all entries in this category,
        'icon': URL to icon of this category,
        'is_category': False for the All Books and Newest categories, True for everything else
        }

    """
    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        ans = {}
        categories = ctx.get_categories(rd, db)
        category_meta = db.field_metadata
        library_id = db.server_library_id

        def getter(x):
            return category_meta[x]["name"]

        displayed_custom_fields = custom_fields_to_display(db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ("formats", "identifiers"):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and category not in displayed_custom_fields:
                continue
            display_name = meta["name"]
            if category.startswith("@"):
                category = category.partition(".")[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{"url": k, "name": v[0], "icon": v[1], "is_category": True} for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x["name"]))
        for name, url, icon in [(_("All books"), "allbooks", "book.png"), (_("Newest"), "newest", "forward.png")]:
            ans.insert(0, {"name": name, "url": url, "icon": icon, "is_category": False})

        for c in ans:
            c["url"] = ctx.url_for(globals()["category"], encoded_name=encode_name(c["url"]), library_id=library_id)
            c["icon"] = ctx.url_for(get_icon, which=c["icon"])

        return ans
Пример #3
0
def book_to_json(ctx, rd, db, book_id,
                 get_category_urls=True, device_compatible=False, device_for_template=None):
    mi = db.get_metadata(book_id, get_cover=False)
    codec = JsonCodec(db.field_metadata)
    if not device_compatible:
        try:
            mi.rating = mi.rating/2.
        except Exception:
            mi.rating = 0.0
    data = codec.encode_book_metadata(mi)
    for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
            'rights', 'book_producer'):
        data.pop(x, None)

    get = partial(ctx.url_for, get_content, book_id=book_id, library_id=db.server_library_id)
    data['cover'] = get(what='cover')
    data['thumbnail'] = get(what='thumb')

    if not device_compatible:
        mi.format_metadata = {k.lower():dict(v) for k, v in
                mi.format_metadata.iteritems()}
        for v in mi.format_metadata.itervalues():
            mtime = v.get('mtime', None)
            if mtime is not None:
                v['mtime'] = isoformat(mtime, as_utc=True)
        data['format_metadata'] = mi.format_metadata
        fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
        pf = prefs['output_format'].lower()
        other_fmts = list(fmts)
        try:
            fmt = pf if pf in fmts else other_fmts[0]
        except:
            fmt = None
        if fmts and fmt:
            other_fmts = [x for x in fmts if x != fmt]
        data['formats'] = sorted(fmts)
        if fmt:
            data['main_format'] = {fmt:get(what=fmt)}
        else:
            data['main_format'] = None
        data['other_formats'] = {fmt:get(what=fmt) for fmt in other_fmts}

        if get_category_urls:
            category_urls = data['category_urls'] = {}
            all_cats = ctx.get_categories(rd, db)
            for key in mi.all_field_keys():
                fm = mi.metadata_for_field(key)
                if (fm and fm['is_category'] and not fm['is_csp'] and
                        key != 'formats' and fm['datatype'] != 'rating'):
                    categories = mi.get(key) or []
                    if isinstance(categories, basestring):
                        categories = [categories]
                    category_urls[key] = dbtags = {}
                    for category in categories:
                        for tag in all_cats.get(key, ()):
                            if tag.original_name == category:
                                dbtags[category] = ctx.url_for(
                                    books_in,
                                    encoded_category=encode_name(tag.category if tag.category else key),
                                    encoded_item=encode_name(tag.original_name if tag.id is None else unicode(tag.id)),
                                    library_id=db.server_library_id
                                )
                                break
    else:
        series = data.get('series', None) or ''
        if series:
            tsorder = tweaks['save_template_title_series_sorting']
            series = title_sort(series, order=tsorder)
        data['_series_sort_'] = series
        if device_for_template:
            import posixpath
            from calibre.devices.utils import create_upload_path
            from calibre.utils.filenames import ascii_filename as sanitize
            from calibre.customize.ui import device_plugins

            for device_class in device_plugins():
                if device_class.__class__.__name__ == device_for_template:
                    template = device_class.save_template()
                    data['_filename_'] = create_upload_path(mi, book_id,
                            template, sanitize, path_type=posixpath)
                    break

    return data, mi.last_modified
Пример #4
0
def category(ctx, rd, encoded_name, library_id):
    '''
    Return a dictionary describing the category specified by name. The

    Optional: ?num=100&offset=0&sort=name&sort_order=asc

    The dictionary looks like::

        {
            'category_name': Category display name,
            'base_url': Base URL for this category,
            'total_num': Total numberof items in this category,
            'offset': The offset for the items returned in this result,
            'num': The number of items returned in this result,
            'sort': How the returned items are sorted,
            'sort_order': asc or desc
            'subcategories': List of sub categories of this category.
            'items': List of items in this category,
        }

    Each subcategory is a dictionary of the same form as those returned by
    /ajax/categories

    Each  item is a dictionary of the form::

        {
            'name': Display name,
            'average_rating': Average rating for books in this item,
            'count': Number of books in this item,
            'url': URL to get list of books in this item,
            'has_children': If True this item contains sub categories, look
            for an entry corresponding to this item in subcategories int he
            main dictionary,
        }

    :param sort: How to sort the returned items. Choices are: name, rating,
                    popularity
    :param sort_order: asc or desc

    To learn how to create subcategories see
    https://manual.calibre-ebook.com/sub_groups.html
    '''

    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        num, offset = get_pagination(rd.query)
        sort, sort_order = rd.query.get('sort'), rd.query.get('sort_order')
        sort = ensure_val(sort, 'name', 'rating', 'popularity')
        sort_order = ensure_val(sort_order, 'asc', 'desc')
        try:
            dname = decode_name(encoded_name)
        except:
            raise HTTPNotFound('Invalid encoding of category name %r'%encoded_name)
        base_url = ctx.url_for(globals()['category'], encoded_name=encoded_name, library_id=db.server_library_id)

        if dname in ('newest', 'allbooks'):
            sort, sort_order = 'timestamp', 'desc'
            rd.query['sort'], rd.query['sort_order'] = sort, sort_order
            return books_in(ctx, rd, encoded_name, encode_name('0'), library_id)

        fm = db.field_metadata
        categories = ctx.get_categories(rd, db)
        hierarchical_categories = db.pref('categories_using_hierarchy', ())

        subcategory = dname
        toplevel = subcategory.partition('.')[0]
        if toplevel == subcategory:
            subcategory = None
        if toplevel not in categories or toplevel not in fm:
            raise HTTPNotFound('Category %r not found'%toplevel)

        # Find items and sub categories
        subcategories = []
        meta = fm[toplevel]
        item_names = {}
        children = set()

        if meta['kind'] == 'user':
            fullname = ((toplevel + '.' + subcategory) if subcategory is not
                                None else toplevel)
            try:
                # User categories cannot be applied to books, so this is the
                # complete set of items, no need to consider sub categories
                items = categories[fullname]
            except:
                raise HTTPNotFound('User category %r not found'%fullname)

            parts = fullname.split('.')
            for candidate in categories:
                cparts = candidate.split('.')
                if len(cparts) == len(parts)+1 and cparts[:-1] == parts:
                    subcategories.append({'name':cparts[-1],
                        'url':candidate,
                        'icon':category_icon(toplevel, meta)})

            category_name = toplevel[1:].split('.')
            # When browsing by user categories we ignore hierarchical normal
            # columns, so children can be empty

        elif toplevel in hierarchical_categories:
            items = []

            category_names = [x.original_name.split('.') for x in categories[toplevel] if
                    '.' in x.original_name]

            if subcategory is None:
                children = set(x[0] for x in category_names)
                category_name = [meta['name']]
                items = [x for x in categories[toplevel] if '.' not in x.original_name]
            else:
                subcategory_parts = subcategory.split('.')[1:]
                category_name = [meta['name']] + subcategory_parts

                lsp = len(subcategory_parts)
                children = set('.'.join(x) for x in category_names if len(x) ==
                        lsp+1 and x[:lsp] == subcategory_parts)
                items = [x for x in categories[toplevel] if x.original_name in
                        children]
                item_names = {x:x.original_name.rpartition('.')[-1] for x in
                        items}
                # Only mark the subcategories that have children themselves as
                # subcategories
                children = set('.'.join(x[:lsp+1]) for x in category_names if len(x) >
                        lsp+1 and x[:lsp] == subcategory_parts)
            subcategories = [{'name':x.rpartition('.')[-1],
                'url':toplevel+'.'+x,
                'icon':category_icon(toplevel, meta)} for x in children]
        else:
            items = categories[toplevel]
            category_name = meta['name']

        for x in subcategories:
            x['url'] = ctx.url_for(globals()['category'], encoded_name=encode_name(x['url']), library_id=db.server_library_id)
            x['icon'] = ctx.url_for(get_icon, which=x['icon'])
            x['is_category'] = True

        sort_keygen = {
                'name': lambda x: sort_key(x.sort if x.sort else x.original_name),
                'popularity': lambda x: x.count,
                'rating': lambda x: x.avg_rating
        }
        items.sort(key=sort_keygen[sort], reverse=sort_order == 'desc')
        total_num = len(items)
        items = items[offset:offset+num]
        items = [{
            'name':item_names.get(x, x.original_name),
            'average_rating': x.avg_rating,
            'count': x.count,
            'url': ctx.url_for(books_in, encoded_category=encode_name(x.category if x.category else toplevel),
                               encoded_item=encode_name(x.original_name if x.id is None else unicode(x.id)),
                               library_id=db.server_library_id
                               ),
            'has_children': x.original_name in children,
            } for x in items]

        return {
                'category_name': category_name,
                'base_url': base_url,
                'total_num': total_num,
                'offset':offset, 'num':len(items), 'sort':sort,
                'sort_order':sort_order,
                'subcategories':subcategories,
                'items':items,
        }
Пример #5
0
def category(ctx, rd, encoded_name, library_id):
    """
    Return a dictionary describing the category specified by name. The

    Optional: ?num=100&offset=0&sort=name&sort_order=asc

    The dictionary looks like::

        {
            'category_name': Category display name,
            'base_url': Base URL for this category,
            'total_num': Total numberof items in this category,
            'offset': The offset for the items returned in this result,
            'num': The number of items returned in this result,
            'sort': How the returned items are sorted,
            'sort_order': asc or desc
            'subcategories': List of sub categories of this category.
            'items': List of items in this category,
        }

    Each subcategory is a dictionary of the same form as those returned by
    /ajax/categories

    Each  item is a dictionary of the form::

        {
            'name': Display name,
            'average_rating': Average rating for books in this item,
            'count': Number of books in this item,
            'url': URL to get list of books in this item,
            'has_children': If True this item contains sub categories, look
            for an entry corresponding to this item in subcategories int he
            main dictionary,
        }

    :param sort: How to sort the returned items. Choices are: name, rating,
                    popularity
    :param sort_order: asc or desc

    To learn how to create subcategories see
    http://manual.calibre-ebook.com/sub_groups.html
    """

    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        num, offset = get_pagination(rd.query)
        sort, sort_order = rd.query.get("sort"), rd.query.get("sort_order")
        sort = ensure_val(sort, "name", "rating", "popularity")
        sort_order = ensure_val(sort_order, "asc", "desc")
        try:
            dname = decode_name(encoded_name)
        except:
            raise HTTPNotFound("Invalid encoding of category name %r" % encoded_name)
        base_url = ctx.url_for(globals()["category"], encoded_name=encoded_name, library_id=db.server_library_id)

        if dname in ("newest", "allbooks"):
            sort, sort_order = "timestamp", "desc"
            rd.query["sort"], rd.query["sort_order"] = sort, sort_order
            return books_in(ctx, rd, encoded_name, encode_name("0"), library_id)

        fm = db.field_metadata
        categories = ctx.get_categories(rd, db)
        hierarchical_categories = db.pref("categories_using_hierarchy", ())

        subcategory = dname
        toplevel = subcategory.partition(".")[0]
        if toplevel == subcategory:
            subcategory = None
        if toplevel not in categories or toplevel not in fm:
            raise HTTPNotFound("Category %r not found" % toplevel)

        # Find items and sub categories
        subcategories = []
        meta = fm[toplevel]
        item_names = {}
        children = set()

        if meta["kind"] == "user":
            fullname = (toplevel + "." + subcategory) if subcategory is not None else toplevel
            try:
                # User categories cannot be applied to books, so this is the
                # complete set of items, no need to consider sub categories
                items = categories[fullname]
            except:
                raise HTTPNotFound("User category %r not found" % fullname)

            parts = fullname.split(".")
            for candidate in categories:
                cparts = candidate.split(".")
                if len(cparts) == len(parts) + 1 and cparts[:-1] == parts:
                    subcategories.append({"name": cparts[-1], "url": candidate, "icon": category_icon(toplevel, meta)})

            category_name = toplevel[1:].split(".")
            # When browsing by user categories we ignore hierarchical normal
            # columns, so children can be empty

        elif toplevel in hierarchical_categories:
            items = []

            category_names = [x.original_name.split(".") for x in categories[toplevel] if "." in x.original_name]

            if subcategory is None:
                children = set(x[0] for x in category_names)
                category_name = [meta["name"]]
                items = [x for x in categories[toplevel] if "." not in x.original_name]
            else:
                subcategory_parts = subcategory.split(".")[1:]
                category_name = [meta["name"]] + subcategory_parts

                lsp = len(subcategory_parts)
                children = set(
                    ".".join(x) for x in category_names if len(x) == lsp + 1 and x[:lsp] == subcategory_parts
                )
                items = [x for x in categories[toplevel] if x.original_name in children]
                item_names = {x: x.original_name.rpartition(".")[-1] for x in items}
                # Only mark the subcategories that have children themselves as
                # subcategories
                children = set(
                    ".".join(x[: lsp + 1]) for x in category_names if len(x) > lsp + 1 and x[:lsp] == subcategory_parts
                )
            subcategories = [
                {"name": x.rpartition(".")[-1], "url": toplevel + "." + x, "icon": category_icon(toplevel, meta)}
                for x in children
            ]
        else:
            items = categories[toplevel]
            category_name = meta["name"]

        for x in subcategories:
            x["url"] = ctx.url_for(
                globals()["category"], encoded_name=encode_name(x["url"]), library_id=db.server_library_id
            )
            x["icon"] = ctx.url_for(get_icon, which=x["icon"])
            x["is_category"] = True

        sort_keygen = {
            "name": lambda x: sort_key(x.sort if x.sort else x.original_name),
            "popularity": lambda x: x.count,
            "rating": lambda x: x.avg_rating,
        }
        items.sort(key=sort_keygen[sort], reverse=sort_order == "desc")
        total_num = len(items)
        items = items[offset : offset + num]
        items = [
            {
                "name": item_names.get(x, x.original_name),
                "average_rating": x.avg_rating,
                "count": x.count,
                "url": ctx.url_for(
                    books_in,
                    encoded_category=encode_name(x.category if x.category else toplevel),
                    encoded_item=encode_name(x.original_name if x.id is None else unicode(x.id)),
                    library_id=db.server_library_id,
                ),
                "has_children": x.original_name in children,
            }
            for x in items
        ]

        return {
            "category_name": category_name,
            "base_url": base_url,
            "total_num": total_num,
            "offset": offset,
            "num": len(items),
            "sort": sort,
            "sort_order": sort_order,
            "subcategories": subcategories,
            "items": items,
        }