← back to Slack To Steve

exports/mica/build.py

53 lines

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)