Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1074,6 +1074,80 @@ def _extract_textlayer_rows(pdf_path: str, page_num: int):
|
|
| 1074 |
return []
|
| 1075 |
|
| 1076 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1077 |
def _patch_ocr_with_textlayer(page_md: str, pdf_path: str, page_num: int) -> str:
|
| 1078 |
"""
|
| 1079 |
Compare OCR table rows against PDF text-layer rows and inject any rows
|
|
@@ -1301,6 +1375,16 @@ def _patch_ocr_with_textlayer(page_md: str, pdf_path: str, page_num: int) -> str
|
|
| 1301 |
log.info("page %d: Case C β new table (%d rows) from text layer",
|
| 1302 |
page_num, len(_cnew_rows))
|
| 1303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1304 |
return result
|
| 1305 |
|
| 1306 |
except Exception as e:
|
|
|
|
| 1074 |
return []
|
| 1075 |
|
| 1076 |
|
| 1077 |
+
def _extract_jb_summary_sections(text_layer: str, needs_checks: bool = True, needs_dab: bool = True) -> str:
|
| 1078 |
+
"""
|
| 1079 |
+
Extract Johnson Bank Checks and Daily Account Balance sections from the PDF
|
| 1080 |
+
text layer and return them as HTML tables.
|
| 1081 |
+
|
| 1082 |
+
Only fires when MM-DD date format is detected (Johnson Bank unique identifier).
|
| 1083 |
+
needs_checks / needs_dab flags prevent re-extraction when already present.
|
| 1084 |
+
"""
|
| 1085 |
+
if not text_layer:
|
| 1086 |
+
return ""
|
| 1087 |
+
|
| 1088 |
+
_CHK_SEC = re.compile(r"Checks\s*\(", re.IGNORECASE)
|
| 1089 |
+
_DAB_SEC = re.compile(r"^Daily\s+Account\s+Balance\s*$", re.IGNORECASE | re.MULTILINE)
|
| 1090 |
+
_CHK_ROW = re.compile(r"^(\d{2}-\d{2})\s+(\*?\d+)\s+(\d{1,3}(?:,\d{3})*\.\d{2})\s*$")
|
| 1091 |
+
_BAL_ROW = re.compile(r"^(\d{2}-\d{2})\s+(\d{1,3}(?:,\d{3})*\.\d{2})\s*$")
|
| 1092 |
+
|
| 1093 |
+
lines = text_layer.splitlines()
|
| 1094 |
+
output = []
|
| 1095 |
+
|
| 1096 |
+
# ββ Checks section βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1097 |
+
if needs_checks:
|
| 1098 |
+
checks = []
|
| 1099 |
+
in_checks = False
|
| 1100 |
+
for line in lines:
|
| 1101 |
+
stripped = line.strip()
|
| 1102 |
+
if _CHK_SEC.search(stripped):
|
| 1103 |
+
in_checks = True
|
| 1104 |
+
continue
|
| 1105 |
+
if in_checks:
|
| 1106 |
+
if _DAB_SEC.match(stripped):
|
| 1107 |
+
break
|
| 1108 |
+
m = _CHK_ROW.match(stripped)
|
| 1109 |
+
if m:
|
| 1110 |
+
checks.append((m.group(1), m.group(2), m.group(3)))
|
| 1111 |
+
|
| 1112 |
+
if checks:
|
| 1113 |
+
rows_html = "\n".join(
|
| 1114 |
+
"<tr><td>" + d + "</td><td>" + n + "</td><td>" + a + "</td></tr>"
|
| 1115 |
+
for d, n, a in checks
|
| 1116 |
+
)
|
| 1117 |
+
output.append("Checks")
|
| 1118 |
+
output.append(
|
| 1119 |
+
"<table>\n<tr><th>Date</th><th>Number</th><th>Amount</th></tr>\n"
|
| 1120 |
+
+ rows_html + "\n</table>"
|
| 1121 |
+
)
|
| 1122 |
+
|
| 1123 |
+
# ββ Daily Account Balance section βββββββββββββββββββββββββββββββββββββββ
|
| 1124 |
+
if needs_dab:
|
| 1125 |
+
balances = []
|
| 1126 |
+
in_bal = False
|
| 1127 |
+
for line in lines:
|
| 1128 |
+
stripped = line.strip()
|
| 1129 |
+
if _DAB_SEC.match(stripped):
|
| 1130 |
+
in_bal = True
|
| 1131 |
+
continue
|
| 1132 |
+
if in_bal:
|
| 1133 |
+
m = _BAL_ROW.match(stripped)
|
| 1134 |
+
if m:
|
| 1135 |
+
balances.append((m.group(1), m.group(2)))
|
| 1136 |
+
|
| 1137 |
+
if balances:
|
| 1138 |
+
rows_html = "\n".join(
|
| 1139 |
+
"<tr><td>" + d + "</td><td>" + b + "</td></tr>"
|
| 1140 |
+
for d, b in balances
|
| 1141 |
+
)
|
| 1142 |
+
output.append("Daily Account Balance")
|
| 1143 |
+
output.append(
|
| 1144 |
+
"<table>\n<tr><th>Date</th><th>Balance</th></tr>\n"
|
| 1145 |
+
+ rows_html + "\n</table>"
|
| 1146 |
+
)
|
| 1147 |
+
|
| 1148 |
+
return "\n\n".join(output)
|
| 1149 |
+
|
| 1150 |
+
|
| 1151 |
def _patch_ocr_with_textlayer(page_md: str, pdf_path: str, page_num: int) -> str:
|
| 1152 |
"""
|
| 1153 |
Compare OCR table rows against PDF text-layer rows and inject any rows
|
|
|
|
| 1375 |
log.info("page %d: Case C β new table (%d rows) from text layer",
|
| 1376 |
page_num, len(_cnew_rows))
|
| 1377 |
|
| 1378 |
+
# Fix 4D: append Johnson Bank Checks + Daily Account Balance
|
| 1379 |
+
# if missing from OCR but present in PDF text layer.
|
| 1380 |
+
needs_checks = "checks" not in result.lower() or "<th>Number</th>" not in result
|
| 1381 |
+
needs_dab = "daily account balance" not in result.lower()
|
| 1382 |
+
if needs_checks or needs_dab:
|
| 1383 |
+
raw_tl = extract_zone_text_pdf(pdf_path, page_num, 0.0, 1.0) or ""
|
| 1384 |
+
jb_extra = _extract_jb_summary_sections(raw_tl, needs_checks, needs_dab)
|
| 1385 |
+
if jb_extra:
|
| 1386 |
+
result = result.rstrip() + "\n\n" + jb_extra
|
| 1387 |
+
|
| 1388 |
return result
|
| 1389 |
|
| 1390 |
except Exception as e:
|