varshakolanu commited on
Commit
646567b
·
verified ·
1 Parent(s): f673d62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +225 -178
app.py CHANGED
@@ -8,223 +8,270 @@ 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
- # Model to match the incoming Vendor Log data from Salesforce
22
  class VendorLog(BaseModel):
23
  vendorLogId: str
24
  vendorId: str
25
- workDetails: str # e.g., "90% completed"
26
- qualityReport: str # e.g., "60% quality"
27
- incidentLog: str # e.g., "Minor"
28
- workCompletionDate: str # e.g., "2025-05-12"
29
- actualCompletionDate: str # e.g., "2025-05-10"
30
- vendorLogName: str # e.g., "abhi"
31
- delayDays: int # e.g., 2
32
-
33
- # Store vendor logs for display in the dashboard
 
34
  vendor_logs = []
35
 
36
  def calculate_scores(log: VendorLog):
37
- # Extract percentages from the string fields
38
- work_completion_percentage = float(log.workDetails.replace('% completed', ''))
39
- quality_percentage = float(log.qualityReport.replace('% quality', ''))
 
40
 
41
- # Quality Score: Directly use the quality percentage
42
- quality_score = quality_percentage
43
 
44
- # Timeliness Score: Based on delay days
45
- 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
46
 
47
- # Safety Score: Based on incident severity
48
- severity_map = {'None': 100.0, 'Low': 80.0, 'Minor': 80.0, 'Medium': 50.0, 'High': 20.0}
49
- safety_score = severity_map.get(log.incidentLog, 100.0)
50
 
51
- # Communication Score: Weighted average of other scores
52
- communication_score = (quality_score * 0.33 + timeliness_score * 0.33 + safety_score * 0.33)
53
 
54
- # Final Score: Average of all scores
55
- final_score = (quality_score + timeliness_score + safety_score + communication_score) / 4
56
 
57
- return {
58
- 'qualityScore': round(quality_score, 2),
59
- 'timelinessScore': round(timeliness_score, 2),
60
- 'safetyScore': round(safety_score, 2),
61
- 'communicationScore': round(communication_score, 2),
62
- 'finalScore': round(final_score, 2)
63
- }
 
 
 
64
 
65
  def get_feedback(score: float, metric: str) -> str:
66
- if score >= 90:
67
- return "Excellent: Maintain this standard"
68
- elif score >= 70:
69
- return "Good: Keep up the good work"
70
- elif score >= 50:
71
- if metric == 'Timeliness':
72
- return "Needs Improvement: Maintain schedules to complete tasks on time"
73
- elif metric == 'Safety':
74
- return "Needs Improvement: Implement stricter safety protocols"
75
- elif metric == 'Quality':
76
- return "Needs Improvement: Focus on improving work quality"
77
- else:
78
- return "Needs Improvement: Enhance coordination with project teams"
79
- else:
80
- if metric == 'Timeliness':
81
- return "Poor: Significant delays detected"
82
- elif metric == 'Safety':
83
- return "Poor: Critical safety issues identified"
84
- elif metric == 'Quality':
85
- return "Poor: Quality standards not met"
86
  else:
87
- return "Poor: Communication issues detected"
 
 
 
 
 
 
 
 
 
 
88
 
89
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
90
- filename = f'report_{vendor_id}.pdf'
91
- c = canvas.Canvas(filename, pagesize=letter)
92
- c.setFont('Helvetica', 12)
93
- c.drawString(100, 750, 'Subcontractor Performance Report')
94
- c.drawString(100, 730, f'Vendor ID: {vendor_id}')
95
- c.drawString(100, 710, f'Vendor Log Name: {vendor_log_name}')
96
- c.drawString(100, 690, f'Quality Score: {scores["qualityScore"]}% ({get_feedback(scores["qualityScore"], "Quality")})')
97
- c.drawString(100, 670, f'Timeliness Score: {scores["timelinessScore"]}% ({get_feedback(scores["timelinessScore"], "Timeliness")})')
98
- c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
99
- c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
100
- c.drawString(100, 610, f'Final Score: {scores["finalScore"]}%')
101
- c.save()
102
-
103
- with open(filename, 'rb') as f:
104
- pdf_content = f.read()
105
- os.remove(filename)
106
- return pdf_content
 
 
 
 
107
 
108
  def determine_alert_flag(final_score: float, all_logs: list):
109
- if not all_logs:
110
- return False
111
- # If final score is less than 50, flag an alert
112
- if final_score < 50:
113
- return True
114
- # Otherwise, flag the vendor with the lowest final score
115
- lowest_score = min([log['scores']['finalScore'] for log in all_logs])
116
- return final_score == lowest_score
 
 
117
 
118
  @app.post('/score')
119
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
120
- if authorization != f'Bearer {API_KEY}':
121
- raise HTTPException(status_code=401, detail='Invalid API key')
122
-
123
- # Calculate scores
124
- scores = calculate_scores(log)
125
-
126
- # Generate PDF
127
- pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
128
- pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
129
-
130
- # Determine alert flag
131
- alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
132
-
133
- # Store the log and scores
134
- vendor_logs.append({
135
- 'vendorLogId': log.vendorLogId,
136
- 'vendorId': log.vendorId,
137
- 'vendorLogName': log.vendorLogName,
138
- 'scores': scores,
139
- 'extracted': True
140
- })
141
-
142
- # Return the response to Salesforce
143
- return {
144
- 'vendorLogId': log.vendorLogId,
145
- 'vendorId': log.vendorId,
146
- 'vendorLogName': log.vendorLogName,
147
- 'qualityScore': scores['qualityScore'],
148
- 'timelinessScore': scores['timelinessScore'],
149
- 'safetyScore': scores['safetyScore'],
150
- 'communicationScore': scores['communicationScore'],
151
- 'finalScore': scores['finalScore'],
152
- 'pdfContent': pdf_base64,
153
- 'alert': alert_flag
154
- }
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  @app.get('/', response_class=HTMLResponse)
157
  async def get_dashboard():
158
- html_content = """
159
- <html>
160
- <head>
161
- <title>Subcontractor Performance Score App</title>
162
- <style>
163
- body { font-family: Arial, sans-serif; margin: 20px; }
164
- table { width: 100%; border-collapse: collapse; margin-top: 20px; }
165
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
166
- th { background-color: #f2f2f2; }
167
- h1, h2 { text-align: center; }
168
- </style>
169
- </head>
170
- <body>
171
- <h1>SUBCONTRACTOR PERFORMANCE SCORE APP GENERATOR</h1>
172
- <h2>VENDOR LOGS</h2>
173
- <table>
174
- <tr>
175
- <th>VENDOR ID</th>
176
- <th>DETAILS EXTRACTED FROM SALESFORCE FROM VENDOR LOG OBJECT</th>
177
- <th>FINAL SCORE CALCULATED BY APP</th>
178
- </tr>
179
- """
180
-
181
- for log in vendor_logs:
182
- html_content += f"""
183
- <tr>
184
- <td>{log['vendorId']}</td>
185
- <td>{'YES' if log['extracted'] else 'NO'}</td>
186
- <td>{log['scores']['finalScore'] if log['extracted'] else 'NOT CALCULATED'}</td>
187
- </tr>
188
  """
189
 
190
- html_content += """
191
- </table>
192
- <h2>SUBCONTRACTOR PERFORMANCE SCORES</h2>
193
- <table>
194
- <tr>
195
- <th>Vendor ID</th>
196
- <th>Vendor Log Name</th>
197
- <th>Quality Score</th>
198
- <th>Timeliness Score</th>
199
- <th>Safety Score</th>
200
- <th>Communication Score</th>
201
- <th>Final Score</th>
202
- <th>Alert Flag</th>
203
- </tr>
204
- """
205
-
206
- for log in vendor_logs:
207
- scores = log['scores']
208
- alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
209
- html_content += f"""
210
- <tr>
211
- <td>{log['vendorId']}</td>
212
- <td>{log['vendorLogName']}</td>
213
- <td>{scores['qualityScore']}%</td>
214
- <td>{scores['timelinessScore']}%</td>
215
- <td>{scores['safetyScore']}%</td>
216
- <td>{scores['communicationScore']}%</td>
217
- <td>{scores['finalScore']}%</td>
218
- <td>{'Checked' if alert_flag else 'Unchecked'}</td>
219
- </tr>
220
  """
221
 
