I am working on csv importers by subclassing the provided csv class and wrapping __init__ to setup config, etc. It worked well, but some of my csv files have a different numbering format. The ones I run into are: debit shown as negative number, and some gives me 0 for debit when it's a credit so that I have an additional line of 0 amount.
These are all easy fixes, through monkey patching the get_amounts (with wrapping old) function in the csv module. The problem is that when I have to patch it twice for the above 2 situations, the patches spill over and change the outcomes depending on how I import my custom importers in xxx.import file.
I can pass the test for each individual importer, but not when I run all tests.
I can surely just copy and change the original csv module to move get_amounts as a class method. But I just want to check if I am doing it right.
Thanks for any suggestion.
W.E.
def get_amounts(iconfig, row): """Get the amount columns of a row.
This is based on the original function in the built-in CSV importer module. In the original function, debit amounts are negated before returning.
If you export transactions from the transaction listing screen, debit values are positive. But if you export transactions from the transaction search screen, the very same debit values are negative. How bizarre!
This is handled by negating the absolute value of debit values.
Credit values are positive regardless of where the CSV export came from.
Sometimes transactions are pending (authorisation only). In this case, the "Transaction Type" column is empty. We use this to ignore these transactions: None is returned for both debit and credit.
The `allow_zero_amounts` argument and corresponding logic has been removed from the original function.
Args: iconfig: A dict of Col to row index. row: A row array containing the values of the given row. Returns: A pair of (debit-amount, credit-amount), both of which are either an instance of Decimal or None, or not available. """ # If transaction type is not populated, the transaction is pending. Return # None instead of actual values. if not row[iconfig[csv.Col.DRCR]]: return (None, None) debit, credit = None, None if csv.Col.AMOUNT in iconfig: credit = row[iconfig[csv.Col.AMOUNT]] else: debit, credit = [row[iconfig[col]] if col in iconfig else None for col in [csv.Col.AMOUNT_DEBIT, csv.Col.AMOUNT_CREDIT]]
# Take the absolute debit value, then negate it. return (-abs(csv.D(debit)) if debit else None, csv.D(credit) if credit else None)
# Monkey patch function in csv module.csv.get_amounts = get_amounts
def normalise(func): @wraps(func) def wrapper(*args, **kwargs): """Makes all debits negative, and all credits positive.""" debit, credit = func(*args, **kwargs) return ( -abs(debit) if debit else debit, abs(credit) if credit else credit ) return wrapper
# Monkey patch function in csv module.csv.get_amounts = normalise(csv.get_amounts)
Note that the cvs importer in the Mercurial repo has an invert_sign
option now, which might do what you need.
invert_sign: Optional[bool] = False,
invert_sign: If true, invert the amount's sign unconditionally.
* brodie....@gmail.com <brodie....@gmail.com> [2020-05-17 01:27]:
> To unsubscribe from this group and stop receiving emails from it, send an email to bean...@googlegroups.com.
| from beancount.ingest.importers import csv |
| class Importer(csv.Importer): |
| csv.get_amounts = normalise(ignore_pending(csv.get_amounts)) |
pytest -v importers/bank1
pytest -v importers/bank2pytest -v importers