diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index b1e017f0..bd502ae7 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -24,8 +24,9 @@ jobs:
python-version: '3.11'
- name: Install Hatch
run: |
- python -m pip install --upgrade pip
+ python -m pip install --upgrade pip wheel
python -m pip install -q hatch pre-commit
+ python -m pip install -q "${{ github.workspace }}"
hatch --version
- name: Release
run: hatch run gen:release
@@ -37,60 +38,30 @@ jobs:
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
github-pages-publish:
+ runs-on: ubuntu-latest
needs: release
if: github.ref == 'refs/heads/main' && github.repository_owner == 'juftin'
- runs-on: ubuntu-latest
+ permissions:
+ contents: write
steps:
- name: Checkout Latest Changes
- uses: actions/checkout@v3
- with:
- path: ${{ github.workspace }}/main
- ref: ${{ github.ref }}
- fetch-depth: 0
- - name: Create gh-pages if not exists
- working-directory: ${{ github.workspace }}/main
- run: |
- git checkout gh-pages || git checkout -b gh-pages
- git push --set-upstream origin gh-pages || true
- git checkout main --
- - name: Checkout gh-pages Branch
uses: actions/checkout@v4
with:
- path: ${{ github.workspace }}/github-pages
- ref: gh-pages
+ ref: ${{ github.ref }}
fetch-depth: 0
- name: Set up Python Environment
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install Hatch
- working-directory: ${{ github.workspace }}/main
run: |
- python -m pip install --upgrade pip
- python -m pip install -q hatch
+ python -m pip install --upgrade pip wheel
+ python -m pip install -q hatch pre-commit
+ python -m pip install -q "${{ github.workspace }}"
hatch --version
- - name: Documentation Generation
- working-directory: ${{ github.workspace }}/main
- run: |
- hatch run docs:build
- - name: Setup Git Config
- run: |
- git config --global user.name "github-actions[bot]"
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- - name: Get Commit SHA from main
- working-directory: ${{ github.workspace }}/main
- run: |
- COMMIT_SHA=$(git rev-parse HEAD)
- PROJECT_VERSION=$(hatch version)
- echo PROJECT_VERSION=${PROJECT_VERSION} >> $GITHUB_ENV
- echo "COMMIT_SHA=${COMMIT_SHA}" >> ${GITHUB_ENV}
- - name: Commit Changes to gh-pages Branch
- working-directory: ${{ github.workspace }}/github-pages
+ - name: Set Up GitHub Actions User
run: |
- git rm -rf . || true
- cp -R ${{ github.workspace }}/main/docs/_build/html/* ${PWD}
- touch .nojekyll
- cp ${{ github.workspace }}/main/docs/README.md .
- git add .
- git diff-index --quiet HEAD || git commit -m "GitHub Pages - ${{ env.PROJECT_VERSION }} - ${{ env.COMMIT_SHA }}"
- git push origin gh-pages --force
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ - name: Deploy Documentation Changes
+ run: hatch run docs:gh-deploy --force
diff --git a/README.md b/README.md
index 4f2a1c0f..66a69ef0 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,23 @@
-[![Lunchable Version](https://img.shields.io/pypi/v/lunchable?color=blue&label=lunchable)](https://github.com/juftin/lunchable)
-[![PyPI](https://img.shields.io/pypi/pyversions/lunchable)](https://pypi.python.org/pypi/lunchable/)
-[![Docker Image Version](https://img.shields.io/docker/v/juftin/lunchable?color=blue&label=docker&logo=docker)](https://hub.docker.com/r/juftin/lunchable)
-[![Testing Status](https://github.com/juftin/lunchable/actions/workflows/tests.yaml/badge.svg?branch=main)](https://github.com/juftin/lunchable/actions/workflows/tests.yaml?query=branch%3Amain)
-[![GitHub License](https://img.shields.io/github/license/juftin/lunchable?color=blue&label=License)](https://github.com/juftin/lunchable/blob/main/LICENSE)
+
+
+
+
+
+
+
+
+
+
+
+
**lunchable** is a Python Client for the [Lunch Money Developer API](https://lunchmoney.dev). It's
built on top of [pydantic](https://github.com/pydantic/pydantic) and [httpx](https://github.com/encode/httpx/),
@@ -27,16 +34,18 @@ pip install lunchable
### Usage
```python
-from typing import Any, Dict, List
+from __future__ import annotations
+
+from typing import Any
from lunchable import LunchMoney
from lunchable.models import TransactionObject
lunch = LunchMoney(access_token="xxxxxxxxxxx")
-transactions: List[TransactionObject] = lunch.get_transactions()
+transactions: list[TransactionObject] = lunch.get_transactions()
first_transaction: TransactionObject = transactions[0]
-transaction_as_dict: Dict[str, Any] = first_transaction.model_dump()
+transaction_as_dict: dict[str, Any] = first_transaction.model_dump()
```
```shell
@@ -45,6 +54,8 @@ lunchable transactions get --limit 5
lunchable http -X GET https://dev.lunchmoney.app/v1/assets
```
+
+
### Check out the [**Docs**](https://juftin.com/lunchable/)
### Looking to contribute? See the [Contributing Guide](docs/source/contributing.md)
@@ -58,3 +69,5 @@ lunchable http -X GET https://dev.lunchmoney.app/v1/assets
[
](https://github.com/juftin)
+
+
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index f016e55b..00000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-SPHINXPROJ = lunchable
-SOURCEDIR = source
-BUILDDIR = _build
-
-# Put it first so that "make" without argument is like "make help".
-help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index e2c26480..00000000
--- a/docs/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# lunchable
-
-A simple Python SDK around the Lunch Money Developer API
-
-#### This is the documentation branch of lunchable
-
-### [See the Documentation Website](https://juftin.github.io/lunchable)
-
-### [See the Latest Changes](https://github.com/juftin/lunchable)
diff --git a/docs/cli.md b/docs/cli.md
new file mode 100644
index 00000000..1a2932a6
--- /dev/null
+++ b/docs/cli.md
@@ -0,0 +1,8 @@
+# lunchable CLI
+
+::: mkdocs-click
+ :module: lunchable._cli
+ :command: cli
+ :prog_name: lunchable
+ :style: table
+ :list_subcommands: True
diff --git a/docs/source/contributing.md b/docs/contributing.md
similarity index 73%
rename from docs/source/contributing.md
rename to docs/contributing.md
index 608ce697..0f03bad3 100644
--- a/docs/source/contributing.md
+++ b/docs/contributing.md
@@ -2,12 +2,26 @@
## Environment Setup
-1. Install [hatch](https://github.com/pypa/hatch)
+> TIP: **pipx**
+>
+> This documentaion uses [pipx] to
+> install and manage non-project command line tools like `hatch` and
+> `pre-commit`. If you don't already have `pipx` installed, make sure to
+> see their [documentation](https://pypa.github.io/pipx/installation/).
+> If you prefer not to use `pipx`, you can use `pip` instead.
+
+1. Install [hatch](https://hatch.pypa.io/latest/)
```shell
pipx install hatch
```
+ > NOTE: **pre-commit**
+ >
+ > Hatch will attempt to set up pre-commit hooks for you using
+ > [pre-commit]. If you don't already,
+ > make sure to install pre-commit as well: `pipx install pre-commit`
+
2. Build the Virtual Environment
```shell
@@ -18,7 +32,7 @@
They can be located by name with the `env find` command:
```shell
- hatch env find default
+ hatch env find test
```
4. Activate the Virtual Environment
@@ -48,6 +62,54 @@ These features include virtual environment management and the organization of co
scripts like linting and testing. All the operations in hatch take place in one
of its managed virtual environments.
+Hatch has a variety of environments, to see them simply ask hatch:
+
+```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output"
+hatch env show
+```
+
+That above command will tell you that there are five environments that
+you can use:
+
+- `default`
+- `docs`
+- `gen`
+- `lint`
+- `test`
+
+Each of these environments has a set of commands that you can run.
+To see the commands for a specific environment, run:
+
+```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output"
+hatch env show default
+```
+
+Here we can see that the `default` environment has the following commands:
+
+- `cov`
+- `test`
+
+The one that we're interested in is `cov`, which will run the tests
+for the project.
+
+```bash
+hatch run cov
+```
+
+Since `cov` is in the default environment, we can run it without
+specifying the environment. However, to run the `serve` command in the
+`docs` environment, we need to specify the environment:
+
+```bash
+hatch run docs:serve
+```
+
+You can see what scripts are available using the `env show` command
+
+```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output"
+hatch env show docs
+```
+
## Committing Code
This project uses [pre-commit] to run a set of
diff --git a/docs/gen_pages.py b/docs/gen_pages.py
new file mode 100644
index 00000000..af987f26
--- /dev/null
+++ b/docs/gen_pages.py
@@ -0,0 +1,36 @@
+"""
+Generate the code reference pages and navigation.
+"""
+
+import logging
+from pathlib import Path
+
+import mkdocs_gen_files
+
+logger = logging.getLogger(__name__)
+
+project_dir = Path(__file__).resolve().parent.parent
+source_code = project_dir.joinpath("lunchable")
+
+for path in sorted(source_code.rglob("*.py")):
+ module_path = path.relative_to(project_dir).with_suffix("")
+ doc_path = path.relative_to(source_code).with_suffix(".md")
+ full_doc_path = Path("reference", doc_path)
+
+ parts = tuple(module_path.parts)
+ if parts[-1] == "__init__":
+ parts = parts[:-1]
+ doc_path = doc_path.with_name("index.md")
+ full_doc_path = full_doc_path.with_name("index.md")
+ elif parts[-1] == "__main__":
+ continue
+ with mkdocs_gen_files.open(full_doc_path, "w") as fd:
+ fd.write(f"# `{parts[-1]}`\n\n::: {'.'.join(parts)}")
+
+ mkdocs_gen_files.set_edit_path(full_doc_path, path)
+
+# Exclude parts that are between two exact `` lines
+readme_content = Path("README.md").read_text()
+readme_content = "\n".join(readme_content.split("\n\n")[::2])
+with mkdocs_gen_files.open("index.md", "w") as index_file:
+ index_file.write(readme_content)
diff --git a/docs/interacting.md b/docs/interacting.md
new file mode 100644
index 00000000..16be2216
--- /dev/null
+++ b/docs/interacting.md
@@ -0,0 +1,72 @@
+# LunchMoney
+
+The `LunchMoney` client is the main entrypoint for interacting with the Lunch Money API.
+It defaults to inheriting the `LUNCHMONEY_ACCESS_TOKEN` environment variable, but can be
+created with an explicit `access_token` parameter.
+
+```python
+from lunchable import LunchMoney
+
+lunch = LunchMoney(access_token="xxxxxxxxxxx")
+```
+
+## Methods
+
+| HTTP Verb | Name | Description |
+|-----------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------|
+| GET | [get_assets](#lunchable.LunchMoney.get_assets) | Get Manually Managed Assets |
+| GET | [get_budgets](#lunchable.LunchMoney.get_budgets) | Get Monthly Budgets |
+| GET | [get_categories](#lunchable.LunchMoney.get_categories) | Get Spending categories |
+| GET | [get_category](#lunchable.LunchMoney.get_category) | Get single category |
+| GET | [get_crypto](#lunchable.LunchMoney.get_crypto) | Get Crypto Assets |
+| GET | [get_plaid_accounts](#lunchable.LunchMoney.get_plaid_accounts) | Get Plaid Synced Assets |
+| GET | [get_recurring_expenses](#lunchable.LunchMoney.get_recurring_expenses) | Get Recurring Expenses |
+| GET | [get_tags](#lunchable.LunchMoney.get_tags) | Get Spending Tags |
+| GET | [get_transaction](#lunchable.LunchMoney.get_transaction) | Get a Transaction by ID |
+| GET | [get_transactions](#lunchable.LunchMoney.get_transactions) | Get Transactions Using Criteria |
+| GET | [get_user](#lunchable.LunchMoney.get_user) | Get Personal User Details |
+| POST | [insert_asset](#lunchable.LunchMoney.insert_asset) | Create a single (manually-managed) asset |
+| POST | [insert_category](#lunchable.LunchMoney.insert_category) | Create a Spending Category |
+| POST | [insert_category_group](#lunchable.LunchMoney.insert_category_group) | Create a Spending Category Group |
+| POST | [insert_into_category_group](#lunchable.LunchMoney.insert_into_category_group) | Add to a Category Group |
+| POST | [insert_transaction_group](#lunchable.LunchMoney.insert_transaction_group) | Create a Transaction Group of Two or More Transactions |
+| POST | [insert_transactions](#lunchable.LunchMoney.insert_transactions) | Create One or Many Lunch Money Transactions |
+| POST | [unsplit_transactions](#lunchable.LunchMoney.unsplit_transactions) | Unsplit Transactions |
+| PUT | [upsert_budget](#lunchable.LunchMoney.upsert_budget) | Upsert a Budget for a Category and Date |
+| PUT | [update_asset](#lunchable.LunchMoney.update_asset) | Update a Single Asset |
+| PUT | [update_category](#lunchable.LunchMoney.update_category) | Update a single category |
+| PUT | [update_crypto](#lunchable.LunchMoney.update_crypto) | Update a Manual Crypto Asset |
+| PUT | [update_transaction](#lunchable.LunchMoney.update_transaction) | Update a Transaction |
+| DELETE | [remove_budget](#lunchable.LunchMoney.remove_budget) | Unset an Existing Budget for a Particular Category in a Particular Month |
+| DELETE | [remove_category](#lunchable.LunchMoney.remove_category) | Delete a single category |
+| DELETE | [remove_category_force](#lunchable.LunchMoney.remove_category_force) | Forcefully delete a single category |
+| DELETE | [remove_transaction_group](#lunchable.LunchMoney.remove_transaction_group) | Delete a Transaction Group |
+
+## Low Level Methods
+
+| Name | Description |
+|------------------------------------------------------------|------------------------------------------------|
+| [amake_request](#lunchable.LunchMoney.amake_request) | Make an async HTTP Request and return its data |
+| [arequest](#lunchable.LunchMoney.arequest) | Make an async HTTP Request |
+| [make_request](#lunchable.LunchMoney.make_request) | Make an HTTP Request and return its data |
+| [process_response](#lunchable.LunchMoney.process_response) | Process a Lunch Money Response |
+| [request](#lunchable.LunchMoney.request) | Make an HTTP Request |
+
+## Attributes
+
+| Name | Description |
+|------------------------------------------------------|----------------------------------|
+| [async_session](#lunchable.LunchMoney.async_session) | Authenticated Async HTTPX Client |
+| [session](#lunchable.LunchMoney.session) | Authenticated HTTPX Client |
+
+## Class Documentation
+
+::: lunchable.LunchMoney
+ handler: python
+ options:
+ show_bases: false
+ allow_inspection: true
+ inherited_members: true
+ group_by_category: true
+ heading_level: 3
+ show_source: false
diff --git a/docs/plugins/index.md b/docs/plugins/index.md
new file mode 100644
index 00000000..ae715937
--- /dev/null
+++ b/docs/plugins/index.md
@@ -0,0 +1,10 @@
+# Plugins
+
+lunchable supports plugins with other, external, services. 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.
+
+### [PushLunch](pushlunch.md): Push Notifications via Pushover
+
+### [SplitLunch](splitlunch.md): Splitwise Integration
+
+### [PrimeLunch](primelunch.md): Amazon Transaction Updater
diff --git a/docs/source/primelunch.md b/docs/plugins/primelunch.md
similarity index 71%
rename from docs/source/primelunch.md
rename to docs/plugins/primelunch.md
index e690b89e..0e335294 100644
--- a/docs/source/primelunch.md
+++ b/docs/plugins/primelunch.md
@@ -32,7 +32,7 @@ the [Amazon Order History Reporter](https://chrome.google.com/webstore/detail/am
plugin because it gives us some functionality that Amazon doesn't: exporting Amazon transactions as they're
grouped on actual credit card transactions.
-## Run via the [Lunchable CLI](cli.md#lunchable-cli)
+## Run via the [Lunchable CLI](../cli.md#lunchable-cli)
You can install lunchable with [pip](https://pypi.org/project/lunchable/) or
[pipx](https://pypa.github.io/pipx/):
@@ -51,18 +51,20 @@ The below command runs the `PrimeLunch` update tool:
lunchable plugins primelunch run -f ~/Downloads/amazon_order_history.csv
```
-:::{important}
-The commands on this documentation correspond to running `PrimeLunch` on a
-Mac or Linux Machine. If you are a Windows user take note of the following items:
-
-- Multiline commands in Windows machines use the `^` character instead of `\` to escape new lines
-- On macs, the default CSV file is located at `~/Downloads/amazon_order_history.csv`, on Windows this file
- is located someplace like `C:\Users\YourUserName\Downloads\amazon_order_history.csv`
-- It's recommended you use the new
- [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701)
- to run `PrimeLunch`
-
-:::
+> IMPORTANT: **Windows Users**
+>
+> The commands on this documentation correspond to running on a
+> Mac or Linux Machine. If you are a Windows user take note of the following items:
+>
+> - Multiline commands in Windows machines use the `^` character instead of `\` to escape new lines
+> - On macs, the default CSV file is located at `~/Downloads/amazon_order_history.csv`, on Windows this file
+> is located someplace like `C:\Users\YourUserName\Downloads\amazon_order_history.csv`
+> - My personal recommendation is to use the
+> [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701)
+> along with
+> [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux) to
+> access a Linux shell on your Windows machine. This will allow you to use the commands
+> as written.
The below command runs the `PrimeLunch` update tool using a date window of fourteen days
instead of the default seven days (these larger windows are especially useful for finding refunds and recurring
@@ -93,11 +95,13 @@ lunchable plugins primelunch run \
## Command Line Documentation
-```{eval-rst}
-.. click:: lunchable._cli:primelunch
- :prog: lunchable plugins primelunch
- :nested: full
-```
+::: mkdocs-click
+ :module: lunchable.plugins.primelunch.primelunch
+ :command: run_primelunch
+ :prog_name: lunchable plugins primelunch run
+ :style: table
+ :list_subcommands: True
+
## References
diff --git a/docs/plugins/pushlunch.md b/docs/plugins/pushlunch.md
new file mode 100644
index 00000000..8591b8ec
--- /dev/null
+++ b/docs/plugins/pushlunch.md
@@ -0,0 +1,51 @@
+# PushLunch: Push Notifications via Pushover
+
+
+
+
+
+
+
+
+---
+
+`PushLunch` supports Push Notifications via [Pushover](https://pushover.net). Pushover supports iOS
+and Android Push notifications. To get started just provide your Pushover
+`User Key` directly or via the `PUSHOVER_USER_KEY` environment variable.
+
+## Run via the Lunchable CLI
+
+The below command checks for un-reviewed transactions in the current period
+and sends them as Push Notifications. The `--continuous` flag tells it to run
+forever which will only send you a push notification once for each transaction.
+By default it will check every 60 minutes, but this can be changed using the
+`--interval` argument.
+
+```console
+lunchable plugins pushlunch notify --continuous
+```
+
+## Run via Docker
+
+```console
+docker run --rm \
+ --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
+ --env PUSHOVER_USER_KEY=${PUSHOVER_USER_KEY} \
+ juftin/lunchable:latest \
+ lunchable plugins pushlunch notify --continuous
+```
+
+## Run via Python
+
+```python
+from lunchable.plugins.pushlunch import PushLunch
+```
+
+::: lunchable.plugins.pushlunch.PushLunch
+ handler: python
+ options:
+ show_bases: false
+ allow_inspection: true
+ heading_level: 3
diff --git a/docs/plugins/splitlunch.md b/docs/plugins/splitlunch.md
new file mode 100644
index 00000000..e184ec1a
--- /dev/null
+++ b/docs/plugins/splitlunch.md
@@ -0,0 +1,114 @@
+# SplitLunch: Splitwise Integration
+
+
+
+
+
+
+
+
+---
+
+## Integrations
+
+This plugin supports different operations, and some of those operations have prerequisites:
+
+### Auto Importer
+
+It supports the auto-importing of Splitwise expenses into Lunch Money transactions. This requires
+a manual asset exist in your Lunch Money account with "Splitwise" in the Name. Expenses that
+have been deleted or which don't impact you (i.e. are only between other users in your group)
+are skipped. By default, payments and expenses for which you are recorded as the payer are
+skipped as well, but these can be overridden by the `--allow-payments` and `--allow-self-paid`
+CLI flags, respectively.
+
+#### Prerequisites
+
+- Accounts:
+ - Splitwise must be in the account name
+
+### LunchMoney -> Splitwise
+
+It supports the creation of Splitwise transactions directly from synced Lunch Money accounts. This syncing requires you create a tag called `SplitLunchImport`. Transactions with this tag will be created in Splitwise with your "financial partner". Once transactions are created in Splitwise they will be split in half in Lunch Money. Half of the split will be marked in the `Reimbursement` category which must be created.
+
+#### Prerequisites
+
+- Financial Partners:
+ - If you only have one friend in Splitwise, this is your Financial Partner
+ - Financial Partners can be individual users or groups and transactions will be split accordingly
+ - Financial Partners must be specified by their Splitwise Group ID, Splitwise User ID, or Email Address
+- Tags:
+ - `SplitLunchImport`
+- Categories:
+ - `Reimbursement`
+
+### SplitLunch
+
+It supports a workflow where you mark transactions as split (identical to `Lunch Money -> Splitwise`) without importing them into Splitwise. This syncing requires you create a tag called `SplitLunch` and a category named `Reimbursement`
+
+#### Prerequisites
+
+- Tags:
+ - `SplitLunch`
+- Categories:
+ - `Reimbursement`
+
+### LunchMoney -> Splitwise (without splitting)
+
+It supports the creation of Splitwise transactions directly from synced Lunch Money accounts. This syncing requires you create a tag called `SplitLunchDirectImport`. Transactions with this tag will be created in Splitwise with the total completely owed by your "financial partner". The entire transaction wil then be categorized as `Reimbursement` without being split.
+
+#### Prerequisites
+
+- Financial Partners:
+ - If you only have one friend in Splitwise, this is your Financial Partner
+ - Financial Partners can be individual users or groups and transactions will be split accordingly
+ - Financial Partners must be specified by their Splitwise Group ID, Splitwise User ID, or Email Address
+- Tags:
+ - `SplitLunchDirectImport`
+- Categories:
+ - `Reimbursement`
+
+> **Note:** Some of the above scenarios allow for tagging of a `Splitwise` tag on updated transactions. This tag must be created for this functionality to work.
+
+## Installation
+
+```shell
+pip install lunchable[splitlunch]
+```
+
+## Run the SplitLunch plugin for the Lunchable CLI
+
+```shell
+lunchable plugins splitlunch --help
+```
+
+## Run the SplitLunch plugin for the Lunchable CLI via Docker
+
+```shell
+docker pull juftin/lunchable
+```
+
+```shell
+docker run \
+ --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
+ --env SPLITWISE_CONSUMER_KEY=${SPLITWISE_CONSUMER_KEY} \
+ --env SPLITWISE_CONSUMER_SECRET=${SPLITWISE_CONSUMER_SECRET} \
+ --env SPLITWISE_API_KEY=${SPLITWISE_API_KEY} \
+ juftin/lunchable:latest \
+ lunchable plugins splitlunch --help
+```
+
+## Run via Python
+
+```python
+from lunchable.plugins.splitlunch import SplitLunch
+```
+
+::: lunchable.plugins.splitlunch.SplitLunch
+ handler: python
+ options:
+ show_bases: false
+ allow_inspection: true
+ heading_level: 3
diff --git a/docs/requirements.txt b/docs/requirements.txt
deleted file mode 100644
index ca6a57a6..00000000
--- a/docs/requirements.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-sphinx
-sphinx-rtd-theme
-autodocsumm
-myst-parser[linkify]
-autoclasstoc
-sphinx-copybutton
-sphinx_autodoc_defaultargs
-sphinxcontrib-apidoc
-sphinx-click
-sphinx-autodoc-typehints
-autodoc_pydantic
-
-requests
-pydantic
-splitwise
-python-dateutil
-click
diff --git a/docs/source/cli.md b/docs/source/cli.md
deleted file mode 100644
index d83c7233..00000000
--- a/docs/source/cli.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# lunchable CLI
-
-```{eval-rst}
-.. click:: lunchable._cli:cli
- :prog: lunchable
- :nested: full
-```
diff --git a/docs/source/cli.rst b/docs/source/cli.rst
deleted file mode 100644
index 1141e676..00000000
--- a/docs/source/cli.rst
+++ /dev/null
@@ -1,182 +0,0 @@
-##################
-Lunchable CLI
-##################
-
-.. code-block:: console
-
- Usage: lunchable [OPTIONS] COMMAND [ARGS]...
-
- Interactions with Lunch Money via lunchable 🍱
-
- Options:
- --version Show the version and exit.
- --access-token TEXT LunchMoney Developer API Access Token
- --debug / --no-debug Enable extra debugging output
- --help Show this message and exit.
-
- Commands:
- http Interact with the LunchMoney API
- plugins Interact with Lunchable Plugins
- transactions Interact with Lunch Money transactions
-
-******************
-Installation
-******************
-
-To use lunchable on the command line, first install it using `pip `_ or
-`pipx `_:
-
-.. code-block:: console
-
- pip install lunchable
-
-
-or, if you're using the Splitwise plugin on the CLI:
-
-.. code-block:: console
-
- pip install "lunchable[splitwise]"
-
-Install Shell Completion
-=========================
-
-
-bash
-###################
-
-.. code-block:: console
-
- _LUNCHABLE_COMPLETE=bash_source lunchable > ~/.lunchable-complete.bash
- echo "[[ ! -f ~/.lunchable-complete.bash ]] || source ~/.lunchable-complete.bash" >> ~/.bashrc
-
-zsh
-###################
-
-.. code-block:: console
-
- _LUNCHABLE_COMPLETE=zsh_source lunchable > ~/.lunchable-complete.zsh
- echo "[[ ! -f ~/.lunchable-complete.zsh ]] || source ~/.lunchable-complete.zsh" >> ~/.zshrc
-
-Run via Docker
-==============
-
-.. code-block:: console
-
- docker pull juftin/lunchable
-
-.. code-block:: console
-
- docker run \
- --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
- juftin/lunchable:latest \
- lunchable transactions get --limit 5
-
-******************
-Examples
-******************
-
-.. code-block:: console
-
- pip install --upgrade lunchable
- export LUNCHMONEY_ACCESS_TOKEN="xxxxxxxxxxx"
-
-.. code-block:: console
-
- lunchable http /v1/me
-
-.. code-block:: json
-
- {
- "user_name": "Justin Flannery",
- "user_email": "Justin@example.com",
- "user_id": 99999,
- "account_id": 99999,
- "budget_name": "🤖 Justin",
- "api_key_label": "Testing"
- }
-
-.. code-block:: console
-
- lunchable http -X GET https://dev.lunchmoney.app/v1/assets
-
-.. code-block:: json
-
- {
- "assets": [
- {
- "id": 99999,
- "type_name": "cash",
- "subtype_name": "digital wallet (paypal, venmo)",
- "name": "Test Account",
- "display_name": "Test Account",
- "balance": "190.2100",
- "balance_as_of": "2022-04-23T07:23:20.000Z",
- "closed_on": "2022-04-23",
- "currency": "usd",
- "institution_name": "Test",
- "exclude_transactions": true,
- "created_at": "2021-09-20T05:32:29.060Z"
- }
- ]
- }
-
-.. code-block:: console
-
- lunchable http -X PUT /v1/assets/99999 --data '{"balance": 200.00}'
-
-.. code-block:: json
-
- {
- "id": 99999,
- "type_name": "cash",
- "subtype_name": "digital wallet (paypal, venmo)",
- "name": "Test Account",
- "display_name": "Test Account",
- "balance": "200.0000",
- "balance_as_of": "2022-10-21T04:22:50.391Z",
- "closed_on": "2022-04-23",
- "currency": "usd",
- "institution_name": "Test",
- "exclude_transactions": true,
- "created_at": "2021-09-20T05:32:29.060Z"
- }
-
-.. code-block:: console
-
- lunchable transactions get --limit 1 --start-date 2022-09-07 --end-date 2022-09-15 | jq
-
-.. code-block:: json
-
- [
- {
- "id": 120998527,
- "date": "2022-09-07",
- "payee": "Ally Bank",
- "amount": -87.5,
- "currency": "usd",
- "notes": "ATCO Transfer",
- "category_id": 229148,
- "asset_id": null,
- "plaid_account_id": 41573,
- "status": "cleared",
- "parent_id": null,
- "is_group": false,
- "group_id": null,
- "tags": null,
- "external_id": null,
- "original_name": "Internet transfer from Interest Checking account XXXXXX2045",
- "type": null,
- "subtype": null,
- "fees": null,
- "price": null,
- "quantity": null
- }
- ]
-
-******************
-Documentation
-******************
-
-.. click:: lunchable._cli:cli
- :prog: lunchable
- :nested: full
diff --git a/docs/source/conf.py b/docs/source/conf.py
deleted file mode 100644
index 16f0434b..00000000
--- a/docs/source/conf.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""
-Sphinx Documentation Generator
-"""
-
-import sys
-from datetime import datetime
-from pathlib import Path
-
-_project_path = Path(__file__).resolve().parent.parent.parent
-_project_dir = str(_project_path)
-sys.path.insert(0, _project_dir)
-
-from lunchable._version import __application__, __author__, __version__ # noqa
-
-_author = __author__
-project = __application__
-copyright = f"{datetime.now().year}, {_author}"
-author = _author
-release = version = __version__
-
-extensions = [
- "sphinx.ext.napoleon",
- "sphinx.ext.autodoc",
- "sphinx.ext.autosummary",
- "sphinx.ext.coverage",
- "sphinx.ext.viewcode",
- "sphinx.ext.intersphinx",
- "sphinx.ext.autosectionlabel",
- "sphinxcontrib.apidoc",
- "sphinxcontrib.autodoc_pydantic",
- "autodocsumm",
- "myst_parser",
- "autoclasstoc",
- "sphinx_copybutton",
- "sphinx_autodoc_typehints",
- "sphinx_autodoc_defaultargs",
- "sphinx_click",
-]
-
-myst_heading_anchors = 5
-myst_enable_extensions = [
- "amsmath",
- "colon_fence",
- "deflist",
- "dollarmath",
- "fieldlist",
- "html_admonition",
- "html_image",
- "linkify",
- "replacements",
- "smartquotes",
- "strikethrough",
- "substitution",
- "tasklist",
-]
-
-templates_path = ["_templates"]
-html_static_path = ["_static"]
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
-
-html_theme = "sphinx_rtd_theme"
-html_theme_options = {
- "display_version": True,
-}
-html_show_sphinx = False
-html_show_sourcelink = False
-# html_logo = "_static/lunchable.png"
-html_favicon = "https://juftin.com/favicon.ico"
-
-source_suffix = {
- ".rst": "restructuredtext",
- ".txt": "markdown",
- ".md": "markdown",
-}
-
-autosummary_generate = True
-intersphinx_mapping = {
- "python": ("https://docs.python.org/3", None),
-}
-
-source_code_dir = _project_path.joinpath("lunchable")
-apidoc_module_dir = str(source_code_dir)
-apidoc_output_dir = str(source_code_dir.parent.joinpath("docs", "source", "api"))
-apidoc_excluded_paths = ["tests"]
-apidoc_separate_modules = True
-
-autodoc_member_order = "bysource"
-autodoc_pydantic_model_show_json = False
-autodoc_pydantic_settings_show_json = False
-
-always_document_default_args = True
-docstring_default_arg_substitution = "**[Default]:**"
-
-autodoc_default_options = {"exclude-members": "with_traceback"}
-
-html_favicon = "https://juftin.com/favicon.ico"
-html_context = {
- "display_github": True,
- "github_user": "juftin",
- "github_repo": "lunchable",
- "github_version": "main/docs/source/",
-}
diff --git a/docs/source/index.rst b/docs/source/index.rst
deleted file mode 100644
index 39073f95..00000000
--- a/docs/source/index.rst
+++ /dev/null
@@ -1,83 +0,0 @@
-**lunchable**
-===================================
-
-.. image:: https://i.imgur.com/FyKDsG3.png
- :width: 400
- :align: center
- :alt: lunchable
- :target: https://github.com/juftin/lunchable
-
-**lunchable** is a Python Client for the
-`Lunch Money Developer API `_.
-It's built on top of `pydantic `_
-and `httpx `_,
-it offers an *intuitive* API, a *simple* CLI,
-complete coverage of all endpoints,
-and *plugins* to other external services
-
-.. image:: https://img.shields.io/pypi/v/lunchable?color=blue&label=lunchable
- :target: https://github.com/juftin/lunchable
- :alt: Lunchable Version
-.. image:: https://img.shields.io/pypi/pyversions/lunchable
- :target: https://pypi.python.org/pypi/lunchable/
- :alt: PyPI
-.. image:: https://img.shields.io/docker/v/juftin/lunchable?color=blue&label=docker&logo=docker
- :target: https://hub.docker.com/r/juftin/lunchable
- :alt: Docker
-.. image:: https://github.com/juftin/lunchable/actions/workflows/tests.yaml/badge.svg?branch=main
- :target: https://github.com/juftin/lunchable/actions/workflows/tests.yaml?query=branch%3Amain
- :alt: Testing Status
-.. image:: https://img.shields.io/github/license/juftin/lunchable?color=blue&label=License
- :target: https://github.com/juftin/lunchable/blob/main/LICENSE
- :alt: GitHub License
-
-.. code-block:: console
-
- pip install lunchable
-
-.. code-block:: python
-
- from typing import Any, Dict, List
-
- from lunchable import LunchMoney
- from lunchable.models import TransactionObject
-
- lunch = LunchMoney(access_token="xxxxxxxxxxx")
- transactions: List[TransactionObject] = lunch.get_transactions()
-
- first_transaction: TransactionObject = transactions[0]
- transaction_as_dict: Dict[str, Any] = first_transaction.model_dump()
-
-.. code-block:: console
-
- export LUNCHMONEY_ACCESS_TOKEN="xxxxxxxxxxx"
- lunchable transactions get --limit 5
-
-.. toctree::
- :maxdepth: 1
- :caption: Contents:
-
- usage.rst
- lunchable.rst
- plugins.rst
- Command Line Interface ⌨️
- API Documentation 🤖
- Contributing 👥
- GitHub 🛠
- Changelog 📝
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
-
-
-.. image:: https://raw.githubusercontent.com/juftin/juftin/main/static/juftin.png
- :width: 60
- :height: 60
- :align: center
- :alt: juftin
- :target: https://github.com/juftin
diff --git a/docs/source/lunchable.rst b/docs/source/lunchable.rst
deleted file mode 100644
index 9e25538c..00000000
--- a/docs/source/lunchable.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-.. _interacting-with-lunchable:
-
-Interacting with Lunch Money
-===================================
-
-.. code-block:: python
-
- from lunchable import LunchMoney
-
- lunch = LunchMoney(access_token="xxxxxxx")
-
-.. currentmodule:: lunchable
-
-.. autoclass:: LunchMoney
- :members:
- :inherited-members:
- :autosummary:
- :toctree:
diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst
deleted file mode 100644
index 1da70045..00000000
--- a/docs/source/plugins.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-====================
-Plugins
-====================
-
-lunchable supports plugins with other, external, services.
-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.
-
-.. toctree::
- :maxdepth: 1
-
- pushlunch.rst
- splitlunch.rst
- primelunch.rst
diff --git a/docs/source/pushlunch.rst b/docs/source/pushlunch.rst
deleted file mode 100644
index 5f9bfdc2..00000000
--- a/docs/source/pushlunch.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-###########################################
-PushLunch: Push Notifications via Pushover
-###########################################
-
-|pic1| |pic2|
-
-.. |pic1| image:: https://pushover.net/images/pushover-logo.svg
- :width: 35%
- :alt: pushover
- :target: https://www.splitwise.com/
-
-.. |pic2| image:: https://i.imgur.com/FyKDsG3.png
- :width: 60%
- :alt: lunchable
- :target: https://github.com/juftin/lunchable
-
---------
-
-`PushLunch` supports Push Notifications via `Pushover `_. Pushover
-supports iOS and Android Push notifications. To get started just provide your Pushover
-`User Key` directly or via the `PUSHOVER_USER_KEY` environment variable.
-
-
-Run via the :ref:`Lunchable CLI`
-================================
-
-The below command checks for un-reviewed transactions in the current period and
-sends them as Push Notifications. The `--continuous` flag tells it to run forever which
-will only send you a push notifaction once for each transaction.
-By default it will check every 60 minutes, but this can be changed using the `--interval`
-argument.
-
-.. code-block:: console
-
- lunchable plugins pushlunch notify --continuous
-
-Run via Docker
-===============
-
-.. code-block:: console
-
- docker run --rm \
- --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
- --env PUSHOVER_USER_KEY=${PUSHOVER_USER_KEY} \
- juftin/lunchable:latest \
- lunchable plugins pushlunch notify --continuous
-
-Run via Python
-===============
-
-.. currentmodule:: lunchable.plugins.pushlunch
-
-.. autoclass:: PushLunch
- :members:
- :autosummary:
- :toctree: _autosummary
diff --git a/docs/source/splitlunch.rst b/docs/source/splitlunch.rst
deleted file mode 100644
index d490b6a7..00000000
--- a/docs/source/splitlunch.rst
+++ /dev/null
@@ -1,138 +0,0 @@
-#################################
-SplitLunch: Splitwise Integration
-#################################
-
-|pic1| |pic2|
-
-.. |pic1| image:: https://assets.splitwise.com/assets/core/logo-square.svg
- :width: 35%
- :alt: splitwise
- :target: https://www.splitwise.com/
-
-.. |pic2| image:: https://i.imgur.com/FyKDsG3.png
- :width: 60%
- :alt: lunchable
- :target: https://github.com/juftin/lunchable
-
---------
-
-Integrations
-============
-
-This plugin supports different operations, and some of those operations
-have prerequisites:
-
-Auto Importer
--------------
-It supports the auto-importing of Splitwise expenses into Lunch Money transactions. This requires a
-manual asset exist in your Lunch Money account with "Splitwise" in the Name. Expenses that have been
-deleted or which don't impact you (i.e. are only between other users in your group) are skipped. By
-default, payments and expenses for which you are recorded as the payer are skipped as well, but
-these can be overridden by the `--allow-payments` and `--allow-self-paid` CLI flags, respectively.
-
- Prerequisites:
- - Accounts:
- - Splitwise must be in the account name
-
-
-
-LunchMoney -> Splitwise
------------------------
-It supports the creation of Splitwise transactions directly from synced Lunch Money
-accounts. This syncing requires you create a tag called `SplitLunchImport`. Transactions
-with this tag will be created in Splitwise with your "financial partner". Once transactions
-are created in Splitwise they will be split in half in Lunch Money. Half of the split will be
-marked in the `Reimbursement` category which must be created.
-
- Prerequisites:
- - Financial Partners:
- - If you only have one friend in Splitwise, this is your Financial Partner
- - Financial Partners can be individual users or groups and transactions will be split accordingly
- - Financial Partners must be specified by their Splitwise Group ID, Splitwise User ID, or Email Address
- - Tags:
- - `SplitLunchImport`
- - Categories:
- - `Reimbursement`
-
-SplitLunch
-----------
-It supports a workflow where you mark transactions as split
-(identical to `Lunch Money -> Splitwise`) without importing them into Splitwise.
-This syncing requires you create a tag called `SplitLunch` and a category named
-`Reimbursement`
-
- Prerequisites:
- - Tags:
- - `SplitLunch`
- - Categories:
- - `Reimbursement`
-
-LunchMoney -> Splitwise (without splitting)
--------------------------------------------
-It supports the creation of Splitwise transactions directly from synced Lunch Money
-accounts. This syncing requires you create a tag called `SplitLunchDirectImport`. Transactions
-with this tag will be created in Splitwise with the total completely owed by your
-"financial partner". The entire transaction wil then be categorized as `Reimbursement` without
-being split.
-
- Prerequisites:
- - Financial Partners:
- - If you only have one friend in Splitwise, this is your Financial Partner
- - Financial Partners can be individual users or groups and transactions will be split accordingly
- - Financial Partners must be specified by their Splitwise Group ID, Splitwise User ID, or Email Address
- - Tags:
- - `SplitLunchDirectImport`
- - Categories:
- - `Reimbursement`
-
-.. note::
-
- Some of the above scenarios allow for tagging of a `Splitwise`
- tag on updated transactions. This tag must be created for this
- functionality to work.
-
-Installation
-============
-
-.. code-block:: console
-
- pip install lunchable[splitlunch]
-
-Run the SplitLunch plugin for the :ref:`Lunchable CLI`
-======================================================
-
-.. code-block:: console
-
- lunchable plugins splitlunch --help
-
-
-Run the SplitLunch plugin for the :ref:`Lunchable CLI` via Docker
-==================================================================
-
-.. code-block:: console
-
- docker pull juftin/lunchable
-
-.. code-block:: console
-
- docker run \
- --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
- --env SPLITWISE_CONSUMER_KEY=${SPLITWISE_CONSUMER_KEY} \
- --env SPLITWISE_CONSUMER_SECRET=${SPLITWISE_CONSUMER_SECRET} \
- --env SPLITWISE_API_KEY=${SPLITWISE_API_KEY} \
- juftin/lunchable:latest \
- lunchable plugins splitlunch --help
-
-Run via Python
-===============
-
-.. code-block:: python
-
- from lunchable.plugins.splitlunch import SplitLunch
-
-.. currentmodule:: lunchable.plugins.splitlunch
-
-.. autoclass:: SplitLunch
- :members:
- :autosummary:
- :toctree: _autosummary
diff --git a/docs/source/usage.rst b/docs/source/usage.rst
deleted file mode 100644
index dd8c30c1..00000000
--- a/docs/source/usage.rst
+++ /dev/null
@@ -1,115 +0,0 @@
-##################
-Usage
-##################
-
-******************
-Installation
-******************
-
-To use lunchable, first install it using pip:
-
-.. code-block:: console
-
- pip install lunchable
-
-******************
-Examples
-******************
-
-Read more about :ref:`interacting-with-lunchable` to see what
-else you can do.
-
-Transactions
-==================
-
-Retrieve a list of :class:`.TransactionObject`
-----------------------------------------------------------------------
-
-.. code-block:: python
-
- from lunchable import LunchMoney
-
- lunch = LunchMoney(access_token="xxxxxxx")
- transactions = lunch.get_transactions(start_date="2020-01-01",
- end_date="2020-01-31")
-
-
-Retrieve a single transaction (:class:`.TransactionObject`)
-----------------------------------------------------------------------
-
-.. code-block:: python
-
- from lunchable import LunchMoney
-
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction = lunch.get_transaction(transaction_id=1234)
-
-
-The above code returns a :class:`.TransactionObject` with ID # 1234 (assuming it exists)
-
-Update a transaction with a :class:`.TransactionUpdateObject`
-----------------------------------------------------------------------
-
-.. code-block:: python
-
- from datetime import datetime
-
- from lunchable import LunchMoney, TransactionUpdateObject
-
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction_note = f"Updated on {datetime.now()}"
- notes_update = TransactionUpdateObject(notes=transaction_note)
- response = lunch.update_transaction(transaction_id=1234,
- transaction=notes_update)
-
-Update a :class:`.TransactionObject` with itself
-----------------------------------------------------------------------
-
-.. code-block:: python
-
- from datetime import datetime, timedelta
-
- from lunchable import LunchMoney
-
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction = lunch.get_transaction(transaction_id=1234)
-
- transaction.notes = f"Updated on {datetime.now()}"
- transaction.date = transaction.date + timedelta(days=1)
- response = lunch.update_transaction(transaction_id=transaction.id,
- transaction=transaction)
-
-Create a new transaction with a :class:`.TransactionInsertObject`
-----------------------------------------------------------------------
-
-.. code-block:: python
-
- from lunchable import LunchMoney, TransactionInsertObject
-
- lunch = LunchMoney(access_token="xxxxxxx")
-
- new_transaction = TransactionInsertObject(payee="Example Restaurant",
- amount=120.00,
- notes="Saturday Dinner")
- new_transaction_ids = lunch.insert_transactions(transactions=new_transaction)
-
-Use the :ref:`Lunchable CLI`
-----------------------------
-
-.. code-block:: console
-
- lunchable transactions get --limit 5
-
-Use the :ref:`Lunchable CLI` via Docker
----------------------------------------
-
-.. code-block:: console
-
- docker pull juftin/lunchable
-
-.. code-block:: console
-
- docker run \
- --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
- juftin/lunchable:latest \
- lunchable transactions get --limit 5
diff --git a/docs/source/_static/lunchable.png b/docs/static/lunchable.png
similarity index 100%
rename from docs/source/_static/lunchable.png
rename to docs/static/lunchable.png
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 00000000..c814bcb9
--- /dev/null
+++ b/docs/usage.md
@@ -0,0 +1,130 @@
+# Usage
+
+## Installation
+
+To use lunchable, first install it using pip:
+
+```shell
+pip install lunchable
+```
+
+## Client
+
+The [LunchMoney](interacting.md#interacting-with-lunch-money) client is the main entrypoint
+for interacting with the Lunch Money API. It defaults to inheriting the `LUNCHMONEY_ACCESS_TOKEN`
+environment variable, but can be created with an explicit `access_token` parameter.
+
+```python
+from lunchable import LunchMoney
+
+lunch = LunchMoney(access_token="xxxxxxx")
+```
+
+Read more about [Interacting with Lunch Money](interacting.md#lunchmoney)
+to see what else you can do.
+
+# Transactions
+
+## Retrieve a list of [`TransactionObject`][lunchable.models.transactions.TransactionObject]
+
+```python
+from __future__ import annotations
+
+from lunchable import LunchMoney
+from lunchable.models import TransactionObject
+
+lunch = LunchMoney(access_token="xxxxxxx")
+transactions: list[TransactionObject] = lunch.get_transactions(
+ start_date="2020-01-01",
+ end_date="2020-01-31"
+)
+```
+
+## Retrieve a single transaction ([`TransactionObject`][lunchable.models.transactions.TransactionObject])
+
+```python
+from lunchable import LunchMoney
+from lunchable.models import TransactionObject
+
+lunch = LunchMoney(access_token="xxxxxxx")
+transaction: TransactionObject = lunch.get_transaction(transaction_id=1234)
+```
+
+The above code returns a TransactionObject with ID # 1234 (assuming it exists)
+
+## Update a transaction with a [`TransactionUpdateObject`][lunchable.models.transactions.TransactionUpdateObject]
+
+```python
+from __future__ import annotations
+
+from datetime import datetime
+from typing import Any
+
+from lunchable import LunchMoney
+from lunchable.models import TransactionUpdateObject
+
+lunch = LunchMoney(access_token="xxxxxxx")
+transaction_note = f"Updated on {datetime.now()}"
+notes_update = TransactionUpdateObject(notes=transaction_note)
+response: dict[str, Any] = lunch.update_transaction(
+ transaction_id=1234,
+ transaction=notes_update
+)
+```
+
+## Update a TransactionObject with itself
+
+```python
+from datetime import datetime, timedelta
+
+from lunchable import LunchMoney
+from lunchable.models import TransactionObject
+
+lunch = LunchMoney(access_token="xxxxxxx")
+transaction: TransactionObject = lunch.get_transaction(transaction_id=1234)
+
+transaction.notes = f"Updated on {datetime.now()}"
+transaction.date = transaction.date + timedelta(days=1)
+response = lunch.update_transaction(
+ transaction_id=transaction.id,
+ transaction=transaction
+)
+```
+
+## Create a new transaction with a [`TransactionInsertObject`][lunchable.models.transactions.TransactionInsertObject]
+
+`transactions` can be a single [`TransactionInsertObject`][lunchable.models.transactions.TransactionInsertObject]
+or a list of [`TransactionInsertObject`][lunchable.models.transactions.TransactionInsertObject].
+
+```python
+from lunchable import LunchMoney
+from lunchable.models import TransactionInsertObject
+
+lunch = LunchMoney(access_token="xxxxxxx")
+
+new_transaction = TransactionInsertObject(
+ payee="Example Restaurant",
+ amount=120.00,
+ notes="Saturday Dinner"
+)
+new_transaction_ids = lunch.insert_transactions(transactions=new_transaction)
+```
+
+## Use the Lunchable CLI
+
+```shell
+lunchable transactions get --limit 5
+```
+
+## Use the Lunchable CLI via Docker
+
+```shell
+docker pull juftin/lunchable
+```
+
+```shell
+docker run \
+ --env LUNCHMONEY_ACCESS_TOKEN=${LUNCHMONEY_ACCESS_TOKEN} \
+ juftin/lunchable:latest \
+ lunchable transactions get --limit 5
+```
diff --git a/lunchable/models/_base.py b/lunchable/models/_base.py
index bee6c903..daa8efd1 100644
--- a/lunchable/models/_base.py
+++ b/lunchable/models/_base.py
@@ -2,7 +2,19 @@
Base Pydantic Object for Containers
"""
-from pydantic import BaseModel
+import textwrap
+from typing import Any
+
+from pydantic import BaseModel, Field
+
+
+def LunchableField(*args: Any, **kwargs: Any) -> Any:
+ """
+ Custom Field for Lunchable Models
+ """
+ if "description" in kwargs:
+ kwargs["description"] = textwrap.dedent(kwargs["description"]).strip()
+ return Field(*args, **kwargs)
class LunchableModel(BaseModel):
diff --git a/lunchable/models/_core.py b/lunchable/models/_core.py
index 236b2ff6..aa6836d0 100644
--- a/lunchable/models/_core.py
+++ b/lunchable/models/_core.py
@@ -158,28 +158,32 @@ def request(
Examples
--------
A recent use of this method was to delete a Tag (which isn't available via the
- Developer API yet) ::
-
- import lunchable
-
- lunch = lunchable.LunchMoney()
-
- # Get All the Tags
- all_tags = lunch.get_tags()
- # Get All The Null Tags (a list of 1 or zero)
- null_tags = [tag for tag in all_tags if tag.name in [None, ""]]
-
- # Create a Cookie dictionary from a browser session
- cookies = {"cookie_keys": "cookie_values"}
-
- for null_tag in null_tags:
- # use the httpx.client embedded in the class to make a request with cookies
- response = lunch.request(
- method="DELETE",
- url=f"https://api.lunchmoney.app/tags/{null_tag.id}",
- cookies=cookies)
- # raise an error for 4XX responses
- response.raise_for_status()
+ Developer API yet)
+
+ ```python
+ import lunchable
+
+ lunch = lunchable.LunchMoney()
+
+ # Get All the Tags
+ all_tags = lunch.get_tags()
+ # Get All The Null Tags (a list of 1 or zero)
+ null_tags = [tag for tag in all_tags if tag.name in [None, ""]]
+
+ # Create a Cookie dictionary from a browser session
+ cookies = {"cookie_keys": "cookie_values"}
+ del lunch.session.headers["authorization"]
+
+ for null_tag in null_tags:
+ # use the httpx.client embedded in the class to make a request with cookies
+ response = lunch.request(
+ method=lunch.Methods.DELETE,
+ url=f"https://api.lunchmoney.app/tags/{null_tag.id}",
+ cookies=cookies
+ )
+ # raise an error for 4XX responses
+ response.raise_for_status()
+ ```
"""
response = self.session.request(
method=method,
diff --git a/lunchable/models/_lunchmoney.py b/lunchable/models/_lunchmoney.py
index 9127749d..f0fcbfb8 100644
--- a/lunchable/models/_lunchmoney.py
+++ b/lunchable/models/_lunchmoney.py
@@ -38,18 +38,21 @@ class LunchMoney(
Lunch Money Python Client.
This class facilitates with connections to
- the `Lunch Money Developer API `_. Authenticate
+ the [Lunch Money Developer API](https://lunchmoney.dev/). Authenticate
with an Access Token. If an access token isn't provided one will attempt to
be inherited from a `LUNCHMONEY_ACCESS_TOKEN` environment variable.
Examples
--------
- ::
+ ```python
+ from __future__ import annotations
- from lunchable import LunchMoney
+ from lunchable import LunchMoney
+ from lunchable.models import TransactionObject
- lunch = LunchMoney(access_token="xxxxxxx")
- transactions = lunch.get_transactions()
+ lunch = LunchMoney(access_token="xxxxxxx")
+ transactions: list[TransactionObject] = lunch.get_transactions()
+ ```
"""
def __init__(self, access_token: Optional[str] = None):
diff --git a/lunchable/models/assets.py b/lunchable/models/assets.py
index 2465d371..8491bd0b 100644
--- a/lunchable/models/assets.py
+++ b/lunchable/models/assets.py
@@ -8,9 +8,10 @@
import logging
from typing import List, Optional, Union
-from pydantic import Field, field_validator
+from pydantic import field_validator
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/models/budgets.py b/lunchable/models/budgets.py
index da78bb9b..4c1db307 100644
--- a/lunchable/models/budgets.py
+++ b/lunchable/models/budgets.py
@@ -8,9 +8,8 @@
import logging
from typing import Any, Dict, List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
@@ -140,7 +139,9 @@ def get_budgets(
"""
params = BudgetParamsGet(start_date=start_date, end_date=end_date).model_dump()
response_data = self.make_request(
- method="GET", url_path=[APIConfig.LUNCHMONEY_BUDGET], params=params
+ method=self.Methods.GET,
+ url_path=[APIConfig.LUNCHMONEY_BUDGET],
+ params=params,
)
budget_objects = [BudgetObject.model_validate(item) for item in response_data]
return budget_objects
@@ -191,7 +192,9 @@ def upsert_budget(
currency=currency,
).model_dump(exclude_none=True)
response_data = self.make_request(
- method="PUT", url_path=[APIConfig.LUNCHMONEY_BUDGET], payload=body
+ method=self.Methods.PUT,
+ url_path=[APIConfig.LUNCHMONEY_BUDGET],
+ payload=body,
)
return response_data["category_group"]
@@ -216,6 +219,8 @@ def remove_budget(self, start_date: datetime.date, category_id: int) -> bool:
start_date=start_date, category_id=category_id
).model_dump()
response_data = self.make_request(
- method="DELETE", url_path=[APIConfig.LUNCHMONEY_BUDGET], params=params
+ method=self.Methods.DELETE,
+ url_path=[APIConfig.LUNCHMONEY_BUDGET],
+ params=params,
)
return response_data
diff --git a/lunchable/models/categories.py b/lunchable/models/categories.py
index cd4557ee..2266738c 100644
--- a/lunchable/models/categories.py
+++ b/lunchable/models/categories.py
@@ -9,10 +9,9 @@
import logging
from typing import List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
from lunchable.exceptions import LunchMoneyError
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/models/crypto.py b/lunchable/models/crypto.py
index 24562087..c35d1d45 100644
--- a/lunchable/models/crypto.py
+++ b/lunchable/models/crypto.py
@@ -8,9 +8,8 @@
import logging
from typing import List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/models/plaid_accounts.py b/lunchable/models/plaid_accounts.py
index b185210c..7e4f1870 100644
--- a/lunchable/models/plaid_accounts.py
+++ b/lunchable/models/plaid_accounts.py
@@ -8,9 +8,8 @@
import logging
from typing import List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/models/recurring_expenses.py b/lunchable/models/recurring_expenses.py
index cfaff58c..898c4e71 100644
--- a/lunchable/models/recurring_expenses.py
+++ b/lunchable/models/recurring_expenses.py
@@ -8,9 +8,8 @@
import logging
from typing import List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
@@ -159,7 +158,7 @@ def get_recurring_expenses(
start_date=start_date, debit_as_negative=debit_as_negative
).model_dump()
response_data = self.make_request(
- method="GET",
+ method=self.Methods.GET,
url_path=[APIConfig.LUNCH_MONEY_RECURRING_EXPENSES],
params=params,
)
diff --git a/lunchable/models/tags.py b/lunchable/models/tags.py
index 22defdbb..fde127e2 100644
--- a/lunchable/models/tags.py
+++ b/lunchable/models/tags.py
@@ -7,9 +7,8 @@
import logging
from typing import List, Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/models/transactions.py b/lunchable/models/transactions.py
index 8cbc1642..7c9dd417 100644
--- a/lunchable/models/transactions.py
+++ b/lunchable/models/transactions.py
@@ -9,10 +9,9 @@
from enum import Enum
from typing import Any, Dict, List, Optional, Union
-from pydantic import Field
-
from lunchable import LunchMoneyError
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
from lunchable.models.tags import TagsObject
@@ -512,13 +511,16 @@ def get_transactions(
Examples
--------
- Retrieve a list of :class:`.TransactionObject` ::
+ Retrieve a list of
+ [TransactionObject][lunchable.models.transactions.TransactionObject]
- from lunchable import LunchMoney
+ ```python
+ from lunchable import LunchMoney
- lunch = LunchMoney(access_token="xxxxxxx")
- transactions = lunch.get_transactions(start_date="2020-01-01",
- end_date="2020-01-31")
+ lunch = LunchMoney(access_token="xxxxxxx")
+ transactions = lunch.get_transactions(start_date="2020-01-01",
+ end_date="2020-01-31")
+ ```
"""
search_params = _TransactionParamsGet(
tag_id=tag_id,
@@ -563,15 +565,18 @@ def get_transaction(self, transaction_id: int) -> TransactionObject:
Examples
--------
- Retrieve a single transaction by its ID ::
+ Retrieve a single transaction by its ID
- from lunchable import LunchMoney
+ ```python
+ from lunchable import LunchMoney
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction = lunch.get_transaction(transaction_id=1234)
+ lunch = LunchMoney(access_token="xxxxxxx")
+ transaction = lunch.get_transaction(transaction_id=1234)
+ ```
- The above code returns a :class:`.TransactionObject` with ID # 1234 (assuming
- it exists)
+ The above code returns a
+ [TransactionObject][lunchable.models.transactions.TransactionObject]
+ with ID # 1234 (assuming it exists)
"""
response_data = self.make_request(
method=self.Methods.GET,
@@ -630,31 +635,38 @@ def update_transaction(
Examples
--------
- Update a transaction with a :class:`.TransactionUpdateObject` ::
+ Update a transaction with a
+ [TransactionUpdateObject][lunchable.models.transactions.TransactionUpdateObject]
- from datetime import datetime
+ ```python
+ from datetime import datetime
- from lunchable import LunchMoney, TransactionUpdateObject
+ from lunchable import LunchMoney, TransactionUpdateObject
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction_note = f"Updated on {datetime.now()}"
- notes_update = TransactionUpdateObject(notes=transaction_note)
- response = lunch.update_transaction(transaction_id=1234,
- transaction=notes_update)
+ lunch = LunchMoney(access_token="xxxxxxx")
+ transaction_note = f"Updated on {datetime.now()}"
+ notes_update = TransactionUpdateObject(notes=transaction_note)
+ response = lunch.update_transaction(transaction_id=1234,
+ transaction=notes_update)
+ ```
- Update a :class:`.TransactionObject` with itself ::
+ Update a
+ [TransactionObject][lunchable.models.transactions.TransactionObject]
+ with itself
- from datetime import datetime, timedelta
+ ```python
+ from datetime import datetime, timedelta
- from lunchable import LunchMoney
+ from lunchable import LunchMoney
- lunch = LunchMoney(access_token="xxxxxxx")
- transaction = lunch.get_transaction(transaction_id=1234)
+ lunch = LunchMoney(access_token="xxxxxxx")
+ transaction = lunch.get_transaction(transaction_id=1234)
- transaction.notes = f"Updated on {datetime.now()}"
- transaction.date = transaction.date + timedelta(days=1)
- response = lunch.update_transaction(transaction_id=transaction.id,
- transaction=transaction)
+ transaction.notes = f"Updated on {datetime.now()}"
+ transaction.date = transaction.date + timedelta(days=1)
+ response = lunch.update_transaction(transaction_id=transaction.id,
+ transaction=transaction)
+ ```
"""
payload = _TransactionUpdateParamsPut(
split=split,
@@ -720,16 +732,19 @@ def insert_transactions(
Examples
--------
- Create a new transaction with a :class:`.TransactionInsertObject` ::
+ Create a new transaction with a
+ [TransactionInsertObject][lunchable.models.transactions.TransactionInsertObject]
- from lunchable import LunchMoney, TransactionInsertObject
+ ```python
+ from lunchable import LunchMoney, TransactionInsertObject
- lunch = LunchMoney(access_token="xxxxxxx")
+ lunch = LunchMoney(access_token="xxxxxxx")
- new_transaction = TransactionInsertObject(payee="Example Restaurant",
- amount=120.00,
- notes="Saturday Dinner")
- new_transaction_ids = lunch.insert_transactions(transactions=new_transaction)
+ new_transaction = TransactionInsertObject(payee="Example Restaurant",
+ amount=120.00,
+ notes="Saturday Dinner")
+ new_transaction_ids = lunch.insert_transactions(transactions=new_transaction)
+ ```
"""
insert_objects = []
if not isinstance(transactions, list):
diff --git a/lunchable/models/user.py b/lunchable/models/user.py
index 6830368c..2bbf35b9 100644
--- a/lunchable/models/user.py
+++ b/lunchable/models/user.py
@@ -7,9 +7,8 @@
import logging
from typing import Optional
-from pydantic import Field
-
from lunchable._config import APIConfig
+from lunchable.models._base import LunchableField as Field
from lunchable.models._base import LunchableModel
from lunchable.models._core import LunchMoneyAPIClient
diff --git a/lunchable/plugins/base/base_app.py b/lunchable/plugins/base/base_app.py
index 27735b99..03d656ab 100644
--- a/lunchable/plugins/base/base_app.py
+++ b/lunchable/plugins/base/base_app.py
@@ -320,6 +320,8 @@ def refresh_transactions(
"""
Refresh App data with the latest transactions
+ Parameters
+ ----------
start_date: Optional[Union[datetime.date, datetime.datetime, str]]
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.
diff --git a/lunchable/plugins/primelunch/primelunch.py b/lunchable/plugins/primelunch/primelunch.py
index c3492ab6..90679cd3 100644
--- a/lunchable/plugins/primelunch/primelunch.py
+++ b/lunchable/plugins/primelunch/primelunch.py
@@ -18,9 +18,13 @@
from rich import print, table
from rich.prompt import Confirm
-from lunchable import TransactionUpdateObject
from lunchable._config.logging_config import set_up_logging
-from lunchable.models import CategoriesObject, TransactionObject, UserObject
+from lunchable.models import (
+ CategoriesObject,
+ TransactionObject,
+ TransactionUpdateObject,
+ UserObject,
+)
from lunchable.plugins.base.pandas_app import LunchablePandasApp
logger = logging.getLogger(__name__)
diff --git a/lunchable/plugins/splitlunch/lunchmoney_splitwise.py b/lunchable/plugins/splitlunch/lunchmoney_splitwise.py
index 94b56b1c..ddfeff7a 100644
--- a/lunchable/plugins/splitlunch/lunchmoney_splitwise.py
+++ b/lunchable/plugins/splitlunch/lunchmoney_splitwise.py
@@ -72,75 +72,6 @@ def _get_splitwise_impact(
class SplitLunch(splitwise.Splitwise):
"""
Lunchable Plugin For Interacting With Splitwise
-
- This plugin supports different operations, and some of those operations
- have prerequisites:
-
- 1) It supports the auto-importing of Splitwise expenses into Lunch Money
- transactions. This requires a manual asset exist in your Lunch Money
- account with "Splitwise" in the Name.
-
- Prerequisites:
- - Accounts:
- - Splitwise must be in the account name
-
- 2) It supports the creation of Splitwise transactions directly from synced Lunch Money
- accounts. This syncing requires you create a tag called `SplitLunchImport`. Transactions
- with this tag will be created in Splitwise with your "financial partner". Once transactions
- are created in Splitwise they will be split in half in Lunch Money. Half of the split will be
- marked in the `Reimbursement` category which must be created.
-
- Prerequisites:
- - Financial Partners:
- - If you only have one friend in Splitwise, this is your Financial Partner
- - Financial Partners must be specified by their Splitwise ID or Email Address
- - Tags:
- - `SplitLunchImport`
- - Categories:
- - `Reimbursement`
-
- 3) It supports a workflow where you mark transactions as split (identical to scenario #2)
- without importing them into Splitwise. This syncing requires you create a tag
- called `SplitLunch` and a category named `Reimbursement`
-
- Prerequisites:
- - Tags:
- - `SplitLunch`
- - Categories:
- - `Reimbursement`
-
- 4) It supports a workflow where you mark transactions as `Reimbursed` and import them to
- Splitwise with the total completely owed by your financial partner.
-
- Prerequisites:
- - Financial Partners:
- - If you only have one friend in Splitwise, this is your Financial Partner
- - Financial Partners must be specified by their Splitwise ID or Email Address
- - Tags:
- - `SplitLunchDirectImport`
- - Categories:
- - `Reimbursement`
-
- Parameters
- ----------
- financial_partner_id: Optional[int]
- Splitwise User ID of financial partner
- financial_partner_email: Optional[str]
- Splitwise linked email address of financial partner
- financial_partner_group_id: Optional[int]
- Splitwise Group ID for financial partner transactions
- consumer_key: Optional[str]
- Consumer Key provided by Splitwise. Defaults to `SPLITWISE_CONSUMER_KEY` environment
- variable
- consumer_secret: Optional[str]
- Consumer Key provided by Splitwise. Defaults to `SPLITWISE_CONSUMER_SECRET`
- environment variable
- api_key: Optional[str]
- Consumer Key provided by Splitwise. Defaults to `SPLITWISE_API_KEY` environment
- variable.
- lunchable_client: LunchMoney
- Instantiated LunchMoney object to use as internal client. One will
- be created using environment variables otherwise.
"""
def __init__(
diff --git a/mkdocs.yaml b/mkdocs.yaml
new file mode 100644
index 00000000..7916f84b
--- /dev/null
+++ b/mkdocs.yaml
@@ -0,0 +1,86 @@
+# $schema: https://squidfunk.github.io/mkdocs-material/schema.json
+
+site_name: lunchable
+nav:
+- Home 🏠: index.md
+- Usage 📖: usage.md
+- LunchMoney 🍽️: interacting.md
+- Plugins 🧩:
+ - plugins/index.md
+ - PushLunch 📲: plugins/pushlunch.md
+ - SplitLunch 🍱: plugins/splitlunch.md
+ - PrimeLunch 🥪: plugins/primelunch.md
+- Command Line Interface ⌨️: cli.md
+- API Documentation 🤖: reference/
+- Contributing 🤝: contributing.md
+theme:
+ favicon: https://juftin.com/favicon.ico
+ logo: https://raw.githubusercontent.com/juftin/juftin/main/static/juftin.png
+ name: material
+ features:
+ - navigation.tracking
+ - content.code.annotate
+ - content.code.copy
+ - navigation.indexes
+ palette:
+ - media: '(prefers-color-scheme: light)'
+ scheme: default
+ accent: purple
+ toggle:
+ icon: material/weather-sunny
+ name: Switch to dark mode
+ - media: '(prefers-color-scheme: dark)'
+ scheme: slate
+ primary: black
+ toggle:
+ icon: material/weather-night
+ name: Switch to light mode
+repo_url: https://github.com/juftin/lunchable
+repo_name: lunchable
+edit_uri: blob/main/docs/
+site_author: juftin
+remote_branch: gh-pages
+#copyright: Copyright © 2023 Justin Flannery
+extra:
+ generator: false
+markdown_extensions:
+- toc:
+ permalink: '#'
+- pymdownx.snippets:
+- pymdownx.magiclink
+- attr_list
+- md_in_html
+- pymdownx.highlight:
+ anchor_linenums: true
+- pymdownx.inlinehilite
+- pymdownx.superfences
+- markdown.extensions.attr_list
+- pymdownx.keys
+- pymdownx.tasklist
+- pymdownx.tilde
+- callouts
+- pymdownx.details
+- pymdownx.emoji
+- pymdownx.tabbed:
+ alternate_style: true
+- mkdocs-click:
+plugins:
+- search
+- markdown-exec
+- section-index
+- autorefs
+- mkdocstrings:
+ handlers:
+ python:
+ import:
+ - https://docs.python.org/3/objects.inv
+ - https://pandas.pydata.org/docs/objects.inv
+ options:
+ docstring_style: numpy
+ extensions:
+ - griffe_fieldz
+- gen-files:
+ scripts:
+ - docs/gen_pages.py
+- literate-nav:
+ nav_file: SUMMARY.md
diff --git a/pyproject.toml b/pyproject.toml
index 828c3e0f..3a42f547 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -81,7 +81,10 @@ parallel = true
source_pkgs = ["lunchable", "tests"]
[tool.hatch.env]
-requires = ["hatch-pip-compile"]
+requires = ["hatch-pip-compile", "hatch-mkdocs"]
+
+[tool.hatch.env.collectors.mkdocs.docs]
+path = "mkdocs.yaml"
[tool.hatch.envs.all]
pip-compile-constraint = ""
@@ -103,30 +106,11 @@ cov = "hatch run test:cov {args:}"
test = "hatch run test:test {args:}"
[tool.hatch.envs.docs]
-dependencies = [
- "sphinx",
- "sphinx-rtd-theme",
- "autodocsumm",
- "myst-parser[linkify]",
- "autoclasstoc",
- "sphinx-copybutton",
- "sphinx_autodoc_defaultargs",
- "sphinxcontrib-apidoc",
- "sphinx-click",
- "sphinx-autodoc-typehints",
- "autodoc_pydantic"
-]
-
-[tool.hatch.envs.docs.scripts]
-build = [
- "rm -rf docs/_build/ && rm -rf docs/source/api/",
- "cd docs && make html"
-]
-deploy = ["mkdocs gh-deploy {args:}"]
-serve = [
- "build",
- "python -m http.server --bind 0.0.0.0 -d docs/_build/html/"
+detached = false
+extra-dependencies = [
+ "griffe-fieldz"
]
+type = "pip-compile"
[tool.hatch.envs.gen]
detached = true
diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt
index ec9a4db9..520a3b7a 100644
--- a/requirements/requirements-docs.txt
+++ b/requirements/requirements-docs.txt
@@ -3,17 +3,19 @@
#
# [constraints] requirements.txt (SHA256: 1d7f43c7c1ecbc074ece84ddfddf590007473fce44e813c9dca4481292391f67)
#
-# - sphinx
-# - sphinx-rtd-theme
-# - autodocsumm
-# - myst-parser[linkify]
-# - autoclasstoc
-# - sphinx-copybutton
-# - sphinx_autodoc_defaultargs
-# - sphinxcontrib-apidoc
-# - sphinx-click
-# - sphinx-autodoc-typehints
-# - autodoc_pydantic
+# - markdown-callouts
+# - markdown-exec
+# - mkdocs
+# - mkdocs-autorefs
+# - mkdocs-click
+# - mkdocs-gen-files
+# - mkdocs-literate-nav
+# - mkdocs-material
+# - mkdocs-section-index
+# - mkdocstrings
+# - mkdocstrings-python
+# - pymdown-extensions
+# - griffe-fieldz
# - click>=8.0.1
# - httpx
# - pydantic<3,>=2
@@ -23,8 +25,6 @@
# - splitwise<3.0.0,>=2.3.0
#
-alabaster==0.7.13
- # via sphinx
annotated-types==0.6.0
# via
# -c requirements.txt
@@ -33,14 +33,8 @@ anyio==4.1.0
# via
# -c requirements.txt
# httpx
-autoclasstoc==1.6.0
- # via hatch.envs.docs
-autodoc-pydantic==2.0.1
- # via hatch.envs.docs
-autodocsumm==0.2.11
- # via hatch.envs.docs
babel==2.14.0
- # via sphinx
+ # via mkdocs-material
certifi==2023.11.17
# via
# -c requirements.txt
@@ -55,14 +49,23 @@ click==8.1.7
# via
# -c requirements.txt
# hatch.envs.docs
- # sphinx-click
-docutils==0.20.1
- # via
- # autoclasstoc
- # myst-parser
- # sphinx
- # sphinx-click
- # sphinx-rtd-theme
+ # mkdocs
+ # mkdocs-click
+ # mkdocstrings
+colorama==0.4.6
+ # via
+ # griffe
+ # mkdocs-material
+fieldz==0.0.2
+ # via griffe-fieldz
+ghp-import==2.1.0
+ # via mkdocs
+griffe==0.38.1
+ # via
+ # griffe-fieldz
+ # mkdocstrings-python
+griffe-fieldz==0.1.1
+ # via hatch.envs.docs
h11==0.14.0
# via
# -c requirements.txt
@@ -81,31 +84,69 @@ idna==3.6
# anyio
# httpx
# requests
-imagesize==1.4.1
- # via sphinx
jinja2==3.1.2
# via
- # myst-parser
- # sphinx
-linkify-it-py==2.0.2
- # via myst-parser
+ # mkdocs
+ # mkdocs-material
+ # mkdocstrings
+markdown==3.5.1
+ # via
+ # markdown-callouts
+ # mkdocs
+ # mkdocs-autorefs
+ # mkdocs-click
+ # mkdocs-material
+ # mkdocstrings
+ # pymdown-extensions
+markdown-callouts==0.3.0
+ # via hatch.envs.docs
+markdown-exec==1.7.0
+ # via hatch.envs.docs
markdown-it-py==3.0.0
# via
# -c requirements.txt
- # mdit-py-plugins
- # myst-parser
# rich
markupsafe==2.1.3
- # via jinja2
-mdit-py-plugins==0.4.0
- # via myst-parser
+ # via
+ # jinja2
+ # mkdocs
+ # mkdocstrings
mdurl==0.1.2
# via
# -c requirements.txt
# markdown-it-py
-more-itertools==10.1.0
- # via autoclasstoc
-myst-parser==2.0.0
+mergedeep==1.3.4
+ # via mkdocs
+mkdocs==1.5.3
+ # via
+ # hatch.envs.docs
+ # mkdocs-autorefs
+ # mkdocs-gen-files
+ # mkdocs-literate-nav
+ # mkdocs-material
+ # mkdocs-section-index
+ # mkdocstrings
+mkdocs-autorefs==0.5.0
+ # via
+ # hatch.envs.docs
+ # mkdocstrings
+mkdocs-click==0.8.1
+ # via hatch.envs.docs
+mkdocs-gen-files==0.5.0
+ # via hatch.envs.docs
+mkdocs-literate-nav==0.6.1
+ # via hatch.envs.docs
+mkdocs-material==9.5.2
+ # via hatch.envs.docs
+mkdocs-material-extensions==1.3.1
+ # via mkdocs-material
+mkdocs-section-index==0.3.8
+ # via hatch.envs.docs
+mkdocstrings==0.24.0
+ # via
+ # hatch.envs.docs
+ # mkdocstrings-python
+mkdocstrings-python==1.7.5
# via hatch.envs.docs
numpy==1.26.2
# via
@@ -116,48 +157,62 @@ oauthlib==3.2.2
# -c requirements.txt
# requests-oauthlib
packaging==23.2
- # via sphinx
+ # via mkdocs
+paginate==0.5.6
+ # via mkdocs-material
pandas==2.1.4
# via
# -c requirements.txt
# hatch.envs.docs
-pbr==6.0.0
- # via sphinxcontrib-apidoc
+pathspec==0.12.1
+ # via mkdocs
+platformdirs==4.1.0
+ # via
+ # mkdocs
+ # mkdocstrings
pydantic==2.5.2
# via
# -c requirements.txt
# hatch.envs.docs
- # autodoc-pydantic
- # pydantic-settings
pydantic-core==2.14.5
# via
# -c requirements.txt
# pydantic
-pydantic-settings==2.1.0
- # via autodoc-pydantic
pygments==2.17.2
# via
# -c requirements.txt
+ # mkdocs-material
# rich
- # sphinx
+pymdown-extensions==10.5
+ # via
+ # hatch.envs.docs
+ # markdown-exec
+ # mkdocs-material
+ # mkdocstrings
python-dateutil==2.8.2
# via
# -c requirements.txt
# hatch.envs.docs
+ # ghp-import
# pandas
-python-dotenv==1.0.0
- # via pydantic-settings
pytz==2023.3.post1
# via
# -c requirements.txt
# pandas
pyyaml==6.0.1
- # via myst-parser
+ # via
+ # mkdocs
+ # pymdown-extensions
+ # pyyaml-env-tag
+pyyaml-env-tag==0.1
+ # via mkdocs
+regex==2023.10.3
+ # via mkdocs-material
requests==2.31.0
# via
# -c requirements.txt
+ # mkdocs-material
# requests-oauthlib
- # sphinx
# splitwise
requests-oauthlib==1.3.1
# via
@@ -176,53 +231,6 @@ sniffio==1.3.0
# -c requirements.txt
# anyio
# httpx
-snowballstemmer==2.2.0
- # via sphinx
-sphinx==7.2.6
- # via
- # hatch.envs.docs
- # autoclasstoc
- # autodoc-pydantic
- # autodocsumm
- # myst-parser
- # sphinx-autodoc-defaultargs
- # sphinx-autodoc-typehints
- # sphinx-click
- # sphinx-copybutton
- # sphinx-rtd-theme
- # sphinxcontrib-apidoc
- # sphinxcontrib-applehelp
- # sphinxcontrib-devhelp
- # sphinxcontrib-htmlhelp
- # sphinxcontrib-jquery
- # sphinxcontrib-qthelp
- # sphinxcontrib-serializinghtml
-sphinx-autodoc-defaultargs==0.1.2
- # via hatch.envs.docs
-sphinx-autodoc-typehints==1.25.2
- # via hatch.envs.docs
-sphinx-click==5.1.0
- # via hatch.envs.docs
-sphinx-copybutton==0.5.2
- # via hatch.envs.docs
-sphinx-rtd-theme==2.0.0
- # via hatch.envs.docs
-sphinxcontrib-apidoc==0.4.0
- # via hatch.envs.docs
-sphinxcontrib-applehelp==1.0.7
- # via sphinx
-sphinxcontrib-devhelp==1.0.5
- # via sphinx
-sphinxcontrib-htmlhelp==2.0.4
- # via sphinx
-sphinxcontrib-jquery==4.1
- # via sphinx-rtd-theme
-sphinxcontrib-jsmath==1.0.1
- # via sphinx
-sphinxcontrib-qthelp==1.0.6
- # via sphinx
-sphinxcontrib-serializinghtml==1.1.9
- # via sphinx
splitwise==2.5.0
# via
# -c requirements.txt
@@ -230,15 +238,16 @@ splitwise==2.5.0
typing-extensions==4.9.0
# via
# -c requirements.txt
+ # fieldz
# pydantic
# pydantic-core
tzdata==2023.3
# via
# -c requirements.txt
# pandas
-uc-micro-py==1.0.2
- # via linkify-it-py
urllib3==2.1.0
# via
# -c requirements.txt
# requests
+watchdog==3.0.0
+ # via mkdocs