222
- html_content += """
223
- </table>
224
- </body>
225
- </html>
226
- """
227
- return HTMLResponse(content=html_content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
  if __name__ == "__main__":
230
  import uvicorn
 
8
  from datetime import datetime
9
  from fastapi.responses import HTMLResponse
10
 
11
+ # Set up logging to capture errors and debug information
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
  app = FastAPI()
16
 
17
+ # Verify API key is set
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
+ # VendorLog model to match Salesforce data
24
  class VendorLog(BaseModel):
25
  vendorLogId: str
26
  vendorId: str
27
+ workDetails: str
28
+ qualityReport: str
29
+ incidentLog: str
30
+ workCompletionDate: str
31
+ actualCompletionDate: str
32
+ vendorLogName: str
33
+ delayDays: int
34
+ project: str
35
+
36
+ # Store vendor logs for display
37
  vendor_logs = []
38
 
39
  def calculate_scores(log: VendorLog):
40
+ try:
41
+ # Extract percentages from the string fields
42
+ work_completion_percentage = float(log.workDetails.replace('% completed', ''))
43
+ quality_percentage = float(log.qualityReport.replace('% quality', ''))
44
 
45
+ # Quality Score: Directly use the quality percentage
46
+ quality_score = quality_percentage
47
 
48
+ # Timeliness Score: Based on delay days
49
+ 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
50
 
51
+ # Safety Score: Based on incident severity
52
+ severity_map = {'None': 100.0, 'Low': 80.0, 'Minor': 80.0, 'Medium': 50.0, 'High': 20.0}
53
+ safety_score = severity_map.get(log.incidentLog, 100.0)
54
 
55
+ # Communication Score: Weighted average of other scores
56
+ communication_score = (quality_score * 0.33 + timeliness_score * 0.33 + safety_score * 0.33)
57
 
58
+ # Final Score: Average of all scores
59
+ final_score = (quality_score + timeliness_score + safety_score + communication_score) / 4
60
 
61
+ return {
62
+ 'qualityScore': round(quality_score, 2),
63
+ 'timelinessScore': round(timeliness_score, 2),
64
+ 'safetyScore': round(safety_score, 2),
65
+ 'communicationScore': round(communication_score, 2),
66
+ 'finalScore': round(final_score, 2)
67
+ }
68
+ except Exception as e:
69
+ logger.error(f"Error calculating scores: {str(e)}")
70
+ raise
71
 
72
  def get_feedback(score: float, metric: str) -> str:
73
+ try:
74
+ if score >= 90:
75
+ return "Excellent: Maintain this standard"
76
+ elif score >= 70:
77
+ return "Good: Keep up the good work"
78
+ elif score >= 50:
79
+ if metric == 'Timeliness':
80
+ return "Needs Improvement: Maintain schedules to complete tasks on time"
81
+ elif metric == 'Safety':
82
+ return "Needs Improvement: Implement stricter safety protocols"
83
+ elif metric == 'Quality':
84
+ return "Needs Improvement: Focus on improving work quality"
85
+ else:
86
+ return "Needs Improvement: Enhance coordination with project teams"
 
 
 
 
 
 
87
  else:
88
+ if metric == 'Timeliness':
89
+ return "Poor: Significant delays detected"
90
+ elif metric == 'Safety':
91
+ return "Poor: Critical safety issues identified"
92
+ elif metric == 'Quality':
93
+ return "Poor: Quality standards not met"
94
+ else:
95
+ return "Poor: Communication issues detected"
96
+ except Exception as e:
97
+ logger.error(f"Error generating feedback: {str(e)}")
98
+ raise
99
 
100
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
101
+ try:
102
+ filename = f'report_{vendor_id}.pdf'
103
+ c = canvas.Canvas(filename, pagesize=letter)
104
+ c.setFont('Helvetica', 12)
105
+ c.drawString(100, 750, 'Subcontractor Performance Report')
106
+ c.drawString(100, 730, f'Vendor ID: {vendor_id}')
107
+ c.drawString(100, 710, f'Vendor Log Name: {vendor_log_name}')
108
+ c.drawString(100, 690, f'Quality Score: {scores["qualityScore"]}% ({get_feedback(scores["qualityScore"], "Quality")})')
109
+ c.drawString(100, 670, f'Timeliness Score: {scores["timelinessScore"]}% ({get_feedback(scores["timelinessScore"], "Timeliness")})')
110
+ c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
111
+ c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
112
+ c.drawString(100, 610, f'Final Score: {scores["finalScore"]}%')
113
+ c.save()
114
+
115
+ with open(filename, 'rb') as f:
116
+ pdf_content = f.read()
117
+ os.remove(filename)
118
+ return pdf_content
119
+ except Exception as e:
120
+ logger.error(f"Error generating PDF: {str(e)}")
121
+ raise
122
 
123
  def determine_alert_flag(final_score: float, all_logs: list):
124
+ try:
125
+ if not all_logs:
126
+ return False
127
+ if final_score < 50:
128
+ return True
129
+ lowest_score = min([log['scores']['finalScore'] for log in all_logs])
130
+ return final_score == lowest_score
131
+ except Exception as e:
132
+ logger.error(f"Error determining alert flag: {str(e)}")
133
+ raise
134
 
135
  @app.post('/score')
136
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
137
+ try:
138
+ logger.info(f"Received Vendor Log: {log}")
139
+ if authorization != f'Bearer {API_KEY}':
140
+ raise HTTPException(status_code=401, detail='Invalid API key')
141
+
142
+ # Calculate scores
143
+ scores = calculate_scores(log)
144
+
145
+ # Generate PDF
146
+ pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
147
+ pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
148
+
149
+ # Determine alert flag
150
+ alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
151
+
152
+ # Store the log and scores
153
+ vendor_logs.append({
154
+ 'vendorLogId': log.vendorLogId,
155
+ 'vendorId': log.vendorId,
156
+ 'vendorLogName': log.vendorLogName,
157
+ 'workDetails': log.workDetails,
158
+ 'qualityReport': log.qualityReport,
159
+ 'incidentLog': log.incidentLog,
160
+ 'workCompletionDate': log.workCompletionDate,
161
+ 'actualCompletionDate': log.actualCompletionDate,
162
+ 'delayDays': log.delayDays,
163
+ 'project': log.project,
164
+ 'scores': scores,
165
+ 'extracted': True
166
+ })
167
+
168
+ # Return the response to Salesforce
169
+ return {
170
+ 'vendorLogId': log.vendorLogId,
171
+ 'vendorId': log.vendorId,
172
+ 'vendorLogName': log.vendorLogName,
173
+ 'qualityScore': scores['qualityScore'],
174
+ 'timelinessScore': scores['timelinessScore'],
175
+ 'safetyScore': scores['safetyScore'],
176
+ 'communicationScore': scores['communicationScore'],
177
+ 'finalScore': scores['finalScore'],
178
+ 'pdfContent': pdf_base64,
179
+ 'alert': alert_flag
180
+ }
181
+ except Exception as e:
182
+ logger.error(f"Error in /score endpoint: {str(e)}")
183
+ raise HTTPException(status_code=500, detail=f"Error processing vendor log: {str(e)}")
184
 
185
  @app.get('/', response_class=HTMLResponse)
186
  async def get_dashboard():
187
+ try:
188
+ html_content = """
189
+ <html>
190
+ <head>
191
+ <title>Subcontractor Performance Score App</title>
192
+ <style>
193
+ body { font-family: Arial, sans-serif; margin: 20px; }
194
+ table { width: 100%; border-collapse: collapse; margin-top: 20px; }
195
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
196
+ th { background-color: #f2f2f2; }
197
+ h1, h2 { text-align: center; }
198
+ </style>
199
+ </head>
200
+ <body>
201
+ <h1>SUBCONTRACTOR PERFORMANCE SCORE APP GENERATOR</h1>
202
+ <h2>VENDOR LOGS SUBMISSION</h2>
203
+ <table>
204
+ <tr>
205
+ <th>Vendor ID</th>
206
+ <th>Vendor Log Name</th>
207
+ <th>Project</th>
208
+ <th>Work Completion Percentage</th>
209
+ <th>Quality Percentage</th>
210
+ <th>Incident Severity</th>
211
+ <th>Work Completion Date</th>
212
+ <th>Actual Completion Date</th>
213
+ <th>Delay Days</th>
214
+ </tr>
 
 
215
  """
216
 
217
+ for log in vendor_logs:
218
+ html_content += f"""
219
+ <tr>
220
+ <td>{log['vendorId']}</td>
221
+ <td>{log['vendorLogName']}</td>
222
+ <td>{log['project']}</td>
223
+ <td>{log['workDetails']}</td>
224
+ <td>{log['qualityReport']}</td>
225
+ <td>{log['incidentLog']}</td>
226
+ <td>{log['workCompletionDate']}</td>
227
+ <td>{log['actualCompletionDate']}</td>
228
+ <td>{log['delayDays']}</td>
229
+ </tr>
230
+ """
231
+
232
+ html_content += """
233
+ </table>
234
+ <h2>SUBCONTRACTOR PERFORMANCE SCORES</h2>
235
+ <table>
236
+ <tr>
237
+ <th>Vendor ID</th>
238
+ <th>Vendor Log Name</th>
239
+ <th>Project</th>
240
+ <th>Quality Score</th>
241
+ <th>Timeliness Score</th>
242
+ <th>Safety Score</th>
243
+ <th>Communication Score</th>
244
+ <th>Final Score</th>
245
+ <th>Alert Flag</th>
246
+ </tr>
247
  """
248
 
249
+ for log in vendor_logs:
250
+ scores = log['scores']
251
+ alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
252
+ html_content += f"""
253
+ <tr>
254
+ <td>{log['vendorId']}</td>
255
+ <td>{log['vendorLogName']}</td>
256
+ <td>{log['project']}</td>
257
+ <td>{scores['qualityScore']}%</td>
258
+ <td>{scores['timelinessScore']}%</td>
259
+ <td>{scores['safetyScore']}%</td>
260
+ <td>{scores['communicationScore']}%</td>
261
+ <td>{scores['finalScore']}%</td>
262
+ <td>{'Checked' if alert_flag else 'Unchecked'}</td>
263
+ </tr>
264
+ """
265
+
266
+ html_content += """
267
+ </table>
268
+ </body>
269
+ </html>
270
+ """
271
+ return HTMLResponse(content=html_content)
272
+ except Exception as e:
273
+ logger.error(f"Error in / endpoint: {str(e)}")
274
+ raise HTTPException(status_code=500, detail=f"Error generating dashboard: {str(e)}")
275
 
276
  if __name__ == "__main__":
277
  import uvicorn