From 406d1c76c943cc5366246f58e159fb5a823d2bea Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 22 Apr 2019 19:11:25 +0200 Subject: [PATCH] Sorting and filtering of lists working (except file formats) Refactored and bugfixing show_cover Refactored import of helper in web.py Fix for displaying /me (gettext) throwing error 500 Fix get search results throwing error 500 Fix routing books_list for python2.7 Fix for "me" and "settings" pages Update sidebarview and list view --- cps/__init__.py | 5 - cps/db.py | 8 -- cps/helper.py | 87 ++++++----- cps/opds.py | 3 +- cps/static/js/filter_list.js | 144 +++++++++++++++++-- cps/templates/author.html | 4 - cps/templates/book_edit.html | 4 - cps/templates/detail.html | 6 +- cps/templates/index.html | 8 -- cps/templates/list.html | 9 +- cps/templates/listenmp3.html | 4 +- cps/templates/shelf.html | 4 - cps/ub.py | 5 +- cps/uploader.py | 2 + cps/web.py | 269 +++++++++++++++++------------------ 15 files changed, 332 insertions(+), 230 deletions(-) diff --git a/cps/__init__.py b/cps/__init__.py index 61af61e2..87a9026f 100755 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -81,10 +81,8 @@ except cPickle.UnpicklingError as error: print("Can't read file cps/translations/iso639.pickle: %s" % error) sys.exit(1) - searched_ids = {} - from worker import WorkerThread global_WorkerThread = WorkerThread() @@ -110,8 +108,6 @@ def create_app(): app.logger.setLevel(config.config_log_level) app.logger.info('Starting Calibre Web...') - # logging.getLogger("uploader").addHandler(file_handler) - # logging.getLogger("uploader").setLevel(config.config_log_level) Principal(app) lm.init_app(app) app.secret_key = os.getenv('SECRET_KEY', 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT') @@ -119,7 +115,6 @@ def create_app(): db.setup_db() babel.init_app(app) global_WorkerThread.start() - return app @babel.localeselector diff --git a/cps/db.py b/cps/db.py index cd16e873..b220dcf2 100755 --- a/cps/db.py +++ b/cps/db.py @@ -46,14 +46,6 @@ def title_sort(title): return title.strip() -def lcase(s): - return unidecode.unidecode(s.lower()) - - -def ucase(s): - return s.upper() - - Base = declarative_base() books_authors_link = Table('books_authors_link', Base.metadata, diff --git a/cps/helper.py b/cps/helper.py index 91199f09..4df27fcf 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -42,6 +42,7 @@ from sqlalchemy.sql.expression import true, and_, false, text, func from iso639 import languages as isoLanguages from pagination import Pagination from werkzeug.datastructures import Headers +import json try: import gdriveutils as gd @@ -150,6 +151,7 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False): e_mail, user_name, _(u"Registration e-mail for user: %(name)s", name=user_name), text) return + def check_send_to_kindle(entry): """ returns all available book formats for sending to Kindle @@ -444,24 +446,33 @@ def delete_book(book, calibrepath, book_format): return delete_book_file(book, calibrepath, book_format) -def get_book_cover(cover_path): - if config.config_use_google_drive: - try: - if not gd.is_gdrive_ready(): - return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg") - path=gd.get_cover_via_gdrive(cover_path) - if path: - return redirect(path) +def get_book_cover(book_id): + book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + if book.has_cover: + + if config.config_use_google_drive: + try: + if not gd.is_gdrive_ready(): + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg") + path=gd.get_cover_via_gdrive(book.path) + if path: + return redirect(path) + else: + app.logger.error(book.path + '/cover.jpg not found on Google Drive') + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg") + except Exception as e: + app.logger.error("Error Message: " + e.message) + app.logger.exception(e) + # traceback.print_exc() + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg") + else: + cover_file_path = os.path.join(config.config_calibre_dir, book.path) + if os.path.isfile(os.path.join(cover_file_path, "cover.jpg")): + return send_from_directory(cover_file_path, "cover.jpg") else: - app.logger.error(cover_path + '/cover.jpg not found on Google Drive') - return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg") - except Exception as e: - app.logger.error("Error Message: " + e.message) - app.logger.exception(e) - # traceback.print_exc() - return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg") + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg") else: - return send_from_directory(os.path.join(config.config_calibre_dir, cover_path), "cover.jpg") + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg") # saves book cover from url @@ -698,24 +709,29 @@ def fill_indexpage(page, database, db_filter, order, *join): return entries, randm, pagination +def get_typeahead(database, query, replace=('','')): + db.session.connection().connection.connection.create_function("lower", 1, lcase) + entries = db.session.query(database).filter(db.func.lower(database.name).ilike("%" + query + "%")).all() + json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) + return json_dumps + # read search results from calibre-database and return it (function is used for feed and simple search def get_search_results(term): - def get_search_results(term): - db.session.connection().connection.connection.create_function("lower", 1, db.lcase) - q = list() - authorterms = re.split("[, ]+", term) - for authorterm in authorterms: - q.append(db.Books.authors.any(db.func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) - - db.Books.authors.any(db.func.lower(db.Authors.name).ilike("%" + term + "%")) - - return db.session.query(db.Books).filter(common_filters()).filter( - db.or_(db.Books.tags.any(db.func.lower(db.Tags.name).ilike("%" + term + "%")), - db.Books.series.any(db.func.lower(db.Series.name).ilike("%" + term + "%")), - db.Books.authors.any(and_(*q)), - db.Books.publishers.any(db.func.lower(db.Publishers.name).ilike("%" + term + "%")), - db.func.lower(db.Books.title).ilike("%" + term + "%") - )).all() + db.session.connection().connection.connection.create_function("lower", 1, lcase) + q = list() + authorterms = re.split("[, ]+", term) + for authorterm in authorterms: + q.append(db.Books.authors.any(db.func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) + + db.Books.authors.any(db.func.lower(db.Authors.name).ilike("%" + term + "%")) + + return db.session.query(db.Books).filter(common_filters()).filter( + db.or_(db.Books.tags.any(db.func.lower(db.Tags.name).ilike("%" + term + "%")), + db.Books.series.any(db.func.lower(db.Series.name).ilike("%" + term + "%")), + db.Books.authors.any(and_(*q)), + db.Books.publishers.any(db.func.lower(db.Publishers.name).ilike("%" + term + "%")), + db.func.lower(db.Books.title).ilike("%" + term + "%") + )).all() 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 @@ -774,3 +790,10 @@ def get_download_link(book_id, book_format): return do_download_file(book, book_format, data, headers) else: abort(404) + + + +############### Database Helper functions + +def lcase(s): + return unidecode.unidecode(s.lower()) diff --git a/cps/opds.py b/cps/opds.py index e5ae906f..a7a75de9 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -308,8 +308,7 @@ def render_xml_template(*args, **kwargs): @opds.route("/opds/cover/") @requires_basic_auth_if_no_ano def feed_get_cover(book_id): - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() - return helper.get_book_cover(book.path) + return helper.get_book_cover(book_id) @opds.route("/opds/readbooks/") @requires_basic_auth_if_no_ano diff --git a/cps/static/js/filter_list.js b/cps/static/js/filter_list.js index 57b195c8..6f337777 100644 --- a/cps/static/js/filter_list.js +++ b/cps/static/js/filter_list.js @@ -16,23 +16,88 @@ */ var direction = 0; // Descending order +var sort = 0; // Show sorted entries + +$("#sort_name").click(function() { + var count = 0; + var index = 0; + var store; + // Append 2nd half of list to first half for easier processing + var cnt = $("#second").contents(); + $("#list").append(cnt); + // Count no of elements + var listItems = $('#list').children(".row"); + var listlength = listItems.length; + // check for each element if its Starting character matches + $(".row").each(function() { + if ( sort === 1) { + store = this.attributes["data-name"]; + } else { + store = this.attributes["data-id"]; + } + $(this).find('a').html(store.value); + if($(this).css("display") != "none") { + count++; + } + }); + /*listItems.sort(function(a,b){ + return $(a).children()[1].innerText.localeCompare($(b).children()[1].innerText) + });*/ + // Find count of middle element + if (count > 20) { + var middle = parseInt(count / 2) + (count % 2); + // search for the middle of all visibe elements + $(".row").each(function() { + index++; + if($(this).css("display") != "none") { + middle--; + if (middle <= 0) { + return false; + } + } + }); + // Move second half of visible elements + $("#second").append(listItems.slice(index, listlength)); + } + sort = (sort + 1) % 2; +}); $("#desc").click(function() { if (direction === 0) { return; } + var index = 0; var list = $('#list'); var second = $('#second'); + // var cnt = ; + list.append(second.contents()); var listItems = list.children(".row"); var reversed, elementLength, middle; - Array.prototype.push.apply(listItems,second.children(".row")) reversed = listItems.get().reverse(); elementLength = reversed.length; + // Find count of middle element + var count = $(".row:visible").length; + if (count > 20) { + var middle = parseInt(count / 2) + (count % 2); - middle = parseInt(elementLength / 2) + (elementLength % 2); + //var middle = parseInt(count / 2) + (count % 2); + // search for the middle of all visible elements + $(reversed).each(function() { + index++; + if($(this).css("display") != "none") { + middle--; + if (middle <= 0) { + return false; + } + } + }); - list.append(reversed.slice(0, middle)); - second.append(reversed.slice(middle,elementLength)); + list.append(reversed.slice(0, index)); + second.append(reversed.slice(index,elementLength)); + } + else { + list.append(reversed.slice(0, elementLength)); + } direction = 0; }); @@ -41,34 +106,91 @@ $("#asc").click(function() { if (direction === 1) { return; } + var index = 0; var list = $("#list"); var second = $('#second'); + list.append(second.contents()); var listItems = list.children(".row"); - Array.prototype.push.apply(listItems,second.children(".row")); reversed = listItems.get().reverse(); elementLength = reversed.length; - middle = parseInt(elementLength / 2) + (elementLength % 2); - list.append(reversed.slice(0, middle)); - second.append(reversed.slice(middle,elementLength)); + // Find count of middle element + var count = $(".row:visible").length; + if (count > 20) { + var middle = parseInt(count / 2) + (count % 2); + + //var middle = parseInt(count / 2) + (count % 2); + // search for the middle of all visible elements + $(reversed).each(function() { + index++; + if($(this).css("display") != "none") { + middle--; + if (middle <= 0) { + return false; + } + } + }); + + // middle = parseInt(elementLength / 2) + (elementLength % 2); + + list.append(reversed.slice(0, index)); + second.append(reversed.slice(index,elementLength)); + } else { + list.append(reversed.slice(0, elementLength)); + } direction = 1; }); $("#all").click(function() { - $(".row").each(function() { + var cnt = $("#second").contents(); + $("#list").append(cnt); + // Find count of middle element + var listItems = $('#list').children(".row"); + var listlength = listItems.length; + var middle = parseInt(listlength / 2) + (listlength % 2); + // go through all elements and make them visible + listItems.each(function() { $(this).show(); }); + // Move second half of all elements + if (listlength > 20) { + $("#second").append(listItems.slice(middle,listlength)); + } }); $(".char").click(function() { var character = this.innerText; - + var count = 0; + var index = 0; + // Append 2nd half of list to first half for easier processing + var cnt = $("#second").contents(); + $("#list").append(cnt); + // Count no of elements + var listItems = $('#list').children(".row"); + var listlength = listItems.length; + // check for each element if its Starting character matches $(".row").each(function() { if (this.attributes["data-id"].value.charAt(0).toUpperCase() !== character) { $(this).hide(); } else { $(this).show(); + count++; } }); - + if (count > 20) { + // Find count of middle element + var middle = parseInt(count / 2) + (count % 2); + // search for the middle of all visibe elements + $(".row").each(function() { + index++; + if($(this).css("display") != "none") { + middle--; + if (middle <= 0) { + return false; + } + } + }); + // Move second half of visible elements + $("#second").append(listItems.slice(index,listlength)); + } }); diff --git a/cps/templates/author.html b/cps/templates/author.html index 4f6d54e2..c3dad34c 100644 --- a/cps/templates/author.html +++ b/cps/templates/author.html @@ -40,11 +40,7 @@
diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index 06e2a98a..9305e204 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -5,11 +5,7 @@
- {% if book.has_cover %} {{ book.title }} - {% else %} - {{ book.title }} - {% endif %}
{% if g.user.role_delete_books() %}
diff --git a/cps/templates/detail.html b/cps/templates/detail.html index b9765917..1c7d901b 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -4,11 +4,7 @@
- {% if entry.has_cover %} {{ entry.title }} - {% else %} - {{ entry.title }} - {% endif %}
@@ -150,7 +146,7 @@ {% for tag in entry.tags %} - {{tag.name}} + {{tag.name}} {%endfor%}

diff --git a/cps/templates/index.html b/cps/templates/index.html index 8063ad81..25c23106 100755 --- a/cps/templates/index.html +++ b/cps/templates/index.html @@ -8,11 +8,7 @@ web. {% for entry in random %}
@@ -75,11 +71,7 @@ web. {% for entry in random %}
diff --git a/cps/templates/list.html b/cps/templates/list.html index f18d376b..b9997375 100644 --- a/cps/templates/list.html +++ b/cps/templates/list.html @@ -3,6 +3,11 @@

{{_(title)}}

{% endif %} -
+
{{entry.count}}
-
+
{% if entry.name %}
{% for number in range(entry.name) %} diff --git a/cps/templates/listenmp3.html b/cps/templates/listenmp3.html index 3ecf3e27..5b07485c 100644 --- a/cps/templates/listenmp3.html +++ b/cps/templates/listenmp3.html @@ -35,9 +35,7 @@

{{ entry.title }}

- {% if entry.has_cover %} - {{ entry.title }} {% else %} - {{ entry.title }} {% endif %} + {{ entry.title }}
{% if entry.ratings.__len__() > 0 %} diff --git a/cps/templates/shelf.html b/cps/templates/shelf.html index 24aff3b4..097ed3ca 100644 --- a/cps/templates/shelf.html +++ b/cps/templates/shelf.html @@ -18,11 +18,7 @@
diff --git a/cps/ub.py b/cps/ub.py index bb055344..421ad218 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -100,7 +100,10 @@ Base = declarative_base() def get_sidebar_config(kwargs=None): kwargs = kwargs or [] if 'content' in kwargs: - content = not kwargs['content'].role_anonymous() + if not isinstance(kwargs['content'], Settings): + content = not kwargs['content'].role_anonymous() + else: + content = False else: content = False sidebar = list() diff --git a/cps/uploader.py b/cps/uploader.py index fc766cf8..4a5ab26f 100644 --- a/cps/uploader.py +++ b/cps/uploader.py @@ -67,6 +67,8 @@ try: from PIL import __version__ as PILversion use_PIL = True except ImportError: + app.logger.warning('cannot import Pillow, using png and webp images as cover will not work: %s', e) + use_generic_pdf_cover = True use_PIL = False diff --git a/cps/web.py b/cps/web.py index e5951799..6393dd9f 100644 --- a/cps/web.py +++ b/cps/web.py @@ -21,31 +21,30 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from cps import mimetypes, global_WorkerThread, searched_ids -from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for -from werkzeug.exceptions import default_exceptions -import helper +from cps import mimetypes, global_WorkerThread, searched_ids, lm, babel, ub, config, get_locale, language_table, app, db from helper import common_filters, get_search_results, fill_indexpage, speaking_language, check_valid_domain, \ - order_authors -import os -from sqlalchemy.exc import IntegrityError + order_authors, get_typeahead, render_task_status, json_serial, get_unique_other_books, get_cc_columns, \ + get_book_cover, get_download_link, send_mail, generate_random_password, send_registration_mail, \ + check_send_to_kindle, check_read_formats, lcase +from flask import render_template, request, redirect, send_from_directory, make_response, g, flash, abort, url_for from flask_login import login_user, logout_user, login_required, current_user -from flask_babel import gettext as _ +from werkzeug.exceptions import default_exceptions from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.datastructures import Headers +from redirect import redirect_back +from pagination import Pagination from babel import Locale as LC from babel.dates import format_date from babel.core import UnknownLocaleError -import base64 +from flask_babel import gettext as _ from sqlalchemy.sql.expression import text, func, true, false, not_ +from sqlalchemy.exc import IntegrityError +import base64 +import os.path import json import datetime import isoLanguages -import os.path import gdriveutils -from redirect import redirect_back -from cps import lm, babel, ub, config, get_locale, language_table, app, db -from pagination import Pagination feature_support = dict() @@ -252,8 +251,8 @@ def before_request(): @login_required def get_email_status_json(): tasks = global_WorkerThread.get_taskstatus() - answer = helper.render_task_status(tasks) - js = json.dumps(answer, default=helper.json_serial) + answer = render_task_status(tasks) + js = json.dumps(answer, default=json_serial) response = make_response(js) response.headers["Content-Type"] = "application/json; charset=utf-8" return response @@ -366,12 +365,6 @@ def get_comic_book(book_id, book_format, page): # ################################### Typeahead ################################################################## -def get_typeahead(database, query, replace=('','')): - db.session.connection().connection.connection.create_function("lower", 1, db.lcase) - entries = db.session.query(database).filter(db.func.lower(database.name).ilike("%" + query + "%")).all() - json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) - return json_dumps - @web.route("/get_authors_json") @login_required_if_no_ano @@ -422,7 +415,7 @@ def get_matching_tags(): tag_dict = {'tags': []} if request.method == "GET": q = db.session.query(db.Books) - db.session.connection().connection.connection.create_function("lower", 1, db.lcase) + db.session.connection().connection.connection.create_function("lower", 1, lcase) author_input = request.args.get('author_name') title_input = request.args.get('book_title') include_tag_inputs = request.args.getlist('include_tag') @@ -497,6 +490,16 @@ def books_list(data, sort, book_id, page): return render_hot_books(page) elif data == "author": return render_author_books(page, book_id, order) + elif data == "publisher": + return render_publisher_books(page, book_id, order) + elif data == "series": + return render_series_books(page, book_id, order) + elif data == "ratings": + return render_ratings_books(page, book_id, order) + elif data == "formats": + return render_formats_books(page, book_id, order) + elif data == "category": + return render_category_books(page, book_id, order) else: entries, random, pagination = fill_indexpage(page, db.Books, True, order) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, @@ -532,24 +535,6 @@ def render_hot_books(page): abort(404) -@web.route("/author") -@login_required_if_no_ano -def author_list(): - if current_user.check_visibility(ub.SIDEBAR_AUTHOR): - entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count'))\ - .join(db.books_authors_link).join(db.Books).filter(common_filters())\ - .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).all() - charlist = db.session.query(func.upper(func.substr(db.Authors.sort,1,1)).label('char')) \ - .join(db.books_authors_link).join(db.Books).filter(common_filters()) \ - .group_by(func.upper(func.substr(db.Authors.sort,1,1))).all() - for entry in entries: - entry.Authors.name = entry.Authors.name.replace('|', ',') - return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, - title=u"Author list", page="authorlist", data='author') - else: - abort(404) - - # ToDo wrong order function def render_author_books(page, book_id, order): entries, __, pagination = fill_indexpage(page, db.Books, db.Books.authors.any(db.Authors.id == book_id), @@ -566,7 +551,7 @@ def render_author_books(page, book_id, order): try: gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret) author_info = gc.find_author(author_name=name) - other_books = helper.get_unique_other_books(entries.all(), author_info.books) + other_books = get_unique_other_books(entries.all(), author_info.books) except Exception: # Skip goodreads, if site is down/inaccessible app.logger.error('Goodreads website is down/inaccessible') @@ -575,6 +560,81 @@ def render_author_books(page, book_id, order): title=name, author=author_info, other_books=other_books, page="author") +def render_publisher_books(page, book_id, order): + publisher = db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first() + if publisher: + entries, random, pagination = fill_indexpage(page, db.Books, + db.Books.publishers.any(db.Publishers.id == book_id), + [db.Books.series_index, order[0]]) + return render_title_template('index.html', random=random, entries=entries, pagination=pagination, + title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher") + else: + abort(404) + + +def render_series_books(page, book_id, order): + name = db.session.query(db.Series).filter(db.Series.id == book_id).first() + if name: + entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.id == book_id), + [db.Books.series_index, order[0]]) + return render_title_template('index.html', random=random, pagination=pagination, entries=entries, + title=_(u"Series: %(serie)s", serie=name.name), page="series") + else: + abort(404) + + +def render_ratings_books(page, book_id, order): + if book_id <=5: + name = db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first() + entries, random, pagination = fill_indexpage(page, db.Books, db.Books.ratings.any(db.Ratings.id == book_id), + [db.Books.timestamp.desc(), order[0]]) + return render_title_template('index.html', random=random, pagination=pagination, entries=entries, + title=_(u"Rating: %(rating)s stars", rating=int(name.rating/2)), page="ratings") + else: + abort(404) + + +def render_formats_books(page, book_id, order): + name = db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first() + if name: + entries, random, pagination = fill_indexpage(page, db.Books, db.Books.data.any(db.Data.format == book_id.upper()), + [db.Books.timestamp.desc(), order[0]]) + return render_title_template('index.html', random=random, pagination=pagination, entries=entries, + title=_(u"File format: %(format)s", format=name.format), page="formats") + else: + abort(404) + + +def render_category_books(page, book_id, order): + name = db.session.query(db.Tags).filter(db.Tags.id == book_id).first() + if name: + entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id), + [db.Series.name, db.Books.series_index, order[0]], + db.books_series_link, db.Series) + return render_title_template('index.html', random=random, entries=entries, pagination=pagination, + title=_(u"Category: %(name)s", name=name.name), page="category") + else: + abort(404) + + +@web.route("/author") +@login_required_if_no_ano +def author_list(): + if current_user.check_visibility(ub.SIDEBAR_AUTHOR): + entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count'))\ + .join(db.books_authors_link).join(db.Books).filter(common_filters())\ + .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).all() + charlist = db.session.query(func.upper(func.substr(db.Authors.sort,1,1)).label('char')) \ + .join(db.books_authors_link).join(db.Books).filter(common_filters()) \ + .group_by(func.upper(func.substr(db.Authors.sort,1,1))).all() + for entry in entries: + entry.Authors.name = entry.Authors.name.replace('|', ',') + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, + title=u"Author list", page="authorlist", data='author') + else: + abort(404) + + @web.route("/publisher") @login_required_if_no_ano def publisher_list(): @@ -585,23 +645,8 @@ def publisher_list(): charlist = db.session.query(func.upper(func.substr(db.Publishers.name,1,1)).label('char')) \ .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Publishers.name,1,1))).all() - return render_title_template('list.html', entries=entries, folder='web.publisher', charlist=charlist, - title=_(u"Publisher list"), page="publisherlist") - else: - abort(404) - - -@web.route("/publisher/", defaults={'page': 1}) -@web.route('/publisher//') -@login_required_if_no_ano -def publisher(book_id, page): - publisher = db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first() - if publisher: - entries, random, pagination = fill_indexpage(page, db.Books, - db.Books.publishers.any(db.Publishers.id == book_id), - [db.Books.series_index]) - return render_title_template('index.html', random=random, entries=entries, pagination=pagination, - title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher") + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, + title=_(u"Publisher list"), page="publisherlist", data="publisher") else: abort(404) @@ -616,22 +661,8 @@ def series_list(): charlist = db.session.query(func.upper(func.substr(db.Series.sort,1,1)).label('char')) \ .join(db.books_series_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Series.sort,1,1))).all() - return render_title_template('list.html', entries=entries, folder='web.series', charlist=charlist, - title=_(u"Series list"), page="serieslist") - else: - abort(404) - - -@web.route("/series//", defaults={'page': 1}) -@web.route("/series//") -@login_required_if_no_ano -def series(book_id, page): - name = db.session.query(db.Series).filter(db.Series.id == book_id).first() - if name: - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.id == book_id), - [db.Books.series_index]) - return render_title_template('index.html', random=random, pagination=pagination, entries=entries, - title=_(u"Series: %(serie)s", serie=name.name), page="series") + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, + title=_(u"Series list"), page="serieslist", data="series") else: abort(404) @@ -644,22 +675,8 @@ def ratings_list(): (db.Ratings.rating/2).label('name'))\ .join(db.books_ratings_link).join(db.Books).filter(common_filters())\ .group_by(text('books_ratings_link.rating')).order_by(db.Ratings.rating).all() - return render_title_template('list.html', entries=entries, folder='web.ratings', charlist=list(), - title=_(u"Ratings list"), page="ratingslist") - else: - abort(404) - - -@web.route("/ratings//", defaults={'page': 1}) -@web.route("/ratings//") -@login_required_if_no_ano -def ratings(book_id, page): - if book_id <=5: - name = db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first() - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.ratings.any(db.Ratings.id == book_id), - [db.Books.timestamp.desc()]) - return render_title_template('index.html', random=random, pagination=pagination, entries=entries, - title=_(u"Rating: %(rating)s stars", rating=int(name.rating/2)), page="ratings") + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(), + title=_(u"Ratings list"), page="ratingslist", data="ratings") else: abort(404) @@ -671,22 +688,8 @@ def formats_list(): entries = db.session.query(db.Data, func.count('data.book').label('count'),db.Data.format.label('format'))\ .join(db.Books).filter(common_filters())\ .group_by(db.Data.format).order_by(db.Data.format).all() - return render_title_template('list.html', entries=entries, folder='web.formats', charlist=list(), - title=_(u"File formats list"), page="formatslist") - else: - abort(404) - - -@web.route("/formats//", defaults={'page': 1}) -@web.route("/formats//") -@login_required_if_no_ano -def formats(book_id, page): - name = db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first() - if name: - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.data.any(db.Data.format == book_id.upper()), - [db.Books.timestamp.desc()]) - return render_title_template('index.html', random=random, pagination=pagination, entries=entries, - title=_(u"File format: %(format)s", format=name.format), page="formats") + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(), + title=_(u"File formats list"), page="formatslist", data="formats") else: abort(404) @@ -714,7 +717,7 @@ def language_overview(): func.count('books_languages_link.book').label('bookcount')).group_by( text('books_languages_link.lang_code')).all() return render_title_template('languages.html', languages=languages, lang_counter=lang_counter, - charlist=charlist, title=_(u"Available languages"), page="langlist") + charlist=charlist, title=_(u"Available languages"), page="langlist", data="language") else: abort(404) @@ -747,33 +750,20 @@ def category_list(): charlist = db.session.query(func.upper(func.substr(db.Tags.name,1,1)).label('char')) \ .join(db.books_tags_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Tags.name,1,1))).all() - return render_title_template('list.html', entries=entries, folder='web.category', charlist=charlist, - title=_(u"Category list"), page="catlist") + return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, + title=_(u"Category list"), page="catlist", data="category") else: abort(404) -@web.route("/category/", defaults={'page': 1}) -@web.route('/category//') -@login_required_if_no_ano -def category(book_id, page): - name = db.session.query(db.Tags).filter(db.Tags.id == book_id).first() - if name: - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id), - (db.Series.name, db.Books.series_index),db.books_series_link, - db.Series) - return render_title_template('index.html', random=random, entries=entries, pagination=pagination, - title=_(u"Category: %(name)s", name=name.name), page="category") - else: - abort(404) - +# ################################### Task functions ################################################################ @web.route("/tasks") @login_required def get_tasks_status(): # if current user admin, show all email, otherwise only own emails tasks = global_WorkerThread.get_taskstatus() - answer = helper.render_task_status(tasks) + answer = render_task_status(tasks) return render_title_template('tasks.html', entries=answer, title=_(u"Tasks"), page="tasks") @@ -798,8 +788,8 @@ def search(): @login_required_if_no_ano def advanced_search(): # Build custom columns names - cc = helper.get_cc_columns() - db.session.connection().connection.connection.create_function("lower", 1, db.lcase) + cc = get_cc_columns() + db.session.connection().connection.connection.create_function("lower", 1, lcase) q = db.session.query(db.Books) include_tag_inputs = request.args.getlist('include_tag') @@ -978,8 +968,7 @@ def render_read_books(page, are_read, as_xml=False, order=None): @web.route("/cover/") @login_required_if_no_ano def get_cover(book_id): - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() - return helper.get_book_cover(book.path) + return get_book_cover(book_id) @web.route("/show//") @@ -1007,7 +996,7 @@ def serve_book(book_id, book_format): @login_required_if_no_ano @download_required def download_link(book_id, book_format, anyname): - return helper.get_download_link(book_id, book_format) + return get_download_link(book_id, book_format) @web.route('/send///') @@ -1018,7 +1007,7 @@ def send_to_kindle(book_id, book_format, convert): if settings.get("mail_server", "mail.example.com") == "mail.example.com": flash(_(u"Please configure the SMTP mail settings first..."), category="error") elif current_user.kindle_mail: - result = helper.send_mail(book_id, book_format, convert, current_user.kindle_mail, config.config_calibre_dir, + result = send_mail(book_id, book_format, convert, current_user.kindle_mail, config.config_calibre_dir, current_user.nickname) if result is None: flash(_(u"Book successfully queued for sending to %(kindlemail)s", kindlemail=current_user.kindle_mail), @@ -1055,7 +1044,7 @@ def register(): if check_valid_domain(to_save["email"]): content.nickname = to_save["nickname"] content.email = to_save["email"] - password = helper.generate_random_password() + password = generate_random_password() content.password = generate_password_hash(password) content.role = config.config_default_role content.sidebar_view = config.config_default_show @@ -1065,7 +1054,7 @@ def register(): ub.session.commit() if feature_support['oauth']: register_user_with_oauth(content) - helper.send_registration_mail(to_save["email"], to_save["nickname"], password) + send_registration_mail(to_save["email"], to_save["nickname"], password) except Exception: ub.session.rollback() flash(_(u"An unknown error occurred. Please try again later."), category="error") @@ -1120,8 +1109,6 @@ def login(): app.logger.info('Login failed for user "' + form['username'] + '" IP-adress: ' + ipAdress) flash(_(u"Wrong Username or Password"), category="error") - # next_url = request.args.get('next') - # if next_url is None or not is_safe_url(next_url): next_url = url_for('web.index') return render_title_template('login.html', title=_(u"login"), next_url=next_url, config=config, page="login") @@ -1223,7 +1210,7 @@ def token_verified(): @web.route("/me", methods=["GET", "POST"]) @login_required def profile(): - # content = ub.session.query(ub.User).filter(ub.User.id == int(current_user.id)).first() + global _ downloads = list() languages = speaking_language() translations = babel.list_translations() + [LC('en')] @@ -1282,9 +1269,9 @@ def profile(): registered_oauth=oauth_check, oauth_status=oauth_status) flash(_(u"Profile updated"), category="success") return render_title_template("user_edit.html", translations=translations, profile=1, languages=languages, - content=current_user, downloads=downloads, title=_(u"%(name)s's profile", - name=current_user.nickname), page="me", registered_oauth=oauth_check, - oauth_status=oauth_status) + content=current_user, downloads=downloads, title= _(u"%(name)s's profile", + name=current_user.nickname), + page="me", registered_oauth=oauth_check, oauth_status=oauth_status) # ###################################Show single book ################################################################## @@ -1344,7 +1331,7 @@ def show_book(book_id): except UnknownLocaleError: entries.languages[index].language_name = _( isoLanguages.get(part3=entries.languages[index].lang_code).name) - cc = helper.get_cc_columns() + cc = get_cc_columns() book_in_shelfs = [] shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).all() for entry in shelfs: @@ -1371,8 +1358,8 @@ def show_book(book_id): entries = order_authors(entries) - kindle_list = helper.check_send_to_kindle(entries) - reader_list = helper.check_read_formats(entries) + kindle_list = check_send_to_kindle(entries) + reader_list = check_read_formats(entries) audioentries = [] for media_format in entries.data: