spendctl 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spendctl-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- spendctl-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- spendctl-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +20 -0
- spendctl-0.1.0/.github/dependabot.yml +10 -0
- spendctl-0.1.0/.github/workflows/ci.yml +31 -0
- spendctl-0.1.0/.github/workflows/release.yml +42 -0
- spendctl-0.1.0/.gitignore +39 -0
- spendctl-0.1.0/.pre-commit-config.yaml +7 -0
- spendctl-0.1.0/ARCHITECTURE.md +155 -0
- spendctl-0.1.0/CHANGELOG.md +38 -0
- spendctl-0.1.0/CODE_OF_CONDUCT.md +39 -0
- spendctl-0.1.0/CONTRIBUTING.md +137 -0
- spendctl-0.1.0/LICENSE +21 -0
- spendctl-0.1.0/PKG-INFO +142 -0
- spendctl-0.1.0/README.md +107 -0
- spendctl-0.1.0/SECURITY.md +32 -0
- spendctl-0.1.0/pyproject.toml +65 -0
- spendctl-0.1.0/src/spendctl/__init__.py +3 -0
- spendctl-0.1.0/src/spendctl/__main__.py +5 -0
- spendctl-0.1.0/src/spendctl/cli.py +477 -0
- spendctl-0.1.0/src/spendctl/config.py +157 -0
- spendctl-0.1.0/src/spendctl/dashboard/__init__.py +0 -0
- spendctl-0.1.0/src/spendctl/dashboard/app.py +16 -0
- spendctl-0.1.0/src/spendctl/dashboard/helpers.py +111 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/1_dashboard.py +572 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/2_spending.py +192 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/3_debt.py +355 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/4_transactions.py +325 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/5_subscriptions.py +189 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/6_loans.py +227 -0
- spendctl-0.1.0/src/spendctl/dashboard/pages/7_report.py +333 -0
- spendctl-0.1.0/src/spendctl/db.py +112 -0
- spendctl-0.1.0/src/spendctl/init_wizard.py +487 -0
- spendctl-0.1.0/src/spendctl/models.py +86 -0
- spendctl-0.1.0/src/spendctl/queries/__init__.py +81 -0
- spendctl-0.1.0/src/spendctl/queries/_utils.py +24 -0
- spendctl-0.1.0/src/spendctl/queries/budget.py +172 -0
- spendctl-0.1.0/src/spendctl/queries/check_ins.py +149 -0
- spendctl-0.1.0/src/spendctl/queries/dashboard.py +135 -0
- spendctl-0.1.0/src/spendctl/queries/reports.py +265 -0
- spendctl-0.1.0/src/spendctl/queries/subscriptions.py +94 -0
- spendctl-0.1.0/src/spendctl/queries/transactions.py +163 -0
- spendctl-0.1.0/src/spendctl/schema.sql +103 -0
- spendctl-0.1.0/tests/__init__.py +0 -0
- spendctl-0.1.0/tests/conftest.py +112 -0
- spendctl-0.1.0/tests/test_budget.py +112 -0
- spendctl-0.1.0/tests/test_check_ins.py +175 -0
- spendctl-0.1.0/tests/test_cli.py +582 -0
- spendctl-0.1.0/tests/test_config.py +268 -0
- spendctl-0.1.0/tests/test_dashboard.py +147 -0
- spendctl-0.1.0/tests/test_db.py +289 -0
- spendctl-0.1.0/tests/test_init_wizard.py +214 -0
- spendctl-0.1.0/tests/test_reports.py +176 -0
- spendctl-0.1.0/tests/test_subscriptions.py +141 -0
- spendctl-0.1.0/tests/test_transactions.py +168 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Report a bug or unexpected behavior
|
|
4
|
+
title: '[BUG] '
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Bug Description
|
|
10
|
+
<!-- A clear description of what the bug is -->
|
|
11
|
+
|
|
12
|
+
## Steps to Reproduce
|
|
13
|
+
1. Run command: `spendctl ...`
|
|
14
|
+
2. See error: ...
|
|
15
|
+
3. ...
|
|
16
|
+
|
|
17
|
+
## Expected Behavior
|
|
18
|
+
<!-- What you expected to happen -->
|
|
19
|
+
|
|
20
|
+
## Actual Behavior
|
|
21
|
+
<!-- What actually happened -->
|
|
22
|
+
|
|
23
|
+
## Environment
|
|
24
|
+
- macOS version:
|
|
25
|
+
- Python version: (`python3 --version`)
|
|
26
|
+
- spendctl version: (`spendctl --version`)
|
|
27
|
+
|
|
28
|
+
## Error Messages
|
|
29
|
+
```
|
|
30
|
+
<!-- Paste any error messages or stack traces here -->
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Additional Context
|
|
34
|
+
<!-- Any other relevant information -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Suggest a new feature or enhancement
|
|
4
|
+
title: '[FEATURE] '
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Feature Description
|
|
10
|
+
<!-- Clear description of the feature you'd like to see -->
|
|
11
|
+
|
|
12
|
+
## Why is this useful?
|
|
13
|
+
<!-- Explain the use case and who would benefit -->
|
|
14
|
+
|
|
15
|
+
## Example Usage
|
|
16
|
+
<!-- Show how you envision using this feature -->
|
|
17
|
+
```bash
|
|
18
|
+
spendctl ...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Alternatives Considered
|
|
22
|
+
<!-- Are there workarounds or alternative approaches? -->
|
|
23
|
+
|
|
24
|
+
## Additional Context
|
|
25
|
+
<!-- Any other relevant information, mockups, or examples -->
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## What does this PR do?
|
|
2
|
+
|
|
3
|
+
<!-- Brief description of the change -->
|
|
4
|
+
|
|
5
|
+
## Why is this needed?
|
|
6
|
+
|
|
7
|
+
<!-- What problem does it solve? Link to related issue if applicable -->
|
|
8
|
+
|
|
9
|
+
## How was this tested?
|
|
10
|
+
|
|
11
|
+
- [ ] Existing tests pass (`pytest`)
|
|
12
|
+
- [ ] New tests added (if applicable)
|
|
13
|
+
- [ ] Manually tested (`spendctl ...`)
|
|
14
|
+
|
|
15
|
+
## Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Code follows existing patterns in the codebase
|
|
18
|
+
- [ ] No unnecessary runtime dependencies added
|
|
19
|
+
- [ ] Documentation updated (if user-facing change)
|
|
20
|
+
- [ ] Lint passes (`ruff check src/ tests/`)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- run: pip install ruff
|
|
18
|
+
- run: ruff check src/ tests/
|
|
19
|
+
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
strategy:
|
|
23
|
+
matrix:
|
|
24
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
- run: pip install -e ".[dev]"
|
|
31
|
+
- run: pytest --cov --cov-report=term-missing
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Extract version from tag
|
|
19
|
+
id: version
|
|
20
|
+
run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
21
|
+
|
|
22
|
+
- name: Extract changelog for this version
|
|
23
|
+
id: changelog
|
|
24
|
+
run: |
|
|
25
|
+
version="${GITHUB_REF#refs/tags/v}"
|
|
26
|
+
awk "/^## \\[${version}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md > /tmp/release-notes.md
|
|
27
|
+
echo "Release notes:"
|
|
28
|
+
cat /tmp/release-notes.md
|
|
29
|
+
|
|
30
|
+
- name: Create GitHub Release
|
|
31
|
+
uses: softprops/action-gh-release@v2
|
|
32
|
+
with:
|
|
33
|
+
name: ${{ steps.version.outputs.version }}
|
|
34
|
+
body_path: /tmp/release-notes.md
|
|
35
|
+
draft: false
|
|
36
|
+
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') }}
|
|
37
|
+
|
|
38
|
+
- name: Build package
|
|
39
|
+
run: pip install build && python -m build
|
|
40
|
+
|
|
41
|
+
- name: Publish to PyPI
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# Testing & Coverage
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Linting
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
|
|
20
|
+
# Database (personal financial data)
|
|
21
|
+
*.db
|
|
22
|
+
|
|
23
|
+
# Streamlit
|
|
24
|
+
.streamlit/
|
|
25
|
+
|
|
26
|
+
# macOS
|
|
27
|
+
.DS_Store
|
|
28
|
+
**/.DS_Store
|
|
29
|
+
|
|
30
|
+
# Editors
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
|
|
37
|
+
# Local development
|
|
38
|
+
CLAUDE.md
|
|
39
|
+
uv.lock
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
This document explains the high-level design of spendctl for contributors and maintainers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
spendctl is a Python CLI that tracks personal finances using SQLite. It provides both a command-line interface and a 7-page Streamlit dashboard for visualization.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
User ──► CLI (argparse) ──► Query Modules ──► SQLite Database
|
|
11
|
+
User ──► Dashboard (Streamlit) ──► Query Modules ──► SQLite Database
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
All data operations flow through the query modules — neither the CLI nor the dashboard issue raw SQL directly (except for a few dashboard-specific aggregations in `helpers.py`).
|
|
15
|
+
|
|
16
|
+
## Directory Structure
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
src/spendctl/
|
|
20
|
+
├── __init__.py # Version string
|
|
21
|
+
├── __main__.py # python -m spendctl entry point
|
|
22
|
+
├── cli.py # Argparse CLI with 13 commands, --json on every command
|
|
23
|
+
├── config.py # Paths, accounts, categories, budgets, accessor functions
|
|
24
|
+
├── db.py # Connection management, schema init, backup
|
|
25
|
+
├── init_wizard.py # Interactive setup wizard and config editing
|
|
26
|
+
├── models.py # Dataclasses (Transaction, CheckIn, Subscription)
|
|
27
|
+
├── schema.sql # DDL: 9 tables, 1 view, 9 indexes
|
|
28
|
+
├── queries/
|
|
29
|
+
│ ├── __init__.py # Public API re-exports
|
|
30
|
+
│ ├── _utils.py # Shared date utilities (default_month, month_range)
|
|
31
|
+
│ ├── budget.py # budget_vs_actual, remaining_budget, total_income, cushion
|
|
32
|
+
│ ├── check_ins.py # add_check_in, get_latest, reconcile
|
|
33
|
+
│ ├── dashboard.py # account_balance, all_balances, net_worth, debt_progress
|
|
34
|
+
│ ├── reports.py # monthly_summary, cash_flow, spending_by_category
|
|
35
|
+
│ ├── subscriptions.py # CRUD for recurring subscriptions
|
|
36
|
+
│ └── transactions.py # CRUD, filtering, duplicate detection
|
|
37
|
+
└── dashboard/
|
|
38
|
+
├── app.py # Streamlit entry point (page config + redirect)
|
|
39
|
+
├── helpers.py # Shared formatting utilities, month selector widget
|
|
40
|
+
└── pages/
|
|
41
|
+
├── 1_dashboard.py # Overview: sparklines, net worth gauge, budget summary
|
|
42
|
+
├── 2_spending.py # Category pie/bar charts, budget alerts
|
|
43
|
+
├── 3_debt.py # Progress bars, payoff projections, interest savings
|
|
44
|
+
├── 4_transactions.py # Filterable transaction table
|
|
45
|
+
├── 5_subscriptions.py # Active/paused/canceled subscription list
|
|
46
|
+
├── 6_loans.py # Student loan payoff tracking
|
|
47
|
+
└── 7_report.py # Printable monthly budget report
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Config System
|
|
51
|
+
|
|
52
|
+
Config lives at `~/.config/spendctl/config.json` and is created by `spendctl init`. The file defines:
|
|
53
|
+
|
|
54
|
+
- **accounts** — list of account objects with name, type, institution, APR
|
|
55
|
+
- **categories** — list with name, group, and monthly budget amount
|
|
56
|
+
- **income_sources** — expected monthly income by name and amount
|
|
57
|
+
- **targets** — financial goals (target date, emergency fund target)
|
|
58
|
+
- **min_payments** — minimum payments keyed by account name
|
|
59
|
+
- **loans** — optional loan detail groups for student loan tracking
|
|
60
|
+
|
|
61
|
+
`config.py` uses lazy loading: the first call to any accessor function reads and caches the JSON file. Subsequent calls return from cache. After `config edit`, `reload_config()` clears the cache and re-reads from disk.
|
|
62
|
+
|
|
63
|
+
Account type is the config's single source of truth — accessor functions like `get_debt_accounts()` and `get_student_loan_accounts()` derive their results from account types rather than separate config keys.
|
|
64
|
+
|
|
65
|
+
Data paths are derived from the config directory:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
~/.config/spendctl/
|
|
69
|
+
├── config.json # User configuration
|
|
70
|
+
└── data/
|
|
71
|
+
├── spendctl.db # Main database
|
|
72
|
+
└── backups/ # Timestamped database backups
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Database Schema
|
|
76
|
+
|
|
77
|
+
The schema lives in `schema.sql` and is applied via `init_db()` on first connection to a new database.
|
|
78
|
+
|
|
79
|
+
**Lookup tables** (seeded from config on first init):
|
|
80
|
+
|
|
81
|
+
| Table | Purpose |
|
|
82
|
+
|-------|---------|
|
|
83
|
+
| `accounts` | Account name, type, institution, APR, starting balance |
|
|
84
|
+
| `categories` | Category name, group, monthly budget amount |
|
|
85
|
+
| `transaction_types` | Expense, Income, Transfer, Debt Payment, Interest, Reconciliation |
|
|
86
|
+
| `budget_income` | Expected income sources and amounts |
|
|
87
|
+
|
|
88
|
+
**Core tables:**
|
|
89
|
+
|
|
90
|
+
| Table | Purpose |
|
|
91
|
+
|-------|---------|
|
|
92
|
+
| `transactions` | Every financial transaction with date, amount, type, accounts, category |
|
|
93
|
+
| `check_ins` | Periodic balance snapshot headers (date, label, notes) |
|
|
94
|
+
| `check_in_balances` | Normalized balance values per account per check-in |
|
|
95
|
+
| `subscriptions` | Recurring charges with status tracking (Active/Canceled/Paused) |
|
|
96
|
+
| `settings` | Key-value store for runtime settings (e.g., baseline date) |
|
|
97
|
+
|
|
98
|
+
Check-ins use a normalized design: `check_ins` holds the snapshot header, and `check_in_balances` holds one row per account per check-in. This means new accounts can be added without a schema change.
|
|
99
|
+
|
|
100
|
+
**Views:**
|
|
101
|
+
|
|
102
|
+
| View | Purpose |
|
|
103
|
+
|------|---------|
|
|
104
|
+
| `v_monthly_expenses` | Pre-aggregated spending by month and category |
|
|
105
|
+
|
|
106
|
+
**Indexes:** 9 indexes on transactions (date, category, type, from/to account, date+category) and check-ins (date, check-in ID, account name) for common query patterns.
|
|
107
|
+
|
|
108
|
+
All queries use parameterized SQL (`?` placeholders) to prevent injection.
|
|
109
|
+
|
|
110
|
+
## Query Layer
|
|
111
|
+
|
|
112
|
+
The 6 query modules in `queries/` provide the data access API:
|
|
113
|
+
|
|
114
|
+
| Module | Key Functions | Purpose |
|
|
115
|
+
|--------|--------------|---------|
|
|
116
|
+
| `transactions` | `add_transaction`, `list_transactions` | Transaction CRUD and filtering |
|
|
117
|
+
| `budget` | `budget_vs_actual`, `remaining_budget`, `total_income` | Budget analysis |
|
|
118
|
+
| `check_ins` | `add_check_in`, `get_latest_check_in` | Balance snapshots |
|
|
119
|
+
| `dashboard` | `account_balance`, `all_balances`, `net_worth`, `debt_progress` | Live balance computation |
|
|
120
|
+
| `reports` | `monthly_summary`, `spending_by_category`, `cash_flow` | Reporting and summaries |
|
|
121
|
+
| `subscriptions` | `list_subscriptions`, `active_monthly_total` | Subscription tracking |
|
|
122
|
+
|
|
123
|
+
All functions accept a `sqlite3.Connection` as the first argument and return plain dicts or lists of dicts. This keeps them easily testable with in-memory databases and JSON-serializable for `--json` output.
|
|
124
|
+
|
|
125
|
+
## CLI
|
|
126
|
+
|
|
127
|
+
`cli.py` uses argparse with a subcommand pattern. Each command has a handler function (`cmd_*`) that:
|
|
128
|
+
|
|
129
|
+
1. Calls `_config_guard()` to verify config exists before doing any work
|
|
130
|
+
2. Validates date/month arguments with regex + `date.fromisoformat()`
|
|
131
|
+
3. Opens a DB connection via `ensure_db()`
|
|
132
|
+
4. Calls the appropriate query function(s)
|
|
133
|
+
5. Serializes output: JSON via `json.dumps()` if `--json` is set, otherwise formatted text
|
|
134
|
+
|
|
135
|
+
The `--json` flag is present on every command. Consumers (AI assistants, shell scripts) can reliably pipe any command to `jq` or a parser without checking first.
|
|
136
|
+
|
|
137
|
+
## Dashboard
|
|
138
|
+
|
|
139
|
+
The Streamlit dashboard is a multi-page app launched via `spendctl dashboard`. It requires the optional `[dashboard]` extras (`streamlit`, `plotly`, `pandas`).
|
|
140
|
+
|
|
141
|
+
- `app.py` sets page config and immediately redirects to the main dashboard page
|
|
142
|
+
- Each page in `pages/` is an independent Streamlit script; Streamlit loads them alphabetically from the directory
|
|
143
|
+
- `helpers.py` provides shared formatting utilities, a consistent color palette, and the month-selector widget used across pages
|
|
144
|
+
- Database connections use `@st.cache_resource` for a single shared connection per server process
|
|
145
|
+
|
|
146
|
+
All charts use Plotly. The dashboard reads account/category configuration from `config.py` to drive chart labels and budget targets, so adding a new account or category in the config is reflected in the dashboard without code changes.
|
|
147
|
+
|
|
148
|
+
## Testing
|
|
149
|
+
|
|
150
|
+
- **Framework:** pytest with in-memory SQLite fixtures
|
|
151
|
+
- **Fixture:** `conftest.py` provides a fully seeded in-memory database with sample transactions, check-ins, and subscriptions — no disk I/O in tests
|
|
152
|
+
- **Config isolation:** Tests monkeypatch `CONFIG_PATH` to a temp directory and reset `_config_cache` in `autouse` fixtures to prevent state leakage between tests
|
|
153
|
+
- **Coverage:** All query modules, CLI argument validation, config loading, init wizard, and database layer
|
|
154
|
+
- **Test files:** 10 test modules, 221 tests, all passing
|
|
155
|
+
- **Config:** pytest settings and coverage in `pyproject.toml`, lint via ruff
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-03-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Interactive setup wizard (`spendctl init`) for first-time configuration
|
|
13
|
+
- Config management commands (`spendctl config show/edit/reset`)
|
|
14
|
+
- 13 CLI commands with `--json` output on every command
|
|
15
|
+
- Transaction tracking: add, list, filter, search, update, delete
|
|
16
|
+
- Account balance computation (transaction-based with starting balance calibration)
|
|
17
|
+
- Budget vs actual comparison with monthly breakdowns
|
|
18
|
+
- Debt paydown progress tracking with per-account metrics
|
|
19
|
+
- Net worth computation grouped by account type
|
|
20
|
+
- Subscription management (active, canceled, paused)
|
|
21
|
+
- Check-in system for periodic balance snapshots (normalized schema)
|
|
22
|
+
- Monthly summary and cash flow reports
|
|
23
|
+
- Spending breakdowns by category and account
|
|
24
|
+
- CSV export
|
|
25
|
+
- Database backup with timestamped copies
|
|
26
|
+
- 7-page Streamlit dashboard (optional `[dashboard]` extra)
|
|
27
|
+
- Overview with sparklines and net worth gauge
|
|
28
|
+
- Spending analysis with category charts
|
|
29
|
+
- Debt progress with payoff projections
|
|
30
|
+
- Transaction browser
|
|
31
|
+
- Subscription tracker
|
|
32
|
+
- Loan detail page
|
|
33
|
+
- Printable monthly report
|
|
34
|
+
- Full community standards: LICENSE, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY
|
|
35
|
+
- CI pipeline with lint + test matrix (Python 3.10-3.12)
|
|
36
|
+
- 221 tests passing
|
|
37
|
+
|
|
38
|
+
[0.1.0]: https://github.com/Jscoats/spendctl/releases/tag/v0.1.0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
+
and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to a positive environment:
|
|
15
|
+
|
|
16
|
+
- Using welcoming and inclusive language
|
|
17
|
+
- Being respectful of differing viewpoints and experiences
|
|
18
|
+
- Gracefully accepting constructive criticism
|
|
19
|
+
- Focusing on what is best for the community
|
|
20
|
+
|
|
21
|
+
Examples of unacceptable behavior:
|
|
22
|
+
|
|
23
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
24
|
+
- Public or private harassment
|
|
25
|
+
- Publishing others' private information without explicit permission
|
|
26
|
+
- Other conduct which could reasonably be considered inappropriate
|
|
27
|
+
|
|
28
|
+
## Enforcement
|
|
29
|
+
|
|
30
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
31
|
+
reported by opening an issue or contacting the project maintainer. All
|
|
32
|
+
complaints will be reviewed and investigated and will result in a response
|
|
33
|
+
that is deemed necessary and appropriate to the circumstances.
|
|
34
|
+
|
|
35
|
+
## Attribution
|
|
36
|
+
|
|
37
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
38
|
+
version 2.1, available at
|
|
39
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Contributing to spendctl
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
This project welcomes contributions from the community. Whether you're fixing a bug, adding a feature, or improving documentation, your help is appreciated.
|
|
6
|
+
|
|
7
|
+
## Reporting Bugs
|
|
8
|
+
|
|
9
|
+
Found a bug? Please [open an issue](https://github.com/Jscoats/spendctl/issues/new?template=bug_report.md) with:
|
|
10
|
+
|
|
11
|
+
- Clear description of the problem
|
|
12
|
+
- Steps to reproduce
|
|
13
|
+
- Expected vs actual behavior
|
|
14
|
+
- Your environment (macOS version, Python version, spendctl version)
|
|
15
|
+
- Relevant error messages or logs
|
|
16
|
+
|
|
17
|
+
**Note:** Please check existing issues first to avoid duplicates.
|
|
18
|
+
|
|
19
|
+
## Requesting Features
|
|
20
|
+
|
|
21
|
+
Have an idea? [Open a feature request](https://github.com/Jscoats/spendctl/issues/new?template=feature_request.md) with:
|
|
22
|
+
|
|
23
|
+
- Clear description of the feature
|
|
24
|
+
- Why it would be useful
|
|
25
|
+
- Example usage (if applicable)
|
|
26
|
+
|
|
27
|
+
## Contributing Code
|
|
28
|
+
|
|
29
|
+
### Prerequisites
|
|
30
|
+
|
|
31
|
+
- Python 3.10+
|
|
32
|
+
- [`uv`](https://docs.astral.sh/uv/) (recommended) or `pip`
|
|
33
|
+
|
|
34
|
+
### Before You Start
|
|
35
|
+
|
|
36
|
+
1. **Check existing issues** - Someone might already be working on it
|
|
37
|
+
2. **Discuss major changes** - Open an issue first for big features/refactors
|
|
38
|
+
3. **Keep it focused** - One feature/fix per pull request
|
|
39
|
+
|
|
40
|
+
### Development Setup
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Clone the repo
|
|
44
|
+
git clone https://github.com/Jscoats/spendctl.git
|
|
45
|
+
cd spendctl
|
|
46
|
+
|
|
47
|
+
# Install in editable mode (primary -- uses uv)
|
|
48
|
+
uv tool install -e ".[dev]" # Install with dev extras
|
|
49
|
+
uv tool install -e ".[dev]" --force # Reinstall after changes
|
|
50
|
+
|
|
51
|
+
# Run tests
|
|
52
|
+
pytest
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Fallback:** If you are not using `uv`, you can install with pip instead:
|
|
56
|
+
```bash
|
|
57
|
+
pip install -e ".[dev]"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Code Guidelines
|
|
61
|
+
|
|
62
|
+
**Follow existing patterns:**
|
|
63
|
+
- Match the style of surrounding code
|
|
64
|
+
- Keep functions focused and testable
|
|
65
|
+
- Add docstrings to new functions
|
|
66
|
+
|
|
67
|
+
**No unnecessary runtime dependencies:**
|
|
68
|
+
- Keep the core package dependency-free (stdlib only)
|
|
69
|
+
- Dashboard extras (`streamlit`, `plotly`, `pandas`) belong in the `[dashboard]` optional extra
|
|
70
|
+
|
|
71
|
+
**Testing:**
|
|
72
|
+
- Add tests for new features
|
|
73
|
+
- Ensure existing tests pass: `pytest`
|
|
74
|
+
- Test files live in `tests/`
|
|
75
|
+
|
|
76
|
+
**Documentation:**
|
|
77
|
+
- Update README.md if adding user-facing features
|
|
78
|
+
- Add docstrings to new functions
|
|
79
|
+
|
|
80
|
+
### Pull Request Process
|
|
81
|
+
|
|
82
|
+
1. **Fork the repo** and create a feature branch
|
|
83
|
+
```bash
|
|
84
|
+
git checkout -b feature/my-new-feature
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
2. **Make your changes**
|
|
88
|
+
- Write clean, readable code
|
|
89
|
+
- Add tests for new functionality
|
|
90
|
+
- Update documentation
|
|
91
|
+
|
|
92
|
+
3. **Test thoroughly**
|
|
93
|
+
```bash
|
|
94
|
+
pytest # All tests must pass
|
|
95
|
+
ruff check src/ tests/ # Lint must pass
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
4. **Commit with clear messages**
|
|
99
|
+
```bash
|
|
100
|
+
git commit -m "Add category budget alerts"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
5. **Push and create a pull request**
|
|
104
|
+
```bash
|
|
105
|
+
git push origin feature/my-new-feature
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
6. **Describe your changes** in the PR:
|
|
109
|
+
- What does it do?
|
|
110
|
+
- Why is it needed?
|
|
111
|
+
- How was it tested?
|
|
112
|
+
|
|
113
|
+
### Code Review
|
|
114
|
+
|
|
115
|
+
- PRs will be reviewed for code quality, test coverage, and alignment with project goals
|
|
116
|
+
- Be patient - reviews may take a few days
|
|
117
|
+
- Be open to feedback and iteration
|
|
118
|
+
|
|
119
|
+
## Documentation
|
|
120
|
+
|
|
121
|
+
Documentation improvements are always welcome! You can help by:
|
|
122
|
+
|
|
123
|
+
- Fixing typos or unclear wording
|
|
124
|
+
- Adding examples to the README
|
|
125
|
+
- Improving code comments
|
|
126
|
+
|
|
127
|
+
## Questions?
|
|
128
|
+
|
|
129
|
+
Not sure about something? Open an issue with the `question` label.
|
|
130
|
+
|
|
131
|
+
## Thank You!
|
|
132
|
+
|
|
133
|
+
Every contribution, no matter how small, helps make this project better. Thank you for being part of the community!
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
**By contributing, you agree that your contributions will be licensed under the project's MIT License.**
|
spendctl-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jscoats
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|