# HG changeset patch # User Louis Opter # Date 1489128902 28800 # Node ID 28548158a32581dc6395b257a2e568380bafb420 # Parent 164da24a29977f711cd68951b6444440e51a8a52 Some minor improvements/fixes This will eventually evolve to something more generic, since I have re-used the same design, and some of the parts here, to write a plugin for Charles Schwab. diff -r 164da24a2997 -r 28548158a325 .hgignore --- a/.hgignore Thu Dec 01 17:14:11 2016 -0800 +++ b/.hgignore Thu Mar 09 22:55:02 2017 -0800 @@ -2,3 +2,4 @@ .*\.py[co]$ .*\.egg-info$ .*\.ofx$ +Session.vim$ diff -r 164da24a2997 -r 28548158a325 README.rst --- a/README.rst Thu Dec 01 17:14:11 2016 -0800 +++ b/README.rst Thu Mar 09 22:55:02 2017 -0800 @@ -3,7 +3,8 @@ This only supports the ``ExportData.csv`` file you can download from the "money management" tool found within their online banking interface (`accessible -here`_). +here`_). Note that access to this tool requires an advanced or premier account +with HSBC. ofxstatement can only process one account at a time, so make sure you export each account separately into different files. diff -r 164da24a2997 -r 28548158a325 setup.py --- a/setup.py Thu Dec 01 17:14:11 2016 -0800 +++ b/setup.py Thu Mar 09 22:55:02 2017 -0800 @@ -27,7 +27,7 @@ version=version, author="Louis Opter", author_email="louis@opter.org", - url="https://github.com/lopter/ofxstatement", + url="https://github.com/lopter/ofxstatement-us-hsbc", description=("HSBC (USA) plugin for ofxstatement"), long_description=long_description, license="GPLv3", @@ -57,6 +57,7 @@ "dev": [ "flake8", "mypy-lang", + "typed-ast", "ipython", "pdbpp", "pep8", diff -r 164da24a2997 -r 28548158a325 src/ofxstatement/plugins/us_hsbc/plugin.py --- a/src/ofxstatement/plugins/us_hsbc/plugin.py Thu Dec 01 17:14:11 2016 -0800 +++ b/src/ofxstatement/plugins/us_hsbc/plugin.py Thu Mar 09 22:55:02 2017 -0800 @@ -18,7 +18,6 @@ import contextlib import csv import datetime -import enum import locale import logging import pdb @@ -28,7 +27,7 @@ from ofxstatement.parser import CsvStatementParser from ofxstatement.plugin import Plugin from ofxstatement.statement import StatementLine, generate_transaction_id -from typing import Any, Dict, Iterable, List +from typing import Dict, Generator, Iterable, List from typing.io import TextIO from .record import CsvIndexes, Record @@ -39,13 +38,31 @@ @contextlib.contextmanager -def _override_locale(category, value): +def _override_locale(category: int, value: str) -> Generator[None, None, None]: save = locale.getlocale(category) locale.setlocale(category, value) yield locale.setlocale(category, save) +class _spawn_debugger_on_exception: + + def __init__(self, errmsg: str) -> None: + self._errmsg = errmsg + + def __enter__(self) -> None: + pass + + def __exit__(self, exc_type, exc_value, exc_tb) -> bool: + if exc_value is not None: + logger.exception(self._errmsg) + logger.info("Press {} to exit the debugger".format( + "^Z" if sys.platform.startswith("win32") else "^D" + )) + pdb.post_mortem() + sys.exit(1) + + class Parser(CsvStatementParser): date_format = "%m/%d/%Y" @@ -65,6 +82,7 @@ "CASH CONCENTRATION VENMO": "XFER", "DEPOSIT FROM": "DIRECTDEP", "DEPOSIT": "DEP", + "HSBC SECURITIES": "XFER", "INTEREST EARNED AND PAID": "INT", "ONLINE PAYMENT TO": "PAYMENT", "PAY TO": "PAYMENT", @@ -106,7 +124,7 @@ logger.info("Skipping row: {}".format(row)) return None # skip (all) the csv headers - try: + with _spawn_debugger_on_exception("Parsing failed:"): sl = super(Parser, self).parse_record(row) record = Record(row) @@ -123,13 +141,6 @@ self.statement.start_date = min(sl.date, self.statement.start_date) self.statement.end_date = max(sl.date, self.statement.end_date) - except Exception: - logger.exception("Parsing failed:") - logger.info("Press {} to exit the debugger".format( - "^Z" if sys.platform.startswith("win32") else "^D" - )) - pdb.post_mortem() - sys.exit(1) return sl @@ -148,9 +159,9 @@ # XXX: how does this gets closed? fin = open(filename, "r", encoding=encoding) - parser = Parser(fin) - parser.statement.bank_id = self.settings.get("routing_number") - parser.statement.account_id = self.settings.get("account_number") - parser.statement.account_type = self.settings.get("account_type", "CHECKING") + p = Parser(fin) + p.statement.bank_id = self.settings.get("routing_number") + p.statement.account_id = self.settings.get("account_number") + p.statement.account_type = self.settings.get("account_type", "CHECKING") - return parser + return p