beancount / smart_importer

Augment Beancount importers with machine learning functionality.
MIT License
248 stars 29 forks source link

Passing too many arguments to extract() #87

Closed jerome-tgl closed 5 years ago

jerome-tgl commented 5 years ago

I've adapted this import.config file to make my own as follows:

import os
import re
import codecs
import csv
import datetime
from beancount.core import number, data, amount
from beancount.ingest import importer

from smart_importer import apply_hooks, PredictPayees, PredictPostings

class Target(object):

    def __init__(self, account, payee=None, narration=None):
        self.account = account
        self.payee = payee
        self.narration = narration

class MyImporter(importer.ImporterProtocol):
    def __init__(self, account, default, mapping):
        self.account = account
        self.default = default
        self.mapping = mapping

    def identify(self, fname):
        return re.match(r"myaccount.xls",
            os.path.basename(fname.name))

    def file_account(self, fname):
        return self.account

    def extract(self, fname):
        fp = codecs.open(fname.name, 'r', 'iso-8859-1')
        lines = fp.readlines()

        # drop top and bottom stuff
        lines = lines[4:]
        entries = []

        def fix_decimals(s):
            return s.replace(',', '.')

        for index, row in enumerate(csv.reader(lines, delimiter='\t')):
            meta = data.new_metadata(fname.name, index)
            date = datetime.datetime.strptime(row[0], ' %d/%m/%Y').date()
            desc = row[2].rstrip()
            payee = ""
            currency = row[4]
            account = self.default
            num = number.D(fix_decimals(row[3]))
            units = amount.Amount(num, currency)

            for p, t in self.mapping.items():
                if p in desc:
                    account = t.account

                    if t.narration:
                        desc = t.narration

                    if t.payee:
                        payee = t.payee

            frm = data.Posting(self.account, units, None, None, None, None)
            to = data.Posting(account, -units, None, None, None, None)
            txn = data.Transaction(meta, date, "*", payee, desc,
                    data.EMPTY_SET, data.EMPTY_SET, [frm, to])

            entries.append(txn)

        return entries

mappings = {
}

my__importer = MyImporter('Asset:CheckingAccount, 'Expenses:Unknown', mappings)
apply_hooks(my__importer, [PredictPostings(), PredictPayees()])

CONFIG = [
    my__importer,
]

but I'm getting this error when I tap the "Extract" button in Fava:

  File "/home/user/.local/lib/python3.7/site-packages/fava/templates/extract.html", line 19, in block "content"
    {% for entry in g.ledger.ingest.extract(request.args.get('filename'), request.args.get('importer')) %}
  File "/home/user/.local/lib/python3.7/site-packages/fava/core/ingest.py", line 117, in extract
    existing_entries=self.ledger.all_entries,
  File "/usr/local/lib/python3.7/dist-packages/beancount/ingest/extract.py", line 69, in extract_from_file
    new_entries = importer.extract(file, **kwargs)
  File "/home/user/.local/lib/python3.7/site-packages/smart_importer/hooks.py", line 41, in patched_extract_method
    imported_entries = unpatched_extract(file, existing_entries)
TypeError: extract() takes 2 positional arguments but 3 were given

Is there anything wrong with my importer or should I modify the /smart_importer/hooks.py file to make it work?

yagebu commented 5 years ago

The Beancount importer class has changed a little some time ago and the extract method now accepts an additional keyword argument existing_entries. I've actually changed smart_importer to pass it as a kwarg now, maybe that fixes it, otherwise you could update your importer to take an additional argument.