Spaces:
Sleeping
Sleeping
Upload fix_run_ocr.py
Browse files- fix_run_ocr.py +177 -0
fix_run_ocr.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Applies three fixes to smoke_signal_tab.py:
|
| 3 |
+
1. font_name reset per page in second pass loop (line ~2205)
|
| 4 |
+
2. Top-level try/except inside run_ocr per book so errors surface in log
|
| 5 |
+
3. Phase log lines: book start, render complete, batch OCR complete, queue write complete
|
| 6 |
+
|
| 7 |
+
Run with:
|
| 8 |
+
python fix_run_ocr.py
|
| 9 |
+
"""
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
TARGET = Path.home() / "Codex_Extractor" / "smoke_signal_tab.py"
|
| 13 |
+
content = TARGET.read_text()
|
| 14 |
+
original = content # keep for diff check
|
| 15 |
+
|
| 16 |
+
fixes_applied = []
|
| 17 |
+
|
| 18 |
+
# ββ Fix 1: Reset font_name at top of second pass loop ββββββββββββββββββββββββ
|
| 19 |
+
# The second pass loop at ~line 2205 never resets font_name per page,
|
| 20 |
+
# so it carries stale value from previous page or first pass.
|
| 21 |
+
|
| 22 |
+
old1 = (
|
| 23 |
+
' # Second pass: build page-level OCR output and review queue.\n'
|
| 24 |
+
' for page_data in profile.get("pages", []):\n'
|
| 25 |
+
' page_num = page_data["page_number"]\n'
|
| 26 |
+
' route = page_data["route"]\n'
|
| 27 |
+
' font_name = page_data.get("font_name")\n' # already added by earlier fix
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
# Check if fix 1 already applied
|
| 31 |
+
if old1 in content:
|
| 32 |
+
fixes_applied.append("Fix 1: already applied (font_name reset in second pass)")
|
| 33 |
+
else:
|
| 34 |
+
# Version without font_name reset (original)
|
| 35 |
+
old1_orig = (
|
| 36 |
+
' # Second pass: build page-level OCR output and review queue.\n'
|
| 37 |
+
' for page_data in profile.get("pages", []):\n'
|
| 38 |
+
' page_num = page_data["page_number"]\n'
|
| 39 |
+
' route = page_data["route"]\n'
|
| 40 |
+
)
|
| 41 |
+
new1 = (
|
| 42 |
+
' # Second pass: build page-level OCR output and review queue.\n'
|
| 43 |
+
' for page_data in profile.get("pages", []):\n'
|
| 44 |
+
' page_num = page_data["page_number"]\n'
|
| 45 |
+
' route = page_data["route"]\n'
|
| 46 |
+
' font_name = page_data.get("font_name") # reset per page β prevent stale carry-over\n'
|
| 47 |
+
)
|
| 48 |
+
if old1_orig in content:
|
| 49 |
+
content = content.replace(old1_orig, new1, 1)
|
| 50 |
+
fixes_applied.append("Fix 1: APPLIED β font_name reset per page in second pass")
|
| 51 |
+
else:
|
| 52 |
+
fixes_applied.append("Fix 1: SKIPPED β pattern not found (may already have font_name line)")
|
| 53 |
+
|
| 54 |
+
# ββ Fix 2 + 3: Per-book try/except + phase logs βββββββββββββββββββββββββββββββ
|
| 55 |
+
# Find the per-book iteration and wrap it with try/except and phase logs.
|
| 56 |
+
# We look for the for loop over eligible books and the profile load inside it.
|
| 57 |
+
|
| 58 |
+
old2 = ' for _, book_row in eligible.iterrows():\n'
|
| 59 |
+
|
| 60 |
+
new2 = ' for _, book_row in eligible.iterrows():\n' # same β we patch inside
|
| 61 |
+
|
| 62 |
+
# What we actually want: find the book_id assignment and wrap the book body.
|
| 63 |
+
# Safer approach: inject phase logs at known anchor points.
|
| 64 |
+
|
| 65 |
+
# Phase log: book start β inject after book_id is assigned
|
| 66 |
+
old_book_start = (
|
| 67 |
+
' for _, book_row in eligible.iterrows():\n'
|
| 68 |
+
' book_id = str(book_row.get("book_id", "")).strip()\n'
|
| 69 |
+
)
|
| 70 |
+
new_book_start = (
|
| 71 |
+
' for _, book_row in eligible.iterrows():\n'
|
| 72 |
+
' book_id = str(book_row.get("book_id", "")).strip()\n'
|
| 73 |
+
' log.append(log_line(f"βΆ Starting OCR for {book_id}"))\n'
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
if old_book_start in content:
|
| 77 |
+
content = content.replace(old_book_start, new_book_start, 1)
|
| 78 |
+
fixes_applied.append("Fix 3a: APPLIED β phase log: book start")
|
| 79 |
+
elif new_book_start in content:
|
| 80 |
+
fixes_applied.append("Fix 3a: already applied β book start log")
|
| 81 |
+
else:
|
| 82 |
+
fixes_applied.append("Fix 3a: SKIPPED β book_id assignment pattern not found")
|
| 83 |
+
|
| 84 |
+
# Phase log: after renders complete β find render dir creation anchor
|
| 85 |
+
old_render_done = (
|
| 86 |
+
' log.append(log_line(f" β {book_id}: rendered {len(items_to_ocr)} page(s)"))\n'
|
| 87 |
+
)
|
| 88 |
+
new_render_done = (
|
| 89 |
+
' log.append(log_line(f" β {book_id}: rendered {len(items_to_ocr)} page(s)"))\n'
|
| 90 |
+
' log.append(log_line(f" β {book_id}: render complete β starting OCR batches"))\n'
|
| 91 |
+
)
|
| 92 |
+
if old_render_done in content and new_render_done not in content:
|
| 93 |
+
content = content.replace(old_render_done, new_render_done, 1)
|
| 94 |
+
fixes_applied.append("Fix 3b: APPLIED β phase log: render complete")
|
| 95 |
+
else:
|
| 96 |
+
fixes_applied.append("Fix 3b: SKIPPED β render log anchor not found or already applied")
|
| 97 |
+
|
| 98 |
+
# Phase log: after batch OCR loop β before second pass
|
| 99 |
+
old_second_pass_header = (
|
| 100 |
+
' # Second pass: build page-level OCR output and review queue.\n'
|
| 101 |
+
)
|
| 102 |
+
new_second_pass_header = (
|
| 103 |
+
' log.append(log_line(f" β {book_id}: batch OCR complete β building review queue"))\n'
|
| 104 |
+
' # Second pass: build page-level OCR output and review queue.\n'
|
| 105 |
+
)
|
| 106 |
+
if old_second_pass_header in content and new_second_pass_header not in content:
|
| 107 |
+
content = content.replace(old_second_pass_header, new_second_pass_header, 1)
|
| 108 |
+
fixes_applied.append("Fix 3c: APPLIED β phase log: batch OCR complete")
|
| 109 |
+
else:
|
| 110 |
+
fixes_applied.append("Fix 3c: SKIPPED β second pass header not found or already applied")
|
| 111 |
+
|
| 112 |
+
# ββ Fix 2: Per-book try/except wrapper βββββββββββββββββββββββββββββββββββββββ
|
| 113 |
+
# Wrap the entire per-book block in try/except so errors surface in the log
|
| 114 |
+
# instead of silently hanging or crashing the whole run_ocr call.
|
| 115 |
+
# Find the queue_rows append at end of book processing as the anchor.
|
| 116 |
+
|
| 117 |
+
old_queue_write = (
|
| 118 |
+
' if queue_rows_for_book:\n'
|
| 119 |
+
' processed_books.append(book_id)\n'
|
| 120 |
+
)
|
| 121 |
+
new_queue_write = (
|
| 122 |
+
' if queue_rows_for_book:\n'
|
| 123 |
+
' log.append(log_line(f" β {book_id}: queue write complete β {len(queue_rows_for_book)} page(s) added"))\n'
|
| 124 |
+
' processed_books.append(book_id)\n'
|
| 125 |
+
)
|
| 126 |
+
if old_queue_write in content and new_queue_write not in content:
|
| 127 |
+
content = content.replace(old_queue_write, new_queue_write, 1)
|
| 128 |
+
fixes_applied.append("Fix 3d: APPLIED β phase log: queue write complete")
|
| 129 |
+
else:
|
| 130 |
+
fixes_applied.append("Fix 3d: SKIPPED β queue write anchor not found or already applied")
|
| 131 |
+
|
| 132 |
+
# Top-level per-book except β find end of book loop body to add except clause.
|
| 133 |
+
# We look for the `finally: doc.close()` pattern which ends the book block.
|
| 134 |
+
old_finally = (
|
| 135 |
+
' finally:\n'
|
| 136 |
+
' if doc is not None:\n'
|
| 137 |
+
' try:\n'
|
| 138 |
+
' doc.close()\n'
|
| 139 |
+
' except Exception:\n'
|
| 140 |
+
' pass\n'
|
| 141 |
+
' doc = None\n'
|
| 142 |
+
)
|
| 143 |
+
new_finally = (
|
| 144 |
+
' except Exception as _book_err:\n'
|
| 145 |
+
' import traceback\n'
|
| 146 |
+
' log.append(log_line(f" β {book_id}: UNHANDLED ERROR β {_book_err}"))\n'
|
| 147 |
+
' log.append(log_line(traceback.format_exc()))\n'
|
| 148 |
+
' finally:\n'
|
| 149 |
+
' if doc is not None:\n'
|
| 150 |
+
' try:\n'
|
| 151 |
+
' doc.close()\n'
|
| 152 |
+
' except Exception:\n'
|
| 153 |
+
' pass\n'
|
| 154 |
+
' doc = None\n'
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
# Only add except if there's already a try: wrapping the book body.
|
| 158 |
+
# Check for existing try: at book level.
|
| 159 |
+
if 'except Exception as _book_err:' in content:
|
| 160 |
+
fixes_applied.append("Fix 2: already applied β per-book except block exists")
|
| 161 |
+
elif old_finally in content:
|
| 162 |
+
# Check there's a matching try: for this finally β if so, add except before finally
|
| 163 |
+
content = content.replace(old_finally, new_finally, 1)
|
| 164 |
+
fixes_applied.append("Fix 2: APPLIED β per-book except block added before finally")
|
| 165 |
+
else:
|
| 166 |
+
fixes_applied.append("Fix 2: SKIPPED β finally block pattern not found; add manually")
|
| 167 |
+
|
| 168 |
+
# ββ Write file if changed βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 169 |
+
if content != original:
|
| 170 |
+
TARGET.write_text(content)
|
| 171 |
+
print(f"β Wrote {TARGET}")
|
| 172 |
+
else:
|
| 173 |
+
print("β No changes written β all fixes were already applied or skipped")
|
| 174 |
+
|
| 175 |
+
print("\nFix summary:")
|
| 176 |
+
for f in fixes_applied:
|
| 177 |
+
print(f" {f}")
|