""" cost_matrix.py ────────────── Deterministic INR cost lookup table for GharScan. No model hallucination here — pure market-rate data (Delhi/NCR, 2026). Cost ranges sourced from: HouseYog, Comaron, UrbanCompany quoted rates, and standard PWD schedule-of-rates for residential repairs. Lower bound = Tier-2/3 city rates OR small affected area (< 2 sq ft) Upper bound = Delhi/NCR metro rates OR large affected area (> 10 sq ft) """ from dataclasses import dataclass from typing import Optional @dataclass(frozen=True) class CostEntry: cost_range_inr: str professional_type: str # painter | mason | waterproofing_contractor | civil_engineer urgency_timeline: str # next_renovation | within_6_months | within_1_month | this_week | immediately disclaimer: Optional[str] = None requires_engineer: bool = False # ── Severity labels ──────────────────────────────────────────────────────────── SEVERITY_LABELS = { 1: "Cosmetic", 2: "Minor", 3: "Moderate", 4: "Serious", 5: "Critical", } SEVERITY_COLORS = { 1: "#22c55e", # green 2: "#84cc16", # lime 3: "#f59e0b", # amber 4: "#ef4444", # red 5: "#7f1d1d", # dark red } # ── Main cost matrix ─────────────────────────────────────────────────────────── # Structure: COST_MATRIX[defect_type][severity_1_to_5] = CostEntry COST_MATRIX: dict[str, dict[int, CostEntry]] = { "hairline_crack": { 1: CostEntry("₹200–₹400", "painter", "next_renovation"), 2: CostEntry("₹400–₹800", "painter", "next_renovation"), 3: CostEntry("₹800–₹1,500", "mason", "within_6_months"), 4: CostEntry("₹1,500–₹3,000","mason", "within_1_month"), 5: CostEntry("₹3,000–₹6,000","civil_engineer", "this_week", disclaimer="Multiple hairline cracks over large area — professional assessment strongly advised."), }, "settlement_crack": { 1: CostEntry("₹400–₹600", "mason", "next_renovation"), 2: CostEntry("₹600–₹1,200", "mason", "within_6_months"), 3: CostEntry("₹1,500–₹4,000", "mason", "within_6_months"), 4: CostEntry("₹5,000–₹15,000", "civil_engineer", "within_1_month", disclaimer="Severity 4 settlement cracks indicate significant differential movement. Professional structural assessment required before any repair.", requires_engineer=True), 5: CostEntry("Assessment required", "civil_engineer", "this_week", disclaimer="URGENT: Wide or active settlement cracks may indicate ongoing foundation failure. Do not delay professional structural inspection.", requires_engineer=True), }, "structural_crack": { 1: CostEntry("₹800–₹1,500", "mason", "within_6_months"), 2: CostEntry("₹2,000–₹6,000", "civil_engineer", "within_1_month"), 3: CostEntry("₹8,000–₹25,000", "civil_engineer", "this_week", requires_engineer=True), 4: CostEntry("Assessment required", "civil_engineer", "this_week", disclaimer="SERIOUS: Structural cracks at this severity require immediate professional inspection. Do not attempt DIY repairs.", requires_engineer=True), 5: CostEntry("Vacate and call engineer", "civil_engineer", "immediately", disclaimer="CRITICAL STRUCTURAL RISK: This defect may compromise load-bearing capacity. Vacate the area immediately and contact a structural engineer.", requires_engineer=True), }, "water_seepage": { 1: CostEntry("₹500–₹1,000", "mason", "next_renovation"), 2: CostEntry("₹1,500–₹4,000", "waterproofing_contractor", "within_6_months"), 3: CostEntry("₹4,000–₹12,000", "waterproofing_contractor", "within_1_month"), 4: CostEntry("₹10,000–₹25,000","waterproofing_contractor", "this_week", disclaimer="Extensive seepage can weaken structural elements over time. Address before next monsoon season."), 5: CostEntry("₹20,000–₹60,000","waterproofing_contractor", "this_week", disclaimer="Active water ingress at this scale indicates waterproofing system failure. Professional treatment of source (terrace/external wall) is required, not just surface patching.", requires_engineer=True), }, "efflorescence": { 1: CostEntry("₹200–₹500", "painter", "next_renovation"), 2: CostEntry("₹500–₹1,500", "mason", "next_renovation"), 3: CostEntry("₹1,500–₹4,000","waterproofing_contractor", "within_6_months"), 4: CostEntry("₹4,000–₹10,000","waterproofing_contractor", "within_1_month", disclaimer="Heavy efflorescence indicates sustained moisture migration — treat the moisture source, not just the surface deposits."), 5: CostEntry("₹10,000–₹20,000","waterproofing_contractor", "this_week", disclaimer="Severe salt deposits with visible masonry deterioration. Source moisture must be identified and stopped before cosmetic repair."), }, "spalling": { 1: CostEntry("₹1,000–₹2,500", "mason", "within_6_months"), 2: CostEntry("₹3,000–₹8,000", "mason", "within_1_month"), 3: CostEntry("₹8,000–₹20,000", "civil_engineer", "this_week", requires_engineer=True), 4: CostEntry("Assessment required", "civil_engineer", "this_week", disclaimer="SERIOUS: Concrete spalling with exposed reinforcement requires professional structural assessment — the scope of repair cannot be estimated from image alone.", requires_engineer=True), 5: CostEntry("Vacate area — engineer required", "civil_engineer", "immediately", disclaimer="CRITICAL: Extensive spalling with exposed steel poses imminent risk of further concrete fall. Vacate the area below.", requires_engineer=True), }, "rebar_rust": { 1: CostEntry("₹2,000–₹5,000", "mason", "within_1_month"), 2: CostEntry("₹5,000–₹15,000", "civil_engineer", "this_week", requires_engineer=True), 3: CostEntry("₹15,000–₹40,000", "civil_engineer", "this_week", disclaimer="Rebar corrosion reduces structural capacity. Extent of rust along rebar length must be assessed by a structural engineer.", requires_engineer=True), 4: CostEntry("Assessment required", "civil_engineer", "immediately", disclaimer="SERIOUS: Visible rebar corrosion with concrete delamination. Structural capacity may be significantly compromised.", requires_engineer=True), 5: CostEntry("Vacate area — urgent inspection", "civil_engineer", "immediately", disclaimer="CRITICAL STRUCTURAL RISK: Extensive rebar corrosion threatens structural integrity. Immediate professional inspection mandatory.", requires_engineer=True), }, "plaster_delamination": { 1: CostEntry("₹300–₹600", "painter", "next_renovation"), 2: CostEntry("₹600–₹1,500", "mason", "next_renovation"), 3: CostEntry("₹1,500–₹5,000", "mason", "within_6_months"), 4: CostEntry("₹5,000–₹12,000","mason", "within_1_month", disclaimer="Large-area delamination often indicates substrate moisture. Identify and treat moisture source before replastering."), 5: CostEntry("₹12,000–₹25,000","mason", "this_week", disclaimer="Extensive plaster delamination over large wall area. Complete replastering required after identifying and treating root cause."), }, "no_defect": { 1: CostEntry("₹0 — No action needed", "none", "next_renovation"), 2: CostEntry("₹0 — No action needed", "none", "next_renovation"), 3: CostEntry("₹0 — No action needed", "none", "next_renovation"), 4: CostEntry("₹0 — No action needed", "none", "next_renovation"), 5: CostEntry("₹0 — No action needed", "none", "next_renovation"), }, } # ── Professional display names ───────────────────────────────────────────────── PROFESSIONAL_DISPLAY = { "painter": "Local Painter / Decorator", "mason": "Licensed Mason (Raj Mistri)", "waterproofing_contractor": "Waterproofing Contractor", "civil_engineer": "Licensed Civil / Structural Engineer", "none": "No professional required", } TIMELINE_DISPLAY = { "next_renovation": "At your next renovation", "within_6_months": "Within 6 months", "within_1_month": "Within 1 month", "this_week": "This week", "immediately": "IMMEDIATELY", } def get_cost_entry(defect_type: str, severity: int) -> CostEntry: """ Primary lookup function. Returns CostEntry for a given defect_type × severity combination. Falls back to a safe default if defect type is not in matrix. """ if defect_type not in COST_MATRIX: return CostEntry( cost_range_inr="Assessment required", professional_type="civil_engineer", urgency_timeline="within_1_month", disclaimer="Unrecognized defect type. Please consult a professional for assessment.", requires_engineer=True, ) severity = max(1, min(5, int(severity))) # Clamp to valid range return COST_MATRIX[defect_type][severity] def build_cost_response(defect_type: str, severity: int) -> dict: """ Returns a fully-structured dict ready for the frontend renderer. Includes display-friendly strings + disclaimer flag. """ entry = get_cost_entry(defect_type, severity) return { "cost_range_inr": entry.cost_range_inr, "professional_type": entry.professional_type, "professional_display": PROFESSIONAL_DISPLAY.get(entry.professional_type, entry.professional_type), "urgency_timeline": entry.urgency_timeline, "urgency_display": TIMELINE_DISPLAY.get(entry.urgency_timeline, entry.urgency_timeline), "severity_label": SEVERITY_LABELS.get(severity, "Unknown"), "severity_color": SEVERITY_COLORS.get(severity, "#6b7280"), "requires_engineer": entry.requires_engineer, "disclaimer": entry.disclaimer, # Hard disclaimer for all Severity 4-5 (Watch-Out 2) "show_liability_banner": severity >= 4, "liability_text": ( "⚠️ These are estimated market rates only. " "For Severity 4–5 defects, always obtain a professional site assessment " "before starting any repair work. GharScan is a triage tool, not a substitute " "for a licensed engineer or contractor." ) if severity >= 4 else None, }