From f092e5a2366b7e0176940269b238f70084373147 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Mar 2018 15:08:26 +0530 Subject: [PATCH] API to get all field names from the server --- src/calibre/srv/code.py | 10 ++++++++ src/pyj/book_list/edit_metadata.pyj | 29 +++++++++++++++++++--- src/pyj/book_list/library_data.pyj | 37 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index af53d0670a..4e585e24d1 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -384,3 +384,13 @@ def tag_browser(ctx, rd): return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db, opts, vl)) return rd.etagged_dynamic_response(etag, generate) + + +@endpoint('/interface-data/field-names/{field}', postprocess=json) +def field_names(ctx, rd, field): + ''' + Get a list of all names for the specified field + Optional: ?library_id= + ''' + db, library_id = get_library_data(ctx, rd)[:2] + return tuple(db.all_field_names(field)) diff --git a/src/pyj/book_list/edit_metadata.pyj b/src/pyj/book_list/edit_metadata.pyj index 6f4b687813..99498731b7 100644 --- a/src/pyj/book_list/edit_metadata.pyj +++ b/src/pyj/book_list/edit_metadata.pyj @@ -11,8 +11,8 @@ from book_list.book_details import ( basic_table_rules, fetch_metadata, field_sorter, no_book, report_load_failure ) from book_list.library_data import ( - book_metadata, current_library_id, library_data, load_status, loaded_book_ids, - set_book_metadata + book_metadata, current_library_id, field_names_for, library_data, load_status, + loaded_book_ids, set_book_metadata ) from book_list.router import back from book_list.top_bar import create_top_bar, set_title @@ -114,6 +114,23 @@ def simple_line_edit(container_id, book_id, field, fm, div, mi): return x +def multiple_line_edit(list_to_ui, ui_to_list, container_id, book_id, field, fm, div, mi): + nonlocal value_to_json + name = fm.name or field + le = E.input(type='text', name=name.replace('#', '_c_'), autocomplete=True) + le.value = (resolved_metadata(mi, field) or v'[]').join(list_to_ui) + form = create_form(le, line_edit_get_value, container_id, book_id, field) + div.appendChild(E.div(style='margin: 0.5ex 1rem', _( + 'Edit the "{0}" below. Multiple items can be separated by {1}.').format(name, list_to_ui.strip()))) + div.appendChild(E.div(style='margin: 0.5ex 1rem', form)) + div.appendChild(E.div(style='margin: 0.5ex 1rem')) + le.focus(), le.select() + value_to_json = def(x): + return [a.strip() for a in x.split(ui_to_list) if a.strip()] + div.lastChild.appendChild(E.span(_('Loading all {}...').format(name))) + field_names_for(field, print) + + def edit_field(container_id, book_id, field): nonlocal value_to_json fm = library_data.field_metadata[field] @@ -125,10 +142,16 @@ def edit_field(container_id, book_id, field): d.style.display = 'block' d.previousSibling.style.display = 'none' clear(d) - simple_line_edit(container_id, book_id, field, fm, d, mi) + if field is 'authors': + multiple_line_edit(' & ', '&', container_id, book_id, field, fm, d, mi) + else: + simple_line_edit(container_id, book_id, field, fm, d, mi) if field is 'title': value_to_json = def(x): return x or _('Untitled') + elif field is 'authors': + value_to_json = def(x): + return [a.strip() for a in x.split('&') if a.strip()] or [_('Unknown')] def render_metadata(mi, table, container_id, book_id): # {{{ diff --git a/src/pyj/book_list/library_data.pyj b/src/pyj/book_list/library_data.pyj index cdf574c5a2..19da14bcdb 100644 --- a/src/pyj/book_list/library_data.pyj +++ b/src/pyj/book_list/library_data.pyj @@ -73,6 +73,9 @@ def update_library_data(data): load_status.ok = True load_status.error_html = None library_data.previous_book_ids = v'[]' + if library_data.for_library is not current_library_id(): + library_data.field_names = {} + library_data.for_library = current_library_id() for key in 'search_result sortable_fields field_metadata metadata virtual_libraries book_display_fields'.split(' '): library_data[key] = data[key] sr = library_data.search_result @@ -140,6 +143,40 @@ def fetch_init_data(): load_status.current_fetch.send() +def field_names_received(library_id, field, proceed, end_type, xhr, event): + if library_id is not current_library_id(): + return + if end_type is not 'load': + if end_type is not 'abort': + proceed(False, field, xhr.error_html) + return + try: + names = JSON.parse(xhr.responseText) + except Exception: + import traceback + traceback.print_exc() + proceed(False, field, 'Invalid JSON from server') + return + library_data.field_names[field] = { + 'loading': False, + 'loaded': True, + 'last_updated_at': Date.now(), + 'names': names + } + proceed(True, field, names) + + +def field_names_for(field, proceed): + if library_data.field_names[field] and library_data.field_names[field].loaded: + proceed(True, field, library_data.field_names[field].names) + if not library_data.field_names[field] or (not library_data.field_names[field].loading and Date.now() - library_data.field_names[field].last_updated_at > 3600 * 1000): + + ajax(f'interface-data/field-names/{encodeURIComponent(field)}', field_names_received.bind(None, current_library_id(), field, proceed), query={'library_id': current_library_id()}).send() + if not library_data.field_names[field]: + library_data.field_names[field] = {'last_updated_at': 0, 'loaded': False, 'names': v'[]'} + library_data.field_names[field].loading = True + + def thumbnail_url(book_id, width, height): return absolute_path( 'get/thumb/{}/{}?sz={}x{}'.format(