From 95ca1e6a9dab8dbff31fef6635648e391a006cdd Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 20 Apr 2020 18:56:39 +0200 Subject: [PATCH] Fix for #1319 --- cps/editbooks.py | 147 ++++++++++++++++++++++++++++++----------------- cps/web.py | 10 ++-- 2 files changed, 98 insertions(+), 59 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 28a8d2a6..5bf36139 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -22,7 +22,7 @@ from __future__ import division, print_function, unicode_literals import os -import datetime +from datetime import datetime import json from shutil import move, copyfile from uuid import uuid4 @@ -47,7 +47,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session # passing input_elements not as a list may lead to undesired results if not isinstance(input_elements, list): raise TypeError(str(input_elements) + " should be passed as a list") - + changed = False input_elements = [x for x in input_elements if x != ''] # we have all input element (authors, series, tags) names now # 1. search for elements to remove @@ -88,6 +88,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session if len(del_elements) > 0: for del_element in del_elements: db_book_object.remove(del_element) + changed = True if len(del_element.books) == 0: db_session.delete(del_element) # if there are elements to add, we add them now! @@ -114,49 +115,48 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session else: # db_type should be tag or language new_element = db_object(add_element) if db_element is None: + changed = True db_session.add(new_element) db_book_object.append(new_element) else: if db_type == 'custom': if db_element.value != add_element: new_element.value = add_element - # new_element = db_element elif db_type == 'languages': if db_element.lang_code != add_element: db_element.lang_code = add_element - # new_element = db_element elif db_type == 'series': if db_element.name != add_element: - db_element.name = add_element # = add_element # new_element = db_object(add_element, add_element) + db_element.name = add_element db_element.sort = add_element - # new_element = db_element elif db_type == 'author': if db_element.name != add_element: db_element.name = add_element db_element.sort = add_element.replace('|', ',') - # new_element = db_element elif db_type == 'publisher': if db_element.name != add_element: db_element.name = add_element db_element.sort = None - # new_element = db_element elif db_element.name != add_element: db_element.name = add_element - # new_element = db_element # add element to book + changed = True db_book_object.append(db_element) + return changed def modify_identifiers(input_identifiers, db_identifiers, db_session): """Modify Identifiers to match input information. input_identifiers is a list of read-to-persist Identifiers objects. db_identifiers is a list of already persisted list of Identifiers objects.""" + changed = False input_dict = dict([ (identifier.type.lower(), identifier) for identifier in input_identifiers ]) db_dict = dict([ (identifier.type.lower(), identifier) for identifier in db_identifiers ]) # delete db identifiers not present in input or modify them with input val for identifier_type, identifier in db_dict.items(): if identifier_type not in input_dict.keys(): db_session.delete(identifier) + changed = True else: input_identifier = input_dict[identifier_type] identifier.type = input_identifier.type @@ -165,6 +165,8 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session): for identifier_type, identifier in input_dict.items(): if identifier_type not in db_dict.keys(): db_session.add(identifier) + changed = True + return changed @editbook.route("/delete//", defaults={'book_format': ""}) @@ -266,7 +268,53 @@ def render_edit_book(book_id): source_formats=valid_source_formats) +def edit_book_ratings(to_save, book): + changed = False + if to_save["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: + changed = True + is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first() + if is_rating: + book.ratings.append(is_rating) + else: + new_rating = db.Ratings(rating=ratingx2) + book.ratings.append(new_rating) + if old_rating: + book.ratings.remove(book.ratings[0]) + else: + if len(book.ratings) > 0: + book.ratings.remove(book.ratings[0]) + changed = True + return changed + + +def edit_book_languages(to_save, book): + input_languages = to_save["languages"].split(',') + unknown_languages = [] + input_l = isoLanguages.get_language_codes(get_locale(), input_languages, unknown_languages) + for l in unknown_languages: + log.error('%s is not a valid language', l) + flash(_(u"%(langname)s is not a valid language", langname=l), category="error") + return modify_database_object(list(input_l), book.languages, db.Languages, db.session, 'languages') + + +def edit_book_publisher(to_save, book): + changed = False + if to_save["publisher"]: + publisher = to_save["publisher"].rstrip().strip() + if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name): + changed |= modify_database_object([publisher], book.publishers, db.Publishers, db.session, 'publisher') + elif len(book.publishers): + changed |= modify_database_object([], book.publishers, db.Publishers, db.session, 'publisher') + return changed + + def edit_cc_data(book_id, book, to_save): + changed = False cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() for c in cc: cc_string = "custom_column_" + str(c.id) @@ -286,14 +334,17 @@ def edit_cc_data(book_id, book, to_save): if cc_db_value is not None: if to_save[cc_string] is not None: setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string]) + changed = True else: del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) db.session.delete(del_cc) + changed = True else: cc_class = db.cc_classes[c.id] new_cc = cc_class(value=to_save[cc_string], book=book_id) db.session.add(new_cc) + changed = True else: if c.datatype == 'rating': @@ -305,6 +356,7 @@ def edit_cc_data(book_id, book, to_save): getattr(book, cc_string).remove(del_cc) if len(del_cc.books) == 0: db.session.delete(del_cc) + changed = True cc_class = db.cc_classes[c.id] new_cc = db.session.query(cc_class).filter( cc_class.value == to_save[cc_string].strip()).first() @@ -312,6 +364,7 @@ def edit_cc_data(book_id, book, to_save): if new_cc is None: new_cc = cc_class(value=to_save[cc_string].strip()) db.session.add(new_cc) + changed = True db.session.flush() new_cc = db.session.query(cc_class).filter( cc_class.value == to_save[cc_string].strip()).first() @@ -324,12 +377,13 @@ def edit_cc_data(book_id, book, to_save): getattr(book, cc_string).remove(del_cc) if not del_cc.books or len(del_cc.books) == 0: db.session.delete(del_cc) + changed = True else: input_tags = to_save[cc_string].split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) - modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session, + changed |= modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session, 'custom') - return cc + return changed def upload_single_file(request, book, book_id): # Check and handle Uploaded file @@ -404,6 +458,7 @@ def upload_cover(request, book): @login_required_if_no_ano @edit_required def edit_book(book_id): + modif_date = False # Show form if request.method != 'POST': return render_edit_book(book_id) @@ -421,6 +476,7 @@ def edit_book(book_id): meta = upload_single_file(request, book, book_id) if upload_cover(request, book) is True: book.has_cover = 1 + modif_date = True try: to_save = request.form.to_dict() merge_metadata(to_save, meta) @@ -432,6 +488,7 @@ def edit_book(book_id): to_save["book_title"] = _(u'Unknown') book.title = to_save["book_title"].rstrip().strip() edited_books_id = book.id + modif_date = True # handle author(s) input_authors = to_save["author_name"].split('&') @@ -440,7 +497,7 @@ def edit_book(book_id): if input_authors == ['']: input_authors = [_(u'Unknown')] # prevent empty Author - modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') + modif_date |= modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') # Search for each author if author is in database, if not, authorname and sorted authorname is generated new # everything then is assembled for sorted author field in database @@ -456,7 +513,7 @@ def edit_book(book_id): if book.author_sort != sort_authors: edited_books_id = book.id book.author_sort = sort_authors - + modif_date = True if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() @@ -469,79 +526,60 @@ def edit_book(book_id): if to_save["cover_url"]: if helper.save_cover_from_url(to_save["cover_url"], book.path) is True: book.has_cover = 1 + modif_date = True else: flash(_(u"Cover is not a jpg file, can't save"), category="error") if book.series_index != to_save["series_index"]: book.series_index = to_save["series_index"] + modif_date = True # Handle book comments/description if len(book.comments): - book.comments[0].text = to_save["description"] + if book.comments[0].text != to_save["description"]: + book.comments[0].text = to_save["description"] + modif_date = True else: - book.comments.append(db.Comments(text=to_save["description"], book=book.id)) + if to_save["description"]: + book.comments.append(db.Comments(text=to_save["description"], book=book.id)) + modif_date = True - # Handle identifiers + # Handle identifiers input_identifiers = identifier_list(to_save, book) - modify_identifiers(input_identifiers, book.identifiers, db.session) + modif_date |= modify_identifiers(input_identifiers, book.identifiers, db.session) # Handle book tags input_tags = to_save["tags"].split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) - modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags') + modif_date |= modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags') # Handle book series input_series = [to_save["series"].strip()] input_series = [x for x in input_series if x != ''] - modify_database_object(input_series, book.series, db.Series, db.session, 'series') + modif_date |= modify_database_object(input_series, book.series, db.Series, db.session, 'series') if to_save["pubdate"]: try: - book.pubdate = datetime.datetime.strptime(to_save["pubdate"], "%Y-%m-%d") + book.pubdate = datetime.strptime(to_save["pubdate"], "%Y-%m-%d") except ValueError: book.pubdate = db.Books.DEFAULT_PUBDATE else: book.pubdate = db.Books.DEFAULT_PUBDATE - if to_save["publisher"]: - publisher = to_save["publisher"].rstrip().strip() - if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name): - modify_database_object([publisher], book.publishers, db.Publishers, db.session, 'publisher') - elif len(book.publishers): - modify_database_object([], book.publishers, db.Publishers, db.session, 'publisher') - + # handle book publisher + modif_date |= edit_book_publisher(to_save, book) # handle book languages - input_languages = to_save["languages"].split(',') - unknown_languages = [] - input_l = isoLanguages.get_language_codes(get_locale(), input_languages, unknown_languages) - for l in unknown_languages: - log.error('%s is not a valid language', l) - flash(_(u"%(langname)s is not a valid language", langname=l), category="error") - modify_database_object(list(input_l), book.languages, db.Languages, db.session, 'languages') + modif_date |= edit_book_languages(to_save, book) # handle book ratings - if to_save["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: - is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first() - if is_rating: - book.ratings.append(is_rating) - else: - new_rating = db.Ratings(rating=ratingx2) - book.ratings.append(new_rating) - if old_rating: - book.ratings.remove(book.ratings[0]) - else: - if len(book.ratings) > 0: - book.ratings.remove(book.ratings[0]) + modif_date |= edit_book_ratings(to_save, book) # handle cc data - edit_cc_data(book_id, book, to_save) + modif_date |= edit_cc_data(book_id, book, to_save) + if modif_date: + book.last_modified = datetime.utcnow() db.session.commit() if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() @@ -703,8 +741,9 @@ def upload(): # combine path and normalize path from windows systems path = os.path.join(author_dir, title_dir).replace('\\', '/') - db_book = db.Books(title, "", db_author.sort, datetime.datetime.now(), datetime.datetime(101, 1, 1), - series_index, datetime.datetime.now(), path, has_cover, db_author, [], db_language) + # Calibre adds books with utc as timezone + db_book = db.Books(title, "", db_author.sort, datetime.utcnow(), datetime(101, 1, 1), + series_index, datetime.utcnow(), path, has_cover, db_author, [], db_language) db_book.authors.append(db_author) if db_series: db_book.series.append(db_series) diff --git a/cps/web.py b/cps/web.py index 794a8aca..ecf080d2 100644 --- a/cps/web.py +++ b/cps/web.py @@ -23,7 +23,7 @@ from __future__ import division, print_function, unicode_literals import os import base64 -import datetime +from datetime import datetime import json import mimetypes import traceback @@ -967,14 +967,14 @@ def advanced_search(): if pub_start: try: searchterm.extend([_(u"Published after ") + - format_date(datetime.datetime.strptime(pub_start, "%Y-%m-%d"), + format_date(datetime.strptime(pub_start, "%Y-%m-%d"), format='medium', locale=get_locale())]) except ValueError: pub_start = u"" if pub_end: try: searchterm.extend([_(u"Published before ") + - format_date(datetime.datetime.strptime(pub_end, "%Y-%m-%d"), + format_date(datetime.strptime(pub_end, "%Y-%m-%d"), format='medium', locale=get_locale())]) except ValueError: pub_start = u"" @@ -1358,7 +1358,7 @@ def verify_token(token): return redirect(url_for('web.index')) # Token expired - if datetime.datetime.now() > auth_token.expiration: + if datetime.now() > auth_token.expiration: ub.session.delete(auth_token) ub.session.commit() @@ -1390,7 +1390,7 @@ def token_verified(): data['message'] = _(u"Token not found") # Token expired - elif datetime.datetime.now() > auth_token.expiration: + elif datetime.now() > auth_token.expiration: ub.session.delete(auth_token) ub.session.commit()