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
This commit is contained in:
Ozzieisaacs 2019-04-22 19:11:25 +02:00
parent bfd0e87a17
commit 406d1c76c9
15 changed files with 336 additions and 234 deletions

View File

@ -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

View File

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

View File

@ -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.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 + "%"))
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()
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())

View File

@ -308,8 +308,7 @@ def render_xml_template(*args, **kwargs):
@opds.route("/opds/cover/<book_id>")
@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

View File

@ -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));
}
});

View File

@ -40,11 +40,7 @@
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" />
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" />
{% endif %}
</a>
</div>
<div class="meta">

View File

@ -5,11 +5,7 @@
<div class="col-sm-3 col-lg-3 col-xs-12">
<div class="cover">
{% if book.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=book.id) }}" alt="{{ book.title }}"/>
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ book.title }}"/>
{% endif %}
</div>
{% if g.user.role_delete_books() %}
<div class="text-center">

View File

@ -4,11 +4,7 @@
<div class="row">
<div class="col-sm-3 col-lg-3 col-xs-5">
<div class="cover">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" />
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %}
</div>
</div>
<div class="col-sm-9 col-lg-9 book-meta">
@ -150,7 +146,7 @@
<span class="glyphicon glyphicon-tags"></span>
{% for tag in entry.tags %}
<a href="{{ url_for('web.category', book_id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
<a href="{{ url_for('web.books_list', data='category', sort='new', book_id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
{%endfor%}
</p>

View File

@ -8,11 +8,7 @@ web. {% for entry in random %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand">
<div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" />
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %}
</a>
</div>
<div class="meta">
@ -75,11 +71,7 @@ web. {% for entry in random %}
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books">
<div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}"/>
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %}
</a>
</div>
<div class="meta">

View File

@ -3,6 +3,11 @@
<h1 class="{{page}}">{{_(title)}}</h1>
<div class="filterheader hidden-xs hidden-sm">
{% if entries.__len__ %}
{% if not entries[0].sort %}
<button id="sort_name" class="btn btn-success"><b>B,A <-> A B</b></button>
{% endif %}
{% endif %}
<button id="desc" class="btn btn-success"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
<button id="asc" class="btn btn-success"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
{% if charlist|length %}
@ -21,9 +26,9 @@
</div>
<div id="second" class="col-xs-12 col-sm-6">
{% endif %}
<div class="row" data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
<div class="row" {% if entry[0].sort %}data-name="{{entry[0].name}}"{% endif %} data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{entry.count}}</span></div>
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for(folder, data=data, sort='new', book_id=entry.format )}}{% else %}{{url_for(folder, data=data, sort='new', book_id=entry[0].id )}}{% endif %}">
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for('web.books_list', data=data, sort='new', book_id=entry.format )}}{% else %}{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].id )}}{% endif %}">
{% if entry.name %}
<div class="rating">
{% for number in range(entry.name) %}

View File

@ -35,9 +35,7 @@
<h2>{{ entry.title }}</h2>
<div class="cover">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" /> {% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" /> {% endif %}
<img src="{{ url_for('web.get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="{{ entry.title }}" />
</div>
{% if entry.ratings.__len__() > 0 %}

View File

@ -18,11 +18,7 @@
<div class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover">
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %}
<img src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" />
{% else %}
<img src="{{ url_for('static', filename='generic_cover.jpg') }}" alt="{{ entry.title }}" />
{% endif %}
</a>
</div>
<div class="meta">

View File

@ -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()

View File

@ -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

View File

@ -21,31 +21,30 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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,6 +535,88 @@ def render_hot_books(page):
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),
[db.Series.name, db.Books.series_index, order[0]], db.books_series_link, db.Series)
if entries is None:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect(url_for("web.index"))
name = db.session.query(db.Authors).filter(db.Authors.id == book_id).first().name.replace('|', ',')
author_info = None
other_books = []
if feature_support['goodreads'] and config.config_use_goodreads:
try:
gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret)
author_info = gc.find_author(author_name=name)
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')
return render_title_template('author.html', entries=entries, pagination=pagination,
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():
@ -550,31 +635,6 @@ def author_list():
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),
[db.Series.name, db.Books.series_index, order[0]], db.books_series_link, db.Series)
if entries is None:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect(url_for("web.index"))
name = db.session.query(db.Authors).filter(db.Authors.id == book_id).first().name.replace('|', ',')
author_info = None
other_books = []
if feature_support['goodreads'] and config.config_use_goodreads:
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)
except Exception:
# Skip goodreads, if site is down/inaccessible
app.logger.error('Goodreads website is down/inaccessible')
return render_title_template('author.html', entries=entries, pagination=pagination,
title=name, author=author_info, other_books=other_books, page="author")
@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/<int:book_id>", defaults={'page': 1})
@web.route('/publisher/<int:book_id>/<int:page>')
@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/<int:book_id>/", defaults={'page': 1})
@web.route("/series/<int:book_id>/<int:page>")
@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/<int:book_id>/", defaults={'page': 1})
@web.route("/ratings/<int:book_id>/<int:page>")
@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/<book_id>/", defaults={'page': 1})
@web.route("/formats/<book_id>/<int:page>")
@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/<int:book_id>", defaults={'page': 1})
@web.route('/category/<int:book_id>/<int:page>')
@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/<int:book_id>")
@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/<book_id>/<book_format>")
@ -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/<int:book_id>/<book_format>/<int:convert>')
@ -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: