Commit 406d1c76 authored by Ozzieisaacs's avatar Ozzieisaacs

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
parent bfd0e87a
......@@ -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
......
......@@ -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,
......
......@@ -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())
......@@ -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
......
......@@ -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));
}
});
......@@ -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">
......
......@@ -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">
......
......@@ -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>
......
......@@ -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">
......
......@@ -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) %}
......
......@@ -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 %}
......
......@@ -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">
......
......@@ -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()
......
......@@ -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
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment