rehan953 commited on
Commit
89e953b
·
verified ·
1 Parent(s): 2946d98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -11
app.py CHANGED
@@ -48,6 +48,10 @@ Primary goals for reconcile rate:
48
  (≤6 rows), append those rows to the preceding debits table and drop the extra ED heading.
49
  Skip the *first* Electronic Debits + table on the page when the previous table is ITM/credits-only
50
  and the fragment is clearly debits (so we do not treat the main debits section as a duplicate).
 
 
 
 
51
  - Fix 17e: Repeat EC/ED fused + 17d passes until stable (handles chained replacements). Centered section titles
52
  match both align=\"center\" and style=\"text-align:center\" on divs.
53
  - Post-pass: dedupe 3-col Date|Description|Amount tables when one is a duplicate fragment
@@ -2944,6 +2948,17 @@ def _is_strong_ucb_electronic_debit_row(grid, r: int) -> bool:
2944
  return False
2945
 
2946
 
 
 
 
 
 
 
 
 
 
 
 
2947
  def _ucb_has_itm_deposit_data_row(grid) -> bool:
2948
  for r in range(1, len(grid)):
2949
  if _is_itm_deposit_row_ucb(grid, r):
@@ -3088,6 +3103,111 @@ _UCB_EC_ED_SINGLE_FUSED_TABLE = re.compile(
3088
  re.DOTALL | re.IGNORECASE,
3089
  )
3090
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3091
 
3092
  def _ucb_strip_redundant_ed_subset_fragment(text: str) -> str:
