280 lines
9.3 KiB
Python
280 lines
9.3 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
quiXzoom Academy — Gemini Bridge via AAMOS
|
||
|
|
Använder AAMOS Academy API för att generera bilder med Gemini
|
||
|
|
"""
|
||
|
|
|
||
|
|
import requests
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import base64
|
||
|
|
from datetime import datetime
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
# AAMOS API-konfiguration
|
||
|
|
AAMOS_BASE_URL = "http://localhost:3100"
|
||
|
|
ACADEMY_API = f"{AAMOS_BASE_URL}/api/aamos/academy"
|
||
|
|
|
||
|
|
# S3-konfiguration
|
||
|
|
S3_BUCKET = "quixzoom-landing-prod"
|
||
|
|
ACADEMY_IMAGES_PATH = "academy/images"
|
||
|
|
|
||
|
|
# Lektionsbilder som ska genereras
|
||
|
|
LESSON_IMAGES = {
|
||
|
|
"m1l1": {
|
||
|
|
"title": "Välkommen till quiXzoom",
|
||
|
|
"prompt": """Generera en vänlig illustration för en online-utbildning om crowdsourcing-fotografering.
|
||
|
|
|
||
|
|
Scen: En person med smartphone fotograferar en bro i solnedgång. Personen ser glad och professionell ut.
|
||
|
|
Stil: Modern, ren illustration (inte fotorealistisk)
|
||
|
|
Färger: Blå (#0066FF), grön (#00C853), svart (#0A0A0A), vita accenter
|
||
|
|
Format: 16:9 bredbild
|
||
|
|
Ingen text i bilden
|
||
|
|
Pedagogisk och inspirerande känsla"""
|
||
|
|
},
|
||
|
|
"m2l1": {
|
||
|
|
"title": "AI-granskning av bilder",
|
||
|
|
"prompt": """Generera en teknisk illustration som visar AI-analys av en fotografi.
|
||
|
|
|
||
|
|
Scen: Dela bilden i två delar. Vänster: Originalbild på en bro. Höger: Samma bild med AI-analys-overlay som markerar skärpa, ljus, komposition med färgkodade indikatorer.
|
||
|
|
Stil: Futuristisk men pedagogisk
|
||
|
|
Färger: Blå (#0066FF), grön (#00C853), orange (#FF6B35) för markeringar
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m2l2": {
|
||
|
|
"title": "Ljus och exponering",
|
||
|
|
"prompt": """Generera en pedagogisk jämförelsebild för fotoutbildning.
|
||
|
|
|
||
|
|
Scen: Tre bilder i rad som visar samma bro från samma vinkel:
|
||
|
|
1. Vänster: Underexponerad (för mörk)
|
||
|
|
2. Mitten: Perfekt exponerad (gyllene timmen, varmt ljus)
|
||
|
|
3. Höger: Överexponerad (för ljus, utbrända detaljer)
|
||
|
|
Stil: Fotorealistisk
|
||
|
|
Format: 16:9 (tre paneler)
|
||
|
|
Ingen text, men tydlig visuell skillnad"""
|
||
|
|
},
|
||
|
|
"m2l3": {
|
||
|
|
"title": "Fokus och skärpa",
|
||
|
|
"prompt": """Generera en pedagogisk bild som visar skillnaden mellan oskarp och skarp fotografi.
|
||
|
|
|
||
|
|
Scen: Två bilder som visar en vägskylt:
|
||
|
|
1. Vänster: Suddig, oskarp bild
|
||
|
|
2. Höger: Knivskarp bild med tydliga detaljer
|
||
|
|
Lägg till ett förstoringsglas-effekt som visar detaljerna tydligare på den skarpa bilden.
|
||
|
|
Stil: Fotorealistisk med grafiska element
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m2l4": {
|
||
|
|
"title": "Framing och komposition",
|
||
|
|
"prompt": """Generera en pedagogisk diagram-bild för fotokomposition.
|
||
|
|
|
||
|
|
Scen: En bro fotograferad med korrekt komposition. Overlay som visar:
|
||
|
|
- Rule of thirds grid (3x3 linjer)
|
||
|
|
- Huvudobjektet centrerat vid skärningspunkter
|
||
|
|
- Pil som pekar på huvudobjektet
|
||
|
|
- Grön markering för "bra" komposition
|
||
|
|
Stil: Fotorealistisk med pedagogiska overlay-element
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m3l1": {
|
||
|
|
"title": "GPS-accuracy",
|
||
|
|
"prompt": """Generera en teknisk illustration om GPS-teknik.
|
||
|
|
|
||
|
|
Scen: En karta över en stad sett uppifrån. En smartphone i mitten som tar emot signaler från 4 GPS-satelliter ovanför (visa som linjer från satelliter till telefon). Färgkodad accuracy-cirkel runt telefonen:
|
||
|
|
- Grön cirkel: Noggrann position (liten cirkel)
|
||
|
|
- Gul cirkel: Medel (större cirkel)
|
||
|
|
- Röd cirkel: Låg accuracy (stor cirkel)
|
||
|
|
Stil: Modern, teknisk illustration
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m4l1": {
|
||
|
|
"title": "Personlig säkerhet i fält",
|
||
|
|
"prompt": """Generera en illustration om säker fotografering.
|
||
|
|
|
||
|
|
Scen: En person (Zoomer) med reflexväst och smartphone fotograferar en bro från ett säkert avstånd. Visa:
|
||
|
|
- Reflexväst på personen
|
||
|
|
- Trafikkegel som markerar säker zon
|
||
|
|
- Säkerhetsavstånd markerat med streckad linje
|
||
|
|
- Bil som passerar på håll
|
||
|
|
Stil: Modern, vänlig illustration
|
||
|
|
Färger: Orange varselväst, blå (#0066FF), grön (#00C853)
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m5l1": {
|
||
|
|
"title": "Batch-fotografering",
|
||
|
|
"prompt": """Generera en karta-baserad illustration för effektiv fotografering.
|
||
|
|
|
||
|
|
Scen: En stiliserad karta med flera fotopunkter (A, B, C, D, E) som är kopplade med en optimerad rutt (grön linje). En person med smartphone och cykel/bil följer rutten. Visa att personen fotograferar flera platser effektivt.
|
||
|
|
Stil: Modern karta/illustration
|
||
|
|
Färger: Grön rutt (#00C853), blå punkter (#0066FF), neutral bakgrund
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text"""
|
||
|
|
},
|
||
|
|
"m6l1": {
|
||
|
|
"title": "Utbetalningsflöde",
|
||
|
|
"prompt": """Generera ett flödesdiagram för utbetalningsprocess.
|
||
|
|
|
||
|
|
Scen: Ett horisontellt flöde som visar:
|
||
|
|
1. Godkänd fotografi (grön bock)
|
||
|
|
2. AI-granskning (robot-ikon)
|
||
|
|
3. Godkännande (stämpel)
|
||
|
|
4. Utbetalning (pengar/bank)
|
||
|
|
5. Pengar på kontot (glad person)
|
||
|
|
Stil: Modern, ren illustration med ikoner
|
||
|
|
Färger: Grön (#00C853) för framsteg, blå (#0066FF) för process
|
||
|
|
Format: 16:9
|
||
|
|
Ingen text, bara ikoner och pilar"""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
def generate_with_aamos(prompt: str, provider: str = "gemini") -> dict:
|
||
|
|
"""Generera innehåll via AAMOS Academy API"""
|
||
|
|
|
||
|
|
url = f"{ACADEMY_API}/ai/generate"
|
||
|
|
|
||
|
|
payload = {
|
||
|
|
"prompt": prompt,
|
||
|
|
"provider": provider,
|
||
|
|
"maxTokens": 2000
|
||
|
|
}
|
||
|
|
|
||
|
|
try:
|
||
|
|
response = requests.post(url, json=payload, timeout=30)
|
||
|
|
|
||
|
|
if response.status_code == 200:
|
||
|
|
return response.json()
|
||
|
|
else:
|
||
|
|
print(f" ⚠️ AAMOS API fel: {response.status_code}")
|
||
|
|
return {"error": f"HTTP {response.status_code}"}
|
||
|
|
|
||
|
|
except requests.exceptions.ConnectionError:
|
||
|
|
print(f" ⚠️ Kunde inte ansluta till AAMOS på {AAMOS_BASE_URL}")
|
||
|
|
return {"error": "Connection failed"}
|
||
|
|
except Exception as e:
|
||
|
|
print(f" ⚠️ Fel: {e}")
|
||
|
|
return {"error": str(e)}
|
||
|
|
|
||
|
|
def generate_image_description(lesson_id: str) -> str:
|
||
|
|
"""Generera bildbeskrivning via AAMOS Gemini"""
|
||
|
|
|
||
|
|
lesson = LESSON_IMAGES.get(lesson_id)
|
||
|
|
if not lesson:
|
||
|
|
return None
|
||
|
|
|
||
|
|
print(f"🎨 Genererar bild för: {lesson['title']}")
|
||
|
|
|
||
|
|
# Använd AAMOS för att generera en detaljerad bildbeskrivning
|
||
|
|
# (Gemini text-generering, inte bildgenerering ännu)
|
||
|
|
result = generate_with_aamos(lesson['prompt'], "gemini")
|
||
|
|
|
||
|
|
if 'text' in result:
|
||
|
|
return result['text']
|
||
|
|
else:
|
||
|
|
return None
|
||
|
|
|
||
|
|
def save_image_metadata(lesson_id: str, description: str):
|
||
|
|
"""Spara bild-metadata för senare generering"""
|
||
|
|
|
||
|
|
metadata = {
|
||
|
|
"lesson_id": lesson_id,
|
||
|
|
"title": LESSON_IMAGES[lesson_id]['title'],
|
||
|
|
"description": description,
|
||
|
|
"prompt": LESSON_IMAGES[lesson_id]['prompt'],
|
||
|
|
"generated_at": datetime.utcnow().isoformat(),
|
||
|
|
"status": "description_ready",
|
||
|
|
"image_url": None
|
||
|
|
}
|
||
|
|
|
||
|
|
# Spara till lokal fil
|
||
|
|
output_dir = Path("academy-generated")
|
||
|
|
output_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
with open(output_dir / f"{lesson_id}.json", "w") as f:
|
||
|
|
json.dump(metadata, f, indent=2, ensure_ascii=False)
|
||
|
|
|
||
|
|
return metadata
|
||
|
|
|
||
|
|
def generate_all_images():
|
||
|
|
"""Generera alla bildbeskrivningar för Academy"""
|
||
|
|
|
||
|
|
print("🚀 quiXzoom Academy — Bildgenerering via AAMOS Gemini")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
results = []
|
||
|
|
|
||
|
|
for lesson_id, lesson in LESSON_IMAGES.items():
|
||
|
|
print(f"\n📚 {lesson['title']}")
|
||
|
|
|
||
|
|
# Generera beskrivning via AAMOS
|
||
|
|
description = generate_image_description(lesson_id)
|
||
|
|
|
||
|
|
if description:
|
||
|
|
# Spara metadata
|
||
|
|
metadata = save_image_metadata(lesson_id, description)
|
||
|
|
results.append(metadata)
|
||
|
|
print(f" ✅ Beskrivning genererad")
|
||
|
|
else:
|
||
|
|
print(f" ❌ Kunde inte generera")
|
||
|
|
|
||
|
|
# Spara sammanfattning
|
||
|
|
summary = {
|
||
|
|
"generated_at": datetime.utcnow().isoformat(),
|
||
|
|
"total_images": len(LESSON_IMAGES),
|
||
|
|
"successful": len(results),
|
||
|
|
"failed": len(LESSON_IMAGES) - len(results),
|
||
|
|
"images": results
|
||
|
|
}
|
||
|
|
|
||
|
|
with open("academy-generated/summary.json", "w") as f:
|
||
|
|
json.dump(summary, f, indent=2, ensure_ascii=False)
|
||
|
|
|
||
|
|
print(f"\n{'=' * 60}")
|
||
|
|
print(f"✅ {len(results)}/{len(LESSON_IMAGES)} bildbeskrivningar genererade")
|
||
|
|
print(f"📄 Sparade i academy-generated/")
|
||
|
|
|
||
|
|
return summary
|
||
|
|
|
||
|
|
def upload_to_s3(image_path: str, s3_key: str) -> str:
|
||
|
|
"""Ladda upp bild till S3"""
|
||
|
|
import subprocess
|
||
|
|
|
||
|
|
cmd = [
|
||
|
|
"aws", "s3", "cp", image_path,
|
||
|
|
f"s3://{S3_BUCKET}/{s3_key}",
|
||
|
|
"--acl", "public-read",
|
||
|
|
"--content-type", "image/png"
|
||
|
|
]
|
||
|
|
|
||
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||
|
|
|
||
|
|
if result.returncode == 0:
|
||
|
|
url = f"https://{S3_BUCKET}.s3.eu-north-1.amazonaws.com/{s3_key}"
|
||
|
|
print(f" ✅ Uppladdad: {url}")
|
||
|
|
return url
|
||
|
|
else:
|
||
|
|
print(f" ❌ Fel: {result.stderr}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
import sys
|
||
|
|
|
||
|
|
if len(sys.argv) > 1 and sys.argv[1] == "--generate":
|
||
|
|
generate_all_images()
|
||
|
|
else:
|
||
|
|
print("Användning:")
|
||
|
|
print(" python academy-gemini-bridge.py --generate")
|
||
|
|
print("")
|
||
|
|
print("Detta kommer att:")
|
||
|
|
print(" 1. Ansluta till AAMOS Academy API (localhost:3100)")
|
||
|
|
print(" 2. Generera bildbeskrivningar med Gemini")
|
||
|
|
print(" 3. Spara metadata för varje bild")
|
||
|
|
print("")
|
||
|
|
print("När bildbeskrivningarna är klara kan du:")
|
||
|
|
print(" - Använda dem för att generera riktiga bilder")
|
||
|
|
print(" - Kopiera in dem i Academy-lektionerna")
|