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
385 lines
13 KiB
Python
385 lines
13 KiB
Python
"""
|
|
IOM Sync Engine — Synchronizes IOM data across apps, systems, and sites
|
|
Handles: quiXzoom app, Landvex API, web dashboards, external systems
|
|
"""
|
|
|
|
from typing import Dict, List, Optional, Any
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
import json
|
|
|
|
|
|
@dataclass
|
|
class SyncTarget:
|
|
"""A synchronization target"""
|
|
name: str
|
|
endpoint: str
|
|
auth_type: str # api_key, oauth, webhook
|
|
auth_config: Dict
|
|
enabled: bool = True
|
|
last_sync: Optional[str] = None
|
|
status: str = "pending"
|
|
|
|
|
|
class IOMSyncEngine:
|
|
"""Synchronizes IOM data across all connected systems"""
|
|
|
|
def __init__(self):
|
|
self.targets = {}
|
|
self.sync_log = []
|
|
|
|
# Default targets
|
|
self._register_default_targets()
|
|
|
|
def _register_default_targets(self):
|
|
"""Register default sync targets"""
|
|
self.register_target(SyncTarget(
|
|
name="quixzoom_app",
|
|
endpoint="https://api.quixzoom.com/v1/iom/sync",
|
|
auth_type="api_key",
|
|
auth_config={"header": "X-API-Key"}
|
|
))
|
|
|
|
self.register_target(SyncTarget(
|
|
name="landvex_api",
|
|
endpoint="https://api.landvex.com/v2/infrastructure",
|
|
auth_type="oauth",
|
|
auth_config={"scope": "infrastructure:write"}
|
|
))
|
|
|
|
self.register_target(SyncTarget(
|
|
name="web_dashboard",
|
|
endpoint="https://dashboard.landvex.com/api/iom",
|
|
auth_type="api_key",
|
|
auth_config={"header": "Authorization"}
|
|
))
|
|
|
|
self.register_target(SyncTarget(
|
|
name="external_gis",
|
|
endpoint="https://gis.partner.com/import",
|
|
auth_type="webhook",
|
|
auth_config={"secret": "shared_secret"}
|
|
))
|
|
|
|
def register_target(self, target: SyncTarget):
|
|
"""Register a sync target"""
|
|
self.targets[target.name] = target
|
|
|
|
def sync_observation(self, observation: Dict) -> Dict:
|
|
"""
|
|
Sync a single observation to all targets
|
|
|
|
Returns:
|
|
Sync results per target
|
|
"""
|
|
results = {}
|
|
|
|
for name, target in self.targets.items():
|
|
if not target.enabled:
|
|
continue
|
|
|
|
try:
|
|
result = self._push_to_target(target, observation)
|
|
results[name] = {
|
|
"status": "success",
|
|
"target": name,
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
target.last_sync = datetime.utcnow().isoformat()
|
|
target.status = "synced"
|
|
|
|
except Exception as e:
|
|
results[name] = {
|
|
"status": "error",
|
|
"target": name,
|
|
"error": str(e),
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
target.status = "error"
|
|
|
|
# Log sync
|
|
self.sync_log.append({
|
|
"type": "observation",
|
|
"goid": observation.get("goid"),
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"results": results
|
|
})
|
|
|
|
return results
|
|
|
|
def sync_rgi_analysis(self, rgi_result: Dict) -> Dict:
|
|
"""Sync RGI analysis to all targets"""
|
|
results = {}
|
|
|
|
for name, target in self.targets.items():
|
|
if not target.enabled:
|
|
continue
|
|
|
|
try:
|
|
# Format for target
|
|
formatted = self._format_for_target(rgi_result, target.name)
|
|
result = self._push_to_target(target, formatted)
|
|
|
|
results[name] = {
|
|
"status": "success",
|
|
"target": name,
|
|
"rgi_score": rgi_result.get("overall_rgi"),
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
except Exception as e:
|
|
results[name] = {
|
|
"status": "error",
|
|
"target": name,
|
|
"error": str(e)
|
|
}
|
|
|
|
return results
|
|
|
|
def sync_rcs_analysis(self, rcs_result: Dict) -> Dict:
|
|
"""Sync RCS analysis to all targets"""
|
|
results = {}
|
|
|
|
for name, target in self.targets.items():
|
|
if not target.enabled:
|
|
continue
|
|
|
|
try:
|
|
formatted = self._format_rcs_for_target(rcs_result, target.name)
|
|
result = self._push_to_target(target, formatted)
|
|
|
|
results[name] = {
|
|
"status": "success",
|
|
"target": name,
|
|
"rcs_score": rcs_result.get("rcs_score"),
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
except Exception as e:
|
|
results[name] = {
|
|
"status": "error",
|
|
"target": name,
|
|
"error": str(e)
|
|
}
|
|
|
|
return results
|
|
|
|
def _format_for_target(self, data: Dict, target_name: str) -> Dict:
|
|
"""Format data for specific target"""
|
|
if target_name == "quixzoom_app":
|
|
# Format for mobile app
|
|
return {
|
|
"type": "rgi_analysis",
|
|
"location": data.get("location"),
|
|
"score": data.get("overall_rgi"),
|
|
"level": data.get("contradiction_level"),
|
|
"layers": data.get("reality_layers", {}),
|
|
"mobile_optimized": True
|
|
}
|
|
|
|
elif target_name == "landvex_api":
|
|
# Format for Landvex API
|
|
return {
|
|
"analysis_type": "reality_gap",
|
|
"location_id": data.get("location"),
|
|
"metrics": {
|
|
"rgi": data.get("overall_rgi"),
|
|
"subscores": data.get("subscores", {})
|
|
},
|
|
"layers": data.get("reality_layers", {}),
|
|
"contradictions": data.get("contradictions", [])
|
|
}
|
|
|
|
elif target_name == "web_dashboard":
|
|
# Format for web dashboard
|
|
return {
|
|
"dashboard_data": True,
|
|
"location": data.get("location"),
|
|
"rgi": data.get("overall_rgi"),
|
|
"subscores": data.get("subscores", {}),
|
|
"layers": data.get("reality_layers", {}),
|
|
"contradictions": data.get("contradictions", []),
|
|
"charts": self._generate_chart_data(data)
|
|
}
|
|
|
|
else:
|
|
return data
|
|
|
|
def _format_rcs_for_target(self, data: Dict, target_name: str) -> Dict:
|
|
"""Format RCS data for specific target"""
|
|
if target_name == "quixzoom_app":
|
|
return {
|
|
"type": "contradiction_alert",
|
|
"location": data.get("location"),
|
|
"rcs_score": data.get("rcs_score"),
|
|
"strongest_contradiction": data.get("strongest_contradictions", [{}])[0],
|
|
"alert_level": "high" if data.get("rcs_score", 0) > 50 else "medium" if data.get("rcs_score", 0) > 20 else "low"
|
|
}
|
|
|
|
elif target_name == "landvex_api":
|
|
return {
|
|
"analysis_type": "contradiction",
|
|
"location_id": data.get("location"),
|
|
"rcs": data.get("rcs_score"),
|
|
"contradictions": data.get("all_contradictions", []),
|
|
"types": data.get("contradiction_types", {})
|
|
}
|
|
|
|
else:
|
|
return data
|
|
|
|
def _generate_chart_data(self, data: Dict) -> Dict:
|
|
"""Generate chart data for dashboard"""
|
|
subscores = data.get("subscores", {})
|
|
|
|
return {
|
|
"radar_chart": {
|
|
"labels": ["Physical", "Operational", "Safety", "Institutional", "Economic", "Maintenance", "Human Exp"],
|
|
"values": [
|
|
subscores.get("physical_reality_gap", {}).get("score", 0),
|
|
subscores.get("operational_reality_gap", {}).get("score", 0),
|
|
subscores.get("safety_reality_gap", {}).get("score", 0),
|
|
subscores.get("institutional_reality_gap", {}).get("score", 0),
|
|
subscores.get("economic_reality_gap", {}).get("score", 0),
|
|
subscores.get("maintenance_reality_gap", {}).get("score", 0),
|
|
subscores.get("human_experience_gap", {}).get("score", 0)
|
|
]
|
|
},
|
|
"layer_chart": {
|
|
"labels": list(data.get("reality_layers", {}).keys()),
|
|
"values": list(data.get("reality_layers", {}).values())
|
|
}
|
|
}
|
|
|
|
def _push_to_target(self, target: SyncTarget, data: Dict) -> bool:
|
|
"""Push data to a target (simulated)"""
|
|
# In production, this would make actual HTTP requests
|
|
# For now, simulate success
|
|
return True
|
|
|
|
def get_sync_status(self) -> Dict:
|
|
"""Get overall sync status"""
|
|
return {
|
|
"targets": len(self.targets),
|
|
"enabled": sum(1 for t in self.targets.values() if t.enabled),
|
|
"last_syncs": {
|
|
name: target.last_sync
|
|
for name, target in self.targets.items()
|
|
},
|
|
"statuses": {
|
|
name: target.status
|
|
for name, target in self.targets.items()
|
|
},
|
|
"recent_logs": self.sync_log[-10:]
|
|
}
|
|
|
|
def get_target_config(self, target_name: str) -> Optional[Dict]:
|
|
"""Get configuration for a target"""
|
|
target = self.targets.get(target_name)
|
|
if target:
|
|
return {
|
|
"name": target.name,
|
|
"endpoint": target.endpoint,
|
|
"auth_type": target.auth_type,
|
|
"enabled": target.enabled,
|
|
"last_sync": target.last_sync,
|
|
"status": target.status
|
|
}
|
|
return None
|
|
|
|
|
|
class IOMWebhookHandler:
|
|
"""Handles incoming webhooks from external systems"""
|
|
|
|
def __init__(self, sync_engine: IOMSyncEngine):
|
|
self.sync_engine = sync_engine
|
|
|
|
def handle_observation_webhook(self, payload: Dict) -> Dict:
|
|
"""Handle observation from external system"""
|
|
# Validate payload
|
|
if "goid" not in payload:
|
|
return {"status": "error", "message": "Missing GOID"}
|
|
|
|
# Process observation
|
|
observation = {
|
|
"goid": payload["goid"],
|
|
"timestamp": payload.get("timestamp", datetime.utcnow().isoformat()),
|
|
"findings": payload.get("findings", []),
|
|
"location": payload.get("location"),
|
|
"media": payload.get("media"),
|
|
"observer_id": payload.get("observer_id")
|
|
}
|
|
|
|
# Sync to all targets
|
|
results = self.sync_engine.sync_observation(observation)
|
|
|
|
return {
|
|
"status": "success",
|
|
"observation_id": payload["goid"],
|
|
"sync_results": results
|
|
}
|
|
|
|
def handle_bulk_sync(self, observations: List[Dict]) -> Dict:
|
|
"""Handle bulk observation sync"""
|
|
results = []
|
|
|
|
for obs in observations:
|
|
result = self.handle_observation_webhook(obs)
|
|
results.append(result)
|
|
|
|
return {
|
|
"status": "success",
|
|
"processed": len(results),
|
|
"successful": sum(1 for r in results if r["status"] == "success"),
|
|
"failed": sum(1 for r in results if r["status"] == "error")
|
|
}
|
|
|
|
|
|
# Example usage
|
|
def example_sync():
|
|
"""Example: Sync RGI analysis to all targets"""
|
|
engine = IOMSyncEngine()
|
|
|
|
# Example RGI result
|
|
rgi_result = {
|
|
"location": "Bangkok Silom",
|
|
"overall_rgi": 38.77,
|
|
"contradiction_level": "medium",
|
|
"subscores": {
|
|
"physical_reality_gap": {"score": 63.33},
|
|
"operational_reality_gap": {"score": 16.67},
|
|
"safety_reality_gap": {"score": 0.0},
|
|
"institutional_reality_gap": {"score": 70.0},
|
|
"economic_reality_gap": {"score": 40.0},
|
|
"maintenance_reality_gap": {"score": 25.0},
|
|
"human_experience_gap": {"score": 20.0}
|
|
},
|
|
"reality_layers": {
|
|
"planned": 100.0,
|
|
"constructed": 100.0,
|
|
"maintained": 33.3,
|
|
"observed": 100.0,
|
|
"human_experience": 100.0,
|
|
"informal_adaptation": 60.0,
|
|
"hidden_systems": 0.0
|
|
}
|
|
}
|
|
|
|
# Sync to all targets
|
|
results = engine.sync_rgi_analysis(rgi_result)
|
|
|
|
print("=== RGI Sync Results ===")
|
|
for target, result in results.items():
|
|
print(f"{target}: {result['status']}")
|
|
|
|
# Get sync status
|
|
status = engine.get_sync_status()
|
|
print(f"\nTotal targets: {status['targets']}")
|
|
print(f"Enabled: {status['enabled']}")
|
|
|
|
return results
|
|
|
|
|
|
if __name__ == '__main__':
|
|
example_sync()
|