Swathi6 commited on
Commit
2d774d8
·
verified ·
1 Parent(s): 894c3a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -73
app.py CHANGED
@@ -8,27 +8,34 @@ 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 to capture errors and debug information
14
- logging.basicConfig(level=logging.INFO)
 
 
 
15
  logger = logging.getLogger(__name__)
16
 
17
  app = FastAPI()
18
 
19
- # Salesforce credentials
20
- SF_USERNAME = os.getenv("SF_USERNAME", "[email protected]")
21
- SF_PASSWORD = os.getenv("SF_PASSWORD", "Internal@1")
22
- SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN", "NbUKcTx45azba5HEdntE9YAh")
23
  SF_DOMAIN = os.getenv("SF_DOMAIN", "login")
24
-
25
- # Verify API key is set
26
  API_KEY = os.getenv("HUGGINGFACE_API_KEY")
27
- if not API_KEY:
28
- logger.error("HUGGINGFACE_API_KEY environment variable not set")
29
- raise ValueError("HUGGINGFACE_API_KEY environment variable not set")
30
 
31
- # Connect to Salesforce
 
 
 
 
 
 
 
 
32
  try:
33
  sf = Salesforce(
34
  username=SF_USERNAME,
@@ -39,9 +46,9 @@ try:
39
  logger.info("Successfully connected to Salesforce")
40
  except Exception as e:
41
  logger.error(f"Failed to connect to Salesforce: {str(e)}")
42
- raise
43
 
44
- # VendorLog model to match Salesforce data
45
  class VendorLog(BaseModel):
46
  vendorLogId: str
47
  vendorId: str
@@ -55,15 +62,58 @@ class VendorLog(BaseModel):
55
  delayDays: int
56
  project: str
57
 
58
- # Store vendor logs for display
59
  vendor_logs = []
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  def fetch_vendor_logs_from_salesforce():
62
  try:
63
  query = """
64
- SELECT Id, Name, Vendor__c, Work_Completion_Percentage__c, Quality_Percentage__c, Incident_Severity__c,
65
- Work_Completion_Date__c, Actual_Completion_Date__c, Delay_Days__c,Project__c
66
-
67
  FROM Vendor_Log__c
68
  """
69
  result = sf.query_all(query)
@@ -73,43 +123,36 @@ def fetch_vendor_logs_from_salesforce():
73
  logger.warning(f"Skipping Vendor_Log__c record with ID {record['Id']} due to missing Vendor__c")
74
  continue
75
  log = VendorLog(
76
- vendorLogId=record['Id'] or "Unknown",
77
- vendorId=record['Name'] or "Unknown",
78
- vendorRecordId=record['Vendor__c'] or "Unknown",
79
- workDetails=str(record['Work_Completion_Percentage__c'] or "0.0"),
80
- qualityReport=str(record['Quality_Percentage__c'] or "0.0"),
81
- incidentLog=record['Incident_Severity__c'] or "None",
82
- workCompletionDate=record['Work_Completion_Date__c'] or "N/A",
83
- actualCompletionDate=record['Actual_Completion_Date__c'] or "N/A",
84
- vendorLogName=record['Name'] or "Unknown",
85
- delayDays=int(record['Delay_Days__c'] or 0),
86
- project=record['Project__c'] or "Unknown"
87
  )
88
  logs.append(log)
 
89
  return logs
90
  except Exception as e:
91
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
92
- raise
93
 
94
  def calculate_scores(log: VendorLog):
95
  try:
96
- work_completion_percentage = float(log.workDetails)
97
- quality_percentage = float(log.qualityReport)
98
 
99
- # Quality Score: Directly use the quality percentage
100
  quality_score = quality_percentage
101
-
102
- # Timeliness Score: Based on delay days
103
  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
104
-
105
- # Safety Score: Based on incident severity
106
  severity_map = {'None': 100.0, 'Low': 80.0, 'Minor': 80.0, 'Medium': 50.0, 'High': 20.0}
107
  safety_score = severity_map.get(log.incidentLog, 100.0)
108
-
109
- # Communication Score: Weighted average of other scores
110
  communication_score = (quality_score * 0.33 + timeliness_score * 0.33 + safety_score * 0.33)
111
 
112
- # Removed finalScore calculation since Final_Score__c is a Formula field
113
  return {
114
  'qualityScore': round(quality_score, 2),
115
  'timelinessScore': round(timeliness_score, 2),
@@ -118,7 +161,7 @@ def calculate_scores(log: VendorLog):
118
  }
119
  except Exception as e:
120
  logger.error(f"Error calculating scores: {str(e)}")
121
- raise
122
 
123
  def get_feedback(score: float, metric: str) -> str:
124
  try:
@@ -146,11 +189,11 @@ def get_feedback(score: float, metric: str) -> str:
146
  return "Poor: Communication issues detected"
147
  except Exception as e:
148
  logger.error(f"Error generating feedback: {str(e)}")
149
- raise
150
 
151
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
152
  try:
153
- filename = f'report_{vendor_id}.pdf'
154
  c = canvas.Canvas(filename, pagesize=letter)
155
  c.setFont('Helvetica', 12)
156
  c.drawString(100, 750, 'Subcontractor Performance Report')
@@ -160,7 +203,6 @@ def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
160
  c.drawString(100, 670, f'Timeliness Score: {scores["timelinessScore"]}% ({get_feedback(scores["timelinessScore"], "Timeliness")})')
161
  c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
162
  c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
163
- # Removed Final Score from PDF since it's a Formula field
164
  c.save()
165
 
166
  with open(filename, 'rb') as f:
@@ -169,26 +211,23 @@ def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
169
  return pdf_content
170
  except Exception as e:
171
  logger.error(f"Error generating PDF: {str(e)}")
172
- raise
173
 
174
  def determine_alert_flag(scores: dict, all_logs: list):
175
  try:
176
  if not all_logs:
177
  return False
178
- # Since finalScore is a Formula field, we'll need to fetch it from Salesforce or adjust logic
179
- # For now, we'll base the alert on the average of other scores
180
- avg_score = (scores['qualityScore'] + scores['timelinessScore'] + scores['safetyScore'] + scores['communicationScore']) / 4
181
  if avg_score < 50:
182
  return True
183
- lowest_avg = min([(log['scores']['qualityScore'] + log['scores']['timelinessScore'] + log['scores']['safetyScore'] + log['scores']['communicationScore']) / 4 for log in all_logs])
184
  return avg_score == lowest_avg
185
  except Exception as e:
186
  logger.error(f"Error determining alert flag: {str(e)}")
187
- raise
188
 
189
  def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes, alert_flag: bool):
190
  try:
191
- # Step 1: Create the Subcontractor_Performance_Score__c record without Final_Score__c
192
  score_record = sf.Subcontractor_Performance_Score__c.create({
193
  'Vendor_Log__c': log.vendorLogId,
194
  'Vendor__c': log.vendorRecordId,
@@ -197,12 +236,10 @@ def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes,
197
  'Safety_Score__c': scores['safetyScore'],
198
  'Communication_Score__c': scores['communicationScore'],
199
  'Alert_Flag__c': alert_flag
200
- # Removed Final_Score__c since it's a Formula field
201
  })
202
  score_record_id = score_record['id']
203
  logger.info(f"Successfully created Subcontractor_Performance_Score__c record with ID: {score_record_id}")
204
 
205
- # Step 2: Upload the PDF as a ContentVersion
206
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
207
  content_version = sf.ContentVersion.create({
208
  'Title': f'Performance_Report_{log.vendorId}',
@@ -210,25 +247,19 @@ def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes,
210
  'VersionData': pdf_base64,
211
  'FirstPublishLocationId': score_record_id
212
  })
213
- logger.info(f"Successfully uploaded PDF as ContentVersion for Vendor Log ID: {log.vendorLogId}")
214
-
215
- # Step 3: Get the ContentDocumentId and construct a URL to the file
216
  content_version_id = content_version['id']
217
  content_version_record = sf.query(f"SELECT ContentDocumentId FROM ContentVersion WHERE Id = '{content_version_id}'")
 
 
 
218
  content_document_id = content_version_record['records'][0]['ContentDocumentId']
219
 
220
- # Construct the URL to the file
221
  pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
