from decimal import Decimal from email.message import EmailMessage from model import Transaction import re from uuid import UUID from datetime import datetime from logging import debug from abc import ABC, abstractmethod class TransactionParser(ABC): @abstractmethod def match(self, msg: EmailMessage) -> bool: pass @abstractmethod def extract(self, msg: EmailMessage) -> Transaction: pass class TransactionParsingFailed(Exception): pass class RogersBankParser(TransactionParser): EXTRACT_RE = re.compile( r"Attempt of \$(\d+\.\d{2}) was made on ([A-z]{3} \d{1,2}, \d{4})[^<]*at ([^<]+) in" ) def __init__(self, account_id: UUID): self.account_id = account_id def match(self, msg: EmailMessage) -> bool: return ( msg["From"] == "Rogers Bank " and msg["Subject"] == "Purchase amount alert" ) def extract(self, msg: EmailMessage) -> Transaction: matches = RogersBankParser.EXTRACT_RE.search(msg.get_body().as_string()) if matches is None: return TransactionParsingFailed("No matches for extraction RE") amount = Decimal(matches[1]) date_raw = matches[2] payee = matches[3] date = datetime.strptime(date_raw, "%b %d, %Y").date() return Transaction( account=self.account_id, date=date, amount=amount, payee=payee, notes="via email", imported_id=msg["Message-ID"], )