integrate question table generate function

This commit is contained in:
kuangji
2026-05-26 17:16:30 +08:00
parent 77b2d6a27d
commit b34e67f4a7
5 changed files with 45 additions and 3 deletions

View File

@@ -24,6 +24,7 @@ from app.analyzer import (
from app.config import load_api_config
from app.docx_parser import parse_docx
from app.report_generator import generate_docx_report, generate_markdown_report
from app.review_filler import fill_review_docx_from_analysis
from app.skill_loader import load_skill_catalog
@@ -33,6 +34,7 @@ OUTPUT_DIR = ROOT_DIR / "outputs"
SKILL_ROOT = ROOT_DIR / "skills"
DEFAULT_SKILL_COLLECTION = "GJB438C-2021_prd_skills"
CONFIG_PATH = ROOT_DIR / "configs" / "api_config.yaml"
REVIEW_DOCX_TEMPLATE = ROOT_DIR / "test" / "附录A文档审查.docx"
MAX_UPLOAD_BYTES = 30 * 1024 * 1024
MAX_SKILL_ARCHIVE_BYTES = 50 * 1024 * 1024
ProgressCallback = Callable[[int, str], None]
@@ -263,14 +265,21 @@ def analyze_saved_docx(
progress(85, "正在生成 Markdown 分析文档")
markdown_path = generate_markdown_report(report, OUTPUT_DIR)
progress(92, "正在生成 DOCX 文档审查单")
review_docx_path = markdown_path.with_name(f"{markdown_path.stem}_review.docx")
fill_review_docx_from_analysis(markdown_path, REVIEW_DOCX_TEMPLATE, review_docx_path)
progress(100, "分析完成")
return {
"source_filename": parsed.filename,
"summary": report.summary,
"matched_skills": report.matched_skills,
"downloads": {"markdown": f"/download/{markdown_path.name}"},
"downloads": {
"markdown": f"/download/{markdown_path.name}",
"review_docx": f"/download/{review_docx_path.name}",
},
"markdown_filename": markdown_path.name,
"review_docx_filename": review_docx_path.name,
}

View File

@@ -5,6 +5,7 @@ const skillUploadStatus = document.querySelector("#skill-upload-status");
const result = document.querySelector("#result");
const summary = document.querySelector("#summary");
const skills = document.querySelector("#skills");
const reviewDocxLink = document.querySelector("#download-review-docx");
const mdLink = document.querySelector("#download-md");
const progressBar = document.querySelector("#analysis-progress");
const statusText = document.querySelector("#analysis-status");
@@ -120,6 +121,7 @@ form.addEventListener("submit", async (event) => {
item.textContent = name;
skills.appendChild(item);
});
reviewDocxLink.href = task.downloads.review_docx;
mdLink.href = task.downloads.markdown;
} catch (error) {
summary.textContent = error.message;

View File

@@ -97,7 +97,7 @@
<p id="summary"></p>
<div id="skills" class="skills"></div>
<div class="downloads">
<!-- <a id="download-docx" href="#">下载 DOCX 报告</a> -->
<a id="download-review-docx" href="#">下载 DOCX 审查单</a>
<a id="download-md" href="#">下载 Markdown 报告</a>
</div>
</section>

View File

@@ -0,0 +1,23 @@
# Handoff - 2026-05-26
## Completed Tasks
- 昨天完成了独立模块 `app/review_filler.py` 向 FastAPI 主流程的集成:在 Markdown 分析报告生成后,自动调用审查单填充逻辑,生成已勾选的 DOCX 文档审查单。
- 新增审查单模板路径 `REVIEW_DOCX_TEMPLATE`,当前沿用 `test/附录A文档审查.docx`,生成结果写入现有 `outputs/` 目录,并通过 `/download/{filename}` 下载。
- 扩展分析任务返回值,在原有 `markdown` 下载项之外新增 `review_docx` 下载项,同时保留 `markdown_filename` 并新增 `review_docx_filename`
- 更新系统 UI在分析结果区域新增“下载 DOCX 审查单”按钮,并在前端轮询任务完成后绑定 `task.downloads.review_docx`
- 补充 Web 集成测试,验证页面包含新下载入口、分析流程生成 DOCX 审查单,并校验 A.2、A.3、A.4 审查表每个序号行均满足三选一互斥勾选。
- 完成验证:`pytest tests/test_web.py tests/test_review_filler.py` 通过,`pytest` 全量测试通过,结果为 `24 passed``git diff --check` 通过。
- 启动本地服务并用真实 `/analyze` 上传流程做了运行验证,确认任务完成后返回 Markdown 和 DOCX 审查单两个下载项。
## Blockers
- 当前审查单模板仍位于 `test/附录A文档审查.docx`,可运行但不够产品化;后续建议迁移到专门的模板或资源目录。
- `app/review_filler.py` 的判定仍依赖 Markdown 自然语言报告和关键词规则,准确性受模型输出格式影响,自动勾选结果仍需要人工复核。
- 本地启发式分析模式下没有结构化“符合项/不符合项”证据段,审查单可生成并通过互斥校验,但判定质量偏保守。
- 默认会填写 A.2、A.3、A.4 全部审查单;如果上传文档只对应单一文档类型,后续可能需要在 Web 流程中提供目标审查表选择。
## Next Steps
- 明天计划将审查单模板从 `test/` 迁移到正式资源目录,例如 `resources/templates/``app/templates/docx/`,并更新常量和测试。
- 优化模型分析输出格式,增加结构化审查证据或审查项结果,降低 `review_filler` 对自然语言关键词匹配的依赖。
- 在 UI 中评估是否增加“目标审查表”选择项,支持只生成 A.2、A.3 或 A.4 的审查单填写结果。
- 增加端到端测试,覆盖 `/analyze` 提交、任务轮询、Markdown 下载和 DOCX 审查单下载的完整 HTTP 流程。
- 继续抽查真实样本文档生成的审查单,重点确认“未通过”和“不适用”判定是否符合人工审查预期。

View File

@@ -6,6 +6,7 @@ from docx import Document
import app.main as main
from app.main import OUTPUT_DIR, ROOT_DIR, analyze_saved_docx, app
from app.review_filler import validate_review_results
class FakeUploadFile:
@@ -44,7 +45,9 @@ def test_index_template_contains_upload_ui() -> None:
assert "analysis-progress" in html
assert "analysis-status" in html
assert "下载 Markdown 报告" in html
assert "下载 DOCX 审查单" in html
assert "download-md" in js
assert "download-review-docx" in js
assert "pollTask" in js
assert "skill_collection" in html
assert "skill-upload-form" in html
@@ -150,9 +153,14 @@ def test_analyze_saved_docx_creates_downloadable_report(tmp_path: Path) -> None:
payload = analyze_saved_docx(docx_path, provider="deepseek", use_model=False)
assert payload["source_filename"] == "upload.docx"
assert "docx" not in payload["downloads"]
assert payload["downloads"]["markdown"].endswith(".md")
assert payload["downloads"]["review_docx"].endswith(".docx")
assert (OUTPUT_DIR / Path(payload["downloads"]["markdown"]).name).exists()
review_docx_path = OUTPUT_DIR / Path(payload["downloads"]["review_docx"]).name
assert review_docx_path.exists()
assert validate_review_results(review_docx_path, "A.2") == []
assert validate_review_results(review_docx_path, "A.3") == []
assert validate_review_results(review_docx_path, "A.4") == []
def test_analyze_saved_docx_uses_selected_collection(tmp_path: Path) -> None: