design final

This commit is contained in:
Aron Petau 2025-10-07 17:48:55 +02:00
parent 99a690972e
commit 2d69d919a6
9 changed files with 149 additions and 227 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -1,78 +0,0 @@
# Development Setup Guide
## Quick Start for Local Development
The `run_app.py` script mimics Docker production conditions while enabling fast iteration.
### Standard Development Mode (Recommended)
```bash
python run_app.py
```
**Features:**
- ✓ Loads environment variables from `.env` file
- ✓ Single Uvicorn worker with **fast auto-reload**
- ✓ Mimics Docker environment (`PYTHONUNBUFFERED=1`)
- ✓ Creates upload directory automatically
- ✓ Binds to `0.0.0.0:8000` (same as Docker)
**Best for:** Quick code iterations, template changes, debugging
### Production-Like Mode
```bash
python run_app.py --gunicorn
```
**Features:**
- ✓ Gunicorn with 4 Uvicorn workers (exactly like Docker)
- ✓ 120s timeout (same as Docker)
- ✓ Gunicorn's reload (slower but tests production server)
- ✓ Tests multi-worker behavior
**Best for:** Testing before deploying to Docker, load testing
## Environment Variables
The script automatically loads `.env` file variables:
- `RATE_PER_M2_BLACK` - Black & white print rate (default: 4.0)
- `RATE_PER_M2_COLOR` - Color print rate (default: 5.0)
- `SERVER_HOSTNAME` - Server hostname (default: einszwovier.local)
- `BOOKSTACK_PORT`, `OPENWEBUI_PORT`, `PORTAINER_PORT` - Service ports
- `MATRIX_USER`, `MATRIX_PASS`, `MATRIX_HOMESERVER` - Matrix integration
## Comparison: Dev vs Docker
| Feature | `python run_app.py` | `docker-compose up` |
|---------|---------------------|---------------------|
| Server | Uvicorn (single worker) | Gunicorn + Uvicorn (4 workers) |
| Auto-reload | ✓ Fast (watches all files) | ✓ Slow (Docker rebuild) |
| Env loading | `.env` file | `.env` file |
| Port | 8000 | 80→8000 |
| Memory limit | None | 1GB limit |
| Health checks | No | Yes |
| Dependencies | All services | Isolated |
## Troubleshooting
### `.env` file not found
Create `.env` file from template:
```bash
cp .env.example .env
```
### Port 8000 already in use
```bash
# Find and kill process
lsof -ti:8000 | xargs kill -9
```
### Environment variables not loading
Ensure `python-dotenv` is installed:
```bash
pip install python-dotenv
```
### Testing Matrix integration locally
1. Start Matrix server: `docker-compose up synapse -d`
2. Run app: `python run_app.py`
3. Submit test order via `/cost` endpoint

View file

@ -1,37 +0,0 @@
services:
bookstack:
image: lscr.io/linuxserver/bookstack:v25.07.2-ls220
container_name: bookstack
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- APP_URL=http://einszwovier.local:6875
- APP_KEY=base64:3qjlIoUX4Tw6fUQgZcxMbz6lb8+dAzqpvItqHvahW1c=
- DB_HOST=bookstack-mariadb
- DB_PORT=3306
- DB_DATABASE=bookstack
- DB_USERNAME=bookstack
- DB_PASSWORD=bookstack8432
volumes:
- ./bookstack_app_data:/config
ports:
- 6875:80
restart: unless-stopped
depends_on:
- bookstack-mariadb
bookstack-mariadb:
image: lscr.io/linuxserver/mariadb:11.4.4
container_name: bookstack-mariadb
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- MYSQL_ROOT_PASSWORD=mysupersecretrootpassword
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=bookstack8432
volumes:
- ./bookstack_db_data:/config
restart: unless-stopped

View file

@ -101,7 +101,8 @@ async def about(request: Request):
return templates.TemplateResponse("about.html", {
"request": request,
"courses": courses
"courses": courses,
"bookstack_url": f"http://{SERVER_HOSTNAME}:{BOOKSTACK_PORT}"
})
@app.get("/cost", response_class=HTMLResponse)

View file

@ -1,12 +0,0 @@
services:
synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
restart: always
ports:
- "8008:8008" # client-server API (internal)
volumes:
- ./matrix/data:/data
environment:
- SYNAPSE_SERVER_NAME=localhost
- SYNAPSE_REPORT_STATS=no

View file

