#!/usr/bin/env python3 """Fill the master multi-type template (assets/template/template.xlsx) from parts.json. Per component type present, writes one .xlsx containing the filled type sheet plus a Meta sheet (template version, date). Column A ("MPN_make_type") is set to __, where make = first word of the manufacturer and typeid comes from the taxonomy (Type_ID.xlsx), supplied per part as "typeid". parts.json: { "parts": [ {"type": "Diode", "mpn": "BAT46WJ", "manufacturer": "Nexperia", "typeid": "SCH", "subclass": "Schottky", "values": {"Description": "...", "Forward Voltage(V)": "0.71", ...}} ] } Usage: python fill_templates.py parts.json --template --dest [--version v1] """ import argparse, json, os, re, datetime import openpyxl from openpyxl.styles import Font, PatternFill, Alignment, Border, Side GREEN="B6D7A8"; GRAY="BFBFBF" def norm(s): return re.sub(r'[^a-z0-9]', '', str(s).lower()) ALIAS={"induactanceuh":"inductanceuh"} # template header typo -> value key def make_tag(manufacturer): w = re.split(r'\s+', str(manufacturer).strip()) first = w[0] if w and w[0] else "NA" return re.sub(r'[^A-Za-z0-9]', '', first) or "NA" def style_header(cell): cell.font=Font(name="Calibri", bold=True); cell.fill=PatternFill("solid", fgColor=GREEN) s=Side(style="thin", color=GRAY); cell.border=Border(left=s,right=s,top=s,bottom=s) cell.alignment=Alignment(horizontal="center", vertical="center", wrap_text=True) def main(): ap=argparse.ArgumentParser() ap.add_argument("parts_json"); ap.add_argument("--template", required=True) ap.add_argument("--dest", required=True); ap.add_argument("--version", default=None) a=ap.parse_args() data=json.load(open(a.parts_json, encoding="utf-8")) version=a.version if not version: vf=os.path.join(os.path.dirname(a.template), "VERSION") version="v"+open(vf).read().strip() if os.path.exists(vf) else "v1" os.makedirs(a.dest, exist_ok=True) master=openpyxl.load_workbook(a.template) by={} for p in data.get("parts", []): by.setdefault(p["type"], []).append(p) for ctype, parts in by.items(): if ctype not in master.sheetnames: print(f"! no template sheet for type '{ctype}' - skipping"); continue msheet=master[ctype] headers=[msheet.cell(1,c).value for c in range(1, msheet.max_column+1)] out=openpyxl.Workbook(); ws=out.active; ws.title=ctype[:31] for c,h in enumerate(headers, start=1): ws.cell(1,c,h); style_header(ws.cell(1,c)) ws.row_dimensions[1].height=30 r=2 for p in parts: vals={norm(k):v for k,v in p.get("values", {}).items()} make=p.get("make") or make_tag(p.get("manufacturer","")) tid=p.get("typeid","NA") colA=f'{p.get("mpn","")}_{make}_{tid}' for c,h in enumerate(headers, start=1): key=norm(h); key=ALIAS.get(key,key) if c==1: ws.cell(r,c,colA) elif key=="class": ws.cell(r,c,p.get("subclass","")) elif key=="manufacturer": ws.cell(r,c,p.get("manufacturer", vals.get("manufacturer",""))) else: ws.cell(r,c,vals.get(key,"")) r+=1 # column widths from openpyxl.utils import get_column_letter for c,h in enumerate(headers, start=1): w=32 if norm(h)=="description" else (24 if c==1 else max(11,min(18,len(str(h))+2))) ws.column_dimensions[get_column_letter(c)].width=w ws.freeze_panes="A2" # Meta sheet meta=out.create_sheet("Meta") rows=[("Template Version", version), ("Generated", datetime.date.today().isoformat()), ("Component Type", ctype), ("Parts in file", len(parts)), ("Source", "datasheet-extractor skill")] for i,(k,v) in enumerate(rows, start=1): meta.cell(i,1,k).font=Font(bold=True); meta.cell(i,2,v) meta.column_dimensions["A"].width=20; meta.column_dimensions["B"].width=40 fn=re.sub(r'\s+','_',ctype)+".xlsx" out.save(os.path.join(a.dest, fn)) print(f"{ctype}: {len(parts)} row(s) -> {fn} (template {version})") if __name__=="__main__": main()