from __future__ import annotations from .idfc import IDFCFirstParser from .axis import AxisParser from .hdfc import HDFCParser from .icici import ICICIParser from .hsbc import HSBCParser from .indian_bank import IndianBankModernParser, IndianBankLegacyParser from .indusind import IndusIndParser from .kotak import KotakParser from .sbi import SBIAccountSummaryParser, SBIModernParser, SBIOtherParser from .central_bank_of_india import CentralBankOfIndiaParser from .yes_bank import YesBankParser from .city_union_bank import CityUnionBankParser from .bank_of_baroda import BankOfBarodaParser from .rbl_bank import RBLBankParser from .base import extract_text PARSERS = [ IDFCFirstParser, AxisParser, CentralBankOfIndiaParser, YesBankParser, CityUnionBankParser, BankOfBarodaParser, RBLBankParser, HDFCParser, ICICIParser, HSBCParser, IndianBankModernParser, IndianBankLegacyParser, IndusIndParser, KotakParser, SBIAccountSummaryParser, SBIOtherParser, SBIModernParser, ] BANK_OPTIONS = [ ("auto", "Auto Detect"), ("axis", "Axis Bank"), ("central_bank_of_india", "Central Bank of India"), ("yes_bank", "YES Bank"), ("city_union_bank", "City Union Bank"), ("bank_of_baroda", "Bank of Baroda"), ("rbl_bank", "RBL Bank"), ("hdfc", "HDFC Bank"), ("icici", "ICICI Bank"), ("hsbc", "HSBC Bank"), ("idfc", "IDFC FIRST Bank"), ("indian_bank", "Indian Bank"), ("indusind", "IndusInd Bank"), ("kotak", "Kotak Mahindra Bank"), ("sbi", "State Bank of India"), ] BANK_PARSERS = { "axis": [AxisParser], "central_bank_of_india": [CentralBankOfIndiaParser], "yes_bank": [YesBankParser], "city_union_bank": [CityUnionBankParser], "bank_of_baroda": [BankOfBarodaParser], "rbl_bank": [RBLBankParser], "hdfc": [HDFCParser], "icici": [ICICIParser], "hsbc": [HSBCParser], "idfc": [IDFCFirstParser], "indian_bank": [IndianBankModernParser, IndianBankLegacyParser], "indusind": [IndusIndParser], "kotak": [KotakParser], "sbi": [SBIAccountSummaryParser, SBIOtherParser, SBIModernParser], } def detect_parser(text): scored = sorted(((parser.detect(text), parser) for parser in PARSERS), key=lambda item: item[0], reverse=True) if not scored or scored[0][0] <= 0: return None, 0 return scored[0][1](), scored[0][0] def _selected_parser(bank_key: str, text: str): candidates = BANK_PARSERS.get(bank_key, []) if not candidates: return None, 0 scored = sorted(((parser.detect(text), parser) for parser in candidates), key=lambda item: item[0], reverse=True) if scored and scored[0][0] > 0: return scored[0][1](), scored[0][0] return None, 0 def parse_pdf(path, bank_hint: str | None = None): hint = (bank_hint or "auto").strip().lower() if hint == "hsbc": return HSBCParser().parse(path, "") text = extract_text(path) if hint and hint != "auto": parser, score = _selected_parser(hint, text) if parser is None: label = dict(BANK_OPTIONS).get(hint, "the selected bank") raise ValueError( f"The uploaded statement does not match the selected {label} format. " "Please verify the selected bank or choose Auto Detect." ) return parser.parse(path, text) parser, score = detect_parser(text) if parser is None and not (text or "").strip(): # Image-only statements cannot be identified by pdftotext. HSBCParser # performs its own OCR identification and raises a precise mismatch error. try: return HSBCParser().parse(path, text) except ValueError: pass if parser is None: raise ValueError( "Unsupported statement format. Select the bank manually or add a bank-specific parser for this statement layout." ) return parser.parse(path, text)