| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- #!/usr/bin/env python3
- """生成 AI 数据清洗 E2E 验收用的 xlsx/csv 样本。
- 依赖:openpyxl
- 用法:python generate_fixtures.py # 输出到同目录 fixtures/
- """
- from __future__ import annotations
- import csv
- import os
- from openpyxl import Workbook
- from openpyxl.styles import Font
- OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures")
- os.makedirs(OUT, exist_ok=True)
- HEADERS = ["姓名", "身份证号", "手机号", "银行卡号", "交易日期", "交易时间", "金额", "数量", "备注"]
- ROWS = [
- ["张三", "11010519491231002X", "13800000000", "6222020200001234",
- "2026-01-05", "2026-01-05 10:30:00", "1,234.50", "3", "正常"],
- ["李四", "110105491231002", "+86 139-0000-1111", "6222 0202 0000 1235",
- "2026/1/6", "2026-01-06T11:00:00", "-100.00", "2", "负数金额"],
- ["王五", "12345", "123", "6.22202E+15",
- "2026年1月7日", "10:30", "(200.50)", "1.5", "坏证件/电话/卡/纯时间"],
- ["赵六", "110105194912310021", "13800002222", "6222020200001236",
- "2026-02-30", "25:00", "约100元", "abc", "坏日期/时间/金额/数量"],
- ["钱七", "11010519491231002X", "13800003333", "6222020200001237",
- "45000", "1704067200", "¥2,300.5", "10", "序列号/epoch"],
- ["孙八", "11010519491231002X", "13800004444", "6222020200001238",
- "20260108", "2026-01-08 09:00:00", "100元", "8", "紧凑日期/带元"],
- [],
- ["周九", "11010519491231002X", "13800005555", "6222020200001239",
- "2026-01-09", "2026-01-09 12:00:00", "300.00", "5", "空行后的行"],
- ]
- def save_xlsx(name: str, sheet_name: str, rows, headers=None, title=None, start_row=1,
- positions=None):
- wb = Workbook()
- ws = wb.active
- ws.title = sheet_name
- row = start_row
- if title:
- ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(headers or HEADERS))
- ws.cell(row=1, column=1, value=title).font = Font(bold=True)
- row = 3
- for c, h in enumerate(headers or HEADERS, 1):
- ws.cell(row=row, column=c, value=h).font = Font(bold=True)
- for i, values in enumerate(rows):
- target = positions[i] if positions else row + 1 + i
- for c, v in enumerate(values, 1):
- ws.cell(row=target, column=c, value=v)
- wb.save(os.path.join(OUT, name))
- def save_csv(name: str, rows):
- with open(os.path.join(OUT, name), "w", newline="", encoding="utf-8") as f:
- w = csv.writer(f)
- w.writerow(HEADERS)
- for values in rows:
- w.writerow(values if values else [""] * len(HEADERS))
- def main():
- # 01:自建表类型推断(标题行 + 空行 + 表头 + 空行隔断)
- save_xlsx("01_self_built_types.xlsx", "经营者信息", ROWS, title="经营者信息表")
- # 02:与 01 同表头但倒序(验证复用必须同序)
- save_xlsx("02_reordered_headers.xlsx", "乱序经营者",
- [list(reversed(r)) for r in ROWS[:3]], headers=list(reversed(HEADERS)))
- # 03:两列标题 + 空行 + 表头 + 空行隔断,验证表头定位与 row_no 缺口
- save_xlsx("03_messy_title_blank.xlsx", "人员交易", [
- ["张三", "1,000.00", "2026-01-01"],
- ["李四", "2,000.00", "2026-01-02"],
- ["王五", "3,000.00", "2026-01-03"],
- ["赵六", "坏金额", "坏日期"],
- ], headers=["姓名", "金额", "日期"], title="2026年人员交易明细",
- positions=[4, 5, 7, 8])
- # 04:CSV 版脏数据(验证 CSV 不再 Skip;可选列脏值只置空)
- save_csv("04_csv_types.csv", ROWS)
- # 05:同表头 20 行,1 行身份证非法(配合把 ai_clean_field.required 临时置 1,
- # 验证必填列坏值整行拒绝 + 失败明细)
- rows05 = []
- for i in range(1, 21):
- bad = i == 1
- rows05.append([
- "坏证件行" if bad else f"正常{i}",
- "12345" if bad else "11010519491231002X",
- "13800000000", "6222020200001234", "2026-01-05",
- "2026-01-05 10:30:00", "100.00", "1", "ok",
- ])
- save_xlsx("05_required_bad.xlsx", "经营者信息", rows05)
- # 07:同 05,但坏证件是完整 18 位(校验位错),验证失败明细不脱敏、原值完整展示
- rows07 = []
- for i in range(1, 21):
- bad = i == 1
- rows07.append([
- "坏18位证件行" if bad else f"正常{i}",
- "110105194912310021" if bad else "11010519491231002X",
- "13800000000", "6222020200001234", "2026-01-05",
- "2026-01-05 10:30:00", "100.00", "1", "ok",
- ])
- save_xlsx("07_required_bad_id18.xlsx", "经营者信息", rows07)
- # 06:干净 CSV(验证 CSV 正常入库)
- save_csv("06_csv_clean.csv", [
- [f"CSV正常{i}", "11010519491231002X", "13800000000", "6222020200001234",
- "2026-01-05", "2026-01-05 10:30:00", "100.00", "1", "ok"]
- for i in range(1, 8)
- ])
- print("fixtures written to", OUT)
- if __name__ == "__main__":
- main()
|