rehan953 commited on
Commit
f035ea9
·
verified ·
1 Parent(s): cccfce8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -62
app.py CHANGED
@@ -1255,63 +1255,44 @@ def _patch_ocr_with_textlayer(page_md: str, pdf_path: str, page_num: int) -> str
1255
  insert_pos = result.find(new_table_html) + len(new_table_html)
1256
  result = result[:insert_pos] + extra_html + result[insert_pos:]
1257
 
1258
- # Case C: NO transaction table exists on this page at all, but text
1259
- # layer has valid rows. Build a brand-new table and prepend it to
1260
- # the page content. This handles pages where OCR dropped the entire
1261
- # transaction section (e.g. East West Bank page 7 with only 2 rows).
1262
- #
1263
- # Guard: only fire if no table on this page already has DATE+DESCRIPTION
1264
- # columns (avoids duplicating non-transaction tables like DAILY BALANCES).
1265
- table_pattern2 = re.compile(r"<table[^>]*>.*?</table>", re.DOTALL | re.IGNORECASE)
1266
- has_txn_table = False
1267
- for tbl in table_pattern2.finditer(result):
1268
- p2 = TableGridParser()
1269
- p2.feed(tbl.group(0))
1270
- g2 = _build_grid(p2.rows)
1271
- if len(g2) < 2:
1272
- continue
1273
- hdr2 = [str(c or "").strip().upper() for c in g2[0]]
1274
- has_date = any(re.search(r"DATE", h) for h in hdr2)
1275
- has_desc = any(re.search(r"(DESCRIPTION|DETAILS?|NARRATION|PARTICULARS?)", h) for h in hdr2)
1276
- if has_date and has_desc:
1277
- has_txn_table = True
1278
- break
1279
-
1280
- if not has_txn_table and tl_rows:
1281
- # Infer date format from text layer to pick column header label
1282
- _hy = re.compile(r"^\d{1,2}-\d{2}")
1283
- _sl = re.compile(r"^\d{1,2}/\d{2}")
1284
- _sample_dates = [_norm(r["date"]) for r in tl_rows[:3]]
1285
- if any(_hy.match(d) for d in _sample_dates):
1286
- date_label = "Date"
1287
- elif any(_sl.match(d) for d in _sample_dates):
1288
- date_label = "Date"
1289
- else:
1290
- date_label = "Trans Date"
1291
 
1292
- has_post = any(r.get("post_date") for r in tl_rows)
1293
- if has_post:
1294
- header_row = [date_label, "Post Date", "Transaction Description", "Amount"]
1295
- else:
1296
- header_row = [date_label, "Transaction Description", "Amount"]
1297
-
1298
- date_ci = 0
1299
- desc_ci = 2 if has_post else 1
1300
- amt_ci = 3 if has_post else 2
1301
-
1302
- new_rows = []
1303
- for r in tl_rows:
1304
- row = [""] * len(header_row)
1305
- row[date_ci] = r["date"]
1306
- row[desc_ci] = r["desc"]
1307
- row[amt_ci] = r["amount"]
1308
- if has_post:
1309
- row[1] = r.get("post_date", "")
1310
- new_rows.append(row)
1311
-
1312
- new_table_html = _grid_to_html([header_row] + new_rows)
1313
- result = new_table_html + "\n\n" + result
1314
- log.info("page %d: created new table from text layer (%d rows)", page_num, len(new_rows))
1315
 
1316
  return result
1317
 
@@ -1646,13 +1627,13 @@ def run_ocr(uploaded_file):
1646
  # Guard 2: suppress footer that is a raw text-layer dump of
1647
  # transaction rows (e.g. East West Bank two-column layout).
1648
  # Detected when the footer contains 3+ date tokens (MM/DD or MM-DD)
1649
- # AND the page already has an OCR table the table already
1650
- # captured this data correctly, so the footer text is redundant.
1651
  _footer_date_re = re.compile(r"\b\d{1,2}[-/]\d{2}\b")
1652
- is_txn_dump = (
1653
- len(_footer_date_re.findall(ftr_clean)) >= 3
1654
- and any("<table" in part.lower() for part in parts)
1655
- )
1656
 
1657
  if not already_present and not is_txn_dump:
1658
  parts.append(ftr_clean)
 
1255
  insert_pos = result.find(new_table_html) + len(new_table_html)
1256
  result = result[:insert_pos] + extra_html + result[insert_pos:]
1257
 
1258
+ # Case C: page has text-layer rows but NO transaction table in OCR output.
1259
+ # Build a new table from the text layer and prepend it.
1260
+ # Guard: only fires when the page has NO table with both DATE + DESCRIPTION cols.
1261
+ # This is placed AFTER the for-loop so it only runs once per page, not per table.
1262
+ if tl_rows:
1263
+ _has_txn_table = False
1264
+ for _tbl in table_pattern.finditer(result):
1265
+ _p2 = TableGridParser()
1266
+ _p2.feed(_tbl.group(0))
1267
+ _g2 = _build_grid(_p2.rows)
1268
+ if len(_g2) < 2:
1269
+ continue
1270
+ _h2 = [str(c or "").strip().upper() for c in _g2[0]]
1271
+ if (any(re.search(r"\bDATE\b", x) for x in _h2) and
1272
+ any(re.search(r"\b(DESCRIPTION|DETAILS?|NARRATION|PARTICULARS?)\b", x) for x in _h2)):
1273
+ _has_txn_table = True
1274
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1275
 
1276
+ if not _has_txn_table:
1277
+ _has_post = any(r.get("post_date") for r in tl_rows)
1278
+ if _has_post:
1279
+ _chdr = ["Date", "Post Date", "Transaction Description", "Amount"]
1280
+ _di2, _pi2, _xi2, _ai2 = 0, 1, 2, 3
1281
+ else:
1282
+ _chdr = ["Date", "Transaction Description", "Amount"]
1283
+ _di2, _xi2, _ai2 = 0, 1, 2
1284
+ _cnew_rows = []
1285
+ for r in tl_rows:
1286
+ _crow = [""] * len(_chdr)
1287
+ _crow[_di2] = r["date"]
1288
+ _crow[_xi2] = r["desc"]
1289
+ _crow[_ai2] = r["amount"]
1290
+ if _has_post:
1291
+ _crow[_pi2] = r.get("post_date", "")
1292
+ _cnew_rows.append(_crow)
1293
+ result = _grid_to_html([_chdr] + _cnew_rows) + "\n\n" + result
1294
+ log.info("page %d: Case C — new table (%d rows) from text layer",
1295
+ page_num, len(_cnew_rows))
 
 
 
1296
 
1297
  return result
1298
 
 
1627
  # Guard 2: suppress footer that is a raw text-layer dump of
1628
  # transaction rows (e.g. East West Bank two-column layout).
1629
  # Detected when the footer contains 3+ date tokens (MM/DD or MM-DD)
1630
+ # AND the footer itself contains amountsmeaning it is transaction
1631
+ # data, not a legitimate page footer like an address or disclaimer.
1632
  _footer_date_re = re.compile(r"\b\d{1,2}[-/]\d{2}\b")
1633
+ _footer_amt_re = re.compile(r"\b\d{1,3}(?:,\d{3})*\.\d{2}\b")
1634
+ _date_hits = len(_footer_date_re.findall(ftr_clean))
1635
+ _amt_hits = len(_footer_amt_re.findall(ftr_clean))
1636
+ is_txn_dump = _date_hits >= 3 and _amt_hits >= 3
1637
 
1638
  if not already_present and not is_txn_dump:
1639
  parts.append(ftr_clean)