← 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&quot; wide on paper / vinyl / peel-and-stick / grasscloth, or 54&quot; 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 &mdash; 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])