books: Loader.from_all() accepts a start FY argument.

This commit is contained in:
Brett Smith 2020-06-04 09:03:10 -04:00
parent 8dbe807efb
commit 2bd3e8b462
3 changed files with 61 additions and 26 deletions

View file

@ -135,16 +135,38 @@ class Loader:
errors.extend(new_errors)
return entries, errors, options_map
def load_all(self) -> LoadResult:
"""Load all of the books
def _path_year(self, path: Path) -> int:
return int(path.stem)
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.
def load_all(self, from_year: Optional[Year]=None) -> LoadResult:
"""Load all of the books from a starting FY
This method loads all of the books, starting from the fiscal year you
specify.
* Pass in a date to start from the FY for that date.
* Pass in an integer >= 1000 to start from that year.
* Pass in a smaller integer to start from an FY relative to today
(e.g., -2 starts two FYs before today).
* Pass is no argument to load all books from the first available FY.
This method finds books by 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(key=lambda path: int(path.stem))
fy_paths.sort(key=self._path_year)
if from_year is not None:
if not isinstance(from_year, int):
from_year = self.fiscal_year.for_date(from_year)
elif from_year < 1000:
from_year = self.fiscal_year.for_date() + from_year
for index, path in enumerate(fy_paths):
if self._path_year(path) >= from_year:
fy_paths = fy_paths[index:]
break
else:
fy_paths = []
return self._load_paths(iter(fy_paths))
def load_fy_range(self,
@ -154,9 +176,8 @@ class Loader:
"""Load books for a range of fiscal years
This method generates a range of fiscal years by calling
FiscalYear.range() with its first two arguments. It returns a string of
Beancount directives to load the books from the first available fiscal
year through the end of the range.
FiscalYear.range() with its arguments. It loads all the books within
that range.
"""
fy_range = self.fiscal_year.range(from_fy, to_fy)
fy_paths = self._iter_fy_books(fy_range)

View file

@ -27,11 +27,13 @@ from . import testutil
from beancount.core import data as bc_data
from conservancy_beancount import books
FY_START_MONTH = 3
books_path = testutil.test_path('books')
@pytest.fixture(scope='module')
def conservancy_loader():
return books.Loader(books_path, books.FiscalYear(3))
return books.Loader(books_path, books.FiscalYear(FY_START_MONTH))
def check_openings(entries):
openings = collections.defaultdict(int)
@ -41,18 +43,13 @@ def check_openings(entries):
for account, count in openings.items():
assert count == 1, f"found {count} open directives for {account}"
def check_narrations(entries, expected):
expected = iter(expected)
expected_next = next(expected)
def txn_dates(entries):
for entry in entries:
if (isinstance(entry, bc_data.Transaction)
and entry.narration == expected_next):
try:
expected_next = next(expected)
except StopIteration:
break
else:
assert None, f"{expected_next} not found in entry narrations"
if isinstance(entry, bc_data.Transaction):
yield entry.date
def txn_years(entries):
return frozenset(date.year for date in txn_dates(entries))
@pytest.mark.parametrize('from_fy,to_fy,expect_years', [
(2019, 2019, range(2019, 2020)),
@ -70,7 +67,7 @@ def check_narrations(entries, expected):
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
check_narrations(entries, [f'{year} donation' for year in expect_years])
assert txn_years(entries).issuperset(expect_years)
def test_load_fy_range_does_not_duplicate_openings(conservancy_loader):
entries, errors, options_map = conservancy_loader.load_fy_range(2010, 2030)
@ -82,8 +79,25 @@ def test_load_fy_range_empty(conservancy_loader):
assert not entries
assert not options_map
def test_load_all(conservancy_loader):
entries, errors, options_map = conservancy_loader.load_all()
@pytest.mark.parametrize('from_year', [None, *range(2018, 2021)])
def test_load_all(conservancy_loader, from_year):
entries, errors, options_map = conservancy_loader.load_all(from_year)
from_year = from_year or 2018
assert not errors
check_narrations(entries, [f'{year} donation' for year in range(2018, 2021)])
check_openings(entries)
assert txn_years(entries).issuperset(range(from_year or 2018, 2021))
@pytest.mark.parametrize('from_date', [
date(2019, 2, 1),
date(2019, 9, 15),
date(2020, 1, 20),
date(2020, 5, 31),
])
def test_load_all_from_date(conservancy_loader, from_date):
from_year = from_date.year
if from_date.month < FY_START_MONTH:
from_year -= 1
entries, errors, options_map = conservancy_loader.load_all(from_date)
assert not errors
check_openings(entries)
assert txn_years(entries).issuperset(range(from_year, 2021))

View file

@ -217,7 +217,7 @@ class TestBooksLoader(books.Loader):
def __init__(self, source):
self.source = source
def load_all(self):
def load_all(self, from_year=None):
return bc_loader.load_file(self.source)
def load_fy_range(self, from_fy, to_fy=None):