market-intelligence / src /dpi /__init__.py
jtlevine's picture
Kenya-first framing in CLAUDE.md; DPI docstrings s/product/module/; drop orphaned requirements-dashboard.txt
23236be
"""
Digital Public Infrastructure (DPI) integration for Market Intelligence.
Exposes the three India DPI services that actually matter for the
sell-timing + credit readiness module:
- Aadhaar eKYC -> identity
- Land Records -> crop/acreage verification
- Kisan Credit Card -> real credit limits for the readiness assessment
Philosophically minimal: this is NOT the full six-service DPI aggregation
that Weather AI 2 does. MI's product doesn't need soil health cards,
crop insurance, or PM-KISAN income support — those exist in WA2 because
weather advisories benefit from that context, and MI's assess_credit_readiness()
benefits from knowing a farmer's actual KCC headroom, not from knowing their
soil micronutrients.
Usage:
from src.dpi import get_agent
agent = get_agent()
profile = agent.get_farmer_profile("FMR-LKSH")
if profile:
print(profile.aadhaar.name, profile.kcc.credit_limit)
The lookup is entirely in-memory against a deterministic simulated
registry (seed=hash(farmer_id)) — no DB calls, no API calls, no async.
The entire module is safe to import from any environment.
"""
from __future__ import annotations
from typing import Optional
from src.dpi.models import (
AadhaarProfile,
FarmerProfile,
KCCRecord,
LandRecord,
)
from src.dpi.simulator import SimulatedDPIRegistry, get_registry
class DPIAgent:
"""Composite agent that resolves farmer_id → FarmerProfile.
Single-purpose: mirrors Weather AI 2's DPIAgent interface but with
the three-service scope appropriate for Market Intelligence. All
lookups hit the in-memory simulated registry; no network, no cache
layer, no async.
"""
def __init__(self, registry: Optional[SimulatedDPIRegistry] = None):
self._registry = registry or get_registry()
def get_farmer_profile(self, farmer_id: str) -> Optional[FarmerProfile]:
"""Return a composite profile for the given farmer_id, or None if unknown."""
return self._registry.lookup_by_farmer_id(farmer_id)
def get_profile_by_phone(self, phone: str) -> Optional[FarmerProfile]:
"""Alternate lookup keyed by Aadhaar-registered phone."""
return self._registry.lookup_by_phone(phone)
def profile_summary(self, profile: FarmerProfile) -> str:
"""One-line human-readable summary of a profile — useful for logs and debugging."""
parts = [
f"{profile.aadhaar.name} ({profile.aadhaar.district})",
f"{profile.total_area:.2f} ha",
]
if profile.kcc:
util = profile.kcc.utilization_pct
parts.append(f"KCC Rs {profile.kcc.credit_limit:,.0f} ({util:.0f}% used)")
return " | ".join(parts)
_AGENT: Optional[DPIAgent] = None
def get_agent() -> DPIAgent:
"""Lazy-init module-level DPIAgent singleton."""
global _AGENT
if _AGENT is None:
_AGENT = DPIAgent()
return _AGENT
__all__ = [
"AadhaarProfile",
"LandRecord",
"KCCRecord",
"FarmerProfile",
"DPIAgent",
"SimulatedDPIRegistry",
"get_agent",
"get_registry",
]