Posting the previous made me think about this some more.
I think this is more concise and generalised:
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)
On Sunday, May 17, 2020 at 12:00:44 PM UTC+10, [email protected] wrote:
>
> I recently encountered a very similar situation. I have transaction
> statements where values in the debit column are negative, but credit values
> are positive-valued. I am also subclassing the built-in csv importer
> class.
>
> At first I thought about using a decorator to wrap the built-in
> `get_amounts()` function. But the values in rows passed to `get_amounts()`
> are strings. Stripping the `-` characters from these strings worked alright
> but didn't feel too clean (I guess transactions parsing is anything but
> clean!). Or maybe negating the debit values returned from the function.
>
> To make matters worse, I found that exporting CSV values from my bank's
> website sometimes had negative values, but other times they are positive --
> for the same transactions!
>
> In the end I just decided to reimplement `get_values()` instead of
> decorating it. That resulted in a more general solution that handles the
> shitty situation above. I just negate the absolute value of debit values. I
> then monkey patch the function in the CSV module.
>
> If you haven't got a good handle on how Python namespaces work, you may
> get tripped up by monkey patching. Spend a bit of time getting familiar
> with it. You'll change the behaviour of your other importers depending on
> the CSV module if you're not careful.
>
> 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
>
>
>
>
>
> On Sunday, May 10, 2020 at 5:11:59 AM UTC+10, [email protected] wrote:
>>
>> Hello,
>>
>> 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.
>>
>>
--
You received this message because you are subscribed to the Google Groups
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/beancount/2bf01ddf-c99c-4507-8fc2-034ed07deba2%40googlegroups.com.