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>
57 lines
3.7 KiB
Python
57 lines
3.7 KiB
Python
"""Commercial-delivery readiness report, separate from technical/SEO pass.
|
|
|
|
It does NOT certify a site's business claims, copyright clearance, legal compliance,
|
|
WCAG conformance or any real-world server/SEO outcome. It reports missing items.
|
|
"""
|
|
from __future__ import annotations
|
|
from pathlib import Path
|
|
from bs4 import BeautifulSoup
|
|
import json
|
|
|
|
REQUIRED=('index.html','about/index.html','contact/index.html','products/index.html','brands/index.html','news/index.html','cases/index.html','404.html')
|
|
DEMO_MARKERS=('example.com','example.test','这是可替换的','虚构示例','演示内容','演示产品','此为 SiteForge','请在后台配置')
|
|
|
|
def inspect(dist:Path)->dict:
|
|
dist=Path(dist)
|
|
blockers=[];warnings=[];metrics={}
|
|
for f in REQUIRED:
|
|
if not (dist/f).is_file():blockers.append({'code':'page_missing','path':f,'message':'缺少标准栏目页面'})
|
|
homepage=dist/'index.html'
|
|
if homepage.is_file():
|
|
soup=BeautifulSoup(homepage.read_text(encoding='utf-8'),'html.parser')
|
|
logo=soup.select_one('header img.pro-brand-logo')
|
|
if not logo:warnings.append({'code':'brand_logo','message':'尚未替换成企业正式 Logo'})
|
|
if not soup.find('meta',attrs={'name':'description'}):blockers.append({'code':'seo_description','message':'首页缺少 SEO 描述标签'})
|
|
if len(soup.select('h1'))!=1:blockers.append({'code':'home_h1','message':'首页 H1 数量不符合预期'})
|
|
else:soup=None
|
|
# Do not build a 5,000-page HTML string and parse every detail document with
|
|
# BeautifulSoup just to count two static CSS classes. Streaming makes the
|
|
# commercial audit linear in file size, rather than consuming excessive CPU.
|
|
metrics['html_pages']=0
|
|
metrics['artwork_fallbacks']=0
|
|
metrics['pages_with_body']=0
|
|
found_demo=False
|
|
for path in dist.rglob('*.html'):
|
|
html=path.read_text(encoding='utf-8',errors='replace')
|
|
metrics['html_pages']+=1
|
|
metrics['artwork_fallbacks']+=html.count('class="pro-art ')
|
|
metrics['pages_with_body']+=int('pro-rich' in html)
|
|
if not found_demo and any(marker in html for marker in DEMO_MARKERS):found_demo=True
|
|
if metrics['artwork_fallbacks']:
|
|
warnings.append({'code':'placeholder_images','count':metrics['artwork_fallbacks'],'message':'部分页面仍使用示意图,正式发布前请替换为真实且有使用权的图片'})
|
|
if found_demo:
|
|
warnings.append({'code':'demo_content','message':'检测到演示或默认文案,正式发布前请逐页核对'})
|
|
if not (dist/'assets'/'commercial.css').is_file():
|
|
warnings.append({'code':'commercial_style','message':'当前构建未包含商业视觉样式(可能是历史主题版本)'})
|
|
contact=dist/'contact/index.html'
|
|
if contact.is_file():
|
|
con=BeautifulSoup(contact.read_text(encoding='utf-8'),'html.parser')
|
|
if not con.select_one('form.pro-inquiry') and not con.select_one('a[href^="mailto:"]'):
|
|
blockers.append({'code':'contact_method','message':'联系页既无已配置表单,也无有效电子邮箱入口'})
|
|
elif not con.select_one('form.pro-inquiry'):
|
|
warnings.append({'code':'form_not_configured','message':'尚未配置 HTTPS 询盘接收服务;目前仅提供邮件联系'})
|
|
if not any((dist/p).is_file() for p in ('privacy/index.html','privacy-policy/index.html')):
|
|
warnings.append({'code':'legal_pages','message':'请根据目标国家/地区法律审查是否需要隐私政策与 Cookie 声明'})
|
|
return {'ready_for_review':not blockers,'commercially_certified':False,'blockers':blockers,'warnings':warnings,
|
|
'metrics':metrics,'note':'仅为本地结构化交付清单,不等于合法合规、内容准确、无障碍、性能或生产上线认证'}
|