Files
boc/generate-academy-images.py
T

225 lines
8.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
quiXzoom Academy — Bildgenerator
Använder OpenAI DALL-E 3 för att generera pedagogiska bilder
"""
import os
import requests
import json
from datetime import datetime
from pathlib import Path
# OpenAI API-nyckel (från AAMOS)
OPENAI_API_KEY = "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"
# S3-konfiguration
S3_BUCKET = "quixzoom-landing-prod"
# Bilder att generera
IMAGES = {
"m1l1": {
"title": "Välkommen till quiXzoom",
"prompt": "A friendly illustration of a person with a smartphone photographing a bridge at sunset. Modern, clean style with blue (#0066FF) and green (#00C853) colors. Pedagogical feel, not photorealistic. 16:9 format. The person looks happy and professional. Warm lighting."
},
"m2l1": {
"title": "AI-granskning av bilder",
"prompt": "A technical illustration showing AI analysis of a photograph. Split image: left side shows original photo of a bridge, right side shows AI analysis overlay with color-coded indicators marking sharpness, lighting, and composition. Futuristic but pedagogical style. Blue and green accents."
},
"m2l2": {
"title": "Ljus och exponering",
"prompt": "Three photos in a row showing the same bridge from the same angle: 1. Left: Underexposed (too dark, details lost), 2. Middle: Perfectly exposed (golden hour, warm light, all details visible), 3. Right: Overexposed (too bright, washed out). Photorealistic style. Educational comparison."
},
"m2l3": {
"title": "Fokus och skärpa",
"prompt": "Two photos showing a road sign: left image is blurry and out of focus, right image is razor sharp with a magnifying glass effect showing crisp details. Photorealistic with graphic overlay elements. Educational comparison style."
},
"m2l4": {
"title": "Framing och komposition",
"prompt": "A bridge photographed with correct composition. Overlay showing rule of thirds grid lines, arrow pointing to main subject, green markings indicating good composition. Educational diagram style with photo-realistic base. Clean and modern."
},
"m3l1": {
"title": "GPS-accuracy",
"prompt": "A technical illustration of a city map from above with a smartphone in the center. 4 GPS satellites above sending signals to the phone. Color-coded accuracy circles around the phone: green (accurate, small circle), yellow (medium), red (low accuracy, large circle). Modern, clean style."
},
"m4l1": {
"title": "Personlig säkerhet",
"prompt": "A person wearing a reflective safety vest and holding a smartphone, photographing a bridge from a safe distance. Traffic cone, safety distance marked with dashed lines, passing car in background. Modern, friendly illustration style. Blue and orange accents."
},
"m5l1": {
"title": "Batch-fotografering",
"prompt": "A stylized map illustration with multiple photo points (A, B, C, D, E) connected by an optimized green route. A person with smartphone efficiently following the route. Modern map style with blue and green colors. Clean and pedagogical."
},
"m6l1": {
"title": "Utbetalningsflöde",
"prompt": "A horizontal flow diagram showing: 1. Approved photo (green checkmark) → 2. AI review (robot icon) → 3. Approval (stamp) → 4. Payment (money/bank icon) → 5. Money in account (happy person). Modern, clean illustration with icons and arrows. Green and blue colors."
}
}
def generate_image(image_id: str, prompt: str) -> str:
"""Generera bild med DALL-E 3"""
url = "https://api.openai.com/v1/images/generations"
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "dall-e-3",
"prompt": prompt,
"size": "1792x1024",
"quality": "standard",
"n": 1
}
try:
response = requests.post(url, headers=headers, json=payload, timeout=60)
if response.status_code == 200:
data = response.json()
image_url = data['data'][0]['url']
return image_url
else:
print(f" ❌ OpenAI fel: {response.status_code}")
print(f" {response.text[:200]}")
return None
except Exception as e:
print(f" ❌ Fel: {e}")
return None
def download_image(url: str, filename: str) -> bool:
"""Ladda ner bild från URL"""
try:
response = requests.get(url, timeout=30)
if response.status_code == 200:
with open(filename, 'wb') as f:
f.write(response.content)
return True
else:
print(f" ❌ Kunde inte ladda ner bild: {response.status_code}")
return False
except Exception as e:
print(f" ❌ Fel vid nedladdning: {e}")
return False
def upload_to_s3(local_path: str, s3_key: str) -> str:
"""Ladda upp bild till S3"""
import subprocess
cmd = [
"aws", "s3", "cp", local_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" ❌ S3-fel: {result.stderr}")
return None
def generate_all_images():
"""Generera alla bilder för Academy"""
print("🎨 quiXzoom Academy — Bildgenerator (DALL-E 3)")
print("=" * 60)
# Skapa output-mapp
output_dir = Path("academy-images")
output_dir.mkdir(exist_ok=True)
results = []
for image_id, image_data in IMAGES.items():
print(f"\n📚 {image_data['title']}")
print(f" ID: {image_id}")
# Generera bild
image_url = generate_image(image_id, image_data['prompt'])
if image_url:
print(f" ✅ Bild genererad")
print(f" 📥 Laddar ner...")
# Ladda ner bild
local_path = output_dir / f"{image_id}.png"
if download_image(image_url, str(local_path)):
print(f" ✅ Sparad: {local_path}")
# Ladda upp till S3
s3_key = f"academy/images/{image_id}.png"
s3_url = upload_to_s3(str(local_path), s3_key)
if s3_url:
results.append({
"id": image_id,
"title": image_data['title'],
"url": s3_url,
"status": "completed"
})
else:
print(f" ❌ Kunde inte ladda ner")
else:
print(f" ❌ Kunde inte generera bild")
# Spara sammanfattning
summary = {
"generated_at": datetime.utcnow().isoformat(),
"total_images": len(IMAGES),
"successful": len(results),
"failed": len(IMAGES) - len(results),
"images": results
}
with open(output_dir / "summary.json", "w") as f:
json.dump(summary, f, indent=2)
print(f"\n{'=' * 60}")
print(f"{len(results)}/{len(IMAGES)} bilder genererade och uppladdade")
print(f"📄 Sammanfattning: academy-images/summary.json")
return summary
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == "--generate":
generate_all_images()
else:
print("Användning:")
print(" python generate-academy-images.py --generate")
print("")
print("Detta kommer att:")
print(" 1. Generera 9 pedagogiska bilder med DALL-E 3")
print(" 2. Ladda ner bilderna lokalt")
print(" 3. Ladda upp till S3 (quixzoom-landing-prod)")
print("")
print("Kostnad: ~$0.20 per bild (totalt ~$1.80)")
print("")
# Testa API-nyckel
print("Testar API-nyckel...")
test = requests.get(
"https://api.openai.com/v1/models",
headers={"Authorization": f"Bearer {OPENAI_API_KEY}"}
)
if test.status_code == 200:
print("✅ API-nyckel fungerar!")
print(f" Tillgängliga modeller: {len(test.json().get('data', []))}")
else:
print(f"❌ API-nyckel fungerar inte: {test.status_code}")