Spaces:
Runtime error
Runtime error
| from starlette.applications import Starlette | |
| from starlette.routing import Route | |
| from starlette.requests import Request | |
| from starlette.responses import JSONResponse | |
| from pydantic import BaseModel, Field | |
| from typing import List, Optional | |
| from datetime import datetime | |
| import uvicorn | |
| import numpy as np | |
| import json | |
| app = Starlette(debug=True) | |
| # In-memory store for historical scores (replace with database for production) | |
| historical_scores = [] | |
| # Define the request body structure | |
| class VendorLog(BaseModel): | |
| vendor_id: str = Field(..., description="Unique identifier for the vendor") | |
| delay_logs: int = Field(..., ge=0, description="Number of delay incidents") | |
| qa_incidents: int = Field(..., ge=0, description="Number of quality assurance incidents") | |
| safety_compliance: int = Field(..., ge=0, description="Number of safety compliance issues") | |
| feedback_logs: int = Field(..., ge=0, description="Number of feedback logs") | |
| work_logs: int = Field(..., ge=1, description="Total number of work logs") | |
| month: str = Field(..., description="Month of the score (YYYY-MM format)") | |
| # Define the response structure | |
| class ScoreResponse(BaseModel): | |
| vendor_id: str | |
| month: str | |
| final_score: float | |
| quality_score: float | |
| timeliness_score: float | |
| safety_score: float | |
| communication_score: float | |
| alert_flag: bool | |
| trend_deviation: Optional[float] | |
| certification_url: Optional[str] | |
| # Scoring weights (configurable) | |
| WEIGHTS = { | |
| "quality": 0.3, | |
| "timeliness": 0.3, | |
| "safety": 0.2, | |
| "communication": 0.2 | |
| } | |
| async def calculate_score(request: Request): | |
| try: | |
| # Parse JSON body | |
| body = await request.json() | |
| logs = VendorLog(**body) | |
| # Validate month format | |
| datetime.strptime(logs.month, "%Y-%m") | |
| # Calculate individual scores | |
| quality_score = (1 - logs.qa_incidents / logs.work_logs) * 100 | |
| timeliness_score = (1 - logs.delay_logs / logs.work_logs) * 100 | |
| safety_score = (1 - logs.safety_compliance / logs.work_logs) * 100 | |
| communication_score = (logs.feedback_logs / logs.work_logs) * 50 # Adjusted logic | |
| # Ensure scores are within 0-100 | |
| quality_score = max(0, min(100, quality_score)) | |
| timeliness_score = max(0, min(100, timeliness_score)) | |
| safety_score = max(0, min(100, safety_score)) | |
| communication_score = max(0, min(100, communication_score)) | |
| # Calculate weighted final score | |
| final_score = ( | |
| WEIGHTS["quality"] * quality_score + | |
| WEIGHTS["timeliness"] * timeliness_score + | |
| WEIGHTS["safety"] * safety_score + | |
| WEIGHTS["communication"] * communication_score | |
| ) | |
| # Alert flag logic | |
| alert_flag = final_score < 50 | |
| # Trend detection (compare with historical scores for this vendor) | |
| vendor_history = [score for score in historical_scores if score["vendor_id"] == logs.vendor_id] | |
| trend_deviation = None | |
| if len(vendor_history) >= 2: | |
| previous_scores = [score["final_score"] for score in vendor_history[-2:]] | |
| trend_deviation = final_score - np.mean(previous_scores) | |
| # Store the current score | |
| historical_scores.append({ | |
| "vendor_id": logs.vendor_id, | |
| "month": logs.month, | |
| "final_score": final_score, | |
| "quality_score": quality_score, | |
| "timeliness_score": timeliness_score, | |
| "safety_score": safety_score, | |
| "communication_score": communication_score | |
| }) | |
| # Mock certification URL (replace with actual logic) | |
| certification_url = f"https://example.com/cert/{logs.vendor_id}" | |
| # Prepare response | |
| response = ScoreResponse( | |
| vendor_id=logs.vendor_id, | |
| month=logs.month, | |
| final_score=round(final_score, 2), | |
| quality_score=round(quality_score, 2), | |
| timeliness_score=round(timeliness_score, 2), | |
| safety_score=round(safety_score, 2), | |
| communication_score=round(communication_score, 2), | |
| alert_flag=alert_flag, | |
| trend_deviation=round(trend_deviation, 2) if trend_deviation is not None else None, | |
| certification_url=certification_url | |
| ) | |
| return JSONResponse(content=response.dict()) | |
| except ValueError as e: | |
| return JSONResponse( | |
| content={"detail": f"Invalid input: {str(e)}"}, | |
| status_code=400 | |
| ) | |
| except json.JSONDecodeError: | |
| return JSONResponse( | |
| content={"detail": "Invalid JSON format"}, | |
| status_code=400 | |
| ) | |
| except Exception as e: | |
| return JSONResponse( | |
| content={"detail": f"Internal server error: {str(e)}"}, | |
| status_code=500 | |
| ) | |
| async def health_check(request: Request): | |
| return JSONResponse(content={"status": "healthy"}) | |
| # Define routes | |
| app.routes.append(Route("/score", endpoint=calculate_score, methods=["POST"])) | |
| app.routes.append(Route("/health", endpoint=health_check, methods=["GET"])) | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=8001) |