5.3 KiB
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)
- Upload → Validate
.pdfextension only - Page-by-page analysis:
- Extract dimensions from
CropBox(preferred) orMediaBox - 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)
- Extract dimensions from
- Cost calculation: area (m²) × rate (from env vars
RATE_PER_M2_BLACK/COLOR)
Matrix Integration (mailer.py)
- Uses
matrix-nioasync client to send orders - Critical: Must set
MATRIX_USER,MATRIX_PASS,MATRIX_HOMESERVERenv 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
# 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:
# 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:
# 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_statedict 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, extendingbase.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:
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:
{
"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:
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