Import files¶
- roastery.importer.import_csv(*, csv_file: Path, config: Config, extract: roastery.importer.ExtractFn, beancount_file: Path = None, clean: roastery.importer.CleanFn = None, csv_args: dict[str, any] = None) None [source]¶
Import a CSV file and write a beancount file.
For each row of the CSV file:
Create an
Entry
using theextract
function.Apply manual edits from
roastery.config.Config.manual_edits_path
.Clean the entry using the
clean
function, if provided.Write the entry to disk as a Beancount transaction.
The resulting Beancount file is created in the same directory as the CSV file, but with the extension changed to
.beancount
. So:statements/foo.csv
->statements/foo.beancount
You can specify a different path with thebeancount_file
parameter.The transaction is flagged with
"!"
if the digest of the entry occurs in the JSON file atroastery.config.Config.flags_path
.- Parameters:
csv_file (Path) – Path of the CSV file to import.
config (Config) – Configuration to use.
csv_args (dict[str, any]) – Arguments to forward to
csv.DictReader
. This is used to parse weird CSV dialects. For example:dict(delimiter=";", quotechar="|")
.extract (roastery.importer.ExtractFn) – How to extract an
Entry
from a row of CSV data. SeeExtractFn
.clean (roastery.importer.CleanFn) – User-implemented cleaning function. See
CleanFn
.beancount_file (Path) – Path of the beancount file to write to.
- Return type:
None
- roastery.importer.CleanFn¶
Receives a
Entry
and can mutate it as desired to classify / clean up the transaction.For example:
def my_clean(entry: Entry) -> None: payee = entry.payee.lower() if payee == "irs": entry.account.cleaned = "Expenses:Tax" elif payee in {"chipotle", "mcdonalds", "five guys"}: entry.account.cleaned = "Expenses:FastFood"
- class roastery.importer.Entry(digest: str, date: ~datetime.date, amount: ~beancount.core.amount.Amount, account: ~roastery.importer.Cleanable, asset_account: str, payee: ~roastery.importer.Cleanable, narration: ~roastery.importer.Cleanable, meta: ~roastery.importer.EntryMeta = <factory>, tags: set[str] = <factory>, links: set[str] = <factory>, flag: str = '*')[source]¶
Entry
represents a row of transaction data from a financial institution.Entries are constructed during the import process. For example by
roastery.importer.import_csv()
.Entry does not aim to abstract all of beancount’s features. For example,
as_transaction
always generates two postings. That makes this abstraction well-suited for transactions from bank accounts or credit cards. It is less applicable to model transactions involving investments or salary.- Parameters:
- digest: str¶
Computed digest of this entry.
Some banks / data sources do not provide a reliable number / ID for a given entry (in e.g. a CSV file). Therefore, implementors of this class will have to provide a way to compute a digest.
This digest is used when storing the user’s manual edits. See also
roastery.edit
.
- amount: Amount¶
Amount of the entry.
- narration: Cleanable¶
Narration to add to this transaction. For example: Settle the tab at ‘t Neutje.
- meta: EntryMeta¶
Dictionary of arbitrary data to attach to the transaction.
Users can instantiate Entry with a
TypedDict
to get type safety for any extra fields they might want to store.
- tags: set[str]¶
Set of arbitrary strings to tag this transaction with.
See https://beancount.github.io/docs/beancount_language_syntax.html#tags
- links: set[str]¶
Set of arbitrary strings to link this transaction with.
See https://beancount.github.io/docs/beancount_language_syntax.html#tags
- flag: str = '*'¶
One of the strings
*
or!
.*
denotes a ‘normal’ transaction.
!
means there is something special / that requires attention about this entry.
!
transactions are highlighted in red in Fava.
- classmethod from_row(*, digest: str, date: date, amount: Amount, asset_account: str, meta: EntryMeta | None = None, original_payee: str | None = None, original_narration: str | None = None) Entry [source]¶
Convenience constructor that can be called by integrators of a new source data type.
- as_transaction() Transaction [source]¶
Turn this entry into a beancount
Transaction
.- Return type:
Transaction
- apply_manual_edits(edits: dict[str, ManualEdits]) None [source]¶
Apply manual edits to this entry.
- Parameters:
edits (dict[str, ManualEdits]) – Dictionary of manual edits, as deserialized from
roastery.config.Config.manual_edits_path
. The keys are the digests of the entires. The values areroastery.edit.ManualEdits
containing the updated field values.- Return type:
None
- class roastery.importer.EntryMeta¶
Type annotation for the metadata dictionary in Entry.
Import formats can define their own institution specific metadata type if they want users to be able to benefit from autocomplete, and things like that.
alias of TypeVar(‘EntryMeta’, bound=
dict
)
- class roastery.importer.Cleanable(original: str | None = None, cleaned: str | None = None, edited: str | None = None)[source]¶
A string field which can store three variants: the original value, the automatically cleaned value, and the value manually edited by the end user.
The value property will return the first non-None value among the edited, cleaned, and original values.
Examples:
>>> Cleanable(original="foo").value 'foo'
>>> Cleanable(original="foo", cleaned="bar").value 'bar'
>>> Cleanable(original="foo", cleaned="bar", edited="baz").value 'baz'