@ -616,6 +616,109 @@ th {
color: #FFFFFF;
}
.info-box {
background-color: #e3f2fd;
border-left: 4px solid #1976d2;
border-radius: 8px;
padding: 1.5em;
margin: 1.5em 0;
line-height: 1.6;
}
.info-box p {
margin: 0.5em 0;
text-align: left;
}
.info-box p:first-child {
margin-top: 0;
}
.info-box p:last-child {
margin-bottom: 0;
}
.info-box a {
color: #1976d2;
font-weight: 600;
text-decoration: none;
}
.info-box a:hover {
text-decoration: underline;
}
/* Pricing Card - Prominent pricing display */
.pricing-card {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 2px solid #28a745;
border-radius: 12px;
padding: 2em;
margin: 2em 0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.pricing-card h2 {
color: #28a745;
font-size: 1.8rem;
margin-bottom: 1.5em;
text-align: center;
}
.price-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5em;
margin-bottom: 1.5em;
}
.price-item {
background-color: white;
border-radius: 8px;
padding: 1.5em;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.price-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}
.price-item.bw {
border-top: 4px solid #6c757d;
}
.price-item.color {
border-top: 4px solid #fd7e14;
}
.price-label {
display: block;
font-size: 1.1rem;
color: #6c757d;
margin-bottom: 0.5em;
font-weight: 600;
}
.price-value {
display: block;
font-size: 2rem;
color: #1C1C1E;
font-weight: bold;
font-family: 'HealTheWebB', sans-serif;
}
.pricing-note {
text-align: center;
color: #6c757d;
font-size: 0.95rem;
margin: 0;
padding-top: 1em;
border-top: 1px solid #dee2e6;
}
.disclaimer-alert {
margin-top: 10px;
padding: 10px 15px;

View file

@ -11,7 +11,9 @@
<p>Seit Dezember 2024 trägt unser Maker Space den Namen <strong>studio einszwovier</strong>. Er ist ein
innovativer, digitaler Lernraum, der Kreativität, Technik und Bildungsgerechtigkeit verbindet. Hier wird
„Making“ erlebbar: Lernende gestalten ihren Lernprozess aktiv, entdecken individuelle Stärken und erleben
durch Selbstwirksamkeit besondere Motivation.</p>
durch Selbstwirksamkeit besondere Motivation.
Betreut wird der Maker Space von <strong>Aron Petau</strong> und <strong>Friedrich Weber</strong>. Einfach vorbeischauen, Ideen vorstellen und loslegen!
</p>
</section>
<section>
@ -27,25 +29,24 @@
<li><strong>Drohnen:</strong> Flugexperimente und Luftbildfotografie.</li>
<li><strong>LEGO SPIKE Roboter:</strong> Spielerisches Erlernen von Robotik und Programmierung.</li>
</ul>
Wenn du mehr über unsere Ausstattung erfahren möchtest oder spezielle Geräte suchst, schau in unsere
<a href="{{ bookstack_url }}" target="_blank">Wissenssammlung</a> oder frag uns direkt im Studio!
</section>
<section>
<h2>Betreuungsteam</h2>
<p>Betreut wird der Maker Space von <strong>Aron Petau</strong> und <strong>Friedrich Weber</strong>. Sie sind
von Dienstag bis Donnerstag von 11:00 bis 16:00 Uhr vor Ort. Einfach vorbeischauen, Ideen vorstellen und loslegen!
</p>
</section>
<section>
<h2>Öffnungszeiten</h2>
<h2>Öffnungszeiten + Kontakt</h2>
<p>Dienstag bis Donnerstag: 11:00 16:00 Uhr<br>
Raum 124, Gabriele-von-Bülow-Gymnasium</p>
<p>E-Mail: <a href="mailto:einszwovier@gvb-gymnasium.de">einszwovier@gvb-gymnasium.de</a></p>
<h2>Standort</h2>
<p>Gabriele-von-Bülow-Gymnasium<br>
Tile-Brügge-Weg 63, 13509 Berlin (Tegel)<br>
Telefon: 030 21 00 52 460<br>
E-Mail: <a href="mailto:info@gvb-gymnasium.de">info@gvb-gymnasium.de</a></p>
</section>
<section>
<h2>Kontakt</h2>
<p>E-Mail: <a href="mailto:einszwovier@gvb-gymnasium.de">einszwovier@gvb-gymnasium.de</a></p>
</section>
<section>
<h2>Aktuelle Kurse</h2>
@ -114,13 +115,5 @@
}
});
</script>
<section>
<h2>Standort</h2>
<p>Gabriele-von-Bülow-Gymnasium<br>
Tile-Brügge-Weg 63, 13509 Berlin (Tegel)<br>
Telefon: 030 21 00 52 460<br>
E-Mail: <a href="mailto:info@gvb-gymnasium.de">info@gvb-gymnasium.de</a></p>
</section>
</div>
{% endblock %}

