Files
cms-SiteForge/app/table_loader.py
T

39 lines
2.0 KiB
Python
Raw Normal View History

"""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}