dev #21

Merged
WorldTeacher merged 46 commits from dev into main 2025-11-24 12:59:41 +00:00
Showing only changes of commit 1ee7901d49 - Show all commits

View File

@@ -5,6 +5,7 @@ from datetime import datetime
from math import ceil from math import ceil
from queue import Empty, Queue from queue import Empty, Queue
from typing import List, Optional, Set, Union from typing import List, Optional, Set, Union
from time import monotonic # <-- NEW
import loguru import loguru
from PySide6.QtCore import QThread, Signal 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 # strong title match required; then author if available; then year if available
if ta and tb and ta == tb: if ta and tb and ta == tb:
# if both have authors, require match
if aa and ab and aa == ab: if aa and ab and aa == ab:
# if both have year, require match
if ya and yb: if ya and yb:
return ya == yb return ya == yb
return True return True
# if one/both authors missing, allow title (+year if both present)
if ya and yb: if ya and yb:
return ya == yb return ya == yb
return True return True
@@ -116,6 +114,10 @@ class NewEditionCheckerThread(QThread):
total_entries_signal = Signal(int) total_entries_signal = Signal(int)
resultsSignal = Signal(list) # list[tuple[BookData, list[BookData]]] 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): def __init__(self, entries: Optional[list["BookData"]] = None, parent=None):
super().__init__(parent) super().__init__(parent)
self.entries: list["BookData"] = entries if entries is not None else [] self.entries: list["BookData"] = entries if entries is not None else []
@@ -194,6 +196,11 @@ class NewEditionCheckerThread(QThread):
for entry in response for entry in response
if not (_norm_isbns(entry.isbn) & _norm_isbns(book.isbn)) 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: if not response:
return None return None
@@ -221,8 +228,14 @@ class NewEditionCheckerThread(QThread):
total = len(self.entries) total = len(self.entries)
self.total_entries_signal.emit(total) self.total_entries_signal.emit(total)
# start timer for metrics
t0 = monotonic()
if total == 0: if total == 0:
log.debug("No entries to process.") log.debug("No entries to process.")
# emit metrics (zero work)
self.rateSignal.emit(0.0)
self.etaSignal.emit(0)
self.resultsSignal.emit([]) self.resultsSignal.emit([])
return return
@@ -255,9 +268,27 @@ class NewEditionCheckerThread(QThread):
processed += int(payload) processed += int(payload)
self.updateSignal.emit(processed, total) self.updateSignal.emit(processed, total)
self.updateProgress.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": elif kind == "result":
self.results.append(payload) self.results.append(payload)
elif kind == "done": elif kind == "done":
finished_workers += 1 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) self.resultsSignal.emit(self.results)