GharScan / cost_matrix.py
Ritvik Shrivastava
feat: initial GharScan deployment
41c0a9e
Raw
History Blame Contribute Delete
11.7 kB
"""
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,
}