chore: baseline V8.10.0-dev.1 with v8_workflows type-mapping fix and trade-pro spec
- 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>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
"""Decode Excel/CSV/JSON source files for a no-side-effect mapping preview."""
|
||||
import csv,io,json
|
||||
from openpyxl import load_workbook
|
||||
|
||||
MAX_ROWS=1000
|
||||
|
||||
def parse_table(name,data):
|
||||
if len(data)>12_000_000:raise ValueError('文件超过12 MB')
|
||||
lower=name.lower()
|
||||
if lower.endswith('.csv'):
|
||||
rows=list(csv.DictReader(io.StringIO(data.decode('utf-8-sig'))))
|
||||
elif lower.endswith('.json'):
|
||||
rows=json.loads(data.decode('utf-8-sig'))
|
||||
if isinstance(rows,dict):rows=rows.get('rows',rows.get('entries',rows.get('items',[])))
|
||||
elif lower.endswith('.xlsx'):
|
||||
wb=load_workbook(io.BytesIO(data),read_only=True,data_only=True)
|
||||
try:
|
||||
ws=wb.active;iterator=ws.iter_rows(values_only=True)
|
||||
header=next(iterator,None)
|
||||
if header is None:raise ValueError('Excel 工作表为空')
|
||||
titles=[str(x or '').strip() for x in header]
|
||||
if not all(titles) or len(set(titles))!=len(titles):raise ValueError('Excel 列标题不能为空或重复')
|
||||
rows=[]
|
||||
for i,values in enumerate(iterator):
|
||||
if i>=MAX_ROWS:raise ValueError('一次最多导入1000条记录')
|
||||
if any(x is not None and str(x).strip() for x in values):
|
||||
rows.append(dict(zip(titles,values)))
|
||||
finally:wb.close()
|
||||
else:raise ValueError('支持 CSV、JSON 或 XLSX 文件')
|
||||
if not isinstance(rows,list) or not 1<=len(rows)<=MAX_ROWS or any(not isinstance(r,dict) for r in rows):
|
||||
raise ValueError('内容必须包含1-1000条对象记录')
|
||||
keys=list(dict.fromkeys(str(k) for r in rows for k in r))
|
||||
if not keys or len(keys)>150:raise ValueError('字段数量不合法')
|
||||
# Date/datetime -> string to avoid JSON encoder serialization exceptions.
|
||||
normalized=[]
|
||||
for row in rows:
|
||||
normalized.append({str(k):v.isoformat() if hasattr(v,'isoformat') else v for k,v in row.items()})
|
||||
return {'filename':name,'columns':keys,'count':len(rows),'rows':normalized}
|
||||
Reference in New Issue
Block a user