# Copilot Instructions for AI Coding Agents ## Project Overview 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. ## Architecture & Data Flow ``` User submits task → Task (pending) → Admin approves → Task (approved) → Admin prints → Task (printed) → Physical kanban card ``` ### Core Components - **`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. - **`app/models.py`**: Single Pydantic model `Task` with factory method `Task.create()` that generates UUID. - **`app/printer.py`**: Minimal printer wrapper (currently unused in main.py - printer logic is duplicated in `/print` route). - **`app/templates/`**: Jinja2 HTML templates with German localization and inline CSS/JS. - **`app/static/fonts/`**: Custom fonts (BauPro, HealTheWeb) + Font Awesome 7.1.0 OTFs for semantic icons. ### State Management Pattern - **Persistence**: Simple JSON file (`data/tasks.json`) loaded into in-memory list on startup. Call `save_tasks()` after every mutation. - **Sessions**: Starlette `SessionMiddleware` stores admin auth state and flash messages (`login_result`, `print_result`, `preview_image`) in cookies. - **Admin auth**: Password-only (no username), checked against `.env` variable. Session flag `admin: True/False`. ### 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 ### Code Organization - **Monolithic by design**: All logic in `main.py` for simplicity. Don't split unless file exceeds 500 lines. - **No database**: JSON file persistence is intentional. Don't add SQLite/Postgres without discussion. - **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 ### Thermal Printer (ESC/POS) - Library: `python-escpos` v3.1 - Connection: Serial over USB (`/dev/ttyUSB0` on Linux, `/dev/tty.usbserial-*` on macOS) - 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-11-20_