8cea7dee20
- SiteForge Enterprise V8.10.0-dev.1 upstream source (210 tests) - Fix: v8_workflows._mapped_input now reads both 类型/分类 columns so WXR imports keep post/product types instead of collapsing to page - Docs: trade-pro theme port spec (R3) and implementation plan Co-Authored-By: Claude Code <noreply@anthropic.com>
51 lines
2.7 KiB
Python
51 lines
2.7 KiB
Python
"""Content assembly: stable, site-scoped brand/category relationships.
|
|
|
|
Only materializes associations supported by source data. Generated brand shells
|
|
are clearly marked and contain no fabricated copy.
|
|
"""
|
|
from collections import defaultdict
|
|
from . import db
|
|
from .core import save_entry
|
|
from .theme_pro import fields,slug
|
|
|
|
KINDS=('product','vehicle')
|
|
|
|
def preview(site_id):
|
|
records=db.rows("SELECT * FROM entries WHERE site_id=? AND type IN ('product','vehicle','brand')",(site_id,))
|
|
brand_pages={r['title'].casefold():r for r in records if r['type']=='brand'}
|
|
routes={r['route'] for r in db.rows('SELECT route FROM entries WHERE site_id=?',(site_id,))}
|
|
brands=defaultdict(list);cats=defaultdict(list);issues=[]
|
|
for row in records:
|
|
if row['type'] not in KINDS:continue
|
|
attrs=fields(row)
|
|
brand=str(attrs.get('brand') or '').strip();category=str(attrs.get('category') or '').strip()
|
|
if brand: brands[brand].append(row['route'])
|
|
else:issues.append({'route':row['route'],'severity':'review','message':'未填写品牌'})
|
|
if category:cats[category].append(row['route'])
|
|
else:issues.append({'route':row['route'],'severity':'review','message':'未填写分类'})
|
|
if not row['cover']:issues.append({'route':row['route'],'severity':'review','message':'缺少产品封面图'})
|
|
candidates=[]
|
|
for name,products in brands.items():
|
|
if name.casefold() in brand_pages:continue
|
|
url=f'/brands/{slug(name)}/'
|
|
if url in routes:
|
|
issues.append({'route':url,'severity':'block','message':'品牌自动生成路径与已有页面冲突'});continue
|
|
candidates.append({'brand':name,'route':url,'products':len(products)})
|
|
return {'brands':[{ 'name':k,'items':len(v)} for k,v in brands.items()],
|
|
'categories':[{'name':k,'items':len(v)} for k,v in cats.items()],
|
|
'brand_pages_missing':candidates,'issues':issues,
|
|
'products':sum(len(x) for x in brands.values()),
|
|
'note':'仅根据已有产品字段建立关系;缺少真实品牌介绍/图片时不会自动编造。'}
|
|
|
|
def commit(site_id):
|
|
result=preview(site_id)
|
|
if any(issue['severity']=='block' for issue in result['issues']):
|
|
raise ValueError('存在路径冲突,请先处理后再装配')
|
|
created=[]
|
|
for brand in result['brand_pages_missing']:
|
|
save_entry(site_id,{'type':'brand','title':brand['brand'],'route':brand['route'],
|
|
'summary':'','body':'','status':'published','meta':{'fields':{'source':'auto-assembled'},'source_auto_assembled':True}},
|
|
source='relations-assembly')
|
|
created.append(brand['route'])
|
|
return {'created_brand_pages':created,'count':len(created),'preview':result}
|