Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1192,6 +1192,83 @@ def synthesize_running_balance_columns(md: str) -> str:
|
|
| 1192 |
return "".join(pieces)
|
| 1193 |
|
| 1194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1195 |
def render_pdf_pages_to_images(pdf_path: str) -> Tuple[List[str], List[int]]:
|
| 1196 |
import pymupdf as fitz
|
| 1197 |
from PIL import Image
|
|
@@ -1331,6 +1408,7 @@ def run_ocr(uploaded_file):
|
|
| 1331 |
merged = stabilize_table_markup(merged, rounds=4)
|
| 1332 |
merged = enrich_transaction_tables_with_daily_balances(merged)
|
| 1333 |
merged = synthesize_running_balance_columns(merged)
|
|
|
|
| 1334 |
merged = stabilize_table_markup(merged, rounds=2)
|
| 1335 |
merged = repair_thead_cell_semantics(merged)
|
| 1336 |
merged = strip_degenerate_html_tables(merged)
|
|
|
|
| 1192 |
return "".join(pieces)
|
| 1193 |
|
| 1194 |
|
| 1195 |
+
def _extract_txn_date_balance_pairs(md: str):
|
| 1196 |
+
pairs = []
|
| 1197 |
+
for tm in re.finditer(r"<table\b[^>]*>.*?</table>", md, flags=re.IGNORECASE | re.DOTALL):
|
| 1198 |
+
t = tm.group(0)
|
| 1199 |
+
rows = _extract_rows_plain_from_table(t)
|
| 1200 |
+
if len(rows) < 2:
|
| 1201 |
+
continue
|
| 1202 |
+
hdr = [c.strip().lower() for c in rows[0]]
|
| 1203 |
+
if not hdr:
|
| 1204 |
+
continue
|
| 1205 |
+
d_i = next((i for i, h in enumerate(hdr) if "date" in h), -1)
|
| 1206 |
+
b_i = next((i for i, h in enumerate(hdr) if "balance" in h), -1)
|
| 1207 |
+
a_i = next((i for i, h in enumerate(hdr) if "amount" in h or "debit" in h or "credit" in h), -1)
|
| 1208 |
+
if d_i < 0 or b_i < 0 or a_i < 0:
|
| 1209 |
+
continue
|
| 1210 |
+
for r in rows[1:]:
|
| 1211 |
+
if len(r) <= max(d_i, b_i):
|
| 1212 |
+
continue
|
| 1213 |
+
dm = re.search(r"\d{1,2}/\d{1,2}(?:/\d{2,4})?", (r[d_i] or ""))
|
| 1214 |
+
if not dm:
|
| 1215 |
+
continue
|
| 1216 |
+
bal = _parse_amount_or_none(r[b_i])
|
| 1217 |
+
if bal is None:
|
| 1218 |
+
continue
|
| 1219 |
+
pairs.append((dm.group(0), bal))
|
| 1220 |
+
return pairs
|
| 1221 |
+
|
| 1222 |
+
|
| 1223 |
+
def augment_with_derived_balance_views(md: str) -> str:
|
| 1224 |
+
"""
|
| 1225 |
+
Add a compact derived daily balance table and inferred beginning/ending summary
|
| 1226 |
+
when the document lacks a robust explicit daily balance section.
|
| 1227 |
+
"""
|
| 1228 |
+
if not md:
|
| 1229 |
+
return md
|
| 1230 |
+
# If there is already a clear daily-balance section, keep output unchanged.
|
| 1231 |
+
if re.search(r"daily\s+(?:ledger\s+)?balances", md, flags=re.IGNORECASE):
|
| 1232 |
+
return md
|
| 1233 |
+
|
| 1234 |
+
pairs = _extract_txn_date_balance_pairs(md)
|
| 1235 |
+
if len(pairs) < 12:
|
| 1236 |
+
return md
|
| 1237 |
+
|
| 1238 |
+
by_day = {}
|
| 1239 |
+
for d, b in pairs:
|
| 1240 |
+
k = "/".join(d.split("/")[:2])
|
| 1241 |
+
by_day[k] = b
|
| 1242 |
+
if len(by_day) < 8:
|
| 1243 |
+
return md
|
| 1244 |
+
|
| 1245 |
+
days = sorted(
|
| 1246 |
+
by_day.keys(),
|
| 1247 |
+
key=lambda x: (int(x.split("/")[0]), int(x.split("/")[1])),
|
| 1248 |
+
)
|
| 1249 |
+
begin = by_day[days[0]]
|
| 1250 |
+
end = by_day[days[-1]]
|
| 1251 |
+
|
| 1252 |
+
has_begin = bool(re.search(r"(?:beginning|starting)\s+balance", md, flags=re.IGNORECASE))
|
| 1253 |
+
has_end = bool(re.search(r"ending\s+balance", md, flags=re.IGNORECASE))
|
| 1254 |
+
|
| 1255 |
+
rows = ["<tr><th>Date</th><th>Balance</th></tr>"]
|
| 1256 |
+
for d in days:
|
| 1257 |
+
rows.append(f"<tr><td>{html.escape(d)}</td><td>{by_day[d]:,.2f}</td></tr>")
|
| 1258 |
+
daily_tbl = "<table>\n" + "\n".join(rows) + "\n</table>"
|
| 1259 |
+
|
| 1260 |
+
extra = ["", "<div align=\"center\">Derived daily balances</div>", daily_tbl]
|
| 1261 |
+
if not has_begin or not has_end:
|
| 1262 |
+
extra.insert(
|
| 1263 |
+
0,
|
| 1264 |
+
(
|
| 1265 |
+
f"Inferred beginning balance: {begin:,.2f}\n"
|
| 1266 |
+
f"Inferred ending balance: {end:,.2f}"
|
| 1267 |
+
),
|
| 1268 |
+
)
|
| 1269 |
+
return md.rstrip() + "\n\n" + "\n\n".join(extra) + "\n"
|
| 1270 |
+
|
| 1271 |
+
|
| 1272 |
def render_pdf_pages_to_images(pdf_path: str) -> Tuple[List[str], List[int]]:
|
| 1273 |
import pymupdf as fitz
|
| 1274 |
from PIL import Image
|
|
|
|
| 1408 |
merged = stabilize_table_markup(merged, rounds=4)
|
| 1409 |
merged = enrich_transaction_tables_with_daily_balances(merged)
|
| 1410 |
merged = synthesize_running_balance_columns(merged)
|
| 1411 |
+
merged = augment_with_derived_balance_views(merged)
|
| 1412 |
merged = stabilize_table_markup(merged, rounds=2)
|
| 1413 |
merged = repair_thead_cell_semantics(merged)
|
| 1414 |
merged = strip_degenerate_html_tables(merged)
|