aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
539 lines
18 KiB
Python
539 lines
18 KiB
Python
"""
|
|
Landvex Aid Intelligence (LAI) Router.
|
|
Dedikerad modul för att mäta biståndseffekt med oberoende verifiering.
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional, Dict, Any
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
from app.database import get_db
|
|
from app.core.security import get_current_user
|
|
from app.models import User
|
|
|
|
router = APIRouter(prefix="/aid", tags=["aid_intelligence"])
|
|
|
|
|
|
class ProjectType(str, Enum):
|
|
SCHOOL = "school"
|
|
HOSPITAL = "hospital"
|
|
ROAD = "road"
|
|
BRIDGE = "bridge"
|
|
WATER = "water"
|
|
POWER = "power"
|
|
AGRICULTURE = "agriculture"
|
|
RENEWABLE = "renewable"
|
|
HOUSING = "housing"
|
|
SANITATION = "sanitation"
|
|
DIGITAL = "digital"
|
|
ENVIRONMENTAL = "environmental"
|
|
EMERGENCY = "emergency"
|
|
FOOD = "food"
|
|
EDUCATION = "education"
|
|
HEALTHCARE = "healthcare"
|
|
MICROFINANCE = "microfinance"
|
|
WOMEN_EMPOWERMENT = "women_empowerment"
|
|
YOUTH_EMPLOYMENT = "youth_employment"
|
|
|
|
|
|
class ProjectStatus(str, Enum):
|
|
PLANNED = "planned"
|
|
ACTIVE = "active"
|
|
COMPLETED = "completed"
|
|
DELAYED = "delayed"
|
|
ABANDONED = "abandoned"
|
|
UNDER_MAINTENANCE = "under_maintenance"
|
|
|
|
|
|
class ObservationType(str, Enum):
|
|
INFRASTRUCTURE = "infrastructure"
|
|
ACTIVITY = "activity"
|
|
MAINTENANCE = "maintenance"
|
|
USAGE = "usage"
|
|
ENVIRONMENTAL = "environmental"
|
|
SAFETY = "safety"
|
|
ACCESSIBILITY = "accessibility"
|
|
|
|
|
|
# ─── PUBLIC ENDPOINTS ─────────────────────────────────────────────────────────
|
|
|
|
@router.get("/public/status")
|
|
async def get_aid_status():
|
|
"""Publik status för LAI."""
|
|
return {
|
|
"service": "Landvex Aid Intelligence",
|
|
"status": "operational",
|
|
"version": "1.0.0",
|
|
"regions": ["Sub-Saharan Africa", "East Africa", "West Africa"],
|
|
"projects_monitored": 1250,
|
|
"observations_collected": 45000,
|
|
"contributors_active": 3200,
|
|
"last_update": datetime.now().isoformat()
|
|
}
|
|
|
|
|
|
@router.get("/public/projects")
|
|
async def list_public_projects(
|
|
country: Optional[str] = None,
|
|
project_type: Optional[ProjectType] = None,
|
|
status: Optional[ProjectStatus] = None,
|
|
):
|
|
"""Lista projekt med filtrering (publik)."""
|
|
projects = [
|
|
{
|
|
"id": "proj-001",
|
|
"name": "Mogadishu Primary School Renovation",
|
|
"country": "Somalia",
|
|
"region": "East Africa",
|
|
"type": "school",
|
|
"status": "active",
|
|
"funding_agency": "UNICEF",
|
|
"budget_usd": 250000,
|
|
"start_date": "2025-03-01",
|
|
"expected_completion": "2026-06-30",
|
|
"aii_score": 72,
|
|
"confidence": 85,
|
|
"observations_count": 156,
|
|
"last_observation": "2026-07-03",
|
|
"location": {"lat": 2.0469, "lng": 45.3182}
|
|
},
|
|
{
|
|
"id": "proj-002",
|
|
"name": "Kampala Water Supply Extension",
|
|
"country": "Uganda",
|
|
"region": "East Africa",
|
|
"type": "water",
|
|
"status": "active",
|
|
"funding_agency": "World Bank",
|
|
"budget_usd": 1200000,
|
|
"start_date": "2024-09-01",
|
|
"expected_completion": "2026-12-31",
|
|
"aii_score": 68,
|
|
"confidence": 78,
|
|
"observations_count": 203,
|
|
"last_observation": "2026-07-02",
|
|
"location": {"lat": 0.3476, "lng": 32.5825}
|
|
},
|
|
{
|
|
"id": "proj-003",
|
|
"name": "Accra Solar Microgrid",
|
|
"country": "Ghana",
|
|
"region": "West Africa",
|
|
"type": "renewable",
|
|
"status": "completed",
|
|
"funding_agency": "GIZ",
|
|
"budget_usd": 450000,
|
|
"start_date": "2024-01-15",
|
|
"expected_completion": "2025-12-31",
|
|
"aii_score": 89,
|
|
"confidence": 92,
|
|
"observations_count": 312,
|
|
"last_observation": "2026-07-01",
|
|
"location": {"lat": 5.6037, "lng": -0.1870}
|
|
}
|
|
]
|
|
|
|
if country:
|
|
projects = [p for p in projects if p["country"].lower() == country.lower()]
|
|
if project_type:
|
|
projects = [p for p in projects if p["type"] == project_type.value]
|
|
if status:
|
|
projects = [p for p in projects if p["status"] == status.value]
|
|
|
|
return {
|
|
"projects": projects,
|
|
"total": len(projects),
|
|
"filters": {
|
|
"country": country,
|
|
"type": project_type.value if project_type else None,
|
|
"status": status.value if status else None
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/public/projects/{project_id}")
|
|
async def get_public_project(project_id: str):
|
|
"""Hämta detaljer för ett specifikt projekt (publik)."""
|
|
return {
|
|
"id": project_id,
|
|
"name": "Mogadishu Primary School Renovation",
|
|
"description": "Renovation and expansion of primary school facilities including classrooms, sanitation, and water access.",
|
|
"country": "Somalia",
|
|
"region": "East Africa",
|
|
"district": "Banadir",
|
|
"type": "school",
|
|
"status": "active",
|
|
"funding_agency": "UNICEF",
|
|
"implementing_partner": "Somali Education Consortium",
|
|
"budget_usd": 250000,
|
|
"start_date": "2025-03-01",
|
|
"expected_completion": "2026-06-30",
|
|
"actual_completion": None,
|
|
"location": {"lat": 2.0469, "lng": 45.3182, "accuracy": "10m"},
|
|
"aii_score": 72,
|
|
"confidence": 85,
|
|
"dimensions": {
|
|
"accessibility": {"score": 80, "confidence": 90},
|
|
"operational_status": {"score": 75, "confidence": 85},
|
|
"maintenance_quality": {"score": 65, "confidence": 80},
|
|
"population_usage": {"score": 70, "confidence": 82},
|
|
"economic_activity": {"score": 60, "confidence": 75},
|
|
"safety": {"score": 78, "confidence": 88},
|
|
"environmental_sustainability": {"score": 72, "confidence": 85},
|
|
"community_engagement": {"score": 85, "confidence": 90}
|
|
},
|
|
"observations": {
|
|
"total": 156,
|
|
"last_30_days": 23,
|
|
"contributors": 12,
|
|
"ai_validated": 148,
|
|
"human_verified": 45
|
|
},
|
|
"timeline": [
|
|
{"date": "2025-03-01", "event": "Project start", "type": "milestone"},
|
|
{"date": "2025-06-15", "event": "Foundation completed", "type": "milestone"},
|
|
{"date": "2025-11-20", "event": "Structure completed", "type": "milestone"},
|
|
{"date": "2026-02-10", "event": "Roof installed", "type": "milestone"},
|
|
{"date": "2026-07-03", "event": "Interior work ongoing", "type": "observation"}
|
|
],
|
|
"risks": [
|
|
{
|
|
"type": "delay",
|
|
"severity": "medium",
|
|
"description": "Interior completion 2 weeks behind schedule",
|
|
"detected": "2026-06-15",
|
|
"confidence": 75
|
|
}
|
|
],
|
|
"transparency": {
|
|
"observations_url": f"/api/v1/aid/public/projects/{project_id}/observations",
|
|
"methodology": "AII-v1.0",
|
|
"data_sources": ["quiXzoom", "satellite", "open_data"]
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/public/projects/{project_id}/observations")
|
|
async def get_project_observations(project_id: str):
|
|
"""Hämta alla observationer för ett projekt (publik)."""
|
|
return {
|
|
"project_id": project_id,
|
|
"observations": [
|
|
{
|
|
"id": "obs-001",
|
|
"type": "infrastructure",
|
|
"date": "2026-07-03",
|
|
"contributor_id": "contr-123",
|
|
"location": {"lat": 2.0469, "lng": 45.3182},
|
|
"media": ["https://cdn.landvex.com/obs/001-a.jpg", "https://cdn.landvex.com/obs/001-b.jpg"],
|
|
"ai_analysis": {
|
|
"construction_progress": 85,
|
|
"material_quality": "good",
|
|
"safety_compliance": "acceptable"
|
|
},
|
|
"confidence": 88,
|
|
"verified": True
|
|
},
|
|
{
|
|
"id": "obs-002",
|
|
"type": "activity",
|
|
"date": "2026-07-02",
|
|
"contributor_id": "contr-124",
|
|
"location": {"lat": 2.0470, "lng": 45.3183},
|
|
"media": ["https://cdn.landvex.com/obs/002-a.jpg"],
|
|
"ai_analysis": {
|
|
"worker_count": 12,
|
|
"activity_level": "high",
|
|
"equipment_present": True
|
|
},
|
|
"confidence": 82,
|
|
"verified": True
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/public/aii")
|
|
async def get_aid_impact_index(
|
|
country: Optional[str] = None,
|
|
region: Optional[str] = None,
|
|
project_type: Optional[ProjectType] = None,
|
|
):
|
|
"""Hämta Aid Impact Index (AII) för regioner eller länder."""
|
|
return {
|
|
"index_name": "Aid Impact Index (AII)",
|
|
"version": "1.0",
|
|
"methodology_url": "https://landvex.com/aid/methodology",
|
|
"filters": {
|
|
"country": country,
|
|
"region": region,
|
|
"type": project_type.value if project_type else None
|
|
},
|
|
"results": [
|
|
{
|
|
"country": "Somalia",
|
|
"region": "East Africa",
|
|
"aii_score": 72,
|
|
"confidence": 85,
|
|
"projects_count": 45,
|
|
"observations_count": 2340,
|
|
"trend": "improving",
|
|
"dimensions": {
|
|
"accessibility": 75,
|
|
"operational_status": 70,
|
|
"maintenance_quality": 65,
|
|
"population_usage": 72,
|
|
"safety": 78
|
|
}
|
|
},
|
|
{
|
|
"country": "Uganda",
|
|
"region": "East Africa",
|
|
"aii_score": 68,
|
|
"confidence": 78,
|
|
"projects_count": 67,
|
|
"observations_count": 3120,
|
|
"trend": "stable",
|
|
"dimensions": {
|
|
"accessibility": 70,
|
|
"operational_status": 72,
|
|
"maintenance_quality": 60,
|
|
"population_usage": 68,
|
|
"safety": 70
|
|
}
|
|
},
|
|
{
|
|
"country": "Ghana",
|
|
"region": "West Africa",
|
|
"aii_score": 81,
|
|
"confidence": 88,
|
|
"projects_count": 89,
|
|
"observations_count": 4560,
|
|
"trend": "improving",
|
|
"dimensions": {
|
|
"accessibility": 85,
|
|
"operational_status": 82,
|
|
"maintenance_quality": 78,
|
|
"population_usage": 80,
|
|
"safety": 82
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/public/countries")
|
|
async def list_countries():
|
|
"""Lista alla länder med aktiv övervakning."""
|
|
return {
|
|
"countries": [
|
|
{"code": "SO", "name": "Somalia", "region": "East Africa", "projects": 45, "aii": 72},
|
|
{"code": "UG", "name": "Uganda", "region": "East Africa", "projects": 67, "aii": 68},
|
|
{"code": "KE", "name": "Kenya", "region": "East Africa", "projects": 92, "aii": 74},
|
|
{"code": "ET", "name": "Ethiopia", "region": "East Africa", "projects": 78, "aii": 65},
|
|
{"code": "TZ", "name": "Tanzania", "region": "East Africa", "projects": 56, "aii": 70},
|
|
{"code": "GH", "name": "Ghana", "region": "West Africa", "projects": 89, "aii": 81},
|
|
{"code": "NG", "name": "Nigeria", "region": "West Africa", "projects": 134, "aii": 63},
|
|
{"code": "SN", "name": "Senegal", "region": "West Africa", "projects": 34, "aii": 76},
|
|
{"code": "ZA", "name": "South Africa", "region": "Southern Africa", "projects": 112, "aii": 82}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/public/risk-alerts")
|
|
async def get_risk_alerts():
|
|
"""Hämta aktuella riskvarningar."""
|
|
return {
|
|
"alerts": [
|
|
{
|
|
"id": "alert-001",
|
|
"severity": "high",
|
|
"type": "abandoned_project",
|
|
"project_id": "proj-045",
|
|
"project_name": "Lagos Road Rehabilitation",
|
|
"country": "Nigeria",
|
|
"description": "No observable activity for 90 days. Equipment removed from site.",
|
|
"detected": "2026-06-15",
|
|
"confidence": 92,
|
|
"observations_supporting": 8
|
|
},
|
|
{
|
|
"id": "alert-002",
|
|
"severity": "medium",
|
|
"type": "deterioration",
|
|
"project_id": "proj-067",
|
|
"project_name": "Nairobi Health Clinic",
|
|
"country": "Kenya",
|
|
"description": "Infrastructure showing signs of accelerated deterioration.",
|
|
"detected": "2026-07-01",
|
|
"confidence": 78,
|
|
"observations_supporting": 5
|
|
},
|
|
{
|
|
"id": "alert-003",
|
|
"severity": "medium",
|
|
"type": "delay",
|
|
"project_id": "proj-001",
|
|
"project_name": "Mogadishu Primary School Renovation",
|
|
"country": "Somalia",
|
|
"description": "Interior completion 2 weeks behind schedule.",
|
|
"detected": "2026-06-20",
|
|
"confidence": 75,
|
|
"observations_supporting": 6
|
|
}
|
|
],
|
|
"total": 3,
|
|
"last_updated": datetime.now().isoformat()
|
|
}
|
|
|
|
|
|
# ─── AUTHENTICATED ENDPOINTS ──────────────────────────────────────────────────
|
|
|
|
@router.post("/projects")
|
|
async def create_project(
|
|
name: str,
|
|
country: str,
|
|
project_type: ProjectType,
|
|
funding_agency: str,
|
|
budget_usd: float,
|
|
location: Dict[str, float],
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Skapa ett nytt projekt för övervakning."""
|
|
return {
|
|
"status": "created",
|
|
"project_id": f"proj-{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
"name": name,
|
|
"country": country,
|
|
"type": project_type.value,
|
|
"funding_agency": funding_agency,
|
|
"budget_usd": budget_usd,
|
|
"location": location,
|
|
"created_at": datetime.now().isoformat(),
|
|
"created_by": current_user.id if hasattr(current_user, 'id') else 'admin'
|
|
}
|
|
|
|
|
|
@router.get("/projects/{project_id}/detailed")
|
|
async def get_project_detailed(
|
|
project_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Hämta detaljerad projektinformation (autentiserad)."""
|
|
return await get_public_project(project_id)
|
|
|
|
|
|
@router.post("/projects/{project_id}/observations")
|
|
async def add_observation(
|
|
project_id: str,
|
|
observation_type: ObservationType,
|
|
location: Dict[str, float],
|
|
media_urls: List[str],
|
|
notes: Optional[str] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Lägg till en observation till ett projekt."""
|
|
return {
|
|
"status": "added",
|
|
"observation_id": f"obs-{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
|
"project_id": project_id,
|
|
"type": observation_type.value,
|
|
"location": location,
|
|
"media": media_urls,
|
|
"notes": notes,
|
|
"submitted_at": datetime.now().isoformat(),
|
|
"submitted_by": current_user.id if hasattr(current_user, 'id') else 'admin'
|
|
}
|
|
|
|
|
|
@router.get("/dashboard")
|
|
async def get_aid_dashboard(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Hämta LAI dashboard för admin."""
|
|
return {
|
|
"organization": "Landvex Aid Intelligence",
|
|
"timestamp": datetime.now().isoformat(),
|
|
"summary": {
|
|
"projects_monitored": 1250,
|
|
"projects_active": 890,
|
|
"projects_completed": 340,
|
|
"projects_at_risk": 20,
|
|
"observations_total": 45000,
|
|
"observations_last_30_days": 3200,
|
|
"contributors_active": 3200,
|
|
"countries_covered": 9,
|
|
"regions_covered": 3
|
|
},
|
|
"kpis": {
|
|
"average_aii_score": 72,
|
|
"average_confidence": 82,
|
|
"verification_rate": 94,
|
|
"ai_validation_rate": 91,
|
|
"response_time_hours": 48
|
|
},
|
|
"funding_monitored": {
|
|
"total_usd": 450000000,
|
|
"by_region": {
|
|
"East Africa": 180000000,
|
|
"West Africa": 150000000,
|
|
"Southern Africa": 120000000
|
|
}
|
|
},
|
|
"recent_activity": [
|
|
{
|
|
"type": "observation",
|
|
"project": "Mogadishu Primary School",
|
|
"description": "New observation added",
|
|
"time": "2026-07-03T14:30:00Z"
|
|
},
|
|
{
|
|
"type": "alert",
|
|
"project": "Lagos Road Rehabilitation",
|
|
"description": "Risk alert: Abandoned project detected",
|
|
"time": "2026-07-02T09:15:00Z"
|
|
}
|
|
],
|
|
"alerts_summary": {
|
|
"high": 1,
|
|
"medium": 2,
|
|
"low": 5
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/reports/executive")
|
|
async def get_executive_report(
|
|
period: str = "quarterly",
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Hämta executive report för ledningen."""
|
|
return {
|
|
"report_type": "Executive Summary",
|
|
"period": period,
|
|
"generated_at": datetime.now().isoformat(),
|
|
"key_findings": [
|
|
"72% average Aid Impact Index across all monitored projects",
|
|
"94% of observations pass AI validation",
|
|
"20 projects flagged for potential risk",
|
|
"East Africa shows strongest improvement trend"
|
|
],
|
|
"recommendations": [
|
|
"Increase observation frequency in high-risk projects",
|
|
"Expand contributor network in West Africa",
|
|
"Implement automated alert escalation",
|
|
"Publish quarterly transparency report"
|
|
],
|
|
"financial_summary": {
|
|
"total_monitored": "$450M",
|
|
"high_performers": "$180M (40%)",
|
|
"at_risk": "$12M (2.7%)",
|
|
"verification_cost": "$450K (0.1%)"
|
|
}
|
|
} |