varshakolanu commited on
Commit
f541eee
·
verified ·
1 Parent(s): ce9ca6b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -57
app.py CHANGED
@@ -5,108 +5,121 @@ from reportlab.pdfgen import canvas
5
  import base64
6
  import os
7
  import logging
8
- from huggingface_hub import InferenceClient
9
- import numpy as np
10
 
11
- # Set up logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
  app = FastAPI()
16
 
17
- # Load API key from environment variable
18
  API_KEY = os.getenv("HUGGINGFACE_API_KEY")
19
  if not API_KEY:
20
  logger.error("HUGGINGFACE_API_KEY environment variable not set")
21
  raise ValueError("HUGGINGFACE_API_KEY environment variable not set")
22
 
23
- # Initialize Hugging Face Inference Client
24
- client = InferenceClient(token=API_KEY)
25
-
26
  class VendorLog(BaseModel):
27
  vendorLogId: str
28
  vendorId: str
29
- workDetails: str
30
- workCompletionDate: str # ISO format (e.g., "2025-05-12")
31
- actualCompletionDate: str # ISO format
32
- incidentLog: str
33
- qualityReport: str
34
  vendorLogName: str
35
-
36
- def analyze_sentiment(text: str) -> float:
37
- """Analyze text sentiment using Hugging Face Inference API."""
38
- try:
39
- if not text:
40
- return 50.0 # Neutral score for empty text
41
- result = client.text_classification(text, model="distilbert-base-uncased-finetuned-sst-2-english")
42
- score = result[0]['score'] if result[0]['label'] == 'POSITIVE' else 1 - result[0]['score']
43
- return score * 100 # Convert to percentage
44
- except Exception as e:
45
- logger.error(f"Sentiment analysis error: {e}")
46
- return 50.0 # Fallback score
47
 
48
  def calculate_scores(log: VendorLog):
49
- # Sentiment analysis for work details, quality report, incident log
50
- work_completion_score = analyze_sentiment(log.workDetails)
51
- quality_score = analyze_sentiment(log.qualityReport)
52
- incident_score = analyze_sentiment(log.incidentLog) if log.incidentLog else 100.0 # No incidents = high score
53
 
54
- # Timeliness score based on date difference
55
- from datetime import datetime
56
- work_date = datetime.fromisoformat(log.workCompletionDate.replace('Z', '+00:00'))
57
- actual_date = datetime.fromisoformat(log.actualCompletionDate.replace('Z', '+00:00'))
58
- delay_days = (actual_date - work_date).days
59
- timeliness_score = 100.0 if delay_days <= 0 else 80.0 if delay_days <= 3 else 60.0 if delay_days <= 7 else 40.0
60
 
61
- # Safety score (inverse of incident sentiment)
62
- safety_score = 100.0 - (incident_score if incident_score < 100.0 else 0.0)
63
 
64
- # Communication score (average of work and quality sentiment)
65
- communication_score = (work_completion_score + quality_score) / 2
 
66
 
67
  return {
68
  'qualityScore': round(quality_score, 2),
69
  'timelinessScore': round(timeliness_score, 2),
70
  'safetyScore': round(safety_score, 2),
71
  'communicationScore': round(communication_score, 2),
72
- 'finalScore': round((quality_score + timeliness_score + safety_score + communication_score) / 4, 2)
73
  }
74
 
75
- def generate_pdf(vendor_id: str, scores: dict):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  filename = f'report_{vendor_id}.pdf'
77
  c = canvas.Canvas(filename, pagesize=letter)
78
  c.setFont('Helvetica', 12)
79
- c.drawString(100, 750, 'Vendor Performance Report')
80
  c.drawString(100, 730, f'Vendor ID: {vendor_id}')
81
- y = 700
82
- for metric, score in scores.items():
83
- c.drawString(100, y, f'{metric.replace("Score", " Score")}: {score}%')
84
- y -= 20
 
85
  c.save()
86
-
87
  with open(filename, 'rb') as f:
88
  pdf_content = f.read()
89
  os.remove(filename)
90
  return pdf_content
91
 
92
- def send_alert(vendor_id: str, final_score: float):
93
- """Placeholder for alert logic (e.g., email or webhook)."""
94
  if final_score < 50:
95
- logger.info(f"Alert: Vendor {vendor_id} has low final score ({final_score}%)")
96
- # Implement email or webhook notification here
97
- return {"vendorId": vendor_id, "message": f"Low performance score ({final_score}%) detected"}
98
- return None
 
 
 
 
99
 
