Skip to content

Commit

Permalink
Catch additional error on not existing custom column linked to read c…
Browse files Browse the repository at this point in the history
…olumn (janeczku#2341)

Prevent metadata changes are lost on edit books with errors (janeczku#2326)
Better log output
Renamed log message on database delete
  • Loading branch information
OzzieIsaacs committed Mar 20, 2022
1 parent 3945960 commit 8cb5989
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 241 deletions.
2 changes: 1 addition & 1 deletion cps/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1237,7 +1237,7 @@ def _db_configuration_update_helper():
config.store_calibre_uuid(calibre_db, db.LibraryId)
# if db changed -> delete shelfs, delete download books, delete read books, kobo sync...
if db_change:
log.info("Calibre Database changed, delete all Calibre-Web info related to old Database")
log.info("Calibre Database changed, all Calibre-Web info related to old Database gets deleted")
ub.session.query(ub.Downloads).delete()
ub.session.query(ub.ArchivedBook).delete()
ub.session.query(ub.ReadBook).delete()
Expand Down
18 changes: 9 additions & 9 deletions cps/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,8 @@ def get_book_read_archived(self, book_id, read_column, allow_show_archived=False
bd = (self.session.query(Books, read_column.value, ub.ArchivedBook.is_archived).select_from(Books)
.join(read_column, read_column.book == book_id,
isouter=True))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(read_column))
# Skip linking read column and return None instead of read status
bd = self.session.query(Books, None, ub.ArchivedBook.is_archived)
return (bd.filter(Books.id == book_id)
Expand Down Expand Up @@ -665,11 +665,11 @@ def common_filters(self, allow_show_archived=False, return_all_languages=False):
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))
except (KeyError, AttributeError):
except (KeyError, AttributeError, IndexError):
pos_content_cc_filter = false()
neg_content_cc_filter = true()
log.error(u"Custom Column No.%d is not existing in calibre database",
self.config.config_restricted_column)
log.error("Custom Column No.{} is not existing in calibre database".format(
self.config.config_restricted_column))
flash(_("Custom Column No.%(column)d is not existing in calibre database",
column=self.config.config_restricted_column),
category="error")
Expand Down Expand Up @@ -727,8 +727,8 @@ def fill_indexpage_with_archived_books(self, page, database, pagesize, db_filter
query = (self.session.query(database, read_column.value, ub.ArchivedBook.is_archived)
.select_from(Books)
.outerjoin(read_column, read_column.book == Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(read_column))
# Skip linking read column and return None instead of read status
query = self.session.query(database, None, ub.ArchivedBook.is_archived)
query = query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id,
Expand Down Expand Up @@ -839,8 +839,8 @@ def search_query(self, term, config_read_column, *join):
read_column = cc_classes[config_read_column]
query = (self.session.query(Books, ub.ArchivedBook.is_archived, read_column.value).select_from(Books)
.outerjoin(read_column, read_column.book == Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config_read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config_read_column))
# Skip linking read column
query = self.session.query(Books, ub.ArchivedBook.is_archived, None)
query = query.outerjoin(ub.ArchivedBook, and_(Books.id == ub.ArchivedBook.book_id,
Expand Down
306 changes: 160 additions & 146 deletions cps/editbooks.py

Large diffs are not rendered by default.

56 changes: 29 additions & 27 deletions cps/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,9 @@ def edit_book_read_status(book_id, read_status=None):
new_cc = cc_class(value=read_status or 1, book=book_id)
calibre_db.session.add(new_cc)
calibre_db.session.commit()
except (KeyError, AttributeError):
log.error(u"Custom Column No.%d is not existing in calibre database", config.config_read_column)
except (KeyError, AttributeError, IndexError):
log.error(
"Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
return "Custom Column No.{} is not existing in calibre database".format(config.config_read_column)
except (OperationalError, InvalidRequestError) as ex:
calibre_db.session.rollback()
Expand Down Expand Up @@ -435,7 +436,8 @@ def rename_all_authors(first_author, renamed_author, calibre_path="", localbook=
new_author_path = os.path.join(calibre_path, new_author_rename_dir)
shutil.move(os.path.normcase(old_author_path), os.path.normcase(new_author_path))
except OSError as ex:
log.error_or_exception("Rename author from: %s to %s: %s", old_author_path, new_author_path, ex)
log.error("Rename author from: %s to %s: %s", old_author_path, new_author_path, ex)
log.debug(ex, exc_info=True)
return _("Rename author from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
src=old_author_path, dest=new_author_path, error=str(ex))
else:
Expand All @@ -446,39 +448,39 @@ def rename_all_authors(first_author, renamed_author, calibre_path="", localbook=
# Moves files in file storage during author/title rename, or from temp dir to file storage
def update_dir_structure_file(book_id, calibre_path, first_author, original_filepath, db_filename, renamed_author):
# get book database entry from id, if original path overwrite source with original_filepath
localbook = calibre_db.get_book(book_id)
local_book = calibre_db.get_book(book_id)
if original_filepath:
path = original_filepath
else:
path = os.path.join(calibre_path, localbook.path)
path = os.path.join(calibre_path, local_book.path)

# Create (current) authordir and titledir from database
authordir = localbook.path.split('/')[0]
titledir = localbook.path.split('/')[1]
# Create (current) author_dir and title_dir from database
author_dir = local_book.path.split('/')[0]
title_dir = local_book.path.split('/')[1]

# Create new_authordir from parameter or from database
# Create new titledir from database and add id
new_authordir = rename_all_authors(first_author, renamed_author, calibre_path, localbook)
# Create new_author_dir from parameter or from database
# Create new title_dir from database and add id
new_author_dir = rename_all_authors(first_author, renamed_author, calibre_path, local_book)
if first_author:
if first_author.lower() in [r.lower() for r in renamed_author]:
if os.path.isdir(os.path.join(calibre_path, new_authordir)):
path = os.path.join(calibre_path, new_authordir, titledir)
if os.path.isdir(os.path.join(calibre_path, new_author_dir)):
path = os.path.join(calibre_path, new_author_dir, title_dir)

new_titledir = get_valid_filename(localbook.title, chars=96) + " (" + str(book_id) + ")"
new_title_dir = get_valid_filename(local_book.title, chars=96) + " (" + str(book_id) + ")"

if titledir != new_titledir or authordir != new_authordir or original_filepath:
if title_dir != new_title_dir or author_dir != new_author_dir or original_filepath:
error = move_files_on_change(calibre_path,
new_authordir,
new_titledir,
localbook,
new_author_dir,
new_title_dir,
local_book,
db_filename,
original_filepath,
path)
if error:
return error

# Rename all files from old names to new names
return rename_files_on_change(first_author, renamed_author, localbook, original_filepath, path, calibre_path)
return rename_files_on_change(first_author, renamed_author, local_book, original_filepath, path, calibre_path)


def upload_new_file_gdrive(book_id, first_author, renamed_author, title, title_dir, original_filepath, filename_ext):
Expand All @@ -490,7 +492,7 @@ def upload_new_file_gdrive(book_id, first_author, renamed_author, title, title_d
title_dir + " (" + str(book_id) + ")")
book.path = gdrive_path.replace("\\", "/")
gd.uploadFileToEbooksFolder(os.path.join(gdrive_path, file_name).replace("\\", "/"), original_filepath)
return rename_files_on_change(first_author, renamed_author, localbook=book, gdrive=True)
return rename_files_on_change(first_author, renamed_author, local_book=book, gdrive=True)


def update_dir_structure_gdrive(book_id, first_author, renamed_author):
Expand Down Expand Up @@ -549,29 +551,29 @@ def move_files_on_change(calibre_path, new_authordir, new_titledir, localbook, d
# change location in database to new author/title path
localbook.path = os.path.join(new_authordir, new_titledir).replace('\\', '/')
except OSError as ex:
log.error_or_exception("Rename title from: %s to %s: %s", path, new_path, ex)
log.error_or_exception("Rename title from {} to {} failed with error: {}".format(path, new_path, ex))
return _("Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
src=path, dest=new_path, error=str(ex))
return False


def rename_files_on_change(first_author,
renamed_author,
localbook,
orignal_filepath="",
local_book,
original_filepath="",
path="",
calibre_path="",
gdrive=False):
# Rename all files from old names to new names
try:
clean_author_database(renamed_author, calibre_path, gdrive=gdrive)
if first_author and first_author not in renamed_author:
clean_author_database([first_author], calibre_path, localbook, gdrive)
if not gdrive and not renamed_author and not orignal_filepath and len(os.listdir(os.path.dirname(path))) == 0:
clean_author_database([first_author], calibre_path, local_book, gdrive)
if not gdrive and not renamed_author and not original_filepath and len(os.listdir(os.path.dirname(path))) == 0:
shutil.rmtree(os.path.dirname(path))
except (OSError, FileNotFoundError) as ex:
log.error_or_exception("Error in rename file in path %s", ex)
return _("Error in rename file in path: %(error)s", error=str(ex))
log.error_or_exception("Error in rename file in path {}".format(ex))
return _("Error in rename file in path: {}".format(str(ex)))
return False


Expand Down
4 changes: 2 additions & 2 deletions cps/render_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ def get_readbooks_ids():
readBooks = calibre_db.session.query(db.cc_classes[config.config_read_column])\
.filter(db.cc_classes[config.config_read_column].value == True).all()
return frozenset([x.book for x in readBooks])
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
return []

# Returns the template for rendering and includes the instance name
Expand Down
2 changes: 1 addition & 1 deletion cps/templates/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ <h3 id="decription">{{_('Description:')}}</h3>
{% if g.user.role_edit() %}
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group" aria-label="Edit/Delete book">
<a href="{{ url_for('edit-book.edit_book', book_id=entry.id) }}" class="btn btn-sm btn-primary" id="edit_book" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit Metadata')}}</a>
<a href="{{ url_for('edit-book.show_edit_book', book_id=entry.id) }}" class="btn btn-sm btn-primary" id="edit_book" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit Metadata')}}</a>
</div>
</div>
{% endif %}
Expand Down
14 changes: 8 additions & 6 deletions cps/ub.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
def signal_store_user_session(object, user):
store_user_session()


def store_user_session():
if flask_session.get('user_id', ""):
flask_session['_user_id'] = flask_session.get('user_id', "")
Expand All @@ -85,15 +86,16 @@ def store_user_session():
else:
log.error("No user id in session")


def delete_user_session(user_id, session_key):
try:
log.debug("Deleted session_key: " + session_key)
session.query(User_Sessions).filter(User_Sessions.user_id==user_id,
User_Sessions.session_key==session_key).delete()
session.query(User_Sessions).filter(User_Sessions.user_id == user_id,
User_Sessions.session_key == session_key).delete()
session.commit()
except (exc.OperationalError, exc.InvalidRequestError) as e:
except (exc.OperationalError, exc.InvalidRequestError) as ex:
session.rollback()
log.exception(e)
log.exception(ex)


def check_user_session(user_id, session_key):
Expand Down Expand Up @@ -209,9 +211,9 @@ def set_view_property(self, page, prop, value):
pass
try:
session.commit()
except (exc.OperationalError, exc.InvalidRequestError):
except (exc.OperationalError, exc.InvalidRequestError) as e:
session.rollback()
# ToDo: Error message
log.error_or_exception(e)

def __repr__(self):
return '<User %r>' % self.name
Expand Down
20 changes: 11 additions & 9 deletions cps/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def add_security_headers(resp):
csp += ''.join([' ' + host for host in config.config_trustedhosts.strip().split(',')])
csp += " 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; img-src 'self' data:"
resp.headers['Content-Security-Policy'] = csp
if request.endpoint == "edit-book.edit_book" or config.config_use_google_drive:
if request.endpoint == "edit-book.show_edit_book" or config.config_use_google_drive:
resp.headers['Content-Security-Policy'] += " *"
elif request.endpoint == "web.read_book":
resp.headers['Content-Security-Policy'] += " blob:;style-src-elem 'self' blob: 'unsafe-inline';"
Expand Down Expand Up @@ -646,8 +646,8 @@ def render_read_books(page, are_read, as_xml=False, order=None):
db.Books.id == db.books_series_link.c.book,
db.Series,
db.cc_classes[config.config_read_column])
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
if not as_xml:
flash(_("Custom Column No.%(column)d is not existing in calibre database",
column=config.config_read_column),
Expand Down Expand Up @@ -826,8 +826,9 @@ def list_books():
books = (calibre_db.session.query(db.Books, read_column.value, ub.ArchivedBook.is_archived)
.select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", read_column)
except (KeyError, AttributeError, IndexError):
log.error(
"Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
# Skip linking read column and return None instead of read status
books = calibre_db.session.query(db.Books, None, ub.ArchivedBook.is_archived)
books = (books.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
Expand Down Expand Up @@ -1139,8 +1140,9 @@ def adv_search_read_status(q, read_status):
else:
q = q.join(db.cc_classes[config.config_read_column], isouter=True) \
.filter(coalesce(db.cc_classes[config.config_read_column].value, False) != True)
except (KeyError, AttributeError):
log.error(u"Custom Column No.%d is not existing in calibre database", config.config_read_column)
except (KeyError, AttributeError, IndexError):
log.error(
"Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
flash(_("Custom Column No.%(column)d is not existing in calibre database",
column=config.config_read_column),
category="error")
Expand Down Expand Up @@ -1262,8 +1264,8 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, read_column.value)
.select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
except (KeyError, AttributeError, IndexError):
log.error("Custom Column No.{} is not existing in calibre database".format(config.config_read_column))
# Skip linking read column
query = calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, None)
query = query.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
Expand Down
4 changes: 2 additions & 2 deletions optional-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# GDrive Integration
google-api-python-client>=1.7.11,<2.41.0
google-api-python-client>=1.7.11,<2.42.0
gevent>20.6.0,<22.0.0
greenlet>=0.4.17,<1.2.0
httplib2>=0.9.2,<0.21.0
Expand All @@ -13,7 +13,7 @@ rsa>=3.4.2,<4.9.0

# Gmail
google-auth-oauthlib>=0.4.3,<0.6.0
google-api-python-client>=1.7.11,<2.41.0
google-api-python-client>=1.7.11,<2.42.0

# goodreads
goodreads>=0.3.2,<0.4.0
Expand Down
Loading

0 comments on commit 8cb5989

Please sign in to comment.