rehan953 commited on
Commit
ba118bb
Β·
verified Β·
1 Parent(s): 631e34a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -45
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
- 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
@@ -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
- import pymupdf as _fitz4d
2373
- _doc4d = _fitz4d.open(path)
2374
- _txt4d = _doc4d[page_num].get_text()
2375
- _doc4d.close()
2376
- jb_extra = _extract_jb_summary_sections(_txt4d, needs_checks, needs_dab)
2377
- if jb_extra:
2378
- stabilized = stabilized.rstrip() + "\n\n" + jb_extra
 
 
 
 
 
 
 
 
 
 
 
 
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