Skill Tracer
Skills › Scientific Agent Skills › infographics › generate_infographic.py

generate_infographic.py

303 lines · skills/infographics/scripts/generate_infographic.py at 5f2bc03

1#!/usr/bin/env python3
2"""
3Generate professional infographics using Nano Banana 2.
4
5This script generates infographics with smart iterative refinement:
6- Uses Nano Banana 2 (Gemini 3.1 Flash Image) for generation
7- Uses Gemini 3.7 Flash for quality review
8- Only regenerates if quality is below threshold
9- Supports 10 infographic types and industry style presets
11Usage:
12 python generate_infographic.py "5 benefits of exercise" -o benefits.png --type list
13 python generate_infographic.py "Company history 2010-2025" -o timeline.png --type timeline --style corporate
14 python generate_infographic.py --list-options
15"""
17import argparse
18import os
19import subprocess
20import sys
21from pathlib import Path
24# Available options for quick reference
25INFOGRAPHIC_TYPES = [
26 "statistical", "timeline", "process", "comparison", "list",
27 "geographic", "hierarchical", "anatomical", "resume", "social"
30STYLE_PRESETS = [
31 "corporate", "healthcare", "technology", "nature", "education",
32 "marketing", "finance", "nonprofit"
35PALETTE_PRESETS = ["wong", "ibm", "tol"]
37DOC_TYPES = [
38 "marketing", "report", "presentation", "social", "internal", "draft", "default"
41# Variables forwarded to the generation subprocess. The child needs the
42# OpenRouter credential; the rest keep networking, TLS, and locale working.
43# Copying the whole parent environment instead would hand the child every
44# unrelated secret that happens to be exported in the calling shell.
45FORWARDED_ENV_VARS = (
46 "PATH", "HOME", "LANG", "LC_ALL", "TMPDIR", "PYTHONPATH",
47 "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
48 "http_proxy", "https_proxy", "no_proxy",
49 "SSL_CERT_FILE", "SSL_CERT_DIR", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE",
50 # Windows needs these for sockets, temp files, and interpreter startup.
51 "SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT",
52 "APPDATA", "LOCALAPPDATA", "USERPROFILE", "TEMP", "TMP",
56def resolve_api_key(explicit=None):
57 """Resolve the OpenRouter key from --api-key, the environment, then any .env file.
59 The .env scan walks up from the working directory and finally checks the
60 script's own directory, so running from anywhere inside a project picks up
61 the key at its root. The child process is handed the resolved value through
62 build_subprocess_env, so it never has to repeat this search.
63 """
64 if explicit:
65 return explicit
67 from_env = os.environ.get("OPENROUTER_API_KEY", "").strip()
68 if from_env:
69 return from_env
71 cwd = Path.cwd()
72 for directory in [cwd, *cwd.parents, Path(__file__).resolve().parent]:
73 env_file = directory / ".env"
74 if not env_file.is_file():
75 continue
76 try:
77 content = env_file.read_text(encoding="utf-8", errors="replace")
78 except OSError:
79 continue
80 for raw in content.splitlines():
81 line = raw.strip()
82 if line.startswith("#") or "=" not in line:
83 continue
84 name, _, value = line.partition("=")
85 if name.strip() == "OPENROUTER_API_KEY":
86 value = value.strip().strip('"').strip("'")
87 if value:
88 return value
90 return None
93def build_subprocess_env(api_key):
94 """Return a minimal environment for the AI generation subprocess."""
95 env = {name: os.environ[name] for name in FORWARDED_ENV_VARS if name in os.environ}
96 if api_key:
97 env["OPENROUTER_API_KEY"] = api_key
98 return env
101def list_options():
102 """Print available types, styles, and palettes."""
103 print("""
104+==============================================================================+
105| INFOGRAPHIC GENERATION OPTIONS |
106+==============================================================================+
108INFOGRAPHIC TYPES (--type):
109------------------------------------------------------------------------------
110 statistical Data-driven infographic with charts, numbers, and statistics
111 timeline Chronological events or milestones
112 process Step-by-step instructions or workflow
113 comparison Side-by-side comparison of options
114 list Tips, facts, or key points in list format
115 geographic Map-based data visualization
116 hierarchical Pyramid or organizational structure
117 anatomical Visual metaphor explaining a system
118 resume Professional skills and experience visualization
119 social Social media optimized content
121STYLE PRESETS (--style):
122------------------------------------------------------------------------------
123 corporate Navy/gold, professional business style
124 healthcare Blue/cyan, trust-inducing medical style
125 technology Blue/violet, modern tech style
126 nature Green/brown, environmental organic style
127 education Blue/coral, friendly academic style
128 marketing Coral/teal/yellow, bold vibrant style
129 finance Navy/gold, conservative professional style
130 nonprofit Orange/sage/sand, warm human-centered style
132COLORBLIND-SAFE PALETTES (--palette):
133------------------------------------------------------------------------------
134 wong Okabe-Ito/Wong (7 chromatic colors plus black)
135 ibm Legacy IBM preset (5 data colors)
136 tol Tol's muted qualitative (9 data colors)
138DOCUMENT TYPES (--doc-type):
139------------------------------------------------------------------------------
140 marketing 8.5/10 threshold - Marketing materials (highest quality)
141 report 8.0/10 threshold - Business reports
142 presentation 7.5/10 threshold - Slides and talks
143 social 7.0/10 threshold - Social media content
144 internal 7.0/10 threshold - Internal use
145 draft 6.5/10 threshold - Working drafts (lowest quality)
146 default 7.5/10 threshold - General purpose
148------------------------------------------------------------------------------
149Examples:
150 python generate_infographic.py "5 benefits of exercise" -o benefits.png --type list
151 python generate_infographic.py "AI adoption 2020-2025" -o timeline.png --type timeline --style technology
152 python generate_infographic.py "Product comparison" -o compare.png --type comparison --palette wong
154""")
157def main():
158 """Command-line interface."""
159 parser = argparse.ArgumentParser(
160 description="Generate infographics using Nano Banana 2 with smart iterative refinement",
161 formatter_class=argparse.RawDescriptionHelpFormatter,
162 epilog="""
163How it works:
164 1. (Optional) Research phase - gather facts using Perplexity Sonar
165 2. Describe your infographic in natural language
166 3. Nano Banana 2 generates it automatically with:
167 - Smart iteration (only regenerates if quality is below threshold)
168 - Quality review by Gemini 3.7 Flash
169 - Document-type aware quality thresholds
170 - Professional-quality output
172Examples:
173 # Simple list infographic
174 python generate_infographic.py "5 benefits of meditation" -o benefits.png --type list
176 # Corporate timeline
177 python generate_infographic.py "Company history 2010-2025" -o timeline.png --type timeline --style corporate
179 # Healthcare statistics with colorblind-safe colors
180 python generate_infographic.py "Heart disease statistics" -o stats.png --type statistical --style healthcare --palette wong
182 # Statistical infographic WITH RESEARCH for accurate data
183 python generate_infographic.py "Global AI market size and growth" -o ai_market.png --type statistical --research
185 # Social media infographic
186 python generate_infographic.py "Save water tips" -o water.png --type social --style marketing
188 # List all available options
189 python generate_infographic.py --list-options
191Environment Variables:
192 OPENROUTER_API_KEY Required for AI generation
193 """
194 )
196 parser.add_argument("prompt", nargs="?",
197 help="Description of the infographic content")
198 parser.add_argument("-o", "--output",
199 help="Output file path")
200 parser.add_argument("--type", "-t", choices=INFOGRAPHIC_TYPES,
201 help="Infographic type preset")
202 parser.add_argument("--style", "-s", choices=STYLE_PRESETS,
203 help="Industry style preset")
204 parser.add_argument("--palette", "-p", choices=PALETTE_PRESETS,
205 help="Colorblind-safe palette")
206 parser.add_argument("--background", "-b", default="white",
207 help="Background color (default: white)")
208 parser.add_argument("--doc-type", default="default", choices=DOC_TYPES,
209 help="Document type for quality threshold (default: default)")
210 parser.add_argument("--iterations", type=int, default=3,
211 help="Maximum refinement iterations (default: 3)")
212 parser.add_argument("--api-key",
213 help="OpenRouter API key (or use OPENROUTER_API_KEY env var)")
214 parser.add_argument("-v", "--verbose", action="store_true",
215 help="Verbose output")
216 parser.add_argument("--research", "-r", action="store_true",
217 help="Research the topic first using Perplexity Sonar for accurate data")
218 parser.add_argument("--context-image", action="append", default=[],
219 help="Reference image path; repeat for up to 14 images")
220 parser.add_argument("--list-options", action="store_true",
221 help="List all available types, styles, and palettes")
223 args = parser.parse_args()
225 # Handle --list-options
226 if args.list_options:
227 list_options()
228 return
230 # Validate required arguments
231 if not args.prompt:
232 parser.error("prompt is required unless using --list-options")
233 if not args.output:
234 parser.error("--output is required")
236 if args.iterations < 1:
237 parser.error("--iterations must be at least 1")
238 if Path(args.output).suffix.lower() != ".png":
239 parser.error("--output must end in .png")
240 if len(args.context_image) > 14:
241 parser.error("--context-image accepts at most 14 references")
243 # Check for API key — resolves --api-key, the environment, then any .env file
244 api_key = resolve_api_key(args.api_key)
245 if not api_key:
246 print("Error: OPENROUTER_API_KEY not found")
247 print("\nFor AI generation, you need an OpenRouter API key.")
248 print("Get one at: https://openrouter.ai/keys")
249 print("\nSet it with:")
250 print(" export OPENROUTER_API_KEY='your_api_key'")
251 print("\nOr add OPENROUTER_API_KEY=your_api_key to a .env file")
252 print("Or use --api-key flag")
253 sys.exit(1)
255 # Find AI generation script
256 script_dir = Path(__file__).parent
257 ai_script = script_dir / "generate_infographic_ai.py"
259 if not ai_script.exists():
260 print(f"Error: AI generation script not found: {ai_script}")
261 sys.exit(1)
263 # Build command
264 cmd = [sys.executable, str(ai_script), args.prompt, "-o", args.output]
266 if args.type:
267 cmd.extend(["--type", args.type])
269 if args.style:
270 cmd.extend(["--style", args.style])
272 if args.palette:
273 cmd.extend(["--palette", args.palette])
275 if args.background != "white":
276 cmd.extend(["--background", args.background])
278 if args.doc_type != "default":
279 cmd.extend(["--doc-type", args.doc_type])
281 if args.iterations != 3:
282 cmd.extend(["--iterations", str(args.iterations)])
284 if args.verbose:
285 cmd.append("-v")
287 if args.research:
288 cmd.append("--research")
290 for image_path in args.context_image:
291 cmd.extend(["--context-image", image_path])
293 # Execute — pass API key via environment to avoid exposure in process listings
294 try:
295 result = subprocess.run(cmd, check=False, env=build_subprocess_env(api_key))
296 sys.exit(result.returncode)
297 except Exception as e:
298 print(f"Error executing AI generation: {e}")
299 sys.exit(1)
302if __name__ == "__main__":
303 main()