Swathi6 commited on
Commit
9679a08
·
verified ·
1 Parent(s): d16892e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -32
app.py CHANGED
@@ -11,15 +11,15 @@ from simple_salesforce import Salesforce
11
  from dotenv import load_dotenv
12
  from datasets import load_dataset
13
 
14
- # Load environment variables
15
- load_dotenv()
16
-
17
- # Set up logging
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
  logger = logging.getLogger(__name__)
20
 
21
  app = FastAPI()
22
 
 
 
 
23
  # Environment variables
24
  SF_USERNAME = os.getenv("SF_USERNAME")
25
  SF_PASSWORD = os.getenv("SF_PASSWORD")
@@ -66,7 +66,7 @@ class VendorLog(BaseModel):
66
  vendor_logs = []
67
 
68
  def validate_salesforce_fields():
69
- """Validate required Salesforce fields"""
70
  try:
71
  vendor_log_fields = [f['name'] for f in sf.Vendor_Log__c.describe()['fields']]
72
  required_vendor_fields = [
@@ -82,19 +82,24 @@ def validate_salesforce_fields():
82
  score_fields = [f['name'] for f in sf.Subcontractor_Performance_Score__c.describe()['fields']]
83
  required_score_fields = [
84
  'Vendor_Log__c', 'Vendor__c', 'Quality_Score__c', 'Timeliness_Score__c',
85
- 'Safety_Score__c', 'Communication_Score__c', 'Alert_Flag__c', 'PDF_Link__c'
86
  ]
 
87
  for field in required_score_fields:
88
  if field not in score_fields:
89
  logger.error(f"Field {field} not found in Subcontractor_Performance_Score__c")
90
  raise ValueError(f"Field {field} not found in Subcontractor_Performance_Score__c")
 
 
 
91
  logger.info("Salesforce fields validated successfully")
 
92
  except Exception as e:
93
  logger.error(f"Error validating Salesforce fields: {str(e)}")
94
  raise
95
 
96
- # Validate fields on startup
97
- validate_salesforce_fields()
98
 
99
  def fetch_huggingface_records(dataset_name: str = "imdb"):
100
  """Fetch records from a Hugging Face dataset."""
@@ -109,7 +114,7 @@ def fetch_huggingface_records(dataset_name: str = "imdb"):
109
  return []
110
 
111
  def fetch_vendor_logs_from_salesforce():
112
- """Fetch vendor logs from Salesforce with null handling."""
113
  try:
114
  query = """
115
  SELECT Id, Name, Vendor__c, Work_Completion_Percentage__c, Quality_Percentage__c,
@@ -120,41 +125,62 @@ def fetch_vendor_logs_from_salesforce():
120
  result = sf.query_all(query)
121
  logs = []
122
  for record in result['records']:
123
- if not record['Vendor__c']:
124
- logger.warning(f"Skipping Vendor_Log__c record with ID {record['Id']} due to missing Vendor__c")
 
125
  continue
126
  # Handle null values for all fields
127
  delay_days = record.get('Delay_Days__c', 0)
128
  if delay_days is None:
129
- logger.warning(f"Delay_Days__c is null for record ID {record['Id']}, defaulting to 0")
130
  delay_days = 0
131
  work_completion = record.get('Work_Completion_Percentage__c', 0.0)
132
  if work_completion is None:
133
- logger.warning(f"Work_Completion_Percentage__c is null for record ID {record['Id']}, defaulting to 0.0")
134
  work_completion = 0.0
135
  quality_percentage = record.get('Quality_Percentage__c', 0.0)
136
  if quality_percentage is None:
137
- logger.warning(f"Quality_Percentage__c is null for record ID {record['Id']}, defaulting to 0.0")
138
  quality_percentage = 0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  log = VendorLog(
140
- vendorLogId=record.get('Id', 'Unknown'),
141
- vendorId=record.get('Name', 'Unknown'),
142
- vendorRecordId=record.get('Vendor__c', 'Unknown'),
143
  workDetails=str(work_completion),
144
  qualityReport=str(quality_percentage),
145
- incidentLog=record.get('Incident_Severity__c', 'None'),
146
- workCompletionDate=record.get('Work_Completion_Date__c', 'N/A'),
147
- actualCompletionDate=record.get('Actual_Completion_Date__c', 'N/A'),
148
- vendorLogName=record.get('Name', 'Unknown'),
149
  delayDays=int(delay_days),
150
- project=record.get('Project__c', 'Unknown')
151
  )
152
  logs.append(log)
153
  logger.info(f"Fetched {len(logs)} vendor logs")
154
  return logs
155
  except Exception as e:
156
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
157
- raise HTTPException(status_code=500, detail=f"Error fetching vendor logs: {str(e)}")
158
 
159
  def calculate_scores(log: VendorLog):
160
  """Calculate vendor performance scores."""
@@ -175,8 +201,13 @@ def calculate_scores(log: VendorLog):
175
  'communicationScore': round(communication_score, 2)
176
  }
177
  except Exception as e:
178
- logger.error(f"Error calculating scores: {str(e)}")
179
- raise HTTPException(status_code=500, detail=f"Error calculating scores: {str(e)}")
 
 
 
 
 
180
 
181
  def get_feedback(score: float, metric: str) -> str:
182
  """Generate feedback based on score and metric."""
@@ -227,7 +258,7 @@ def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
227
  os.remove(filename)
228
  return pdf_content
229
  except Exception as e:
230
- logger.error(f"Error generating PDF: {str(e)}")
231
  raise HTTPException(status_code=500, detail=f"Error generating PDF: {str(e)}")
232
 
233
  def determine_alert_flag(scores: dict, all_logs: list):
@@ -273,11 +304,14 @@ def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes,
273
  raise ValueError("Failed to retrieve ContentDocumentId")
274
  content_document_id = content_version_record['records'][0]['ContentDocumentId']
275
 
276
- pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
277
- sf.Subcontractor_Performance_Score__c.update(score_record_id, {'PDF_Link__c': pdf_url})
278
- logger.info(f"Updated Subcontractor_Performance_Score__c record with PDF URL: {pdf_url}")
 
 
 
279
  except Exception as e:
280
- logger.error(f"Error storing scores in Salesforce: {str(e)}")
281
  raise HTTPException(status_code=500, detail=f"Error storing scores: {str(e)}")
282
 
283
  @app.post('/score')
@@ -336,7 +370,6 @@ async def get_dashboard():
336
  if not any(existing_log['vendorLogId'] == log.vendorLogId for existing_log in vendor_logs):
337
  scores = calculate_scores(log)
338
  pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
339
- pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
340
  alert_flag = determine_alert_flag(scores, vendor_logs)
341
  store_scores_in_salesforce(log, scores, pdf_content, alert_flag)
342
  vendor_logs.append({
@@ -360,4 +393,201 @@ async def get_dashboard():
360
  <title>Subcontractor Performance Score App</title>
361
  <style>
362
  body { font-family: Arial, sans-serif; margin: 20px; }
363
- table { width: 100%; border-collapse: collapse; margin-top: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  from dotenv import load_dotenv
12
  from datasets import load_dataset
13
 
14
+ # Configure logging
 
 
 
15
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
  logger = logging.getLogger(__name__)
17
 
18
  app = FastAPI()
19
 
20
+ # Load environment variables
21
+ load_dotenv()
22
+
23
  # Environment variables
24
  SF_USERNAME = os.getenv("SF_USERNAME")
25
  SF_PASSWORD = os.getenv("SF_PASSWORD")
 
66
  vendor_logs = []
67
 
68
  def validate_salesforce_fields():
69
+ """Validate required Salesforce fields, log warnings if optional fields are missing."""
70
  try:
71
  vendor_log_fields = [f['name'] for f in sf.Vendor_Log__c.describe()['fields']]
72
  required_vendor_fields = [
 
82
  score_fields = [f['name'] for f in sf.Subcontractor_Performance_Score__c.describe()['fields']]
83
  required_score_fields = [
84
  'Vendor_Log__c', 'Vendor__c', 'Quality_Score__c', 'Timeliness_Score__c',
85
+ 'Safety_Score__c', 'Communication_Score__c', 'Alert_Flag__c'
86
  ]
87
+ optional_score_fields = ['PDF_Link__c']
88
  for field in required_score_fields:
89
  if field not in score_fields:
90
  logger.error(f"Field {field} not found in Subcontractor_Performance_Score__c")
91
  raise ValueError(f"Field {field} not found in Subcontractor_Performance_Score__c")
92
+ for field in optional_score_fields:
93
+ if field not in score_fields:
94
+ logger.warning(f"Optional field {field} not found in Subcontractor_Performance_Score__c, skipping related functionality")
95
  logger.info("Salesforce fields validated successfully")
96
+ return 'PDF_Link__c' in score_fields
97
  except Exception as e:
98
  logger.error(f"Error validating Salesforce fields: {str(e)}")
99
  raise
100
 
101
+ # Check if PDF_Link__c exists
102
+ has_pdf_link = validate_salesforce_fields()
103
 
104
  def fetch_huggingface_records(dataset_name: str = "imdb"):
105
  """Fetch records from a Hugging Face dataset."""
 
114
  return []
115
 
116
  def fetch_vendor_logs_from_salesforce():
117
+ """Fetch vendor logs from Salesforce with comprehensive null handling."""
118
  try:
119
  query = """
120
  SELECT Id, Name, Vendor__c, Work_Completion_Percentage__c, Quality_Percentage__c,
 
125
  result = sf.query_all(query)
126
  logs = []
127
  for record in result['records']:
128
+ # Skip records with missing critical fields
129
+ if not record['Vendor__c'] or not record['Id']:
130
+ logger.warning(f"Skipping Vendor_Log__c record with ID {record.get('Id', 'Unknown')} due to missing Vendor__c or Id")
131
  continue
132
  # Handle null values for all fields
133
  delay_days = record.get('Delay_Days__c', 0)
134
  if delay_days is None:
135
+ logger.info(f"Delay_Days__c is null for record ID {record['Id']}, defaulting to 0")
136
  delay_days = 0
137
  work_completion = record.get('Work_Completion_Percentage__c', 0.0)
138
  if work_completion is None:
139
+ logger.info(f"Work_Completion_Percentage__c is null for record ID {record['Id']}, defaulting to 0.0")
140
  work_completion = 0.0
141
  quality_percentage = record.get('Quality_Percentage__c', 0.0)
142
  if quality_percentage is None:
143
+ logger.info(f"Quality_Percentage__c is null for record ID {record['Id']}, defaulting to 0.0")
144
  quality_percentage = 0.0
145
+ incident_severity = record.get('Incident_Severity__c', 'None')
146
+ if incident_severity is None:
147
+ logger.info(f"Incident_Severity__c is null for record ID {record['Id']}, defaulting to 'None'")
148
+ incident_severity = 'None'
149
+ work_completion_date = record.get('Work_Completion_Date__c', 'N/A')
150
+ if work_completion_date is None:
151
+ logger.info(f"Work_Completion_Date__c is null for record ID {record['Id']}, defaulting to 'N/A'")
152
+ work_completion_date = 'N/A'
153
+ actual_completion_date = record.get('Actual_Completion_Date__c', 'N/A')
154
+ if actual_completion_date is None:
155
+ logger.info(f"Actual_Completion_Date__c is null for record ID {record['Id']}, defaulting to 'N/A'")
156
+ actual_completion_date = 'N/A'
157
+ project = record.get('Project__c', 'Unknown')
158
+ if project is None:
159
+ logger.info(f"Project__c is null for record ID {record['Id']}, defaulting to 'Unknown'")
160
+ project = 'Unknown'
161
+ name = record.get('Name', 'Unknown')
162
+ if name is None:
163
+ logger.info(f"Name is null for record ID {record['Id']}, defaulting to 'Unknown'")
164
+ name = 'Unknown'
165
  log = VendorLog(
166
+ vendorLogId=record['Id'],
167
+ vendorId=name,
168
+ vendorRecordId=record['Vendor__c'],
169
  workDetails=str(work_completion),
170
  qualityReport=str(quality_percentage),
171
+ incidentLog=incident_severity,
172
+ workCompletionDate=work_completion_date,
173
+ actualCompletionDate=actual_completion_date,
174
+ vendorLogName=name,
175
  delayDays=int(delay_days),
176
+ project=project
177
  )
178
  logs.append(log)
179
  logger.info(f"Fetched {len(logs)} vendor logs")
180
  return logs
181
  except Exception as e:
182
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
183
+ return [] # Return empty list to prevent dashboard crash
184
 
185
  def calculate_scores(log: VendorLog):
186
  """Calculate vendor performance scores."""
 
201
  'communicationScore': round(communication_score, 2)
202
  }
