Skip to content

Commit a0c2d28

Browse files
committed
Added checks
1 parent dac7bec commit a0c2d28

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

ledgertools/__init__.py

Whitespace-only changes.

ledgertools/checks.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import os
2+
import math
3+
4+
class CheckFailed(Exception):
5+
'''raise this when there's at least one check does not pass'''
6+
7+
def valid_accounts():
8+
try:
9+
with open('.valid_accounts', 'r') as infile:
10+
lines = infile.readlines()
11+
return [line.rstrip() for line in lines]
12+
except:
13+
print('No ".valid_accounts" file found!')
14+
return []
15+
16+
def transaction_checks():
17+
return [check_sum_is_zero, check_valid_accounts]
18+
19+
def check_sum_is_zero(transaction, abs_tol=0.001):
20+
sum_of_postings = sum([p.amount for p in transaction.postings])
21+
return math.isclose(sum_of_postings, 0, abs_tol=abs_tol), f'Sum of postings is off by {sum_of_postings:.2f}'
22+
23+
def check_valid_accounts(transaction):
24+
invalid_accounts = []
25+
for posting in transaction.postings:
26+
if posting.account not in valid_accounts():
27+
invalid_accounts.append(posting.account)
28+
return len(invalid_accounts) < 1, f'Invalid account names: {invalid_accounts}'

ledgertools/cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ def cli():
1515
@click.option('-f', '--file', 'in_file', help='Input file name', prompt='Input file name')
1616
@click.option('-n', '--name', default='transactions.json', help='Output file name')
1717
@click.option('-p', '--pickle', 'as_pickle', is_flag=True, help='Output as pickle file')
18+
@click.option('--run-checks', 'run_checks', is_flag=True, help='Run standard checks on data')
1819
@click.option('--stdout', is_flag=True, help='Output to stdout, supresses output files')
19-
def read(in_file, name, as_pickle, stdout):
20+
def read(in_file, name, as_pickle, run_checks, stdout):
2021
click.secho(f'Reading input file: {in_file}', fg='green')
21-
transactions = read_file(in_file)
22+
transactions = read_file(in_file, run_checks)
2223

2324
if stdout:
2425
print(json.dumps(transactions, sort_keys=True, indent=4, ensure_ascii=False))

ledgertools/read.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import pendulum
55

66
from tqdm import tqdm
7+
from ledgertools.checks import transaction_checks, CheckFailed
8+
79

810
# regex copied from https://github.com/Tagirijus/ledger-parse
911
PAT_TRANSACTION_DATA = re.compile(r'(?P<year>\d{4})[/|-](?P<month>\d{2})[/|-](?P<day>\d{2})(?:=(?P<year_aux>\d{4})[/|-](?P<month_aux>\d{2})[/|-](?P<day_aux>\d{2}))? (?P<state>[\*|!])?[ ]?(\((?P<code>[^\)].+)\) )?(?P<payee>.+)')
@@ -15,10 +17,10 @@ class Ledger():
1517
"""
1618
def __init__(self, ledger_filename=None, raw_transactions=None):
1719
self._transactions = []
18-
if raw_transactions is not None:
19-
self._transactions = self._import_raw_transactions(raw_transactions)
2020
if ledger_filename is not None:
2121
self._transactions = self.import_ledger_file(ledger_filename)
22+
if raw_transactions is not None:
23+
self._transactions = self._import_raw_transactions(raw_transactions)
2224

2325
@property
2426
def json(self):
@@ -28,6 +30,17 @@ def json(self):
2830
def transactions(self):
2931
return self._transactions
3032

33+
def run_checks(self, strict=True):
34+
n_checks_failed = 0
35+
for transaction in self._transactions:
36+
for check in transaction_checks():
37+
success, info = transaction.run_checks(check)
38+
if not success:
39+
n_checks_failed += 1
40+
print(f'{check.__name__} failed for transaction\n {transaction}{info}')
41+
if n_checks_failed > 0 and strict:
42+
raise CheckFailed(f'{n_checks_failed} checks failed')
43+
3144
def _parse_tags(self, string):
3245
tags = {}
3346
# split string in parts
@@ -187,7 +200,7 @@ def json(self):
187200
status=self.status,
188201
code=self.code,
189202
description=self.description,
190-
transactions=[p.json for p in self.postings],
203+
postings=[p.json for p in self.postings],
191204
comment=self.comment)
192205

193206
@property
@@ -234,6 +247,9 @@ def postings(self):
234247
def add_posting(self, posting):
235248
self._postings.append(posting)
236249

250+
def run_checks(self, check):
251+
return check(self)
252+
237253

238254
class Posting():
239255
"""Represents a single posting
@@ -275,5 +291,8 @@ def date(self):
275291
return self._primary_date.isoformat()
276292

277293

278-
def read_file(in_file):
279-
return Ledger(ledger_filename=in_file).json
294+
def read_file(in_file, run_checks=False):
295+
ledger = Ledger(ledger_filename=in_file)
296+
if run_checks:
297+
ledger.run_checks(strict=False)
298+
return ledger.json

0 commit comments

Comments
 (0)