← back to Dw Unbuyable Recovery Pilot

tk11041-innovations-reconcile/rescrape/extract.py

71 lines

import re,html,json,os,csv
BASE='/private/tmp/claude-501/-Users-macstudio3-Projects-designerwallcoverings/92987ed5-7379-4db6-b4d6-853407d8799b/scratchpad/inn'
MAP={'CSO':('costine','Costine','live'),'ENS':('enchase','Enchase','live'),
     'FLN':('flanders','Flanders','live'),'PRO':('province','Province','live'),
     'SKL':('skylark','Skylark','live'),'TTM':('tatami','Tatami','live'),
     'VCO':('vinyl cork','Vinyl Cork','live'),'WHL':('whistler','Whistler','discontinued')}
FILES={'CSO':'costine','ENS':'enchase','FLN':'flanders','PRO':'province','SKL':'skylark',
       'TTM':'tatami','VCO':'vinyl_cork','WHL':'_wayback_whistler'}
SPEC_KEYS=['Composition','Backing','Total Weight','Width','Repeat','Full Roll','Fire Rating','Seaming','Environmental Impact and Sustainability']

def flat(p):
    h=open(p,encoding='utf-8',errors='replace').read()
    t=re.sub(r'<(script|style)[^>]*>.*?</\1>',' ',h,flags=re.S|re.I)
    return html.unescape(re.sub(r'\s+',' ',re.sub(r'<[^>]+>',' ',t))), h

def specs(t):
    out={}
    i=t.find('Specifications')
    if i<0: return out
    seg=t[i:i+900]
    for k in SPEC_KEYS:
        m=re.search(re.escape(k)+r':\s*(.+?)(?=\s(?:'+'|'.join(re.escape(x) for x in SPEC_KEYS)+r'):|\s*Product Downloads|\s*Seen In|$)',seg)
        if m: out[k]=m.group(1).strip()
    return out

def colorways(t,pfx):
    d={}
    for m in re.finditer(r'\b('+pfx+r'-\d{2,3})\s+([A-Z][A-Za-z ]{1,24}?)(?=\s+'+pfx+r'-\d|\s+Item is|\s+ORDER|\s+Select|\s+Line \d|\s{2,}|$)',t):
        code,name=m.group(1),m.group(2).strip()
        if name.lower() in ('item','order','select','line'): continue
        d.setdefault(code,name.title())
    return d

pat={}
for pfx,fn in FILES.items():
    p=os.path.join(BASE,'pages',fn+'.html')
    t,h=flat(p)
    slug,disp,state=MAP[pfx]
    pat[pfx]={'slug':slug,'pattern':disp,'site_state':state,'specs':specs(t),
              'colorways':colorways(t,pfx),
              'desc':(re.search(r'"description":"(.*?)"',h).group(1)[:400] if re.search(r'"description":"(.*?)"',h) else ''),
              'product_url':('https://www.innovationsusa.com/item/'+slug.replace(' ','%20')) if state=='live' else 'DISCONTINUED (archived: web.archive.org/web/20230127073736/https://www.innovationsusa.com/item/whistler)'}

rows=[]
for line in open('/tmp/cohort51.tsv',encoding='utf-8'):
    line=line.rstrip('\n')
    if not line: continue
    mfr,title=line.split('\t',1)
    pfx=mfr.split('-')[0]
    P=pat.get(pfx,{})
    cw=P.get('colorways',{}).get(mfr,'')
    dwcw=re.search(r'\b([A-Z][A-Z \-]{2,})\s+Memo Sample',title)
    rows.append({'mfr_sku':mfr,'dw_title':title,'innovations_pattern':P.get('pattern',''),
        'innovations_colorway':cw,'dw_colorway':(dwcw.group(1).strip().title() if dwcw else ''),
        'colorway_match':('YES' if cw and dwcw and cw.lower()==dwcw.group(1).strip().lower() else ('n/a' if not cw else 'CHECK')),
        'site_state':P.get('site_state',''),'product_url':P.get('product_url',''),
        'image_url':f'https://www.innovationsusa.com/storage/sku/350x350/{mfr}.jpg' if P.get('site_state')=='live' else '',
        'width':P.get('specs',{}).get('Width',''),'full_roll':P.get('specs',{}).get('Full Roll',''),
        'composition':P.get('specs',{}).get('Composition',''),'repeat':P.get('specs',{}).get('Repeat',''),
        'fire_rating':P.get('specs',{}).get('Fire Rating','')})

os.makedirs(BASE+'/out',exist_ok=True)
json.dump({'patterns':pat,'items':rows},open(BASE+'/out/innovations-identity-map.json','w'),indent=2)
with open(BASE+'/out/innovations-identity-map.csv','w',newline='') as f:
    w=csv.DictWriter(f,fieldnames=list(rows[0].keys())); w.writeheader(); w.writerows(rows)
res=sum(1 for r in rows if r['innovations_pattern'])
cw=sum(1 for r in rows if r['colorway_match']=='YES')
print(f'items={len(rows)} identity_resolved={res} colorway_confirmed={cw}')
for p,v in pat.items():
    print(f"  {p} -> {v['pattern']:<12} {v['site_state']:<13} colorways={len(v['colorways'])} specs={len(v['specs'])}")