203
  except Exception as e:
204
+ logger.error(f"Error calculating scores for log {log.vendorLogId}: {str(e)}")
205
+ return {
206
+ 'qualityScore': 0.0,
207
+ 'timelinessScore': 0.0,
208
+ 'safetyScore': 0.0,
209
+ 'communicationScore': 0.0
210
+ }
211
 
212
  def get_feedback(score: float, metric: str) -> str:
213
  """Generate feedback based on score and metric."""
 
258
  os.remove(filename)
259
  return pdf_content
260
  except Exception as e:
261
+ logger.error(f"Error generating PDF for vendor {vendor_id}: {str(e)}")
262
  raise HTTPException(status_code=500, detail=f"Error generating PDF: {str(e)}")
263
 
264
  def determine_alert_flag(scores: dict, all_logs: list):
 
304
  raise ValueError("Failed to retrieve ContentDocumentId")
305
  content_document_id = content_version_record['records'][0]['ContentDocumentId']
306
 
307
+ if has_pdf_link:
308
+ pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
309
+ sf.Subcontractor_Performance_Score__c.update(score_record_id, {'PDF_Link__c': pdf_url})
310
+ logger.info(f"Updated Subcontractor_Performance_Score__c record with PDF URL: {pdf_url}")
311
+ else:
312
+ logger.info(f"Skipping PDF_Link__c update as field is not available")
313
  except Exception as e:
314
+ logger.error(f"Error storing scores in Salesforce for log {log.vendorLogId}: {str(e)}")
315
  raise HTTPException(status_code=500, detail=f"Error storing scores: {str(e)}")
316
 
317
  @app.post('/score')
 
370
  if not any(existing_log['vendorLogId'] == log.vendorLogId for existing_log in vendor_logs):
371
  scores = calculate_scores(log)
372
  pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
 
373
  alert_flag = determine_alert_flag(scores, vendor_logs)
374
  store_scores_in_salesforce(log, scores, pdf_content, alert_flag)
375
  vendor_logs.append({
 
393
  <title>Subcontractor Performance Score App</title>
394
  <style>
395
  body { font-family: Arial, sans-serif; margin: 20px; }
396
+ table { width: 100%; border-collapse: collapse; margin-top: 20px; }
397
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
398
+ th { background-color: #f2f2f2; }
399
+ h1, h2 { text-align: center; }
400
+ .generate-btn {
401
+ display: block;
402
+ margin: 20px auto;
403
+ padding: 10px 20px;
404
+ background-color: #4CAF50;
405
+ color: white;
406
+ border: none;
407
+ border-radius: 5px;
408
+ cursor: pointer;
409
+ font-size: 16px;
410
+ }
411
+ .generate-btn:hover { background-color: #45a049; }
412
+ </style>
413
+ <script>
414
+ async function generateScores() {
415
+ try {
416
+ const response = await fetch('/generate', { method: 'POST' });
417
+ if (response.ok) {
418
+ window.location.reload();
419
+ } else {
420
+ alert('Error generating scores');
421
+ }
422
+ } catch (error) {
423
+ alert('Error: ' + error.message);
424
+ }
425
+ }
426
+ </script>
427
+ </head>
428
+ <body>
429
+ <h1>SUBCONTRACTOR PERFORMANCE SCORE APP GENERATOR</h1>
430
+ <h2>VENDOR LOGS SUBMISSION</h2>
431
+ <table>
432
+ <tr>
433
+ <th>Vendor ID</th>
434
+ <th>Vendor Log Name</th>
435
+ <th>Project</th>
436
+ <th>Work Completion Percentage</th>
437
+ <th>Quality Percentage</th>
438
+ <th>Incident Severity</th>
439
+ <th>Work Completion Date</th>
440
+ <th>Actual Completion Date</th>
441
+ <th>Delay Days</th>
442
+ </tr>
443
+ """
444
+
445
+ if not vendor_logs:
446
+ html_content += """
447
+ <tr>
448
+ <td colspan="9" style="text-align: center;">No vendor logs available</td>
449
+ </tr>
450
+ """
451
+ else:
452
+ for log in vendor_logs:
453
+ html_content += f"""
454
+ <tr>
455
+ <td>{log['vendorId']}</td>
456
+ <td>{log['vendorLogName']}</td>
457
+ <td>{log['project']}</td>
458
+ <td>{log['workDetails']}</td>
459
+ <td>{log['qualityReport']}</td>
460
+ <td>{log['incidentLog']}</td>
461
+ <td>{log['workCompletionDate']}</td>
462
+ <td>{log['actualCompletionDate']}</td>
463
+ <td>{log['delayDays']}</td>
464
+ </tr>
465
+ """
466
+
467
+ html_content += """
468
+ </table>
469
+ <button class="generate-btn" onclick="generateScores()">Generate</button>
470
+ <h2>SUBCONTRACTOR PERFORMANCE SCORES</h2>
471
+ <table>
472
+ <tr>
473
+ <th>Vendor ID</th>
474
+ <th>Vendor Log Name</th>
475
+ <th>Project</th>
476
+ <th>Quality Score</th>
477
+ <th>Timeliness Score</th>
478
+ <th>Safety Score</th>
479
+ <th>Communication Score</th>
480
+ <th>Alert Flag</th>
481
+ </tr>
482
+ """
483
+
484
+ if not vendor_logs:
485
+ html_content += """
486
+ <tr>
487
+ <td colspan="8" style="text-align: center;">No scores available</td>
488
+ </tr>
489
+ """
490
+ else:
491
+ for log in vendor_logs:
492
+ scores = log['scores']
493
+ alert_flag = determine_alert_flag(scores, vendor_logs)
494
+ html_content += f"""
495
+ <tr>
496
+ <td>{log['vendorId']}</td>
497
+ <td>{log['vendorLogName']}</td>
498
+ <td>{log['project']}</td>
499
+ <td>{scores['qualityScore']}%</td>
500
+ <td>{scores['timelinessScore']}%</td>
501
+ <td>{scores['safetyScore']}%</td>
502
+ <td>{scores['communicationScore']}%</td>
503
+ <td>{'Checked' if alert_flag else 'Unchecked'}</td>
504
+ </tr>
505
+ """
506
+
507
+ html_content += """
508
+ </table>
509
+ </body>
510
+ </html>
511
+ """
512
+ return HTMLResponse(content=html_content)
513
+ except Exception as e:
514
+ logger.error(f"Error in / endpoint: {str(e)}")
515
+ html_content = """
516
+ <html>
517
+ <head><title>Error</title></head>
518
+ <body>
519
+ <h1>Error Loading Dashboard</h1>
520
+ <p>An error occurred while loading the dashboard. Please check the server logs.</p>
521
+ </body>
522
+ </html>
523
+ """
524
+ return HTMLResponse(content=html_content)
525
+
526
+ @app.post('/generate')
527
+ async def generate_scores():
528
+ """Generate scores for all vendor logs."""
529
+ try:
530
+ global vendor_logs
531
+ fetched_logs = fetch_vendor_logs_from_salesforce()
532
+ vendor_logs = []
533
+ for log in fetched_logs:
534
+ scores = calculate_scores(log)
535
+ pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
536
+ alert_flag = determine_alert_flag(scores, vendor_logs)
537
+ store_scores_in_salesforce(log, scores, pdf_content, alert_flag)
538
+ vendor_logs.append({
539
+ 'vendorLogId': log.vendorLogId,
540
+ 'vendorId': log.vendorId,
541
+ 'vendorLogName': log.vendorLogName,
542
+ 'workDetails': log.workDetails,
543
+ 'qualityReport': log.qualityReport,
544
+ 'incidentLog': log.incidentLog,
545
+ 'workCompletionDate': log.workCompletionDate,
546
+ 'actualCompletionDate': log.actualCompletionDate,
547
+ 'delayDays': log.delayDays,
548
+ 'project': log.project,
549
+ 'scores': scores,
550
+ 'extracted': True
551
+ })
552
+ logger.info(f"Generated scores for {len(vendor_logs)} logs")
553
+ return {"status": "success"}
554
+ except Exception as e:
555
+ logger.error(f"Error in /generate endpoint: {str(e)}")
556
+ return {"status": "error", "message": str(e)}
557
+
558
+ @app.get('/huggingface-records')
559
+ async def get_huggingface_records():
560
+ """Fetch and return Hugging Face dataset records."""
561
+ try:
562
+ records = fetch_huggingface_records()
563
+ if not records:
564
+ logger.warning("No records fetched from Hugging Face")
565
+ return {"records": [], "message": "No records available"}
566
+ logger.info(f"Fetched {len(records)} Hugging Face records")
567
+ return {"records": records}
568
+ except Exception as e:
569
+ logger.error(f"Error fetching Hugging Face records: {str(e)}")
570
+ return {"records": [], "message": f"Failed to fetch Hugging Face records: {str(e)}"}
571
+
572
+ @app.get('/debug')
573
+ async def debug_info():
574
+ """Return debug information about Salesforce and Hugging Face."""
575
+ try:
576
+ log_count = sf.query("SELECT COUNT() FROM Vendor_Log__c")['totalSize']
577
+ fields = [f['name'] for f in sf.Vendor_Log__c.describe()['fields']]
578
+ score_fields = [f['name'] for f in sf.Subcontractor_Performance_Score__c.describe()['fields']]
579
+ hf_records = fetch_huggingface_records()
580
+ return {
581
+ "salesforce_connected": True,
582
+ "vendor_log_count": log_count,
583
+ "vendor_log_fields": fields,
584
+ "score_fields": score_fields,
585
+ "huggingface_records_sample": hf_records[:2] # Limit to 2 for brevity
586
+ }
587
+ except Exception as e:
588
+ logger.error(f"Debug error: {str(e)}")
589
+ return {"salesforce_connected": False, "error": str(e)}
590
+
591
+ if __name__ == "__main__":
592
+ import uvicorn
593
+ uvicorn.run(app, host="0.0.0.0", port=7860)