3c57c167f2
导入 SiteForge Factory V5 基线源码: - FastAPI + SQLite + Docker Compose(Web + Worker)架构 - 四套商业主题 × 14 页面模板(1.1.0,含历史版本快照) - 四种建站模式骨架 + 迁移审计 + 发布门禁 - 52 项 pytest 测试 - 补全计划主文档 docs/superpowers/plans/ Co-Authored-By: Claude Code <noreply@anthropic.com>
39 lines
2.0 KiB
Python
39 lines
2.0 KiB
Python
"""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}
|