← back to Wallco Ai
scripts/draft_corey_letter.py
127 lines
#!/usr/bin/env python3
"""
Compose a Gmail draft to Corey with 6 wallco.ai design suggestions.
- Pulls the 6 most-recent SDXL designs from spoon_all_designs
- Base64-embeds 400×400 thumbnails inline in HTML
- POSTs to George /api/drafts on Mac1 (steve@designerwallcoverings.com)
- Leaves To: blank — Steve picks Corey in Gmail before sending
"""
import base64, io, json, os, subprocess, urllib.request
from pathlib import Path
from PIL import Image
GEORGE = 'http://100.107.67.67:9850'
# George basic-auth — env-first; set GMAIL_BASIC_AUTH_B64 (see secrets-manager/.env)
AUTH = os.environ['GMAIL_BASIC_AUTH_B64']
def psql_json(sql):
r = subprocess.run(['psql','dw_unified','-At','-q'], input=sql, capture_output=True, text=True, check=True, timeout=10)
return json.loads(r.stdout.strip()) if r.stdout.strip() else []
def img_to_b64_thumb(path, size=420):
img = Image.open(path).convert('RGB')
img.thumbnail((size, size), Image.LANCZOS)
buf = io.BytesIO()
img.save(buf, 'JPEG', quality=82, optimize=True)
return base64.b64encode(buf.getvalue()).decode('ascii')
# Pull the 6 newest SDXL designs (the Corey set we just generated)
rows = psql_json("""SELECT COALESCE(json_agg(t),'[]'::json) FROM (
SELECT id, dominant_hex, prompt, local_path FROM spoon_all_designs
WHERE generator='replicate' ORDER BY id DESC LIMIT 6) t;""")
# Render in chronological order (oldest first → matches the original numbering 1–6)
rows = list(reversed(rows))
print(f'Composing draft with {len(rows)} designs')
short_titles = [
'Pale celadon trellis · watercolor butterflies, soft pinks',
'Charcoal lattice · iridescent fantasy butterflies, jewel tones',
'Cream Chinese fretwork · hand-painted swallowtails, coral',
'Sage diamond lattice · monarch wings, muted gold',
'Ivory architectural trellis · morpho-blue butterflies',
'Blush garden trellis · hummingbird & butterfly motifs',
]
notes = [
'Most delicate; reads quiet from across a room, sings up close.',
'Most dramatic; the lattice grounds the iridescence — works in a powder room or library.',
'Hand-painted feel; the swallowtail silhouettes pop against the cream fretwork.',
'The Loma Linda direction with a muted-gold palette; warm, sun-faded, classic.',
'Most architectural; the blues feel grown-up and gallery-ready.',
'Most playful; could read storybook in a child\'s room or witty in a powder.',
]
cards_html = []
for i, r in enumerate(rows):
b64 = img_to_b64_thumb(r['local_path'])
hex_ = r['dominant_hex'] or '#888'
title = short_titles[i] if i < len(short_titles) else f"Design {i+1}"
note = notes[i] if i < len(notes) else ''
cards_html.append(f"""
<table cellpadding="0" cellspacing="0" border="0" style="margin:0 0 24px;width:100%;max-width:560px">
<tr>
<td style="vertical-align:top;width:200px;padding-right:18px">
<img src="data:image/jpeg;base64,{b64}" width="200" height="200"
style="display:block;border:1px solid #ddd;border-radius:6px" alt="Design {i+1}">
</td>
<td style="vertical-align:top;font:14px/1.5 -apple-system,Helvetica,Arial,sans-serif;color:#1a1a1a">
<div style="font-family:Georgia,serif;font-size:17px;font-weight:400;margin-bottom:4px">{i+1}. {title}</div>
<div style="font-size:11px;color:#888;letter-spacing:.04em;text-transform:uppercase;margin-bottom:8px">
<span style="display:inline-block;width:10px;height:10px;background:{hex_};border-radius:50%;border:1px solid #ddd;vertical-align:middle;margin-right:5px"></span>{hex_}
</div>
<div style="color:#444">{note}</div>
</td>
</tr>
</table>""")
body = f"""<div style="font:14px/1.6 -apple-system,Helvetica,Arial,sans-serif;color:#1a1a1a;max-width:600px">
<p>Hi Corey,</p>
<p>I sketched six directions for the trellis-with-butterflies idea — each one a different mood within the same family. Trellis is the structure; the butterflies are the surprise. Tell me which ones feel right and I'll refine. Any of them can come at 24" wide on paper / vinyl / peel-and-stick / grasscloth, or 54" commercial vinyl for the larger areas.</p>
<hr style="border:0;border-top:1px solid #eee;margin:18px 0">
{''.join(cards_html)}
<hr style="border:0;border-top:1px solid #eee;margin:18px 0">
<p>Next steps — pick your favorites, then I'll:</p>
<ol>
<li>Tighten the palette and butterfly scale</li>
<li>Print a memo sample at scale on your chosen substrate</li>
<li>Render a wall mock-up at the actual ceiling height of the room</li>
</ol>
<p>If none of these are quite right, just tell me the direction (lighter, darker, more graphic, more painterly, larger butterflies, fewer butterflies) and I'll iterate. Each variant takes about a minute on my end.</p>
<p>Steve<br>Designer Wallcoverings — Sherman Oaks<br>
<a href="mailto:steve@designerwallcoverings.com">steve@designerwallcoverings.com</a></p>
</div>"""
# POST to George drafts
payload = {
'subject': 'Six trellis-with-butterfly directions — pick your favorites',
'body': body,
# 'to' left blank — Steve picks Corey in Gmail
}
req = urllib.request.Request(
f'{GEORGE}/api/drafts',
data=json.dumps(payload).encode('utf-8'),
headers={
'Authorization': f'Basic {AUTH}',
'Content-Type': 'application/json',
'X-Account': 'steve-office' # steve@designerwallcoverings.com
}
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
resp = json.loads(r.read())
print(json.dumps(resp, indent=2))
print()
print(f"Draft saved with id: {resp.get('draftId')}")
print(f"Account: {resp.get('account')}")
print(f"Body size: {len(body):,} bytes (HTML, base64 thumbnails inline)")
except Exception as e:
print(f'ERROR: {e}')
if hasattr(e, 'read'):
print(e.read()[:400])