From 016c7b4b1c2a798e152827ae507ab346ce407981 Mon Sep 17 00:00:00 2001 From: Virgil Grigoras Date: Sun, 30 Sep 2018 14:08:55 +0200 Subject: [PATCH 1/5] Add ability to store and edit publishers --- cps/db.py | 2 +- cps/static/js/edit_books.js | 25 +++++++++++++++++++++++++ cps/templates/book_edit.html | 2 +- cps/web.py | 25 +++++++++++++++++++------ 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/cps/db.py b/cps/db.py index 54e42d25..09580666 100755 --- a/cps/db.py +++ b/cps/db.py @@ -211,7 +211,7 @@ class Publishers(Base): name = Column(String) sort = Column(String) - def __init__(self, name, sort): + def __init__(self, name, sort = "ASC"): self.name = name self.sort = sort diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index 1d182887..35515aa1 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -142,6 +142,17 @@ var languages = new Bloodhound({ } }); +var publishers = new Bloodhound({ + name: "publisher", + datumTokenizer: function datumTokenizer(datum) { + return [datum.name]; + }, + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: getPath() + "/get_publishers_json?q=%QUERY" + } +}); + function sourceSplit(query, cb, split, source) { var bhAdapter = source.ttAdapter(); @@ -224,6 +235,20 @@ promiseLanguages.done(function() { ); }); +var promisePublishers = publishers.initialize(); +promisePublishers.done(function() { + $("#publisher").typeahead( + { + highlight: true, minLength: 0, + hint: true + }, { + name: "publishers", + displayKey: "name", + source: publishers.ttAdapter() + } + ); +}); + $("#search").on("change input.typeahead:selected", function() { var form = $("form").serialize(); $.getJSON( getPath() + "/get_matching_tags", form, function( data ) { diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index e2907624..0276975c 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -101,7 +101,7 @@
- +
diff --git a/cps/web.py b/cps/web.py index c93ca00d..b4132111 100644 --- a/cps/web.py +++ b/cps/web.py @@ -532,6 +532,10 @@ def fill_indexpage(page, database, db_filter, order, *join): # Modifies different Database objects, first check if elements have to be added to database, than check # if elements have to be deleted, because they are no longer used def modify_database_object(input_elements, db_book_object, db_object, db_session, db_type): + # passing input_elements not as a list may lead to undesired results + if type(input_elements) is not list: + raise TypeError(str(input_elements) + " should be passed as a list") + input_elements = [x for x in input_elements if x != ''] # we have all input element (authors, series, tags) names now # 1. search for elements to remove @@ -551,7 +555,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session # if the element was not found in the new list, add it to remove list if not found: del_elements.append(c_elements) - # 2. search for elements that need to be added + # 2. search for elements that need to be added add_elements = [] for inp_element in input_elements: found = False @@ -1030,7 +1034,17 @@ def get_authors_json(): json_dumps = json.dumps([dict(name=r.name.replace('|',',')) for r in entries]) return json_dumps + +@app.route("/get_publishers_json", methods=['GET', 'POST']) +@login_required_if_no_ano +def get_publishers_json(): + if request.method == "GET": + query = request.args.get('q') + entries = db.session.query(db.Publishers).filter(db.Publishers.name.ilike("%" + query + "%")).all() + json_dumps = json.dumps([dict(name=r.name.replace('|',',')) for r in entries]) + return json_dumps + @app.route("/get_tags_json", methods=['GET', 'POST']) @login_required_if_no_ano def get_tags_json(): @@ -3549,11 +3563,10 @@ def edit_book(book_id): book.pubdate = db.Books.DEFAULT_PUBDATE else: book.pubdate = db.Books.DEFAULT_PUBDATE - '''if len(book.publishers): - if to_save["publisher"] != book.publishers[0].name: - modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series') - else: - modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series')''' + + if to_save["publisher"]: + if len(book.publishers) == 0 or (len(book.publishers) > 0 and to_save["publisher"] != book.publishers[0].name): + modify_database_object([to_save["publisher"]], book.publishers, db.Publishers, db.session, 'publisher') # handle book languages input_languages = to_save["languages"].split(',') From 5129bc36018f85a26f68302aec01e8372251114f Mon Sep 17 00:00:00 2001 From: Virgil Grigoras Date: Sun, 30 Sep 2018 18:30:24 +0200 Subject: [PATCH 2/5] Add entry for publishers to the left menu (+ setting for showing / hiding) + separate publisher page --- cps/templates/config_view_edit.html | 4 ++++ cps/templates/detail.html | 15 +++++++++++---- cps/templates/layout.html | 3 +++ cps/templates/user_edit.html | 4 ++++ cps/ub.py | 8 ++++++++ cps/web.py | 29 +++++++++++++++++++++++++++++ 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/cps/templates/config_view_edit.html b/cps/templates/config_view_edit.html index 5cbf8e65..88c4a4bc 100644 --- a/cps/templates/config_view_edit.html +++ b/cps/templates/config_view_edit.html @@ -143,6 +143,10 @@
+
+ + +
diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 420b98a6..57475736 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -120,13 +120,20 @@
{% endif %} + {% if entry.publishers|length > 0 %}
-

- {{_('Publisher')}}:{% for publisher in entry.publishers %} {{publisher.name}}{% if not loop.last %},{% endif %}{% endfor %} -

+

+ {{_('Publisher')}}: + {% for publisher in entry.publishers %} + {{publisher.name}} + {% if not loop.last %},{% endif %} + {% endfor %} + +

- {% endif %} + {% endif %} + {% if entry.pubdate[:10] != '0101-01-01' %}

{{_('Publishing date')}}: {{entry.pubdate|formatdate}}

{% endif %} diff --git a/cps/templates/layout.html b/cps/templates/layout.html index 8ccce35c..d0f6469f 100644 --- a/cps/templates/layout.html +++ b/cps/templates/layout.html @@ -159,6 +159,9 @@ {% if g.user.show_author() %} {%endif%} + {% if g.user.show_publisher() %} + + {%endif%} {% if g.user.filter_language() == 'all' and g.user.show_language() %} {%endif%} diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index ecf8042e..48630cea 100644 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -89,6 +89,10 @@ +
+ + +
diff --git a/cps/ub.py b/cps/ub.py index f1b19d02..2a41663e 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -41,6 +41,7 @@ SIDEBAR_READ_AND_UNREAD = 256 SIDEBAR_RECENT = 512 SIDEBAR_SORTED = 1024 MATURE_CONTENT = 2048 +SIDEBAR_PUBLISHER = 4096 DEFAULT_PASS = "admin123" DEFAULT_PORT = int(os.environ.get("CALIBRE_PORT", 8083)) @@ -136,6 +137,9 @@ class UserBase: def show_author(self): return bool((self.sidebar_view is not None)and(self.sidebar_view & SIDEBAR_AUTHOR == SIDEBAR_AUTHOR)) + def show_publisher(self): + return bool((self.sidebar_view is not None)and(self.sidebar_view & SIDEBAR_PUBLISHER == SIDEBAR_PUBLISHER)) + def show_best_rated_books(self): return bool((self.sidebar_view is not None)and(self.sidebar_view & SIDEBAR_BEST_RATED == SIDEBAR_BEST_RATED)) @@ -485,6 +489,10 @@ class Config: return bool((self.config_default_show is not None) and (self.config_default_show & SIDEBAR_AUTHOR == SIDEBAR_AUTHOR)) + def show_publisher(self): + return bool((self.config_default_show is not None) and + (self.config_default_show & SIDEBAR_PUBLISHER == SIDEBAR_PUBLISHER)) + def show_best_rated_books(self): return bool((self.config_default_show is not None) and (self.config_default_show & SIDEBAR_BEST_RATED == SIDEBAR_BEST_RATED)) diff --git a/cps/web.py b/cps/web.py index b4132111..0f359fd6 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1415,6 +1415,31 @@ def author(book_id, page): title=name, author=author_info, other_books=other_books, page="author") +@app.route("/publisher") +@login_required_if_no_ano +def publisher_list(): + if current_user.show_publisher(): + entries = db.session.query(db.Publishers, func.count('books_publishers_link.book').label('count'))\ + .join(db.books_publishers_link).join(db.Books).filter(common_filters())\ + .group_by('books_publishers_link.publisher').order_by(db.Publishers.sort).all() + return render_title_template('list.html', entries=entries, folder='publisher', + title=_(u"Publisher list"), page="publisherlist") + else: + abort(404) + + +@app.route("/publisher/", defaults={'page': 1}) +@app.route('/publisher//') +@login_required_if_no_ano +def publisher(book_id, page): + entries, random, pagination = fill_indexpage(page, db.Books, db.Books.publishers.any(db.Publishers.id == book_id), + (db.Series.name, db.Books.series_index), db.books_series_link, db.Series) + + name = db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first().name + return render_title_template('index.html', random=random, entries=entries, pagination=pagination, + title=_(u"Publisher: %(name)s", name=name), page="publisher") + + def get_unique_other_books(library_books, author_books): # Get all identifiers (ISBN, Goodreads, etc) and filter author's books by that list so we show fewer duplicates # Note: Not all images will be shown, even though they're available on Goodreads.com. @@ -2774,6 +2799,8 @@ def profile(): content.sidebar_view += ub.SIDEBAR_BEST_RATED if "show_author" in to_save: content.sidebar_view += ub.SIDEBAR_AUTHOR + if "show_publisher" in to_save: + content.sidebar_view += ub.SIDEBAR_PUBLISHER if "show_read_and_unread" in to_save: content.sidebar_view += ub.SIDEBAR_READ_AND_UNREAD if "show_detail_random" in to_save: @@ -2884,6 +2911,8 @@ def view_configuration(): content.config_default_show = content.config_default_show + ub.SIDEBAR_RANDOM if "show_author" in to_save: content.config_default_show = content.config_default_show + ub.SIDEBAR_AUTHOR + if "show_publisher" in to_save: + content.config_default_show = content.config_default_show + ub.SIDEBAR_PUBLISHER if "show_best_rated" in to_save: content.config_default_show = content.config_default_show + ub.SIDEBAR_BEST_RATED if "show_read_and_unread" in to_save: From 6a007ec88158e1d076780cba1ae086ca0eeb31a2 Mon Sep 17 00:00:00 2001 From: Virgil Grigoras Date: Sun, 30 Sep 2018 18:42:48 +0200 Subject: [PATCH 3/5] fix indentations --- cps/templates/index.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cps/templates/index.xml b/cps/templates/index.xml index 97848b5e..d3caa3fa 100644 --- a/cps/templates/index.xml +++ b/cps/templates/index.xml @@ -39,7 +39,7 @@ {{ current_time }} {{_('Show Random Books')}} -{% if not current_user.is_anonymous %} + {% if not current_user.is_anonymous %} {{_('Read Books')}} @@ -47,7 +47,7 @@ {{ current_time }} {{_('Read Books')}} -{% endif %} + {% endif %} {{_('Unread Books')}} @@ -61,35 +61,35 @@ {{url_for('feed_authorindex')}} {{ current_time }} {{_('Books ordered by Author')}} - - + + {{_('Category list')}} {{url_for('feed_categoryindex')}} {{ current_time }} {{_('Books ordered by category')}} - - + + {{_('Series list')}} {{url_for('feed_seriesindex')}} {{ current_time }} {{_('Books ordered by series')}} - - + + {{_('Public Shelves')}} {{url_for('feed_shelfindex', public="public")}} {{ current_time }} {{_('Books organized in public shelfs, visible to everyone')}} - - {% if not current_user.is_anonymous %} - + + {% if not current_user.is_anonymous %} + {{_('Your Shelves')}} {{url_for('feed_shelfindex')}} {{ current_time }} {{_("User's own shelfs, only visible to the current user himself")}} - - {% endif %} + + {% endif %} From 1ac9b3d837dffa0dbc4b67a4e2b772e83441b6be Mon Sep 17 00:00:00 2001 From: Virgil Grigoras Date: Sun, 30 Sep 2018 18:48:36 +0200 Subject: [PATCH 4/5] Update OPDS-part to display publishers --- cps/templates/feed.xml | 3 +++ cps/templates/index.xml | 9 ++++++++- cps/web.py | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cps/templates/feed.xml b/cps/templates/feed.xml index 9454187b..82e92416 100644 --- a/cps/templates/feed.xml +++ b/cps/templates/feed.xml @@ -43,6 +43,9 @@ {{entry.authors[0].name}} + + {{entry.publishers[0].name}} + {{entry.language}} {% for tag in entry.tags %} {{url_for('feed_authorindex')}} {{ current_time }} {{_('Books ordered by Author')}} - + + + {{_('Publishers')}} + + {{url_for('feed_publisherindex')}} + {{ current_time }} + {{_('Books ordered by publisher')}} + {{_('Category list')}} diff --git a/cps/web.py b/cps/web.py index 0f359fd6..4a397083 100644 --- a/cps/web.py +++ b/cps/web.py @@ -752,6 +752,26 @@ def feed_author(book_id): return render_xml_template('feed.xml', entries=entries, pagination=pagination) +@app.route("/opds/publisher") +@requires_basic_auth_if_no_ano +def feed_publisherindex(): + off = request.args.get("offset") or 0 + entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\ + .group_by('books_publishers_link.publisher').order_by(db.Publishers.sort).limit(config.config_books_per_page).offset(off) + pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, + len(db.session.query(db.Publishers).all())) + return render_xml_template('feed.xml', listelements=entries, folder='feed_publisher', pagination=pagination) + + +@app.route("/opds/publisher/") +@requires_basic_auth_if_no_ano +def feed_publisher(book_id): + off = request.args.get("offset") or 0 + entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, db.Books.publishers.any(db.Publishers.id == book_id), [db.Books.timestamp.desc()]) + return render_xml_template('feed.xml', entries=entries, pagination=pagination) + + @app.route("/opds/category") @requires_basic_auth_if_no_ano def feed_categoryindex(): From a798dc94a968a6a1bc19a3c858593441676dbea8 Mon Sep 17 00:00:00 2001 From: Virgil Grigoras Date: Mon, 1 Oct 2018 10:45:51 +0200 Subject: [PATCH 5/5] Satisfy "Codacy/PR Quality Review" --- cps/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index 4a397083..a6204968 100644 --- a/cps/web.py +++ b/cps/web.py @@ -533,7 +533,7 @@ def fill_indexpage(page, database, db_filter, order, *join): # if elements have to be deleted, because they are no longer used def modify_database_object(input_elements, db_book_object, db_object, db_session, db_type): # passing input_elements not as a list may lead to undesired results - if type(input_elements) is not list: + if not isinstance(input_elements, list): raise TypeError(str(input_elements) + " should be passed as a list") input_elements = [x for x in input_elements if x != '']