#!/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}")