From 5f0660a4e57da6555f0d6fbbbbd41dcde11a7380 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sat, 23 May 2020 10:16:29 +0200 Subject: [PATCH] Refactored helper.py and db.py --- cps/__init__.py | 2 - cps/admin.py | 12 ++-- cps/db.py | 173 +++++++++++++++++++++++++++++++++++++++++++-- cps/editbooks.py | 19 ++--- cps/helper.py | 169 ++++---------------------------------------- cps/jinjia.py | 7 -- cps/kobo.py | 12 ++-- cps/opds.py | 131 +++++++++++++++++++--------------- cps/shelf.py | 9 ++- cps/web.py | 179 ++++++++++++++++++++++++++--------------------- cps/worker.py | 14 ++-- 11 files changed, 382 insertions(+), 345 deletions(-) diff --git a/cps/__init__.py b/cps/__init__.py index 04ff4431..d557649c 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -37,8 +37,6 @@ from . import config_sql, logger, cache_buster, cli, ub, db from .reverseproxy import ReverseProxied from .server import WebServer -# import queue -# queue = queue.Queue() mimetypes.init() mimetypes.add_type('application/xhtml+xml', '.xhtml') diff --git a/cps/admin.py b/cps/admin.py index 9d593954..947d0087 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -39,7 +39,7 @@ from sqlalchemy.sql.expression import func from . import constants, logger, helper, services from . import db, calibre_db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils -from .helper import speaking_language, check_valid_domain, send_test_mail, reset_password, generate_password_hash +from .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash from .gdriveutils import is_gdrive_ready, gdrive_support from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano @@ -149,9 +149,9 @@ def configuration(): @admin_required def view_configuration(): readColumn = calibre_db.session.query(db.Custom_Columns)\ - .filter(and_(db.Custom_Columns.datatype == 'bool',db.Custom_Columns.mark_for_delete == 0)).all() + .filter(and_(db.Custom_Columns.datatype == 'bool', db.Custom_Columns.mark_for_delete == 0)).all() restrictColumns= calibre_db.session.query(db.Custom_Columns)\ - .filter(and_(db.Custom_Columns.datatype == 'text',db.Custom_Columns.mark_for_delete == 0)).all() + .filter(and_(db.Custom_Columns.datatype == 'text', db.Custom_Columns.mark_for_delete == 0)).all() return render_title_template("config_view_edit.html", conf=config, readColumns=readColumn, restrictColumns=restrictColumns, title=_(u"UI Configuration"), page="uiconfig") @@ -878,7 +878,7 @@ def _handle_edit_user(to_save, content,languages, translations, kobo_support, do @admin_required def new_user(): content = ub.User() - languages = speaking_language() + languages = calibre_db.speaking_language() translations = [LC('en')] + babel.list_translations() kobo_support = feature_support['kobo'] and config.config_kobo_sync if request.method == "POST": @@ -942,11 +942,11 @@ def edit_user(user_id): flash(_(u"User not found"), category="error") return redirect(url_for('admin.admin')) downloads = list() - languages = speaking_language() + languages = calibre_db.speaking_language() translations = babel.list_translations() + [LC('en')] kobo_support = feature_support['kobo'] and config.config_kobo_sync for book in content.downloads: - downloadbook = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + downloadbook = calibre_db.get_book(book.book_id) if downloadbook: downloads.append(downloadbook) else: diff --git a/cps/db.py b/cps/db.py index a22215b0..5c487e43 100644 --- a/cps/db.py +++ b/cps/db.py @@ -22,10 +22,9 @@ import sys import os import re import ast +import json from datetime import datetime import threading -import time -import queue from sqlalchemy import create_engine from sqlalchemy import Table, Column, ForeignKey, CheckConstraint @@ -33,12 +32,24 @@ from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float from sqlalchemy.orm import relationship, sessionmaker, scoped_session from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.exc import OperationalError +from flask_login import current_user +from sqlalchemy.sql.expression import and_, true, false, text, func, or_ +from babel import Locale as LC +from babel.core import UnknownLocaleError +from flask_babel import gettext as _ + +from . import logger, ub, isoLanguages +from .pagination import Pagination + +try: + import unidecode + use_unidecode = True +except ImportError: + use_unidecode = False + -from . import logger -# session = None cc_exceptions = ['datetime', 'comments', 'composite', 'series'] cc_classes = {} -# engine = None Base = declarative_base() @@ -327,6 +338,7 @@ class CalibreDB(threading.Thread): self.session = None self.queue = None self.log = None + self.config = None def add_queue(self,queue): self.queue = queue @@ -356,6 +368,7 @@ class CalibreDB(threading.Thread): self.queue.put('dummy') def setup_db(self, config, app_db_path): + self.config = config self.dispose() # global engine @@ -441,6 +454,149 @@ class CalibreDB(threading.Thread): self.session = Session() return True + def get_book(self, book_id): + return self.session.query(Books).filter(Books.id == book_id).first() + + def get_filtered_book(self, book_id, allow_show_archived=False): + return self.session.query(Books).filter(Books.id == book_id).\ + filter(self.common_filters(allow_show_archived)).first() + + def get_book_by_uuid(self, book_uuid): + return self.session.query(Books).filter(Books.uuid == book_uuid).first() + + def get_book_format(self, book_id, format): + return self.session.query(Data).filter(Data.book == book_id).filter(Data.format == format).first() + + # Language and content filters for displaying in the UI + def common_filters(self, allow_show_archived=False): + if not allow_show_archived: + archived_books = ( + ub.session.query(ub.ArchivedBook) + .filter(ub.ArchivedBook.user_id == int(current_user.id)) + .filter(ub.ArchivedBook.is_archived == True) + .all() + ) + archived_book_ids = [archived_book.book_id for archived_book in archived_books] + archived_filter = Books.id.notin_(archived_book_ids) + else: + archived_filter = true() + + if current_user.filter_language() != "all": + lang_filter = Books.languages.any(Languages.lang_code == current_user.filter_language()) + else: + lang_filter = true() + negtags_list = current_user.list_denied_tags() + postags_list = current_user.list_allowed_tags() + neg_content_tags_filter = false() if negtags_list == [''] else Books.tags.any(Tags.name.in_(negtags_list)) + pos_content_tags_filter = true() if postags_list == [''] else Books.tags.any(Tags.name.in_(postags_list)) + if self.config.config_restricted_column: + pos_cc_list = current_user.allowed_column_value.split(',') + pos_content_cc_filter = true() if pos_cc_list == [''] else \ + getattr(Books, 'custom_column_' + str(self.config.config_restricted_column)). \ + any(cc_classes[self.config.config_restricted_column].value.in_(pos_cc_list)) + neg_cc_list = current_user.denied_column_value.split(',') + neg_content_cc_filter = false() if neg_cc_list == [''] else \ + getattr(Books, 'custom_column_' + str(self.config.config_restricted_column)). \ + any(cc_classes[self.config.config_restricted_column].value.in_(neg_cc_list)) + else: + pos_content_cc_filter = true() + neg_content_cc_filter = false() + return and_(lang_filter, pos_content_tags_filter, ~neg_content_tags_filter, + pos_content_cc_filter, ~neg_content_cc_filter, archived_filter) + + # Fill indexpage with all requested data from database + def fill_indexpage(self, page, database, db_filter, order, *join): + return self.fill_indexpage_with_archived_books(page, database, db_filter, order, False, *join) + + def fill_indexpage_with_archived_books(self, page, database, db_filter, order, allow_show_archived, *join): + if current_user.show_detail_random(): + randm = self.session.query(Books) \ + .filter(self.common_filters(allow_show_archived)) \ + .order_by(func.random()) \ + .limit(self.config.config_random_books) + else: + randm = false() + off = int(int(self.config.config_books_per_page) * (page - 1)) + query = self.session.query(database) \ + .join(*join, isouter=True) \ + .filter(db_filter) \ + .filter(self.common_filters(allow_show_archived)) + pagination = Pagination(page, self.config.config_books_per_page, + len(query.all())) + entries = query.order_by(*order).offset(off).limit(self.config.config_books_per_page).all() + for book in entries: + book = self.order_authors(book) + return entries, randm, pagination + + # Orders all Authors in the list according to authors sort + def order_authors(self, entry): + sort_authors = entry.author_sort.split('&') + authors_ordered = list() + error = False + for auth in sort_authors: + # ToDo: How to handle not found authorname + result = self.session.query(Authors).filter(Authors.sort == auth.lstrip().strip()).first() + if not result: + error = True + break + authors_ordered.append(result) + if not error: + entry.authors = authors_ordered + return entry + + def get_typeahead(self, database, query, replace=('', ''), tag_filter=true()): + query = query or '' + self.session.connection().connection.connection.create_function("lower", 1, self.lcase) + entries = self.session.query(database).filter(tag_filter). \ + filter(func.lower(database.name).ilike("%" + query + "%")).all() + json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) + return json_dumps + + def check_exists_book(self, authr, title): + self.session.connection().connection.connection.create_function("lower", 1, self.lcase) + q = list() + authorterms = re.split(r'\s*&\s*', authr) + for authorterm in authorterms: + q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) + + return self.session.query(Books)\ + .filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first() + + # read search results from calibre-database and return it (function is used for feed and simple search + def get_search_results(self, term): + term.strip().lower() + self.session.connection().connection.connection.create_function("lower", 1, self.lcase) + q = list() + authorterms = re.split("[, ]+", term) + for authorterm in authorterms: + q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%"))) + + return self.session.query(Books).filter(self.common_filters()).filter( + or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")), + Books.series.any(func.lower(Series.name).ilike("%" + term + "%")), + Books.authors.any(and_(*q)), + Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")), + func.lower(Books.title).ilike("%" + term + "%") + )).order_by(Books.sort).all() + + # Creates for all stored languages a translated speaking name in the array for the UI + def speaking_language(self, languages=None): + from . import get_locale + + if not languages: + languages = self.session.query(Languages) \ + .join(books_languages_link) \ + .join(Books) \ + .filter(self.common_filters()) \ + .group_by(text('books_languages_link.lang_code')).all() + for lang in languages: + try: + cur_l = LC.parse(lang.lang_code) + lang.name = cur_l.get_language_name(get_locale()) + except UnknownLocaleError: + lang.name = _(isoLanguages.get(part3=lang.lang_code).name) + return languages + def update_title_sort(self, config, conn=None): # user defined sort function for calibre databases (Series, etc.) def _title_sort(title): @@ -481,8 +637,13 @@ class CalibreDB(threading.Thread): if table is not None: Base.metadata.remove(table) - def reconnect_db(self, config, app_db_path): self.session.close() self.engine.dispose() self.setup_db(config, app_db_path) + + def lcase(self, s): + try: + return unidecode.unidecode(s.lower()) + except Exception as e: + self.log.exception(e) diff --git a/cps/editbooks.py b/cps/editbooks.py index e54a5a28..cb9424a2 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -35,7 +35,6 @@ from sqlalchemy.exc import OperationalError from . import constants, logger, isoLanguages, gdriveutils, uploader, helper from . import config, get_locale, ub, worker, db from . import calibre_db -from .helper import order_authors, common_filters from .web import login_required_if_no_ano, render_title_template, edit_required, upload_required @@ -176,7 +175,7 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session): @login_required def delete_book(book_id, book_format): if current_user.role_delete_books(): - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.get_book(book_id) if book: try: result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper()) @@ -250,9 +249,7 @@ def delete_book(book_id, book_format): def render_edit_book(book_id): calibre_db.update_title_sort(config) cc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() - book = calibre_db.session.query(db.Books)\ - .filter(db.Books.id == book_id).filter(common_filters()).first() - + book = calibre_db.get_filtered_book(book_id) if not book: flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error") return redirect(url_for("web.index")) @@ -260,7 +257,7 @@ def render_edit_book(book_id): for lang in book.languages: lang.language_name = isoLanguages.get_language_name(get_locale(), lang.lang_code) - book = order_authors(book) + book = calibre_db.order_authors(book) author_names = [] for authr in book.authors: @@ -447,8 +444,7 @@ def upload_single_file(request, book, book_id): return redirect(url_for('web.show_book', book_id=book.id)) file_size = os.path.getsize(saved_filename) - is_format = calibre_db.session.query(db.Data).filter(db.Data.book == book_id).\ - filter(db.Data.format == file_ext.upper()).first() + is_format = calibre_db.get_book_format(book_id, file_ext.upper()) # Format entry already exists, no need to update the database if is_format: @@ -500,8 +496,7 @@ def edit_book(book_id): # create the function for sorting... calibre_db.update_title_sort(config) - book = calibre_db.session.query(db.Books)\ - .filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.get_filtered_book(book_id) # Book not found if not book: @@ -702,7 +697,7 @@ def upload(): series_index = meta.series_id if title != _(u'Unknown') and authr != _(u'Unknown'): - entry = helper.check_exists_book(authr, title) + entry = calibre_db.check_exists_book(authr, title) if entry: log.info("Uploaded book probably exists in library") flash(_(u"Uploaded book probably exists in the library, consider to change before upload new: ") @@ -807,7 +802,7 @@ def upload(): calibre_db.update_title_sort(config) # Reread book. It's important not to filter the result, as it could have language which hide it from # current users view (tags are not stored/extracted from metadata and could also be limited) - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.get_book(book_id) # upload book to gdrive if nesseccary and add "(bookid)" to folder name if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() diff --git a/cps/helper.py b/cps/helper.py index a0e35c4a..b59d9bed 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -74,8 +74,8 @@ log = logger.create() # Convert existing book entry to new format def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, user_id, kindle_mail=None): - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() - data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == old_book_format).first() + book = calibre_db.get_book(book_id) + data = calibre_db.get_book_format(book.id, old_book_format) if not data: error_message = _(u"%(format)s format not found for book id: %(book)d", format=old_book_format, book=book_id) log.error("convert_book_format: %s", error_message) @@ -212,7 +212,7 @@ def check_read_formats(entry): # 3: If Pdf file is existing, it's directly send to kindle email def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id): """Send email with attachments""" - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.get_book(book_id) if convert == 1: # returns None if success, otherwise errormessage @@ -324,7 +324,7 @@ def delete_book_file(book, calibrepath, book_format=None): def update_dir_structure_file(book_id, calibrepath, first_author): - localbook = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + localbook = calibre_db.get_book(book_id) path = os.path.join(calibrepath, localbook.path) authordir = localbook.path.split('/')[0] @@ -383,7 +383,7 @@ def update_dir_structure_file(book_id, calibrepath, first_author): def update_dir_structure_gdrive(book_id, first_author): error = False - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.get_book(book_id) path = book.path authordir = book.path.split('/')[0] @@ -494,18 +494,17 @@ def get_cover_on_failure(use_generic_cover): def get_book_cover(book_id): - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters(allow_show_archived=True)).first() + book = calibre_db.get_filtered_book(book_id, allow_show_archived=True) return get_book_cover_internal(book, use_generic_cover_on_failure=True) def get_book_cover_with_uuid(book_uuid, use_generic_cover_on_failure=True): - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.get_book_by_uuid(book_uuid) return get_book_cover_internal(book, use_generic_cover_on_failure) -def get_book_cover_internal(book, - use_generic_cover_on_failure): +def get_book_cover_internal(book, use_generic_cover_on_failure): if book and book.has_cover: if config.config_use_google_drive: try: @@ -725,66 +724,12 @@ def render_task_status(tasklist): return renderedtasklist -# Language and content filters for displaying in the UI -def common_filters(allow_show_archived=False): - if not allow_show_archived: - archived_books = ( - ub.session.query(ub.ArchivedBook) - .filter(ub.ArchivedBook.user_id == int(current_user.id)) - .filter(ub.ArchivedBook.is_archived == True) - .all() - ) - archived_book_ids = [archived_book.book_id for archived_book in archived_books] - archived_filter = db.Books.id.notin_(archived_book_ids) - else: - archived_filter = true() - - if current_user.filter_language() != "all": - lang_filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language()) - else: - lang_filter = true() - negtags_list = current_user.list_denied_tags() - postags_list = current_user.list_allowed_tags() - neg_content_tags_filter = false() if negtags_list == [''] else db.Books.tags.any(db.Tags.name.in_(negtags_list)) - pos_content_tags_filter = true() if postags_list == [''] else db.Books.tags.any(db.Tags.name.in_(postags_list)) - if config.config_restricted_column: - pos_cc_list = current_user.allowed_column_value.split(',') - pos_content_cc_filter = true() if pos_cc_list == [''] else \ - getattr(db.Books, 'custom_column_' + str(config.config_restricted_column)).\ - any(db.cc_classes[config.config_restricted_column].value.in_(pos_cc_list)) - neg_cc_list = current_user.denied_column_value.split(',') - neg_content_cc_filter = false() if neg_cc_list == [''] else \ - getattr(db.Books, 'custom_column_' + str(config.config_restricted_column)).\ - any(db.cc_classes[config.config_restricted_column].value.in_(neg_cc_list)) - else: - pos_content_cc_filter = true() - neg_content_cc_filter = false() - return and_(lang_filter, pos_content_tags_filter, ~neg_content_tags_filter, - pos_content_cc_filter, ~neg_content_cc_filter, archived_filter) - - def tags_filters(): negtags_list = current_user.list_denied_tags() postags_list = current_user.list_allowed_tags() neg_content_tags_filter = false() if negtags_list == [''] else db.Tags.name.in_(negtags_list) pos_content_tags_filter = true() if postags_list == [''] else db.Tags.name.in_(postags_list) return and_(pos_content_tags_filter, ~neg_content_tags_filter) - # return ~(false()) if postags_list == [''] else db.Tags.in_(postags_list) - - -# Creates for all stored languages a translated speaking name in the array for the UI -def speaking_language(languages=None): - if not languages: - languages = calibre_db.session.query(db.Languages).join(db.books_languages_link).join(db.Books)\ - .filter(common_filters())\ - .group_by(text('books_languages_link.lang_code')).all() - for lang in languages: - try: - cur_l = LC.parse(lang.lang_code) - lang.name = cur_l.get_language_name(get_locale()) - except UnknownLocaleError: - lang.name = _(isoLanguages.get(part3=lang.lang_code).name) - return languages # checks if domain is in database (including wildcards) @@ -801,76 +746,9 @@ def check_valid_domain(domain_text): return not len(result) -# Orders all Authors in the list according to authors sort -def order_authors(entry): - sort_authors = entry.author_sort.split('&') - authors_ordered = list() - error = False - for auth in sort_authors: - # ToDo: How to handle not found authorname - result = calibre_db.session.query(db.Authors).filter(db.Authors.sort == auth.lstrip().strip()).first() - if not result: - error = True - break - authors_ordered.append(result) - if not error: - entry.authors = authors_ordered - return entry - - -# Fill indexpage with all requested data from database -def fill_indexpage(page, database, db_filter, order, *join): - return fill_indexpage_with_archived_books(page, database, db_filter, order, False, *join) - - -def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_show_archived, *join): - if current_user.show_detail_random(): - randm = calibre_db.session.query(db.Books).filter(common_filters(allow_show_archived))\ - .order_by(func.random()).limit(config.config_random_books) - else: - randm = false() - off = int(int(config.config_books_per_page) * (page - 1)) - query = calibre_db.session.query(database).join(*join, isouter=True).\ - filter(db_filter).\ - filter(common_filters(allow_show_archived)) - pagination = Pagination(page, config.config_books_per_page, - len(query.all())) - entries = query.order_by(*order).offset(off).limit(config.config_books_per_page).all() - for book in entries: - book = order_authors(book) - return entries, randm, pagination - - -def get_typeahead(database, query, replace=('', ''), tag_filter=true()): - query = query or '' - calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) - entries = calibre_db.session.query(database).filter(tag_filter).\ - filter(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): - calibre_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(func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) - - db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + term + "%")) - - return calibre_db.session.query(db.Books).filter(common_filters()).filter( - or_(db.Books.tags.any(func.lower(db.Tags.name).ilike("%" + term + "%")), - db.Books.series.any(func.lower(db.Series.name).ilike("%" + term + "%")), - db.Books.authors.any(and_(*q)), - db.Books.publishers.any(func.lower(db.Publishers.name).ilike("%" + term + "%")), - func.lower(db.Books.title).ilike("%" + term + "%") - )).order_by(db.Books.sort).all() - - def get_cc_columns(filter_config_custom_read=False): - tmpcc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() + tmpcc = calibre_db.session.query(db.Custom_Columns)\ + .filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() cc = [] r = None if config.config_columns_to_ignore: @@ -887,10 +765,9 @@ def get_cc_columns(filter_config_custom_read=False): def get_download_link(book_id, book_format, client): book_format = book_format.split(".")[0] - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.get_filtered_book(book_id) if book: - data1 = calibre_db.session.query(db.Data).filter(db.Data.book == book.id)\ - .filter(db.Data.format == book_format.upper()).first() + data1 = data = calibre_db.get_book_format(book.id, book_format.upper()) else: abort(404) if data1: @@ -908,25 +785,3 @@ def get_download_link(book_id, book_format, client): return do_download_file(book, book_format, client, data1, headers) else: abort(404) - - -def check_exists_book(authr, title): - calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) - q = list() - authorterms = re.split(r'\s*&\s*', authr) - for authorterm in authorterms: - q.append(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) - - return calibre_db.session.query(db.Books).filter( - and_(db.Books.authors.any(and_(*q)), - func.lower(db.Books.title).ilike("%" + title + "%") - )).first() - -############### Database Helper functions - - -def lcase(s): - try: - return unidecode.unidecode(s.lower()) - except Exception as e: - log.exception(e) diff --git a/cps/jinjia.py b/cps/jinjia.py index 2c231582..28c2621a 100644 --- a/cps/jinjia.py +++ b/cps/jinjia.py @@ -111,10 +111,3 @@ def timestamptodate(date, fmt=None): @jinjia.app_template_filter('yesno') def yesno(value, yes, no): return yes if value else no - - -'''@jinjia.app_template_filter('canread') -def canread(ext): - if isinstance(ext, db.Data): - ext = ext.format - return ext.lower() in EXTENSIONS_READER''' diff --git a/cps/kobo.py b/cps/kobo.py index cb07a1d2..462e4ca1 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -256,7 +256,7 @@ def HandleMetadataRequest(book_uuid): if not current_app.wsgi_app.is_proxied: log.debug('Kobo: Received unproxied request, changed request port to server port') log.info("Kobo library metadata request received for book %s" % book_uuid) - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.get_book_by_uuid(book_uuid) if not book or not book.data: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() @@ -474,7 +474,7 @@ def add_items_to_shelf(items, shelf): items_unknown_to_calibre.append(item) continue - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() + book = calibre_db.get_book_by_uuid(item["RevisionId"]) if not book: items_unknown_to_calibre.append(item) continue @@ -545,7 +545,7 @@ def HandleTagRemoveItem(tag_id): items_unknown_to_calibre.append(item) continue - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() + book = calibre_db.get_book_by_uuid(item["RevisionId"]) if not book: items_unknown_to_calibre.append(item) continue @@ -613,7 +613,7 @@ def create_kobo_tag(shelf): "Type": "UserTag" } for book_shelf in shelf.books: - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none() + book = calibre_db.get_book_by_uuid(book_shelf.book_id) if not book: log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id) continue @@ -629,7 +629,7 @@ def create_kobo_tag(shelf): @kobo.route("/v1/library//state", methods=["GET", "PUT"]) @login_required def HandleStateRequest(book_uuid): - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.get_book_by_uuid(book_uuid) if not book or not book.data: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() @@ -804,7 +804,7 @@ def TopLevelEndpoint(): @login_required def HandleBookDeletionRequest(book_uuid): log.info("Kobo book deletion request received for book %s" % book_uuid) - book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.get_book_by_uuid(book_uuid) if not book: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() diff --git a/cps/opds.py b/cps/opds.py index 6b850371..78d5d8ed 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -31,9 +31,9 @@ from sqlalchemy.sql.expression import func, text, or_, and_ from werkzeug.security import check_password_hash from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages -from .helper import fill_indexpage, get_download_link, get_book_cover, speaking_language +from .helper import get_download_link, get_book_cover from .pagination import Pagination -from .web import common_filters, get_search_results, render_read_books, download_required +from .web import render_read_books, download_required from flask_babel import gettext as _ from babel import Locale as LC from babel.core import UnknownLocaleError @@ -100,15 +100,15 @@ def feed_normal_search(): @requires_basic_auth_if_no_ano def feed_new(): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, True, [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, True, [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @opds.route("/opds/discover") @requires_basic_auth_if_no_ano def feed_discover(): - entries = calibre_db.session.query(db.Books).filter(common_filters()).order_by(func.random())\ + entries = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).order_by(func.random())\ .limit(config.config_books_per_page) pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page)) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -118,9 +118,9 @@ def feed_discover(): @requires_basic_auth_if_no_ano def feed_best_rated(): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, db.Books.ratings.any(db.Ratings.rating > 9), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, db.Books.ratings.any(db.Ratings.rating > 9), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -133,16 +133,13 @@ def feed_hot(): hot_books = all_books.offset(off).limit(config.config_books_per_page) entries = list() for book in hot_books: - downloadBook = calibre_db.session.query(db.Books).filter(db.Books.id == book.Downloads.book_id).first() + downloadBook = calibre_db.get_book(book.Downloads.book_id) if downloadBook: entries.append( - calibre_db.session.query(db.Books).filter(common_filters()) - .filter(db.Books.id == book.Downloads.book_id).first() + calibre_db.get_filtered_book(book.Downloads.book_id) ) else: ub.delete_download(book.Downloads.book_id) - # ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() - # ub.session.commit() numBooks = entries.__len__() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, numBooks) @@ -153,8 +150,10 @@ def feed_hot(): @requires_basic_auth_if_no_ano def feed_authorindex(): off = request.args.get("offset") or 0 - entries = calibre_db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\ - .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).limit(config.config_books_per_page)\ + entries = calibre_db.session.query(db.Authors).join(db.books_authors_link).join(db.Books)\ + .filter(calibre_db.common_filters())\ + .group_by(text('books_authors_link.author'))\ + .order_by(db.Authors.sort).limit(config.config_books_per_page)\ .offset(off) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(calibre_db.session.query(db.Authors).all())) @@ -165,10 +164,10 @@ def feed_authorindex(): @requires_basic_auth_if_no_ano def feed_author(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.authors.any(db.Authors.id == book_id), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.authors.any(db.Authors.id == book_id), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -176,8 +175,11 @@ def feed_author(book_id): @requires_basic_auth_if_no_ano def feed_publisherindex(): off = request.args.get("offset") or 0 - entries = calibre_db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\ - .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.sort)\ + entries = calibre_db.session.query(db.Publishers)\ + .join(db.books_publishers_link)\ + .join(db.Books).filter(calibre_db.common_filters())\ + .group_by(text('books_publishers_link.publisher'))\ + .order_by(db.Publishers.sort)\ .limit(config.config_books_per_page).offset(off) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(calibre_db.session.query(db.Publishers).all())) @@ -188,10 +190,10 @@ def feed_publisherindex(): @requires_basic_auth_if_no_ano def feed_publisher(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.publishers.any(db.Publishers.id == book_id), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.publishers.any(db.Publishers.id == book_id), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -199,8 +201,14 @@ def feed_publisher(book_id): @requires_basic_auth_if_no_ano def feed_categoryindex(): off = request.args.get("offset") or 0 - entries = calibre_db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters())\ - .group_by(text('books_tags_link.tag')).order_by(db.Tags.name).offset(off).limit(config.config_books_per_page) + entries = calibre_db.session.query(db.Tags)\ + .join(db.books_tags_link)\ + .join(db.Books)\ + .filter(calibre_db.common_filters())\ + .group_by(text('books_tags_link.tag'))\ + .order_by(db.Tags.name)\ + .offset(off)\ + .limit(config.config_books_per_page) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(calibre_db.session.query(db.Tags).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_category', pagination=pagination) @@ -210,10 +218,10 @@ def feed_categoryindex(): @requires_basic_auth_if_no_ano def feed_category(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.tags.any(db.Tags.id == book_id), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.tags.any(db.Tags.id == book_id), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -221,8 +229,13 @@ def feed_category(book_id): @requires_basic_auth_if_no_ano def feed_seriesindex(): off = request.args.get("offset") or 0 - entries = calibre_db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters())\ - .group_by(text('books_series_link.series')).order_by(db.Series.sort).offset(off).all() + entries = calibre_db.session.query(db.Series)\ + .join(db.books_series_link)\ + .join(db.Books)\ + .filter(calibre_db.common_filters())\ + .group_by(text('books_series_link.series'))\ + .order_by(db.Series.sort)\ + .offset(off).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(calibre_db.session.query(db.Series).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_series', pagination=pagination) @@ -232,10 +245,10 @@ def feed_seriesindex(): @requires_basic_auth_if_no_ano def feed_series(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.series.any(db.Series.id == book_id), - [db.Books.series_index]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.series.any(db.Series.id == book_id), + [db.Books.series_index]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -245,8 +258,11 @@ def feed_ratingindex(): off = request.args.get("offset") or 0 entries = calibre_db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), (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() + .join(db.books_ratings_link)\ + .join(db.Books)\ + .filter(calibre_db.common_filters()) \ + .group_by(text('books_ratings_link.rating'))\ + .order_by(db.Ratings.rating).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(entries)) @@ -260,10 +276,10 @@ def feed_ratingindex(): @requires_basic_auth_if_no_ano def feed_ratings(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.ratings.any(db.Ratings.id == book_id), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.ratings.any(db.Ratings.id == book_id), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -271,8 +287,10 @@ def feed_ratings(book_id): @requires_basic_auth_if_no_ano def feed_formatindex(): off = request.args.get("offset") or 0 - entries = calibre_db.session.query(db.Data).join(db.Books).filter(common_filters()) \ - .group_by(db.Data.format).order_by(db.Data.format).all() + entries = calibre_db.session.query(db.Data).join(db.Books)\ + .filter(calibre_db.common_filters()) \ + .group_by(db.Data.format)\ + .order_by(db.Data.format).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(entries)) @@ -286,10 +304,10 @@ def feed_formatindex(): @requires_basic_auth_if_no_ano def feed_format(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.data.any(db.Data.format == book_id.upper()), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.data.any(db.Data.format == book_id.upper()), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -299,7 +317,7 @@ def feed_format(book_id): def feed_languagesindex(): off = request.args.get("offset") or 0 if current_user.filter_language() == u"all": - languages = speaking_language() + languages = calibre_db.speaking_language() else: try: cur_l = LC.parse(current_user.filter_language()) @@ -320,10 +338,10 @@ def feed_languagesindex(): @requires_basic_auth_if_no_ano def feed_languages(book_id): off = request.args.get("offset") or 0 - entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), - db.Books, - db.Books.languages.any(db.Languages.id == book_id), - [db.Books.timestamp.desc()]) + entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), + db.Books, + db.Books.languages.any(db.Languages.id == book_id), + [db.Books.timestamp.desc()]) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -356,7 +374,7 @@ def feed_shelf(book_id): books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == book_id).order_by( ub.BookShelf.order.asc()).all() for book in books_in_shelf: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.get_book(book.book_id) result.append(cur_book) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(result)) @@ -390,8 +408,7 @@ def get_metadata_calibre_companion(uuid, library): def feed_search(term): if term: - term = term.strip().lower() - entries = get_search_results(term) + entries = calibre_db.get_search_results(term) entriescount = len(entries) if len(entries) > 0 else 1 pagination = Pagination(1, entriescount, entriescount) return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination) diff --git a/cps/shelf.py b/cps/shelf.py index 1620789c..b13fe556 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -30,7 +30,6 @@ from sqlalchemy.sql.expression import func from . import logger, ub, searched_ids, db, calibre_db from .web import render_title_template -from .helper import common_filters shelf = Blueprint('shelf', __name__) @@ -320,11 +319,11 @@ def show_shelf(shelf_type, shelf_id): books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\ .order_by(ub.BookShelf.order.asc()).all() for book in books_in_shelf: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() + cur_book = calibre_db.get_filtered_book(book.book_id) if cur_book: result.append(cur_book) else: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.get_book(book.book_id) if not cur_book: log.info('Not existing book %s in %s deleted', book.book_id, shelf) ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book.book_id).delete() @@ -356,7 +355,7 @@ def order_shelf(shelf_id): books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ .order_by(ub.BookShelf.order.asc()).all() for book in books_in_shelf2: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() + cur_book = calibre_db.get_filtered_book(book.book_id) if cur_book: result.append({'title': cur_book.title, 'id': cur_book.id, @@ -364,7 +363,7 @@ def order_shelf(shelf_id): 'series': cur_book.series, 'series_index': cur_book.series_index}) else: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.get_book(book.book_id) result.append({'title': _('Hidden Book'), 'id': cur_book.id, 'author': [], diff --git a/cps/web.py b/cps/web.py index 7ac17156..021a034e 100644 --- a/cps/web.py +++ b/cps/web.py @@ -52,10 +52,9 @@ from . import constants, logger, isoLanguages, services, worker from . import searched_ids, lm, babel, db, ub, config, get_locale, app from . import calibre_db from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download -from .helper import common_filters, get_search_results, fill_indexpage, fill_indexpage_with_archived_books, \ - speaking_language, check_valid_domain, order_authors, get_typeahead, render_task_status, json_serial, \ +from .helper import check_valid_domain, render_task_status, json_serial, \ 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, tags_filters, reset_password + send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password from .pagination import Pagination from .redirect import redirect_back @@ -440,7 +439,7 @@ def toggle_read(book_id): else: try: calibre_db.update_title_sort(config) - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.get_filtered_book(book_id) read_status = getattr(book, 'custom_column_' + str(config.config_read_column)) if len(read_status): read_status[0].value = not read_status[0].value @@ -497,7 +496,7 @@ def update_view(): @web.route("/ajax/getcomic///") @login_required def get_comic_book(book_id, book_format, page): - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.get_book(book_id) if not book: return "", 204 else: @@ -551,25 +550,25 @@ def get_comic_book(book_id, book_format, page): @web.route("/get_authors_json", methods=['GET']) @login_required_if_no_ano def get_authors_json(): - return get_typeahead(db.Authors, request.args.get('q'), ('|', ',')) + return calibre_db.get_typeahead(db.Authors, request.args.get('q'), ('|', ',')) @web.route("/get_publishers_json", methods=['GET']) @login_required_if_no_ano def get_publishers_json(): - return get_typeahead(db.Publishers, request.args.get('q'), ('|', ',')) + return calibre_db.get_typeahead(db.Publishers, request.args.get('q'), ('|', ',')) @web.route("/get_tags_json", methods=['GET']) @login_required_if_no_ano def get_tags_json(): - return get_typeahead(db.Tags, request.args.get('q'), tag_filter=tags_filters()) + return calibre_db.get_typeahead(db.Tags, request.args.get('q'), tag_filter=tags_filters()) @web.route("/get_series_json", methods=['GET']) @login_required_if_no_ano def get_series_json(): - return get_typeahead(db.Series, request.args.get('q')) + return calibre_db.get_typeahead(db.Series, request.args.get('q')) @web.route("/get_languages_json", methods=['GET']) @@ -591,7 +590,7 @@ def get_languages_json(): def get_matching_tags(): tag_dict = {'tags': []} q = calibre_db.session.query(db.Books) - calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) + calibre_db.session.connection().connection.connection.create_function("lower", 1, calibre_db.lcase) author_input = request.args.get('author_name') or '' title_input = request.args.get('book_title') or '' include_tag_inputs = request.args.getlist('include_tag') or '' @@ -621,7 +620,7 @@ def get_matching_tags(): @web.route('/page/') @login_required_if_no_ano def index(page): - entries, random, pagination = fill_indexpage(page, db.Books, True, [db.Books.timestamp.desc()]) + entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, [db.Books.timestamp.desc()]) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Recently Added Books"), page="root") @@ -648,15 +647,17 @@ def books_list(data, sort, book_id, page): if data == "rated": if current_user.check_visibility(constants.SIDEBAR_BEST_RATED): - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.ratings.any(db.Ratings.rating > 9), - order) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.ratings.any(db.Ratings.rating > 9), + order) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id, title=_(u"Top Rated Books"), page="rated") else: abort(404) elif data == "discover": if current_user.check_visibility(constants.SIDEBAR_RANDOM): - entries, __, pagination = fill_indexpage(page, db.Books, True, [func.randomblob(2)]) + entries, __, pagination = calibre_db.calibre_db.fill_indexpage(page, db.Books, True, [func.randomblob(2)]) pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page) return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id, title=_(u"Discover (Random Books)"), page="discover") @@ -685,7 +686,7 @@ def books_list(data, sort, book_id, page): elif data == "archived": return render_archived_books(page, order) else: - entries, random, pagination = fill_indexpage(page, db.Books, True, order) + entries, random, pagination = calibre_db.fill_indexpage(page, db.Books, True, order) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, title=_(u"Books"), page="newest") @@ -693,7 +694,7 @@ def books_list(data, sort, book_id, page): def render_hot_books(page): if current_user.check_visibility(constants.SIDEBAR_HOT): if current_user.show_detail_random(): - random = calibre_db.session.query(db.Books).filter(common_filters()) \ + random = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \ .order_by(func.random()).limit(config.config_random_books) else: random = false() @@ -703,7 +704,7 @@ def render_hot_books(page): hot_books = all_books.offset(off).limit(config.config_books_per_page) entries = list() for book in hot_books: - downloadBook = calibre_db.session.query(db.Books).filter(common_filters()).filter( + downloadBook = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).filter( db.Books.id == book.Downloads.book_id).first() if downloadBook: entries.append(downloadBook) @@ -720,9 +721,12 @@ def render_hot_books(page): def render_author_books(page, author_id, order): - entries, __, pagination = fill_indexpage(page, db.Books, db.Books.authors.any(db.Authors.id == author_id), - [order[0], db.Series.name, db.Books.series_index], - db.books_series_link, db.Series) + entries, __, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.authors.any(db.Authors.id == author_id), + [order[0], db.Series.name, db.Books.series_index], + db.books_series_link, + db.Series) if entries is None or not len(entries): flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"), category="error") @@ -745,10 +749,12 @@ def render_author_books(page, author_id, order): def render_publisher_books(page, book_id, order): publisher = calibre_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.Series.name, order[0], db.Books.series_index], - db.books_series_link, db.Series) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.publishers.any(db.Publishers.id == book_id), + [db.Series.name, order[0], db.Books.series_index], + db.books_series_link, + db.Series) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id, title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher") else: @@ -758,8 +764,10 @@ def render_publisher_books(page, book_id, order): def render_series_books(page, book_id, order): name = calibre_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]]) + entries, random, pagination = calibre_db.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, id=book_id, title=_(u"Series: %(serie)s", serie=name.name), page="series") else: @@ -768,8 +776,10 @@ def render_series_books(page, book_id, order): def render_ratings_books(page, book_id, order): name = calibre_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]]) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.ratings.any(db.Ratings.id == book_id), + [db.Books.timestamp.desc(), order[0]]) if name and name.rating <= 10: return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id, title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings") @@ -780,9 +790,10 @@ def render_ratings_books(page, book_id, order): def render_formats_books(page, book_id, order): name = calibre_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]]) + entries, random, pagination = calibre_db.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, id=book_id, title=_(u"File format: %(format)s", format=name.format), page="formats") else: @@ -792,9 +803,11 @@ def render_formats_books(page, book_id, order): def render_category_books(page, book_id, order): name = calibre_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), - [order[0], db.Series.name, db.Books.series_index], - db.books_series_link, db.Series) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.tags.any(db.Tags.id == book_id), + [order[0], 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, id=book_id, title=_(u"Category: %(name)s", name=name.name), page="category") else: @@ -810,8 +823,10 @@ def render_language_books(page, name, order): lang_name = _(isoLanguages.get(part3=name).name) except KeyError: abort(404) - entries, random, pagination = fill_indexpage(page, db.Books, db.Books.languages.any(db.Languages.lang_code == name), - [db.Books.timestamp.desc(), order[0]]) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db.Books.languages.any(db.Languages.lang_code == name), + [db.Books.timestamp.desc(), order[0]]) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name, title=_(u"Language: %(name)s", name=lang_name), page="language") @@ -827,10 +842,10 @@ def books_table(): def author_list(): if current_user.check_visibility(constants.SIDEBAR_AUTHOR): entries = calibre_db.session.query(db.Authors, func.count('books_authors_link.book').label('count')) \ - .join(db.books_authors_link).join(db.Books).filter(common_filters()) \ + .join(db.books_authors_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).all() charlist = calibre_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()) \ + .join(db.books_authors_link).join(db.Books).filter(calibre_db.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('|', ',') @@ -845,10 +860,10 @@ def author_list(): def publisher_list(): if current_user.check_visibility(constants.SIDEBAR_PUBLISHER): entries = calibre_db.session.query(db.Publishers, func.count('books_publishers_link.book').label('count')) \ - .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \ + .join(db.books_publishers_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.name).all() charlist = calibre_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()) \ + .join(db.books_publishers_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, title=_(u"Publishers"), page="publisherlist", data="publisher") @@ -862,19 +877,19 @@ def series_list(): if current_user.check_visibility(constants.SIDEBAR_SERIES): if current_user.series_view == 'list': entries = calibre_db.session.query(db.Series, func.count('books_series_link.book').label('count')) \ - .join(db.books_series_link).join(db.Books).filter(common_filters()) \ + .join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(text('books_series_link.series')).order_by(db.Series.sort).all() charlist = calibre_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()) \ + .join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, title=_(u"Series"), page="serieslist", data="series") else: entries = calibre_db.session.query(db.Books, func.count('books_series_link').label('count')) \ - .join(db.books_series_link).join(db.Series).filter(common_filters()) \ + .join(db.books_series_link).join(db.Series).filter(calibre_db.common_filters()) \ .group_by(text('books_series_link.series')).order_by(db.Series.sort).all() charlist = calibre_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()) \ + .join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all() return render_title_template('grid.html', entries=entries, folder='web.books_list', charlist=charlist, @@ -889,7 +904,7 @@ def ratings_list(): if current_user.check_visibility(constants.SIDEBAR_RATING): entries = calibre_db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), (db.Ratings.rating / 2).label('name')) \ - .join(db.books_ratings_link).join(db.Books).filter(common_filters()) \ + .join(db.books_ratings_link).join(db.Books).filter(calibre_db.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.books_list', charlist=list(), title=_(u"Ratings list"), page="ratingslist", data="ratings") @@ -901,8 +916,10 @@ def ratings_list(): @login_required_if_no_ano def formats_list(): if current_user.check_visibility(constants.SIDEBAR_FORMAT): - entries = calibre_db.session.query(db.Data, func.count('data.book').label('count'), db.Data.format.label('format')) \ - .join(db.Books).filter(common_filters()) \ + entries = calibre_db.session.query(db.Data, + func.count('data.book').label('count'), + db.Data.format.label('format')) \ + .join(db.Books).filter(calibre_db.common_filters()) \ .group_by(db.Data.format).order_by(db.Data.format).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(), title=_(u"File formats list"), page="formatslist", data="formats") @@ -916,7 +933,7 @@ def language_overview(): if current_user.check_visibility(constants.SIDEBAR_LANGUAGE): charlist = list() if current_user.filter_language() == u"all": - languages = speaking_language() + languages = calibre_db.speaking_language() # ToDo: generate first character list for languages else: try: @@ -944,10 +961,10 @@ def language_overview(): def category_list(): if current_user.check_visibility(constants.SIDEBAR_CATEGORY): entries = calibre_db.session.query(db.Tags, func.count('books_tags_link.book').label('count')) \ - .join(db.books_tags_link).join(db.Books).order_by(db.Tags.name).filter(common_filters()) \ + .join(db.books_tags_link).join(db.Books).order_by(db.Tags.name).filter(calibre_db.common_filters()) \ .group_by(text('books_tags_link.tag')).all() charlist = calibre_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()) \ + .join(db.books_tags_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(func.upper(func.substr(db.Tags.name, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, title=_(u"Categories"), page="catlist", data="category") @@ -980,8 +997,7 @@ def reconnect(): def search(): term = request.args.get("query") if term: - term.strip().lower() - entries = get_search_results(term) + entries = calibre_db.get_search_results(term) ids = list() for element in entries: ids.append(element.id) @@ -1004,8 +1020,8 @@ def search(): def advanced_search(): # Build custom columns names cc = get_cc_columns() - calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) - q = calibre_db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort) + calibre_db.session.connection().connection.connection.create_function("lower", 1, calibre_db.lcase) + q = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).order_by(db.Books.sort) include_tag_inputs = request.args.getlist('include_tag') exclude_tag_inputs = request.args.getlist('exclude_tag') @@ -1064,7 +1080,7 @@ def advanced_search(): searchterm.extend(serie.name for serie in serie_names) language_names = calibre_db.session.query(db.Languages).filter(db.Languages.id.in_(include_languages_inputs)).all() if language_names: - language_names = speaking_language(language_names) + language_names = calibre_db.speaking_language(language_names) searchterm.extend(language.name for language in language_names) if rating_high: searchterm.extend([_(u"Rating <= %(rating)s", rating=rating_high)]) @@ -1137,15 +1153,15 @@ def advanced_search(): return render_title_template('search.html', adv_searchterm=searchterm, entries=q, title=_(u"search"), page="search") # prepare data for search-form - tags = calibre_db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters()) \ + tags = calibre_db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(text('books_tags_link.tag')).order_by(db.Tags.name).all() - series = calibre_db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters()) \ - .group_by(text('books_series_link.series')).order_by(db.Series.name).filter(common_filters()).all() - extensions = calibre_db.session.query(db.Data).join(db.Books).filter(common_filters()) \ + series = calibre_db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \ + .group_by(text('books_series_link.series')).order_by(db.Series.name).filter(calibre_db.common_filters()).all() + extensions = calibre_db.session.query(db.Data).join(db.Books).filter(calibre_db.common_filters()) \ .group_by(db.Data.format).order_by(db.Data.format).all() if current_user.filter_language() == u"all": - languages = speaking_language() + languages = calibre_db.speaking_language() else: languages = None return render_title_template('search_form.html', tags=tags, languages=languages, extensions=extensions, @@ -1160,20 +1176,22 @@ def render_read_books(page, are_read, as_xml=False, order=None, *args, **kwargs) ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED) else: db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED - entries, random, pagination = fill_indexpage(page, db.Books, - db_filter, - order, - ub.ReadBook, db.Books.id==ub.ReadBook.book_id) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db_filter, + order, + ub.ReadBook, db.Books.id==ub.ReadBook.book_id) else: try: if are_read: db_filter = db.cc_classes[config.config_read_column].value == True else: db_filter = coalesce(db.cc_classes[config.config_read_column].value, False) != True - entries, random, pagination = fill_indexpage(page, db.Books, - db_filter, - order, - db.cc_classes[config.config_read_column]) + entries, random, pagination = calibre_db.fill_indexpage(page, + db.Books, + db_filter, + order, + db.cc_classes[config.config_read_column]) except KeyError: log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column) if not as_xml: @@ -1207,8 +1225,11 @@ def render_archived_books(page, order): archived_filter = db.Books.id.in_(archived_book_ids) - entries, random, pagination = fill_indexpage_with_archived_books(page, db.Books, archived_filter, order, - allow_show_archived=True) + entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, + db.Books, + archived_filter, + order, + allow_show_archived=True) name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')' pagename = "archived" @@ -1230,9 +1251,8 @@ def get_cover(book_id): @viewer_required def serve_book(book_id, book_format, anyname): book_format = book_format.split(".")[0] - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() - data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()) \ - .first() + book = calibre_db.get_book(book_id) + data = calibre_db.get_book_format(book.id, book_format.upper()) log.info('Serving book: %s', data.name) if config.config_use_google_drive: headers = Headers() @@ -1514,7 +1534,7 @@ def token_verified(): @login_required def profile(): downloads = list() - languages = speaking_language() + languages = calibre_db.speaking_language() translations = babel.list_translations() + [LC('en')] kobo_support = feature_support['kobo'] and config.config_kobo_sync if feature_support['oauth']: @@ -1523,9 +1543,9 @@ def profile(): oauth_status = None for book in current_user.downloads: - downloadBook = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + downloadBook = calibre_db.get_book(book.book_id) if downloadBook: - downloads.append(calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) + downloads.append(downloadBook) else: ub.delete_download(book.book_id) if request.method == "POST": @@ -1604,7 +1624,7 @@ def profile(): @login_required_if_no_ano @viewer_required def read_book(book_id, book_format): - book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.get_filtered_book(book_id) if not book: flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") log.debug(u"Error opening eBook. File does not exist or file is not accessible:") @@ -1628,7 +1648,7 @@ def read_book(book_id, book_format): else: for fileExt in constants.EXTENSIONS_AUDIO: if book_format.lower() == fileExt: - entries = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + entries = calibre_db.get_filtered_book(book_id) log.debug(u"Start mp3 listening for %d", book_id) return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(), title=_(u"Read a Book"), entry=entries, bookmark=bookmark) @@ -1654,8 +1674,7 @@ def read_book(book_id, book_format): @web.route("/book/") @login_required_if_no_ano def show_book(book_id): - entries = calibre_db.session.query(db.Books).filter(and_(db.Books.id == book_id, - common_filters(allow_show_archived=True))).first() + entries = calibre_db.get_filtered_book(book_id, allow_show_archived=True) if entries: for index in range(0, len(entries.languages)): try: diff --git a/cps/worker.py b/cps/worker.py index 0efee596..2f5938b9 100644 --- a/cps/worker.py +++ b/cps/worker.py @@ -291,7 +291,7 @@ class WorkerThread(threading.Thread): w_session = Session() engine.execute("attach database '{}' as calibre;".format(dbpath))''' file_path = self.queue[index]['file_path'] - bookid = self.queue[index]['bookid'] + book_id = self.queue[index]['bookid'] format_old_ext = u'.' + self.queue[index]['settings']['old_book_format'].lower() format_new_ext = u'.' + self.queue[index]['settings']['new_book_format'].lower() @@ -299,15 +299,15 @@ class WorkerThread(threading.Thread): # if it does - mark the conversion task as complete and return a success # this will allow send to kindle workflow to continue to work if os.path.isfile(file_path + format_new_ext): - log.info("Book id %d already converted to %s", bookid, format_new_ext) - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == bookid).first() + log.info("Book id %d already converted to %s", book_id, format_new_ext) + cur_book = calibre_db.get_book(book_id) self.queue[index]['path'] = file_path self.queue[index]['title'] = cur_book.title self._handleSuccess() return file_path + format_new_ext else: log.info("Book id %d - target format of %s does not exist. Moving forward with convert.", - bookid, + book_id, format_new_ext) if config.config_kepubifypath and format_old_ext == '.epub' and format_new_ext == '.kepub': @@ -324,13 +324,13 @@ class WorkerThread(threading.Thread): check, error_message = self._convert_calibre(file_path, format_old_ext, format_new_ext, index) if check == 0: - cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == bookid).first() + cur_book = calibre_db.get_book(book_id) if os.path.isfile(file_path + format_new_ext): # self.db_queue.join() new_format = db.Data(name=cur_book.data[0].name, book_format=self.queue[index]['settings']['new_book_format'].upper(), - book=bookid, uncompressed_size=os.path.getsize(file_path + format_new_ext)) - task = {'task':'add_format','id': bookid, 'format': new_format} + book=book_id, uncompressed_size=os.path.getsize(file_path + format_new_ext)) + task = {'task':'add_format','id': book_id, 'format': new_format} self.db_queue.put(task) # To Do how to handle error?