demo seed change 7
This commit is contained in:
@@ -388,6 +388,7 @@ async def clone_demo_data(
|
||||
quality_score=batch_data.get('quality_score'),
|
||||
waste_quantity=batch_data.get('waste_quantity'),
|
||||
defect_quantity=batch_data.get('defect_quantity'),
|
||||
waste_defect_type=batch_data.get('waste_defect_type'),
|
||||
equipment_used=batch_data.get('equipment_used'),
|
||||
staff_assigned=batch_data.get('staff_assigned'),
|
||||
station_id=batch_data.get('station_id'),
|
||||
@@ -395,6 +396,7 @@ async def clone_demo_data(
|
||||
forecast_id=batch_data.get('forecast_id'),
|
||||
is_rush_order=batch_data.get('is_rush_order', False),
|
||||
is_special_recipe=batch_data.get('is_special_recipe', False),
|
||||
is_ai_assisted=batch_data.get('is_ai_assisted', False),
|
||||
production_notes=batch_data.get('production_notes'),
|
||||
quality_notes=batch_data.get('quality_notes'),
|
||||
delay_reason=batch_data.get('delay_reason'),
|
||||
|
||||
293
services/production/app/api/sustainability.py
Normal file
293
services/production/app/api/sustainability.py
Normal file
@@ -0,0 +1,293 @@
|
||||
"""
|
||||
Production Service - Sustainability API
|
||||
Exposes production-specific sustainability metrics following microservices principles
|
||||
Each service owns its domain data
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from fastapi import APIRouter, Depends, Path, Query, Request
|
||||
import structlog
|
||||
|
||||
from shared.auth.decorators import get_current_user_dep
|
||||
from app.services.production_service import ProductionService
|
||||
from shared.routing import RouteBuilder
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
# Create route builder for consistent URL structure
|
||||
route_builder = RouteBuilder('production')
|
||||
|
||||
router = APIRouter(tags=["production-sustainability"])
|
||||
|
||||
|
||||
def get_production_service(request: Request) -> ProductionService:
|
||||
"""Dependency injection for production service"""
|
||||
from app.core.database import database_manager
|
||||
from app.core.config import settings
|
||||
notification_service = getattr(request.app.state, 'notification_service', None)
|
||||
return ProductionService(database_manager, settings, notification_service)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/tenants/{tenant_id}/production/sustainability/waste-metrics",
|
||||
response_model=dict,
|
||||
summary="Get production waste metrics",
|
||||
description="""
|
||||
Returns production-specific waste metrics for sustainability tracking.
|
||||
|
||||
This endpoint is part of the microservices architecture where each service
|
||||
owns its domain data. Frontend aggregates data from multiple services.
|
||||
|
||||
Metrics include:
|
||||
- Total production waste from batches (waste_quantity + defect_quantity)
|
||||
- Production volumes (planned vs actual)
|
||||
- Waste breakdown by defect type
|
||||
- AI-assisted batch tracking
|
||||
"""
|
||||
)
|
||||
async def get_production_waste_metrics(
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
start_date: Optional[datetime] = Query(None, description="Start date for metrics (default: 30 days ago)"),
|
||||
end_date: Optional[datetime] = Query(None, description="End date for metrics (default: now)"),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
production_service: ProductionService = Depends(get_production_service)
|
||||
):
|
||||
"""
|
||||
Get production waste metrics for sustainability dashboard
|
||||
|
||||
Returns production-specific metrics that frontend will aggregate with
|
||||
inventory metrics for complete sustainability picture.
|
||||
"""
|
||||
try:
|
||||
# Set default dates
|
||||
if not end_date:
|
||||
end_date = datetime.now()
|
||||
if not start_date:
|
||||
start_date = end_date - timedelta(days=30)
|
||||
|
||||
# Get waste analytics from production service
|
||||
waste_data = await production_service.get_waste_analytics(
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
|
||||
# Enrich with metadata
|
||||
response = {
|
||||
**waste_data,
|
||||
"service": "production",
|
||||
"period": {
|
||||
"start_date": start_date.isoformat(),
|
||||
"end_date": end_date.isoformat(),
|
||||
"days": (end_date - start_date).days
|
||||
},
|
||||
"metadata": {
|
||||
"data_source": "production_batches",
|
||||
"calculation_method": "SUM(waste_quantity + defect_quantity)",
|
||||
"filters_applied": {
|
||||
"status": ["COMPLETED", "QUALITY_CHECK"],
|
||||
"date_range": f"{start_date.date()} to {end_date.date()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"Production waste metrics retrieved",
|
||||
tenant_id=str(tenant_id),
|
||||
total_waste_kg=waste_data.get('total_production_waste', 0),
|
||||
period_days=(end_date - start_date).days,
|
||||
user_id=current_user.get('user_id')
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Error getting production waste metrics",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e)
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/tenants/{tenant_id}/production/sustainability/baseline",
|
||||
response_model=dict,
|
||||
summary="Get production baseline metrics",
|
||||
description="""
|
||||
Returns baseline production metrics from the first 90 days of operation.
|
||||
|
||||
Used by frontend to calculate SDG 12.3 compliance (waste reduction targets).
|
||||
If tenant has less than 90 days of data, returns industry average baseline.
|
||||
"""
|
||||
)
|
||||
async def get_production_baseline(
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
production_service: ProductionService = Depends(get_production_service)
|
||||
):
|
||||
"""
|
||||
Get baseline production metrics for SDG compliance calculations
|
||||
|
||||
Frontend uses this to calculate:
|
||||
- Waste reduction percentage vs baseline
|
||||
- Progress toward SDG 12.3 targets
|
||||
- Grant eligibility based on improvement
|
||||
"""
|
||||
try:
|
||||
baseline_data = await production_service.get_baseline_metrics(tenant_id)
|
||||
|
||||
# Add metadata
|
||||
response = {
|
||||
**baseline_data,
|
||||
"service": "production",
|
||||
"metadata": {
|
||||
"baseline_period_days": 90,
|
||||
"calculation_method": "First 90 days of production data",
|
||||
"fallback": "Industry average (25%) if insufficient data"
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"Production baseline metrics retrieved",
|
||||
tenant_id=str(tenant_id),
|
||||
has_baseline=baseline_data.get('has_baseline', False),
|
||||
baseline_waste_pct=baseline_data.get('waste_percentage'),
|
||||
user_id=current_user.get('user_id')
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Error getting production baseline",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e)
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/tenants/{tenant_id}/production/sustainability/ai-impact",
|
||||
response_model=dict,
|
||||
summary="Get AI waste reduction impact",
|
||||
description="""
|
||||
Analyzes the impact of AI-assisted production on waste reduction.
|
||||
|
||||
Compares waste rates between:
|
||||
- AI-assisted batches (with is_ai_assisted=true)
|
||||
- Manual batches (is_ai_assisted=false)
|
||||
|
||||
Shows ROI of AI features for sustainability.
|
||||
"""
|
||||
)
|
||||
async def get_ai_waste_impact(
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
start_date: Optional[datetime] = Query(None, description="Start date (default: 30 days ago)"),
|
||||
end_date: Optional[datetime] = Query(None, description="End date (default: now)"),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
production_service: ProductionService = Depends(get_production_service)
|
||||
):
|
||||
"""
|
||||
Get AI impact on waste reduction
|
||||
|
||||
Frontend uses this to showcase:
|
||||
- Value proposition of AI features
|
||||
- Waste avoided through AI assistance
|
||||
- Financial ROI of AI investment
|
||||
"""
|
||||
try:
|
||||
# Set default dates
|
||||
if not end_date:
|
||||
end_date = datetime.now()
|
||||
if not start_date:
|
||||
start_date = end_date - timedelta(days=30)
|
||||
|
||||
# Get AI impact analytics (we'll implement this)
|
||||
ai_impact = await production_service.get_ai_waste_impact(
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"AI waste impact retrieved",
|
||||
tenant_id=str(tenant_id),
|
||||
ai_waste_reduction_pct=ai_impact.get('waste_reduction_percentage'),
|
||||
user_id=current_user.get('user_id')
|
||||
)
|
||||
|
||||
return ai_impact
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Error getting AI waste impact",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e)
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/tenants/{tenant_id}/production/sustainability/summary",
|
||||
response_model=dict,
|
||||
summary="Get production sustainability summary",
|
||||
description="""
|
||||
Quick summary endpoint combining all production sustainability metrics.
|
||||
|
||||
Useful for dashboard widgets that need overview data without multiple calls.
|
||||
"""
|
||||
)
|
||||
async def get_production_sustainability_summary(
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
days: int = Query(30, ge=7, le=365, description="Number of days to analyze"),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
production_service: ProductionService = Depends(get_production_service)
|
||||
):
|
||||
"""
|
||||
Get comprehensive production sustainability summary
|
||||
|
||||
Combines waste metrics, baseline, and AI impact in one response.
|
||||
Optimized for dashboard widgets.
|
||||
"""
|
||||
try:
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=days)
|
||||
|
||||
# Get all metrics in parallel (within service)
|
||||
waste_data = await production_service.get_waste_analytics(tenant_id, start_date, end_date)
|
||||
baseline_data = await production_service.get_baseline_metrics(tenant_id)
|
||||
|
||||
# Try to get AI impact (may not be available for all tenants)
|
||||
try:
|
||||
ai_impact = await production_service.get_ai_waste_impact(tenant_id, start_date, end_date)
|
||||
except:
|
||||
ai_impact = {"available": False}
|
||||
|
||||
summary = {
|
||||
"service": "production",
|
||||
"period_days": days,
|
||||
"waste_metrics": waste_data,
|
||||
"baseline": baseline_data,
|
||||
"ai_impact": ai_impact,
|
||||
"last_updated": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"Production sustainability summary retrieved",
|
||||
tenant_id=str(tenant_id),
|
||||
period_days=days,
|
||||
user_id=current_user.get('user_id')
|
||||
)
|
||||
|
||||
return summary
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Error getting production sustainability summary",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e)
|
||||
)
|
||||
raise
|
||||
Reference in New Issue
Block a user