diff --git a/cps/db.py b/cps/db.py index fc11770c..fe940d12 100644 --- a/cps/db.py +++ b/cps/db.py @@ -756,16 +756,21 @@ class CalibreDB(): except Exception as ex: log.debug_or_exception(ex) # display authors in right order - entries = self.order_authors(entries, True) + entries = self.order_authors(entries, True, join_archive_read) return entries, randm, pagination # Orders all Authors in the list according to authors sort - def order_authors(self, entries, list_return=False): + def order_authors(self, entries, list_return=False, combined=False): for entry in entries: - sort_authors = entry.author_sort.split('&') + if combined: + sort_authors = entry.Books.author_sort.split('&') + ids = [a.id for a in entry.Books.authors] + + else: + sort_authors = entry.author_sort.split('&') + ids = [a.id for a in entry.authors] authors_ordered = list() error = False - ids = [a.id for a in entry.authors] for auth in sort_authors: results = self.session.query(Authors).filter(Authors.sort == auth.lstrip().strip()).all() # ToDo: How to handle not found authorname @@ -776,7 +781,10 @@ class CalibreDB(): if r.id in ids: authors_ordered.append(r) if not error: - entry.authors = authors_ordered + if combined: + entry.Books.authors = authors_ordered + else: + entry.authors = authors_ordered if list_return: return entries else: @@ -841,7 +849,8 @@ class CalibreDB(): )) # read search results from calibre-database and return it (function is used for feed and simple search - def get_search_results(self, term, offset=None, order=None, limit=None, config_read_column=False, *join): + def get_search_results(self, term, offset=None, order=None, limit=None, allow_show_archived=False, + config_read_column=False, *join): order = order[0] if order else [Books.sort] pagination = None result = self.search_query(term, config_read_column, *join).order_by(*order).all() diff --git a/cps/editbooks.py b/cps/editbooks.py index 7bfa35cf..9c942b52 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -50,6 +50,7 @@ from .services.worker import WorkerThread from .tasks.upload import TaskUpload from .render_template import render_title_template from .usermanagement import login_required_if_no_ano +from .kobo_sync_status import change_archived_books try: from functools import wraps @@ -1185,10 +1186,9 @@ def edit_list_book(param): ret = Response(json.dumps({'success': True, 'newValue': ' & '.join([author.replace('|',',') for author in input_authors])}), mimetype='application/json') - elif param =='is_archive': - # ToDo save - ret = Response(json.dumps({'success': True, 'newValue': vals['value']}), - mimetype='application/json') + elif param =='is_archived': + change_archived_books(book.id, vals['value']=="True") + ret = "" elif param =='read_status': # ToDo save ret = Response(json.dumps({'success': True, 'newValue': vals['value']}), @@ -1197,8 +1197,12 @@ def edit_list_book(param): new_val = dict() new_val[param] = vals['value'] edit_single_cc_data(book.id, book, param[14:], new_val) - ret = Response(json.dumps({'success': True, 'newValue': vals['value']}), - mimetype='application/json') + # ToDo: Very hacky find better solution + if vals['value'] in ["True", "False"]: + ret = "" + else: + ret = Response(json.dumps({'success': True, 'newValue': vals['value']}), + mimetype='application/json') else: return _("Parameter not found"), 400 book.last_modified = datetime.utcnow() diff --git a/cps/helper.py b/cps/helper.py index c30479a2..73731908 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -52,7 +52,7 @@ except ImportError: from . import calibre_db from .tasks.convert import TaskConvert -from . import logger, config, get_locale, db, ub +from . import logger, config, get_locale, db, ub, kobo_sync_status from . import gdriveutils as gd from .constants import STATIC_DIR as _STATIC_DIR from .subproc_wrapper import process_wait @@ -431,7 +431,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author, orignal_filepa for file in file_list: shutil.move(os.path.normcase(os.path.join(dir_name, file)), os.path.normcase(os.path.join(new_path + dir_name[len(path):], file))) - # os.unlink(os.path.normcase(os.path.join(dir_name, file))) + # 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("Rename title from: %s to %s: %s", path, new_path, ex) log.debug(ex, exc_info=True) @@ -441,12 +442,11 @@ def update_dir_structure_file(book_id, calibrepath, first_author, orignal_filepa # Rename all files from old names to new names try: clean_author_database(renamed_author, calibrepath) - if first_author not in renamed_author: clean_author_database([first_author], calibrepath, localbook) if not renamed_author and not orignal_filepath and len(os.listdir(os.path.dirname(path))) == 0: shutil.rmtree(os.path.dirname(path)) - except (OSError) as ex: + except (OSError, FileNotFoundError) as ex: log.error("Error in rename file in path %s", ex) log.debug(ex, exc_info=True) return _("Error in rename file in path: %(error)s", error=str(ex)) diff --git a/cps/kobo.py b/cps/kobo.py index 482eb13a..b0917a47 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -949,7 +949,8 @@ def HandleBookDeletionRequest(book_uuid): return redirect_or_proxy_request() book_id = book.id - archived_book = ( + is_archived = kobo_sync_status.change_archived_books(book_id, True) + '''archived_book = ( ub.session.query(ub.ArchivedBook) .filter(ub.ArchivedBook.book_id == book_id) .first() @@ -960,8 +961,8 @@ def HandleBookDeletionRequest(book_uuid): archived_book.last_modified = datetime.datetime.utcnow() ub.session.merge(archived_book) - ub.session_commit() - if archived_book.is_archived: + ub.session_commit()''' + if is_archived: kobo_sync_status.remove_synced_book(book_id) return "", 204 diff --git a/cps/kobo_sync_status.py b/cps/kobo_sync_status.py index b88cb6ac..eee47d89 100644 --- a/cps/kobo_sync_status.py +++ b/cps/kobo_sync_status.py @@ -20,7 +20,7 @@ from flask_login import current_user from . import ub import datetime -from sqlalchemy.sql.expression import or_ +from sqlalchemy.sql.expression import or_, and_ # Add the current book id to kobo_synced_books table for current user, if entry is already present, # do nothing (safety precaution) @@ -42,18 +42,18 @@ def remove_synced_book(book_id): ub.session_commit() -def add_archived_books(book_id): - archived_book = (ub.session.query(ub.ArchivedBook) - .filter(ub.ArchivedBook.book_id == book_id) - .filter(ub.ArchivedBook.user_id == current_user.id) - .first()) +def change_archived_books(book_id, state=None, message=None): + archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id), + ub.ArchivedBook.book_id == book_id)).first() if not archived_book: archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) - archived_book.is_archived = True + + archived_book.is_archived = state if state else not archived_book.is_archived archived_book.last_modified = datetime.datetime.utcnow() ub.session.merge(archived_book) - ub.session_commit() + ub.session_commit(message) + return archived_book.is_archived # select all books which are synced by the current user and do not belong to a synced shelf and them to archive @@ -65,7 +65,7 @@ def update_on_sync_shelfs(user_id): .filter(or_(ub.Shelf.kobo_sync == 0, ub.Shelf.kobo_sync == None)) .filter(ub.KoboSyncedBooks.user_id == user_id).all()) for b in books_to_archive: - add_archived_books(b.book_id) + change_archived_books(b.book_id, True) ub.session.query(ub.KoboSyncedBooks) \ .filter(ub.KoboSyncedBooks.book_id == b.book_id) \ .filter(ub.KoboSyncedBooks.user_id == user_id).delete() diff --git a/cps/opds.py b/cps/opds.py index f249218c..0b0a8923 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -528,8 +528,8 @@ def get_metadata_calibre_companion(uuid, library): def feed_search(term): if term: entries, __, ___ = calibre_db.get_search_results(term, config_read_column=config.config_read_column) - entriescount = len(entries) if len(entries) > 0 else 1 - pagination = Pagination(1, entriescount, entriescount) + entries_count = len(entries) if len(entries) > 0 else 1 + pagination = Pagination(1, entries_count, entries_count) return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination) else: return render_xml_template('feed.xml', searchterm="") diff --git a/cps/static/js/table.js b/cps/static/js/table.js index fbb4df3e..48c7631a 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -810,10 +810,11 @@ function checkboxChange(checkbox, userId, field, field_index) { } function BookCheckboxChange(checkbox, userId, field) { + var value = checkbox.checked ? "True" : "False"; $.ajax({ method: "post", url: getPath() + "/ajax/editbooks/" + field, - data: {"pk": userId, "value": checkbox.checked}, + data: {"pk": userId, "value": value}, error: function(data) { handleListServerResponse([{type:"danger", message:data.responseText}]) }, diff --git a/cps/templates/book_table.html b/cps/templates/book_table.html index 6d6a0e80..92c8a2f5 100644 --- a/cps/templates/book_table.html +++ b/cps/templates/book_table.html @@ -71,8 +71,8 @@ {{ text_table_row('publishers', _('Enter Publishers'),_('Publishers'), false, true) }} {{_('Comments')}} - {{ book_checkbox_row('is_archived', _('Enter Archiv Status'), false)}} - {{ book_checkbox_row('read_status', _('Enter Read Status'), false)}} + {{ book_checkbox_row('is_archived', _('Archiv Status'), false)}} + {{ book_checkbox_row('read_status', _('Read Status'), false)}} {% for c in cc %} {% if c.datatype == "int" %} {{c.name}} @@ -89,7 +89,7 @@ {% elif c.datatype == "comments" %} {{c.name}} {% elif c.datatype == "bool" %} - {{ book_checkbox_row('custom_column_' + c.id|string, _('Enter ') + c.name, c.name, visiblility, all_roles, false)}} + {{ book_checkbox_row('custom_column_' + c.id|string, c.name, false)}} {% else %} {% endif %} diff --git a/cps/templates/search.html b/cps/templates/search.html index 627beaf8..7640192f 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -41,21 +41,21 @@ {% for entry in entries %}
- {% if entry.has_cover is defined %} - - - {{ entry.title }} - {% if entry.id in read_book_ids %}{% endif %} + {% if entry.Books.has_cover is defined %} + + + {{ entry.Books.title }} + {% if entry.Books.id in read_book_ids %}{% endif %} {% endif %}
- -

{{entry.title|shortentitle}}

+
+

{{entry.Books.title|shortentitle}}

- {% for author in entry.authors %} + {% for author in entry.Books.authors %} {% if loop.index > g.config_authors_max and g.config_authors_max != 0 %} {% if not loop.first %} & @@ -71,24 +71,24 @@ {{author.name.replace('|',',')|shortentitle(30)}} {% endif %} {% endfor %} - {% for format in entry.data %} + {% for format in entry.Books.data %} {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} {% endif %} {% endfor %}

- {% if entry.series.__len__() > 0 %} + {% if entry.Books.series.__len__() > 0 %}

- - {{entry.series[0].name}} + + {{entry.Books.series[0].name}} - ({{entry.series_index|formatseriesindex}}) + ({{entry.Books.series_index|formatseriesindex}})

{% endif %} - {% if entry.ratings.__len__() > 0 %} + {% if entry.Books.ratings.__len__() > 0 %}
- {% for number in range((entry.ratings[0].rating/2)|int(2)) %} + {% for number in range((entry.Books.ratings[0].rating/2)|int(2)) %} {% if loop.last and loop.index < 5 %} {% for numer in range(5 - loop.index) %} diff --git a/cps/web.py b/cps/web.py index 67a121be..af5401d8 100644 --- a/cps/web.py +++ b/cps/web.py @@ -56,6 +56,7 @@ from .redirect import redirect_back from .usermanagement import login_required_if_no_ano from .kobo_sync_status import remove_synced_book from .render_template import render_title_template +from .kobo_sync_status import change_archived_books feature_support = { 'ldap': bool(services.ldap), @@ -197,17 +198,8 @@ def toggle_read(book_id): @web.route("/ajax/togglearchived/", methods=['POST']) @login_required def toggle_archived(book_id): - archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id), - ub.ArchivedBook.book_id == book_id)).first() - if archived_book: - archived_book.is_archived = not archived_book.is_archived - archived_book.last_modified = datetime.utcnow() - else: - archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id) - archived_book.is_archived = True - ub.session.merge(archived_book) - ub.session_commit("Book {} archivebit toggled".format(book_id)) - if archived_book.is_archived: + is_archived = change_archived_books(book_id, message="Book {} archivebit toggled".format(book_id)) + if is_archived: remove_synced_book(book_id) return "" @@ -759,6 +751,7 @@ def render_search_results(term, offset=None, order=None, limit=None): offset, order, limit, + False, config.config_read_column, *join) return render_title_template('search.html', @@ -836,7 +829,7 @@ def list_books(): elif not state: order = [db.Books.timestamp.desc()] - total_count = filtered_count = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(False)).count() + total_count = filtered_count = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(allow_show_archived=True)).count() if state is not None: if search: books = calibre_db.search_query(search, config.config_read_column).all() @@ -860,24 +853,26 @@ def list_books(): 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, int(current_user.id) == ub.ArchivedBook.user_id)) - .filter(calibre_db.common_filters()).all()) + .filter(calibre_db.common_filters(allow_show_archived=True)).all()) entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order, True) elif search: entries, filtered_count, __ = calibre_db.get_search_results(search, off, [order,''], limit, + True, config.config_read_column, *join) else: - entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), - limit, - db.Books, - True, - order, - True, - config.config_read_column, - *join) + entries, __, __ = calibre_db.fill_indexpage_with_archived_books((int(off) / (int(limit)) + 1), + db.Books, + limit, + True, + order, + True, + True, + config.config_read_column, + *join) result = list() for entry in entries: diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index dea6d5e9..b5b6f04c 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
-

