rehan953 commited on
Commit
e8fb17b
·
verified ·
1 Parent(s): 79c2a66

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +100 -0
app.py CHANGED
@@ -733,6 +733,102 @@ def strip_degenerate_html_tables(md: str) -> str:
733
  return re.sub(r"\n{3,}", "\n\n", out)
734
 
735
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
  def _strip_trailing_money_tokens(rest: str, max_take: int = 2) -> Tuple[str, List[str]]:
737
  """Strip 1–2 currency tokens from the right of *rest*; return (description, tokens oldest-first)."""
738
  cur = rest.rstrip()
@@ -904,6 +1000,8 @@ def light_stabilize_markdown(page_md: str) -> str:
904
  else:
905
  out_blocks.append(coalesce_loose_ledger_lines(b))
906
  merged = close_unclosed_html("\n\n".join(out_blocks))
 
 
907
  merged = repair_thead_cell_semantics(merged)
908
  merged = stabilize_table_markup(merged, rounds=4)
909
  merged = strip_degenerate_html_tables(merged)
@@ -1445,6 +1543,8 @@ def run_ocr(uploaded_file):
1445
 
1446
  merged = "\n\n---page-separator---\n\n".join(all_pages) if all_pages else "(No content)"
1447
  if merged and merged != "(No content)" and not merged.lstrip().startswith("Error:"):
 
 
1448
  merged = stabilize_table_markup(merged, rounds=4)
1449
  merged = enrich_transaction_tables_with_daily_balances(merged)
1450
  # Keep transaction amounts untouched; add explicit snapshot for metadata extraction.
 
733
  return re.sub(r"\n{3,}", "\n\n", out)
734
 
735
 
736
+ _LONG_NUM_TOKEN = re.compile(r"\b\d{7,}\b")
737
+
738
+
739
+ def mask_non_monetary_long_numbers(md: str) -> str:
740
+ """
741
+ Prevent long reference/trace/account IDs from being interpreted as monetary
742
+ values by downstream transaction extraction.
743
+ """
744
+ if not md:
745
+ return md
746
+
747
+ def repl(m: re.Match) -> str:
748
+ tok = m.group(0)
749
+ # Keep common money-like tokens (handled elsewhere with decimal cents).
750
+ if re.match(r"^\d+\.\d{2}$", tok):
751
+ return tok
752
+ # Keep YYYYMMDD-like date compact tokens.
753
+ if len(tok) == 8 and tok.startswith(("19", "20")):
754
+ return tok
755
+ return f"ID#{tok}"
756
+
757
+ return _LONG_NUM_TOKEN.sub(repl, md)
758
+
759
+
760
+ def directionalize_amount_headers(md: str) -> str:
761
+ """
762
+ Convert ambiguous 'Amount' headers into 'Credit' or 'Debit' when table
763
+ context clearly indicates transaction direction (e.g. Deposits, Checks).
764
+ """
765
+ if not md or "<table" not in md.lower():
766
+ return md
767
+
768
+ def map_header_token(label: str, ctx: str) -> str:
769
+ low = label.strip().lower()
770
+ if "amount" not in low:
771
+ return label
772
+ if re.search(r"\bcredit|deposit|deposits|incoming|received|interest earned|payment received\b", ctx):
773
+ return re.sub(r"amount", "Credit", label, flags=re.IGNORECASE)
774
+ if re.search(r"\bdebit|debits|withdrawal|withdrawals|check|checks|fee|fees|charge|charges|payment|payments\b", ctx):
775
+ return re.sub(r"amount", "Debit", label, flags=re.IGNORECASE)
776
+ return label
777
+
778
+ def repl_table(m: re.Match) -> str:
779
+ full = m.group(0)
780
+ # Table-local context from headers and first rows.
781
+ ctx = _cell_plain_text(full).lower()
782
+ changed = False
783
+
784
+ def repl_th(thm: re.Match) -> str:
785
+ nonlocal changed
786
+ tag = thm.group(0)
787
+ inner_m = re.search(r"<th\b[^>]*>(.*?)</th>", tag, flags=re.IGNORECASE | re.DOTALL)
788
+ if not inner_m:
789
+ return tag
790
+ inner = inner_m.group(1)
791
+ plain = _cell_plain_text(inner)
792
+ mapped = map_header_token(plain, ctx)
793
+ if mapped == plain:
794
+ return tag
795
+ changed = True
796
+ return re.sub(
797
+ r"(<th\b[^>]*>)(.*?)(</th>)",
798
+ rf"\1{html.escape(mapped)}\3",
799
+ tag,
800
+ count=1,
801
+ flags=re.IGNORECASE | re.DOTALL,
802
+ )
803
+
804
+ out = re.sub(r"<th\b[^>]*>.*?</th>", repl_th, full, flags=re.IGNORECASE | re.DOTALL)
805
+ if changed:
806
+ return out
807
+ # Fallback for OCR tables that put header row in <td>.
808
+ if "amount" in ctx and ("deposit" in ctx or "debit" in ctx or "withdraw" in ctx or "check" in ctx or "fee" in ctx):
809
+ out2 = re.sub(
810
+ r"(<td\b[^>]*>\s*)amount(\s*</td>)",
811
+ lambda mm: mm.group(1)
812
+ + (
813
+ "Credit"
814
+ if ("deposit" in ctx or "credit" in ctx or "incoming" in ctx or "received" in ctx)
815
+ else "Debit"
816
+ )
817
+ + mm.group(2),
818
+ full,
819
+ flags=re.IGNORECASE,
820
+ )
821
+ return out2
822
+ return full
823
+
824
+ return re.sub(
825
+ r"<table\b[^>]*>.*?</table>",
826
+ repl_table,
827
+ md,
828
+ flags=re.IGNORECASE | re.DOTALL,
829
+ )
830
+
831
+
832
  def _strip_trailing_money_tokens(rest: str, max_take: int = 2) -> Tuple[str, List[str]]:
833
  """Strip 1–2 currency tokens from the right of *rest*; return (description, tokens oldest-first)."""
834
  cur = rest.rstrip()
 
1000
  else:
1001
  out_blocks.append(coalesce_loose_ledger_lines(b))
1002
  merged = close_unclosed_html("\n\n".join(out_blocks))
1003
+ merged = mask_non_monetary_long_numbers(merged)
1004
+ merged = directionalize_amount_headers(merged)
1005
  merged = repair_thead_cell_semantics(merged)
1006
  merged = stabilize_table_markup(merged, rounds=4)
1007
  merged = strip_degenerate_html_tables(merged)
 
1543
 
1544
  merged = "\n\n---page-separator---\n\n".join(all_pages) if all_pages else "(No content)"
1545
  if merged and merged != "(No content)" and not merged.lstrip().startswith("Error:"):
1546
+ merged = mask_non_monetary_long_numbers(merged)
1547
+ merged = directionalize_amount_headers(merged)
1548
  merged = stabilize_table_markup(merged, rounds=4)
1549
  merged = enrich_transaction_tables_with_daily_balances(merged)
1550
  # Keep transaction amounts untouched; add explicit snapshot for metadata extraction.