222
-
223
- # Step 4: Update the Subcontractor_Performance_Score__c record with the PDF URL
224
- sf.Subcontractor_Performance_Score__c.update(score_record_id, {
225
- 'PDF_Link__c': pdf_url
226
- })
227
  logger.info(f"Successfully updated Subcontractor_Performance_Score__c record with PDF URL: {pdf_url}")
228
-
229
  except Exception as e:
230
  logger.error(f"Error storing scores in Salesforce: {str(e)}")
231
- raise
232
 
233
  @app.post('/score')
234
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
@@ -269,6 +300,8 @@ async def score_vendor(log: VendorLog, authorization: str = Header(...)):
269
  'pdfContent': pdf_base64,
270
  'alert': alert_flag
271
  }
 
 
272
  except Exception as e:
273
  logger.error(f"Error in /score endpoint: {str(e)}")
274
  raise HTTPException(status_code=500, detail=f"Error processing vendor log: {str(e)}")
@@ -325,11 +358,15 @@ async def get_dashboard():
325
  </style>
326
  <script>
327
  async function generateScores() {
328
- const response = await fetch('/generate', { method: 'POST' });
329
- if (response.ok) {
330
- window.location.reload();
331
- } else {
332
- alert('Error generating scores');
 
 
 
 
333
  }
334
  }
335
  </script>
@@ -449,11 +486,24 @@ async def generate_scores():
449
  'scores': scores,
450
  'extracted': True
451
  })
 
452
  return {"status": "success"}
453
  except Exception as e:
454
  logger.error(f"Error in /generate endpoint: {str(e)}")
455
  raise HTTPException(status_code=500, detail=f"Error generating scores: {str(e)}")
456
 
457
- if __name__ == "__main__":
458
- import uvicorn
459
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  from datetime import datetime
9
  from fastapi.responses import HTMLResponse
10
  from simple_salesforce import Salesforce
11
+ from dotenv import load_dotenv
12
+ from datasets import load_dataset # For Hugging Face
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")
26
+ SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
27
  SF_DOMAIN = os.getenv("SF_DOMAIN", "login")
 
 
28
  API_KEY = os.getenv("HUGGINGFACE_API_KEY")
 
 
 
29
 
30
+ # Validate environment variables
31
+ required_env_vars = ["SF_USERNAME", "SF_PASSWORD", "SF_SECURITY_TOKEN", "HUGGINGFACE_API_KEY"]
32
+ for var in required_env_vars:
33
+ if not os.getenv(var):
34
+ logger.error(f"Environment variable {var} is not set")
35
+ raise ValueError(f"Environment variable {var} is not set")
36
+
37
+ # Salesforce connection
38
+ sf = None
39
  try:
