Some Scripts Not Working

93 views
Skip to first unread message

Bruce Berkowicz

unread,
Sep 2, 2022, 11:54:08 AM9/2/22
to Beancount
Hello, I have just begun attempting to use beancount (v2). I have written an importer. 
bean-extract works (thankfully) but bean-identify (and bean-file) throw an error that I cannot figure out.

I am on macOS Catalina. I am running python version 3.9.4 in a virtual environment.
I am using beancount v 2.3.5:

(beancount-v2) ➜  finances git:(main) bean-identify -V

Beancount 2.3.5 (git:d8a45f5e; 2022-02-18)


The error messages look like this:

(beancount-v2) ➜  finances git:(main) bean-identify config.py downloads

account = Assets:Chase:Checking   name = Chase2818_Activity

**** /Users/bruceberk/work/finances/downloads/Chase2818_Activity_20220901.CSV

Traceback (most recent call last):

  File "/Users/bruceberk/work/finances/venv/bin/bean-identify", line 8, in <module>

    sys.exit(identify_main())

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/scripts_utils.py", line 31, in identify_main

    return trampoline_to_ingest(identify)

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/scripts_utils.py", line 198, in trampoline_to_ingest

    return run_import_script_and_ingest(parser)

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/scripts_utils.py", line 246, in run_import_script_and_ingest

    return ingest(importers_list)

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/scripts_utils.py", line 140, in ingest

    args.command(args, parser, importers_list, abs_downloads, hooks=hooks)

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/identify.py", line 99, in run

    return identify(importers_list, files_or_directories)

  File "/Users/bruceberk/work/finances/venv/lib/python3.9/site-packages/beancount/ingest/identify.py", line 85, in identify

    logfile.write('Importer:    {}\n'.format(importer.name() if importer else '-'))

TypeError: 'str' object is not callable


The relevant part of my config file looks like:

CONFIG = [ChaseBankImporter("Assets:Chase:Checking", "Chase2818_Activity", "USD")]


My identify function is:

def identify(self, file):
    # check file naming convention
    return re.match(self.name, path.basename(file.name)) and\
re.match("Details,Posting Date,Description,Amount,Type,Balance,Check or Slip #",
file.head())

When I run

python3 -m beancount.scripts.deps

there is no output.

Any help as to what is going on would be greatly appreaciated.

Bruce

Daniele Nicolodi

unread,
Sep 2, 2022, 1:22:15 PM9/2/22
to bean...@googlegroups.com
On 02/09/2022 16:52, Bruce Berkowicz wrote:
> Hello, I have just begun attempting to use beancount (v2). I have
> written an importer.
> bean-extract works (thankfully) but bean-identify (and bean-file) throw
> an error that I cannot figure out.

>     logfile.write('Importer:    {}\n'.format(importer.name() if
> importer else '-'))
> TypeError: 'str' object is not callable

The Beancount Importer interface requires name() to be a method
returning a string. Your importer defines name as a (class or instance)
attribute of type str.

> When I run
>
> python3 -m beancount.scripts.deps
>
> there is no output.

This is unusual but unrelated to the problem above.

Cheers,
Dan

Bruce Berkowicz

unread,
Sep 2, 2022, 2:49:17 PM9/2/22
to Beancount
Hello,
Thanks for helping.
I left out code that I thought was unimportant.
My class does have a name function that returns the name of the importer.
So I am not sure why importer.name() is failing.

My class is actually this:

from decimal import Decimal
from os import path
import logging
import csv
import re
from beancount.core import data
from beancount.core.amount import Amount
from dateutil.parser import parse

from beancount.ingest import importer


class ChaseBankImporter(importer.ImporterProtocol):
def __init__(self, account, name, currency='USD'):
self.account = account
self.name = name
self.currency = currency

print(f'account = {self.account} name = {self.name}')

def name(self):
"""Name of this importer"""
return "ChaseBankImporter"

def file_name(self, file):
return path.basename(file.name)

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


def identify(self, file):
# check file naming convention
return re.match(self.name, path.basename(file.name)) and\
re.match("Details,Posting Date,Description,Amount,Type,Balance,Check or Slip #",
file.head())

def get_post_info(self, row):
"""Decipher row description and return a tuple with posting info"""
txn_type = '?'
payee = 'Payee'
description = row['Description']
offset_acct = 'Expenses'

if ';' in description:
items = description.split(';')
payee = items[0]
description = items[1]

if description.startswith('APPLECARD'):
return tuple(['*', 'AppleCard', 'AppleCard Payment', 'Liabilities:AppleCard'])

if payee.startswith('Football Pool Payout'):
return tuple(['*', payee, description, 'Assets:Football-Pool'])

if 'teterborobowl' in description:
return tuple(['*', 'Bowling', 'Bowling', 'Expenses:Recreation:Bowling'])

if 'CHECK DEPOSIT 12/27' in description:
return tuple(['*', 'Gift Reimburse', 'Gift Reimburse', 'Expenses:GiftGiven:Other'])

if payee == 'House Repair':
return tuple(['*', payee, description, 'Expenses:House:Repair'])

if description.startswith('ATM WITHDRAWAL'):
return tuple(['*', 'Transfer to Cash', 'ATM Withdrawal', 'Assets:Cash'])

if 'NEW JERSEY NATURAL GAS' in description:
return tuple(['*', 'NJNG', 'NJNG Payment', 'Expenses:Utilities:Gas'])

if 'FIRST ENERGY' in description:
return tuple(['*', 'JCPL', 'JCPL Payment', 'Expenses:Utilities:Electric'])

if description.startswith('VANGUARD SELL'):
return tuple(['*', 'Vanguard Deposit', 'Transfer from Vanguard', 'Assets:Vanguard'])

if payee == 'Football Pool Deposit':
return tuple(['*', payee, description, 'Assets:Football-Pool'])

if 'CABLEVISION' in description:
return tuple(['*', 'Cablevision', 'Cablevision Payment', 'Expenses:Utilities:Cable'])

if 'VERIZON' in description:
return tuple(['*', 'Verizon', 'Verizon Payment', 'Expenses:Utilities:Cellphone'])

if payee == 'Landscaper':
return tuple(['*', payee, description, 'Expenses:House:Landscaping'])

if payee == 'Concert Tix':
return tuple(['*', payee, description, 'Expenses:Recreation:Concerts'])

return tuple([txn_type, payee, description, offset_acct])

def extract(self, file):
"""Process the CSV file and create beancount entries"""
entries = []
index = 0
unknown_count = 0

with open(file.name) as fd:
for index, row in enumerate(csv.DictReader(fd)):
meta = data.new_metadata(file.name, index)
date = parse(row['Posting Date']).date()

post_info = self.get_post_info(row)
if post_info[0] == '?':
unknown_count += 1

amount = Amount(Decimal(row['Amount']), self.currency)
balance = Amount(Decimal(row['Balance']), self.currency)

postings = [
data.Posting(self.account, amount, None, None, None, None),
data.Posting(post_info[3], -amount, None, None, None, None)
]

entries.append(
data.Transaction(
meta,
date,
post_info[0],
post_info[1],
post_info[2],
data.EMPTY_SET,
data.EMPTY_SET,
postings,
)
)

index += 1

if unknown_count > 0:
logging.warning(f'*** THERE WERE {unknown_count} UNKNOWN TRANSACTIONS')

if index > 0:
meta = data.new_metadata(file.name, index-1)
entries.append(
data.Balance(
meta,
date,
self.account,
balance,
None,
None,
)
)

return entries

Daniele Nicolodi

unread,
Sep 2, 2022, 3:00:55 PM9/2/22
to bean...@googlegroups.com
On 02/09/2022 20:49, Bruce Berkowicz wrote:
> Hello,
> Thanks for helping.
> I left out code that I thought was unimportant.
> My class does have a name function that returns the name of the importer.
> So I am not sure why importer.name() is failing.
>
> My class is actually this:

The code below has no indentation. I think something went wrong...

> class ChaseBankImporter(importer.ImporterProtocol):
> def __init__(self, account, name, currency='USD'):
> self.account = account
> self.name = name
^^^^^^^^^^^^^^^^

The problem is here, this overrides the ``name()`` method with a
``name`` instance property.

Cheers,
Dan

Bruce Berkowicz

unread,
Sep 2, 2022, 3:21:05 PM9/2/22
to bean...@googlegroups.com
Thank you, thank you, thank you! It now works.
Wow, I would have never found that on my own.

Copy/pasting python code into this message - I do not know how to preserve the indentation.

Bruce

--
You received this message because you are subscribed to a topic in the Google Groups "Beancount" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beancount/N5fCig2YZck/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beancount+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/69aa2745-2658-20b0-4546-e7448b2af536%40grinta.net.

Martin Blais

unread,
Sep 2, 2022, 4:43:51 PM9/2/22
to bean...@googlegroups.com
I think running pylint on the code may have helped, but I'm not sure if it would have caught your name attribute override.


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 beancount+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/CAAtie6arL-Rz0Pgk2OBdPMBueUf5-%3D%2Bakx%3DhuN%2BWrxupQNRqtg%40mail.gmail.com.

Bruce Berkowicz

unread,
Sep 2, 2022, 8:31:21 PM9/2/22
to bean...@googlegroups.com
Back around 2000 I needed to learn Java for work. I bought a book and started in. Of course the first program is a Hello World (it's mandatory ever since K&R).
I typed the code in and it wouldn't compile. Unfortunately, the book didn't clue me in that in Java, the name of the file must be the same as the name of the class. I had put my HelloWorld class in a file named program1.java.

Not the first or last forehead slap.

Reply all
Reply to author
Forward
0 new messages