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
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
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
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, }
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, }