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.

Reply via email to