- Add SMTP fields to TicketEmailSettings model (host, port, TLS/SSL, credentials, from email/name) - Update serializers with SMTP fields and is_smtp_configured flag - Add TicketEmailTestSmtpView for testing SMTP connections - Update frontend API types and hooks for SMTP settings - Add collapsible IMAP and SMTP configuration sections with "Configured" badges - Fix TypeScript errors in mockData.ts (missing required fields, type mismatches) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
144 lines
6.1 KiB
Python
144 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate plugin icons using Gemini 2.5 Flash Image API.
|
|
Based on: https://ai.google.dev/gemini-api/docs/image-generation
|
|
"""
|
|
from google import genai
|
|
from PIL import Image
|
|
import os
|
|
import time
|
|
|
|
# API Key
|
|
client = genai.Client(api_key="AIzaSyBF3_KnttSerq2BK2CaRKSTJHN8BoXp6Hw")
|
|
|
|
OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# Plugin configurations with improved prompts
|
|
plugins = [
|
|
{
|
|
'filename': 'daily-appointment-summary.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background transitioning from deep indigo (#4338ca) to vibrant blue (#3b82f6).
|
|
|
|
Central element: A stylized white clipboard or document with 3 horizontal lines representing a list, and a small clock or sun symbol in the top right corner indicating "daily".
|
|
|
|
Design principles: Minimalist, clean lines, no text, suitable for a scheduling/calendar application. The icon should be instantly recognizable as "daily summary" or "daily report" at small sizes like 32x32.'''
|
|
},
|
|
{
|
|
'filename': 'no-show-tracker.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background from deep red (#b91c1c) to coral red (#ef4444).
|
|
|
|
Central element: A white calendar or appointment card with a prominent empty circle or "absent" symbol - perhaps a chair silhouette with a question mark, or a clock with a slash through it indicating a missed appointment.
|
|
|
|
Design principles: Minimalist, clean lines, no text. The icon should clearly communicate "missed" or "no-show" at a glance. Professional warning aesthetic without being alarming.'''
|
|
},
|
|
{
|
|
'filename': 'birthday-greetings.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background from magenta (#c026d3) to pink (#ec4899).
|
|
|
|
Central element: A white birthday cake with 2-3 simple candles, or a wrapped gift box with a ribbon bow. Add subtle confetti dots or small stars around it for a celebratory feel.
|
|
|
|
Design principles: Minimalist, cheerful but professional, clean lines, no text. Should feel warm and celebratory while still fitting a business application aesthetic.'''
|
|
},
|
|
{
|
|
'filename': 'monthly-revenue-report.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background from emerald green (#059669) to teal (#14b8a6).
|
|
|
|
Central element: A white line chart showing an upward trend with 3-4 data points connected by lines, or ascending bar chart with dollar sign integrated subtly. The graph should clearly show growth/success.
|
|
|
|
Design principles: Minimalist, professional finance/analytics aesthetic, clean lines, no text. Should instantly communicate "revenue" and "growth" at small sizes.'''
|
|
},
|
|
{
|
|
'filename': 'appointment-reminder-24hr.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background from amber (#d97706) to orange (#f97316).
|
|
|
|
Central element: A white notification bell with a small circular badge, combined with a "24" numeral or a clock face showing time. The bell should be the primary focus with the time element secondary.
|
|
|
|
Design principles: Minimalist, attention-grabbing but professional, clean lines, no text except possibly "24". Should clearly indicate "reminder" and "advance notice" at small sizes.'''
|
|
},
|
|
{
|
|
'filename': 'inactive-customer-reengagement.png',
|
|
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
|
|
|
Style: Modern gradient background from violet (#7c3aed) to purple (#a855f7).
|
|
|
|
Central element: A white person silhouette or user icon with a curved "return" arrow circling back to them, or a magnet symbol attracting a small person icon. Should convey "bringing customers back".
|
|
|
|
Design principles: Minimalist, warm and welcoming professional aesthetic, clean lines, no text. Should communicate "re-engagement" or "win back" at a glance.'''
|
|
}
|
|
]
|
|
|
|
|
|
def generate_logo(prompt: str, filename: str) -> bool:
|
|
"""Generate a logo using Gemini 2.5 Flash Image (per official docs)"""
|
|
print(f"\nGenerating {filename}...")
|
|
|
|
try:
|
|
# Using the exact format from Google's documentation
|
|
response = client.models.generate_content(
|
|
model='gemini-2.5-flash-image',
|
|
contents=[prompt],
|
|
)
|
|
|
|
# Process response per docs
|
|
for part in response.parts:
|
|
if part.inline_data is not None:
|
|
# Get the raw image data and convert to PIL
|
|
from io import BytesIO
|
|
image_data = part.inline_data.data
|
|
pil_image = Image.open(BytesIO(image_data))
|
|
|
|
# Resize to 256x256 for consistency
|
|
if pil_image.size != (256, 256):
|
|
pil_image = pil_image.resize((256, 256), Image.Resampling.LANCZOS)
|
|
|
|
output_path = os.path.join(OUTPUT_DIR, filename)
|
|
pil_image.save(output_path, 'PNG')
|
|
print(f" ✓ Saved: {output_path}")
|
|
return True
|
|
elif part.text is not None:
|
|
print(f" Model text: {part.text[:100]}...")
|
|
|
|
print(" ✗ No image in response")
|
|
return False
|
|
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
if "RESOURCE_EXHAUSTED" in error_msg or "quota" in error_msg.lower():
|
|
print(f" ✗ Quota exceeded - try again later")
|
|
else:
|
|
print(f" ✗ Error: {type(e).__name__}: {error_msg[:300]}")
|
|
return False
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("Generating Plugin Icons with Gemini 2.5 Flash Image")
|
|
print("=" * 60)
|
|
|
|
success_count = 0
|
|
for i, plugin in enumerate(plugins):
|
|
if i > 0:
|
|
print("\nWaiting 5 seconds...")
|
|
time.sleep(5)
|
|
|
|
if generate_logo(plugin['prompt'], plugin['filename']):
|
|
success_count += 1
|
|
|
|
print("\n" + "=" * 60)
|
|
print(f"Complete: {success_count}/{len(plugins)} icons generated")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|