Start Time: 2021-12-02 06:37:36

+

Start Time: 2021-12-04 22:17:44

-

Stop Time: 2021-12-02 10:15:15

+

Stop Time: 2021-12-05 01:46:42

-

Duration: 2h 57 min

+

Duration: 2h 48 min

@@ -411,11 +411,11 @@ - + TestEbookConvertCalibre 14 - 14 - 0 + 12 + 2 0 0 @@ -488,11 +488,31 @@ - +
TestEbookConvertCalibre - test_convert_xss
- PASS + +
+ FAIL +
+ + + + @@ -542,11 +562,31 @@ - +
TestEbookConvertCalibre - test_user_convert_xss
- PASS + +
+ FAIL +
+ + + + @@ -705,12 +745,12 @@ - + TestEditAdditionalBooks 16 - 14 - 0 - 0 + 6 + 2 + 6 2 Detail @@ -719,11 +759,31 @@ - +
TestEditAdditionalBooks - test_change_upload_formats
- PASS + +
+ FAIL +
+ + + + @@ -737,11 +797,31 @@ - +
TestEditAdditionalBooks - test_delete_role
- PASS + +
+ FAIL +
+ + + + @@ -782,38 +862,138 @@ - +
TestEditAdditionalBooks - test_title_sort
- PASS + +
+ ERROR +
+ + + + - +
TestEditAdditionalBooks - test_upload_edit_role
- PASS + +
+ ERROR +
+ + + + - +
TestEditAdditionalBooks - test_upload_metadata_cbr
- PASS + +
+ ERROR +
+ + + + - +
TestEditAdditionalBooks - test_upload_metadata_cbt
- PASS + +
+ ERROR +
+ + + + @@ -844,11 +1024,37 @@ - +
TestEditAdditionalBooks - test_writeonly_path
- PASS + +
+ ERROR +
+ + + + @@ -871,31 +1077,105 @@ - +
TestEditAdditionalBooks - test_xss_custom_comment_edit
- PASS + +
+ ERROR +
+ + + + - - TestEditBooks - 35 - 34 + + _ErrorHolder + 1 0 0 1 + 0 - Detail + Detail - + + +
tearDownClass (test_edit_additional_books)
+ + +
+ ERROR +
+ + + + + + + + + + + TestEditBooks + 35 + 14 + 1 + 19 + 1 + + Detail + + + + + +
TestEditBooks - test_download_book
@@ -904,7 +1184,7 @@ - +
TestEditBooks - test_edit_author
@@ -913,7 +1193,7 @@ - +
TestEditBooks - test_edit_category
@@ -922,97 +1202,297 @@ - +
TestEditBooks - test_edit_comments
- PASS + +
+ FAIL +
+ + + + - +
TestEditBooks - test_edit_custom_bool
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_categories
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_comment
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_date
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_float
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_int
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_rating
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_single_select
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_custom_text
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_edit_language
@@ -1021,7 +1501,7 @@ - +
TestEditBooks - test_edit_publisher
@@ -1030,7 +1510,7 @@ - +
TestEditBooks - test_edit_publishing_date
@@ -1039,7 +1519,7 @@ - +
TestEditBooks - test_edit_rating
@@ -1048,7 +1528,7 @@ - +
TestEditBooks - test_edit_series
@@ -1057,28 +1537,48 @@ - +
TestEditBooks - test_edit_title
- PASS + +
+ ERROR +
+ + + + - +
TestEditBooks - test_rename_uppercase_lowercase
- SKIP + SKIP
-