Claude commited on
Commit
4ee95d7
·
1 Parent(s): 2d7fa7e

feat: persistent noise pattern exclusion — reject NON_STORY_TEXT to block on next OCR run

Browse files
Files changed (1) hide show
  1. smoke_signal_tab.py +53 -0
smoke_signal_tab.py CHANGED
@@ -57,6 +57,48 @@ for d in [SOURCE_DIR, MANIFEST_CSV.parent, PROFILES_DIR, OCR_RAW_DIR,
57
  d.mkdir(parents=True, exist_ok=True)
58
 
59
  GOLD_FILE = GOLD_DIR / "gold_corrections.jsonl"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  DECISIONS_CSV = REVIEW_DIR / "review_decisions.csv"
61
  QUEUE_CSV = REVIEW_DIR / "review_queue.csv"
62
  BANNER_DATA_URI_FILE = Path(__file__).resolve().parent / "assets" / "smoke_signal_banner_data_uri.txt"
@@ -2371,6 +2413,13 @@ def run_ocr(page_selection: str = "", page_exclusion: str = "", replace_book_que
2371
  method = "skipped-no-surya"
2372
 
2373
  raw_text = " ".join(r["text"] for r in regions)[:500]
 
 
 
 
 
 
 
2374
  corrected_text, punct_flags, punct_score = _apply_punctuation_corrections(
2375
  raw_text,
2376
  book_id,
@@ -2656,6 +2705,10 @@ def save_review_decision(idx: int, final_text: str, action: str, reviewer: str,
2656
  }) + "\n")
2657
 
2658
  learned_pairs = 0
 
 
 
 
2659
  if action == "edited":
2660
  learned_pairs = _record_punctuation_correction(
2661
  raw_text_original,
 
57
  d.mkdir(parents=True, exist_ok=True)
58
 
59
  GOLD_FILE = GOLD_DIR / "gold_corrections.jsonl"
60
+ NOISE_DIR = SS_ROOT / "calibration" / "noise_patterns"
61
+
62
+
63
+ def _noise_path(book_id: str) -> Path:
64
+ NOISE_DIR.mkdir(parents=True, exist_ok=True)
65
+ return NOISE_DIR / f"{book_id}_noise.json"
66
+
67
+
68
+ def _load_noise_patterns(book_id: str) -> list:
69
+ path = _noise_path(book_id)
70
+ if not path.exists():
71
+ return []
72
+ try:
73
+ return json.loads(path.read_text())
74
+ except Exception:
75
+ return []
76
+
77
+
78
+ def _save_noise_pattern(book_id: str, pattern: str) -> None:
79
+ pattern = pattern.strip()
80
+ if not pattern or len(pattern) < 3:
81
+ return
82
+ patterns = _load_noise_patterns(book_id)
83
+ if pattern not in patterns:
84
+ patterns.append(pattern)
85
+ _noise_path(book_id).write_text(json.dumps(patterns, indent=2))
86
+
87
+
88
+ def _text_matches_noise(text: str, patterns: list) -> bool:
89
+ if not patterns or not text:
90
+ return False
91
+ text_tokens = set(text.lower().split())
92
+ if not text_tokens:
93
+ return False
94
+ for pattern in patterns:
95
+ pat_tokens = set(pattern.lower().split())
96
+ if not pat_tokens:
97
+ continue
98
+ overlap = len(text_tokens & pat_tokens) / len(text_tokens)
99
+ if overlap >= 0.6:
100
+ return True
101
+ return False
102
  DECISIONS_CSV = REVIEW_DIR / "review_decisions.csv"
103
  QUEUE_CSV = REVIEW_DIR / "review_queue.csv"
104
  BANNER_DATA_URI_FILE = Path(__file__).resolve().parent / "assets" / "smoke_signal_banner_data_uri.txt"
 
2413
  method = "skipped-no-surya"
2414
 
2415
  raw_text = " ".join(r["text"] for r in regions)[:500]
2416
+
2417
+ # Skip pages matching known noise patterns for this book
2418
+ _noise_pats = _load_noise_patterns(book_id)
2419
+ if _noise_pats and _text_matches_noise(raw_text, _noise_pats):
2420
+ log.append(log_line(f" ⊘ {book_id} p{page_num}: matched noise pattern — skipped"))
2421
+ continue
2422
+
2423
  corrected_text, punct_flags, punct_score = _apply_punctuation_corrections(
2424
  raw_text,
2425
  book_id,
 
2705
  }) + "\n")
2706
 
2707
  learned_pairs = 0
2708
+ if action == "rejected" and str(reason).strip() == "NON_STORY_TEXT":
2709
+ noise_text = (final_text.strip() or raw_text.strip())
2710
+ if noise_text:
2711
+ _save_noise_pattern(str(item.get("book_id", "")), noise_text)
2712
  if action == "edited":
2713
  learned_pairs = _record_punctuation_correction(
2714
  raw_text_original,