From d0e78e06b6bb095bdbad79df8c6f362bcd884d4d Mon Sep 17 00:00:00 2001 From: kyxap Date: Sat, 22 Jun 2024 23:44:13 -0400 Subject: [PATCH 1/3] improvements for device.py: - multiple author match support - debug print - NO_AUTHOR_MATCH tag for Author mismatch - '&' > 'and' support in book title --- src/calibre/gui2/device.py | 78 ++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 1c00b1ac9d..9ddc680a80 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -940,6 +940,14 @@ device_signals = DeviceSignals() # }}} +def debug_prints(*args): + """ + Helper method for prints outputs if application running in debug mode + """ + if DEBUG: + prints(*args) + + class DeviceMixin: # {{{ def __init__(self, *args, **kwargs): @@ -1215,12 +1223,10 @@ class DeviceMixin: # {{{ self.device_manager.slow_driveinfo() # set_books_in_library might schedule a sync_booklists job - if DEBUG: - prints('DeviceJob: metadata_downloaded: Starting set_books_in_library') + debug_prints('DeviceJob: metadata_downloaded: Starting set_books_in_library') self.set_books_in_library(job.result, reset=True, add_as_step_to_job=job) - if DEBUG: - prints('DeviceJob: metadata_downloaded: updating views') + debug_prints('DeviceJob: metadata_downloaded: updating views') mainlist, cardalist, cardblist = job.result self.memory_view.set_database(mainlist) self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA, @@ -1234,17 +1240,14 @@ class DeviceMixin: # {{{ self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA, self.device_manager.device.BACKLOADING_ERROR_MESSAGE is None) - if DEBUG: - prints('DeviceJob: metadata_downloaded: syncing') + debug_prints('DeviceJob: metadata_downloaded: syncing') self.sync_news() self.sync_catalogs() - if DEBUG: - prints('DeviceJob: metadata_downloaded: refreshing ondevice') + debug_prints('DeviceJob: metadata_downloaded: refreshing ondevice') self.refresh_ondevice() - if DEBUG: - prints('DeviceJob: metadata_downloaded: sending metadata_available signal') + debug_prints('DeviceJob: metadata_downloaded: sending metadata_available signal') device_signals.device_metadata_available.emit() def refresh_ondevice(self, reset_only=False): @@ -1851,13 +1854,18 @@ class DeviceMixin: # {{{ except: return False + # Define the cleaning function string_pat = re.compile(r'(?u)\W|[_]') def clean_string(x): try: + # Replace '&' with 'and' kobo automatically doing this before adding data into db + x = x.replace('&', 'and') + # Convert to lowercase if x is not None or empty x = x.lower() if x else '' except Exception: x = '' + # Perform regex substitution return string_pat.sub('', x) update_metadata = ( @@ -1909,6 +1917,23 @@ class DeviceMixin: # {{{ if get_covers and desired_thumbnail_height != 0: self.update_thumbnail(book) + def extract_id_from_dict(author_to_look_for, target_dict): + """ + Extracts id from dict with full match by author or partial match for cases when + book has multiple authors. + """ + debug_prints('Trying to extract id for author:', author_to_look_for) + if author_to_look_for in target_dict: + return target_dict[book_authors] + else: + # for cases when multiple authors like: Author A & Author B => 'authoraauthorb' need to match 'authorb' + for author in target_dict: + if author_to_look_for in author: + return target_dict[author] + + debug_prints('Id is not extracted!') + return None + def updateq(id_, book): try: if not update_metadata: @@ -1948,8 +1973,7 @@ class DeviceMixin: # {{{ for book in booklist: if book: total_book_count += 1 - if DEBUG: - prints('DeviceJob: set_books_in_library: books to process=', total_book_count) + debug_prints('DeviceJob: set_books_in_library: books to process=', total_book_count) start_time = time.time() @@ -2010,16 +2034,24 @@ class DeviceMixin: # {{{ # Compare against both author and author sort, because # either can appear as the author book_authors = clean_string(authors_to_string(book.authors)) - if book_authors in d['authors']: - id_ = d['authors'][book_authors] - update_book(id_, book) + extracted_id = None + authors_id = extract_id_from_dict(book_authors, d['authors']) + if authors_id is not None: + extracted_id = authors_id book.in_library = 'AUTHOR' - book.application_id = id_ - elif book_authors in d['author_sort']: - id_ = d['author_sort'][book_authors] - update_book(id_, book) - book.in_library = 'AUTH_SORT' - book.application_id = id_ + else: + author_sort_id = extract_id_from_dict(book_authors, d['author_sort']) + if author_sort_id is not None: + extracted_id = author_sort_id + book.in_library = 'AUTH_SORT' + + update_book(extracted_id, book) + book.application_id = extracted_id + + if extracted_id is None: + book.in_library = 'NO_AUTHOR_MATCH' + debug_prints('No author match for book: ', book) + else: # Book definitely not matched. Clear its application ID book.application_id = None @@ -2089,9 +2121,7 @@ class DeviceMixin: # {{{ except: traceback.print_exc() - if DEBUG: - prints('DeviceJob: set_books_in_library finished: time=', - time.time() - start_time) + debug_prints('DeviceJob: set_books_in_library finished: time=', time.time() - start_time) # The status line is reset when the job finishes return update_metadata # }}} From ee309818fef252fce72f9106b0be7536937b3100 Mon Sep 17 00:00:00 2001 From: kyxap Date: Sun, 23 Jun 2024 08:54:26 -0400 Subject: [PATCH 2/3] Upd based on PR comments --- src/calibre/gui2/device.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 9ddc680a80..f408a4741a 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1859,8 +1859,6 @@ class DeviceMixin: # {{{ def clean_string(x): try: - # Replace '&' with 'and' kobo automatically doing this before adding data into db - x = x.replace('&', 'and') # Convert to lowercase if x is not None or empty x = x.lower() if x else '' except Exception: @@ -1922,7 +1920,7 @@ class DeviceMixin: # {{{ Extracts id from dict with full match by author or partial match for cases when book has multiple authors. """ - debug_prints('Trying to extract id for author:', author_to_look_for) + debug_prints('Trying to extract id for author:', author_to_look_for, ' in:', target_dict) if author_to_look_for in target_dict: return target_dict[book_authors] else: @@ -2049,8 +2047,7 @@ class DeviceMixin: # {{{ book.application_id = extracted_id if extracted_id is None: - book.in_library = 'NO_AUTHOR_MATCH' - debug_prints('No author match for book: ', book) + debug_prints('No author match for a book: ', book) else: # Book definitely not matched. Clear its application ID From 43e8c195dc28e202a62634916bb784b21acd660e Mon Sep 17 00:00:00 2001 From: kyxap Date: Sun, 23 Jun 2024 10:58:56 -0400 Subject: [PATCH 3/3] Using debug_print, added some documentation --- src/calibre/devices/usbms/driver.py | 28 +++++++++++++++++++++++- src/calibre/gui2/device.py | 33 ++++++++++++----------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index db09fbd8b3..ce7fac4746 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -24,11 +24,37 @@ from polyglot.builtins import itervalues, string_or_bytes def debug_print(*args, **kw): + ''' + Prints debug information to the console if debugging is enabled. + + This function prints a message prefixed with a timestamp showing the elapsed time + since the first call to this function. The message is printed only if debugging is enabled. + + Parameters: + *args : tuple + Variable length argument list to be printed. + **kw : dict + Arbitrary keyword arguments to be passed to the `print` function. + + Attributes: + base_time : float + The timestamp of the first call to this function. Stored as an attribute of the function. + + Behavior: + - On the first call, initializes `base_time` to the current time using `time.monotonic()`. + - If `is_debugging()` returns True, prints the elapsed time since `base_time` along with the provided arguments. + ''' + + # Get the base_time attribute, initializing it on the first call base_time = getattr(debug_print, 'base_time', None) if base_time is None: + # Set base_time to the current monotonic time if it hasn't been set debug_print.base_time = base_time = time.monotonic() + + # Check if debugging is enabled if is_debugging(): - prints('DEBUG: %6.1f'%(time.monotonic()-base_time), *args, **kw) + # Print the elapsed time and the provided arguments if debugging is enabled + prints('DEBUG: %6.1f' % (time.monotonic() - base_time), *args, **kw) def safe_walk(top, topdown=True, onerror=None, followlinks=False, maxdepth=128): diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index f408a4741a..62649e3569 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -30,6 +30,7 @@ from calibre.devices.errors import ( from calibre.devices.folder_device.driver import FOLDER_DEVICE from calibre.devices.interface import DevicePlugin, currently_connected_device from calibre.devices.scanner import DeviceScanner +from calibre.devices.usbms.driver import debug_print from calibre.ebooks.covers import cprefs, generate_cover, override_prefs, scale_cover from calibre.ebooks.metadata import authors_to_string from calibre.gui2 import ( @@ -940,14 +941,6 @@ device_signals = DeviceSignals() # }}} -def debug_prints(*args): - """ - Helper method for prints outputs if application running in debug mode - """ - if DEBUG: - prints(*args) - - class DeviceMixin: # {{{ def __init__(self, *args, **kwargs): @@ -1223,10 +1216,10 @@ class DeviceMixin: # {{{ self.device_manager.slow_driveinfo() # set_books_in_library might schedule a sync_booklists job - debug_prints('DeviceJob: metadata_downloaded: Starting set_books_in_library') + debug_print('DeviceJob: metadata_downloaded: Starting set_books_in_library') self.set_books_in_library(job.result, reset=True, add_as_step_to_job=job) - debug_prints('DeviceJob: metadata_downloaded: updating views') + debug_print('DeviceJob: metadata_downloaded: updating views') mainlist, cardalist, cardblist = job.result self.memory_view.set_database(mainlist) self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA, @@ -1240,14 +1233,14 @@ class DeviceMixin: # {{{ self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA, self.device_manager.device.BACKLOADING_ERROR_MESSAGE is None) - debug_prints('DeviceJob: metadata_downloaded: syncing') + debug_print('DeviceJob: metadata_downloaded: syncing') self.sync_news() self.sync_catalogs() - debug_prints('DeviceJob: metadata_downloaded: refreshing ondevice') + debug_print('DeviceJob: metadata_downloaded: refreshing ondevice') self.refresh_ondevice() - debug_prints('DeviceJob: metadata_downloaded: sending metadata_available signal') + debug_print('DeviceJob: metadata_downloaded: sending metadata_available signal') device_signals.device_metadata_available.emit() def refresh_ondevice(self, reset_only=False): @@ -1916,11 +1909,11 @@ class DeviceMixin: # {{{ self.update_thumbnail(book) def extract_id_from_dict(author_to_look_for, target_dict): - """ + ''' Extracts id from dict with full match by author or partial match for cases when book has multiple authors. - """ - debug_prints('Trying to extract id for author:', author_to_look_for, ' in:', target_dict) + ''' + debug_print('Trying to extract id for author:', author_to_look_for, ' in:', target_dict) if author_to_look_for in target_dict: return target_dict[book_authors] else: @@ -1929,7 +1922,7 @@ class DeviceMixin: # {{{ if author_to_look_for in author: return target_dict[author] - debug_prints('Id is not extracted!') + debug_print('Id is not extracted!') return None def updateq(id_, book): @@ -1971,7 +1964,7 @@ class DeviceMixin: # {{{ for book in booklist: if book: total_book_count += 1 - debug_prints('DeviceJob: set_books_in_library: books to process=', total_book_count) + debug_print('DeviceJob: set_books_in_library: books to process=', total_book_count) start_time = time.time() @@ -2047,7 +2040,7 @@ class DeviceMixin: # {{{ book.application_id = extracted_id if extracted_id is None: - debug_prints('No author match for a book: ', book) + debug_print('No author match for a book:\n', book) else: # Book definitely not matched. Clear its application ID @@ -2118,7 +2111,7 @@ class DeviceMixin: # {{{ except: traceback.print_exc() - debug_prints('DeviceJob: set_books_in_library finished: time=', time.time() - start_time) + debug_print('DeviceJob: set_books_in_library finished: time=', time.time() - start_time) # The status line is reset when the job finishes return update_metadata # }}}