100
  @app.post('/score')
101
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
102
  if authorization != f'Bearer {API_KEY}':
103
  raise HTTPException(status_code=401, detail='Invalid API key')
104
-
105
  scores = calculate_scores(log)
106
- pdf_content = generate_pdf(log.vendorId, scores)
107
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
108
- alert = send_alert(log.vendorId, scores['finalScore'])
109
-
 
 
 
 
 
 
 
 
110
  return {
111
  'vendorLogId': log.vendorLogId,
112
  'vendorId': log.vendorId,
@@ -118,9 +131,78 @@ async def score_vendor(log: VendorLog, authorization: str = Header(...)):
118
  'finalScore': scores['finalScore'],
119
  'pdfContent': pdf_base64,
120
  'pdfUrl': f'/files/report_{log.vendorId}.pdf',
121
- 'alert': alert
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  if __name__ == "__main__":
125
  import uvicorn
126
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
5
  import base64
6
  import os
7
  import logging
8
+ from datetime import datetime
9
+ from fastapi.responses import HTMLResponse
10
 
 
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
  app = FastAPI()
15
 
 
16
  API_KEY = os.getenv("HUGGINGFACE_API_KEY")
17
  if not API_KEY:
18
  logger.error("HUGGINGFACE_API_KEY environment variable not set")
19
  raise ValueError("HUGGINGFACE_API_KEY environment variable not set")
20
 
 
 
 
21
  class VendorLog(BaseModel):
22
  vendorLogId: str
23
  vendorId: str
24
+ workCompletionPercentage: float
25
+ qualityPercentage: float
26
+ incidentSeverity: str
27
+ workCompletionDate: str
28
+ actualCompletionDate: str
29
  vendorLogName: str
30
+ delayDays: int
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  def calculate_scores(log: VendorLog):
33
+ quality_score = log.qualityPercentage
 
 
 
34
 
35
+ timeliness_score = 100.0 if log.delayDays <= 0 else 80.0 if log.delayDays <= 3 else 60.0 if log.delayDays <= 7 else 40.0
 
 
 
 
 
36
 
37
+ severity_map = {'None': 100.0, 'Low': 80.0, 'Medium': 50.0, 'High': 20.0}
38
+ safety_score = severity_map.get(log.incidentSeverity, 100.0)
39
 
40
+ communication_score = (quality_score * 0.33 + timeliness_score * 0.33 + safety_score * 0.33)
41
+
42
+ final_score = (quality_score + timeliness_score + safety_score + communication_score) / 4
43
 
44
  return {
45
  'qualityScore': round(quality_score, 2),
46
  'timelinessScore': round(timeliness_score, 2),
47
  'safetyScore': round(safety_score, 2),
48
  'communicationScore': round(communication_score, 2),
49
+ 'finalScore': round(final_score, 2)
50
  }
51
 
52
+ def get_feedback(score: float, metric: str) -> str:
53
+ if score >= 90:
54
+ return "Excellent: Maintain this standard"
55
+ elif score >= 70:
56
+ return "Good: Keep up the good work"
57
+ elif score >= 50:
58
+ if metric == 'Timeliness':
59
+ return "Needs Improvement: Maintain schedules to complete tasks on time"
60
+ elif metric == 'Safety':
61
+ return "Needs Improvement: Implement stricter safety protocols"
62
+ elif metric == 'Quality':
63
+ return "Needs Improvement: Focus on improving work quality"
64
+ else:
65
+ return "Needs Improvement: Enhance coordination with project teams"
66
+ else:
67
+ if metric == 'Timeliness':
68
+ return "Poor: Significant delays detected"
69
+ elif metric == 'Safety':
70
+ return "Poor: Critical safety issues identified"
71
+ elif metric == 'Quality':
72
+ return "Poor: Quality standards not met"
73
+ else:
74
+ return "Poor: Communication issues detected"
75
+
76
+ def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
77
  filename = f'report_{vendor_id}.pdf'
78
  c = canvas.Canvas(filename, pagesize=letter)
79
  c.setFont('Helvetica', 12)
80
+ c.drawString(100, 750, 'Subcontractor Performance Report')
81
  c.drawString(100, 730, f'Vendor ID: {vendor_id}')
82
+ c.drawString(100, 710, f'Quality Score: {scores["qualityScore"]}% ({get_feedback(scores["qualityScore"], "Quality")})')
83
+ c.drawString(100, 690, f'Timeliness Score: {scores["timelinessScore"]}% ({get_feedback(scores["timelinessScore"], "Timeliness")})')
84
+ c.drawString(100, 670, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
85
+ c.drawString(100, 650, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
86
+ c.drawString(100, 630, f'Final Score: {scores["finalScore"]}%')
87
  c.save()
88
+
89
  with open(filename, 'rb') as f:
90
  pdf_content = f.read()
91
  os.remove(filename)
92
  return pdf_content
93
 
94
+ def determine_alert_flag(final_score: float, all_scores: list):
 
95
  if final_score < 50:
96
+ return True
97
+ above_50 = any(score['scores']['finalScore'] >= 50 for score in all_scores)
98
+ if not above_50:
99
+ lowest_score = min([score['scores']['finalScore'] for score in all_scores])
100
+ return final_score == lowest_score
101
+ return False
102
+
103
+ vendor_logs = []
104
 
105
  @app.post('/score')
106
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
107
  if authorization != f'Bearer {API_KEY}':
108
  raise HTTPException(status_code=401, detail='Invalid API key')
109
+
110
  scores = calculate_scores(log)
111
+ pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
112
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
113
+ alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
114
+
115
+ vendor_logs.append({
116
+ 'vendorLogId': log.vendorLogId,
117
+ 'vendorId': log.vendorId,
118
+ 'vendorLogName': log.vendorLogName,
119
+ 'scores': scores,
120
+ 'extracted': True
121
+ })
122
+
123
  return {
124
  'vendorLogId': log.vendorLogId,
125
  'vendorId': log.vendorId,
 
131
  'finalScore': scores['finalScore'],
132
  'pdfContent': pdf_base64,
133
  'pdfUrl': f'/files/report_{log.vendorId}.pdf',
134
+ 'alertFlag': alert_flag
135
  }
136
 
137
+ @app.get('/', response_class=HTMLResponse)
138
+ async def get_dashboard():
139
+ html_content = """
140
+ <html>
141
+ <head>
142
+ <title>Subcontractor Performance Score App</title>
143
+ <style>
144
+ body { font-family: Arial, sans-serif; margin: 20px; }
145
+ table { width: 100%; border-collapse: collapse; margin-top: 20px; }
146
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
147
+ th { background-color: #f2f2f2; }
148
+ h1, h2 { text-align: center; }
149
+ </style>
150
+ </head>
151
+ <body>
152
+ <h1>Subcontractor Performance Score App Generator</h1>
153
+ <h2>Vendor Logs</h2>
154
+ <table>
155
+ <tr>
156
+ <th>Vendor ID</th>
157
+ <th>Details Extracted</th>
158
+ <th>Final Score</th>
159
+ </tr>
160
+ """
161
+ for log in vendor_logs:
162
+ html_content += f"""
163
+ <tr>
164
+ <td>{log['vendorId']}</td>
165
+ <td>{'YES' if log['extracted'] else 'NO'}</td>
166
+ <td>{log['scores']['finalScore'] if log['extracted'] else 'NOT CALCULATED'}</td>
167
+ </tr>
168
+ """
169
+ html_content += """
170
+ </table>
171
+ <h2>Subcontractor Performance Scores</h2>
172
+ <table>
173
+ <tr>
174
+ <th>Vendor ID</th>
175
+ <th>Vendor Log Name</th>
176
+ <th>Quality Score</th>
177
+ <th>Timeliness Score</th>
178
+ <th>Safety Score</th>
179
+ <th>Communication Score</th>
180
+ <th>Final Score</th>
181
+ <th>Alert Flag</th>
182
+ </tr>
183
+ """
184
+ for log in vendor_logs:
185
+ scores = log['scores']
186
+ alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
187
+ html_content += f"""
188
+ <tr>
189
+ <td>{log['vendorId']}</td>
190
+ <td>{log['vendorLogName']}</td>
191
+ <td>{scores['qualityScore']}%</td>
192
+ <td>{scores['timelinessScore']}%</td>
193
+ <td>{scores['safetyScore']}%</td>
194
+ <td>{scores['communicationScore']}%</td>
195
+ <td>{scores['finalScore']}%</td>
196
+ <td>{'Checked' if alert_flag else 'Unchecked'}</td>
197
+ </tr>
198
+ """
199
+ html_content += """
200
+ </table>
201
+ </body>
202
+ </html>
203
+ """
204
+ return HTMLResponse(content=html_content)
205
+
206
  if __name__ == "__main__":
207
  import uvicorn
208
  uvicorn.run(app, host="0.0.0.0", port=7860)