mirror of
https://github.com/JonathanHerrewijnen/calibre-web.git
synced 2024-11-10 21:24:15 +00:00
Catch additional error on not existing custom column linked to read column (#2341)
Prevent metadata changes are lost on edit books with errors (#2326) Better log output Renamed log message on database delete
This commit is contained in:
parent
39459603d4
commit
8cb5989c97
@ -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()
|
||||
|
18
cps/db.py
18
cps/db.py
@ -620,8 +620,8 @@ class CalibreDB:
|
||||
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)
|
||||
@ -665,11 +665,11 @@ class CalibreDB:
|
||||
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")
|
||||
@ -727,8 +727,8 @@ class CalibreDB:
|
||||
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,
|
||||
@ -839,8 +839,8 @@ class CalibreDB:
|
||||
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,
|
||||
|
306
cps/editbooks.py
306
cps/editbooks.py
@ -289,13 +289,13 @@ def delete_whole_book(book_id, book):
|
||||
def render_delete_book_result(book_format, json_response, warning, book_id):
|
||||
if book_format:
|
||||
if json_response:
|
||||
return json.dumps([warning, {"location": url_for("edit-book.edit_book", book_id=book_id),
|
||||
return json.dumps([warning, {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "success",
|
||||
"format": book_format,
|
||||
"message": _('Book Format Successfully Deleted')}])
|
||||
else:
|
||||
flash(_('Book Format Successfully Deleted'), category="success")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
else:
|
||||
if json_response:
|
||||
return json.dumps([warning, {"location": url_for('web.index'),
|
||||
@ -316,16 +316,16 @@ def delete_book_from_table(book_id, book_format, json_response):
|
||||
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
|
||||
if not result:
|
||||
if json_response:
|
||||
return json.dumps([{"location": url_for("edit-book.edit_book", book_id=book_id),
|
||||
return json.dumps([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": error}])
|
||||
else:
|
||||
flash(error, category="error")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
if error:
|
||||
if json_response:
|
||||
warning = {"location": url_for("edit-book.edit_book", book_id=book_id),
|
||||
warning = {"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "warning",
|
||||
"format": "",
|
||||
"message": error}
|
||||
@ -343,13 +343,13 @@ def delete_book_from_table(book_id, book_format, json_response):
|
||||
log.error_or_exception(ex)
|
||||
calibre_db.session.rollback()
|
||||
if json_response:
|
||||
return json.dumps([{"location": url_for("edit-book.edit_book", book_id=book_id),
|
||||
return json.dumps([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": ex}])
|
||||
else:
|
||||
flash(str(ex), category="error")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
else:
|
||||
# book not found
|
||||
@ -357,13 +357,13 @@ def delete_book_from_table(book_id, book_format, json_response):
|
||||
return render_delete_book_result(book_format, json_response, warning, book_id)
|
||||
message = _("You are missing permissions to delete books")
|
||||
if json_response:
|
||||
return json.dumps({"location": url_for("edit-book.edit_book", book_id=book_id),
|
||||
return json.dumps({"location": url_for("edit-book.show_edit_book", book_id=book_id),
|
||||
"type": "danger",
|
||||
"format": "",
|
||||
"message": message})
|
||||
else:
|
||||
flash(message, category="error")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
|
||||
def render_edit_book(book_id):
|
||||
@ -413,18 +413,18 @@ def render_edit_book(book_id):
|
||||
|
||||
def edit_book_ratings(to_save, book):
|
||||
changed = False
|
||||
if to_save["rating"].strip():
|
||||
if to_save.get("rating","").strip():
|
||||
old_rating = False
|
||||
if len(book.ratings) > 0:
|
||||
old_rating = book.ratings[0].rating
|
||||
ratingx2 = int(float(to_save["rating"]) * 2)
|
||||
if ratingx2 != old_rating:
|
||||
rating_x2 = int(float(to_save.get("rating","")) * 2)
|
||||
if rating_x2 != old_rating:
|
||||
changed = True
|
||||
is_rating = calibre_db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
|
||||
is_rating = calibre_db.session.query(db.Ratings).filter(db.Ratings.rating == rating_x2).first()
|
||||
if is_rating:
|
||||
book.ratings.append(is_rating)
|
||||
else:
|
||||
new_rating = db.Ratings(rating=ratingx2)
|
||||
new_rating = db.Ratings(rating=rating_x2)
|
||||
book.ratings.append(new_rating)
|
||||
if old_rating:
|
||||
book.ratings.remove(book.ratings[0])
|
||||
@ -622,24 +622,26 @@ def edit_cc_data(book_id, book, to_save, cc):
|
||||
'custom')
|
||||
return changed
|
||||
|
||||
|
||||
# returns None if no file is uploaded
|
||||
# returns False if an error occours, in all other cases the ebook metadata is returned
|
||||
def upload_single_file(file_request, book, book_id):
|
||||
# Check and handle Uploaded file
|
||||
if 'btn-upload-format' in file_request.files:
|
||||
requested_file = file_request.files['btn-upload-format']
|
||||
requested_file = file_request.files.get('btn-upload-format', None)
|
||||
if requested_file:
|
||||
# check for empty request
|
||||
if requested_file.filename != '':
|
||||
if not current_user.role_upload():
|
||||
abort(403)
|
||||
flash(_(u"User has no rights to upload additional file formats"), category="error")
|
||||
return False
|
||||
if '.' in requested_file.filename:
|
||||
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
|
||||
if file_ext not in constants.EXTENSIONS_UPLOAD and '' not in constants.EXTENSIONS_UPLOAD:
|
||||
flash(_("File extension '%(ext)s' is not allowed to be uploaded to this server", ext=file_ext),
|
||||
category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
return False
|
||||
else:
|
||||
flash(_('File to be uploaded must have an extension'), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
return False
|
||||
|
||||
file_name = book.path.rsplit('/', 1)[-1]
|
||||
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
|
||||
@ -651,12 +653,12 @@ def upload_single_file(file_request, book, book_id):
|
||||
os.makedirs(filepath)
|
||||
except OSError:
|
||||
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
return False
|
||||
try:
|
||||
requested_file.save(saved_filename)
|
||||
except OSError:
|
||||
flash(_(u"Failed to store file %(file)s.", file=saved_filename), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
return False
|
||||
|
||||
file_size = os.path.getsize(saved_filename)
|
||||
is_format = calibre_db.get_book_format(book_id, file_ext.upper())
|
||||
@ -674,7 +676,7 @@ def upload_single_file(file_request, book, book_id):
|
||||
calibre_db.session.rollback()
|
||||
log.error_or_exception("Database error: {}".format(e))
|
||||
flash(_(u"Database error: %(error)s.", error=e.orig), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
return False # return redirect(url_for('web.show_book', book_id=book.id))
|
||||
|
||||
# Queue uploader info
|
||||
link = '<a href="{}">{}</a>'.format(url_for('web.show_book', book_id=book.id), escape(book.title))
|
||||
@ -684,15 +686,16 @@ def upload_single_file(file_request, book, book_id):
|
||||
return uploader.process(
|
||||
saved_filename, *os.path.splitext(requested_file.filename),
|
||||
rarExecutable=config.config_rarfile_location)
|
||||
|
||||
return None
|
||||
|
||||
def upload_cover(cover_request, book):
|
||||
if 'btn-upload-cover' in cover_request.files:
|
||||
requested_file = cover_request.files['btn-upload-cover']
|
||||
requested_file = cover_request.files.get('btn-upload-cover', None)
|
||||
if requested_file:
|
||||
# check for empty request
|
||||
if requested_file.filename != '':
|
||||
if not current_user.role_upload():
|
||||
abort(403)
|
||||
flash(_(u"User has no rights to upload cover"), category="error")
|
||||
return False
|
||||
ret, message = helper.save_cover(requested_file, book.path)
|
||||
if ret is True:
|
||||
return True
|
||||
@ -716,25 +719,6 @@ def handle_title_on_edit(book, book_title):
|
||||
def handle_author_on_edit(book, author_name, update_stored=True):
|
||||
# handle author(s)
|
||||
input_authors, renamed = prepare_authors(author_name)
|
||||
'''input_authors = author_name.split('&')
|
||||
input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
|
||||
# Remove duplicates in authors list
|
||||
input_authors = helper.uniq(input_authors)
|
||||
# we have all author names now
|
||||
if input_authors == ['']:
|
||||
input_authors = [_(u'Unknown')] # prevent empty Author
|
||||
|
||||
renamed = list()
|
||||
for in_aut in input_authors:
|
||||
renamed_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == in_aut).first()
|
||||
if renamed_author and in_aut != renamed_author.name:
|
||||
renamed.append(renamed_author.name)
|
||||
all_books = calibre_db.session.query(db.Books) \
|
||||
.filter(db.Books.authors.any(db.Authors.name == renamed_author.name)).all()
|
||||
sorted_renamed_author = helper.get_sorted_author(renamed_author.name)
|
||||
sorted_old_author = helper.get_sorted_author(in_aut)
|
||||
for one_book in all_books:
|
||||
one_book.author_sort = one_book.author_sort.replace(sorted_renamed_author, sorted_old_author)'''
|
||||
|
||||
change = modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author')
|
||||
|
||||
@ -754,12 +738,19 @@ def handle_author_on_edit(book, author_name, update_stored=True):
|
||||
change = True
|
||||
return input_authors, change, renamed
|
||||
|
||||
@EditBook.route("/admin/book/<int:book_id>", methods=['GET'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def show_edit_book(book_id):
|
||||
return render_edit_book(book_id)
|
||||
|
||||
@EditBook.route("/admin/book/<int:book_id>", methods=['GET', 'POST'])
|
||||
|
||||
@EditBook.route("/admin/book/<int:book_id>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_book(book_id):
|
||||
modify_date = False
|
||||
edit_error = False
|
||||
|
||||
# create the function for sorting...
|
||||
try:
|
||||
@ -768,109 +759,120 @@ def edit_book(book_id):
|
||||
log.error_or_exception(e)
|
||||
calibre_db.session.rollback()
|
||||
|
||||
# Show form
|
||||
if request.method != 'POST':
|
||||
return render_edit_book(book_id)
|
||||
|
||||
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
|
||||
|
||||
# Book not found
|
||||
if not book:
|
||||
flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"),
|
||||
category="error")
|
||||
return redirect(url_for("web.index"))
|
||||
|
||||
meta = upload_single_file(request, book, book_id)
|
||||
if upload_cover(request, book) is True:
|
||||
book.has_cover = 1
|
||||
modify_date = True
|
||||
to_save = request.form.to_dict()
|
||||
|
||||
try:
|
||||
to_save = request.form.to_dict()
|
||||
merge_metadata(to_save, meta)
|
||||
# Update book
|
||||
# Update folder of book on local disk
|
||||
edited_books_id = None
|
||||
|
||||
# handle book title
|
||||
title_author_error = None
|
||||
# handle book title change
|
||||
title_change = handle_title_on_edit(book, to_save["book_title"])
|
||||
|
||||
input_authors, authorchange, renamed = handle_author_on_edit(book, to_save["author_name"])
|
||||
if authorchange or title_change:
|
||||
# handle book author change
|
||||
input_authors, author_change, renamed = handle_author_on_edit(book, to_save["author_name"])
|
||||
if author_change or title_change:
|
||||
edited_books_id = book.id
|
||||
modify_date = True
|
||||
title_author_error = helper.update_dir_structure(edited_books_id,
|
||||
config.config_calibre_dir,
|
||||
input_authors[0],
|
||||
renamed_author=renamed)
|
||||
if title_author_error:
|
||||
flash(title_author_error, category="error")
|
||||
calibre_db.session.rollback()
|
||||
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
|
||||
|
||||
# handle upload other formats from local disk
|
||||
meta = upload_single_file(request, book, book_id)
|
||||
# only merge metadata if file was uploaded and no error occurred (meta equals not false or none)
|
||||
if meta:
|
||||
merge_metadata(to_save, meta)
|
||||
# handle upload covers from local disk
|
||||
cover_upload_success = upload_cover(request, book)
|
||||
if cover_upload_success:
|
||||
book.has_cover = 1
|
||||
modify_date = True
|
||||
|
||||
# upload new covers or new file formats to google drive
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
|
||||
error = ""
|
||||
if edited_books_id:
|
||||
error = helper.update_dir_structure(edited_books_id, config.config_calibre_dir, input_authors[0],
|
||||
renamed_author=renamed)
|
||||
if to_save.get("cover_url", None):
|
||||
if not current_user.role_upload():
|
||||
edit_error = True
|
||||
flash(_(u"User has no rights to upload cover"), category="error")
|
||||
if to_save["cover_url"].endswith('/static/generic_cover.jpg'):
|
||||
book.has_cover = 0
|
||||
else:
|
||||
result, error = helper.save_cover_from_url(to_save["cover_url"], book.path)
|
||||
if result is True:
|
||||
book.has_cover = 1
|
||||
modify_date = True
|
||||
else:
|
||||
flash(error, category="error")
|
||||
|
||||
if not error:
|
||||
if "cover_url" in to_save:
|
||||
if to_save["cover_url"]:
|
||||
if not current_user.role_upload():
|
||||
calibre_db.session.rollback()
|
||||
return "", 403
|
||||
if to_save["cover_url"].endswith('/static/generic_cover.jpg'):
|
||||
book.has_cover = 0
|
||||
else:
|
||||
result, error = helper.save_cover_from_url(to_save["cover_url"], book.path)
|
||||
if result is True:
|
||||
book.has_cover = 1
|
||||
modify_date = True
|
||||
else:
|
||||
flash(error, category="error")
|
||||
|
||||
# Add default series_index to book
|
||||
modify_date |= edit_book_series_index(to_save["series_index"], book)
|
||||
# Handle book comments/description
|
||||
modify_date |= edit_book_comments(Markup(to_save['description']).unescape(), book)
|
||||
# Handle identifiers
|
||||
input_identifiers = identifier_list(to_save, book)
|
||||
modification, warning = modify_identifiers(input_identifiers, book.identifiers, calibre_db.session)
|
||||
if warning:
|
||||
flash(_("Identifiers are not Case Sensitive, Overwriting Old Identifier"), category="warning")
|
||||
modify_date |= modification
|
||||
# Handle book tags
|
||||
modify_date |= edit_book_tags(to_save['tags'], book)
|
||||
# Handle book series
|
||||
modify_date |= edit_book_series(to_save["series"], book)
|
||||
# handle book publisher
|
||||
modify_date |= edit_book_publisher(to_save['publisher'], book)
|
||||
# handle book languages
|
||||
# Add default series_index to book
|
||||
modify_date |= edit_book_series_index(to_save["series_index"], book)
|
||||
# Handle book comments/description
|
||||
modify_date |= edit_book_comments(Markup(to_save['description']).unescape(), book)
|
||||
# Handle identifiers
|
||||
input_identifiers = identifier_list(to_save, book)
|
||||
modification, warning = modify_identifiers(input_identifiers, book.identifiers, calibre_db.session)
|
||||
if warning:
|
||||
flash(_("Identifiers are not Case Sensitive, Overwriting Old Identifier"), category="warning")
|
||||
modify_date |= modification
|
||||
# Handle book tags
|
||||
modify_date |= edit_book_tags(to_save['tags'], book)
|
||||
# Handle book series
|
||||
modify_date |= edit_book_series(to_save["series"], book)
|
||||
# handle book publisher
|
||||
modify_date |= edit_book_publisher(to_save['publisher'], book)
|
||||
# handle book languages
|
||||
try:
|
||||
modify_date |= edit_book_languages(to_save['languages'], book)
|
||||
# handle book ratings
|
||||
modify_date |= edit_book_ratings(to_save, book)
|
||||
# handle cc data
|
||||
modify_date |= edit_all_cc_data(book_id, book, to_save)
|
||||
except ValueError as e:
|
||||
flash(str(e), category="error")
|
||||
edit_error = True
|
||||
# handle book ratings
|
||||
modify_date |= edit_book_ratings(to_save, book)
|
||||
# handle cc data
|
||||
modify_date |= edit_all_cc_data(book_id, book, to_save)
|
||||
|
||||
if to_save["pubdate"]:
|
||||
try:
|
||||
book.pubdate = datetime.strptime(to_save["pubdate"], "%Y-%m-%d")
|
||||
except ValueError:
|
||||
book.pubdate = db.Books.DEFAULT_PUBDATE
|
||||
else:
|
||||
if to_save.get("pubdate", None):
|
||||
try:
|
||||
book.pubdate = datetime.strptime(to_save["pubdate"], "%Y-%m-%d")
|
||||
except ValueError as e:
|
||||
book.pubdate = db.Books.DEFAULT_PUBDATE
|
||||
|
||||
if modify_date:
|
||||
book.last_modified = datetime.utcnow()
|
||||
kobo_sync_status.remove_synced_book(edited_books_id, all=True)
|
||||
|
||||
calibre_db.session.merge(book)
|
||||
calibre_db.session.commit()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
if "detail_view" in to_save:
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
else:
|
||||
flash(_("Metadata successfully updated"), category="success")
|
||||
return render_edit_book(book_id)
|
||||
flash(str(e), category="error")
|
||||
edit_error = True
|
||||
else:
|
||||
book.pubdate = db.Books.DEFAULT_PUBDATE
|
||||
|
||||
if modify_date:
|
||||
book.last_modified = datetime.utcnow()
|
||||
kobo_sync_status.remove_synced_book(edited_books_id, all=True)
|
||||
|
||||
calibre_db.session.merge(book)
|
||||
calibre_db.session.commit()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
if meta is not False \
|
||||
and edit_error is not True \
|
||||
and title_author_error is not True \
|
||||
and cover_upload_success is not False:
|
||||
flash(_("Metadata successfully updated"), category="success")
|
||||
if "detail_view" in to_save:
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
else:
|
||||
calibre_db.session.rollback()
|
||||
flash(error, category="error")
|
||||
return render_edit_book(book_id)
|
||||
except ValueError as e:
|
||||
log.error_or_exception("Error: {}".format(e))
|
||||
calibre_db.session.rollback()
|
||||
flash(str(e), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
@ -882,14 +884,14 @@ def edit_book(book_id):
|
||||
except Exception as ex:
|
||||
log.error_or_exception(ex)
|
||||
calibre_db.session.rollback()
|
||||
flash(_("Error editing book, please check logfile for details"), category="error")
|
||||
flash(_("Error editing book: {}".format(ex)), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
|
||||
|
||||
def merge_metadata(to_save, meta):
|
||||
if to_save['author_name'] == _(u'Unknown'):
|
||||
if to_save.get('author_name', "") == _(u'Unknown'):
|
||||
to_save['author_name'] = ''
|
||||
if to_save['book_title'] == _(u'Unknown'):
|
||||
if to_save.get('book_title', "") == _(u'Unknown'):
|
||||
to_save['book_title'] = ''
|
||||
for s_field, m_field in [
|
||||
('tags', 'tags'), ('author_name', 'author'), ('series', 'series'),
|
||||
@ -1117,7 +1119,7 @@ def upload():
|
||||
|
||||
if len(request.files.getlist("btn-upload")) < 2:
|
||||
if current_user.role_edit() or current_user.role_admin():
|
||||
resp = {"location": url_for('edit-book.edit_book', book_id=book_id)}
|
||||
resp = {"location": url_for('edit-book.show_edit_book', book_id=book_id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
else:
|
||||
resp = {"location": url_for('web.show_book', book_id=book_id)}
|
||||
@ -1139,7 +1141,7 @@ def convert_bookformat(book_id):
|
||||
|
||||
if (book_format_from is None) or (book_format_to is None):
|
||||
flash(_(u"Source or destination format for conversion missing"), category="error")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
log.info('converting: book id: %s from: %s to: %s', book_id, book_format_from, book_format_to)
|
||||
rtn = helper.convert_book_format(book_id, config.config_calibre_dir, book_format_from.upper(),
|
||||
@ -1151,7 +1153,7 @@ def convert_bookformat(book_id):
|
||||
category="success")
|
||||
else:
|
||||
flash(_(u"There was an error converting this book: %(res)s", res=rtn), category="error")
|
||||
return redirect(url_for('edit-book.edit_book', book_id=book_id))
|
||||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
|
||||
@EditBook.route("/ajax/getcustomenum/<int:c_id>")
|
||||
@ -1211,10 +1213,15 @@ def edit_list_book(param):
|
||||
mimetype='application/json')
|
||||
elif param == 'title':
|
||||
sort_param = book.sort
|
||||
handle_title_on_edit(book, vals.get('value', ""))
|
||||
helper.update_dir_structure(book.id, config.config_calibre_dir)
|
||||
ret = Response(json.dumps({'success': True, 'newValue': book.title}),
|
||||
mimetype='application/json')
|
||||
if handle_title_on_edit(book, vals.get('value', "")):
|
||||
rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir)
|
||||
if not rename_error:
|
||||
ret = Response(json.dumps({'success': True, 'newValue': book.title}),
|
||||
mimetype='application/json')
|
||||
else:
|
||||
ret = Response(json.dumps({'success': False,
|
||||
'msg': rename_error}),
|
||||
mimetype='application/json')
|
||||
elif param == 'sort':
|
||||
book.sort = vals['value']
|
||||
ret = Response(json.dumps({'success': True, 'newValue': book.sort}),
|
||||
@ -1225,11 +1232,17 @@ def edit_list_book(param):
|
||||
mimetype='application/json')
|
||||
elif param == 'authors':
|
||||
input_authors, __, renamed = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true")
|
||||
helper.update_dir_structure(book.id, config.config_calibre_dir, input_authors[0], renamed_author=renamed)
|
||||
ret = Response(json.dumps({
|
||||
'success': True,
|
||||
'newValue': ' & '.join([author.replace('|', ',') for author in input_authors])}),
|
||||
mimetype='application/json')
|
||||
rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir, input_authors[0],
|
||||
renamed_author=renamed)
|
||||
if not rename_error:
|
||||
ret = Response(json.dumps({
|
||||
'success': True,
|
||||
'newValue': ' & '.join([author.replace('|', ',') for author in input_authors])}),
|
||||
mimetype='application/json')
|
||||
else:
|
||||
ret = Response(json.dumps({'success': False,
|
||||
'msg': rename_error}),
|
||||
mimetype='application/json')
|
||||
elif param == 'is_archived':
|
||||
is_archived = change_archived_books(book.id, vals['value'] == "True",
|
||||
message="Book {} archive bit set to: {}".format(book.id, vals['value']))
|
||||
@ -1356,8 +1369,8 @@ def table_xchange_author_title():
|
||||
author_names.append(authr.name.replace('|', ','))
|
||||
|
||||
title_change = handle_title_on_edit(book, " ".join(author_names))
|
||||
input_authors, authorchange, renamed = handle_author_on_edit(book, authors)
|
||||
if authorchange or title_change:
|
||||
input_authors, author_change, renamed = handle_author_on_edit(book, authors)
|
||||
if author_change or title_change:
|
||||
edited_books_id = book.id
|
||||
modify_date = True
|
||||
|
||||
@ -1365,8 +1378,9 @@ def table_xchange_author_title():
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
|
||||
if edited_books_id:
|
||||
helper.update_dir_structure(edited_books_id, config.config_calibre_dir, input_authors[0],
|
||||
renamed_author=renamed)
|
||||
# toDo: Handle error
|
||||
edit_error = helper.update_dir_structure(edited_books_id, config.config_calibre_dir, input_authors[0],
|
||||
renamed_author=renamed)
|
||||
if modify_date:
|
||||
book.last_modified = datetime.utcnow()
|
||||
try:
|
||||
|
@ -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()
|
||||
@ -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:
|
||||
@ -446,31 +448,31 @@ 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)
|
||||
@ -478,7 +480,7 @@ def update_dir_structure_file(book_id, calibre_path, first_author, original_file
|
||||
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):
|
||||
@ -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):
|
||||
@ -549,7 +551,7 @@ 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
|
||||
@ -557,8 +559,8 @@ def move_files_on_change(calibre_path, new_authordir, new_titledir, localbook, d
|
||||
|
||||
def rename_files_on_change(first_author,
|
||||
renamed_author,
|
||||
localbook,
|
||||
orignal_filepath="",
|
||||
local_book,
|
||||
original_filepath="",
|
||||
path="",
|
||||
calibre_path="",
|
||||
gdrive=False):
|
||||
@ -566,12 +568,12 @@ def rename_files_on_change(first_author,
|
||||
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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -295,7 +295,7 @@
|
||||
{% 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 %}
|
||||
|
14
cps/ub.py
14
cps/ub.py
@ -67,6 +67,7 @@ logged_in = dict()
|
||||
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', "")
|
||||
@ -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):
|
||||
@ -209,9 +211,9 @@ class UserBase:
|
||||
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
|
||||
|
20
cps/web.py
20
cps/web.py
@ -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';"
|
||||
@ -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),
|
||||
@ -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,
|
||||
@ -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")
|
||||
@ -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,
|
||||
|
@ -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
|
||||
@ -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
|
||||
|
@ -37,20 +37,20 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
|
||||
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2022-03-14 21:13:33</p>
|
||||
<p class='text-justify attribute'><strong>Start Time: </strong>2022-03-19 22:04:05</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2022-03-15 02:05:20</p>
|
||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2022-03-20 02:50:09</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>4h 3 min</p>
|
||||
<p class='text-justify attribute'><strong>Duration: </strong>3h 58 min</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1589,8 +1589,8 @@
|
||||
<tr id="su" class="failClass">
|
||||
<td>TestEditBooksOnGdrive</td>
|
||||
<td class="text-center">20</td>
|
||||
<td class="text-center">17</td>
|
||||
<td class="text-center">3</td>
|
||||
<td class="text-center">16</td>
|
||||
<td class="text-center">4</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">
|
||||
@ -1735,11 +1735,31 @@
|
||||
|
||||
|
||||
|
||||
<tr id='pt16.16' class='hiddenRow bg-success'>
|
||||
<tr id="ft16.16" class="none bg-danger">
|
||||
<td>
|
||||
<div class='testcase'>TestEditBooksOnGdrive - test_edit_title</div>
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft16.16')">FAIL</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id="div_ft16.16" class="popup_window test_output" style="display:block;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||
onclick="document.getElementById('div_ft16.16').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 145, in test_edit_title
|
||||
self.assertTrue(self.check_element_on_page((By.ID, 'flash_success')))
|
||||
AssertionError: False is not true</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@ -1761,7 +1781,7 @@
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 853, in test_upload_book_epub
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 855, in test_upload_book_epub
|
||||
self.assertEqual('8936', resp.headers['Content-Length'])
|
||||
AssertionError: '8936' != '1103'
|
||||
- 8936
|
||||
@ -1801,7 +1821,7 @@ AssertionError: '8936' != '1103'
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 766, in test_upload_cover_hdd
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 768, in test_upload_cover_hdd
|
||||
self.assertGreater(diff('original.png', 'jpeg.png', delete_diff_file=True), 0.02)
|
||||
AssertionError: 0.0 not greater than 0.02</pre>
|
||||
</div>
|
||||
@ -1830,9 +1850,9 @@ AssertionError: 0.0 not greater than 0.02</pre>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 935, in test_watch_metadata
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 937, in test_watch_metadata
|
||||
self.assertNotIn('series', book)
|
||||
AssertionError: 'series' unexpectedly found in {'id': 5, 'reader': [], 'title': 'testbook', 'author': ['John Döe'], 'rating': 0, 'languages': ['English'], 'identifier': [], 'cover': '/cover/5?edit=2e081d1c-86d2-461f-a309-e51e1e378161', 'tag': [], 'publisher': ['Randomhäus'], 'pubdate': 'Jan 19, 2017', 'comment': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate', 'add_shelf': [], 'del_shelf': [], 'edit_enable': True, 'kindle': None, 'kindlebtn': None, 'download': ['EPUB (6.7 kB)'], 'read': False, 'archived': False, 'series_all': 'Book 1 of test', 'series_index': '1', 'series': 'test', 'cust_columns': []}</pre>
|
||||
AssertionError: 'series' unexpectedly found in {'id': 5, 'reader': [], 'title': 'testbook', 'author': ['John Döe'], 'rating': 0, 'languages': ['English'], 'identifier': [], 'cover': '/cover/5?edit=34e51cc2-2413-4a23-8324-26d568a421ba', 'tag': [], 'publisher': ['Randomhäus'], 'pubdate': 'Jan 19, 2017', 'comment': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate', 'add_shelf': [], 'del_shelf': [], 'edit_enable': True, 'kindle': None, 'kindlebtn': None, 'download': ['EPUB (6.7 kB)'], 'read': False, 'archived': False, 'series_all': 'Book 1 of test', 'series_index': '1', 'series': 'test', 'cust_columns': []}</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@ -2825,34 +2845,65 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
|
||||
|
||||
<tr id="su" class="passClass">
|
||||
<td>TestMergeBooksList</td>
|
||||
<td class="text-center">2</td>
|
||||
<td class="text-center">2</td>
|
||||
<tr id="su" class="errorClass">
|
||||
<td>_ErrorHolder</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">0</td>
|
||||
<td class="text-center">
|
||||
<a onclick="showClassDetail('c31', 2)">Detail</a>
|
||||
<a onclick="showClassDetail('c31', 1)">Detail</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr id='pt31.1' class='hiddenRow bg-success'>
|
||||
<tr id="et31.1" class="none bg-info">
|
||||
<td>
|
||||
<div class='testcase'>TestMergeBooksList - test_book_merge</div>
|
||||
<div class='testcase'>setUpClass (test_merge_books_list)</div>
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr id='pt31.2' class='hiddenRow bg-success'>
|
||||
<td>
|
||||
<div class='testcase'>TestMergeBooksList - test_delete_book</div>
|
||||
<td colspan='6'>
|
||||
<div class="text-center">
|
||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_et31.1')">ERROR</a>
|
||||
</div>
|
||||
<!--css div popup start-->
|
||||
<div id="div_et31.1" class="popup_window test_output" style="display:block;">
|
||||
<div class='close_button pull-right'>
|
||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||
onclick="document.getElementById('div_et31.1').style.display='none'"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="text-left pull-left">
|
||||
<pre class="text-left">Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_merge_books_list.py", line 24, in setUpClass
|
||||
startup(cls, cls.py_version, {'config_calibre_dir': TEST_DB})
|
||||
File "/home/ozzie/Development/calibre-web-test/test/helper_func.py", line 175, in startup
|
||||
inst.driver = webdriver.Firefox()
|
||||
File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/firefox/webdriver.py", line 178, in __init__
|
||||
RemoteWebDriver.__init__(
|
||||
File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 269, in __init__
|
||||
self.start_session(capabilities, browser_profile)
|
||||
File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 360, in start_session
|
||||
response = self.execute(Command.NEW_SESSION, parameters)
|
||||
File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 425, in execute
|
||||
self.error_handler.check_response(response)
|
||||
File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py", line 247, in check_response
|
||||
raise exception_class(message, screen, stacktrace)
|
||||
selenium.common.exceptions.WebDriverException: Message: Process unexpectedly closed with status 0
|
||||
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/home/ozzie/Development/calibre-web-test/test/test_merge_books_list.py", line 28, in setUpClass
|
||||
cls.driver.quit()
|
||||
AttributeError: 'NoneType' object has no attribute 'quit'</pre>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<!--css div popup end-->
|
||||
</td>
|
||||
<td colspan='6' align='center'>PASS</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@ -4600,10 +4651,10 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr id='total_row' class="text-center bg-grey">
|
||||
<td>Total</td>
|
||||
<td>405</td>
|
||||
<td>396</td>
|
||||
<td>3</td>
|
||||
<td>0</td>
|
||||
<td>404</td>
|
||||
<td>393</td>
|
||||
<td>4</td>
|
||||
<td>1</td>
|
||||
<td>6</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
@ -4776,7 +4827,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestCliGdrivedb</td>
|
||||
</tr>
|
||||
|
||||
@ -4806,7 +4857,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestEbookConvertCalibreGDrive</td>
|
||||
</tr>
|
||||
|
||||
@ -4836,7 +4887,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestEbookConvertGDriveKepubify</td>
|
||||
</tr>
|
||||
|
||||
@ -4878,7 +4929,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestEditAuthorsGdrive</td>
|
||||
</tr>
|
||||
|
||||
@ -4914,7 +4965,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestEditBooksOnGdrive</td>
|
||||
</tr>
|
||||
|
||||
@ -4956,7 +5007,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
|
||||
<tr>
|
||||
<th>google-api-python-client</th>
|
||||
<td>2.40.0</td>
|
||||
<td>2.41.0</td>
|
||||
<td>TestSetupGdrive</td>
|
||||
</tr>
|
||||
|
||||
@ -5046,7 +5097,7 @@ AssertionError: 'series' unexpectedly found in {'id': 5, 're
|
||||
</div>
|
||||
|
||||
<script>
|
||||
drawCircle(396, 3, 0, 6);
|
||||
drawCircle(393, 4, 1, 6);
|
||||
showCase(5);
|
||||
</script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user