Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1076,78 +1076,126 @@ def _extract_textlayer_rows(pdf_path: str, page_num: int):
|
|
| 1076 |
|
| 1077 |
def _extract_jb_summary_sections(text_layer: str, needs_checks: bool = True, needs_dab: bool = True) -> str:
|
| 1078 |
"""
|
| 1079 |
-
|
| 1080 |
-
text
|
| 1081 |
|
| 1082 |
-
|
| 1083 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1084 |
"""
|
| 1085 |
if not text_layer:
|
| 1086 |
return ""
|
| 1087 |
|
| 1088 |
-
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
_BAL_ROW = re.compile(r"^(\d{2}-\d{2})\s+(\d{1,3}(?:,\d{3})*\.\d{2})\s*$")
|
| 1092 |
|
| 1093 |
-
lines
|
| 1094 |
output = []
|
| 1095 |
|
| 1096 |
-
# ββ Checks
|
| 1097 |
if needs_checks:
|
| 1098 |
checks = []
|
| 1099 |
-
|
| 1100 |
-
|
| 1101 |
-
|
| 1102 |
-
|
| 1103 |
-
|
| 1104 |
-
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1111 |
|
| 1112 |
if checks:
|
| 1113 |
-
|
| 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 |
-
+
|
| 1121 |
)
|
| 1122 |
|
| 1123 |
-
# ββ Daily Account Balance
|
| 1124 |
if needs_dab:
|
| 1125 |
balances = []
|
| 1126 |
-
|
| 1127 |
-
|
| 1128 |
-
|
| 1129 |
-
|
| 1130 |
-
|
| 1131 |
-
|
| 1132 |
-
|
| 1133 |
-
|
| 1134 |
-
|
| 1135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1136 |
|
| 1137 |
if balances:
|
| 1138 |
-
|
| 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 |
-
+
|
| 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
|
|
@@ -2369,13 +2417,25 @@ def run_ocr(uploaded_file):
|
|
| 2369 |
needs_checks = "checks" not in stabilized.lower() or "<th>Number</th>" not in stabilized
|
| 2370 |
needs_dab = "daily account balance" not in stabilized.lower()
|
| 2371 |
if needs_checks or needs_dab:
|
| 2372 |
-
|
| 2373 |
-
|
| 2374 |
-
|
| 2375 |
-
|
| 2376 |
-
|
| 2377 |
-
|
| 2378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2379 |
except Exception:
|
| 2380 |
pass # safe no-op β never break other PDFs
|
| 2381 |
|
|
|
|
| 1076 |
|
| 1077 |
def _extract_jb_summary_sections(text_layer: str, needs_checks: bool = True, needs_dab: bool = True) -> str:
|
| 1078 |
"""
|
| 1079 |
+
Parse Johnson Bank Checks and Daily Account Balance from pdfminer column-separated
|
| 1080 |
+
page text and return as HTML tables.
|
| 1081 |
|
| 1082 |
+
pdfminer outputs multi-column sections as separate column lists:
|
| 1083 |
+
Date Number Amount
|
| 1084 |
+
04-14 3259 45.72
|
| 1085 |
+
becomes:
|
| 1086 |
+
"Date\n04-14\n04-09\n\nNumber\n3259\n3260\n\nAmount\n45.72\n1,628.00"
|
| 1087 |
+
|
| 1088 |
+
Uses pdfminer (always available) not pymupdf.
|
| 1089 |
"""
|
| 1090 |
if not text_layer:
|
| 1091 |
return ""
|
| 1092 |
|
| 1093 |
+
_DATE_RE = re.compile(r"^\d{2}-\d{2}$")
|
| 1094 |
+
_MONEY_RE = re.compile(r"^\d{1,3}(?:,\d{3})*\.\d{2}$")
|
| 1095 |
+
_CHECK_RE = re.compile(r"^\*?\d+$")
|
|
|
|
| 1096 |
|
| 1097 |
+
lines = [l.strip() for l in text_layer.splitlines()]
|
| 1098 |
output = []
|
| 1099 |
|
| 1100 |
+
# ββ Checks βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1101 |
if needs_checks:
|
| 1102 |
checks = []
|
| 1103 |
+
i = 0
|
| 1104 |
+
while i < len(lines):
|
| 1105 |
+
if lines[i].lower().startswith("checks"):
|
| 1106 |
+
i += 1
|
| 1107 |
+
while i < len(lines):
|
| 1108 |
+
if "daily account balance" in lines[i].lower():
|
| 1109 |
+
break
|
| 1110 |
+
if lines[i] == "Date":
|
| 1111 |
+
i += 1
|
| 1112 |
+
dates = []
|
| 1113 |
+
while i < len(lines) and _DATE_RE.match(lines[i]):
|
| 1114 |
+
dates.append(lines[i]); i += 1
|
| 1115 |
+
while i < len(lines) and lines[i] != "Number":
|
| 1116 |
+
i += 1
|
| 1117 |
+
i += 1
|
| 1118 |
+
numbers = []
|
| 1119 |
+
while i < len(lines):
|
| 1120 |
+
if _CHECK_RE.match(lines[i]):
|
| 1121 |
+
numbers.append(lines[i]); i += 1
|
| 1122 |
+
elif lines[i] == "":
|
| 1123 |
+
i += 1
|
| 1124 |
+
else:
|
| 1125 |
+
break
|
| 1126 |
+
while i < len(lines) and lines[i] != "Amount":
|
| 1127 |
+
i += 1
|
| 1128 |
+
i += 1
|
| 1129 |
+
amounts = []
|
| 1130 |
+
while i < len(lines):
|
| 1131 |
+
if _MONEY_RE.match(lines[i]):
|
| 1132 |
+
amounts.append(lines[i]); i += 1
|
| 1133 |
+
elif lines[i] == "":
|
| 1134 |
+
i += 1
|
| 1135 |
+
else:
|
| 1136 |
+
break
|
| 1137 |
+
for d, n, a in zip(dates, numbers, amounts):
|
| 1138 |
+
checks.append((d, n, a))
|
| 1139 |
+
else:
|
| 1140 |
+
i += 1
|
| 1141 |
+
break
|
| 1142 |
+
i += 1
|
| 1143 |
|
| 1144 |
if checks:
|
| 1145 |
+
rows = "\n".join(
|
| 1146 |
"<tr><td>" + d + "</td><td>" + n + "</td><td>" + a + "</td></tr>"
|
| 1147 |
for d, n, a in checks
|
| 1148 |
)
|
| 1149 |
output.append("Checks")
|
| 1150 |
output.append(
|
| 1151 |
"<table>\n<tr><th>Date</th><th>Number</th><th>Amount</th></tr>\n"
|
| 1152 |
+
+ rows + "\n</table>"
|
| 1153 |
)
|
| 1154 |
|
| 1155 |
+
# ββ Daily Account Balance βββββββββββββββββββββββββββββββββββββββββββββ
|
| 1156 |
if needs_dab:
|
| 1157 |
balances = []
|
| 1158 |
+
i = 0
|
| 1159 |
+
while i < len(lines):
|
| 1160 |
+
if "daily account balance" in lines[i].lower():
|
| 1161 |
+
i += 1
|
| 1162 |
+
while i < len(lines):
|
| 1163 |
+
if lines[i] == "Date":
|
| 1164 |
+
i += 1
|
| 1165 |
+
dates = []
|
| 1166 |
+
while i < len(lines) and _DATE_RE.match(lines[i]):
|
| 1167 |
+
dates.append(lines[i]); i += 1
|
| 1168 |
+
while i < len(lines) and lines[i] != "Balance":
|
| 1169 |
+
i += 1
|
| 1170 |
+
i += 1
|
| 1171 |
+
bals = []
|
| 1172 |
+
while i < len(lines):
|
| 1173 |
+
if _MONEY_RE.match(lines[i]):
|
| 1174 |
+
bals.append(lines[i]); i += 1
|
| 1175 |
+
elif lines[i] == "":
|
| 1176 |
+
i += 1
|
| 1177 |
+
else:
|
| 1178 |
+
break
|
| 1179 |
+
for d, b in zip(dates, bals):
|
| 1180 |
+
balances.append((d, b))
|
| 1181 |
+
else:
|
| 1182 |
+
i += 1
|
| 1183 |
+
break
|
| 1184 |
+
i += 1
|
| 1185 |
|
| 1186 |
if balances:
|
| 1187 |
+
rows = "\n".join(
|
| 1188 |
"<tr><td>" + d + "</td><td>" + b + "</td></tr>"
|
| 1189 |
for d, b in balances
|
| 1190 |
)
|
| 1191 |
output.append("Daily Account Balance")
|
| 1192 |
output.append(
|
| 1193 |
"<table>\n<tr><th>Date</th><th>Balance</th></tr>\n"
|
| 1194 |
+
+ rows + "\n</table>"
|
| 1195 |
)
|
| 1196 |
|
| 1197 |
return "\n\n".join(output)
|
| 1198 |
|
|
|
|
| 1199 |
def _patch_ocr_with_textlayer(page_md: str, pdf_path: str, page_num: int) -> str:
|
| 1200 |
"""
|
| 1201 |
Compare OCR table rows against PDF text-layer rows and inject any rows
|
|
|
|
| 2417 |
needs_checks = "checks" not in stabilized.lower() or "<th>Number</th>" not in stabilized
|
| 2418 |
needs_dab = "daily account balance" not in stabilized.lower()
|
| 2419 |
if needs_checks or needs_dab:
|
| 2420 |
+
# Try pdfminer first (column-aware), fall back to pymupdf
|
| 2421 |
+
_txt4d = ""
|
| 2422 |
+
try:
|
| 2423 |
+
from pdfminer.high_level import extract_text as _pm_extract
|
| 2424 |
+
_txt4d = _pm_extract(path, page_numbers=[page_num])
|
| 2425 |
+
except Exception:
|
| 2426 |
+
pass
|
| 2427 |
+
if not _txt4d:
|
| 2428 |
+
try:
|
| 2429 |
+
import pymupdf as _fitz4d
|
| 2430 |
+
_d4 = _fitz4d.open(path)
|
| 2431 |
+
_txt4d = _d4[page_num].get_text()
|
| 2432 |
+
_d4.close()
|
| 2433 |
+
except Exception:
|
| 2434 |
+
pass
|
| 2435 |
+
if _txt4d:
|
| 2436 |
+
jb_extra = _extract_jb_summary_sections(_txt4d, needs_checks, needs_dab)
|
| 2437 |
+
if jb_extra:
|
| 2438 |
+
stabilized = stabilized.rstrip() + "\n\n" + jb_extra
|
| 2439 |
except Exception:
|
| 2440 |
pass # safe no-op β never break other PDFs
|
| 2441 |
|