docs: experiment with generated documentation, let's keep it in git for
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
now
This commit is contained in:
228
docs/by-claude-opus/development.md
Normal file
228
docs/by-claude-opus/development.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# Development Guide
|
||||
|
||||
## Development Environment
|
||||
|
||||
### Required Tools
|
||||
|
||||
| Tool | Version | Purpose |
|
||||
|------|---------|---------|
|
||||
| Python | 3.13+ | Runtime |
|
||||
| uv | Latest | Dependency management |
|
||||
| Docker | Latest | Container builds |
|
||||
| Git | Any | Version control |
|
||||
| Make | Any | Build automation |
|
||||
|
||||
### Initial Setup
|
||||
|
||||
```bash
|
||||
# 1. Clone the repository
|
||||
git clone <repo-url>
|
||||
cd fuj-management
|
||||
|
||||
# 2. Install dependencies (creates .venv automatically)
|
||||
uv sync
|
||||
|
||||
# 3. Activate the virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# 4. Set up credentials
|
||||
mkdir -p .secret
|
||||
# Copy your Google service account JSON here:
|
||||
cp ~/Downloads/fuj-management-bot-credentials.json .secret/
|
||||
|
||||
# 5. (Optional) Set Fio API token
|
||||
export FIO_API_TOKEN=your_token_here
|
||||
```
|
||||
|
||||
### IDE Configuration
|
||||
|
||||
The `.vscode/` directory contains workspace settings. If using VS Code, the Python interpreter should automatically detect the `.venv` directory.
|
||||
|
||||
**PYTHONPATH note**: When running scripts from the project root, the Makefile sets `PYTHONPATH=scripts:$PYTHONPATH`. If your IDE doesn't do this, you may see import errors in `match_payments.py` and other scripts that import sibling modules.
|
||||
|
||||
## Project Dependencies
|
||||
|
||||
Defined in `pyproject.toml`:
|
||||
|
||||
| Dependency | Version | Purpose |
|
||||
|------------|---------|---------|
|
||||
| `flask` | ≥3.1.3 | Web framework |
|
||||
| `google-api-python-client` | ≥2.162.0 | Google Sheets API |
|
||||
| `google-auth-httplib2` | ≥0.2.0 | Google auth transport |
|
||||
| `google-auth-oauthlib` | ≥1.2.1 | OAuth2 support |
|
||||
| `qrcode[pil]` | ≥8.0 | QR code generation (with PIL/Pillow backend) |
|
||||
|
||||
The project uses `uv` with `package = false` in `[tool.uv]`, meaning it's not an installable package — dependencies are synced directly to the virtual environment.
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
### Python Style
|
||||
|
||||
- No linter or formatter is configured — the codebase uses a pragmatic, readable style
|
||||
- Type hints are used for function signatures but not exhaustively
|
||||
- Docstrings follow Google-style format on key functions
|
||||
- Scripts use `if __name__ == "__main__": main()` pattern
|
||||
|
||||
### Import Pattern
|
||||
|
||||
Scripts in the `scripts/` directory import from each other as top-level modules:
|
||||
|
||||
```python
|
||||
# In match_payments.py:
|
||||
from attendance import get_members_with_fees
|
||||
from czech_utils import normalize, parse_month_references
|
||||
from sync_fio_to_sheets import get_sheets_service, DEFAULT_SPREADSHEET_ID
|
||||
```
|
||||
|
||||
This works because `scripts/` is added to `sys.path` at runtime (by `app.py` on startup, by Makefile via `PYTHONPATH`, or by scripts adding their own directory to `sys.path`).
|
||||
|
||||
### Template Style
|
||||
|
||||
- All CSS is inline (no external stylesheets)
|
||||
- No CSS preprocessors or frameworks
|
||||
- No JavaScript frameworks — plain DOM manipulation
|
||||
- Terminal-inspired aesthetic: monospace fonts, green-on-black, dashed borders
|
||||
|
||||
### Commit Conventions
|
||||
|
||||
The project uses [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
```
|
||||
feat: add keyboard navigation to member popup
|
||||
fix: correct diacritic-insensitive search filter
|
||||
chore: update dependencies
|
||||
```
|
||||
|
||||
AI commits include a co-author trailer:
|
||||
```
|
||||
Co-authored-by: Antigravity <antigravity@google.com>
|
||||
```
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### Why No Database?
|
||||
|
||||
Google Sheets serves as the database because:
|
||||
1. Club members can view and correct data without special tools
|
||||
2. No database server to manage or back up
|
||||
3. Built-in version history and collaborative editing
|
||||
4. Good enough for ~40 members and ~hundreds of transactions
|
||||
|
||||
### Why No Template Inheritance?
|
||||
|
||||
Each HTML template is self-contained. While this means CSS duplication, it keeps each page fully independent and easy to understand. For a 3-page app, the duplication cost is minimal.
|
||||
|
||||
### Why Flask Development Server in Production?
|
||||
|
||||
The Docker container runs Flask's built-in server (`python3 app.py`) rather than gunicorn or waitress. This is intentional — the dashboard is an internal tool accessed by one person at a time. The simplicity outweighs the performance cost.
|
||||
|
||||
### Why Scrape HTML When There's an API?
|
||||
|
||||
The Fio transparent page scraping exists as a **zero-configuration fallback**. Not everyone has an API token, and the transparent page is always publicly accessible. The API is preferred when available (richer data, stable IDs).
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Web Route
|
||||
|
||||
1. Add the route function in `app.py`:
|
||||
```python
|
||||
@app.route("/new-page")
|
||||
def new_page():
|
||||
# Fetch data
|
||||
record_step("fetch_data")
|
||||
# Process
|
||||
record_step("process_data")
|
||||
return render_template("new_page.html", ...)
|
||||
```
|
||||
|
||||
2. Create `templates/new_page.html` (copy structure from `fees.html`)
|
||||
|
||||
3. Add a link in the nav bar across all templates:
|
||||
```html
|
||||
<a href="/new-page">[New Page]</a>
|
||||
```
|
||||
|
||||
4. Add a test in `tests/test_app.py`
|
||||
|
||||
### Adding a New Script
|
||||
|
||||
1. Create `scripts/new_script.py`
|
||||
2. Add a Makefile target:
|
||||
```makefile
|
||||
new-target: $(PYTHON)
|
||||
$(PYTHON) scripts/new_script.py
|
||||
```
|
||||
3. Update `make help` output
|
||||
4. Add the `.PHONY` declaration
|
||||
|
||||
### Modifying Fee Rules
|
||||
|
||||
Fee rules are defined as constants in `scripts/attendance.py`:
|
||||
```python
|
||||
FEE_FULL = 750 # 2+ practices
|
||||
FEE_SINGLE = 200 # 1 practice
|
||||
```
|
||||
|
||||
The calculation logic is in `calculate_fee()`:
|
||||
```python
|
||||
def calculate_fee(attendance_count: int) -> int:
|
||||
if attendance_count == 0: return 0
|
||||
if attendance_count == 1: return FEE_SINGLE
|
||||
return FEE_FULL
|
||||
```
|
||||
|
||||
### Adding a New Czech Month Form
|
||||
|
||||
If you encounter a Czech month declension not yet supported, add it to `CZECH_MONTHS` in `scripts/czech_utils.py`:
|
||||
|
||||
```python
|
||||
CZECH_MONTHS = {
|
||||
"leden": 1, "ledna": 1, "lednu": 1,
|
||||
"lednem": 1, # New instrumental case
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Project History
|
||||
|
||||
The project evolved through distinct phases:
|
||||
|
||||
1. **Design phase** — Initial brainstorming captured in `docs/project-notes.md`
|
||||
2. **CLI tools** — `calculate_fees.py` and `match_payments.py` for command-line workflows
|
||||
3. **Bank integration** — `fio_utils.py` for transparent page scraping, later API support
|
||||
4. **Google Sheets sync** — `sync_fio_to_sheets.py` + `infer_payments.py` for the ledger pipeline
|
||||
5. **Web dashboard** — `app.py` with the `/fees`, `/reconcile`, and `/payments` pages
|
||||
6. **Interactive features** — Modal popups, QR payments, keyboard navigation, search filter
|
||||
7. **Fee exceptions** — Manual override system via the `exceptions` sheet tab
|
||||
8. **CI/CD** — Gitea Actions for Docker builds and Kubernetes deployment
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No data." on the web dashboard
|
||||
|
||||
The attendance Google Sheet couldn't be fetched, or it returned empty data. Check:
|
||||
- Internet connectivity
|
||||
- The sheet ID in `attendance.py` is still valid
|
||||
- The sheet's public sharing settings haven't changed
|
||||
|
||||
### Slow page loads
|
||||
|
||||
Each page fetches data from Google Sheets on every request (no caching). Typical load times are 1-3 seconds. If significantly slower:
|
||||
- Check the performance breakdown (click the render time in the footer)
|
||||
- Google Sheets API rate limiting may be the cause
|
||||
|
||||
### Import errors in scripts
|
||||
|
||||
Ensure `PYTHONPATH` includes the `scripts/` directory:
|
||||
```bash
|
||||
export PYTHONPATH=scripts:$PYTHONPATH
|
||||
```
|
||||
|
||||
Or use the Makefile, which sets this automatically.
|
||||
|
||||
### "Could not fetch exceptions" warning
|
||||
|
||||
The `exceptions` tab doesn't exist in the Payments Google Sheet. This is non-fatal — reconciliation proceeds without fee overrides.
|
||||
|
||||
---
|
||||
|
||||
*Development guide generated from comprehensive code analysis on 2026-03-03.*
|
||||
Reference in New Issue
Block a user