| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- import tempfile
- import unittest
- from concurrent.futures import ThreadPoolExecutor
- from pathlib import Path
- from types import SimpleNamespace
- from unittest.mock import patch
- from docx import Document
- from docx.enum.text import WD_BREAK
- from docx.oxml import OxmlElement
- from models import BidOutline, Chapter, CompanyInfo, TemplateSection, TemplateStructure, TenderAnalysis
- from step3_outlining.template_parser import (
- _read_template_body_texts, get_template_text_for_chapter, get_template_text_for_section,
- )
- from step4_writing import _write_single_chapter
- from step4_writing.content_injector import _ContentInjector
- class TemplateTextReadingTests(unittest.TestCase):
- def setUp(self):
- self.directory = tempfile.TemporaryDirectory()
- self.addCleanup(self.directory.cleanup)
- self.path = Path(self.directory.name) / 'template.docx'
- doc = Document()
- doc.add_paragraph('第四章 服务方案')
- doc.add_paragraph('')
- doc.add_table(rows=1, cols=1).cell(0, 0).text = '表内文本不计入正文索引'
- p = doc.add_paragraph('正文\t制表\n换行')
- p.add_run().add_break(WD_BREAK.PAGE)
- link = OxmlElement('w:hyperlink')
- run = OxmlElement('w:r')
- text = OxmlElement('w:t')
- text.text = '链接'
- run.append(text)
- run.append(OxmlElement('w:noBreakHyphen'))
- link.append(run)
- p._p.append(link)
- doc.add_paragraph('第五章 其他方案')
- self.expected = [p.text for p in doc.paragraphs]
- doc.save(self.path)
- self.structure = TemplateStructure(file_path=str(self.path), sections=[
- TemplateSection(name='第四章 服务方案', start_para=0, end_para=2),
- TemplateSection(name='第五章 其他方案', start_para=3, end_para=99),
- ])
- def test_top_level_indices_and_visible_text_match_python_docx(self):
- self.assertEqual(_read_template_body_texts(str(self.path)), self.expected)
- def test_parallel_section_and_title_reads_do_not_use_native_parser(self):
- expected = '\n'.join(t.strip() for t in self.expected[:3] if t.strip())
- def read(index):
- if index % 2:
- return get_template_text_for_section(self.structure, '第四章 服务方案')
- return get_template_text_for_chapter(self.structure, '第四章 服务方案', use_llm=False)
- with patch('step3_outlining.template_parser.DocxDocument', side_effect=AssertionError(
- '并发纯文本取文不得进入python-docx原生解析器'
- )), ThreadPoolExecutor(max_workers=5) as pool:
- self.assertEqual(list(pool.map(read, range(25))), [expected] * 25)
- self.assertEqual(get_template_text_for_section(self.structure, '第五章 其他方案'), self.expected[3])
- self.assertEqual(get_template_text_for_section(self.structure, '不存在'), '')
- def test_step4_chapter_generation_uses_streamed_template_text(self):
- chapter = Chapter(id='4', title='服务方案', template_section='第四章 服务方案')
- outline = BidOutline(project_name='测试项目', chapters=[chapter])
- with patch('step3_outlining.template_parser.DocxDocument', side_effect=TypeError(
- "'CT_P' object is not callable"
- )):
- result = _write_single_chapter(
- chapter=chapter, writer=SimpleNamespace(), injector=_ContentInjector({}, []),
- analysis=TenderAnalysis(project_name='测试项目'), company_info=CompanyInfo(),
- template_structure=self.structure, cfg=SimpleNamespace(enforce_word_limit=False),
- outline=outline, placeholder_map={},
- )
- self.assertEqual(result.generated_content, '\n'.join(t for t in self.expected[:3] if t))
- self.assertTrue(result.preserve_template_layout)
|