# Copilot Instructions for Studio EinsZwoVier Web Application ## Project Overview This is a **FastAPI-based web application** serving as a multi-service hub for Studio EinsZwoVier, a collaborative print studio. The main app provides a PDF print cost calculator with automated ink coverage analysis, integrated with a Matrix server for order collection and payment tracking. ## Architecture & Service Boundaries ### Core FastAPI Application (`main.py`, port 80→8000) - **Landing page** (`/`) with presence state management (studio open/closed indicator) - **Cost calculator** (`/cost`) for PDF analysis and price estimation - **About page** (`/about`) - **Order submission** via Matrix integration ### Multi-Container Stack (Docker Compose) The application orchestrates 7+ containerized services: - **web**: FastAPI app (this repo's code) - **synapse**: Matrix server for print job collection (port 8008) - **ollama**: Local LLM inference (port 11434) - **open-webui**: LLM chat interface (port 8080) - **bookstack + mariadb**: Documentation wiki - **docmost + postgres + redis**: Knowledge management system - **watchtower**: Auto-updates all containers every 24h ### PDF Processing Pipeline (`cost_calculator.py`) 1. **Upload** → Validate `.pdf` extension only 2. **Page-by-page analysis**: - Extract dimensions from `CropBox` (preferred) or `MediaBox` - Convert PDF points to meters: `(points / 72.0) * 0.0254` - Render at 150 DPI using `pdf2image` - Calculate ink coverage: pixels with RGB < 250 = ink - Detect color vs B&W: HSV saturation analysis (>0.1% pixels with sat>10 = color) 3. **Cost calculation**: area (m²) × rate (from env vars `RATE_PER_M2_BLACK`/`COLOR`) ### Matrix Integration (`mailer.py`) - Uses `matrix-nio` async client to send orders - **Critical**: Must set `MATRIX_USER`, `MATRIX_PASS`, `MATRIX_HOMESERVER` env vars - Sends structured summary (German language) + PDF upload to hardcoded room ID - **Synchronous wrapper** `send_order_sync()` bridges async/sync contexts for FastAPI ## Developer Workflows ### Local Development ```bash # Option 1: Direct Python execution (auto-reload) python run_app.py # Option 2: Full stack with Docker Compose docker-compose up --build -d # Access at: http://localhost (or einszwovier.local in local network) ``` ### Environment Configuration Create `.env` file with: ```bash # Print rates (€/m²) RATE_PER_M2_BLACK=4.0 RATE_PER_M2_COLOR=5.0 # Matrix integration MATRIX_USER=@bot:homeserver MATRIX_PASS=secret MATRIX_HOMESERVER=http://einszwovier.local:8008 # Service ports SYNAPSE_PORT=8008 OLLAMA_PORT=11434 OPENWEBUI_PORT=8080 BOOKSTACK_PORT=6875 # BookStack/DB configs (see docker-compose.yml for full list) ``` ### Debugging Matrix Integration Use `get_room_id.py` to discover Matrix room IDs: ```python # Connects to local Matrix server, joins room, prints room IDs python get_room_id.py ``` Then update the hardcoded room ID in `main.py` line 127: `room_id="!eFW..."` ## Project-Specific Conventions ### Presence State Management - In-memory global `presence_state` dict tracks studio open/closed status - API endpoints: `GET /presence`, `POST /presence`, `POST /presence/toggle` - Background task `update_presence_periodically()` runs every 5 min (currently no-op placeholder) ### German UI/UX - Templates use German language exclusively - Error/success messages in German (e.g., "Dein Auftrag wurde erfolgreich gesendet!") - Cost calculator displays "S/W" (Schwarz/Weiß) vs "Farbe" (color) ### File Organization - **Uploads persist** in `data/uploads/` (mounted volume in Docker) - **Templates** in `templates/` (Jinja2, extending `base.html`) - **Static assets** in `static/` (CSS, fonts, images) - **Service data** in dedicated dirs: `bookstack/`, `docmost/`, `matrix/`, `ollama/`, `open-webui/` ### Docker Production Command The `Dockerfile` uses **Gunicorn with Uvicorn workers** for production: ```bash gunicorn main:app -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 4 --timeout 120 ``` But `docker-compose.yml` overrides with `--reload` for dev convenience. ## Critical Integration Points ### PDF Analysis Return Structure `analyze_pdf()` returns: ```python { "filename": str, "pages": [{"page": int, "width_m": float, "height_m": float, "area_m2": float, "ink_pct": float, "is_color": bool, "cost": float}], "total_area_black": float, "total_area_color": float, "total_cost_black": float, "total_cost_color": float, "grand_total": float } ``` ### Matrix Room ID Hardcoding ⚠️ **Known technical debt**: Room ID is hardcoded in `send_order_endpoint()`. Update when Matrix server changes or for different deployment environments. ### Async/Sync Bridge Pattern Matrix client is async, but FastAPI endpoint is sync. Pattern used: ```python def send_order_sync(...): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(send_order(...)) loop.close() ``` ## External Dependencies - **Poppler** (system): Required for `pdf2image` (installed in Dockerfile) - **OpenCV** (system): Requires `libgl1`, `libglib2.0-0` (installed in Dockerfile) - **Font files**: Custom fonts in `static/fonts/` (BauPro, HealTheWebA/B, SISTEMAS) ## Testing & Utilities - `test_send_pdf.py`: Manual test script for Matrix integration - No automated test suite currently implemented