books: Add docstrings throughout.
This commit is contained in:
parent
e2dda7ae0c
commit
323521344a
1 changed files with 59 additions and 1 deletions
|
@ -1,4 +1,8 @@
|
|||
"""books - Tools for loading the books"""
|
||||
"""books - Tools for loading the books
|
||||
|
||||
This module provides common functionality for loading books split by fiscal
|
||||
year and doing common operations on the results.
|
||||
"""
|
||||
# Copyright © 2020 Brett Smith
|
||||
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
|
||||
#
|
||||
|
@ -39,10 +43,25 @@ PathLike = Union[str, Path]
|
|||
Year = Union[int, datetime.date]
|
||||
|
||||
class FiscalYear(NamedTuple):
|
||||
"""Convert to and from fiscal years and calendar dates
|
||||
|
||||
Given a month and date that a fiscal year starts, this class provides
|
||||
methods to calculate the fiscal year of a given calendar date; to return
|
||||
important calendar dates associated with the fiscal year; and iterate
|
||||
fiscal years.
|
||||
|
||||
Most methods can accept either an int, representing a fiscal year;
|
||||
or a date. When you pass a date, the method will calculate that date's
|
||||
corresponding fiscal year, and use it as the argument.
|
||||
"""
|
||||
month: int = 3
|
||||
day: int = 1
|
||||
|
||||
def for_date(self, date: Optional[datetime.date]=None) -> int:
|
||||
"""Return the fiscal year of a given calendar date
|
||||
|
||||
The default date is today's date.
|
||||
"""
|
||||
if date is None:
|
||||
date = datetime.date.today()
|
||||
if (date.month, date.day) < self:
|
||||
|
@ -51,14 +70,17 @@ class FiscalYear(NamedTuple):
|
|||
return date.year
|
||||
|
||||
def first_date(self, year: Year) -> datetime.date:
|
||||
"""Return the first calendar date of a fiscal year"""
|
||||
if isinstance(year, datetime.date):
|
||||
year = self.for_date(year)
|
||||
return datetime.date(year, self.month, self.day)
|
||||
|
||||
def last_date(self, year: Year) -> datetime.date:
|
||||
"""Return the last calendar date of a fiscal year"""
|
||||
return self.next_fy_date(year) - datetime.timedelta(days=1)
|
||||
|
||||
def next_fy_date(self, year: Year) -> datetime.date:
|
||||
"""Return the last calendar date of a fiscal year"""
|
||||
if isinstance(year, datetime.date):
|
||||
year = self.for_date(year)
|
||||
return datetime.date(year + 1, self.month, self.day)
|
||||
|
@ -100,12 +122,25 @@ class FiscalYear(NamedTuple):
|
|||
|
||||
|
||||
class LoadResult(NamedTuple):
|
||||
"""Common functionality for loaded books
|
||||
|
||||
This class is type-compatible with the return value of the loader
|
||||
functions in ``beancount.loader``. This provides named access to the
|
||||
results, as well as common functionality methods.
|
||||
"""
|
||||
entries: Entries
|
||||
errors: Errors
|
||||
options_map: OptionsMap
|
||||
|
||||
@classmethod
|
||||
def empty(cls, error: Optional[Error]=None) -> 'LoadResult':
|
||||
"""Create a return result that represents nothing loaded
|
||||
|
||||
If an error is provided, it will be the sole error reported.
|
||||
|
||||
This method is useful to create a LoadResult when one can't be
|
||||
created normally; e.g., because a books path is not properly configured.
|
||||
"""
|
||||
errors: Errors = []
|
||||
if error is not None:
|
||||
errors.append(error)
|
||||
|
@ -116,6 +151,14 @@ class LoadResult(NamedTuple):
|
|||
rewrites: Iterable[Union[Path, rewrite.RewriteRuleset]]=(),
|
||||
search_terms: Iterable[cliutil.SearchTerm]=(),
|
||||
) -> Iterator[data.Posting]:
|
||||
"""Iterate all the postings in this LoadResult
|
||||
|
||||
If ``rewrites`` are provided, postings will be passed through them all.
|
||||
See the ``reports.rewrite`` pydoc for details.
|
||||
|
||||
If ``search_terms`` are provided, postings will be filtered through
|
||||
them all. See the ``cliutil.SearchTerm`` pydoc for details.
|
||||
"""
|
||||
postings = data.Posting.from_entries(self.entries)
|
||||
for ruleset in rewrites:
|
||||
if isinstance(ruleset, Path):
|
||||
|
@ -126,9 +169,14 @@ class LoadResult(NamedTuple):
|
|||
return postings
|
||||
|
||||
def load_account_metadata(self) -> None:
|
||||
"""Load account metadata from this LoadResult"""
|
||||
return data.Account.load_from_books(self.entries, self.options_map)
|
||||
|
||||
def print_errors(self, out_file: TextIO) -> bool:
|
||||
"""Report errors from this LoadResult to ``out_file``
|
||||
|
||||
Returns True if errors were reported, False otherwise.
|
||||
"""
|
||||
for error in self.errors:
|
||||
bc_printer.print_error(error, file=out_file)
|
||||
try:
|
||||
|
@ -139,6 +187,11 @@ class LoadResult(NamedTuple):
|
|||
return True
|
||||
|
||||
def returncode(self) -> int:
|
||||
"""Return an appropriate Unix exit code for this LoadResult
|
||||
|
||||
If this LoadResult has errors, or no entries, return an exit code that
|
||||
best represents that. Otherwise, return the standard OK exit code 0.
|
||||
"""
|
||||
if self.errors:
|
||||
if self.entries:
|
||||
return cliutil.ExitCode.BeancountErrors
|
||||
|
@ -265,6 +318,11 @@ class Loader:
|
|||
from_fy: Optional[Year]=None,
|
||||
to_fy: Optional[Year]=None,
|
||||
) -> LoadResult:
|
||||
"""High-level, "do-what-I-mean"-ish books loader
|
||||
|
||||
Most tools can call this with a books loader from configuration, plus
|
||||
one or two fiscal year arguments, to get the LoadResult they want.
|
||||
"""
|
||||
if loader is None:
|
||||
return cls.load_none()
|
||||
elif to_fy is None:
|
||||
|
|
Loading…
Reference in a new issue