Spaces:
Runtime error
Runtime error
File size: 5,279 Bytes
aad7814 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | """Global P2 high-confidence lexicon coverage and backend/frontend sync tests."""
from __future__ import annotations
import re
from pathlib import Path
import pytest
from backend.core.notes_high_confidence_lexicon import HIGH_CONFIDENCE_LEXICON
from backend.core.notes_keyword_router import classify_note_cascade
# Sample fragments per user-specified group (section_id, note_line).
P2_ROUTING_CASES: list[tuple[str, str]] = [
# A β About the inspection
("A4", "Weather dry and overcast during inspection, no raining."),
("A5", "Property vacant and unoccupied at time of visit."),
("A1", "Surveyor name John Smith RICS member."),
("A3", "Related party disclosure noted for vendor."),
# B β Overall
("B2", "Urgent repair required to roof coverings."),
("B1", "Condition rating 3 applied to several elements."),
("B3", "Further investigation recommended by specialist."),
# C β Property
("C1", "Detached property of traditional brick construction."),
("C2", "Built around 1930 with later extended rear wing."),
("C5", "Local amenities and facilities within walking distance."),
("C4", "Mains gas and mains electric connected."),
# G β Grounds (before D/E/F)
("G1", "Garage roof pitched with concrete tiles."),
("G2", "Permanent outbuilding used as workshop."),
("G3", "Driveway and patio with boundary wall fencing."),
# D β Outside
("D1", "Chimney stack with defective flaunching and pots."),
("D2", "Pitched roof with slipped slates and battens visible."),
("D3", "UPVC gutters and rainwater downpipe to gullies."),
("D4", "External walls 300mm cavity wall with step cracking and lintel corrosion."),
("D5", "uPVC windows double glazed with timber frame casement."),
("D6", "Front door and patio door threshold worn, bifold to rear."),
# E β Inside
("E1", "Loft conversion with visible rafters and purlins."),
("E2", "Ceiling cracks to plasterboard lath and plaster."),
("E3", "Stud wall internal partitions require plastering."),
("E4", "Suspended timber floor with springy floorboards."),
("E5", "Chimney breast hearth with woodburner and flue liner."),
# F β Services
("F1", "Consumer unit fuse board wiring and sockets tested."),
("F2", "Gas meter and gas supply LPG bottle store."),
("F4", "Boiler and radiators central heating system."),
("F5", "Hot water cylinder with immersion heater."),
("F6", "Manhole and inspection chamber on foul drain soil pipe."),
# I β Risks
("I3", "Suspected asbestos artex to ceiling."),
("I1", "Evidence of woodworm and structural movement."),
("I2", "Japanese knotweed identified in rear garden."),
# J β Energy / insulation disambiguation
("E1", "Roof insulation between rafters in roof void."),
("J1", "Cavity insulation below recommended thickness."),
("J1", "Loft insulation thin at hatch."),
("J1", "General insulation upgrade recommended throughout."),
("J3", "Low energy lighting with LED fittings."),
# Original regression cases
("D4", "External walls: 300mm cavity wall construction with step cracking"),
("D3", "Rainwater fittings: UPVC gutters and gullies present. Rainwater downpipe discharges below ground."),
("G1", "G1, garage roof pitched design with concrete tiles"),
]
@pytest.mark.parametrize("expected_section,note_line", P2_ROUTING_CASES, ids=lambda p: f"{p[0]}:{p[1][:40]}")
def test_p2_high_confidence_routes_globally(expected_section: str, note_line: str):
section_id, score, _ = classify_note_cascade(note_line)
assert section_id == expected_section, f"Expected {expected_section}, got {section_id} for: {note_line!r}"
assert score == 1.0
def test_insulation_disambiguation_roof_vs_cavity_vs_loft():
assert classify_note_cascade("Roof insulation between trusses in loft void.")[0] == "E1"
assert classify_note_cascade("Cavity insulation missing to external walls.")[0] == "J1"
assert classify_note_cascade("Loft insulation thin at hatch.")[0] == "J1"
assert classify_note_cascade("Insulation upgrade recommended.")[0] == "J1"
def test_garage_before_roof_coverings():
sid, _, _ = classify_note_cascade("Garage roof pitched design with concrete tiles")
assert sid == "G1"
def test_chimney_repoint_before_wall_repoint():
sid, _, _ = classify_note_cascade("Chimney stack: Brick chimney with repointing required.")
assert sid == "D1"
def test_asbestos_in_garage_routes_i3_not_g1():
sid, _, _ = classify_note_cascade("Suspected chrysotile artex to ceiling in garage.")
assert sid == "I3"
def test_frontend_high_confidence_synced_with_backend():
"""Frontend routing is delegated to POST /route-notes; lexicon lives in Python only."""
from backend.core.notes_high_confidence_lexicon import HIGH_CONFIDENCE_LEXICON
html = Path("frontend/index.html").read_text(encoding="utf-8")
assert "/route-notes" in html
assert "highConfidence" not in html
assert len(HIGH_CONFIDENCE_LEXICON) >= 50
def test_lexicon_covers_all_canonical_leaf_groups():
"""Every parent section AβN has at least one P2 entry."""
parents = {sid[0] for sid, _ in HIGH_CONFIDENCE_LEXICON}
assert parents == set("ABCDEFGHIJKLMN")
|