From 018f3ca250b38d6f2c4eba1c73e3f40225660075 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Mon, 26 Jul 2021 07:52:01 +0200 Subject: [PATCH] Sort authors additionally to series and series_index (Fix #2001) Sqlalchemy version2 is now a global flag --- cps/constants.py | 3 +++ cps/db.py | 4 ++++ cps/kobo.py | 15 +++++++-------- cps/shelf.py | 10 +++++++--- cps/web.py | 30 ++++++++++++++++++++---------- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/cps/constants.py b/cps/constants.py index f8ee3721..a0daa515 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -20,6 +20,9 @@ from __future__ import division, print_function, unicode_literals import sys import os from collections import namedtuple +from sqlalchemy import __version__ as sql_version + +sqlalchemy_version2 = ([int(x) for x in sql_version.split('.')] >= [2,0,0]) # if installed via pip this variable is set to true (empty file with name .HOMEDIR present) HOME_CONFIG = os.path.isfile(os.path.join(os.path.dirname(os.path.abspath(__file__)), '.HOMEDIR')) diff --git a/cps/db.py b/cps/db.py index 3dd446bc..ed164afc 100644 --- a/cps/db.py +++ b/cps/db.py @@ -690,6 +690,8 @@ class CalibreDB(): randm = false() off = int(int(pagesize) * (page - 1)) query = self.session.query(database) + if len(join) == 6: + query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5]) if len(join) == 3: query = query.outerjoin(join[0], join[1]).outerjoin(join[2]) elif len(join) == 2: @@ -755,6 +757,8 @@ class CalibreDB(): for authorterm in authorterms: q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) query = self.session.query(Books) + if len(join) == 6: + query = query.outerjoin(join[0], join[1]).outerjoin(join[2]).outerjoin(join[3], join[4]).outerjoin(join[5]) if len(join) == 3: query = query.outerjoin(join[0], join[1]).outerjoin(join[2]) elif len(join) == 2: diff --git a/cps/kobo.py b/cps/kobo.py index 11381170..6952a692 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -44,11 +44,11 @@ from werkzeug.datastructures import Headers from sqlalchemy import func from sqlalchemy.sql.expression import and_, or_ from sqlalchemy.exc import StatementError -from sqlalchemy import __version__ as sql_version from sqlalchemy.sql import select import requests from . import config, logger, kobo_auth, db, calibre_db, helper, shelf as shelf_lib, ub +from .constants import sqlalchemy_version2 from .helper import get_download_link from .services import SyncToken as SyncToken from .web import download_required @@ -66,7 +66,6 @@ kobo_auth.register_url_value_preprocessor(kobo) log = logger.create() -sql2 = ([int(x) for x in sql_version.split('.')] >= [2,0,0]) def get_store_url_for_current_request(): # Programmatically modify the current url to point to the official Kobo store @@ -159,7 +158,7 @@ def HandleSyncRequest(): only_kobo_shelves = current_user.kobo_only_shelves_sync if only_kobo_shelves: - if sql2: + if sqlalchemy_version2: changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.BookShelf.date_added, @@ -183,7 +182,7 @@ def HandleSyncRequest(): .distinct() ) else: - if sql2: + if sqlalchemy_version2: changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.ArchivedBook.is_archived) else: changed_entries = calibre_db.session.query(db.Books, @@ -202,7 +201,7 @@ def HandleSyncRequest(): changed_entries = changed_entries.filter(db.Books.id > sync_token.books_last_id) reading_states_in_new_entitlements = [] - if sql2: + if sqlalchemy_version2: books = calibre_db.session.execute(changed_entries.limit(SYNC_ITEM_LIMIT)) else: books = changed_entries.limit(SYNC_ITEM_LIMIT) @@ -246,7 +245,7 @@ def HandleSyncRequest(): new_books_last_created = max(ts_created, new_books_last_created) - if sql2: + if sqlalchemy_version2: max_change = calibre_db.session.execute(changed_entries .filter(ub.ArchivedBook.is_archived) .order_by(func.datetime(ub.ArchivedBook.last_modified).desc()))\ @@ -260,7 +259,7 @@ def HandleSyncRequest(): new_archived_last_modified = max(new_archived_last_modified, max_change) # no. of books returned - if sql2: + if sqlalchemy_version2: entries = calibre_db.session.execute(changed_entries).all() book_count = len(entries) else: @@ -697,7 +696,7 @@ def sync_shelves(sync_token, sync_results, only_kobo_shelves=False): }) extra_filters.append(ub.Shelf.kobo_sync) - if sql2: + if sqlalchemy_version2: shelflist = ub.session.execute(select(ub.Shelf).outerjoin(ub.BookShelf).filter( or_(func.datetime(ub.Shelf.last_modified) > sync_token.tags_last_modified, func.datetime(ub.BookShelf.date_added) > sync_token.tags_last_modified), diff --git a/cps/shelf.py b/cps/shelf.py index 3416be9b..d232e850 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -380,7 +380,9 @@ def order_shelf(shelf_id): def change_shelf_order(shelf_id, order): - result = calibre_db.session.query(db.Books).join(ub.BookShelf, ub.BookShelf.book_id == db.Books.id) \ + result = calibre_db.session.query(db.Books).outerjoin(db.books_series_link, + db.Books.id == db.books_series_link.c.book)\ + .outerjoin(db.Series).join(ub.BookShelf, ub.BookShelf.book_id == db.Books.id) \ .filter(ub.BookShelf.shelf == shelf_id).order_by(*order).all() for index, entry in enumerate(result): book = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ @@ -410,9 +412,11 @@ def render_show_shelf(shelf_type, shelf_id, page_no, sort_param): if sort_param == 'old': change_shelf_order(shelf_id, [db.Books.timestamp]) if sort_param == 'authaz': - change_shelf_order(shelf_id, [db.Books.author_sort.asc()]) + change_shelf_order(shelf_id, [db.Books.author_sort.asc(), db.Series.name, db.Books.series_index]) if sort_param == 'authza': - change_shelf_order(shelf_id, [db.Books.author_sort.desc()]) + change_shelf_order(shelf_id, [db.Books.author_sort.desc(), + db.Series.name.desc(), + db.Books.series_index.desc()]) page = "shelf.html" pagesize = 0 else: diff --git a/cps/web.py b/cps/web.py index 056fa0d8..d9f634a4 100644 --- a/cps/web.py +++ b/cps/web.py @@ -360,9 +360,9 @@ def get_sort_function(sort, data): if sort == 'old': order = [db.Books.timestamp] if sort == 'authaz': - order = [db.Books.author_sort.asc()] + order = [db.Books.author_sort.asc(), db.Series.name, db.Books.series_index] if sort == 'authza': - order = [db.Books.author_sort.desc()] + order = [db.Books.author_sort.desc(), db.Series.name.desc(), db.Books.series_index.desc()] if sort == 'seriesasc': order = [db.Books.series_index.asc()] if sort == 'seriesdesc': @@ -410,7 +410,10 @@ def render_books_list(data, sort, book_id, page): return render_adv_search_results(term, offset, order, config.config_books_per_page) else: website = data or "newest" - entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order) + entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order, + db.books_series_link, + db.Books.id == db.books_series_link.c.book, + db.Series) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Books"), page=website) @@ -509,8 +512,10 @@ def render_author_books(page, author_id, order): flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"), category="error") return redirect(url_for("web.index")) - - author = calibre_db.session.query(db.Authors).get(author_id) + if constants.sqlalchemy_version2: + author = calibre_db.session.get(db.Authors, author_id) + else: + author = calibre_db.session.query(db.Authors).get(author_id) author_name = author.name.replace('|', ',') author_info = None @@ -713,7 +718,8 @@ def render_prepare_search_form(cc): def render_search_results(term, offset=None, order=None, limit=None): - entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit) + join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series + entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit, *join) return render_title_template('search.html', searchterm=term, pagination=pagination, @@ -775,8 +781,10 @@ def list_books(): order = [db.Publishers.name.asc()] if order == "asc" else [db.Publishers.name.desc()] join = db.books_publishers_link,db.Books.id == db.books_publishers_link.c.book, db.Publishers elif sort == "authors": - order = [db.Authors.name.asc()] if order == "asc" else [db.Authors.name.desc()] - join = db.books_authors_link,db.Books.id == db.books_authors_link.c.book, db.Authors + order = [db.Authors.name.asc(), db.Series.name, db.Books.series_index] if order == "asc" \ + else [db.Authors.name.desc(), db.Series.name.desc(), db.Books.series_index.desc()] + join = db.books_authors_link, db.Books.id == db.books_authors_link.c.book, db.Authors, \ + db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series elif sort == "languages": order = [db.Languages.lang_code.asc()] if order == "asc" else [db.Languages.lang_code.desc()] join = db.books_languages_link,db.Books.id == db.books_languages_link.c.book, db.Languages @@ -793,7 +801,7 @@ def list_books(): filtered_count = len(books) else: books = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).all() - entries = calibre_db.get_checkbox_sorted(books, state, off, limit,order) + entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order) elif search: entries, filtered_count, __ = calibre_db.get_search_results(search, off, order, limit, *join) else: @@ -1242,7 +1250,9 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): cc = get_cc_columns(filter_config_custom_read=True) calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase) - q = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(True)) + q = calibre_db.session.query(db.Books).outerjoin(db.books_series_link, db.Books.id == db.books_series_link.c.book)\ + .outerjoin(db.Series)\ + .filter(calibre_db.common_filters(True)) # parse multiselects to a complete dict tags = dict()