3093
  """
@@ -3247,14 +3367,6 @@ def _ucb_prune_ed_fragment_dupes_vs_prev_table(text: str) -> str:
3247
  return None
3248
  return k
3249
 
3250
- def _grid_has_strong_debit_row(grid):
3251
- for r in range(1, len(grid)):
3252
- if not any(str(c or "").strip() for c in grid[r]):
3253
- continue
3254
- if _is_strong_ucb_electronic_debit_row(grid, r):
3255
- return True
3256
- return False
3257
-
3258
  for _ in range(12):
3259
  changed = False
3260
  for m in ed_block.finditer(text):
@@ -3274,7 +3386,7 @@ def _ucb_prune_ed_fragment_dupes_vs_prev_table(text: str) -> str:
3274
  ):
3275
  continue
3276
  # Legitimate layout: ITM/credits-only table, then Electronic Debits + main debits — do not prune.
3277
- if not _grid_has_strong_debit_row(g_big) and _grid_has_strong_debit_row(g_frag):
3278
  continue
3279
  cb = Counter()
3280
  for r in range(1, len(g_big)):
@@ -3389,9 +3501,10 @@ def _split_ucb_deposits_electronic_sections_html(text: str) -> str:
3389
  return new
3390
 
3391
  text = _UCB_DEP_CONT_BLOCK.sub(_repl, text)
3392
- # Fix 17e: repeat fused EC/ED fix + duplicate ED strip + 17g prune until stable.
3393
  for _ in range(16):
3394
- nxt = _split_ucb_fused_ec_ed_headings_single_table_html(text)
 
3395
  nxt = _ucb_strip_redundant_ed_subset_fragment(nxt)
3396
  nxt = _ucb_prune_ed_fragment_dupes_vs_prev_table(nxt)
3397
  if nxt == text:
 
48
  (≤6 rows), append those rows to the preceding debits table and drop the extra ED heading.
49
  Skip the *first* Electronic Debits + table on the page when the previous table is ITM/credits-only
50
  and the fragment is clearly debits (so we do not treat the main debits section as a duplicate).
51
+ - Fix 17h: UCB sometimes OCRs Electronic Credits (+ optional Electronic Debits) then the *main debits* DA3
52
+ table first and a *second* DA3 table whose rows start with ITM DEPOSIT (credits) plus duplicate
53
+ debits. Reorder to Credits + ITM table + Electronic Debits + merged debits; drop dup keys from the
54
+ second table against the first; merge remaining orphan rows into the debits table.
55
  - Fix 17e: Repeat EC/ED fused + 17d passes until stable (handles chained replacements). Centered section titles
56
  match both align=\"center\" and style=\"text-align:center\" on divs.
57
  - Post-pass: dedupe 3-col Date|Description|Amount tables when one is a duplicate fragment
 
2948
  return False
2949
 
2950
 
2951
+ def _ucb_grid_has_any_strong_debit_row(grid) -> bool:
2952
+ if not grid:
2953
+ return False
2954
+ for r in range(1, len(grid)):
2955
+ if not any(str(c or "").strip() for c in grid[r]):
2956
+ continue
2957
+ if _is_strong_ucb_electronic_debit_row(grid, r):
2958
+ return True
2959
+ return False
2960
+
2961
+
2962
  def _ucb_has_itm_deposit_data_row(grid) -> bool:
2963
  for r in range(1, len(grid)):
2964
  if _is_itm_deposit_row_ucb(grid, r):
 
3103
  re.DOTALL | re.IGNORECASE,
3104
  )
3105
 
3106
+ # Fix 17h: debits DA3 first, then DA3 with leading ITM + duplicate tail (no ED between tables).
3107
+ _UCB_EC_TWO_DA3_DEBITS_BEFORE_ITM_TAIL = re.compile(
3108
+ r'(?P<lead>(?:' + _UCB_CENTER_DIV + r'\s*</div>\s*)?)'
3109
+ r'(?P<ecdiv>' + _UCB_CENTER_DIV + r'\s*Electronic\s+Credits\s*</div>\s*)'
3110
+ r'(?P<ed_maybe>(?:' + _UCB_CENTER_DIV + r'\s*Electronic\s+Debits\s*</div>\s*)?)'
3111
+ r'(?P<t1><table[^>]*>.*?</table>)'
3112
+ r'\s*'
3113
+ r'(?P<t2><table[^>]*>.*?</table>)',
3114
+ re.DOTALL | re.IGNORECASE,
3115
+ )
3116
+
3117
+
3118
+ def _ucb_reorder_ec_ed_debits_first_itm_tail_html(text: str) -> str:
3119
+ """
3120
+ Fix 17h: Electronic Credits (+ optional Electronic Debits) then two DA3 tables where the first is
3121
+ clearly debits and the second begins with a run of ITM DEPOSIT rows (mis-ordered vs credits section).
3122
+ """
3123
+ if not text or "electronic credits" not in text.lower():
3124
+ return text
3125
+ low = text.lower()
3126
+ if "itm deposit" not in low and "itm dep" not in low:
3127
+ return text
3128
+ pat = _UCB_EC_TWO_DA3_DEBITS_BEFORE_ITM_TAIL
3129
+ _dn = re.compile(r"^\d{1,2}[/\-]\d{2}")
3130
+ _amt_ok = re.compile(r"^-?\d+(?:\.\d{1,4})?-?$")
3131
+
3132
+ def _key_cells(cells):
3133
+ parts = [str(c or "").strip() for c in cells[:3]]
3134
+ while len(parts) < 3:
3135
+ parts.append("")
3136
+ date_s = re.sub(r"\s+", "", parts[0])
3137
+ amt_s = re.sub(r"[\s$,]", "", parts[2]).lower()
3138
+ amt_s = amt_s.replace("\u2212", "-").replace("\u2013", "-")
3139
+ return date_s, amt_s
3140
+
3141
+ def _row_loose_key(grid, r):
3142
+ cells = [str(c or "").strip() for c in grid[r]]
3143
+ if not any(cells):
3144
+ return None
3145
+ k = _key_cells(cells)
3146
+ if not (_dn.match(k[0]) and _amt_ok.match(k[1]) and "." in k[1]):
3147
+ return None
3148
+ return k
3149
+
3150
+ m = pat.search(text)
3151
+ if not m:
3152
+ return text
3153
+ g1 = _parse_da3_grid_from_table_html(m.group("t1"))
3154
+ g2 = _parse_da3_grid_from_table_html(m.group("t2"))
3155
+ if not g1 or not g2 or not _is_da3_header(g1) or not _is_da3_header(g2):
3156
+ return text
3157
+ if not _ucb_grid_has_any_strong_debit_row(g1):
3158
+ return text
3159
+ itm_end = 1
3160
+ while itm_end < len(g2):
3161
+ if not any(str(c or "").strip() for c in g2[itm_end]):
3162
+ itm_end += 1
3163
+ continue
3164
+ if not _is_itm_deposit_row_ucb(g2, itm_end):
3165
+ break
3166
+ itm_end += 1
3167
+ if itm_end <= 1:
3168
+ return text
3169
+ cb = Counter()
3170
+ for r in range(1, len(g1)):
3171
+ kk = _row_loose_key(g1, r)
3172
+ if kk:
3173
+ cb[kk] += 1
3174
+ new_tail = []
3175
+ for r in range(itm_end, len(g2)):
3176
+ if not any(str(c or "").strip() for c in g2[r]):
3177
+ continue
3178
+ if _is_itm_deposit_row_ucb(g2, r):
3179
+ continue
3180
+ kk = _row_loose_key(g2, r)
3181
+ if kk and cb.get(kk, 0) > 0:
3182
+ cb[kk] -= 1
3183
+ continue
3184
+ new_tail.append(list(g2[r]))
3185
+ g_itm = [list(g2[0])] + [list(g2[r]) for r in range(1, itm_end)]
3186
+ merged = [list(g1[0])] + [list(r) for r in g1[1:]] + new_tail
3187
+ max_c = max(len(r) for r in merged)
3188
+ for row in merged:
3189
+ while len(row) < max_c:
3190
+ row.append("")
3191
+ max_ci = max(len(r) for r in g_itm)
3192
+ for row in g_itm:
3193
+ while len(row) < max_ci:
3194
+ row.append("")
3195
+ sect = '<div align="center">\n\n{title}\n\n</div>\n\n'
3196
+ parts = []
3197
+ lead = m.group("lead") or ""
3198
+ if lead.strip():
3199
+ parts.append(lead.rstrip())
3200
+ parts.extend(
3201
+ [
3202
+ sect.format(title="Electronic Credits"),
3203
+ _grid_to_html(g_itm),
3204
+ sect.format(title="Electronic Debits"),
3205
+ _grid_to_html(merged),
3206
+ ]
3207
+ )
3208
+ new_blob = "\n\n".join(parts)
3209
+ return text[: m.start()] + new_blob + text[m.end() :]
3210
+
3211
 
3212
  def _ucb_strip_redundant_ed_subset_fragment(text: str) -> str:
3213
  """
 
3367
  return None
3368
  return k
3369
 
 
 
 
 
 
 
 
 
3370
  for _ in range(12):
3371
  changed = False
3372
  for m in ed_block.finditer(text):
 
3386
  ):
3387
  continue
3388
  # Legitimate layout: ITM/credits-only table, then Electronic Debits + main debits — do not prune.
3389
+ if not _ucb_grid_has_any_strong_debit_row(g_big) and _ucb_grid_has_any_strong_debit_row(g_frag):
3390
  continue
3391
  cb = Counter()
3392
  for r in range(1, len(g_big)):
 
3501
  return new
3502
 
3503
  text = _UCB_DEP_CONT_BLOCK.sub(_repl, text)
3504
+ # Fix 17e: repeat 17h reorder + fused EC/ED fix + duplicate ED strip + 17g prune until stable.
3505
  for _ in range(16):
3506
+ nxt = _ucb_reorder_ec_ed_debits_first_itm_tail_html(text)
3507
+ nxt = _split_ucb_fused_ec_ed_headings_single_table_html(nxt)
3508
  nxt = _ucb_strip_redundant_ed_subset_fragment(nxt)
3509
  nxt = _ucb_prune_ed_fragment_dupes_vs_prev_table(nxt)
3510
  if nxt == text: