Skip to content

Commit

Permalink
refs #30 reads default value for known account from the importer in…
Browse files Browse the repository at this point in the history
…stance.
  • Loading branch information
johannesjh committed Apr 30, 2018
1 parent 5e1b2d2 commit 9dddd5f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 48 deletions.
14 changes: 14 additions & 0 deletions smart_importer/predict_postings.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,28 @@ def patched_extract_function(self, original_extract_function):

@wraps(original_extract_function)
def wrapper(self, file, existing_entries=None):

# read the importer's existing entries, if provided as argument to its `extract` method:
decorator.existing_entries = existing_entries

# read the importer's `extract`ed entries
logger.debug(f"About to call the importer's extract function to receive entries to be imported...")
if 'existing_entries' in inspect.signature(original_extract_function).parameters:
decorator.imported_transactions = original_extract_function(self, file, existing_entries)
else:
decorator.imported_transactions = original_extract_function(self, file)

# read the importer's file_account, to be used as default value for the decorator's known `account`:
if inspect.ismethod(self.file_account) and not decorator.account:
logger.debug("Trying to read the importer's file_account, "
"to be used as default value for the decorator's `account` argument...")
file_account = self.file_account(file)
if file_account:
decorator.account = file_account
logger.debug(f"Read file_account {file_account} from the importer; "
f"using it as known account in the decorator.")
else:
logger.debug(f"Could not retrieve file_account from the importer.")

return decorator.enhance_transactions()

Expand Down
44 changes: 20 additions & 24 deletions smart_importer/tests/predict_payees_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from beancount.core.data import Transaction
from beancount.ingest.importer import ImporterProtocol
from beancount.parser import parser

from smart_importer import machinelearning_helpers as ml
from smart_importer.predict_payees import PredictPayees

Expand Down Expand Up @@ -97,10 +98,13 @@ class Testdata:
]


class BasicImporter(ImporterProtocol):
class BasicTestImporter(ImporterProtocol):
def extract(self, file, existing_entries=None):
return Testdata.test_data

def file_account(self, file):
return Testdata.known_account


class PredictPayeesTest(unittest.TestCase):
'''
Expand All @@ -118,11 +122,11 @@ def setUp(self):
account="Assets:US:BofA:Checking",
overwrite_existing_payees=False
)
class DecoratedImporter(BasicImporter):
class DecoratedTestImporter(BasicTestImporter):
pass

self.importerClass = DecoratedImporter
self.importer = DecoratedImporter()
self.importerClass = DecoratedTestImporter
self.importer = DecoratedTestImporter()

def test_dummy_importer(self):
'''
Expand Down Expand Up @@ -192,11 +196,11 @@ def test_class_decoration_with_arguments(self):
training_data=Testdata.training_data,
account=Testdata.known_account
)
class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
pass

i = SmartImporter()
self.assertIsInstance(i, SmartImporter,
i = SmartTestImporter()
self.assertIsInstance(i, SmartTestImporter,
'The decorated importer shall still be an instance of the undecorated class.')
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_payees = [transaction.payee for transaction in transactions]
Expand All @@ -210,24 +214,20 @@ def test_method_decoration_with_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))
testcase = self

class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
@PredictPayees(
training_data=Testdata.training_data,
account=Testdata.known_account
)
def extract(self, file, existing_entries=None):
testcase.assertIsInstance(self, SmartImporter)
testcase.assertIsInstance(self, SmartTestImporter)
return super().extract(file, existing_entries=existing_entries)

i = SmartImporter()
i = SmartTestImporter()
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_payees = [transaction.payee for transaction in transactions]
self.assertEqual(predicted_payees, Testdata.correct_predictions)

# TODO: implement reasonable defaults to fix this test case:
@unittest.skip(
"smart imports without arguments currently fail "
"because the already known account is not filtered from the training data")
def test_class_decoration_without_arguments(self):
'''
Verifies that the decorator can be applied to importer classes,
Expand All @@ -236,19 +236,15 @@ def test_class_decoration_without_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))

@PredictPayees()
class SmartImporter(BasicImporter): pass
class SmartTestImporter(BasicTestImporter): pass

i = SmartImporter()
self.assertIsInstance(i, SmartImporter,
i = SmartTestImporter()
self.assertIsInstance(i, SmartTestImporter,
'The decorated importer shall still be an instance of the undecorated class.')
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_payees = [transaction.payee for transaction in transactions]
self.assertEqual(predicted_payees, Testdata.correct_predictions)

# TODO: implement reasonable defaults to fix this test case:
@unittest.skip(
"smart imports without arguments currently fail "
"because the already known account is not filtered from the training data")
def test_method_decoration_without_arguments(self):
'''
Verifies that the decorator can be applied to an importer's extract method,
Expand All @@ -257,13 +253,13 @@ def test_method_decoration_without_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))
testcase = self

class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
@PredictPayees()
def extract(self, file, existing_entries=None):
testcase.assertIsInstance(self, SmartImporter)
testcase.assertIsInstance(self, SmartTestImporter)
return super().extract(file, existing_entries=existing_entries)

i = SmartImporter()
i = SmartTestImporter()
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_payees = [transaction.payee for transaction in transactions]
self.assertEqual(predicted_payees, Testdata.correct_predictions)
Expand Down
44 changes: 20 additions & 24 deletions smart_importer/tests/predict_postings_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from beancount.core.data import Transaction
from beancount.ingest.importer import ImporterProtocol
from beancount.parser import parser

from smart_importer.predict_postings import PredictPostings

LOG_LEVEL = logging.DEBUG
Expand Down Expand Up @@ -93,10 +94,13 @@ class Testdata:
]


class BasicImporter(ImporterProtocol):
class BasicTestImporter(ImporterProtocol):
def extract(self, file, existing_entries=None):
return Testdata.test_data

def file_account(self, file):
return Testdata.known_account


class PredictPostingsTest(unittest.TestCase):
'''
Expand All @@ -113,11 +117,11 @@ def setUp(self):
training_data=Testdata.training_data,
account="Assets:US:BofA:Checking"
)
class DecoratedImporter(BasicImporter):
class DecoratedTestImporter(BasicTestImporter):
pass

self.importerClass = DecoratedImporter
self.importer = DecoratedImporter()
self.importerClass = DecoratedTestImporter
self.importer = DecoratedTestImporter()

def test_unchanged_narrations(self):
'''
Expand Down Expand Up @@ -178,11 +182,11 @@ def test_class_decoration_with_arguments(self):
training_data=Testdata.training_data,
account=Testdata.known_account
)
class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
pass

i = SmartImporter()
self.assertIsInstance(i, SmartImporter,
i = SmartTestImporter()
self.assertIsInstance(i, SmartTestImporter,
'The decorated importer shall still be an instance of the undecorated class.')
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_accounts = [entry.postings[-1].account for entry in transactions]
Expand All @@ -196,24 +200,20 @@ def test_method_decoration_with_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))
testcase = self

class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
@PredictPostings(
training_data=Testdata.training_data,
account=Testdata.known_account
)
def extract(self, file, existing_entries=None):
testcase.assertIsInstance(self, SmartImporter)
testcase.assertIsInstance(self, SmartTestImporter)
return super().extract(file, existing_entries=existing_entries)

i = SmartImporter()
i = SmartTestImporter()
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_accounts = [entry.postings[-1].account for entry in transactions]
self.assertEqual(predicted_accounts, Testdata.correct_predictions)

# TODO: implement reasonable defaults to fix this test case:
@unittest.skip(
"smart imports without arguments currently fail "
"because the already known account is not filtered from the training data")
def test_class_decoration_with_empty_arguments(self):
'''
Verifies that the decorator can be applied to importer classes,
Expand All @@ -222,19 +222,15 @@ def test_class_decoration_with_empty_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))

@PredictPostings()
class SmartImporter(BasicImporter): pass
class SmartTestImporter(BasicTestImporter): pass

i = SmartImporter()
self.assertIsInstance(i, SmartImporter,
i = SmartTestImporter()
self.assertIsInstance(i, SmartTestImporter,
'The decorated importer shall still be an instance of the undecorated class.')
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_accounts = [transaction.postings[-1].account for transaction in transactions]
self.assertEqual(predicted_accounts, Testdata.correct_predictions)

# TODO: implement reasonable defaults to fix this test case:
@unittest.skip(
"smart imports without arguments currently fail "
"because the already known account is not filtered from the training data")
def test_method_decoration_with_empty_arguments(self):
'''
Verifies that the decorator can be applied to an importer's extract method,
Expand All @@ -243,13 +239,13 @@ def test_method_decoration_with_empty_arguments(self):
logger.info("Running Test Case: {id}".format(id=self.id().split('.')[-1]))
testcase = self

class SmartImporter(BasicImporter):
class SmartTestImporter(BasicTestImporter):
@PredictPostings()
def extract(self, file, existing_entries=None):
testcase.assertIsInstance(self, SmartImporter)
testcase.assertIsInstance(self, SmartTestImporter)
return super().extract(file, existing_entries=existing_entries)

i = SmartImporter()
i = SmartTestImporter()
transactions = i.extract('file', existing_entries=Testdata.training_data)
predicted_accounts = [entry.postings[-1].account for entry in transactions]
self.assertEqual(predicted_accounts, Testdata.correct_predictions)
Expand Down

0 comments on commit 9dddd5f

Please sign in to comment.