fix filenotfound
This commit is contained in:
parent
e80bb9577a
commit
926aaefd85
3 changed files with 152 additions and 37 deletions
187
.github/copilot-instructions.md
vendored
187
.github/copilot-instructions.md
vendored
|
|
@ -2,50 +2,163 @@
|
||||||
# Copilot Instructions for AI Coding Agents
|
# Copilot Instructions for AI Coding Agents
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
AutoKanban is a FastAPI-based web application for managing a physical kanban workflow, integrating with a 58mm Arduino thermal printer. The system allows users to submit tasks, admins to approve and print them, and cards to be pinned to a physical board.
|
AutoKanban is a FastAPI web app for managing a physical kanban workflow with a 58mm Arduino thermal printer. Users submit tasks via web UI, admins approve and print them to physical cards. Designed for Raspberry Pi deployment but tested on macOS.
|
||||||
|
|
||||||
## Major Components
|
## Architecture & Data Flow
|
||||||
- `app/main.py`: Main FastAPI app, task model, admin session, printer/image generation, static mounts.
|
```
|
||||||
- `app/models.py`: Pydantic Task model.
|
User submits task → Task (pending) → Admin approves → Task (approved) → Admin prints → Task (printed) → Physical kanban card
|
||||||
- `app/printer.py`: (Optional) Printer abstraction for cross-platform serial printing.
|
```
|
||||||
- `app/templates/`: Jinja2 HTML templates for UI.
|
|
||||||
- `app/static/`: Static files (fonts, CSS, images, Font Awesome OTFs).
|
|
||||||
- `data/tasks.json`: Persistent task storage (JSON).
|
|
||||||
- `.env`: Secrets (admin password, etc). Not committed.
|
|
||||||
- `.gitignore`: Ignores `.env`, `out/`, and other sensitive/generated files.
|
|
||||||
- `out/`: Stores generated card preview images (debug mode).
|
|
||||||
|
|
||||||
## Key Features & Workflows
|
### Core Components
|
||||||
- Users submit tasks via web UI.
|
- **`app/main.py`** (270 lines): Monolithic FastAPI app containing all routes, business logic, session management, and card rendering. All state managed in-memory with JSON persistence.
|
||||||
- Admins approve tasks and print them to a 58mm ESC/POS thermal printer.
|
- **`app/models.py`**: Single Pydantic model `Task` with factory method `Task.create()` that generates UUID.
|
||||||
- Card preview images are generated using Pillow (debug mode), with custom and Font Awesome fonts.
|
- **`app/printer.py`**: Minimal printer wrapper (currently unused in main.py - printer logic is duplicated in `/print` route).
|
||||||
- Semantic icon rendering: Task keywords map to Font Awesome icons.
|
- **`app/templates/`**: Jinja2 HTML templates with German localization and inline CSS/JS.
|
||||||
- Admin authentication via password (from `.env`).
|
- **`app/static/fonts/`**: Custom fonts (BauPro, HealTheWeb) + Font Awesome 7.1.0 OTFs for semantic icons.
|
||||||
- Persistent storage in `data/tasks.json`.
|
|
||||||
- German localization in UI and card output.
|
|
||||||
|
|
||||||
## Build, Test, and Debug
|
### State Management Pattern
|
||||||
- Install dependencies: `pip install -r requirements.txt`
|
- **Persistence**: Simple JSON file (`data/tasks.json`) loaded into in-memory list on startup. Call `save_tasks()` after every mutation.
|
||||||
- Run app: `uvicorn app.main:app --reload`
|
- **Sessions**: Starlette `SessionMiddleware` stores admin auth state and flash messages (`login_result`, `print_result`, `preview_image`) in cookies.
|
||||||
- For card preview (debug): Set `DEBUG_PRINT_TO_IMAGE = True` in `app/main.py`.
|
- **Admin auth**: Password-only (no username), checked against `.env` variable. Session flag `admin: True/False`.
|
||||||
- Fonts: Place custom and Font Awesome OTFs in `app/static/fonts/`.
|
|
||||||
- Set admin password in `.env` as `ADMIN_PASSWORD=yourpassword`.
|
### Dual-Mode Printing Architecture
|
||||||
|
**Key insight**: App has two print modes controlled by `DEBUG_PRINT_TO_IMAGE` flag in `main.py`:
|
||||||
|
- `True` (default for testing): Generates PNG previews using Pillow with custom fonts, semantic icons, and card layout to `out/` directory.
|
||||||
|
- `False` (production): Prints to ESC/POS thermal printer via serial (`/dev/ttyUSB0`, 19200 baud).
|
||||||
|
|
||||||
|
**Why this matters**: When modifying print logic, update BOTH branches in the `/print/{task_id}` route (lines 142-244).
|
||||||
|
|
||||||
|
### Semantic Icon System
|
||||||
|
`KEYWORD_ICONS` list in `main.py` maps German/English keywords to Font Awesome unicode codepoints:
|
||||||
|
- Pattern: `(['keyword1', 'keyword2'], '\uf0f4')`
|
||||||
|
- Matching: Case-insensitive substring search in task content
|
||||||
|
- Default icon: `\uf328` (fa-sticky-note)
|
||||||
|
- Add new mappings by extending this list - requires knowing FA unicode values.
|
||||||
|
|
||||||
|
## Developer Workflows
|
||||||
|
|
||||||
|
### Running the App
|
||||||
|
**Preferred method** (handles venv automatically):
|
||||||
|
```bash
|
||||||
|
python start_app.py # Launches uvicorn on 0.0.0.0:8000 with --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual method**:
|
||||||
|
```bash
|
||||||
|
source .venv/bin/activate
|
||||||
|
uvicorn app.main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### First-Time Setup
|
||||||
|
1. Create `.env` file with `ADMIN_PASSWORD=yourpassword`
|
||||||
|
2. Ensure `data/` directory exists (create if missing - .gitignored)
|
||||||
|
3. Install system fonts or update font paths in `main.py` (lines 29-31)
|
||||||
|
4. For real printing: Set `DEBUG_PRINT_TO_IMAGE = False` and configure serial device
|
||||||
|
|
||||||
|
### Testing Print Without Hardware
|
||||||
|
1. Keep `DEBUG_PRINT_TO_IMAGE = True`
|
||||||
|
2. Submit task → approve → print
|
||||||
|
3. Check `out/task_{uuid}.png` for rendered card
|
||||||
|
4. Preview images shown in web UI after printing
|
||||||
|
|
||||||
|
### Debugging Font Issues
|
||||||
|
Font loading uses fallback chain (lines 129-135):
|
||||||
|
```python
|
||||||
|
load_font(path, size, fallback=None, font_label="descriptive_name")
|
||||||
|
```
|
||||||
|
- Logs errors but gracefully falls back to default font
|
||||||
|
- Font errors appended to `font_error_msgs[]` and rendered on card preview
|
||||||
|
|
||||||
## Project Conventions
|
## Project Conventions
|
||||||
- Use environment variables for secrets (never hardcode passwords).
|
|
||||||
- Use Pillow's robust font loading with error handling for card previews.
|
### Code Organization
|
||||||
- All static assets (fonts, images) go in `app/static/`.
|
- **Monolithic by design**: All logic in `main.py` for simplicity. Don't split unless file exceeds 500 lines.
|
||||||
- All persistent data in `data/`.
|
- **No database**: JSON file persistence is intentional. Don't add SQLite/Postgres without discussion.
|
||||||
- All generated output in `out/` (gitignored).
|
- **German-first**: UI text, card labels, and comments use German. Keep this convention.
|
||||||
|
|
||||||
|
### Security Patterns
|
||||||
|
- **Never commit**: `.env`, `data/tasks.json`, `out/*.png` (all in `.gitignore`)
|
||||||
|
- **Session secret**: Hardcoded as `"CHANGE_THIS_SECRET"` (line 18) - should be env var in production
|
||||||
|
- **Admin password**: Must be set in `.env` or app will crash on login attempt
|
||||||
|
|
||||||
|
### Font Asset Management
|
||||||
|
- Custom fonts in `app/static/fonts/` (committed to repo)
|
||||||
|
- Font Awesome 7.1.0 desktop OTFs (committed) - use Solid 900 weight for card icons
|
||||||
|
- Paths defined as constants at top of `main.py` - update these if fonts move
|
||||||
|
- Required fonts: `FONT_BOLD`, `FONT_REGULAR`, `FA_FONT`
|
||||||
|
|
||||||
|
### Task Status Lifecycle
|
||||||
|
Tasks have exactly 3 states: `pending` → `approved` → `printed`
|
||||||
|
- Users can only submit (creates `pending`)
|
||||||
|
- Admins can approve (`pending` → `approved`)
|
||||||
|
- Anyone can print `approved` tasks
|
||||||
|
- Admins can re-print `printed` tasks
|
||||||
|
|
||||||
## Integration Points
|
## Integration Points
|
||||||
- Thermal printer: ESC/POS via `python-escpos` (serial connection, `/dev/ttyUSB0` by default).
|
|
||||||
- Font Awesome: Use OTF from `app/static/fonts/fontawesome-free-7.1.0-desktop/otfs/`.
|
|
||||||
|
|
||||||
## How to Contribute
|
### Thermal Printer (ESC/POS)
|
||||||
- Update this file as new features, files, or conventions are added.
|
- Library: `python-escpos` v3.1
|
||||||
- Reference key files and directories.
|
- Connection: Serial over USB (`/dev/ttyUSB0` on Linux, `/dev/tty.usbserial-*` on macOS)
|
||||||
- Keep instructions concise and actionable for future AI agents and developers.
|
- Baudrate: 19200 (standard for Arduino thermal printers)
|
||||||
|
- See `WIRING.md` for Raspberry Pi GPIO wiring diagram
|
||||||
|
- Permissions: User must be in `dialout` group on Linux
|
||||||
|
|
||||||
|
### Font Awesome Integration
|
||||||
|
- Version: 7.1.0 Free (Solid weight)
|
||||||
|
- Format: OTF desktop fonts (not web fonts)
|
||||||
|
- Icon mapping: Manual unicode lookup required for new icons
|
||||||
|
- Path: `app/static/fonts/fontawesome-free-7.1.0-desktop/otfs/Font Awesome 7 Free-Solid-900.otf`
|
||||||
|
|
||||||
|
## Critical Code Sections
|
||||||
|
|
||||||
|
### Card Rendering Logic (lines 142-239)
|
||||||
|
Complex Pillow-based layout with:
|
||||||
|
- Dynamic text wrapping based on pixel width (not character count)
|
||||||
|
- Vertical centering calculation using `getbbox()` for text height
|
||||||
|
- Icon positioning in lower-right corner
|
||||||
|
- Border and padding calculations for 354×236px card
|
||||||
|
|
||||||
|
**Common pitfalls**:
|
||||||
|
- Font methods (`getlength()`, `getbbox()`) may fail if font is None - wrap in try/except
|
||||||
|
- Text wrapping happens twice: word-based via `textwrap`, then pixel-based
|
||||||
|
- Changing card dimensions requires recalculating all spacing
|
||||||
|
|
||||||
|
### Task Persistence (lines 65-70)
|
||||||
|
Simple load/save pattern - no locking or concurrency control:
|
||||||
|
```python
|
||||||
|
tasks: List[Task] = load_tasks() # On startup
|
||||||
|
save_tasks() # After every mutation
|
||||||
|
```
|
||||||
|
**Risk**: Race conditions if multiple requests mutate simultaneously. Not handled - single-user/low-traffic assumption.
|
||||||
|
|
||||||
|
## Platform-Specific Notes
|
||||||
|
|
||||||
|
### macOS Development
|
||||||
|
- Printer device likely `/dev/tty.usbserial-*` (check `ls /dev/tty.*`)
|
||||||
|
- Use `DEBUG_PRINT_TO_IMAGE = True` for testing without hardware
|
||||||
|
- Font paths may need adjustment (DejaVu fonts not standard on macOS)
|
||||||
|
|
||||||
|
### Raspberry Pi Deployment
|
||||||
|
- Enable UART/serial interface via `raspi-config`
|
||||||
|
- Add user to `dialout` group: `sudo usermod -a -G dialout $USER`
|
||||||
|
- Default device `/dev/ttyUSB0` should work for USB-serial printers
|
||||||
|
- See `WIRING.md` for GPIO hardware serial wiring (pins 8/10)
|
||||||
|
|
||||||
|
## When Making Changes
|
||||||
|
|
||||||
|
### Adding New Routes
|
||||||
|
- Follow existing pattern: FastAPI route → mutate `tasks` list → call `save_tasks()` → redirect with session flash message
|
||||||
|
- Admin-only routes: Check `request.session.get("admin")` first
|
||||||
|
|
||||||
|
### Modifying Card Layout
|
||||||
|
- Update constants: `CARD_WIDTH_PX`, `CARD_HEIGHT_PX` (lines 25-26)
|
||||||
|
- Test with `DEBUG_PRINT_TO_IMAGE = True` before printing to hardware
|
||||||
|
- Remember to update BOTH debug and production print branches
|
||||||
|
|
||||||
|
### Adding Icon Keywords
|
||||||
|
- Extend `KEYWORD_ICONS` list (lines 33-58)
|
||||||
|
- Use Font Awesome unicode lookup: https://fontawesome.com/v7/icons
|
||||||
|
- Format: `(['keyword'], '\ufXXX')` where XXX is FA codepoint
|
||||||
|
|
||||||
---
|
---
|
||||||
_Last updated: 2025-10-20_
|
_Last updated: 2025-11-20_
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ def load_tasks():
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def save_tasks():
|
def save_tasks():
|
||||||
|
TASKS_FILE.parent.mkdir(parents=True, exist_ok=True) # Ensure data/ directory exists
|
||||||
with open(TASKS_FILE, "w", encoding="utf-8") as f:
|
with open(TASKS_FILE, "w", encoding="utf-8") as f:
|
||||||
json.dump([t.dict() for t in tasks], f, ensure_ascii=False, indent=2)
|
json.dump([t.dict() for t in tasks], f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@ pyserial==3.5
|
||||||
python-barcode==0.16.1
|
python-barcode==0.16.1
|
||||||
pyyaml==6.0.3
|
pyyaml==6.0.3
|
||||||
qrcode==8.2
|
qrcode==8.2
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue