rehan953 commited on
Commit
3ba0707
·
verified ·
1 Parent(s): 3e23bb2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -0
app.py CHANGED
@@ -38,6 +38,11 @@ Primary goals for reconcile rate:
38
  — take the rightmost plausible money token from Description+Amount text for the amount.
39
  - Fix 20: DA3 in-table dedupe — cap consecutive identical normalized keys at 2 rows (keeps legitimate
40
  back-to-back identical charges; still collapses 3+ OCR stutters).
 
 
 
 
 
41
  """
42
 
43
  # Patch asyncio first (before Gradio imports it) to suppress Python 3.13 cleanup noise
@@ -3153,6 +3158,8 @@ _DA3_STRICT_AMOUNT = re.compile(r"^-?\$?\d{1,3}(?:,\d{3})*\.\d{2}-?$")
3153
  _DA3_MONEY_TOKEN = re.compile(
3154
  r"(?<![\d,])(-?\$?\d{1,3}(?:,\d{3})*\.\d{2}|-?\$?\d{2,6}\.\d{2})(?!\d)"
3155
  )
 
 
3156
 
3157
 
3158
  def _repair_da3_garbled_amount_cells(grid):
@@ -3381,6 +3388,122 @@ def _parse_da3_grid_from_table_html(table_html: str):
3381
  return None
3382
 
3383
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3384
  def _trim_adjacent_da3_suffix_duplicating_next_table_prefix(text: str) -> str:
3385
  """
3386
  Remove trailing data rows from a DA3 table when they are repeated as the opening
@@ -3568,9 +3691,11 @@ def stabilize_tables_and_text(page_md: str) -> str:
3568
 
3569
  stabilized = "\n\n".join(out_blocks)
3570
  stabilized = convert_plaintext_bank_sections(stabilized)
 
3571
  stabilized = normalize_html_tables(stabilized)
3572
  stabilized = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(stabilized)
3573
  stabilized = _split_ucb_deposits_electronic_sections_html(stabilized)
 
3574
  stabilized = _dedupe_subset_transaction_tables_html(stabilized)
3575
  return close_unclosed_html(stabilized)
3576
 
@@ -3700,6 +3825,7 @@ def run_ocr(uploaded_file):
3700
  stabilized = normalize_html_tables(stabilized)
3701
  stabilized = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(stabilized)
3702
  stabilized = _split_ucb_deposits_electronic_sections_html(stabilized)
 
3703
  stabilized = _dedupe_subset_transaction_tables_html(stabilized)
3704
  elif is_pdf and is_nfcu_doc:
3705
  # Navy-only additive fix: rebuild malformed summary table from
@@ -3783,6 +3909,7 @@ def run_ocr(uploaded_file):
3783
  if all_pages and merged and not merged.startswith("Error") and "<table" in merged.lower():
3784
  merged = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(merged)
3785
  merged = _split_ucb_deposits_electronic_sections_html(merged)
 
3786
  merged = _dedupe_subset_transaction_tables_html(merged)
3787
  return merged
3788
 
 
38
  — take the rightmost plausible money token from Description+Amount text for the amount.
39
  - Fix 20: DA3 in-table dedupe — cap consecutive identical normalized keys at 2 rows (keeps legitimate
40
  back-to-back identical charges; still collapses 3+ OCR stutters).
