Skip to content

Plugins#

lunchable plugins are Python packages outside of the lunchable package that can be installed into the same environment as lunchable to add additional functionality to the CLI. To install all the known plugins, and their dependencies, install lunchable with the plugins extra:

pipx install "lunchable[plugins]"

lunchable supports CLI plugins with other external packages. See below for what's been built already. If you can't find what you're looking for, consider building it yourself and opening a pull-request to add it to the list:

LunchableApp#

Lunchable provides a LunchableApp class that can be used to easily build plugins, apps, and more. Notice a few of the main attributes / methods of the LunchableApp class:

attribute / method description type
lunch The LunchMoney client LunchMoney
data ¹ The LunchableData object LunchableData
refresh_data Refresh all data (besides Transactions) method
refresh_transactions Refresh transactions, takes same parameters as LunchMoney.get_transactions() method
refresh ² Refresh the data for one particular model, takes **kwargs method
clear_transactions ³ Clear all transactions from the internal data method

¹ This attribute contains all of the data that is loaded from LunchMoney. It has attributes for assets, categories, plaid_accounts, tags, transactions, crypto and user. These attributes (except for user) are dict[int, LunchableModel] objects, where the key is the ID of the object and the value is the object itself.

² This method refreshes all of the data for one particular model. For example, refresh(AssetsObject) will refresh the assets on the underling data.assets attribute and return a dict[int, AssetsObject] object.

³ This the same as running app.data.transactions.clear()

An Example App#

from __future__ import annotations

from typing import Any

from lunchable.models import AssetsObject, TransactionUpdateObject
from lunchable.plugins import LunchableApp


class MyCustomApp(LunchableApp):
    """
    My Custom App
    """

    def do_something_with_assets(self) -> None:
        """
        Do something with the assets
        """
        if not self.data.assets:
            # If the data hasn't been loaded yet, load it
            # The following method loads all of the data besides Transactions
            # (Assets, Categories, Plaid Accounts, Tags, Crypto, User)
            self.refresh_data()
        for asset_id, asset in self.data.assets.items():
            # Do something with the asset
            print(asset_id, asset)

    def do_something_with_transactions(self) -> None:
        """
        Do something with the transactions
        """
        if not self.data.transactions:
            # If the transactions haven't been loaded yet, load them
            self.refresh_transactions(start_date="2021-01-01", end_date="2021-01-31")
        # Refresh the latest assets
        latest_assets: dict[int, AssetsObject] = self.refresh(model=AssetsObject)
        for transaction_id, transaction in self.data.transactions.items():
            if transaction.asset_id:
                asset = latest_assets[transaction.asset_id]
                print(transaction_id, transaction, asset)

    def update_transaction(self, transaction_id: int, payee: str) -> dict[str, Any]:
        """
        You can do anything you want with the `self
        """
        update_transaction = TransactionUpdateObject(payee=payee)
        response = self.lunch.update_transaction(transaction_id=transaction_id,
                                                 transaction=update_transaction)
        return response


if __name__ == "__main__":
    app = MyCustomApp(access_token="xxxxxxxx")
    app.do_something_with_assets()
    app.do_something_with_transactions()
    app.update_transaction(transaction_id=12345, payee="New Payee")

Choose a subset of data to load#

If you don't want to load all of the data, you can specify which data you want to load by specifying the lunchable_models attribute of the LunchableApp class. The following example will only sync the assets and plaid_accounts data when the refresh_data() method is called:

from __future__ import annotations

from typing import ClassVar

from lunchable.models import AssetsObject, PlaidAccountObject, LunchableModel

from lunchable.plugins import LunchableApp


class CustomApp(LunchableApp):
    """
    Custom Lunchable App

    This app syncs Plaid Accounts and Assets when its `refresh_data` method
    is called.
    """

    lunchable_models: ClassVar[list[type[LunchableModel]]] = [
        PlaidAccountObject,
        AssetsObject,
    ]

    def do_something_with_assets(self) -> None:
        """
        Do something with the assets
        """
        if not self.data.plaid_accounts:
            self.refresh_data()
        for plaid_account_id, plaid_account in self.data.plaid_accounts.items():
            print(plaid_account_id, plaid_account)

Building a Plugin#

Plugins are built separate Python packages and are detected by lunchable via the lunchable.cli entrypoint, these are click command line applications. To add your own plugin to lunchable you'll need to add a new entrypoint to your package. The below example shows how to do this with hatch, a modern, standards-based Python package manager:

import click


@click.group
def plugin_name():
    """
    Plugin description
    """
    pass


@plugin_name.command
def command():
    """
    Plugin description
    """
    pass
[project.entry-points."lunchable.cli"]
your-package = "your_package.cli:plugin_name"

The above example will add a new command / group to the lunchable plugins CLI. When your package is installed into the same environment as lunchable, your plugin will be accessible via the lunchable plugins command:

lunchable plugins plugin-name command

API Documentation#

Pre-Built Lunchable App

This app comes with a data property which represents all the base data the app should need.

This app comes with a refresh_data method which will refresh all of the data in the app, except for transactions. To refresh transactions, use the refresh_transactions method.

