#!/usr/bin/env python3 """Stadt-Symbolbilder fuer Busfahrt. Liest cities.json, generiert fuer jede Stadt mit imagePrompt das fehlende Bild via DALL-E-3 im verbindlichen GeoGraSim-Bildstil. Aufruf: set -a; source App/.env.local; set +a python3 App/sims/busfahrt/scripts/generate-city-images.py # alles fehlende python3 App/sims/busfahrt/scripts/generate-city-images.py tokio rom # gezielt Ueberschreiben: FORCE=1 python3 ... tokio """ import json, os, sys, time, urllib.request, urllib.error KEY = os.environ.get('OPENAI_API_KEY') if not KEY: sys.exit('OPENAI_API_KEY env var nicht gesetzt — vorher: set -a; source App/.env.local; set +a') SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) CITIES_FILE = os.path.join(SCRIPT_DIR, '..', 'assets', 'data', 'cities.json') IMG_DIR = os.path.join(SCRIPT_DIR, '..', 'assets', 'cities') os.makedirs(IMG_DIR, exist_ok=True) with open(CITIES_FILE, encoding='utf-8') as f: cities = json.load(f) force = os.environ.get('FORCE') == '1' arg_targets = set(sys.argv[1:]) todo = [] for city in cities: if not city.get('imagePrompt'): continue if arg_targets and city['id'] not in arg_targets: continue out_path = os.path.join(IMG_DIR, f"{city['id']}.png") if os.path.exists(out_path) and not force: continue todo.append((city, out_path)) if not todo: print("nichts zu tun — alle Bilder vorhanden (FORCE=1 zum Ueberschreiben)") sys.exit(0) print(f"-> generiere {len(todo)} Bilder ...") ok_count = 0 fail_count = 0 for i, (city, out_path) in enumerate(todo, 1): print(f"[{i}/{len(todo)}] {city['id']}: ", end='', flush=True) payload = json.dumps({ "model": "dall-e-3", "prompt": city['imagePrompt'], "n": 1, "size": "1792x1024", "quality": "standard", }).encode() req = urllib.request.Request( "https://api.openai.com/v1/images/generations", data=payload, headers={ "Authorization": f"Bearer {KEY}", "Content-Type": "application/json", }, ) try: with urllib.request.urlopen(req, timeout=180) as resp: data = json.loads(resp.read().decode()) url = data['data'][0]['url'] except urllib.error.HTTPError as e: body = e.read().decode() print(f"x HTTP {e.code} — {body[:200]}") fail_count += 1 time.sleep(5) continue except Exception as e: print(f"x {e}") fail_count += 1 time.sleep(5) continue try: urllib.request.urlretrieve(url, out_path) size = os.path.getsize(out_path) print(f"ok ({size//1024} KB)") ok_count += 1 except Exception as e: print(f"x download {e}") fail_count += 1 continue time.sleep(2.5) print(f"\nFertig: {ok_count} ok, {fail_count} Fehler")