diff --git a/30 SběrDat/verify_photos.py b/30 SběrDat/verify_photos.py index dbc7185..ea5bcf3 100644 --- a/30 SběrDat/verify_photos.py +++ b/30 SběrDat/verify_photos.py @@ -214,6 +214,19 @@ def verify_file(local_path: Path, row) -> list[tuple[str, str, str]]: return errors +def _progress(msg: str) -> None: + """In-place jednořádkový progress na stdout (přepisuje stejný řádek).""" + # \r = návrat na začátek řádku, \033[K = smaž zbytek řádku + sys.stdout.write(f"\r{msg}\033[K") + sys.stdout.flush() + + +def _progress_clear() -> None: + """Ukončí progress řádek nulou + newline (aby další log.info šel na čistý řádek).""" + sys.stdout.write("\n") + sys.stdout.flush() + + # ── Zpracování ─────────────────────────────────────────────────────────────── @@ -231,7 +244,8 @@ def reset_state(conn, dry_run: bool) -> None: log.info("Reset done. Cleared verified_at on %d rows.", affected) -def process_batch(conn, batch_size: int, dry_run: bool) -> int: +def process_batch(conn, batch_size: int, dry_run: bool, + batch_num: int = 0, total_so_far: int = 0) -> int: where = "" if REVERIFY_ALL else "WHERE verified_at IS NULL" with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur: cur.execute( @@ -249,23 +263,29 @@ def process_batch(conn, batch_size: int, dry_run: bool) -> int: if not rows: return 0 + batch_total = len(rows) processed = 0 - for row in rows: + crit_n = warn_n = info_n = 0 + t_start = time.time() + + for idx, row in enumerate(rows, 1): photo_id = row["id"] local_path = db_path_to_local(row["file_path"]) try: errors = verify_file(local_path, row) except Exception: + # Vyčistit progress a logovat exception celý + _progress_clear() log.exception("Unexpected error verifying id=%d (%s)", photo_id, local_path) errors = [("critical", "read_error", "Unexpected exception during verification")] if dry_run: for sev, code, msg in errors: + _progress_clear() log.info("[DRY RUN] id=%d %s/%s: %s", photo_id, sev, code, msg) else: with conn.cursor() as cur: - # Smažeme staré záznamy pro tuto fotku — vždy chceme čerstvý snapshot. cur.execute("DELETE FROM photo_errors WHERE photo_id = %s", (photo_id,)) if errors: psycopg2.extras.execute_values( @@ -277,14 +297,24 @@ def process_batch(conn, batch_size: int, dry_run: bool) -> int: conn.commit() processed += 1 + for sev, _, _ in errors: + if sev == "critical": crit_n += 1 + elif sev == "warning": warn_n += 1 + elif sev == "info": info_n += 1 - if errors: - crit = sum(1 for s, _, _ in errors if s == "critical") - warn = sum(1 for s, _, _ in errors if s == "warning") - info = sum(1 for s, _, _ in errors if s == "info") - log.info("id=%d: %d errors (crit=%d, warn=%d, info=%d) — %s", - photo_id, len(errors), crit, warn, info, local_path.name) + # Single-line progress + elapsed = time.time() - t_start + rate = processed / elapsed if elapsed > 0 else 0 + name = local_path.name[:40] + _progress( + f"[batch {batch_num}] {idx}/{batch_total} " + f"total={total_so_far + processed} " + f"id={photo_id} " + f"crit={crit_n} warn={warn_n} info={info_n} " + f"{rate:.1f}/s {name}" + ) + _progress_clear() return processed @@ -322,7 +352,8 @@ def main(): batch_num += 1 t0 = time.time() - count = process_batch(conn, remaining, args.dry_run) + count = process_batch(conn, remaining, args.dry_run, + batch_num=batch_num, total_so_far=total_processed) elapsed = time.time() - t0 if count == 0: