import zipfile from pathlib import Path import pytest from docx import Document from review_agent.application_form_fill.schemas import MergedField, TemplateSpec from review_agent.application_form_fill.services.word_fill import create_word_export, fill_template from review_agent.models import ( ApplicationFormFillArtifact, ApplicationFormFillBatch, Conversation, ExportedSummaryFile, FileSummaryBatch, ) pytestmark = pytest.mark.django_db def _spec(): return TemplateSpec( code="registration_certificate", name="注册证格式", source_file="template.docx", output_label="注册证格式", applies_when={"registration_type": ["首次注册"]}, file_format="docx", fields=[ {"key": "product_name", "label": "产品名称", "target": {"type": "table_row", "row_label": "产品名称"}}, {"key": "intended_use", "label": "预期用途", "target": {"type": "table_row", "row_label": "预期用途"}}, ], ) def _template(path): document = Document() table = document.add_table(rows=2, cols=2) table.rows[0].cells[0].text = "产品名称" table.rows[1].cells[0].text = "预期用途" document.save(path) def test_word_fill_writes_table_rows(tmp_path): template_path = tmp_path / "template.docx" output_path = tmp_path / "filled.docx" _template(template_path) fill_template( template_path, output_path, _spec(), { "product_name": MergedField("product_name", "产品名称", "甲胎蛋白检测试剂盒", "说明书.txt", "证据", 0.8), "intended_use": MergedField("intended_use", "预期用途", "用于体外检测", "说明书.txt", "证据", 0.8), }, ) document = Document(output_path) assert document.tables[0].rows[0].cells[1].text == "甲胎蛋白检测试剂盒" assert document.tables[0].rows[1].cells[1].text == "用于体外检测" def test_word_fill_highlights_conflict_in_docx_xml(tmp_path): template_path = tmp_path / "template.docx" output_path = tmp_path / "filled.docx" _template(template_path) fill_template( template_path, output_path, _spec(), { "product_name": MergedField( "product_name", "产品名称", "甲胎蛋白检测试剂盒", "说明书.txt", "证据", 0.8, has_conflict=True, ) }, conflicts=[{"field_key": "product_name"}], ) with zipfile.ZipFile(output_path) as package: document_xml = package.read("word/document.xml").decode("utf-8") assert 'w:fill="FFFF00"' in document_xml assert 'w:color w:val="FF0000"' in document_xml def test_create_word_export_records_artifact_and_export(settings, tmp_path, django_user_model): settings.MEDIA_ROOT = tmp_path user = django_user_model.objects.create_user(username="owner", password="pass") conversation = Conversation.objects.create(user=user, title="会话") summary = FileSummaryBatch.objects.create(conversation=conversation, user=user, batch_no="FS-WORD") batch = ApplicationFormFillBatch.objects.create( conversation=conversation, user=user, source_summary_batch=summary, batch_no="AFF-WORD", product_name="甲胎蛋白检测试剂盒", work_dir=str(tmp_path / "aff" / "AFF-WORD"), ) template_path = tmp_path / "template.docx" _template(template_path) exported = create_word_export( batch, _spec(), template_path, {"product_name": MergedField("product_name", "产品名称", "甲胎蛋白检测试剂盒", "说明书.txt", "证据", 0.8)}, ) assert exported.export_type == ExportedSummaryFile.ExportType.WORD assert exported.workflow_type == "application_form_fill" assert exported.workflow_batch_id == batch.pk assert ApplicationFormFillArtifact.objects.filter( batch=batch, artifact_type=ApplicationFormFillArtifact.ArtifactType.FILLED_TEMPLATE, ).exists() def test_create_word_export_sanitizes_product_name_newlines(settings, tmp_path, django_user_model): settings.MEDIA_ROOT = tmp_path user = django_user_model.objects.create_user(username="owner", password="pass") conversation = Conversation.objects.create(user=user, title="会话") summary = FileSummaryBatch.objects.create(conversation=conversation, user=user, batch_no="FS-WORD-NL") batch = ApplicationFormFillBatch.objects.create( conversation=conversation, user=user, source_summary_batch=summary, batch_no="AFF-WORD-NL", product_name="原体核酸检测试剂盒(荧\n光PCR法)", work_dir=str(tmp_path / "aff" / "AFF-WORD-NL"), ) template_path = tmp_path / "template.docx" _template(template_path) exported = create_word_export( batch, _spec(), template_path, {"product_name": MergedField("product_name", "产品名称", "原体核酸检测试剂盒", "说明书.txt", "证据", 0.8)}, ) assert "\n" not in exported.file_name assert "\r" not in exported.file_name assert Path(exported.storage_path).exists()