From 6cadb702e956bdf198772129cc06e7a199aabf3e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 17 Feb 2016 16:15:44 +0530 Subject: [PATCH] CS Book Details: Show series --- src/calibre/srv/code.py | 19 +++++++++++++++- src/pyj/book_list/book_details.pyj | 36 ++++++++++++++++++++++-------- src/pyj/utils.pyj | 27 ++++++++++++++++++++++ 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index 924b313d35..61934cb6b0 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -4,12 +4,13 @@ from __future__ import (unicode_literals, division, absolute_import, print_function) -import re, hashlib, random +import re, hashlib, random, os from functools import partial from threading import Lock from json import load as load_json_file from calibre import prepare_string_for_xml, as_unicode +from calibre.constants import config_dir from calibre.customize.ui import available_input_formats from calibre.db.view import sanitize_sort_field_name from calibre.srv.ajax import get_db, search_result @@ -84,6 +85,21 @@ def get_basic_query_data(ctx, query): sorts, orders = ['timestamp'], ['desc'] return library_id, db, sorts, orders +_use_roman = None + +def get_use_roman(): + global _use_roman + if _use_roman is None: + try: + with lopen(os.path.join(config_dir, 'gui.py'), 'rb') as f: + raw = f.read() + except EnvironmentError: + _use_roman = False + else: + m = re.search(br'use_roman_numerals_for_series_number\s*=\s*(True|False)', raw) + _use_roman = m is not None and m.group(1) == b'True' + return _use_roman + DEFAULT_NUMBER_OF_BOOKS = 50 @endpoint('/interface-data/init', postprocess=json) @@ -101,6 +117,7 @@ def interface_data(ctx, rd): '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 = {} diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj index 5dd0ca12d1..2b5c596d79 100644 --- a/src/pyj/book_list/book_details.pyj +++ b/src/pyj/book_list/book_details.pyj @@ -11,6 +11,7 @@ from book_list.globals import get_boss from modals import error_dialog from widgets import create_spinner, create_button from date import format_date +from utils import fmt_sidx bd_counter = 0 @@ -37,15 +38,6 @@ def get_preferred_format(metadata, output_format, input_formats): IGNORED_FIELDS = {'title', 'id', 'urls_from_identifiers', 'lang_names', 'last_modified'} -def allowed_fields(field): - if str.endswith(field, '_index'): - return False - if str.startswith(field, '#'): - return True - if field in IGNORED_FIELDS or str.endswith(field, '_sort'): - return False - return True - default_sort = {f:i+1 for i, f in enumerate(('title', 'title_sort', 'authors', 'author_sort', 'series', 'rating', 'pubdate', 'tags', 'timestamp', 'pubdate', 'identifiers', 'languages', 'publisher', 'last_modified'))} default_sort['formats'] = 999 @@ -69,6 +61,17 @@ def read_format(ev): get_boss().ui.book_details_panel.read_format(fmt) def render_metadata(mi, interface_data, table, field_list=None): + def allowed_fields(field): + if str.endswith(field, '_index'): + fm = interface_data.field_metadata[field[:-len('_index')]] + if fm and fm.datatype == 'series': + return False + if str.startswith(field, '#'): + return True + if field in IGNORED_FIELDS or str.endswith(field, '_sort'): + return False + return True + fields = field_list or sorted(filter(allowed_fields, mi), key=field_sorter(interface_data.field_metadata)) comments = [] @@ -177,6 +180,19 @@ def render_metadata(mi, interface_data, table, field_list=None): fmt = interface_data['gui_' + field + '_display_format'] or (fm['display'] or {}).date_format add_row(name, format_date(val, fmt)) + def process_series(field, fm, name, val): + if val: + ifield = field + '_index' + try: + ival = float(mi[ifield]) + except Exception: + ival = 1.0 + ival = fmt_sidx(ival, use_roman=interface_data.use_roman_numerals_for_series_number) + table.appendChild(E.tr(E.td(name + ':'), E.td())) + table.lastChild.lastChild.appendChild(E.span(ival, _(' of '), E.a( + data_search=JSON.stringify([name, val]), onclick=execute_search, + title=str.format(_('Click to see books with {0}: {1}'), name, val), href='javascript: void(0)', val))) + def process_field(field, fm): name = fm.name or field datatype = fm.datatype @@ -201,6 +217,8 @@ def render_metadata(mi, interface_data, table, field_list=None): func = process_languages elif datatype == 'datetime': func = process_datetime + elif datatype == 'series': + func = process_series if func: func(field, fm, name, val) else: diff --git a/src/pyj/utils.pyj b/src/pyj/utils.pyj index 49a17dee0f..7501ee994a 100644 --- a/src/pyj/utils.pyj +++ b/src/pyj/utils.pyj @@ -54,3 +54,30 @@ def parse_url_params(url=None, allow_multiple=False): else: ans[key] = val return ans + +_roman = list(zip( +[1000,900,500,400,100,90,50,40,10,9,5,4,1], +["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"] +)) + +def roman(num): + if num <= 0 or num >= 4000 or int(num) != num: + return num + '' + result = [] + for d, r in _roman: + while num >= d: + result.append(r) + num -= d + return result.join('') + +def fmt_sidx(val, fmt='{:.2f}', use_roman=True): + if val is undefined or val is None or val == '': + return '1' + if int(val) == float(val): + if use_roman: + return roman(val) + return int(val) + '' + return str.format(fmt, float(val)) + +if __name__ == '__main__': + print(fmt_sidx(10), fmt_sidx(1.2))