chore: prepare docfill project for gitea
This commit is contained in:
473
scripts/make-manual-test-materials.py
Normal file
473
scripts/make-manual-test-materials.py
Normal file
@@ -0,0 +1,473 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
from docx import Document
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
|
||||
from docx.shared import Inches, Pt
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, Font, PatternFill, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
OUT = ROOT / "manual_tests"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if OUT.exists():
|
||||
shutil.rmtree(OUT)
|
||||
OUT.mkdir(parents=True)
|
||||
|
||||
build_excel_to_word(OUT / "01_excel_to_word")
|
||||
build_sqlite_to_excel(OUT / "02_sqlite_to_excel")
|
||||
build_excel_to_excel(OUT / "03_excel_to_excel")
|
||||
build_filter_work_order_to_word(OUT / "04_filter_work_order_to_word")
|
||||
write_readme()
|
||||
|
||||
print(OUT)
|
||||
|
||||
|
||||
def build_excel_to_word(path: Path) -> None:
|
||||
path.mkdir(parents=True)
|
||||
write_daily_source_xlsx(path / "source_daily_data.xlsx")
|
||||
write_daily_word_template(path / "daily_report_template.docx")
|
||||
write_json(
|
||||
path / "task.json",
|
||||
{
|
||||
"vars": {"date": "2026-06-18", "batch": "默认班次"},
|
||||
"data": {
|
||||
"type": "excel",
|
||||
"path": "./source_daily_data.xlsx",
|
||||
"sheet": "Daily",
|
||||
"header_row": 1,
|
||||
},
|
||||
"template": {"path": "./daily_report_template.docx"},
|
||||
"output": {
|
||||
"path": "./out/daily_{{date}}_{{name}}_{{_row}}.docx",
|
||||
"overwrite": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def build_sqlite_to_excel(path: Path) -> None:
|
||||
path.mkdir(parents=True)
|
||||
write_summary_sqlite(path / "source_summary.db")
|
||||
write_summary_excel_template(path / "department_summary_template.xlsx")
|
||||
write_json(
|
||||
path / "task.json",
|
||||
{
|
||||
"vars": {"date": "2026-06-18"},
|
||||
"data": {
|
||||
"type": "sqlite",
|
||||
"path": "./source_summary.db",
|
||||
"query": "select department, report_date, total_count, abnormal_count, owner from department_summary where report_date = ? order by department",
|
||||
"params": ["{{date}}"],
|
||||
},
|
||||
"template": {"path": "./department_summary_template.xlsx"},
|
||||
"output": {
|
||||
"path": "./out/summary_{{department}}_{{_row}}.xlsx",
|
||||
"overwrite": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def build_excel_to_excel(path: Path) -> None:
|
||||
path.mkdir(parents=True)
|
||||
write_audit_source_xlsx(path / "source_audit_data.xlsx")
|
||||
write_audit_excel_template(path / "audit_template.xlsx")
|
||||
write_json(
|
||||
path / "task.json",
|
||||
{
|
||||
"vars": {"date": "2026-06-18"},
|
||||
"data": {
|
||||
"type": "excel",
|
||||
"path": "./source_audit_data.xlsx",
|
||||
"sheet": "Audit",
|
||||
"header_row": 2,
|
||||
},
|
||||
"template": {"path": "./audit_template.xlsx"},
|
||||
"output": {
|
||||
"path": "./out/audit_{{date}}_{{employee}}_{{_row}}.xlsx",
|
||||
"overwrite": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def build_filter_work_order_to_word(path: Path) -> None:
|
||||
path.mkdir(parents=True)
|
||||
write_work_order_source_xlsx(path / "source_work_orders.xlsx")
|
||||
write_work_order_word_template(path / "work_order_template.docx")
|
||||
write_json(
|
||||
path / "task.json",
|
||||
{
|
||||
"vars": {"date": "2026-06-18"},
|
||||
"data": {
|
||||
"type": "excel",
|
||||
"path": "./source_work_orders.xlsx",
|
||||
"sheet": "工单",
|
||||
"header_row": 1,
|
||||
},
|
||||
"filter": {
|
||||
"equals": {
|
||||
"日期": "{{date}}",
|
||||
"工单编号": "{{ticket_no}}",
|
||||
},
|
||||
"expect": "one",
|
||||
},
|
||||
"template": {"path": "./work_order_template.docx"},
|
||||
"output": {
|
||||
"path": "./out/work_order_{{日期}}_{{工单编号}}.docx",
|
||||
"overwrite": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def write_daily_source_xlsx(path: Path) -> None:
|
||||
rows = [
|
||||
["name", "department", "done", "plan", "risk"],
|
||||
["张三/测试", "营销部", "完成 A 系统订单核对 128 条;同步 B 系统回款 35 笔", "跟进 3 条异常订单并补充截图", "无"],
|
||||
["李四", "客服部", "整理投诉工单 21 条;完成日报提交", "复核高优先级工单并同步主管", "等待业务确认 1 条"],
|
||||
]
|
||||
write_table_workbook(path, "Daily", rows, title="日报源数据")
|
||||
|
||||
|
||||
def write_daily_word_template(path: Path) -> None:
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.top_margin = Inches(0.8)
|
||||
section.bottom_margin = Inches(0.8)
|
||||
section.left_margin = Inches(0.85)
|
||||
section.right_margin = Inches(0.85)
|
||||
|
||||
title = doc.add_paragraph()
|
||||
title_run = title.add_run("员工日报(测试模板)")
|
||||
title_run.bold = True
|
||||
title_run.font.size = Pt(18)
|
||||
|
||||
meta = doc.add_table(rows=4, cols=2)
|
||||
meta.alignment = WD_TABLE_ALIGNMENT.LEFT
|
||||
set_table_style(meta)
|
||||
labels = ["日期", "员工", "部门", "班次"]
|
||||
values = ["{{date}}", "{{na", "{{department}}", "{{batch}}"]
|
||||
for i, label in enumerate(labels):
|
||||
meta.cell(i, 0).text = label
|
||||
if label == "员工":
|
||||
p = meta.cell(i, 1).paragraphs[0]
|
||||
p.add_run(values[i])
|
||||
p.add_run("me}}")
|
||||
else:
|
||||
meta.cell(i, 1).text = values[i]
|
||||
|
||||
for heading, placeholder in [
|
||||
("今日完成", "{{done}}"),
|
||||
("明日计划", "{{plan}}"),
|
||||
("风险备注", "{{risk}}"),
|
||||
]:
|
||||
doc.add_paragraph()
|
||||
h = doc.add_paragraph()
|
||||
r = h.add_run(heading)
|
||||
r.bold = True
|
||||
r.font.size = Pt(12)
|
||||
p = doc.add_paragraph()
|
||||
p.add_run(placeholder)
|
||||
|
||||
doc.save(path)
|
||||
|
||||
|
||||
def write_summary_sqlite(path: Path) -> None:
|
||||
conn = sqlite3.connect(path)
|
||||
try:
|
||||
conn.executescript(
|
||||
"""
|
||||
create table department_summary (
|
||||
department text,
|
||||
report_date text,
|
||||
total_count integer,
|
||||
abnormal_count integer,
|
||||
owner text
|
||||
);
|
||||
insert into department_summary values
|
||||
('客服部', '2026-06-18', 76, 0, '李四'),
|
||||
('营销部', '2026-06-18', 128, 3, '张三'),
|
||||
('营销部', '2026-06-17', 115, 2, '张三');
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def write_summary_excel_template(path: Path) -> None:
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Summary"
|
||||
ws.append(["部门日报汇总(测试模板)", ""])
|
||||
ws.append(["部门", "{{department}}"])
|
||||
ws.append(["日期", "{{report_date}}"])
|
||||
ws.append(["负责人", "{{owner}}"])
|
||||
ws.append(["处理总数", "{{total_count}}"])
|
||||
ws.append(["异常数", "{{abnormal_count}}"])
|
||||
style_sheet(ws, title_rows={1})
|
||||
ws.column_dimensions["A"].width = 18
|
||||
ws.column_dimensions["B"].width = 32
|
||||
wb.save(path)
|
||||
|
||||
|
||||
def write_audit_source_xlsx(path: Path) -> None:
|
||||
rows = [
|
||||
["日报质检导出"],
|
||||
["employee", "date", "item", "score", "comment"],
|
||||
["王五", "2026-06-18", "日报完整性", "92", "字段齐全,说明清楚"],
|
||||
["赵六", "2026-06-18", "系统截图一致性", "87", "缺少 1 张截图,需要补充"],
|
||||
]
|
||||
write_table_workbook(path, "Audit", rows, title=None)
|
||||
|
||||
|
||||
def write_audit_excel_template(path: Path) -> None:
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "AuditReport"
|
||||
ws.append(["日报质检结果(测试模板)", ""])
|
||||
ws.append(["日期", "{{date}}"])
|
||||
ws.append(["员工", "{{employee}}"])
|
||||
ws.append(["检查项", "{{item}}"])
|
||||
ws.append(["得分", "{{score}}"])
|
||||
ws.append(["评语", "{{comment}}"])
|
||||
style_sheet(ws, title_rows={1})
|
||||
ws.column_dimensions["A"].width = 16
|
||||
ws.column_dimensions["B"].width = 42
|
||||
wb.save(path)
|
||||
|
||||
|
||||
def write_work_order_source_xlsx(path: Path) -> None:
|
||||
rows = [
|
||||
["日期", "工单编号", "客户名称", "系统来源", "处理结果", "下一步"],
|
||||
["2026-06-18", "GD-001", "A 公司", "PMS3.0", "已派单", "等待现场反馈"],
|
||||
["2026-06-18", "GD-002", "B 公司", "D5000", "已完成", "归档并同步日报"],
|
||||
["2026-06-19", "GD-001", "C 公司", "风控平台", "待复核", "补充截图"],
|
||||
]
|
||||
write_table_workbook(path, "工单", rows, title="工单源数据")
|
||||
|
||||
|
||||
def write_work_order_word_template(path: Path) -> None:
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.top_margin = Inches(0.8)
|
||||
section.bottom_margin = Inches(0.8)
|
||||
section.left_margin = Inches(0.85)
|
||||
section.right_margin = Inches(0.85)
|
||||
|
||||
title = doc.add_paragraph()
|
||||
title_run = title.add_run("工单处理记录(筛选测试模板)")
|
||||
title_run.bold = True
|
||||
title_run.font.size = Pt(18)
|
||||
|
||||
table = doc.add_table(rows=6, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.LEFT
|
||||
set_table_style(table)
|
||||
rows = [
|
||||
("日期", "{{日期}}"),
|
||||
("工单编号", "{{工单编号}}"),
|
||||
("客户名称", "{{客户名称}}"),
|
||||
("系统来源", "{{系统来源}}"),
|
||||
("处理结果", "{{处理结果}}"),
|
||||
("下一步", "{{下一步}}"),
|
||||
]
|
||||
for idx, (label, value) in enumerate(rows):
|
||||
table.cell(idx, 0).text = label
|
||||
table.cell(idx, 1).text = value
|
||||
|
||||
doc.save(path)
|
||||
|
||||
|
||||
def write_table_workbook(path: Path, sheet: str, rows: list[list[str]], title: str | None) -> None:
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = sheet
|
||||
for row in rows:
|
||||
ws.append(row)
|
||||
title_rows = {1} if title else set()
|
||||
style_sheet(ws, title_rows=title_rows)
|
||||
for idx, width in enumerate([18, 16, 44, 36, 28], start=1):
|
||||
ws.column_dimensions[get_column_letter(idx)].width = width
|
||||
wb.save(path)
|
||||
|
||||
|
||||
def style_sheet(ws, title_rows: set[int]) -> None:
|
||||
header_fill = PatternFill("solid", fgColor="D9EAF7")
|
||||
title_fill = PatternFill("solid", fgColor="1F4E79")
|
||||
thin = Side(style="thin", color="D9E2F3")
|
||||
border = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
for row in ws.iter_rows():
|
||||
for cell in row:
|
||||
cell.alignment = Alignment(vertical="center", wrap_text=True)
|
||||
cell.border = border
|
||||
if cell.row in title_rows:
|
||||
cell.font = Font(bold=True, color="FFFFFF", size=13)
|
||||
cell.fill = title_fill
|
||||
elif cell.row == min(title_rows or {1}) + 1 or (not title_rows and cell.row == 1):
|
||||
cell.font = Font(bold=True)
|
||||
cell.fill = header_fill
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
|
||||
def set_table_style(table) -> None:
|
||||
for row in table.rows:
|
||||
for cell in row.cells:
|
||||
cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
|
||||
for paragraph in cell.paragraphs:
|
||||
for run in paragraph.runs:
|
||||
run.font.size = Pt(10.5)
|
||||
|
||||
|
||||
def write_json(path: Path, data: dict) -> None:
|
||||
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def write_readme() -> None:
|
||||
text = """# docfill 手工测试素材
|
||||
|
||||
本目录提供四组可直接运行的本地测试素材,用于验证 `docfill` 对筛选、Word、Excel、Excel 数据源和 SQLite 数据源的支持情况。
|
||||
|
||||
运行前进入项目根目录:
|
||||
|
||||
```bash
|
||||
cd /Users/zhaoyilun/Documents/xls_doc
|
||||
```
|
||||
|
||||
构建工具:
|
||||
|
||||
```bash
|
||||
bash scripts/build.sh
|
||||
```
|
||||
|
||||
## 案例 1:Excel 数据生成 Word 日报
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
manual_tests/01_excel_to_word
|
||||
```
|
||||
|
||||
运行命令:
|
||||
|
||||
```bash
|
||||
dist/docfill-darwin-arm64 run -c manual_tests/01_excel_to_word/task.json --var batch=早班 --verbose
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```text
|
||||
generated 2 file(s)
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/01_excel_to_word/out/daily_2026-06-18_张三_测试_1.docx
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/01_excel_to_word/out/daily_2026-06-18_李四_2.docx
|
||||
```
|
||||
|
||||
## 案例 2:SQLite 数据生成 Excel 汇总表
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
manual_tests/02_sqlite_to_excel
|
||||
```
|
||||
|
||||
运行命令:
|
||||
|
||||
```bash
|
||||
dist/docfill-darwin-arm64 run -c manual_tests/02_sqlite_to_excel/task.json --var date=2026-06-18 --verbose
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```text
|
||||
generated 2 file(s)
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/02_sqlite_to_excel/out/summary_客服部_1.xlsx
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/02_sqlite_to_excel/out/summary_营销部_2.xlsx
|
||||
```
|
||||
|
||||
## 案例 3:Excel 数据生成 Excel 质检表
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
manual_tests/03_excel_to_excel
|
||||
```
|
||||
|
||||
运行命令:
|
||||
|
||||
```bash
|
||||
dist/docfill-darwin-arm64 run -c manual_tests/03_excel_to_excel/task.json --var date=2026-06-18 --verbose
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```text
|
||||
generated 2 file(s)
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/03_excel_to_excel/out/audit_2026-06-18_王五_1.xlsx
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/03_excel_to_excel/out/audit_2026-06-18_赵六_2.xlsx
|
||||
```
|
||||
|
||||
## 案例 4:按日期和工单编号筛选一条工单并生成 Word
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
manual_tests/04_filter_work_order_to_word
|
||||
```
|
||||
|
||||
输入数据内容:
|
||||
|
||||
```text
|
||||
日期 工单编号 客户名称 系统来源 处理结果 下一步
|
||||
2026-06-18 GD-001 A 公司 PMS3.0 已派单 等待现场反馈
|
||||
2026-06-18 GD-002 B 公司 D5000 已完成 归档并同步日报
|
||||
2026-06-19 GD-001 C 公司 风控平台 待复核 补充截图
|
||||
```
|
||||
|
||||
运行命令:
|
||||
|
||||
```bash
|
||||
dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-002 --verbose
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```text
|
||||
generated 1 file(s)
|
||||
- /Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/out/work_order_2026-06-18_GD-002.docx
|
||||
```
|
||||
|
||||
查看结果:
|
||||
|
||||
```bash
|
||||
open manual_tests/04_filter_work_order_to_word/out
|
||||
```
|
||||
|
||||
## 重新生成素材
|
||||
|
||||
如需重新生成全部手工测试文件,执行:
|
||||
|
||||
```bash
|
||||
/Users/zhaoyilun/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3 scripts/make-manual-test-materials.py
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```text
|
||||
/Users/zhaoyilun/Documents/xls_doc/manual_tests
|
||||
```
|
||||
"""
|
||||
(OUT / "README.md").write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -25,6 +25,10 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "write excel-to-word fixtures: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := writeExcelFilterToWord(filepath.Join(*base, "excel-filter-to-word")); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "write excel-filter-to-word fixtures: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := writeSQLiteToExcel(filepath.Join(*base, "sqlite-to-excel")); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "write sqlite-to-excel fixtures: %v\n", err)
|
||||
os.Exit(1)
|
||||
@@ -45,6 +49,21 @@ func writeExcelToWord(dir string) error {
|
||||
return writeDocx(filepath.Join(dir, "template.docx"), `<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:r><w:t>员工:{{na</w:t></w:r><w:r><w:t>me}}</w:t></w:r></w:p><w:p><w:r><w:t>日期:{{date}}</w:t></w:r></w:p><w:p><w:r><w:t>完成:{{done}}</w:t></w:r></w:p><w:p><w:r><w:t>计划:{{plan}}</w:t></w:r></w:p></w:body></w:document>`)
|
||||
}
|
||||
|
||||
func writeExcelFilterToWord(dir string) error {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeWorkbook(filepath.Join(dir, "data.xlsx"), "Tickets", [][]string{
|
||||
{"date", "ticket_no", "customer", "result"},
|
||||
{"2026-06-18", "GD-001", "A 公司", "已派单"},
|
||||
{"2026-06-18", "GD-002", "B 公司", "已完成"},
|
||||
{"2026-06-19", "GD-001", "C 公司", "待复核"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return writeDocx(filepath.Join(dir, "template.docx"), `<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:r><w:t>工单:{{ticket_no}}</w:t></w:r></w:p><w:p><w:r><w:t>日期:{{date}}</w:t></w:r></w:p><w:p><w:r><w:t>客户:{{customer}}</w:t></w:r></w:p><w:p><w:r><w:t>结果:{{result}}</w:t></w:r></w:p></w:body></w:document>`)
|
||||
}
|
||||
|
||||
func writeSQLiteToExcel(dir string) error {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
|
||||
@@ -17,10 +17,13 @@ cp -R examples "$TMP/examples"
|
||||
go run ./scripts/smoke-fixtures --base "$TMP/examples"
|
||||
|
||||
"$TMP/docfill" run -c "$TMP/examples/excel-to-word/task.json" --var date=2026-06-18
|
||||
"$TMP/docfill" run -c "$TMP/examples/excel-filter-to-word/task.json" --var date=2026-06-18 --var ticket_no=GD-002
|
||||
"$TMP/docfill" run -c "$TMP/examples/sqlite-to-excel/task.json" --var date=2026-06-18
|
||||
|
||||
test -f "$TMP/examples/excel-to-word/out/daily_2026-06-18_张三_1.docx"
|
||||
test -f "$TMP/examples/excel-to-word/out/daily_2026-06-18_李四_2.docx"
|
||||
test -f "$TMP/examples/excel-filter-to-word/out/ticket_2026-06-18_GD-002.docx"
|
||||
test ! -f "$TMP/examples/excel-filter-to-word/out/ticket_2026-06-18_GD-001.docx"
|
||||
|
||||
xlsx_count="$(find "$TMP/examples/sqlite-to-excel/out" -type f -name '*.xlsx' | wc -l | tr -d ' ')"
|
||||
if [[ "$xlsx_count" != "2" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user