rehan953 commited on
Commit
c9ed81b
·
verified ·
1 Parent(s): 7ab2c22

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -11
app.py CHANGED
@@ -28,6 +28,10 @@ Primary goals for reconcile rate:
28
  - Fix 16: remove duplicate rows within a single Date|Description|Amount table; multi-pass table dedupe + final doc-wide dedupe
29
  - Fix 17: UCB page 3 — split one merged table under Deposits (continued) into Deposits + Electronic Credits +
30
  Electronic Debits (PDF text order); drop orphan empty EC/ED headers that followed the merged table
 
 
 
 
31
  - Post-pass: dedupe 3-col Date|Description|Amount tables when one is a duplicate fragment
32
  (later subset of earlier, earlier subset of later, or identical row sets)
33
  - Fix 18: adjacent DA3 tables — if the last K data rows of table N match the first K rows of table N+1
@@ -2898,6 +2902,108 @@ def _ucb_split_grid_deposits_ec_ed(grid):
2898
  return g_dep, g_ec, g_ed
2899
 
2900
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2901
  def _try_split_ucb_deposits_table_to_three(prefix: str, table_html: str):
2902
  try:
2903
  p = TableGridParser()
@@ -2926,18 +3032,20 @@ _UCB_DEP_CONT_BLOCK = re.compile(
2926
 
2927
 
2928
  def _split_ucb_deposits_electronic_sections_html(text: str) -> str:
2929
- if not text or "deposits (continued)" not in text.lower():
2930
  return text
2931
-
2932
- def _repl(m):
2933
- prefix = m.group(1)
2934
- table_html = m.group(2)
2935
- new = _try_split_ucb_deposits_table_to_three(prefix, table_html)
2936
- if new is None:
2937
- return m.group(0)
2938
- return new
2939
-
2940
- return _UCB_DEP_CONT_BLOCK.sub(_repl, text)
 
 
2941
 
2942
 
2943
  def _parse_da3_grid_from_table_html(table_html: str):
 
28
  - Fix 16: remove duplicate rows within a single Date|Description|Amount table; multi-pass table dedupe + final doc-wide dedupe
29
  - Fix 17: UCB page 3 — split one merged table under Deposits (continued) into Deposits + Electronic Credits +
30
  Electronic Debits (PDF text order); drop orphan empty EC/ED headers that followed the merged table
31
+ - Fix 17b: When markdown has Electronic Credits (+ optional Electronic Debits headings) but a single fused
32
+ Date|Description|Amount table (common when Deposits (continued) is absent from OCR), split after a
33
+ leading ITM DEPOSIT run when the next row matches generic electronic-debit patterns (POS purchase,
34
+ ATM withdrawal, etc.). Safe no-op when there is no ITM prefix or no such debit row after it.
35
  - Post-pass: dedupe 3-col Date|Description|Amount tables when one is a duplicate fragment
36
  (later subset of earlier, earlier subset of later, or identical row sets)
37
  - Fix 18: adjacent DA3 tables — if the last K data rows of table N match the first K rows of table N+1
 
2902
  return g_dep, g_ec, g_ed
2903
 
2904
 
2905
+ def _is_strong_ucb_electronic_debit_row(grid, r: int) -> bool:
2906
+ """True for typical card/ACH debit lines (generic; avoids splitting on POS return / transfer-in credits)."""
2907
+ t = _row_text_full_ucb(grid, r)
2908
+ if not t or not t.strip():
2909
+ return False
2910
+ if "pos return" in t:
2911
+ return False
2912
+ if "dda transfer" in t and "in" in t:
2913
+ return False
2914
+ if "pos purchase" in t:
2915
+ return True
2916
+ if "atm withdrawal" in t:
2917
+ return True
2918
+ if "atm" in t and "withdrawal" in t:
2919
+ return True
2920
+ if "debit card" in t:
2921
+ return True
2922
+ if "point of sale" in t:
2923
+ return True
2924
+ if "recur payment" in t:
2925
+ return True
2926
+ return False
2927
+
2928
+
2929
+ def _ucb_split_grid_electronic_credits_debits_after_itm_prefix(grid):
2930
+ """
2931
+ Fix 17b: one fused DA3 table under Electronic Credits (+ optional empty EC/ED headings) where OCR
2932
+ merged ITM deposits with POS/ATM debits. Split after the leading ITM DEPOSIT run when the next
2933
+ data row is a strong electronic-debit pattern.
2934
+ """
2935
+ if not grid or len(grid) < 3:
2936
+ return None
2937
+ if not _is_da3_header(grid):
2938
+ return None
2939
+ data_start = 1
2940
+ i = data_start
2941
+ while i < len(grid) and _is_itm_deposit_row_ucb(grid, i):
2942
+ i += 1
2943
+ if i == data_start or i >= len(grid):
2944
+ return None
2945
+ if not _is_strong_ucb_electronic_debit_row(grid, i):
2946
+ return None
2947
+ hdr = [list(grid[0])]
2948
+ g_ec = hdr + grid[data_start:i]
2949
+ g_ed = hdr + grid[i:]
2950
+ if len(g_ec) <= 1 or len(g_ed) <= 1:
2951
+ return None
2952
+ return g_ec, g_ed
2953
+
2954
+
2955
+ def _try_split_ucb_ec_ed_from_fused_table_html(lead_before_ec: str, table_html: str):
2956
+ try:
2957
+ p = TableGridParser()
2958
+ p.feed(table_html)
2959
+ grid = _build_grid(p.rows)
2960
+ except Exception:
2961
+ return None
2962
+ pair = _ucb_split_grid_electronic_credits_debits_after_itm_prefix(grid)
2963
+ if not pair:
2964
+ return None
2965
+ g_ec, g_ed = pair
2966
+ sect = '<div align="center">\n\n{title}\n\n</div>\n\n'
2967
+ parts = []
2968
+ if lead_before_ec and lead_before_ec.strip():
2969
+ parts.append(lead_before_ec.rstrip())
2970
+ parts.extend(
2971
+ [
2972
+ sect.format(title="Electronic Credits"),
2973
+ _grid_to_html(g_ec),
2974
+ sect.format(title="Electronic Debits"),
2975
+ _grid_to_html(g_ed),
2976
+ ]
2977
+ )
2978
+ return "\n\n".join(parts)
2979
+
2980
+
2981
+ # Optional empty centered banner only; EC/ED title divs are re-emitted after a successful split
2982
+ # so we do not duplicate headings.
2983
+ _UCB_EC_ED_SINGLE_FUSED_TABLE = re.compile(
2984
+ r'(?P<lead>(?:<div\s+align=["\']center["\']\s*>\s*</div>\s*)?)'
2985
+ r'<div\s+align=["\']center["\']\s*>\s*Electronic\s+Credits\s*</div>\s*'
2986
+ r'(?:<div\s+align=["\']center["\']\s*>\s*Electronic\s+Debits\s*</div>\s*)?'
2987
+ r'(?P<table><table[^>]*>.*?</table>)',
2988
+ re.DOTALL | re.IGNORECASE,
2989
+ )
2990
+
2991
+
2992
+ def _split_ucb_fused_ec_ed_headings_single_table_html(text: str) -> str:
2993
+ if not text or "electronic credits" not in text.lower():
2994
+ return text
2995
+ if "<table" not in text.lower():
2996
+ return text
2997
+
2998
+ def _repl(m):
2999
+ lead = m.group("lead")
3000
+ table_html = m.group("table")
3001
+ new = _try_split_ucb_ec_ed_from_fused_table_html(lead, table_html)
3002
+ return new if new is not None else m.group(0)
3003
+
3004
+ return _UCB_EC_ED_SINGLE_FUSED_TABLE.sub(_repl, text)
3005
+
3006
+
3007
  def _try_split_ucb_deposits_table_to_three(prefix: str, table_html: str):
3008
  try:
3009
  p = TableGridParser()
 
3032
 
3033
 
3034
  def _split_ucb_deposits_electronic_sections_html(text: str) -> str:
3035
+ if not text:
3036
  return text
3037
+ if "deposits (continued)" in text.lower():
3038
+
3039
+ def _repl(m):
3040
+ prefix = m.group(1)
3041
+ table_html = m.group(2)
3042
+ new = _try_split_ucb_deposits_table_to_three(prefix, table_html)
3043
+ if new is None:
3044
+ return m.group(0)
3045
+ return new
3046
+
3047
+ text = _UCB_DEP_CONT_BLOCK.sub(_repl, text)
3048
+ return _split_ucb_fused_ec_ed_headings_single_table_html(text)
3049
 
3050
 
3051
  def _parse_da3_grid_from_table_html(table_html: str):