[object Object]

← back to Slack To Steve

mica catalog grid PNG generator (contact sheet from micawallpaper products + dw_unified)

8679504d70dadcc68945ab09811f5f53ce2407b6 · 2026-09-24 14:46:56 -0700 · Steve Abrams

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files touched

Diff

commit 8679504d70dadcc68945ab09811f5f53ce2407b6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 24 14:46:56 2026 -0700

    mica catalog grid PNG generator (contact sheet from micawallpaper products + dw_unified)
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---
 exports/mica/build.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/exports/mica/build.py b/exports/mica/build.py
new file mode 100644
index 0000000..45bd8f8
--- /dev/null
+++ b/exports/mica/build.py
@@ -0,0 +1,52 @@
+import json, subprocess, io, os, re, urllib.request, concurrent.futures as cf
+from PIL import Image, ImageDraw, ImageFont
+P=json.load(open(os.path.expanduser('~/Projects/micawallpaper/data/products.json')))
+P=P if isinstance(P,list) else P.get('products')
+handles=[p['handle'] for p in P]
+q="select handle, coalesce(dw_sku,''), coalesce(mfr_sku,''), coalesce(min_variant_price::text,''), coalesce(retail_price::text,''), status, coalesce(image_url,'') from shopify_products where handle = any(string_to_array('%s',','))" % ",".join(h.replace("'","''") for h in handles)
+out=subprocess.run(['psql','-h','/tmp','dw_unified','-At','-F','\t','-c',q],capture_output=True,text=True).stdout
+db={}
+for l in out.splitlines():
+    f=l.split('\t')
+    if len(f)>=7: db[f[0]]=f
+P=[p for p in P if p['handle'] in db and db[p['handle']][5]!='ARCHIVED']
+print('live+draft',len(P))
+def get(u):
+    u+=('&' if '?' in u else '?')+'width=360'
+    try:
+        r=urllib.request.urlopen(urllib.request.Request(u,headers={'User-Agent':'Mozilla/5.0'}),timeout=25).read()
+        return Image.open(io.BytesIO(r)).convert('RGB')
+    except Exception: return None
+def fetch(p):
+    im=get(p['image_url'])
+    if im is None and p['handle'] in db and db[p['handle']][6]: im=get(db[p['handle']][6])
+    return im
+with cf.ThreadPoolExecutor(16) as ex: imgs=list(ex.map(fetch,P))
+print('images ok',sum(i is not None for i in imgs),'/',len(P),'db rows',len(db))
+F=lambda s,b=False: ImageFont.truetype('/System/Library/Fonts/Helvetica.ttc',s,index=1 if b else 0)
+COLS,ROWS=8,10; W,H,T=240,340,240; PAD=14; HEAD=90
+per=COLS*ROWS; pages=(len(P)+per-1)//per
+def trunc(d,t,f,w):
+    while d.textlength(t,font=f)>w and len(t)>3: t=t[:-2]
+    return t if d.textlength(t,font=f)<=w else t
+for pg in range(pages):
+    items=list(range(pg*per,min(len(P),(pg+1)*per)))
+    rows=(len(items)+COLS-1)//COLS
+    im=Image.new('RGB',(COLS*W+PAD,HEAD+rows*H+PAD),'#f6f3ec'); d=ImageDraw.Draw(im)
+    d.text((PAD,18),'MICA WALLPAPER — active catalog',font=F(30,True),fill='#2a2a2a')
+    d.text((PAD,58),f'Page {pg+1}/{pages} · items {items[0]+1}–{items[-1]+1} of {len(P)} · micawallpaper.com · {__import__("datetime").date.today()}',font=F(15),fill='#777')
+    for n,i in enumerate(items):
+        p=P[i]; x=PAD+(n%COLS)*W; y=HEAD+(n//COLS)*H
+        d.rectangle([x,y,x+W-PAD,y+H-PAD],fill='white',outline='#ddd6c4')
+        im0=imgs[i]
+        if im0:
+            s=max(T/im0.width,T/im0.height); im0=im0.resize((int(im0.width*s)+1,int(im0.height*s)+1))
+            l=(im0.width-(W-PAD-2))//2; t=(im0.height-(T-2))//2
+            im.paste(im0.crop((max(l,0),max(t,0),max(l,0)+W-PAD-2,max(t,0)+T-2)).resize((W-PAD-2,T-2)),(x+1,y+1))
+        r=db.get(p['handle']); dwsku=(r[1] if r and r[1] else p['sku']); price=(r[3] or r[4]) if r else ''
+        v=p['vendor']; 
+        d.text((x+6,y+T+4),trunc(d,p['title'],F(13,True),W-PAD-12),font=F(13,True),fill='#222')
+        d.text((x+6,y+T+23),trunc(d,dwsku+('  [DRAFT]' if r and r[5]=='DRAFT' else ''),F(12),W-PAD-12),font=F(12),fill='#c0392b' if r and r[5]=='DRAFT' else '#555')
+        d.text((x+6,y+T+40),trunc(d,v+(f' · ${price}' if price and price!='4.25' else ''),F(12),W-PAD-12),font=F(12),fill='#8a7a45')
+        d.text((x+6,y+T+57),trunc(d,', '.join(t for t in p['tags'] if t.lower() not in('mica','wallcovering'))[:60],F(11),W-PAD-12),font=F(11),fill='#999')
+    im.save(f'mica-grid-{pg+1}.png'); print('saved',pg+1,im.size)

← 2d31494 slack bridge: #general call mode (@mention/'claude'), staff  ·  back to Slack To Steve  ·  MIC- SKU grid PNG generator (active MIC-prefix products from d48a5ac →