Files
figma-templates-showcase/scripts/match-and-update.py
2026-04-22 16:46:51 +08:00

112 lines
4.5 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Match Figma cloud Drafts files (from figma-files.json) back to W{N} entries in
manifest.json by fuzzy name match, then update web/data.json with figma_key+url.
"""
import json, re, sys, difflib
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
def normalize(s):
s = s.lower()
s = re.sub(r'[^a-z0-9]+', ' ', s).strip()
return s
def best_match(target, candidates):
"""Return (score, candidate) best match from candidates list of dicts with .name"""
nt = normalize(target)
best = (0, None)
for c in candidates:
nc = normalize(c['name'])
# exact / prefix / contains / sequence
if nt == nc: s = 1.0
elif nt in nc or nc in nt: s = 0.85
else: s = difflib.SequenceMatcher(None, nt, nc).ratio()
if s > best[0]:
best = (s, c)
return best
def main():
manifest = json.loads((ROOT/'manifest.json').read_text())
figma_files_path = ROOT/'figma-files.json'
if not figma_files_path.exists():
print(f"missing {figma_files_path}", file=sys.stderr)
sys.exit(1)
figma_files = json.loads(figma_files_path.read_text()) # list of {key, name, ...}
# Match each template source file to ALL matching cloud files (supports multi-variant)
matches = []
used_keys = set()
for t in manifest['templates']:
source_files = t['fig'] if t['fig'] else t['sketch']
if not source_files: continue
kind = 'fig' if t['fig'] else 'sketch'
variants = []
for src in source_files:
stem = Path(src).stem
# Match this specific source stem to best unused cloud file
score, cand = best_match(stem, [f for f in figma_files if f['key'] not in used_keys])
if cand and score >= 0.6:
used_keys.add(cand['key'])
variants.append({
'source_stem': stem,
'matched': cand['name'],
'key': cand['key'],
'score': round(score, 3)
})
matches.append({
'W': t['id'], 'name': t['name'], 'kind': kind,
'source_count': len(source_files),
'variants': variants,
})
# Update web/data.json
data_path = ROOT/'web'/'data.json'
data = json.loads(data_path.read_text())
by_W = {m['W']: m for m in matches}
for t in data['templates']:
m = by_W.get(t['id'])
if m and m['variants']:
first = m['variants'][0]
t['figma_key'] = first['key']
t['figma_url'] = f"https://www.figma.com/file/{first['key']}"
t['figma_variants'] = [
{'name': v['source_stem'], 'matched': v['matched'], 'key': v['key'],
'url': f"https://www.figma.com/file/{v['key']}"}
for v in m['variants']
]
else:
t['figma_key'] = None
t['figma_url'] = None
t['figma_variants'] = []
# update banner with imported count + variant total
imported = sum(1 for t in data['templates'] if t['figma_key'])
total_variants = sum(len(t.get('figma_variants', [])) for t in data['templates'])
fig_cnt = sum(1 for t in manifest['templates'] if t['fig'])
sketch_only_cnt = sum(1 for t in manifest['templates'] if not t['fig'] and t['sketch'])
data['imported_summary'] = (
f"✅ <b>{imported}/56 套</b>(共 {total_variants} 个文件)已云端就位在 "
f"<a href='https://www.figma.com/files/team/1304178887825899477/drafts' target='_blank'>Figma Drafts</a>"
f"{fig_cnt} .fig + {sketch_only_cnt} .sketch 转换)。"
f"点卡片 → modal → 多变体 tab 切换 + iframe 实时投射。"
)
data_path.write_text(json.dumps(data, ensure_ascii=False, indent=2))
# write match report
report = ROOT/'figma-match-report.json'
report.write_text(json.dumps(matches, ensure_ascii=False, indent=2))
total_matchable = sum(1 for t in manifest['templates'] if t['fig'] or t['sketch'])
multi_variant = sum(1 for m in matches if len(m['variants']) > 1)
print(f"Matched {imported}/{total_matchable} templates, {total_variants} total file keys, {multi_variant} with multi variants")
print(f"Report: {report.relative_to(ROOT)}")
unmatched = [m for m in matches if not m['variants']]
if unmatched:
print(f"\n⚠️ {len(unmatched)} unmatched:")
for m in unmatched:
print(f" {m['W']:4s} {m['name'][:50]:50s}")
if __name__ == '__main__':
main()