← back to Dw Vendor Microsites
seed-manifest.sh
142 lines
#!/usr/bin/env bash
# seed-manifest.sh
#
# Enumerates all *_catalog tables in dw_unified that have rows (READ-ONLY), runs each
# through the private-label/exclusion gate, and seeds manifest.json with one PENDING
# entry per vendor + wave assignment. Pilot-6 = wave 1; the rest chunked into waves of 6.
# Nicolette Mayer = EXCLUDED. Idempotent: re-running refreshes gate fields but preserves
# any existing build_status/port for already-built vendors.
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MANIFEST="$REPO/manifest.json"
# helper: run a single-line SQL over ssh, tuples-only/unaligned
psql_run() { ssh my-server "sudo -u postgres psql -d dw_unified -tAc \"$1\""; }
# pilot-6 roster (DTD-locked)
PILOT=(cowtan_tout armani_casa brewster_york phillip_jeffries kravet relativity_textiles)
echo "== enumerating *_catalog tables with rows =="
# table list, excluding non-vendor utility tables
TABLES_JSON="$(psql_run "select coalesce(json_agg(json_build_object('t', table_name)),'[]') from information_schema.tables where table_schema='public' and table_name like '%_catalog' and table_name not in ('vendor_catalog','connie_competitor_catalog','connie_our_catalog');")"
# resolve registry rows once: catalog_table -> vendor_code mapping
REG_JSON="$(psql_run "select coalesce(json_agg(row_to_json(t)),'[]') from (select vendor_code, vendor_name, catalog_table, is_private_label, private_label_name, parent_brand, display_prices, do_not_price from vendor_registry where catalog_table is not null and catalog_table <> '') t;")"
python3 - "$MANIFEST" "$REPO" "$TABLES_JSON" "$REG_JSON" "${PILOT[@]}" <<'PY'
import sys, json, os, re, subprocess, datetime
manifest_path, repo, tables_json, reg_json = sys.argv[1:5]
pilot = sys.argv[5:]
tables = [x['t'] for x in (json.loads(tables_json) or [])]
reg = json.loads(reg_json) or []
# index registry by catalog_table
by_table = {}
for r in reg:
ct = (r.get('catalog_table') or '').strip()
if ct: by_table[ct] = r
def count(table):
try:
out = subprocess.run(['ssh','my-server',f"sudo -u postgres psql -d dw_unified -tAc \"select count(*) from {table};\""],
capture_output=True, text=True, timeout=40)
return int(out.stdout.strip() or 0)
except Exception:
return 0
def slug(s):
return re.sub(r'[^a-z0-9]+','-', (s or '').lower()).strip('-')
# load existing manifest to preserve build state
try:
m = json.load(open(manifest_path))
except Exception:
m = {}
m.setdefault('vendors', {})
# build entries
candidates = [] # (vendor_code, entry)
excluded = 0
for table in tables:
r = by_table.get(table)
if not r:
# table exists but no registry row -> derive a code from the table name
vcode = table[:-len('_catalog')] if table.endswith('_catalog') else table
vname = vcode.replace('_',' ').title()
is_pl=False; pl_name=''; parent=''; do_not_p=False
else:
vcode = r['vendor_code']; vname = r.get('vendor_name') or vcode
is_pl = bool(r.get('is_private_label')); pl_name=(r.get('private_label_name') or '').strip()
parent=(r.get('parent_brand') or '').strip(); do_not_p=bool(r.get('do_not_price'))
# EXCLUSION
if re.sub(r'[^a-z]','', vcode.lower())=='nicolettemayer' or 'nicolette mayer' in (vname or '').lower():
m['vendors'][vcode] = {**m['vendors'].get(vcode,{}), 'vendor_code':vcode,'vendor_name':vname,
'catalog_table':table,'status':'EXCLUDED','build_status':'EXCLUDED',
'reason':'Nicolette Mayer — archived, must stay off','wave':0}
excluded += 1
continue
rc = count(table)
if rc <= 0:
continue # skip empty tables
house = pl_name if (is_pl and pl_name) else vname
banned = []
if is_pl:
for nm in [vname, parent]:
nm=(nm or '').strip()
if nm and nm.lower()!=house.lower(): banned.append(nm)
EXTRA={'malibu wallpaper':['Brewster','York','Chesapeake','Seabrook','NextWall','WallQuest'],
'phillipe romano':['Relativity Textiles','Justin David','TWIL','Command54','York','Brewster'],
'hollywood':['Command54']}
for k,v in EXTRA.items():
if house.lower()==k: banned += v
seen=set(); out=[]
for b in banned:
bl=b.strip()
if bl and bl.lower()!=house.lower() and bl.lower() not in seen:
seen.add(bl.lower()); out.append(bl)
banned=out
entry = m['vendors'].get(vcode, {})
entry.update({
'vendor_code':vcode,'vendor_name':vname,'catalog_table':table,
'is_private_label':is_pl,'house_name':house,'subdomain':slug(house),
'banned_names':banned,'table_count':rc,
'samples_only':True,
'status':'OK',
})
entry.setdefault('build_status','PENDING')
entry.setdefault('deploy_status','PENDING')
candidates.append((vcode, entry, rc))
# wave assignment: pilot-6 = wave 1; rest sorted by image-rich-likely (row count desc as proxy), chunked by 6
pilot_set = set(pilot)
for vcode, entry, rc in candidates:
if vcode in pilot_set:
entry['wave']=1
rest = [(c,e,rc) for (c,e,rc) in candidates if c not in pilot_set]
rest.sort(key=lambda x:-x[2]) # bigger catalogs first
wave=2; i=0
for (c,e,rc) in rest:
e['wave']=wave
i+=1
if i%6==0: wave+=1
for vcode, entry, rc in candidates:
m['vendors'][vcode]=entry
m['updated_at']=datetime.datetime.now().isoformat()
total = len([v for v in m['vendors'].values() if v.get('status')!='EXCLUDED'])
waves = sorted(set(v.get('wave') for v in m['vendors'].values() if v.get('status')!='EXCLUDED' and v.get('wave')))
m['stats']={'total_vendors':len(m['vendors']),'buildable':total,'excluded':excluded,'waves':len(waves)}
json.dump(m, open(manifest_path,'w'), indent=2)
print(f"seeded: {len(m['vendors'])} vendors total, {total} buildable, {excluded} excluded, {len(waves)} waves")
print("pilot-6 (wave 1):", ', '.join(c for c,e,_ in candidates if c in pilot_set))
PY