Swathi6 commited on
Commit
7e08e15
·
verified ·
1 Parent(s): ee090a6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -83
app.py CHANGED
@@ -8,7 +8,11 @@ import logging
8
  from datetime import datetime
9
  from fastapi.responses import HTMLResponse
10
  from simple_salesforce import Salesforce
11
- import json
 
 
 
 
12
 
13
  # Set up logging
14
  logging.basicConfig(level=logging.INFO)
@@ -21,6 +25,8 @@ SF_USERNAME = os.getenv("SF_USERNAME")
21
  SF_PASSWORD = os.getenv("SF_PASSWORD")
22
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
23
  SF_DOMAIN = os.getenv("SF_DOMAIN", "login")
 
 
24
 
25
  # Validate environment variables
26
  required_env_vars = ["SF_USERNAME", "SF_PASSWORD", "SF_SECURITY_TOKEN"]
@@ -29,6 +35,13 @@ for var in required_env_vars:
29
  logger.error(f"Environment variable {var} is not set")
30
  raise ValueError(f"Environment variable {var} is not set")
31
 
 
 
 
 
 
 
 
32
  # Connect to Salesforce
33
  try:
34
  sf = Salesforce(
@@ -59,6 +72,41 @@ class VendorLog(BaseModel):
59
  # Store vendor logs for display
60
  vendor_logs = []
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  def fetch_vendor_logs_from_salesforce():
63
  try:
64
  query = """
@@ -66,13 +114,11 @@ def fetch_vendor_logs_from_salesforce():
66
  Incident_Severity__c, Work_Completion_Date__c, Actual_Completion_Date__c,
67
  Delay_Days__c, Project__c
68
  FROM Vendor_Log__c
 
69
  """
70
  result = sf.query_all(query)
71
  logs = []
72
  for record in result['records']:
73
- if not record['Vendor__c']:
74
- logger.warning(f"Skipping Vendor_Log__c record with ID {record['Id']} due to missing Vendor__c")
75
- continue
76
  log = VendorLog(
77
  vendorLogId=record['Id'] or "Unknown",
78
  vendorId=record['Name'] or "Unknown",
@@ -93,7 +139,7 @@ def fetch_vendor_logs_from_salesforce():
93
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
94
  raise HTTPException(status_code=500, detail=f"Error fetching vendor logs: {str(e)}")
95
 
96
- def calculate_scores(log: VendorLog):
97
  try:
98
  work_completion_percentage = float(log.workDetails)
99
  quality_percentage = float(log.qualityReport)
@@ -118,9 +164,41 @@ def calculate_scores(log: VendorLog):
118
  'communicationScore': round(communication_score, 2)
119
  }
120
  except Exception as e:
121
- logger.error(f"Error calculating scores: {str(e)}")
122
  raise HTTPException(status_code=500, detail=f"Error calculating scores: {str(e)}")
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  def get_feedback(score: float, metric: str) -> str:
125
  try:
126
  if score >= 90:
@@ -151,7 +229,7 @@ def get_feedback(score: float, metric: str) -> str:
151
 
152
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
153
  try:
154
- filename = f'report_{vendor_id}.pdf'
155
  c = canvas.Canvas(filename, pagesize=letter)
156
  c.setFont('Helvetica', 12)
157
  c.drawString(100, 750, 'Subcontractor Performance Report')
@@ -222,7 +300,6 @@ def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes,
222
  pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
223
 
224
  # Update Subcontractor_Performance_Score__c with PDF URL
225
- # Note: Changed PDF_Link__c to Certification_URL__c to match your previous Salesforce schema
226
  sf.Subcontractor_Performance_Score__c.update(score_record_id, {
227
  'Certification_URL__c': pdf_url
228
  })
@@ -235,10 +312,6 @@ def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes,
235
  async def score_vendor(log: VendorLog, authorization: str = Header(None)):
236
  try:
237
  logger.info(f"Received Vendor Log: {log}")
238
- # Optional: Add API key check if needed
239
- # if authorization != f'Bearer {API_KEY}':
240
- # raise HTTPException(status_code=401, detail='Invalid API key')
241
-
242
  scores = calculate_scores(log)
243
  pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
244
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
@@ -308,24 +381,21 @@ async def get_dashboard():
308
  <html>
309
  <head>
310
  <title>Subcontractor Performance Score App</title>
 
311
  <style>
312
- body { font-family: Arial, sans-serif; margin: 20px; }
 
313
  table { width: 100%; border-collapse: collapse; margin-top: 20px; }
314
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
315
- th { background-color: #f2f2f2; }
316
- h1, h2 { text-align: center; }
317
  .generate-btn {
318
- display: block;
319
- margin: 20px auto;
320
- padding: 10px 20px;
321
- background-color: #4CAF50;
322
- color: white;
323
- border: none;
324
- border-radius: 5px;
325
- cursor: pointer;
326
- font-size: 16px;
327
  }
328
  .generate-btn:hover { background-color: #45a049; }
 
329
  </style>
330
  <script>
331
  async function generateScores() {
@@ -343,86 +413,96 @@ async def get_dashboard():
343
  </script>
344
  </head>
345
  <body>
346
- <h1>SUBCONTRACTOR PERFORMANCE SCORE APP GENERATOR</h1>
347
- <h2>VENDOR LOGS SUBMISSION</h2>
348
- <table>
349
- <tr>
350
- <th>Vendor ID</th>
351
- <th>Vendor Log Name</th>
352
- <th>Project</th>
353
- <th>Work Completion Percentage</th>
354
- <th>Quality Percentage</th>
355
- <th>Incident Severity</th>
356
- <th>Work Completion Date</th>
357
- <th>Actual Completion Date</th>
358
- <th>Delay Days</th>
359
- </tr>
 
 
 
 
360
  """
361
 
362
  if not vendor_logs:
363
  html_content += """
364
- <tr>
365
- <td colspan="9" style="text-align: center;">No vendor logs available</td>
366
- </tr>
367
  """
368
  else:
369
  for log in vendor_logs:
370
  html_content += f"""
371
- <tr>
372
- <td>{log['vendorId']}</td>
373
- <td>{log['vendorLogName']}</td>
374
- <td>{log['project']}</td>
375
- <td>{log['workDetails']}</td>
376
- <td>{log['qualityReport']}</td>
377
- <td>{log['incidentLog']}</td>
378
- <td>{log['workCompletionDate']}</td>
379
- <td>{log['actualCompletionDate']}</td>
380
- <td>{log['delayDays']}</td>
381
- </tr>
382
  """
383
 
384
  html_content += """
385
- </table>
386
- <button class="generate-btn" onclick="generateScores()">Generate</button>
387
- <h2>SUBCONTRACTOR PERFORMANCE SCORES</h2>
388
- <table>
389
- <tr>
390
- <th>Vendor ID</th>
391
- <th>Vendor Log Name</th>
392
- <th>Project</th>
393
- <th>Quality Score</th>
394
- <th>Timeliness Score</th>
395
- <th>Safety Score</th>
396
- <th>Communication Score</th>
397
- <th>Alert Flag</th>
398
- </tr>
 
 
 
 
399
  """
400
 
401
  if not vendor_logs:
402
  html_content += """
403
- <tr>
404
- <td colspan="8" style="text-align: center;">No scores available</td>
405
- </tr>
406
  """
407
  else:
408
  for log in vendor_logs:
409
  scores = log['scores']
410
  alert_flag = determine_alert_flag(scores, vendor_logs)
411
  html_content += f"""
412
- <tr>
413
- <td>{log['vendorId']}</td>
414
- <td>{log['vendorLogName']}</td>
415
- <td>{log['project']}</td>
416
- <td>{scores['qualityScore']}%</td>
417
- <td>{scores['timelinessScore']}%</td>
418
- <td>{scores['safetyScore']}%</td>
419
- <td>{scores['communicationScore']}%</td>
420
- <td>{'Checked' if alert_flag else 'Unchecked'}</td>
421
- </tr>
422
  """
423
 
424
  html_content += """
425
- </table>
 
 
426
  </body>
427
  </html>
428
  """
@@ -467,7 +547,25 @@ async def generate_scores():
467
  logger.error(f"Error in /generate endpoint: {str(e)}")
468
  raise HTTPException(status_code=500, detail=f"Error generating scores: {str(e)}")
469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
  if __name__ == "__main__":
471
  import uvicorn
472
- uvicorn.run(app, host="0.0.0.0", port=7860)
473
-
 
8
  from datetime import datetime
9
  from fastapi.responses import HTMLResponse
10
  from simple_salesforce import Salesforce
11
+ import requests
12
+ from dotenv import load_dotenv
13
+
14
+ # Load environment variables
15
+ load_dotenv()
16
 
17
  # Set up logging
18
  logging.basicConfig(level=logging.INFO)
 
25
  SF_PASSWORD = os.getenv("SF_PASSWORD")
26
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
27
  SF_DOMAIN = os.getenv("SF_DOMAIN", "login")
28
+ HUGGINGFACE_API_KEY = os.getenv("HUGGINGFACE_API_KEY")
29
+ HUGGINGFACE_API_URL = os.getenv("HUGGINGFACE_API_URL")
30
 
31
  # Validate environment variables
32
  required_env_vars = ["SF_USERNAME", "SF_PASSWORD", "SF_SECURITY_TOKEN"]
 
35
  logger.error(f"Environment variable {var} is not set")
36
  raise ValueError(f"Environment variable {var} is not set")
37
 
38
+ # Optional Hugging Face validation
39
+ USE_HUGGINGFACE = HUGGINGFACE_API_KEY and HUGGINGFACE_API_URL
40
+ if USE_HUGGINGFACE:
41
+ logger.info("Hugging Face integration enabled")
42
+ else:
43
+ logger.info("Hugging Face integration disabled; using local scoring")
44
+
45
  # Connect to Salesforce
46
  try:
47
  sf = Salesforce(
 
72
  # Store vendor logs for display
73
  vendor_logs = []
74
 
75
+ def validate_salesforce_fields():
76
+ """Verify required fields exist in Salesforce"""
77
+ try:
78
+ # Check Vendor_Log__c fields
79
+ vendor_log_desc = sf.Vendor_Log__c.describe()
80
+ required_fields = [
81
+ 'Vendor__c', 'Work_Completion_Percentage__c', 'Quality_Percentage__c',
82
+ 'Incident_Severity__c', 'Work_Completion_Date__c', 'Actual_Completion_Date__c',
83
+ 'Delay_Days__c', 'Project__c'
84
+ ]
85
+ available_fields = [field['name'] for field in vendor_log_desc['fields']]
86
+ for field in required_fields:
87
+ if field not in available_fields:
88
+ logger.error(f"Field {field} not found in Vendor_Log__c")
89
+ raise ValueError(f"Field {field} not found in Vendor_Log__c")
90
+
91
+ # Check Subcontractor_Performance_Score__c fields
92
+ score_desc = sf.Subcontractor_Performance_Score__c.describe()
93
+ required_score_fields = [
94
+ 'Vendor__c', 'Month__c', 'Quality_Score__c', 'Timeliness_Score__c',
95
+ 'Safety_Score__c', 'Communication_Score__c', 'Alert_Flag__c', 'Certification_URL__c'
96
+ ]
97
+ available_score_fields = [field['name'] for field in score_desc['fields']]
98
+ for field in required_score_fields:
99
+ if field not in available_score_fields:
100
+ logger.error(f"Field {field} not found in Subcontractor_Performance_Score__c")
101
+ raise ValueError(f"Field {field} not found in Subcontractor_Performance_Score__c")
102
+ logger.info("All required Salesforce fields validated successfully")
103
+ except Exception as e:
104
+ logger.error(f"Error validating Salesforce fields: {str(e)}")
105
+ raise
106
+
107
+ # Validate fields on startup
108
+ validate_salesforce_fields()
109
+
110
  def fetch_vendor_logs_from_salesforce():
111
  try:
112
  query = """
 
114
  Incident_Severity__c, Work_Completion_Date__c, Actual_Completion_Date__c,
115
  Delay_Days__c, Project__c
116
  FROM Vendor_Log__c
117
+ WHERE Vendor__c != null
118
  """
119
  result = sf.query_all(query)
120
  logs = []
121
  for record in result['records']:
 
 
 
122
  log = VendorLog(
123
  vendorLogId=record['Id'] or "Unknown",
124
  vendorId=record['Name'] or "Unknown",
 
139
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
140
  raise HTTPException(status_code=500, detail=f"Error fetching vendor logs: {str(e)}")
141
 
142
+ def calculate_scores_local(log: VendorLog):
143
  try:
144
  work_completion_percentage = float(log.workDetails)
145
  quality_percentage = float(log.qualityReport)
 
164
  'communicationScore': round(communication_score, 2)
165
  }
166
  except Exception as e:
167
+ logger.error(f"Error calculating local scores: {str(e)}")
168
  raise HTTPException(status_code=500, detail=f"Error calculating scores: {str(e)}")
169
 
170
+ def calculate_scores_huggingface(log: VendorLog):
171
+ try:
172
+ payload = {
173
+ 'vendor_id': log.vendorId,
174
+ 'delay_days': log.delayDays,
175
+ 'work_completion_percentage': float(log.workDetails),
176
+ 'quality_percentage': float(log.qualityReport),
177
+ 'incident_severity': log.incidentLog,
178
+ 'communication_frequency': 5 # Placeholder; adjust as needed
179
+ }
180
+ headers = {
181
+ 'Authorization': f'Bearer {HUGGINGFACE_API_KEY}',
182
+ 'Content-Type': 'application/json'
183
+ }
184
+ response = requests.post(HUGGINGFACE_API_URL, json=payload, headers=headers, timeout=30)
185
+ response.raise_for_status()
186
+ result = response.json()
187
+ return {
188
+ 'qualityScore': round(result.get('quality_score', 0), 2),
189
+ 'timelinessScore': round(result.get('timeliness_score', 0), 2),
190
+ 'safetyScore': round(result.get('safety_score', 0), 2),
191
+ 'communicationScore': round(result.get('communication_score', 0), 2)
192
+ }
193
+ except Exception as e:
194
+ logger.error(f"Error calculating Hugging Face scores: {str(e)}")
195
+ raise HTTPException(status_code=500, detail=f"Error with Hugging Face API: {str(e)}")
196
+
197
+ def calculate_scores(log: VendorLog):
198
+ if USE_HUGGINGFACE:
199
+ return calculate_scores_huggingface(log)
200
+ return calculate_scores_local(log)
201
+
202
  def get_feedback(score: float, metric: str) -> str:
203
  try:
204
  if score >= 90:
 
229
 
230
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
231
  try:
232
+ filename = f'report_{vendor_id}_{datetime.now().strftime("%Y%m%d%H%M%S")}.pdf'
233
  c = canvas.Canvas(filename, pagesize=letter)
234
  c.setFont('Helvetica', 12)
235
  c.drawString(100, 750, 'Subcontractor Performance Report')
 
300
  pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
301
 
302
  # Update Subcontractor_Performance_Score__c with PDF URL
 
303
  sf.Subcontractor_Performance_Score__c.update(score_record_id, {
304
  'Certification_URL__c': pdf_url
305
  })
 
312
  async def score_vendor(log: VendorLog, authorization: str = Header(None)):
313
  try:
314
  logger.info(f"Received Vendor Log: {log}")
 
 
 
 
315
  scores = calculate_scores(log)
316
  pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
317
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
 
381
  <html>
382
  <head>
383
  <title>Subcontractor Performance Score App</title>
384
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
385
  <style>
386
+ body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f9; }
387
+ .container { max-width: 1200px; }
388
  table { width: 100%; border-collapse: collapse; margin-top: 20px; }
389
+ th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
390
+ th { background-color: #007bff; color: white; }
391
+ h1, h2 { text-align: center; color: #333; }
392
  .generate-btn {
393
+ display: block; margin: 20px auto; padding: 10px 20px;
394
+ background-color: #4CAF50; color: white; border: none;
395
+ border-radius: 5px; cursor: pointer; font-size: 16px;
 
 
 
 
 
 
396
  }
397
  .generate-btn:hover { background-color: #45a049; }
398
+ .table-striped tbody tr:nth-of-type(odd) { background-color: #f9f9f9; }
399
  </style>
400
  <script>
401
  async function generateScores() {
 
413
  </script>
414
  </head>
415
  <body>
416
+ <div class="container">
417
+ <h1>SUBCONTRACTOR PERFORMANCE SCORE APP</h1>
418
+ <h2>VENDOR LOGS SUBMISSION</h2>
419
+ <table class="table table-striped">
420
+ <thead>
421
+ <tr>
422
+ <th>Vendor ID</th>
423
+ <th>Vendor Log Name</th>
424
+ <th>Project</th>
425
+ <th>Work Completion %</th>
426
+ <th>Quality %</th>
427
+ <th>Incident Severity</th>
428
+ <th>Work Completion Date</th>
429
+ <th>Actual Completion Date</th>
430
+ <th>Delay Days</th>
431
+ </tr>
432
+ </thead>
433
+ <tbody>
434
  """
435
 
436
  if not vendor_logs:
437
  html_content += """
438
+ <tr>
439
+ <td colspan="9" style="text-align: center;">No vendor logs available</td>
440
+ </tr>
441
  """
442
  else:
443
  for log in vendor_logs:
444
  html_content += f"""
445
+ <tr>
446
+ <td>{log['vendorId']}</td>
447
+ <td>{log['vendorLogName']}</td>
448
+ <td>{log['project']}</td>
449
+ <td>{log['workDetails']}</td>
450
+ <td>{log['qualityReport']}</td>
451
+ <td>{log['incidentLog']}</td>
452
+ <td>{log['workCompletionDate']}</td>
453
+ <td>{log['actualCompletionDate']}</td>
454
+ <td>{log['delayDays']}</td>
455
+ </tr>
456
  """
457
 
458
  html_content += """
459
+ </tbody>
460
+ </table>
461
+ <button class="generate-btn" onclick="generateScores()">Generate Scores</button>
462
+ <h2>SUBCONTRACTOR PERFORMANCE SCORES</h2>
463
+ <table class="table table-striped">
464
+ <thead>
465
+ <tr>
466
+ <th>Vendor ID</th>
467
+ <th>Vendor Log Name</th>
468
+ <th>Project</th>
469
+ <th>Quality Score</th>
470
+ <th>Timeliness Score</th>
471
+ <th>Safety Score</th>
472
+ <th>Communication Score</th>
473
+ <th>Alert Flag</th>
474
+ </tr>
475
+ </thead>
476
+ <tbody>
477
  """
478
 
479
  if not vendor_logs:
480
  html_content += """
481
+ <tr>
482
+ <td colspan="8" style="text-align: center;">No scores available</td>
483
+ </tr>
484
  """
485
  else:
486
  for log in vendor_logs:
487
  scores = log['scores']
488
  alert_flag = determine_alert_flag(scores, vendor_logs)
489
  html_content += f"""
490
+ <tr>
491
+ <td>{log['vendorId']}</td>
492
+ <td>{log['vendorLogName']}</td>
493
+ <td>{log['project']}</td>
494
+ <td>{scores['qualityScore']}%</td>
495
+ <td>{scores['timelinessScore']}%</td>
496
+ <td>{scores['safetyScore']}%</td>
497
+ <td>{scores['communicationScore']}%</td>
498
+ <td>{'Checked' if alert_flag else 'Unchecked'}</td>
499
+ </tr>
500
  """
501
 
502
  html_content += """
503
+ </tbody>
504
+ </table>
505
+ </div>
506
  </body>
507
  </html>
508
  """
 
547
  logger.error(f"Error in /generate endpoint: {str(e)}")
548
  raise HTTPException(status_code=500, detail=f"Error generating scores: {str(e)}")
549
 
550
+ @app.get('/health')
551
+ async def health_check():
552
+ try:
553
+ # Test Salesforce connection
554
+ result = sf.query("SELECT Id FROM Vendor_Log__c LIMIT 1")
555
+ return {
556
+ "status": "healthy",
557
+ "salesforce_connection": "success",
558
+ "vendor_log_count": result['totalSize'],
559
+ "huggingface_enabled": USE_HUGGINGFACE
560
+ }
561
+ except Exception as e:
562
+ logger.error(f"Health check failed: {str(e)}")
563
+ return {
564
+ "status": "unhealthy",
565
+ "salesforce_connection": f"failed: {str(e)}",
566
+ "huggingface_enabled": USE_HUGGINGFACE
567
+ }
568
+
569
  if __name__ == "__main__":
570
  import uvicorn
571
+ uvicorn.run(app, host="0.0.0.0", port=7860)