View file

@ -8,6 +8,36 @@
<div class="container">
<h1>Kostenrechner für Drucke</h1>
<div class="info-box">
<p>
<strong>So funktioniert's:</strong> Lade dein PDF hoch, um eine Kostenschätzung zu erhalten.
Du kannst beliebige PDFs hochladen, um die Kosten zu berechnen.
</p>
<p>
<strong>Wichtig:</strong> Schicke den Auftrag nur ab, wenn du die Datei wirklich drucken lassen möchtest!
Für reine Kostenschätzung reicht es, die Berechnung anzusehen, ohne den Auftrag zu senden.
</p>
<p>
<strong>Alternative:</strong> Du kannst uns deine Datei und Infos auch per E-Mail schicken:
<a href="mailto:einszwovier@gvb-gymnasium.de">einszwovier@gvb-gymnasium.de</a>
</p>
</div>
<div class="pricing-card">
<h2>📊 Aktuelle Preise</h2>
<div class="price-grid">
<div class="price-item bw">
<span class="price-label">Schwarz/Weiß</span>
<span class="price-value">{{ rate_black if rate_black else 'RATE_PER_M2_BLACK' }} €/m²</span>
</div>
<div class="price-item color">
<span class="price-label">Farbe</span>
<span class="price-value">{{ rate_color if rate_color else 'RATE_PER_M2_COLOR' }} €/m²</span>
</div>
</div>
<p class="pricing-note">💡 Die Preise decken nur Materialkosten(Tinte & Papier) kein Gewinn!</p>
</div>
{% if error %}
<p class="error">{{ error }}</p>
{% endif %}
@ -17,12 +47,6 @@
<button type="submit">Hochladen & Berechnen</button>
</form>
<p class="rate-info">
Preise werden über Umgebungsvariablen festgelegt:<br>
S/W: {{ rate_black if rate_black else 'RATE_PER_M2_BLACK' }} € / m²,
Farbe: {{ rate_color if rate_color else 'RATE_PER_M2_COLOR' }} € / m²
</p>
{% if result %}
<h2>Ergebnisse für {{ result.filename }}</h2>

View file

@ -1,72 +0,0 @@
import os
import asyncio
from io import BytesIO
from nio import AsyncClient, UploadResponse, RoomSendResponse
from dotenv import load_dotenv
load_dotenv()
async def main():
# Get credentials from environment (adjust if needed)
matrix_user = os.environ.get("MATRIX_USER", "@einszwovier_bot:localhost")
matrix_pass = os.environ.get("MATRIX_PASS")
homeserver = os.environ.get("MATRIX_HOMESERVER", "http://localhost:8008")
room_id = os.environ.get("MATRIX_ROOM") # e.g. "!abc123:localhost"
if not all([matrix_user, matrix_pass, room_id]):
raise RuntimeError("Missing MATRIX_USER, MATRIX_PASS or MATRIX_ROOM")
client = AsyncClient(homeserver, matrix_user)
login_resp = await client.login(matrix_pass)
if getattr(login_resp, "access_token", None) is None:
print("❌ Login failed:", login_resp)
return
print("✅ Logged in as", matrix_user)
pdf_path = "data/uploads/einszwovier infographics 2.pdf" # <-- put any small PDF here
with open(pdf_path, "rb") as f:
pdf_bytes = f.read()
# ✅ Upload PDF (nio returns (resp, err))
upload_resp, upload_err = await client.upload(
data_provider=BytesIO(pdf_bytes),
content_type="application/pdf",
filename=os.path.basename(pdf_path),
filesize=len(pdf_bytes),
)
if upload_err:
print("❌ Upload error:", upload_err)
await client.close()
return
if isinstance(upload_resp, UploadResponse) and upload_resp.content_uri:
print("✅ Upload succeeded:", upload_resp.content_uri)
else:
print("❌ Upload failed:", upload_resp)
await client.close()
return
# Send file message to room
file_resp = await client.room_send(
room_id=room_id,
message_type="m.room.message",
content={
"msgtype": "m.file",
"body": os.path.basename(pdf_path),
"url": upload_resp.content_uri,
},
)
if isinstance(file_resp, RoomSendResponse) and file_resp.event_id:
print("✅ PDF sent to room", room_id)
else:
print("❌ Failed to send PDF:", file_resp)
await client.logout()
await client.close()
if __name__ == "__main__":
asyncio.run(main())