41
+ - Fix 21: Cross-table DA3 repair for MSBILL / MICROSOFT#G… PIN rows — when Amount is junk in one table
42
+ but another table repeats the same authorization id with a strict amount, copy that amount
43
+ (then subset dedupe can drop the stray duplicate block, e.g. under Deposits).
44
+ - Fix 22: Drop a single OCR “flat line” between pages: (continued) + DESCRIPTION + AMOUNT + many MM/DD
45
+ tokens (noise before a proper <table>).
46
  """
47
 
48
  # Patch asyncio first (before Gradio imports it) to suppress Python 3.13 cleanup noise
 
3158
  _DA3_MONEY_TOKEN = re.compile(
3159
  r"(?<![\d,])(-?\$?\d{1,3}(?:,\d{3})*\.\d{2}|-?\$?\d{2,6}\.\d{2})(?!\d)"
3160
  )
3161
+ _MSFT_PIN_G_REF = re.compile(r"MICROSOFT#(G\d{6,16})", re.IGNORECASE)
3162
+ _DA3_LEADING_DATE = re.compile(r"^\d{1,2}/\d{1,2}(?:/\d{2,4})?$")
3163
 
3164
 
3165
  def _repair_da3_garbled_amount_cells(grid):
 
3388
  return None
3389
 
3390
 
3391
+ def _strip_orphan_continued_da3_flatline(text: str) -> str:
3392
+ """
3393
+ Fix 22: Remove one-line OCR dumps like
3394
+ '…(continued) DATE DESCRIPTION AMOUNT($) 04/09 … 04/11 … 239.55' that duplicate the
3395
+ following HTML table and pollute markdown.
3396
+ """
3397
+ if not text or "(continued)" not in text.lower():
3398
+ return text
3399
+ lines = text.splitlines(keepends=True)
3400
+ out = []
3401
+ for line in lines:
3402
+ core = line.rstrip("\r\n")
3403
+ if (
3404
+ len(core) > 80
3405
+ and "(continued)" in core.lower()
3406
+ and "description" in core.lower()
3407
+ and "amount" in core.lower()
3408
+ and len(re.findall(r"\d{2}/\d{2}", core)) >= 3
3409
+ ):
3410
+ continue
3411
+ out.append(line)
3412
+ return "".join(out)
3413
+
3414
+
3415
+ def _patch_da3_msft_g_ref_amounts_across_tables(text: str) -> str:
3416
+ """
3417
+ Fix 21: Build MICROSOFT#G… -> strict Amount from any DA3 row, then patch other DA3 rows
3418
+ with the same id whose Amount cell is not strict currency (junk fused into Amount).
3419
+ """
3420
+ if not text or "<table" not in text.lower():
3421
+ return text
3422
+ if "microsoft#" not in text.lower():
3423
+ return text
3424
+
3425
+ tbl_pat = re.compile(r"<table[^>]*>.*?</table>", re.DOTALL | re.IGNORECASE)
3426
+
3427
+ def _collect():
3428
+ g_map = {}
3429
+ ambiguous = set()
3430
+ for m in tbl_pat.finditer(text):
3431
+ g = _parse_da3_grid_from_table_html(m.group(0))
3432
+ if not g:
3433
+ continue
3434
+ for row in g[1:]:
3435
+ cells = [str(c or "").strip() for c in row]
3436
+ while len(cells) < 3:
3437
+ cells.append("")
3438
+ c0, c1, c2 = cells[0], cells[1], cells[2]
3439
+ if not _DA3_LEADING_DATE.match(c0):
3440
+ continue
3441
+ if not _DA3_STRICT_AMOUNT.match(c2):
3442
+ continue
3443
+ blob = f"{c0} {c1} {c2}"
3444
+ for gid in _MSFT_PIN_G_REF.findall(blob):
3445
+ k = gid.upper()
3446
+ if k in ambiguous:
3447
+ continue
3448
+ trip = (c0, c1, c2)
3449
+ prev = g_map.get(k)
3450
+ if prev is not None and prev != trip:
3451
+ ambiguous.add(k)
3452
+ g_map.pop(k, None)
3453
+ elif prev is None:
3454
+ g_map[k] = trip
3455
+ return g_map, ambiguous
3456
+
3457
+ g_map, ambiguous = _collect()
3458
+ if not g_map:
3459
+ return text
3460
+
3461
+ out_chunks = []
3462
+ last = 0
3463
+ changed_any = False
3464
+ for m in tbl_pat.finditer(text):
3465
+ out_chunks.append(text[last : m.start()])
3466
+ table_html = m.group(0)
3467
+ grid = _parse_da3_grid_from_table_html(table_html)
3468
+ if not grid:
3469
+ out_chunks.append(table_html)
3470
+ last = m.end()
3471
+ continue
3472
+ new_grid = [list(grid[0])]
3473
+ changed = False
3474
+ for row in grid[1:]:
3475
+ cells = [str(c or "").strip() for c in row]
3476
+ while len(cells) < 3:
3477
+ cells.append("")
3478
+ c0, c1, c2 = cells[0], cells[1], cells[2]
3479
+ if not _DA3_LEADING_DATE.match(c0):
3480
+ new_grid.append(row)
3481
+ continue
3482
+ if _DA3_STRICT_AMOUNT.match(c2):
3483
+ new_grid.append(row)
3484
+ continue
3485
+ blob = f"{c0} {c1} {c2}"
3486
+ ids = _MSFT_PIN_G_REF.findall(blob)
3487
+ if len(ids) != 1:
3488
+ new_grid.append(row)
3489
+ continue
3490
+ k = ids[0].upper()
3491
+ if k in ambiguous or k not in g_map:
3492
+ new_grid.append(row)
3493
+ continue
3494
+ d0, d1, d2 = g_map[k]
3495
+ new_grid.append([d0, d1, d2])
3496
+ changed = True
3497
+ if changed:
3498
+ out_chunks.append(_grid_to_html(new_grid))
3499
+ changed_any = True
3500
+ else:
3501
+ out_chunks.append(table_html)
3502
+ last = m.end()
3503
+ out_chunks.append(text[last:])
3504
+ return "".join(out_chunks) if changed_any else text
3505
+
3506
+
3507
  def _trim_adjacent_da3_suffix_duplicating_next_table_prefix(text: str) -> str:
3508
  """
3509
  Remove trailing data rows from a DA3 table when they are repeated as the opening
 
3691
 
3692
  stabilized = "\n\n".join(out_blocks)
3693
  stabilized = convert_plaintext_bank_sections(stabilized)
3694
+ stabilized = _strip_orphan_continued_da3_flatline(stabilized)
3695
  stabilized = normalize_html_tables(stabilized)
3696
  stabilized = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(stabilized)
3697
  stabilized = _split_ucb_deposits_electronic_sections_html(stabilized)
3698
+ stabilized = _patch_da3_msft_g_ref_amounts_across_tables(stabilized)
3699
  stabilized = _dedupe_subset_transaction_tables_html(stabilized)
3700
  return close_unclosed_html(stabilized)
3701
 
 
3825
  stabilized = normalize_html_tables(stabilized)
3826
  stabilized = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(stabilized)
3827
  stabilized = _split_ucb_deposits_electronic_sections_html(stabilized)
3828
+ stabilized = _patch_da3_msft_g_ref_amounts_across_tables(stabilized)
3829
  stabilized = _dedupe_subset_transaction_tables_html(stabilized)
3830
  elif is_pdf and is_nfcu_doc:
3831
  # Navy-only additive fix: rebuild malformed summary table from
 
3909
  if all_pages and merged and not merged.startswith("Error") and "<table" in merged.lower():
3910
  merged = _trim_adjacent_da3_suffix_duplicating_next_table_prefix(merged)
3911
  merged = _split_ucb_deposits_electronic_sections_html(merged)
3912
+ merged = _patch_da3_msft_g_ref_amounts_across_tables(merged)
3913
  merged = _dedupe_subset_transaction_tables_html(merged)
3914
  return merged
3915