bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
430 lines
16 KiB
Python
430 lines
16 KiB
Python
"""
|
|
Decision Support Engine
|
|
Transforms observations → decisions → actions → outcomes
|
|
Not selling data. Selling better decisions.
|
|
"""
|
|
|
|
from typing import Dict, List, Optional, Tuple
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
class DecisionType(str, Enum):
|
|
"""Types of decisions supported"""
|
|
INVESTMENT = "investment" # Where to invest
|
|
MAINTENANCE = "maintenance" # What to maintain first
|
|
RISK_MITIGATION = "risk_mitigation" # How to reduce risk
|
|
ESTABLISHMENT = "establishment" # Where to establish business
|
|
RESOURCE_ALLOCATION = "resource_allocation" # How to allocate budget
|
|
SAFETY_IMPROVEMENT = "safety_improvement" # How to improve safety
|
|
|
|
|
|
class Stakeholder(str, Enum):
|
|
"""Target stakeholders"""
|
|
MUNICIPALITY = "municipality"
|
|
PROPERTY_OWNER = "property_owner"
|
|
INFRASTRUCTURE_OWNER = "infrastructure_owner"
|
|
INSURANCE = "insurance"
|
|
BANK = "bank"
|
|
RETAIL = "retail"
|
|
LOGISTICS = "logistics"
|
|
CITIZEN = "citizen"
|
|
|
|
|
|
@dataclass
|
|
class DecisionRecommendation:
|
|
"""A decision recommendation"""
|
|
decision_type: DecisionType
|
|
stakeholder: Stakeholder
|
|
recommendation: str
|
|
expected_impact: float
|
|
confidence: float
|
|
cost_estimate_usd: Optional[float]
|
|
timeline_months: Optional[int]
|
|
supporting_data: List[Dict]
|
|
risks: List[str]
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
"decision_type": self.decision_type.value,
|
|
"stakeholder": self.stakeholder.value,
|
|
"recommendation": self.recommendation,
|
|
"expected_impact": round(self.expected_impact, 2),
|
|
"confidence": round(self.confidence, 2),
|
|
"cost_estimate_usd": self.cost_estimate_usd,
|
|
"timeline_months": self.timeline_months,
|
|
"supporting_data": self.supporting_data,
|
|
"risks": self.risks
|
|
}
|
|
|
|
|
|
class DecisionEngine:
|
|
"""Generates decision recommendations from reality signals"""
|
|
|
|
def __init__(self):
|
|
self.decision_models = self._load_decision_models()
|
|
self.visual_geo_enabled = True # NEW
|
|
|
|
def _load_decision_models(self) -> Dict:
|
|
"""Load decision models for each stakeholder"""
|
|
return {
|
|
Stakeholder.MUNICIPALITY: {
|
|
"priorities": ["safety", "maintenance_backlog", "citizen_satisfaction", "budget_efficiency"],
|
|
"decision_types": [DecisionType.MAINTENANCE, DecisionType.SAFETY_IMPROVEMENT, DecisionType.RESOURCE_ALLOCATION],
|
|
"budget_range": [100000, 10000000]
|
|
},
|
|
Stakeholder.PROPERTY_OWNER: {
|
|
"priorities": ["property_value", "tenant_satisfaction", "maintenance_cost", "market_position"],
|
|
"decision_types": [DecisionType.INVESTMENT, DecisionType.MAINTENANCE, DecisionType.ESTABLISHMENT],
|
|
"budget_range": [50000, 5000000]
|
|
},
|
|
Stakeholder.INFRASTRUCTURE_OWNER: {
|
|
"priorities": ["asset_condition", "failure_risk", "compliance", "lifecycle_cost"],
|
|
"decision_types": [DecisionType.MAINTENANCE, DecisionType.RISK_MITIGATION, DecisionType.INVESTMENT],
|
|
"budget_range": [500000, 50000000]
|
|
},
|
|
Stakeholder.INSURANCE: {
|
|
"priorities": ["risk_assessment", "claim_prevention", "portfolio_optimization"],
|
|
"decision_types": [DecisionType.RISK_MITIGATION, DecisionType.RESOURCE_ALLOCATION],
|
|
"budget_range": [0, 0] # Risk-based pricing
|
|
},
|
|
Stakeholder.RETAIL: {
|
|
"priorities": ["foot_traffic", "demographics", "competition", "accessibility"],
|
|
"decision_types": [DecisionType.ESTABLISHMENT, DecisionType.INVESTMENT],
|
|
"budget_range": [100000, 2000000]
|
|
}
|
|
}
|
|
|
|
def recommend(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
location_signals: Dict[str, float],
|
|
constraints: Optional[Dict] = None,
|
|
visual_evidence: Optional[Dict] = None # NEW
|
|
) -> List[DecisionRecommendation]:
|
|
"""
|
|
Generate decision recommendations for a stakeholder
|
|
|
|
Args:
|
|
stakeholder: Who is making the decision
|
|
location_signals: Reality signals for the location
|
|
constraints: Budget, timeline, etc.
|
|
|
|
Returns:
|
|
Prioritized recommendations
|
|
"""
|
|
model = self.decision_models.get(stakeholder, {})
|
|
recommendations = []
|
|
|
|
for decision_type in model.get("decision_types", []):
|
|
rec = self._generate_recommendation(
|
|
stakeholder, decision_type, location_signals, constraints, visual_evidence
|
|
)
|
|
if rec:
|
|
recommendations.append(rec)
|
|
|
|
# Sort by expected impact
|
|
recommendations.sort(key=lambda x: x.expected_impact, reverse=True)
|
|
|
|
return recommendations
|
|
|
|
def _generate_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
decision_type: DecisionType,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> Optional[DecisionRecommendation]:
|
|
"""Generate a specific recommendation"""
|
|
|
|
if decision_type == DecisionType.MAINTENANCE:
|
|
return self._maintenance_recommendation(stakeholder, signals, constraints, visual_evidence)
|
|
elif decision_type == DecisionType.SAFETY_IMPROVEMENT:
|
|
return self._safety_recommendation(stakeholder, signals, constraints, visual_evidence)
|
|
elif decision_type == DecisionType.INVESTMENT:
|
|
return self._investment_recommendation(stakeholder, signals, constraints, visual_evidence)
|
|
elif decision_type == DecisionType.ESTABLISHMENT:
|
|
return self._establishment_recommendation(stakeholder, signals, constraints, visual_evidence)
|
|
elif decision_type == DecisionType.RISK_MITIGATION:
|
|
return self._risk_recommendation(stakeholder, signals, constraints, visual_evidence)
|
|
|
|
return None
|
|
|
|
def _maintenance_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> DecisionRecommendation:
|
|
"""Generate maintenance recommendation"""
|
|
|
|
condition = signals.get("maintenance_quality", 50)
|
|
backlog = signals.get("maintenance_backlog", 0)
|
|
|
|
if condition < 40:
|
|
urgency = "critical"
|
|
expected_impact = 80
|
|
elif condition < 60:
|
|
urgency = "high"
|
|
expected_impact = 60
|
|
else:
|
|
urgency = "medium"
|
|
expected_impact = 40
|
|
|
|
return DecisionRecommendation(
|
|
decision_type=DecisionType.MAINTENANCE,
|
|
stakeholder=stakeholder,
|
|
recommendation=f"Prioritize {urgency} maintenance. Current quality: {condition}/100.",
|
|
expected_impact=expected_impact,
|
|
confidence=0.75,
|
|
cost_estimate_usd=constraints.get("budget_usd", 100000) if constraints else 100000,
|
|
timeline_months=6,
|
|
supporting_data=[
|
|
{"signal": "maintenance_quality", "value": condition},
|
|
{"signal": "maintenance_backlog", "value": backlog}
|
|
],
|
|
risks=["Budget overrun", "Disruption during work"]
|
|
)
|
|
|
|
def _safety_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> DecisionRecommendation:
|
|
"""Generate safety improvement recommendation"""
|
|
|
|
safety = signals.get("safety_index", 50)
|
|
lighting = signals.get("lighting", 50)
|
|
|
|
interventions = []
|
|
if lighting < 50:
|
|
interventions.append("improve lighting")
|
|
if safety < 50:
|
|
interventions.append("increase surveillance")
|
|
|
|
return DecisionRecommendation(
|
|
decision_type=DecisionType.SAFETY_IMPROVEMENT,
|
|
stakeholder=stakeholder,
|
|
recommendation=f"Improve safety: {', '.join(interventions)}. Current safety: {safety}/100.",
|
|
expected_impact=(100 - safety) * 0.8,
|
|
confidence=0.7,
|
|
cost_estimate_usd=120000,
|
|
timeline_months=4,
|
|
supporting_data=[
|
|
{"signal": "safety_index", "value": safety},
|
|
{"signal": "lighting", "value": lighting}
|
|
],
|
|
risks=["Limited effectiveness if not combined with other measures"]
|
|
)
|
|
|
|
def _investment_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> DecisionRecommendation:
|
|
"""Generate investment recommendation"""
|
|
|
|
property_value = signals.get("property_value", 50)
|
|
growth_potential = signals.get("growth_potential", 50)
|
|
|
|
return DecisionRecommendation(
|
|
decision_type=DecisionType.INVESTMENT,
|
|
stakeholder=stakeholder,
|
|
recommendation=f"Investment opportunity. Current value: {property_value}/100. Growth potential: {growth_potential}/100.",
|
|
expected_impact=growth_potential * 0.9,
|
|
confidence=0.65,
|
|
cost_estimate_usd=constraints.get("budget_usd", 500000) if constraints else 500000,
|
|
timeline_months=12,
|
|
supporting_data=[
|
|
{"signal": "property_value", "value": property_value},
|
|
{"signal": "growth_potential", "value": growth_potential}
|
|
],
|
|
risks=["Market downturn", "Construction delays"]
|
|
)
|
|
|
|
def _establishment_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> DecisionRecommendation:
|
|
"""Generate establishment recommendation"""
|
|
|
|
foot_traffic = signals.get("pedestrian_flow", 50)
|
|
competition = signals.get("retail_density", 50)
|
|
demographics = signals.get("family_friendly", 50)
|
|
|
|
# Calculate opportunity score
|
|
opportunity = (foot_traffic * 0.4 + demographics * 0.3 + (100 - competition) * 0.3)
|
|
|
|
return DecisionRecommendation(
|
|
decision_type=DecisionType.ESTABLISHMENT,
|
|
stakeholder=stakeholder,
|
|
recommendation=f"Establishment opportunity score: {opportunity:.0f}/100. Foot traffic: {foot_traffic}, Competition: {competition}.",
|
|
expected_impact=opportunity * 0.85,
|
|
confidence=0.6,
|
|
cost_estimate_usd=300000,
|
|
timeline_months=8,
|
|
supporting_data=[
|
|
{"signal": "pedestrian_flow", "value": foot_traffic},
|
|
{"signal": "retail_density", "value": competition},
|
|
{"signal": "family_friendly", "value": demographics}
|
|
],
|
|
risks=["Competition increase", "Changing demographics"]
|
|
)
|
|
|
|
def _risk_recommendation(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
signals: Dict[str, float],
|
|
constraints: Optional[Dict],
|
|
visual_evidence: Optional[Dict] = None
|
|
) -> DecisionRecommendation:
|
|
"""Generate risk mitigation recommendation"""
|
|
|
|
risk_score = signals.get("infrastructure_reliability", 50)
|
|
failure_probability = 100 - risk_score
|
|
|
|
return DecisionRecommendation(
|
|
decision_type=DecisionType.RISK_MITIGATION,
|
|
stakeholder=stakeholder,
|
|
recommendation=f"Risk mitigation needed. Failure probability: {failure_probability:.0f}%. Current reliability: {risk_score}/100.",
|
|
expected_impact=failure_probability * 0.9,
|
|
confidence=0.8,
|
|
cost_estimate_usd=200000,
|
|
timeline_months=6,
|
|
supporting_data=[
|
|
{"signal": "infrastructure_reliability", "value": risk_score},
|
|
{"signal": "failure_probability", "value": failure_probability}
|
|
],
|
|
risks=["Unexpected failures", "Cost escalation"]
|
|
)
|
|
|
|
def prioritize_investments(
|
|
self,
|
|
stakeholder: Stakeholder,
|
|
locations: List[Dict],
|
|
budget_usd: float
|
|
) -> Dict:
|
|
"""
|
|
Prioritize investments across multiple locations
|
|
|
|
Returns:
|
|
Prioritized list with ROI estimates
|
|
"""
|
|
opportunities = []
|
|
|
|
for location in locations:
|
|
signals = location.get("signals", {})
|
|
|
|
# Calculate investment attractiveness
|
|
attractiveness = (
|
|
signals.get("property_value", 50) * 0.3 +
|
|
signals.get("growth_potential", 50) * 0.3 +
|
|
signals.get("safety_index", 50) * 0.2 +
|
|
signals.get("connectivity", 50) * 0.2
|
|
)
|
|
|
|
# Estimate ROI
|
|
estimated_roi = attractiveness * 0.5 # Simplified
|
|
|
|
opportunities.append({
|
|
"location_id": location.get("id"),
|
|
"location_name": location.get("name"),
|
|
"attractiveness": round(attractiveness, 1),
|
|
"estimated_roi": round(estimated_roi, 1),
|
|
"recommended_investment": min(budget_usd * 0.2, 1000000),
|
|
"priority": "high" if attractiveness > 75 else "medium" if attractiveness > 50 else "low"
|
|
})
|
|
|
|
# Sort by attractiveness
|
|
opportunities.sort(key=lambda x: x["attractiveness"], reverse=True)
|
|
|
|
# Allocate budget
|
|
allocated = 0
|
|
for opp in opportunities:
|
|
if allocated + opp["recommended_investment"] <= budget_usd:
|
|
opp["allocated"] = opp["recommended_investment"]
|
|
allocated += opp["allocated"]
|
|
else:
|
|
remaining = budget_usd - allocated
|
|
if remaining > 0:
|
|
opp["allocated"] = remaining
|
|
allocated += remaining
|
|
else:
|
|
opp["allocated"] = 0
|
|
|
|
return {
|
|
"stakeholder": stakeholder.value,
|
|
"total_budget": budget_usd,
|
|
"allocated": allocated,
|
|
"opportunities": opportunities
|
|
}
|
|
|
|
|
|
# Example usage
|
|
def example_decisions():
|
|
"""Example: Generate decision recommendations"""
|
|
engine = DecisionEngine()
|
|
|
|
# Signals for a location
|
|
signals = {
|
|
"safety_index": 45,
|
|
"lighting": 35,
|
|
"maintenance_quality": 40,
|
|
"property_value": 60,
|
|
"growth_potential": 70,
|
|
"pedestrian_flow": 80,
|
|
"retail_density": 65,
|
|
"family_friendly": 55,
|
|
"infrastructure_reliability": 50
|
|
}
|
|
|
|
# Municipality recommendations
|
|
print("=== Municipality Decisions ===")
|
|
recs = engine.recommend(Stakeholder.MUNICIPALITY, signals, {"budget_usd": 500000})
|
|
for rec in recs:
|
|
print(f"\n{rec.decision_type.value.upper()}")
|
|
print(f" Recommendation: {rec.recommendation}")
|
|
print(f" Expected impact: {rec.expected_impact}")
|
|
print(f" Cost: ${rec.cost_estimate_usd:,}")
|
|
print(f" Timeline: {rec.timeline_months} months")
|
|
|
|
# Property owner recommendations
|
|
print("\n=== Property Owner Decisions ===")
|
|
recs = engine.recommend(Stakeholder.PROPERTY_OWNER, signals, {"budget_usd": 1000000})
|
|
for rec in recs:
|
|
print(f"\n{rec.decision_type.value.upper()}")
|
|
print(f" Recommendation: {rec.recommendation}")
|
|
print(f" Expected impact: {rec.expected_impact}")
|
|
|
|
# Prioritize investments
|
|
print("\n=== Investment Prioritization ===")
|
|
locations = [
|
|
{"id": "LOC-001", "name": "Downtown", "signals": {"property_value": 80, "growth_potential": 75, "safety_index": 70}},
|
|
{"id": "LOC-002", "name": "Suburb A", "signals": {"property_value": 60, "growth_potential": 85, "safety_index": 80}},
|
|
{"id": "LOC-003", "name": "Industrial", "signals": {"property_value": 40, "growth_potential": 50, "safety_index": 45}}
|
|
]
|
|
|
|
result = engine.prioritize_investments(Stakeholder.PROPERTY_OWNER, locations, 2000000)
|
|
|
|
print(f"Budget: ${result['total_budget']:,}")
|
|
print(f"Allocated: ${result['allocated']:,}")
|
|
print("\nPriorities:")
|
|
for opp in result["opportunities"]:
|
|
print(f" {opp['location_name']}: {opp['priority']} (ROI: {opp['estimated_roi']}, Allocated: ${opp.get('allocated', 0):,})")
|
|
|
|
return engine
|
|
|
|
|
|
if __name__ == '__main__':
|
|
example_decisions()
|