40
  sf = Salesforce(
41
  username=SF_USERNAME,
 
46
  logger.info("Successfully connected to Salesforce")
47
  except Exception as e:
48
  logger.error(f"Failed to connect to Salesforce: {str(e)}")
49
+ raise RuntimeError(f"Cannot connect to Salesforce: {str(e)}")
50
 
51
+ # VendorLog model
52
  class VendorLog(BaseModel):
53
  vendorLogId: str
54
  vendorId: str
 
62
  delayDays: int
63
  project: str
64
 
65
+ # Store vendor logs
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 = [
73
+ 'Vendor__c', 'Work_Completion_Percentage__c', 'Quality_Percentage__c',
74
+ 'Incident_Severity__c', 'Work_Completion_Date__c', 'Actual_Completion_Date__c',
75
+ 'Delay_Days__c', 'Project__c'
76
+ ]
77
+ for field in required_vendor_fields:
78
+ if field not in vendor_log_fields:
79
+ logger.error(f"Field {field} not found in Vendor_Log__c")
80
+ raise ValueError(f"Field {field} not found in Vendor_Log__c")
81
+
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."""
101
+ try:
102
+ os.environ["HUGGINGFACE_TOKEN"] = API_KEY
103
+ dataset = load_dataset(dataset_name)
104
+ logger.info(f"Successfully fetched dataset: {dataset_name}")
105
+ records = [record for record in dataset['train']] # Assuming 'train' split
106
+ return records[:10] # Limit to 10 records for demonstration
107
+ except Exception as e:
108
+ logger.error(f"Error fetching Hugging Face dataset {dataset_name}: {str(e)}")
109
+ return []
110
+
111
  def fetch_vendor_logs_from_salesforce():
112
  try:
113
  query = """
114
+ SELECT Id, Name, Vendor__c, Work_Completion_Percentage__c, Quality_Percentage__c,
115
+ Incident_Severity__c, Work_Completion_Date__c, Actual_Completion_Date__c,
116
+ Delay_Days__c, Project__c
117
  FROM Vendor_Log__c
118
  """
119
  result = sf.query_all(query)
 
123
  logger.warning(f"Skipping Vendor_Log__c record with ID {record['Id']} due to missing Vendor__c")
124
  continue
125
  log = VendorLog(
126
+ vendorLogId=record.get('Id', 'Unknown'),
127
+ vendorId=record.get('Name', 'Unknown'),
128
+ vendorRecordId=record.get('Vendor__c', 'Unknown'),
129
+ workDetails=str(record.get('Work_Completion_Percentage__c', 0.0)),
130
+ qualityReport=str(record.get('Quality_Percentage__c', 0.0)),
131
+ incidentLog=record.get('Incident_Severity__c', 'None'),
132
+ workCompletionDate=record.get('Work_Completion_Date__c', 'N/A'),
133
+ actualCompletionDate=record.get('Actual_Completion_Date__c', 'N/A'),
134
+ vendorLogName=record.get('Name', 'Unknown'),
135
+ delayDays=int(record.get('Delay_Days__c', 0)),
136
+ project=record.get('Project__c', 'Unknown')
137
  )
138
  logs.append(log)
139
+ logger.info(f"Fetched {len(logs)} vendor logs")
140
  return logs
141
  except Exception as e:
142
  logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
143
+ raise HTTPException(status_code=500, detail=f"Error fetching vendor logs: {str(e)}")
144
 
145
  def calculate_scores(log: VendorLog):
146
  try:
147
+ work_completion_percentage = float(log.workDetails or 0.0)
148
+ quality_percentage = float(log.qualityReport or 0.0)
149
 
 
150
  quality_score = quality_percentage
 
 
151
  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
 
 
152
  severity_map = {'None': 100.0, 'Low': 80.0, 'Minor': 80.0, 'Medium': 50.0, 'High': 20.0}
153
  safety_score = severity_map.get(log.incidentLog, 100.0)
 
 
154
  communication_score = (quality_score * 0.33 + timeliness_score * 0.33 + safety_score * 0.33)
155
 
 
156
  return {
157
  'qualityScore': round(quality_score, 2),
158
  'timelinessScore': round(timeliness_score, 2),
 
161
  }
162
  except Exception as e:
163
  logger.error(f"Error calculating scores: {str(e)}")
164
+ raise HTTPException(status_code=500, detail=f"Error calculating scores: {str(e)}")
165
 
166
  def get_feedback(score: float, metric: str) -> str:
167
  try:
 
189
  return "Poor: Communication issues detected"
190
  except Exception as e:
191
  logger.error(f"Error generating feedback: {str(e)}")
192
+ return "Feedback unavailable"
193
 
194
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
195
  try:
196
+ filename = f'report_{vendor_id}_{datetime.now().strftime("%Y%m%d%H%M%S")}.pdf'
197
  c = canvas.Canvas(filename, pagesize=letter)
198
  c.setFont('Helvetica', 12)
199
  c.drawString(100, 750, 'Subcontractor Performance Report')
 
203
  c.drawString(100, 670, f'Timeliness Score: {scores["timelinessScore"]}% ({get_feedback(scores["timelinessScore"], "Timeliness")})')
204
  c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
205
  c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
 
206
  c.save()
207
 
208
  with open(filename, 'rb') as f:
 
211
  return pdf_content
212
  except Exception as e:
213
  logger.error(f"Error generating PDF: {str(e)}")
214
+ raise HTTPException(status_code=500, detail=f"Error generating PDF: {str(e)}")
215
 
216
  def determine_alert_flag(scores: dict, all_logs: list):
217
  try:
218
  if not all_logs:
219
  return False
220
+ avg_score = sum(scores.values()) / 4
 
 
221
  if avg_score < 50:
222
  return True
223
+ lowest_avg = min([sum(log['scores'].values()) / 4 for log in all_logs], default=avg_score)
224
  return avg_score == lowest_avg
225
  except Exception as e:
226
  logger.error(f"Error determining alert flag: {str(e)}")
227
+ return False
228
 
229
  def store_scores_in_salesforce(log: VendorLog, scores: dict, pdf_content: bytes, alert_flag: bool):
230
  try:
 
231
  score_record = sf.Subcontractor_Performance_Score__c.create({
232
  'Vendor_Log__c': log.vendorLogId,
233
  'Vendor__c': log.vendorRecordId,
 
236
  'Safety_Score__c': scores['safetyScore'],
237
  'Communication_Score__c': scores['communicationScore'],
238
  'Alert_Flag__c': alert_flag
 
239
  })
240
  score_record_id = score_record['id']
241
  logger.info(f"Successfully created Subcontractor_Performance_Score__c record with ID: {score_record_id}")
242
 
 
243
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
244
  content_version = sf.ContentVersion.create({
245
  'Title': f'Performance_Report_{log.vendorId}',
 
247
  'VersionData': pdf_base64,
248
  'FirstPublishLocationId': score_record_id
249
  })
 
 
 
250
  content_version_id = content_version['id']
251
  content_version_record = sf.query(f"SELECT ContentDocumentId FROM ContentVersion WHERE Id = '{content_version_id}'")
252
+ if content_version_record['totalSize'] == 0:
253
+ logger.error(f"No ContentVersion for ID: {content_version_id}")
254
+ raise ValueError("Failed to retrieve ContentDocumentId")
255
  content_document_id = content_version_record['records'][0]['ContentDocumentId']
256
 
 
257
  pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_document_id}"
258
+ sf.Subcontractor_Performance_Score__c.update(score_record_id, {'PDF_Link__c': pdf_url})
 
 
 
 
259
  logger.info(f"Successfully updated Subcontractor_Performance_Score__c record with PDF URL: {pdf_url}")
 
260
  except Exception as e:
261
  logger.error(f"Error storing scores in Salesforce: {str(e)}")
262
+ raise HTTPException(status_code=500, detail=f"Error storing scores: {str(e)}")
263
 
264
  @app.post('/score')
265
  async def score_vendor(log: VendorLog, authorization: str = Header(...)):
 
300
  'pdfContent': pdf_base64,
301
  'alert': alert_flag
302
  }
303
+ except HTTPException as e:
304
+ raise
305
  except Exception as e:
306
  logger.error(f"Error in /score endpoint: {str(e)}")
307
  raise HTTPException(status_code=500, detail=f"Error processing vendor log: {str(e)}")
 
358
  </style>
359
  <script>
360
  async function generateScores() {
361
+ try {
362
+ const response = await fetch('/generate', { method: 'POST' });
363
+ if (response.ok) {
364
+ window.location.reload();
365
+ } else {
366
+ alert('Error generating scores');
367
+ }
368
+ } catch (error) {
369
+ alert('Error: ' + error.message);
370
  }
371
  }
372
  </script>
 
486
  'scores': scores,
487
  'extracted': True
488
  })
489
+ logger.info(f"Generated scores for {len(vendor_logs)} logs")
490
  return {"status": "success"}
491
  except Exception as e:
492
  logger.error(f"Error in /generate endpoint: {str(e)}")
493
  raise HTTPException(status_code=500, detail=f"Error generating scores: {str(e)}")
494
 
495
+ @app.get('/huggingface-records')
496
+ async def get_huggingface_records():
497
+ """Fetch and return Hugging Face dataset records."""
498
+ try:
499
+ records = fetch_huggingface_records()
500
+ if not records:
501
+ raise HTTPException(status_code=404, detail="No records fetched from Hugging Face")
502
+ return {"records": records}
503
+ except Exception as e:
504
+ logger.error(f"Error fetching Hugging Face records: {str(e)}")
505
+ raise HTTPException(status_code=500, detail=f"Failed to fetch Hugging Face records: {str(e)}")
506
+
507
+ @app.get('/debug')
508
+ async def debug_info():
509
+ """Return