books: Add Loader.load_all method.

This commit is contained in:
Brett Smith 2020-05-25 10:37:21 -04:00
parent 6801d12359
commit 9595d3334d
2 changed files with 57 additions and 26 deletions

View file

@ -118,6 +118,35 @@ class Loader:
if path.exists():
yield path
def _load_paths(self, paths: Iterator[Path]) -> LoadResult:
try:
entries, errors, options_map = bc_loader.load_file(next(paths))
except StopIteration:
entries, errors, options_map = [], [], {}
for load_path in paths:
new_entries, new_errors, new_options = bc_loader.load_file(load_path)
# We only want transactions from the new fiscal year.
# We don't want the opening balance, duplicate definitions, etc.
fy_filename = str(load_path.parent.parent / load_path.name)
entries.extend(
entry for entry in new_entries
if entry.meta.get('filename') == fy_filename
)
errors.extend(new_errors)
return entries, errors, options_map
def load_all(self) -> LoadResult:
"""Load all of the books
This method loads all of the books. It finds the books by simply
globbing the filesystem. It still loads each fiscal year in sequence to
provide the best cache utilization.
"""
path = Path(self.books_root, 'books')
fy_paths = list(path.glob('[1-9][0-9][0-9][0-9].beancount'))
fy_paths.sort()
return self._load_paths(iter(fy_paths))
def load_fy_range(self,
from_fy: Year,
to_fy: Optional[Year]=None,
@ -131,18 +160,4 @@ class Loader:
"""
fy_range = self.fiscal_year.range(from_fy, to_fy)
fy_paths = self._iter_fy_books(fy_range)
try:
entries, errors, options_map = bc_loader.load_file(next(fy_paths))
except StopIteration:
entries, errors, options_map = [], [], {}
for load_path in fy_paths:
new_entries, new_errors, new_options = bc_loader.load_file(load_path)
# We only want transactions from the new fiscal year.
# We don't want the opening balance, duplicate definitions, etc.
fy_filename = str(load_path.parent.parent / load_path.name)
entries.extend(
entry for entry in new_entries
if entry.meta.get('filename') == fy_filename
)
errors.extend(new_errors)
return entries, errors, options_map
return self._load_paths(fy_paths)

View file

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import collections
import re
from datetime import date
@ -23,6 +24,7 @@ import pytest
from . import testutil
from beancount.core import data as bc_data
from conservancy_beancount import books
books_path = testutil.test_path('books')
@ -31,6 +33,20 @@ books_path = testutil.test_path('books')
def conservancy_loader():
return books.Loader(books_path, books.FiscalYear(3))
def check_openings(entries):
openings = collections.defaultdict(int)
for entry in entries:
if isinstance(entry, bc_data.Open):
openings[entry.account] += 1
for account, count in openings.items():
assert count == 1, f"found {count} open directives for {account}"
def get_narrations(entries):
return {
entry.narration for entry in entries
if isinstance(entry, bc_data.Transaction)
}
@pytest.mark.parametrize('from_fy,to_fy,expect_years', [
(2019, 2019, range(2019, 2020)),
(0, 2019, range(2019, 2020)),
@ -47,26 +63,26 @@ def conservancy_loader():
def test_load_fy_range(conservancy_loader, from_fy, to_fy, expect_years):
entries, errors, options_map = conservancy_loader.load_fy_range(from_fy, to_fy)
assert not errors
narrations = {getattr(entry, 'narration', None) for entry in entries}
narrations = get_narrations(entries)
assert ('2018 donation' in narrations) == (2018 in expect_years)
assert ('2019 donation' in narrations) == (2019 in expect_years)
assert ('2020 donation' in narrations) == (2020 in expect_years)
def test_load_fy_range_does_not_duplicate_openings(conservancy_loader):
entries, errors, options_map = conservancy_loader.load_fy_range(2010, 2030)
openings = []
open_accounts = set()
for entry in entries:
try:
open_accounts.add(entry.account)
except AttributeError:
pass
else:
openings.append(entry)
assert len(openings) == len(open_accounts)
check_openings(entries)
def test_load_fy_range_empty(conservancy_loader):
entries, errors, options_map = conservancy_loader.load_fy_range(2020, 2019)
assert not errors
assert not entries
assert not options_map
def test_load_all(conservancy_loader):
entries, errors, options_map = conservancy_loader.load_all()
assert not errors
narrations = get_narrations(entries)
assert '2018 donation' in narrations
assert '2019 donation' in narrations
assert '2020 donation' in narrations
check_openings(entries)