Skip to content

Commit 86cfc10

Browse files
committed
wip
1 parent dcaff05 commit 86cfc10

31 files changed

+1034
-953
lines changed

docs/plugins/index.md

Lines changed: 193 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,198 @@
11
# Plugins
22

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

6-
### [PushLunch](pushlunch.md): Push Notifications via Pushover
7+
- [PushLunch](pushlunch.md): Push Notifications via Pushover
8+
- [SplitLunch](splitlunch.md): Splitwise Integration
9+
- [PrimeLunch](primelunch.md): Amazon Transaction Updater
710

8-
### [SplitLunch](splitlunch.md): Splitwise Integration
11+
To install all the known plugins, and their dependencies, install
12+
lunchable with the `plugins` extra:
913

10-
### [PrimeLunch](primelunch.md): Amazon Transaction Updater
14+
```shell
15+
pipx install "lunchable[plugins]"
16+
```
17+
18+
## LunchableApp
19+
20+
Lunchable provides a [LunchableApp](../reference/plugins/app/#lunchable.plugins.app.LunchableApp)
21+
class that can be used to easily build plugins
22+
and more. Notice a few of the main attributes / methods of the `LunchableApp` class:
23+
24+
attribute / method | description | type
25+
------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------
26+
**`lunch`** | The `LunchMoney` client | [LunchMoney](../interacting/#lunchmoney)
27+
**`data`** ¹ | The `LunchableData` object | [LunchableData](#lunchable.plugins.app.LunchableData)
28+
[refresh_data](#lunchable.plugins.LunchableApp.refresh_data) | Refresh all data (besides Transactions) | `method`
29+
[refresh_transactions](#lunchable.plugins.LunchableApp.refresh_transactions) | Refresh transactions, takes same parameters as `LunchMoney.get_transactions()` | `method`
30+
[refresh](#lunchable.plugins.LunchableApp.refresh) ² | Refresh the data for one particular model, takes **kwargs | `method`
31+
[clear_transactions](#lunchable.plugins.LunchableApp.clear_transactions) | Clear all transactions from the internal data | `method`
32+
33+
> ¹ This attribute contains all of the data that is loaded from LunchMoney. It has attributes
34+
> for `assets`, `categories`, `plaid_accounts`, `tags`, `transactions`, `crypto` and `user`.
35+
> These attributes (except for `user`) are `dict[int, LunchableModel]` objects, where the key is
36+
> the ID of the object and the value is the object itself.
37+
38+
> ² this method refreshes all of the data for one particular model. For example,
39+
> `refresh(AssetsObject)` will refresh the assets data and return a
40+
> `dict[int, AssetsObject]` object.
41+
42+
```python
43+
from __future__ import annotations
44+
45+
from typing import Any
46+
47+
from lunchable.models import AssetsObject, TransactionUpdateObject
48+
from lunchable.plugins import LunchableApp
49+
50+
51+
class MyCustomApp(LunchableApp):
52+
"""
53+
My Custom App
54+
"""
55+
56+
def do_something_with_assets(self) -> None:
57+
"""
58+
Do something with the assets
59+
"""
60+
if not self.data.assets:
61+
# If the data hasn't been loaded yet, load it
62+
# The following method loads all of the data besides Transactions
63+
# (Assets, Categories, Plaid Accounts, Tags, Crypto, User)
64+
self.refresh_data()
65+
for asset_id, asset in self.data.assets.items():
66+
# Do something with the asset
67+
print(asset_id, asset)
68+
69+
def do_something_with_transactions(self) -> None:
70+
"""
71+
Do something with the transactions
72+
"""
73+
if not self.data.transactions:
74+
# If the transactions haven't been loaded yet, load them
75+
self.refresh_transactions(start_date="2021-01-01", end_date="2021-01-31")
76+
# Refresh the latest assets
77+
latest_assets: dict[int, AssetsObject] = self.refresh(model=AssetsObject)
78+
for transaction_id, transaction in self.data.transactions.items():
79+
if transaction.asset_id:
80+
asset = latest_assets[transaction.asset_id]
81+
print(transaction_id, transaction, asset)
82+
83+
def update_transaction(self, transaction_id: int, payee: str) -> dict[str, Any]:
84+
"""
85+
You can do anything you want with the `self
86+
"""
87+
update_transaction = TransactionUpdateObject(payee=payee)
88+
response = self.lunch.update_transaction(transaction_id=transaction_id,
89+
transaction=update_transaction)
90+
return response
91+
92+
93+
if __name__ == "__main__":
94+
app = MyCustomApp(access_token="xxxxxxxx")
95+
app.do_something_with_assets()
96+
app.do_something_with_transactions()
97+
app.update_transaction(transaction_id=12345, payee="New Payee")
98+
```
99+
100+
### Choose a subset of data to load
101+
102+
If you don't want to load all of the data, you can specify which data you want to load by
103+
specifying the `lunchable_models` attribute of the `LunchableApp` class. The following example
104+
will only sync the `assets` and `plaid_accounts` data when the `refresh_data()` method is called:
105+
106+
```python
107+
from __future__ import annotations
108+
109+
from typing import ClassVar
110+
111+
from lunchable.models import AssetsObject, PlaidAccountObject, LunchableModel
112+
113+
from lunchable.plugins import LunchableApp
114+
115+
116+
class CustomApp(LunchableApp):
117+
"""
118+
Custom Lunchable App
119+
120+
This app syncs Plaid Accounts and Assets when its `refresh_data` method
121+
is called.
122+
"""
123+
124+
lunchable_models: ClassVar[list[type[LunchableModel]]] = [
125+
PlaidAccountObject,
126+
AssetsObject,
127+
]
128+
129+
def do_something_with_assets(self) -> None:
130+
"""
131+
Do something with the assets
132+
"""
133+
if not self.data.plaid_accounts:
134+
self.refresh_data()
135+
for plaid_account_id, plaid_account in self.data.plaid_accounts.items():
136+
print(plaid_account_id, plaid_account)
137+
```
138+
139+
## Building a Plugin
140+
141+
Plugins are built seperate Python packages and are detected by lunchable via
142+
the `lunchable.cli` entrypoint. To add your own plugin to lunchable you'll need
143+
to add a new entrypoint to your package. The below example shows how to do this
144+
with hatch, a modern, standards-based Python package manager:
145+
146+
```python
147+
import click
148+
149+
150+
@click.command()
151+
def plugin_name():
152+
"""
153+
Plugin description
154+
"""
155+
pass
156+
157+
158+
@plugin_name.command()
159+
def command():
160+
"""
161+
Plugin description
162+
"""
163+
pass
164+
```
165+
166+
```toml
167+
[project.entry-points."lunchable.cli"]
168+
your-package = "your_package.cli:plugin_name"
169+
```
170+
171+
The above example will add a new `command` / `group` to the lunchable `plugins` CLI. When
172+
your package is installed into the same environment as lunchable, your plugin will be
173+
accessible via the `lunchable plugins` command:
174+
175+
```shell
176+
lunchable plugins plugin-name command
177+
```
178+
179+
## API Documentation
180+
181+
::: lunchable.plugins.LunchableApp
182+
handler: python
183+
options:
184+
show_bases: false
185+
allow_inspection: true
186+
inherited_members: true
187+
group_by_category: true
188+
heading_level: 3
189+
show_source: false
190+
191+
::: lunchable.plugins.app.LunchableData
192+
handler: python
193+
options:
194+
show_bases: false
195+
allow_inspection: true
196+
group_by_category: true
197+
heading_level: 3
198+
show_source: false

docs/plugins/primelunch.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ lunchable plugins primelunch run \
9696
## Command Line Documentation
9797

9898
::: mkdocs-click
99-
:module: lunchable._cli
99+
:module: lunchable.plugins.primelunch.cli
100100
:command: run_primelunch
101101
:prog_name: lunchable plugins primelunch run
102102
:style: table

docs/plugins/pushlunch.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ docker run --rm \
4343
from lunchable.plugins.pushlunch import PushLunch
4444
```
4545

46-
::: lunchable.plugins.pushlunch.PushLunch
46+
::: lunchable.plugins.pushlunch.pushover.PushLunch
4747
handler: python
4848
options:
4949
show_bases: false

docs/plugins/splitlunch.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ docker run \
103103
## Run via Python
104104

105105
```python
106-
from lunchable.plugins.splitlunch import SplitLunch
106+
from lunchable.plugins.splitlunch.lunchmoney_splitwise import SplitLunch
107107
```
108108

109-
::: lunchable.plugins.splitlunch.SplitLunch
109+
::: lunchable.plugins.splitlunch.lunchmoney_splitwise.SplitLunch
110110
handler: python
111111
options:
112112
show_bases: false

0 commit comments

Comments
 (0)