Changeset - 2bd3e8b462d2
[Not reviewed]
0 3 0
Brett Smith - 4 years ago 2020-06-04 13:03:10
brettcsmith@brettcsmith.org
books: Loader.from_all() accepts a start FY argument.
3 files changed with 61 insertions and 26 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/books.py
Show inline comments
...
 
@@ -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)
tests/test_books_loader.py
Show inline comments
...
 
@@ -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_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_narrations(entries, [f'{year} donation' for year in range(2018, 2021)])
 
    check_openings(entries)
 
    assert txn_years(entries).issuperset(range(from_year, 2021))
tests/testutil.py
Show inline comments
...
 
@@ -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):
0 comments (0 inline, 0 general)