__init__(access_token=None) #

Lunchable App Initialization

Parameters:

Name Type Description Default
access_token str | None

Lunchmoney Developer API Access Token. Inherited from LUNCHMONEY_ACCESS_TOKEN environment variable if not provided

None

clear_transactions() #

Clear Transactions from the App

refresh(model, **kwargs) #

Refresh a Lunchable Model

Parameters:

Name Type Description Default
model Type[LunchableModel]

Type of Lunchable Model to refresh

required
kwargs Any

Additional keyword arguments to pass to the function that fetches the data.

{}

Returns:

Type Description
LunchableModel | Dict[int, LunchableModel]

Unless you're requesting the UserObject, this method will return a dictionary of the refreshed data, keyed by the object's ID.

Examples:

from typing import Dict

from lunchable.models import CategoriesObject
from lunchable.plugins import LunchableApp

app = LunchableApp()
categories: Dict[int, CategoriesObject] = app.refresh(CategoriesObject)

refresh_data(models=None) #

Refresh the data in the Lunchable App

Parameters:

Name Type Description Default
models List[Type[LunchableModel]] | None

Explicit list of Lunchable Models to refresh. If not provided, all models defined in will be refreshed (which by default is all of them except for transactions)

None

Examples:

from typing import Dict

from lunchable.models import PlaidAccountObject
from lunchable.plugins import LunchableApp

app = LunchableApp()
app.refresh_data()
plaid_accounts: Dict[int, PlaidAccountObject] = app.data.plaid_accounts
from typing import Dict

from lunchable.models import AssetsObject
from lunchable.plugins import LunchableApp

app = LunchableApp()
app.refresh_data(models=[AssetsObject])
assets: Dict[int, AssetsObject] = app.data.assets

refresh_transactions(start_date=None, end_date=None, tag_id=None, recurring_id=None, plaid_account_id=None, category_id=None, asset_id=None, group_id=None, is_group=None, status=None, debit_as_negative=None, pending=None, params=None) #

Refresh App data with the latest transactions

Parameters:

Name Type Description Default
start_date date | datetime | str | None

Denotes the beginning of the time period to fetch transactions for. Defaults to beginning of current month. Required if end_date exists. Format: YYYY-MM-DD.

None
end_date date | datetime | str | None

Denotes the end of the time period you'd like to get transactions for. Defaults to end of current month. Required if start_date exists.

None
tag_id int | None

Filter by tag. Only accepts IDs, not names.

None
recurring_id int | None

Filter by recurring expense

None
plaid_account_id int | None

Filter by Plaid account

None
category_id int | None

Filter by category. Will also match category groups.

None
asset_id int | None

Filter by asset

None
group_id int | None

Filter by group_id (if the transaction is part of a specific group)

None
is_group bool | None

Filter by group (returns transaction groups)

None
status str | None

Filter by status (Can be cleared or uncleared. For recurring transactions, use recurring)

None
debit_as_negative bool | None

Pass in true if you'd like expenses to be returned as negative amounts and credits as positive amounts. Defaults to false.

None
pending bool | None

Pass in true if you'd like to include imported transactions with a pending status.

None
params Dict[str, Any] | None

Additional Query String Params

None

Returns:

Type Description
Dict[int, TransactionObject]

Examples:

from typing import Dict

from lunchable.models import TransactionObject
from lunchable.plugins import LunchableApp

app = LunchableApp()
transactions: Dict[int, TransactionObject] = app.refresh_transactions(
    start_date="2021-01-01", end_date="2021-01-31"
)

Data Container for Lunchable App Data

Parameters:

Name Type Description Default
plaid_accounts Dict[int, PlaidAccountObject]

Plaid Accounts

{}
transactions Dict[int, TransactionObject]

Transactions

{}
categories Dict[int, CategoriesObject]

Categories

{}
assets Dict[int, AssetsObject]

Assets

{}
tags Dict[int, TagsObject]

Tags

{}
crypto Dict[int, CryptoObject]

Crypto

{}
user UserObject

User

UserObject(user_id=0, user_name='', user_email='', account_id=0, budget_name='', api_key_label=None)

asset_map: Dict[int, PlaidAccountObject | AssetsObject] property #

Asset Mapping Across Plaid Accounts and Assets

Returns:

Type Description
Dict[int, Union[PlaidAccountObject, AssetsObject]]

assets_list: List[AssetsObject] property #

List of Assets

Returns:

Type Description
List[AssetsObject]

categories_list: List[CategoriesObject] property #

List of Categories

Returns:

Type Description
List[CategoriesObject]

crypto_list: List[CryptoObject] property #

List of Crypto

Returns:

Type Description
List[CryptoObject]

plaid_accounts_list: List[PlaidAccountObject] property #

List of Plaid Accounts

Returns:

Type Description
List[PlaidAccountObject]

tags_list: List[TagsObject] property #

List of Tags

Returns:

Type Description
List[TagsObject]

transactions_list: List[TransactionObject] property #

List of Transactions

Returns:

Type Description
List[TransactionObject]