File size: 5,153 Bytes
e08e8ba
 
 
 
09c69ec
 
 
e269dfa
09c69ec
e08e8ba
e269dfa
e08e8ba
09c69ec
 
 
e269dfa
 
 
09c69ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e269dfa
09c69ec
 
 
 
 
 
 
 
e08e8ba
09c69ec
e08e8ba
 
 
 
09c69ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e08e8ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09c69ec
 
e08e8ba
 
 
 
 
 
 
 
 
09c69ec
e08e8ba
 
 
 
 
 
 
09c69ec
e08e8ba
 
 
09c69ec
e269dfa
d7b5306
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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)