docs: experiment with generated documentation, let's keep it in git for
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s

now
This commit is contained in:
2026-03-11 11:57:30 +01:00
parent e83d6af1f5
commit 9b99f6d33b
17 changed files with 2367 additions and 0 deletions

View 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.*