2025-10-06 15:27:01 +02:00
|
|
|
# services/suppliers/app/api/analytics.py
|
2025-08-21 20:28:14 +02:00
|
|
|
"""
|
2025-10-06 15:27:01 +02:00
|
|
|
Supplier Analytics API endpoints (ANALYTICS)
|
|
|
|
|
Consolidates performance metrics, delivery stats, and all analytics operations
|
2025-08-21 20:28:14 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from datetime import datetime, timedelta
|
2025-10-06 15:27:01 +02:00
|
|
|
from typing import List, Optional, Dict, Any
|
2025-08-21 20:28:14 +02:00
|
|
|
from uuid import UUID
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
2025-10-06 15:27:01 +02:00
|
|
|
from sqlalchemy.orm import Session
|
2025-08-21 20:28:14 +02:00
|
|
|
import structlog
|
|
|
|
|
|
2025-09-04 23:19:53 +02:00
|
|
|
from shared.auth.decorators import get_current_user_dep
|
2025-10-06 15:27:01 +02:00
|
|
|
from shared.auth.access_control import require_user_role, analytics_tier_required
|
|
|
|
|
from shared.routing import RouteBuilder
|
2025-08-21 20:28:14 +02:00
|
|
|
from app.core.database import get_db
|
|
|
|
|
from app.services.performance_service import PerformanceTrackingService, AlertService
|
|
|
|
|
from app.services.dashboard_service import DashboardService
|
2025-10-06 15:27:01 +02:00
|
|
|
from app.services.delivery_service import DeliveryService
|
2025-08-21 20:28:14 +02:00
|
|
|
from app.schemas.performance import (
|
2025-10-06 15:27:01 +02:00
|
|
|
PerformanceMetric, Alert, PerformanceDashboardSummary,
|
|
|
|
|
SupplierPerformanceInsights, PerformanceAnalytics, BusinessModelInsights,
|
|
|
|
|
AlertSummary, PerformanceReportRequest, ExportDataResponse
|
2025-08-21 20:28:14 +02:00
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
from app.schemas.suppliers import DeliveryPerformanceStats, DeliverySummaryStats
|
2025-08-21 20:28:14 +02:00
|
|
|
from app.models.performance import PerformancePeriod, PerformanceMetricType, AlertType, AlertSeverity
|
|
|
|
|
|
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# Create route builder for consistent URL structure
|
|
|
|
|
route_builder = RouteBuilder('suppliers')
|
|
|
|
|
|
|
|
|
|
router = APIRouter(tags=["analytics"])
|
2025-08-21 20:28:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ===== Dependency Injection =====
|
|
|
|
|
|
|
|
|
|
async def get_performance_service() -> PerformanceTrackingService:
|
|
|
|
|
"""Get performance tracking service"""
|
|
|
|
|
return PerformanceTrackingService()
|
|
|
|
|
|
|
|
|
|
async def get_alert_service() -> AlertService:
|
|
|
|
|
"""Get alert service"""
|
|
|
|
|
return AlertService()
|
|
|
|
|
|
|
|
|
|
async def get_dashboard_service() -> DashboardService:
|
|
|
|
|
"""Get dashboard service"""
|
|
|
|
|
return DashboardService()
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# ===== Delivery Analytics =====
|
2025-08-21 20:28:14 +02:00
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("deliveries/performance-stats"),
|
|
|
|
|
response_model=DeliveryPerformanceStats
|
|
|
|
|
)
|
|
|
|
|
async def get_delivery_performance_stats(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
days_back: int = Query(30, ge=1, le=365, description="Number of days to analyze"),
|
|
|
|
|
supplier_id: Optional[UUID] = Query(None, description="Filter by supplier ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: Session = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get delivery performance statistics"""
|
|
|
|
|
try:
|
|
|
|
|
service = DeliveryService(db)
|
|
|
|
|
stats = await service.get_delivery_performance_stats(
|
|
|
|
|
tenant_id=current_user.tenant_id,
|
|
|
|
|
days_back=days_back,
|
|
|
|
|
supplier_id=supplier_id
|
|
|
|
|
)
|
|
|
|
|
return DeliveryPerformanceStats(**stats)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting delivery performance stats", error=str(e))
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve delivery performance statistics")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("deliveries/summary-stats"),
|
|
|
|
|
response_model=DeliverySummaryStats
|
|
|
|
|
)
|
|
|
|
|
async def get_delivery_summary_stats(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
db: Session = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get delivery summary statistics for dashboard"""
|
|
|
|
|
try:
|
|
|
|
|
service = DeliveryService(db)
|
|
|
|
|
stats = await service.get_upcoming_deliveries_summary(current_user.tenant_id)
|
|
|
|
|
return DeliverySummaryStats(**stats)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting delivery summary stats", error=str(e))
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve delivery summary statistics")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ===== Performance Metrics =====
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
route_builder.build_analytics_route("performance/{supplier_id}/calculate"),
|
|
|
|
|
response_model=PerformanceMetric
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def calculate_supplier_performance(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
supplier_id: UUID = Path(...),
|
|
|
|
|
period: PerformancePeriod = Query(...),
|
|
|
|
|
period_start: datetime = Query(...),
|
|
|
|
|
period_end: datetime = Query(...),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
performance_service: PerformanceTrackingService = Depends(get_performance_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Calculate performance metrics for a supplier"""
|
|
|
|
|
try:
|
|
|
|
|
metric = await performance_service.calculate_supplier_performance(
|
|
|
|
|
db, supplier_id, tenant_id, period, period_start, period_end
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
if not metric:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail="Unable to calculate performance metrics"
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Performance metrics calculated",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
supplier_id=str(supplier_id),
|
|
|
|
|
period=period.value)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return metric
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error calculating performance metrics",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
supplier_id=str(supplier_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to calculate performance metrics"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/{supplier_id}/metrics"),
|
|
|
|
|
response_model=List[PerformanceMetric]
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_supplier_performance_metrics(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
supplier_id: UUID = Path(...),
|
|
|
|
|
metric_type: Optional[PerformanceMetricType] = Query(None),
|
|
|
|
|
period: Optional[PerformancePeriod] = Query(None),
|
|
|
|
|
date_from: Optional[datetime] = Query(None),
|
|
|
|
|
date_to: Optional[datetime] = Query(None),
|
|
|
|
|
limit: int = Query(50, ge=1, le=500),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get performance metrics for a supplier"""
|
|
|
|
|
try:
|
|
|
|
|
# TODO: Implement get_supplier_performance_metrics in service
|
|
|
|
|
# For now, return empty list
|
|
|
|
|
metrics = []
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return metrics
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting performance metrics",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
supplier_id=str(supplier_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve performance metrics"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# ===== Alert Management =====
|
2025-08-21 20:28:14 +02:00
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.post(
|
|
|
|
|
route_builder.build_analytics_route("performance/alerts/evaluate"),
|
|
|
|
|
response_model=List[Alert]
|
|
|
|
|
)
|
|
|
|
|
@require_user_role(['admin', 'owner'])
|
2025-08-21 20:28:14 +02:00
|
|
|
async def evaluate_performance_alerts(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
supplier_id: Optional[UUID] = Query(None, description="Specific supplier to evaluate"),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
alert_service: AlertService = Depends(get_alert_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Evaluate and create performance-based alerts"""
|
|
|
|
|
try:
|
|
|
|
|
alerts = await alert_service.evaluate_performance_alerts(db, tenant_id, supplier_id)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Performance alerts evaluated",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
alerts_created=len(alerts))
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return alerts
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error evaluating performance alerts",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to evaluate performance alerts"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/alerts"),
|
|
|
|
|
response_model=List[Alert]
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_supplier_alerts(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
supplier_id: Optional[UUID] = Query(None),
|
|
|
|
|
alert_type: Optional[AlertType] = Query(None),
|
|
|
|
|
severity: Optional[AlertSeverity] = Query(None),
|
|
|
|
|
date_from: Optional[datetime] = Query(None),
|
|
|
|
|
date_to: Optional[datetime] = Query(None),
|
|
|
|
|
limit: int = Query(50, ge=1, le=500),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get supplier alerts with filtering"""
|
|
|
|
|
try:
|
|
|
|
|
# TODO: Implement get_supplier_alerts in service
|
|
|
|
|
# For now, return empty list
|
|
|
|
|
alerts = []
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return alerts
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting supplier alerts",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve supplier alerts"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/alerts/summary"),
|
|
|
|
|
response_model=List[AlertSummary]
|
|
|
|
|
)
|
|
|
|
|
async def get_alert_summary(
|
2025-08-21 20:28:14 +02:00
|
|
|
tenant_id: UUID = Path(...),
|
2025-10-06 15:27:01 +02:00
|
|
|
date_from: Optional[datetime] = Query(None),
|
|
|
|
|
date_to: Optional[datetime] = Query(None),
|
2025-08-21 20:28:14 +02:00
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
2025-10-06 15:27:01 +02:00
|
|
|
dashboard_service: DashboardService = Depends(get_dashboard_service),
|
2025-08-21 20:28:14 +02:00
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
2025-10-06 15:27:01 +02:00
|
|
|
"""Get alert summary by type and severity"""
|
2025-08-21 20:28:14 +02:00
|
|
|
try:
|
2025-10-06 15:27:01 +02:00
|
|
|
summary = await dashboard_service.get_alert_summary(db, tenant_id, date_from, date_to)
|
|
|
|
|
|
|
|
|
|
return summary
|
|
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
2025-10-06 15:27:01 +02:00
|
|
|
logger.error("Error getting alert summary",
|
2025-08-21 20:28:14 +02:00
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-10-06 15:27:01 +02:00
|
|
|
detail="Failed to retrieve alert summary"
|
2025-08-21 20:28:14 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# ===== Dashboard Analytics =====
|
2025-08-21 20:28:14 +02:00
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_dashboard_route("performance/summary"),
|
|
|
|
|
response_model=PerformanceDashboardSummary
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_performance_dashboard_summary(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
date_from: Optional[datetime] = Query(None),
|
|
|
|
|
date_to: Optional[datetime] = Query(None),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
dashboard_service: DashboardService = Depends(get_dashboard_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get comprehensive performance dashboard summary"""
|
|
|
|
|
try:
|
|
|
|
|
summary = await dashboard_service.get_performance_dashboard_summary(
|
|
|
|
|
db, tenant_id, date_from, date_to
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Performance dashboard summary retrieved",
|
|
|
|
|
tenant_id=str(tenant_id))
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return summary
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting dashboard summary",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve dashboard summary"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/{supplier_id}/insights"),
|
|
|
|
|
response_model=SupplierPerformanceInsights
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_supplier_performance_insights(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
supplier_id: UUID = Path(...),
|
|
|
|
|
days_back: int = Query(30, ge=1, le=365),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
dashboard_service: DashboardService = Depends(get_dashboard_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get detailed performance insights for a specific supplier"""
|
|
|
|
|
try:
|
|
|
|
|
insights = await dashboard_service.get_supplier_performance_insights(
|
|
|
|
|
db, tenant_id, supplier_id, days_back
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Supplier performance insights retrieved",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
supplier_id=str(supplier_id))
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return insights
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting supplier insights",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
supplier_id=str(supplier_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve supplier insights"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/performance"),
|
|
|
|
|
response_model=PerformanceAnalytics
|
|
|
|
|
)
|
|
|
|
|
@analytics_tier_required
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_performance_analytics(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
period_days: int = Query(90, ge=1, le=365),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
dashboard_service: DashboardService = Depends(get_dashboard_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get advanced performance analytics"""
|
|
|
|
|
try:
|
|
|
|
|
analytics = await dashboard_service.get_performance_analytics(
|
|
|
|
|
db, tenant_id, period_days
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Performance analytics retrieved",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
period_days=period_days)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return analytics
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting performance analytics",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve performance analytics"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/business-model"),
|
|
|
|
|
response_model=BusinessModelInsights
|
|
|
|
|
)
|
|
|
|
|
@analytics_tier_required
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_business_model_insights(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
dashboard_service: DashboardService = Depends(get_dashboard_service),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Get business model detection and insights"""
|
|
|
|
|
try:
|
|
|
|
|
insights = await dashboard_service.get_business_model_insights(db, tenant_id)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
logger.info("Business model insights retrieved",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
detected_model=insights.detected_model)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return insights
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting business model insights",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve business model insights"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# ===== Export and Reporting =====
|
2025-08-21 20:28:14 +02:00
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.post(
|
|
|
|
|
route_builder.build_analytics_route("performance/reports/generate"),
|
|
|
|
|
response_model=ExportDataResponse
|
|
|
|
|
)
|
|
|
|
|
@require_user_role(['admin', 'owner'])
|
2025-08-21 20:28:14 +02:00
|
|
|
async def generate_performance_report(
|
|
|
|
|
report_request: PerformanceReportRequest,
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Generate a performance report"""
|
|
|
|
|
try:
|
|
|
|
|
# TODO: Implement report generation
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
|
|
|
detail="Report generation not yet implemented"
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error generating performance report",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to generate performance report"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/export")
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def export_performance_data(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
format: str = Query("json", description="Export format: json, csv, excel"),
|
|
|
|
|
date_from: Optional[datetime] = Query(None),
|
|
|
|
|
date_to: Optional[datetime] = Query(None),
|
|
|
|
|
supplier_ids: Optional[List[UUID]] = Query(None),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
|
|
|
|
db: AsyncSession = Depends(get_db)
|
|
|
|
|
):
|
|
|
|
|
"""Export performance data"""
|
|
|
|
|
try:
|
|
|
|
|
if format.lower() not in ["json", "csv", "excel"]:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail="Unsupported export format. Use: json, csv, excel"
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
# TODO: Implement data export
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
|
|
|
detail="Data export not yet implemented"
|
|
|
|
|
)
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error exporting performance data",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to export performance data"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
# ===== Configuration and Health =====
|
2025-08-21 20:28:14 +02:00
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/config")
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_performance_config(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep)
|
|
|
|
|
):
|
|
|
|
|
"""Get performance tracking configuration"""
|
|
|
|
|
try:
|
|
|
|
|
from app.core.config import settings
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
config = {
|
|
|
|
|
"performance_tracking": {
|
|
|
|
|
"enabled": settings.PERFORMANCE_TRACKING_ENABLED,
|
|
|
|
|
"calculation_interval_minutes": settings.PERFORMANCE_CALCULATION_INTERVAL_MINUTES,
|
|
|
|
|
"cache_ttl_seconds": settings.PERFORMANCE_CACHE_TTL
|
|
|
|
|
},
|
|
|
|
|
"thresholds": {
|
|
|
|
|
"excellent_delivery_rate": settings.EXCELLENT_DELIVERY_RATE,
|
|
|
|
|
"good_delivery_rate": settings.GOOD_DELIVERY_RATE,
|
|
|
|
|
"acceptable_delivery_rate": settings.ACCEPTABLE_DELIVERY_RATE,
|
|
|
|
|
"poor_delivery_rate": settings.POOR_DELIVERY_RATE,
|
|
|
|
|
"excellent_quality_rate": settings.EXCELLENT_QUALITY_RATE,
|
|
|
|
|
"good_quality_rate": settings.GOOD_QUALITY_RATE,
|
|
|
|
|
"acceptable_quality_rate": settings.ACCEPTABLE_QUALITY_RATE,
|
|
|
|
|
"poor_quality_rate": settings.POOR_QUALITY_RATE
|
|
|
|
|
},
|
|
|
|
|
"alerts": {
|
|
|
|
|
"enabled": settings.ALERTS_ENABLED,
|
|
|
|
|
"evaluation_interval_minutes": settings.ALERT_EVALUATION_INTERVAL_MINUTES,
|
|
|
|
|
"retention_days": settings.ALERT_RETENTION_DAYS,
|
|
|
|
|
"critical_delivery_delay_hours": settings.CRITICAL_DELIVERY_DELAY_HOURS,
|
|
|
|
|
"critical_quality_rejection_rate": settings.CRITICAL_QUALITY_REJECTION_RATE
|
|
|
|
|
},
|
|
|
|
|
"dashboard": {
|
|
|
|
|
"cache_ttl_seconds": settings.DASHBOARD_CACHE_TTL,
|
|
|
|
|
"refresh_interval_seconds": settings.DASHBOARD_REFRESH_INTERVAL,
|
|
|
|
|
"default_analytics_period_days": settings.DEFAULT_ANALYTICS_PERIOD_DAYS
|
|
|
|
|
},
|
|
|
|
|
"business_model": {
|
|
|
|
|
"detection_enabled": settings.ENABLE_BUSINESS_MODEL_DETECTION,
|
|
|
|
|
"central_bakery_threshold": settings.CENTRAL_BAKERY_THRESHOLD_SUPPLIERS,
|
|
|
|
|
"individual_bakery_threshold": settings.INDIVIDUAL_BAKERY_THRESHOLD_SUPPLIERS
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
return config
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting performance config",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to retrieve performance configuration"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-10-06 15:27:01 +02:00
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_analytics_route("performance/health")
|
|
|
|
|
)
|
2025-08-21 20:28:14 +02:00
|
|
|
async def get_performance_health(
|
|
|
|
|
tenant_id: UUID = Path(...),
|
|
|
|
|
current_user: dict = Depends(get_current_user_dep)
|
|
|
|
|
):
|
|
|
|
|
"""Get performance service health status"""
|
|
|
|
|
try:
|
|
|
|
|
return {
|
|
|
|
|
"service": "suppliers-performance",
|
|
|
|
|
"status": "healthy",
|
|
|
|
|
"timestamp": datetime.now().isoformat(),
|
|
|
|
|
"tenant_id": str(tenant_id),
|
|
|
|
|
"features": {
|
|
|
|
|
"performance_tracking": "enabled",
|
|
|
|
|
"alerts": "enabled",
|
|
|
|
|
"dashboard_analytics": "enabled",
|
|
|
|
|
"business_model_detection": "enabled"
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 15:27:01 +02:00
|
|
|
|
2025-08-21 20:28:14 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Error getting performance health",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get performance health status"
|
2025-10-06 15:27:01 +02:00
|
|
|
)
|