diff --git a/src/backend/thread_neweditions.py b/src/backend/thread_neweditions.py index 45e662f..245c091 100644 --- a/src/backend/thread_neweditions.py +++ b/src/backend/thread_neweditions.py @@ -5,6 +5,7 @@ from datetime import datetime from math import ceil from queue import Empty, Queue from typing import List, Optional, Set, Union +from time import monotonic # <-- NEW import loguru from PySide6.QtCore import QThread, Signal @@ -49,13 +50,10 @@ def _same_book(a: BookData, b: BookData) -> bool: # strong title match required; then author if available; then year if available if ta and tb and ta == tb: - # if both have authors, require match if aa and ab and aa == ab: - # if both have year, require match if ya and yb: return ya == yb return True - # if one/both authors missing, allow title (+year if both present) if ya and yb: return ya == yb return True @@ -116,6 +114,10 @@ class NewEditionCheckerThread(QThread): total_entries_signal = Signal(int) resultsSignal = Signal(list) # list[tuple[BookData, list[BookData]]] + # NEW: metrics signals + rateSignal = Signal(float) # items per second ("it/s") + etaSignal = Signal(int) # seconds remaining (-1 when unknown) + def __init__(self, entries: Optional[list["BookData"]] = None, parent=None): super().__init__(parent) self.entries: list["BookData"] = entries if entries is not None else [] @@ -194,6 +196,11 @@ class NewEditionCheckerThread(QThread): for entry in response if not (_norm_isbns(entry.isbn) & _norm_isbns(book.isbn)) ] + response = [ + entry + for entry in response + if book.publisher in entry.publisher + ] if not response: return None @@ -221,8 +228,14 @@ class NewEditionCheckerThread(QThread): total = len(self.entries) self.total_entries_signal.emit(total) + # start timer for metrics + t0 = monotonic() + if total == 0: log.debug("No entries to process.") + # emit metrics (zero work) + self.rateSignal.emit(0.0) + self.etaSignal.emit(0) self.resultsSignal.emit([]) return @@ -255,9 +268,27 @@ class NewEditionCheckerThread(QThread): processed += int(payload) self.updateSignal.emit(processed, total) self.updateProgress.emit(processed, total) + + # ---- NEW: compute & emit metrics ---- + elapsed = max(1e-9, monotonic() - t0) + rate = processed / elapsed # items per second + remaining = max(0, total - processed) + eta_sec = int(round(remaining / rate)) if rate > 0 else -1 + + self.rateSignal.emit(rate) + # clamp negative just in case + self.etaSignal.emit(max(0, eta_sec) if eta_sec >= 0 else -1) + # ------------------------------------- + elif kind == "result": self.results.append(payload) elif kind == "done": finished_workers += 1 + # Final metrics on completion + elapsed_total = max(1e-9, monotonic() - t0) + final_rate = total / elapsed_total + self.rateSignal.emit(final_rate) + self.etaSignal.emit(0) + self.resultsSignal.emit(self.results)