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
261 lines
7.5 KiB
Python
261 lines
7.5 KiB
Python
"""
|
|
Mobile SDK for quiXzoom app
|
|
iOS/Android integration with IOM platform
|
|
"""
|
|
|
|
from typing import Dict, List, Optional
|
|
from dataclasses import dataclass
|
|
import json
|
|
|
|
|
|
@dataclass
|
|
class MobileConfig:
|
|
"""Configuration for mobile SDK"""
|
|
api_url: str
|
|
api_key: str
|
|
timeout: int = 30
|
|
max_image_size: int = 10 * 1024 * 1024 # 10MB
|
|
cache_enabled: bool = True
|
|
offline_mode: bool = False
|
|
|
|
|
|
class IOMMobileSDK:
|
|
"""
|
|
Mobile SDK for quiXzoom app
|
|
|
|
Features:
|
|
- Offline observation capture
|
|
- Image compression
|
|
- GPS tagging
|
|
- Batch sync
|
|
- Push notifications
|
|
- Visual geolocation (NEW)
|
|
- Evidence package extraction (NEW)
|
|
"""
|
|
|
|
def __init__(self, config: MobileConfig):
|
|
self.config = config
|
|
self.offline_queue: List[Dict] = []
|
|
self.cache: Dict = {}
|
|
self.visual_geo_enabled = True
|
|
|
|
def capture_observation(
|
|
self,
|
|
image_data: bytes,
|
|
latitude: float,
|
|
longitude: float,
|
|
notes: Optional[str] = None
|
|
) -> Dict:
|
|
"""
|
|
Capture observation from mobile device
|
|
|
|
Args:
|
|
image_data: JPEG/PNG image bytes
|
|
latitude: GPS latitude
|
|
longitude: GPS longitude
|
|
notes: Optional notes
|
|
|
|
Returns:
|
|
Observation data
|
|
"""
|
|
# Compress image
|
|
compressed = self._compress_image(image_data)
|
|
|
|
# Extract visual geolocation evidence (NEW)
|
|
evidence_package = None
|
|
if self.visual_geo_enabled:
|
|
evidence_package = self._extract_evidence(compressed)
|
|
|
|
# Create observation with evidence
|
|
observation = {
|
|
"id": f"OBS-MOB-{self._generate_id()}",
|
|
"timestamp": self._get_timestamp(),
|
|
"latitude": latitude,
|
|
"longitude": longitude,
|
|
"image_size": len(compressed),
|
|
"notes": notes,
|
|
"status": "pending",
|
|
"evidence_package": evidence_package, # NEW
|
|
"visual_geolocation": {
|
|
"enabled": self.visual_geo_enabled,
|
|
"evidence_layers": len(evidence_package) if evidence_package else 0
|
|
}
|
|
}
|
|
|
|
# Queue for sync
|
|
if self.config.offline_mode:
|
|
self.offline_queue.append(observation)
|
|
return {"status": "queued", "observation": observation}
|
|
|
|
# Sync immediately
|
|
return self._sync_observation(observation, compressed)
|
|
|
|
def sync_offline_queue(self) -> Dict:
|
|
"""Sync queued observations"""
|
|
results = []
|
|
|
|
for observation in self.offline_queue:
|
|
result = self._sync_observation(observation, None)
|
|
results.append(result)
|
|
|
|
self.offline_queue.clear()
|
|
|
|
return {
|
|
"synced": len(results),
|
|
"results": results
|
|
}
|
|
|
|
def get_dashboard(self) -> Dict:
|
|
"""Get mobile-optimized dashboard"""
|
|
return {
|
|
"stats": {
|
|
"missions_completed": 12,
|
|
"missions_available": 34,
|
|
"earnings_usd": 245.50,
|
|
"rating": 4.8
|
|
},
|
|
"nearby_missions": [
|
|
{
|
|
"id": "MIS-001",
|
|
"title": "Street lighting inspection",
|
|
"distance_m": 450,
|
|
"reward_usd": 15.00,
|
|
"deadline": "2026-06-27T18:00:00Z"
|
|
},
|
|
{
|
|
"id": "MIS-002",
|
|
"title": "Sidewalk condition survey",
|
|
"distance_m": 890,
|
|
"reward_usd": 20.00,
|
|
"deadline": "2026-06-28T12:00:00Z"
|
|
}
|
|
],
|
|
"notifications": [
|
|
{
|
|
"type": "mission_available",
|
|
"message": "New mission near you!",
|
|
"timestamp": "2026-06-26T11:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
|
|
def submit_mission(
|
|
self,
|
|
mission_id: str,
|
|
observations: List[Dict],
|
|
metadata: Optional[Dict] = None
|
|
) -> Dict:
|
|
"""Submit completed mission"""
|
|
submission = {
|
|
"mission_id": mission_id,
|
|
"submitted_at": self._get_timestamp(),
|
|
"observations": observations,
|
|
"metadata": metadata or {},
|
|
"status": "submitted"
|
|
}
|
|
|
|
# In production, send to API
|
|
return {
|
|
"status": "submitted",
|
|
"submission_id": f"SUB-{self._generate_id()}",
|
|
"estimated_review_time": "24h"
|
|
}
|
|
|
|
def get_mission_details(self, mission_id: str) -> Dict:
|
|
"""Get mission details"""
|
|
return {
|
|
"id": mission_id,
|
|
"title": "Street lighting inspection",
|
|
"description": "Inspect and photograph street lighting in your area",
|
|
"requirements": [
|
|
"iPhone 12+ or Android 12MP+",
|
|
"Daylight photos",
|
|
"GPS enabled"
|
|
],
|
|
"reward_usd": 15.00,
|
|
"deadline": "2026-06-27T18:00:00Z",
|
|
"location": {
|
|
"latitude": 59.3293,
|
|
"longitude": 18.0686,
|
|
"radius_m": 500
|
|
}
|
|
}
|
|
|
|
def _extract_evidence(self, image_data: bytes) -> Dict:
|
|
"""Extract evidence package from image (NEW)"""
|
|
# In production, call visual geolocation API
|
|
# For now, return simulated evidence
|
|
return {
|
|
"visual_objects": [],
|
|
"semantic_objects": [],
|
|
"text_detections": [],
|
|
"geometric_features": [],
|
|
"environmental_signals": [],
|
|
"temporal_signals": {}
|
|
}
|
|
|
|
def _compress_image(self, image_data: bytes) -> bytes:
|
|
"""Compress image for mobile upload"""
|
|
# In production, use PIL to compress
|
|
# For now, return as-is
|
|
return image_data
|
|
|
|
def _sync_observation(self, observation: Dict, image_data: Optional[bytes]) -> Dict:
|
|
"""Sync observation to server"""
|
|
# In production, send to API
|
|
return {
|
|
"status": "synced",
|
|
"observation_id": observation["id"]
|
|
}
|
|
|
|
def _generate_id(self) -> str:
|
|
"""Generate unique ID"""
|
|
import uuid
|
|
return uuid.uuid4().hex[:8]
|
|
|
|
def _get_timestamp(self) -> str:
|
|
"""Get current timestamp"""
|
|
from datetime import datetime
|
|
return datetime.utcnow().isoformat()
|
|
|
|
|
|
# Example usage
|
|
def example_mobile_sdk():
|
|
"""Example: Mobile SDK usage"""
|
|
print("=== Mobile SDK Demo ===\n")
|
|
|
|
config = MobileConfig(
|
|
api_url="https://api.iom.landvex.com",
|
|
api_key="mobile_api_key",
|
|
offline_mode=True
|
|
)
|
|
|
|
sdk = IOMMobileSDK(config)
|
|
|
|
# Capture observation
|
|
print("Capturing observation...")
|
|
result = sdk.capture_observation(
|
|
image_data=b"fake_image_data",
|
|
latitude=59.3293,
|
|
longitude=18.0686,
|
|
notes="Broken street light"
|
|
)
|
|
print(f"Status: {result['status']}")
|
|
|
|
# Get dashboard
|
|
print("\nGetting dashboard...")
|
|
dashboard = sdk.get_dashboard()
|
|
print(f"Missions available: {dashboard['stats']['missions_available']}")
|
|
print(f"Earnings: ${dashboard['stats']['earnings_usd']}")
|
|
|
|
# Sync offline queue
|
|
print("\nSyncing offline queue...")
|
|
sync_result = sdk.sync_offline_queue()
|
|
print(f"Synced: {sync_result['synced']} observations")
|
|
|
|
return sdk
|
|
|
|
|
|
if __name__ == '__main__':
|
|
example_mobile_sdk()
|