from __future__ import annotations from docx import Document from backend.config import settings from backend.core import ingest, section_mapper, template_discoverer from backend.core.pii_scrubber import scrub_rag_chunk from backend.core.report_assembler import to_docx from backend.models.schema import RatingSystem, TemplateSchema def _write_bundle(tmp_path): """Minimal PDF-structure docx + Word paragraphs docx.""" tpl = tmp_path / "template.docx" doc = Document() doc.add_paragraph("D1 Chimney stacks") doc.add_paragraph("D2 Roof coverings") doc.save(str(tpl)) paras = tmp_path / "paragraphs.docx" doc2 = Document() doc2.add_paragraph("Section E1 - Chimney Stacks") doc2.add_paragraph("The chimney stacks were inspected from ground level.") doc2.add_paragraph("Section E2 - Roof Coverings") doc2.add_paragraph("The roof covering comprises slate.") doc2.save(str(paras)) return tpl, paras def test_no_rating_when_schema_undetected(tmp_path, monkeypatch): monkeypatch.setattr(settings, "data_dir", str(tmp_path / "data")) tpl, paras = _write_bundle(tmp_path) tenant = "t-int" ingest.ingest_report_template(tenant, tpl) ingest.ingest_standard_paragraphs(tenant, paras) ingest.ingest_reference(tenant, paras) schema = template_discoverer.load_schema(tenant) schema.rating_system = RatingSystem(detected=False) template_discoverer.save_schema(tenant, schema) result = section_mapper.generate_report( tenant, "D1: chimney cracked\n\nroof slate slipped", interference_level="maximum" ) for sec in result.sections: assert sec.rating_value is None def test_pii_never_in_docx_output(tmp_path, monkeypatch): monkeypatch.setattr(settings, "data_dir", str(tmp_path / "data")) tpl, paras = _write_bundle(tmp_path) tenant = "t-pii" ingest.ingest_report_template(tenant, tpl) ingest.ingest_standard_paragraphs(tenant, paras) ingest.ingest_reference(tenant, paras) notes = "D1: chimney cracked at SW1A 1AA\n\nroof slate slipped" result = section_mapper.generate_report(tenant, notes, interference_level="maximum") schema = template_discoverer.load_schema(tenant) docx = to_docx(result, schema) assert b"SW1A" not in docx assert b"1AA" not in docx full = scrub_rag_chunk(result.unassigned_text + "".join(s.text for s in result.sections)) assert "SW1A" not in full def test_unmatched_observation_preserved_in_notes_field(tmp_path, monkeypatch): """Offline stitch still produces section text from notes (no invention).""" monkeypatch.setattr(settings, "data_dir", str(tmp_path / "data")) tpl, paras = _write_bundle(tmp_path) tenant = "t-ground" ingest.ingest_report_template(tenant, tpl) ingest.ingest_standard_paragraphs(tenant, paras) ingest.ingest_reference(tenant, paras) result = section_mapper.generate_report( tenant, "D1: unusual widget failure on chimney", interference_level="maximum" ) d1 = next(s for s in result.sections if s.section_id == "D1") assert "widget" in d1.text.lower() or "chimney" in d1.text.lower()