144 lines
5.3 KiB
Markdown
144 lines
5.3 KiB
Markdown
# 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
|