lokesh341 commited on
Commit
2e47d2a
·
verified ·
1 Parent(s): 64c90a4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -162
app.py CHANGED
@@ -1,16 +1,15 @@
1
- from flask import Flask, request, Response, jsonify, render_template
2
  from pydantic import BaseModel
3
  from reportlab.lib.pagesizes import letter
4
  from reportlab.pdfgen import canvas
5
  import base64
6
  import os
7
  import logging
8
- import traceback
9
- import requests
10
  from datetime import datetime
11
  from simple_salesforce import Salesforce
12
 
13
- # Set up logging with more detail
14
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
15
  logger = logging.getLogger(__name__)
16
 
@@ -22,10 +21,6 @@ SF_PASSWORD = "Internal@1"
22
  SF_SECURITY_TOKEN = "NbUKcTx45azba5HEdntE9YAh"
23
  SF_DOMAIN = "login"
24
 
25
- # Hugging Face API details (mocked for now)
26
- HUGGING_FACE_API_URL = "https://api-inference.huggingface.co/models/your-model-name"
27
- HUGGING_FACE_API_TOKEN = "your-hugging-face-api-token"
28
-
29
  # Initialize Salesforce connection
30
  try:
31
  sf = Salesforce(
@@ -36,16 +31,20 @@ try:
36
  )
37
  logger.info("Successfully connected to Salesforce")
38
 
39
- # Debug: Describe the Subcontractor_Performance_Score__c object to log its fields
40
- object_description = sf.Subcontractor_Performance_Score__c.describe()
41
- logger.info("Fields on Subcontractor_Performance_Score__c:")
42
- for field in object_description['fields']:
43
- logger.info(f"Field Name: {field['name']}")
 
 
 
 
44
  except Exception as e:
45
  logger.error(f"Failed to connect to Salesforce: {str(e)}")
46
- raise ValueError("Failed to connect to Salesforce")
47
 
48
- # VendorLog model to match Salesforce data
49
  class VendorLog(BaseModel):
50
  vendorLogId: str
51
  vendorId: str
@@ -62,14 +61,12 @@ class VendorLog(BaseModel):
62
  vendor_logs = []
63
 
64
  def fetch_vendor_logs_from_salesforce():
65
- """Fetch vendor logs from Salesforce to send to Hugging Face."""
66
- try:
67
- # Validate Salesforce session
68
- if not sf.session_id:
69
- logger.error("Salesforce session is invalid")
70
- raise ValueError("Salesforce session is invalid")
71
 
72
- # Use a more flexible query
73
  query = """
74
  SELECT Id, Vendor_Log_Id__c, VendorId__c, Work_Details__c, Quality_Report__c,
75
  Incident_Log__c, Work_Completion_Date__c, Actual_Completion_Date__c,
@@ -80,7 +77,6 @@ def fetch_vendor_logs_from_salesforce():
80
  result = sf.query(query)
81
  logs = []
82
  for record in result['records']:
83
- # Handle missing fields with defaults
84
  log = VendorLog(
85
  vendorLogId=record.get('Vendor_Log_Id__c', 'Unknown'),
86
  vendorId=record.get('VendorId__c', 'Unknown'),
@@ -97,29 +93,48 @@ def fetch_vendor_logs_from_salesforce():
97
  logger.info(f"Fetched {len(logs)} vendor logs from Salesforce")
98
  return logs
99
  except Exception as e:
100
- logger.error(f"Error fetching vendor logs from Salesforce: {str(e)}")
 
 
 
 
 
 
101
  return []
102
 
103
- def calculate_scores_with_hugging_face(log: VendorLog):
104
- """Send data to Hugging Face and get scores (mocked for now)."""
105
  try:
106
- # Prepare data for Hugging Face
107
- payload = {
108
- "workDetails": log.workDetails,
109
- "qualityReport": log.qualityReport,
110
- "incidentLog": log.incidentLog,
111
- "delayDays": log.delayDays
112
- }
113
-
114
- # Mocked Hugging Face API call (replace with actual API call)
115
- # headers = {"Authorization": f"Bearer {HUGGING_FACE_API_TOKEN}"}
116
- # response = requests.post(HUGGING_FACE_API_URL, json=payload, headers=headers)
117
- # if response.status_code != 200:
118
- # logger.error(f"Hugging Face API error: {response.text}")
119
- # raise ValueError("Failed to get scores from Hugging Face")
120
- # scores = response.json()
121
-
122
- # Mocked response for now
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  scores = {
124
  'qualityScore': float(log.qualityReport.replace('% quality', '')),
125
  'timelinessScore': 100.0 if log.delayDays <= 0 else 80.0 if log.delayDays <= 3 else 60.0 if log.delayDays <= 7 else 40.0,
@@ -130,15 +145,18 @@ def calculate_scores_with_hugging_face(log: VendorLog):
130
  scores['communicationScore'] = (scores['qualityScore'] * 0.33 + scores['timelinessScore'] * 0.33 + scores['safetyScore'] * 0.33)
131
  scores['finalScore'] = (scores['qualityScore'] + scores['timelinessScore'] + scores['safetyScore'] + scores['communicationScore']) / 4
132
 
133
- # Round scores
134
  for key in scores:
135
  scores[key] = round(scores[key], 2)
136
-
137
- logger.debug(f"Calculated scores for vendor {log.vendorId}: {scores}")
138
  return scores
139
  except Exception as e:
140
- logger.error(f"Error calculating scores with Hugging Face: {str(e)}")
141
- raise
 
 
 
 
 
 
142
 
143
  def get_feedback(score: float, metric: str) -> str:
144
  try:
@@ -147,31 +165,18 @@ def get_feedback(score: float, metric: str) -> str:
147
  elif score >= 70:
148
  return "Good: Keep up the good work"
149
  elif score >= 50:
150
- if metric == 'Timeliness':
151
- return "Needs Improvement: Maintain schedules to complete tasks on time"
152
- elif metric == 'Safety':
153
- return "Needs Improvement: Implement stricter safety protocols"
154
- elif metric == 'Quality':
155
- return "Needs Improvement: Focus on improving work quality"
156
- else:
157
- return "Needs Improvement: Enhance coordination with project teams"
158
  else:
159
- if metric == 'Timeliness':
160
- return "Poor: Significant delays detected"
161
- elif metric == 'Safety':
162
- return "Poor: Critical safety issues identified"
163
- elif metric == 'Quality':
164
- return "Poor: Quality standards not met"
165
- else:
166
- return "Poor: Communication issues detected"
167
  except Exception as e:
168
  logger.error(f"Error generating feedback: {str(e)}")
169
  return "Feedback unavailable"
170
 
171
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
 
172
  try:
173
- filename = f'report_{vendor_id}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf'
174
- c = canvas.Canvas(filename, pagesize=letter)
175
  c.setFont('Helvetica', 12)
176
  c.drawString(100, 750, 'Subcontractor Performance Report')
177
  c.drawString(100, 730, f'Vendor ID: {vendor_id}')
@@ -181,23 +186,16 @@ def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
181
  c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
182
  c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
183
  c.drawString(100, 610, f'Final Score: {scores["finalScore"]}%')
 
184
  c.save()
185
-
186
- with open(filename, 'rb') as f:
187
- pdf_content = f.read()
188
-
189
- # Clean up the file
190
- try:
191
- os.remove(filename)
192
- except Exception as e:
193
- logger.warning(f"Could not delete temporary PDF file {filename}: {str(e)}")
194
-
195
- return pdf_content
196
  except Exception as e:
197
  logger.error(f"Error generating PDF: {str(e)}")
198
  raise
199
 
200
  def determine_alert_flag(final_score: float, all_logs: list):
 
201
  try:
202
  if not all_logs:
203
  return False
@@ -211,58 +209,32 @@ def determine_alert_flag(final_score: float, all_logs: list):
211
 
212
  @app.route('/score', methods=['POST'])
213
  def score_vendor():
 
214
  try:
215
- # Validate the Authorization header
216
- authorization = request.headers.get('Authorization')
217
- if not authorization:
218
- logger.warning("Authorization header missing in request")
219
- return jsonify({'error': 'Authorization header missing'}), 401
220
-
221
- # Parse the request data
222
  data = request.get_json()
223
  if not data:
224
- logger.warning("Invalid or missing JSON data in request")
225
  return jsonify({'error': 'Invalid request data'}), 400
226
 
227
- # Validate and create VendorLog instance
228
  log = VendorLog(**data)
229
- logger.info(f"Received Vendor Log: {log}")
230
-
231
- # Validate Salesforce session
232
- if not sf.session_id:
233
- logger.error("Salesforce session invalid")
234
- return jsonify({'error': 'Salesforce session invalid'}), 401
235
-
236
- # Calculate scores using Hugging Face
237
- scores = calculate_scores_with_hugging_face(log)
238
 
239
- # Generate PDF
240
- pdf_content = generate_pdf(log.vendorId, log.vendorLogName, scores)
241
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
242
 
243
- # Determine alert flag
244
  alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
245
 
246
- # Store the log and scores in memory
247
  vendor_logs.append({
248
  'vendorLogId': log.vendorLogId,
249
  'vendorId': log.vendorId,
250
  'vendorLogName': log.vendorLogName,
251
- 'workDetails': log.workDetails,
252
- 'qualityReport': log.qualityReport,
253
- 'incidentLog': log.incidentLog,
254
- 'workCompletionDate': log.workCompletionDate,
255
- 'actualCompletionDate': log.actualCompletionDate,
256
- 'delayDays': log.delayDays,
257
- 'project': log.project,
258
  'scores': scores,
259
  'extracted': True
260
  })
261
 
262
- # Save scores and PDF to Salesforce
263
- try:
264
  sf.Subcontractor_Performance_Score__c.create({
265
- 'VendorId__c': log.vendorId, # Updated field name
266
  'Month__c': datetime.now().strftime('%Y-%m-%d'),
267
  'Quality_Score__c': scores['qualityScore'],
268
  'Timeliness_Score__c': scores['timelinessScore'],
@@ -272,76 +244,84 @@ def score_vendor():
272
  'Certification_URL__c': pdf_base64,
273
  'Alert_Flag__c': alert_flag
274
  })
275
- logger.info(f"Successfully saved scores to Salesforce for Vendor Log: {log.vendorLogId}")
276
- except Exception as e:
277
- logger.error(f"Error saving to Salesforce: {str(e)}")
278
- # Continue even if Salesforce save fails
279
 
280
- # Return the response
281
  return jsonify({
282
  'vendorLogId': log.vendorLogId,
283
  'vendorId': log.vendorId,
284
  'vendorLogName': log.vendorLogName,
285
- 'qualityScore': scores['qualityScore'],
286
- 'timelinessScore': scores['timelinessScore'],
287
- 'safetyScore': scores['safetyScore'],
288
- 'communicationScore': scores['communicationScore'],
289
- 'finalScore': scores['finalScore'],
290
  'pdfContent': pdf_base64,
291
  'alert': alert_flag
292
  }), 200
293
  except Exception as e:
294
  logger.error(f"Error in /score endpoint: {str(e)}")
295
- return jsonify({'error': f"Error processing vendor log: {str(e)}"}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
  @app.route('/', methods=['GET'])
298
  def get_dashboard():
 
299
  try:
300
- # Fetch data from Salesforce with updated field name
301
- query = """
302
- SELECT VendorId__c, Month__c, Quality_Score__c, Timeliness_Score__c,
303
- Safety_Score__c, Communication_Score__c, Final_Score__c, Certification_URL__c,
304
- Alert_Flag__c
305
- FROM Subcontractor_Performance_Score__c
306
- WHERE Month__c = '2025-05-01'
307
- """
308
- try:
309
- result = sf.query(query)
310
- except Exception as e:
311
- logger.error(f"Error querying Salesforce: {str(e)}")
312
- # Fallback to empty data to render the dashboard
313
- result = {'records': []}
314
 
 
315
  vendor_logs.clear()
316
- for record in result['records']:
317
- # Handle missing or null scores
318
- scores = {
319
- 'qualityScore': record.get('Quality_Score__c', 0.0) or 0.0,
320
- 'timelinessScore': record.get('Timeliness_Score__c', 0.0) or 0.0,
321
- 'safetyScore': record.get('Safety_Score__c', 0.0) or 0.0,
322
- 'communicationScore': record.get('Communication_Score__c', 0.0) or 0.0,
323
- 'finalScore': record.get('Final_Score__c', 0.0) or 0.0
324
- }
325
  vendor_logs.append({
326
- 'vendorId': record.get('VendorId__c', 'Unknown'),
327
- 'vendorLogName': f"Vendor {record.get('VendorId__c', 'Unknown')}",
328
- 'scores': scores,
 
329
  'extracted': True
330
  })
331
 
332
- # Calculate summary metrics
333
  total_vendors = len(vendor_logs)
334
  performance_alerts = sum(1 for log in vendor_logs if determine_alert_flag(log['scores']['finalScore'], vendor_logs))
335
  top_performers = sum(1 for log in vendor_logs if log['scores']['finalScore'] >= 90)
336
  improving_vendors = sum(1 for log in vendor_logs if log['scores']['finalScore'] >= 70)
337
 
338
- # Sort vendor logs by final score for leaderboard
339
  sorted_logs = sorted(vendor_logs, key=lambda x: x['scores']['finalScore'], reverse=True)
340
- top_logs_data = sorted_logs[:5] # Top 5 for leaderboard
341
- top_performing_logs = sorted_logs[:4] # Top 4 for "Top Performing Vendors" section
342
  alert_logs = [log for log in vendor_logs if determine_alert_flag(log['scores']['finalScore'], vendor_logs)][:3]
343
 
344
- # Prepare data for the leaderboard
345
  top_logs = []
346
  for idx, log in enumerate(top_logs_data, 1):
347
  scores = log['scores']
@@ -360,7 +340,6 @@ def get_dashboard():
360
  'status_text': status_text
361
  })
362
 
363
- # Ensure all variables are defined for the template
364
  template_data = {
365
  'total_vendors': total_vendors,
366
  'performance_alerts': performance_alerts,
@@ -373,15 +352,14 @@ def get_dashboard():
373
  'alert_logs': alert_logs,
374
  'top_performing_logs': top_performing_logs,
375
  'vendor_logs': vendor_logs,
376
- 'sorted_logs': sorted_logs
 
 
377
  }
378
 
379
- logger.info(f"Rendering dashboard with data: {template_data}")
380
  return render_template('dashboard.html', **template_data)
381
  except Exception as e:
382
- error_trace = traceback.format_exc()
383
- logger.error(f"Error in / endpoint: {str(e)}\nStack trace:\n{error_trace}")
384
- # Fallback to render the dashboard with empty data
385
  template_data = {
386
  'total_vendors': 0,
387
  'performance_alerts': 0,
@@ -394,19 +372,20 @@ def get_dashboard():
394
  'alert_logs': [],
395
  'top_performing_logs': [],
396
  'vendor_logs': [],
397
- 'sorted_logs': []
 
 
398
  }
399
- logger.info("Rendering dashboard with fallback data due to error")
400
  return render_template('dashboard.html', **template_data)
401
 
402
  @app.route('/document', methods=['GET'])
403
  def get_document():
 
404
  try:
405
  return render_template('document.html')
406
  except Exception as e:
407
- error_trace = traceback.format_exc()
408
- logger.error(f"Error in /document endpoint: {str(e)}\nStack trace:\n{error_trace}")
409
- return jsonify({'error': f"Error generating document: {str(e)}"}), 500
410
 
411
  if __name__ == "__main__":
412
  app.run(host="0.0.0.0", port=7860, debug=True)
 
1
+ from flask import Flask, request, Response, jsonify, render_template, send_file
2
  from pydantic import BaseModel
3
  from reportlab.lib.pagesizes import letter
4
  from reportlab.pdfgen import canvas
5
  import base64
6
  import os
7
  import logging
8
+ import io
 
9
  from datetime import datetime
10
  from simple_salesforce import Salesforce
11
 
12
+ # Set up logging
13
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
14
  logger = logging.getLogger(__name__)
15
 
 
21
  SF_SECURITY_TOKEN = "NbUKcTx45azba5HEdntE9YAh"
22
  SF_DOMAIN = "login"
23
 
 
 
 
 
24
  # Initialize Salesforce connection
25
  try:
26
  sf = Salesforce(
 
31
  )
32
  logger.info("Successfully connected to Salesforce")
33
 
34
+ # Debug: Describe objects for field verification
35
+ for obj in ['Subcontractor_Performance_Score__c', 'Vendor_Log__c']:
36
+ try:
37
+ description = getattr(sf, obj).describe()
38
+ logger.info(f"Fields on {obj}:")
39
+ for field in description['fields']:
40
+ logger.info(f"Field Name: {field['name']}")
41
+ except Exception as e:
42
+ logger.error(f"Error describing {obj}: {str(e)}")
43
  except Exception as e:
44
  logger.error(f"Failed to connect to Salesforce: {str(e)}")
45
+ sf = None
46
 
47
+ # VendorLog model
48
  class VendorLog(BaseModel):
49
  vendorLogId: str
50
  vendorId: str
 
61
  vendor_logs = []
62
 
63
  def fetch_vendor_logs_from_salesforce():
64
+ """Fetch vendor logs from Salesforce."""
65
+ if not sf:
66
+ logger.error("Salesforce connection not initialized")
67
+ return []
 
 
68
 
69
+ try:
70
  query = """
71
  SELECT Id, Vendor_Log_Id__c, VendorId__c, Work_Details__c, Quality_Report__c,
72
  Incident_Log__c, Work_Completion_Date__c, Actual_Completion_Date__c,
 
77
  result = sf.query(query)
78
  logs = []
79
  for record in result['records']:
 
80
  log = VendorLog(
81
  vendorLogId=record.get('Vendor_Log_Id__c', 'Unknown'),
82
  vendorId=record.get('VendorId__c', 'Unknown'),
 
93
  logger.info(f"Fetched {len(logs)} vendor logs from Salesforce")
94
  return logs
95
  except Exception as e:
96
+ logger.error(f"Error fetching vendor logs: {str(e)}")
97
+ return []
98
+
99
+ def fetch_performance_scores():
100
+ """Fetch performance scores from Salesforce."""
101
+ if not sf:
102
+ logger.error("Salesforce connection not initialized")
103
  return []
104
 
 
 
105
  try:
106
+ query = """
107
+ SELECT VendorId__c, Month__c, Quality_Score__c, Timeliness_Score__c,
108
+ Safety_Score__c, Communication_Score__c, Final_Score__c, Certification_URL__c,
109
+ Alert_Flag__c
110
+ FROM Subcontractor_Performance_Score__c
111
+ WHERE Month__c = '2025-05-01'
112
+ """
113
+ result = sf.query(query)
114
+ scores = []
115
+ for record in result['records']:
116
+ scores.append({
117
+ 'vendorId': record.get('VendorId__c', 'Unknown'),
118
+ 'vendorLogName': f"Vendor {record.get('VendorId__c', 'Unknown')}",
119
+ 'scores': {
120
+ 'qualityScore': record.get('Quality_Score__c', 0.0) or 0.0,
121
+ 'timelinessScore': record.get('Timeliness_Score__c', 0.0) or 0.0,
122
+ 'safetyScore': record.get('Safety_Score__c', 0.0) or 0.0,
123
+ 'communicationScore': record.get('Communication_Score__c', 0.0) or 0.0,
124
+ 'finalScore': record.get('Final_Score__c', 0.0) or 0.0
125
+ },
126
+ 'certification_url': record.get('Certification_URL__c', ''),
127
+ 'alert_flag': record.get('Alert_Flag__c', False)
128
+ })
129
+ logger.info(f"Fetched {len(scores)} performance scores from Salesforce")
130
+ return scores
131
+ except Exception as e:
132
+ logger.error(f"Error fetching performance scores: {str(e)}")
133
+ return []
134
+
135
+ def calculate_scores(log: VendorLog):
136
+ """Calculate scores for a vendor log."""
137
+ try:
138
  scores = {
139
  'qualityScore': float(log.qualityReport.replace('% quality', '')),
140
  'timelinessScore': 100.0 if log.delayDays <= 0 else 80.0 if log.delayDays <= 3 else 60.0 if log.delayDays <= 7 else 40.0,
 
145
  scores['communicationScore'] = (scores['qualityScore'] * 0.33 + scores['timelinessScore'] * 0.33 + scores['safetyScore'] * 0.33)
146
  scores['finalScore'] = (scores['qualityScore'] + scores['timelinessScore'] + scores['safetyScore'] + scores['communicationScore']) / 4
147
 
 
148
  for key in scores:
149
  scores[key] = round(scores[key], 2)
 
 
150
  return scores
151
  except Exception as e:
152
+ logger.error(f"Error calculating scores: {str(e)}")
153
+ return {
154
+ 'qualityScore': 0.0,
155
+ 'timelinessScore': 0.0,
156
+ 'safetyScore': 0.0,
157
+ 'communicationScore': 0.0,
158
+ 'finalScore': 0.0
159
+ }
160
 
161
  def get_feedback(score: float, metric: str) -> str:
162
  try:
 
165
  elif score >= 70:
166
  return "Good: Keep up the good work"
167
  elif score >= 50:
168
+ return f"Needs Improvement: Enhance {metric.lower()} performance"
 
 
 
 
 
 
 
169
  else:
170
+ return f"Poor: Critical issues in {metric.lower()}"
 
 
 
 
 
 
 
171
  except Exception as e:
172
  logger.error(f"Error generating feedback: {str(e)}")
173
  return "Feedback unavailable"
174
 
175
  def generate_pdf(vendor_id: str, vendor_log_name: str, scores: dict):
176
+ """Generate a PDF report for a vendor."""
177
  try:
178
+ buffer = io.BytesIO()
179
+ c = canvas.Canvas(buffer, pagesize=letter)
180
  c.setFont('Helvetica', 12)
181
  c.drawString(100, 750, 'Subcontractor Performance Report')
182
  c.drawString(100, 730, f'Vendor ID: {vendor_id}')
 
186
  c.drawString(100, 650, f'Safety Score: {scores["safetyScore"]}% ({get_feedback(scores["safetyScore"], "Safety")})')
187
  c.drawString(100, 630, f'Communication Score: {scores["communicationScore"]}% ({get_feedback(scores["communicationScore"], "Communication")})')
188
  c.drawString(100, 610, f'Final Score: {scores["finalScore"]}%')
189
+ c.drawString(100, 590, f'Generated On: May 15, 2025, 12:01 PM IST')
190
  c.save()
191
+ buffer.seek(0)
192
+ return buffer
 
 
 
 
 
 
 
 
 
193
  except Exception as e:
194
  logger.error(f"Error generating PDF: {str(e)}")
195
  raise
196
 
197
  def determine_alert_flag(final_score: float, all_logs: list):
198
+ """Determine if an alert flag should be set."""
199
  try:
200
  if not all_logs:
201
  return False
 
209
 
210
  @app.route('/score', methods=['POST'])
211
  def score_vendor():
212
+ """Score a vendor and save to Salesforce."""
213
  try:
 
 
 
 
 
 
 
214
  data = request.get_json()
215
  if not data:
 
216
  return jsonify({'error': 'Invalid request data'}), 400
217
 
 
218
  log = VendorLog(**data)
219
+ scores = calculate_scores(log)
 
 
 
 
 
 
 
 
220
 
221
+ pdf_buffer = generate_pdf(log.vendorId, log.vendorLogName, scores)
222
+ pdf_content = pdf_buffer.getvalue()
223
  pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
224
 
 
225
  alert_flag = determine_alert_flag(scores['finalScore'], vendor_logs)
226
 
 
227
  vendor_logs.append({
228
  'vendorLogId': log.vendorLogId,
229
  'vendorId': log.vendorId,
230
  'vendorLogName': log.vendorLogName,
 
 
 
 
 
 
 
231
  'scores': scores,
232
  'extracted': True
233
  })
234
 
235
+ if sf:
 
236
  sf.Subcontractor_Performance_Score__c.create({
237
+ 'VendorId__c': log.vendorId,
238
  'Month__c': datetime.now().strftime('%Y-%m-%d'),
239
  'Quality_Score__c': scores['qualityScore'],
240
  'Timeliness_Score__c': scores['timelinessScore'],
 
244
  'Certification_URL__c': pdf_base64,
245
  'Alert_Flag__c': alert_flag
246
  })
247
+ logger.info(f"Saved scores to Salesforce for Vendor Log: {log.vendorLogId}")
 
 
 
248
 
 
249
  return jsonify({
250
  'vendorLogId': log.vendorLogId,
251
  'vendorId': log.vendorId,
252
  'vendorLogName': log.vendorLogName,
253
+ 'scores': scores,
 
 
 
 
254
  'pdfContent': pdf_base64,
255
  'alert': alert_flag
256
  }), 200
257
  except Exception as e:
258
  logger.error(f"Error in /score endpoint: {str(e)}")
259
+ return jsonify({'error': str(e)}), 500
260
+
261
+ @app.route('/generate_report', methods=['POST'])
262
+ def generate_report():
263
+ """Generate a report for a selected vendor."""
264
+ try:
265
+ data = request.form
266
+ vendor_name = data.get('vendor_name')
267
+ report_month = data.get('report_month', 'May, 2025')
268
+
269
+ # Fetch the vendor's performance score
270
+ performance_scores = fetch_performance_scores()
271
+ vendor_data = next((v for v in performance_scores if v['vendorLogName'] == vendor_name), None)
272
+
273
+ if not vendor_data:
274
+ return jsonify({'error': 'Vendor not found'}), 404
275
+
276
+ # Generate PDF
277
+ pdf_buffer = generate_pdf(vendor_data['vendorId'], vendor_name, vendor_data['scores'])
278
+
279
+ # Save to Salesforce
280
+ pdf_content = pdf_buffer.getvalue()
281
+ pdf_base64 = base64.b64encode(pdf_content).decode('utf-8')
282
+ if sf:
283
+ sf.Subcontractor_Performance_Score__c.update(vendor_data['vendorId'], {
284
+ 'Certification_URL__c': pdf_base64
285
+ })
286
+
287
+ return send_file(
288
+ pdf_buffer,
289
+ attachment_filename=f'report_{vendor_data["vendorId"]}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf',
290
+ as_attachment=True
291
+ )
292
+ except Exception as e:
293
+ logger.error(f"Error in /generate_report endpoint: {str(e)}")
294
+ return jsonify({'error': str(e)}), 500
295
 
296
  @app.route('/', methods=['GET'])
297
  def get_dashboard():
298
+ """Render the dashboard with Salesforce data."""
299
  try:
300
+ # Fetch data from Salesforce
301
+ performance_scores = fetch_performance_scores()
302
+ vendor_logs_list = fetch_vendor_logs_from_salesforce()
 
 
 
 
 
 
 
 
 
 
 
303
 
304
+ # Prepare data for dashboard
305
  vendor_logs.clear()
306
+ for score in performance_scores:
 
 
 
 
 
 
 
 
307
  vendor_logs.append({
308
+ 'vendorLogId': score['vendorId'],
309
+ 'vendorId': score['vendorId'],
310
+ 'vendorLogName': score['vendorLogName'],
311
+ 'scores': score['scores'],
312
  'extracted': True
313
  })
314
 
 
315
  total_vendors = len(vendor_logs)
316
  performance_alerts = sum(1 for log in vendor_logs if determine_alert_flag(log['scores']['finalScore'], vendor_logs))
317
  top_performers = sum(1 for log in vendor_logs if log['scores']['finalScore'] >= 90)
318
  improving_vendors = sum(1 for log in vendor_logs if log['scores']['finalScore'] >= 70)
319
 
 
320
  sorted_logs = sorted(vendor_logs, key=lambda x: x['scores']['finalScore'], reverse=True)
321
+ top_logs_data = sorted_logs[:5]
322
+ top_performing_logs = sorted_logs[:4]
323
  alert_logs = [log for log in vendor_logs if determine_alert_flag(log['scores']['finalScore'], vendor_logs)][:3]
324
 
 
325
  top_logs = []
326
  for idx, log in enumerate(top_logs_data, 1):
327
  scores = log['scores']
 
340
  'status_text': status_text
341
  })
342
 
 
343
  template_data = {
344
  'total_vendors': total_vendors,
345
  'performance_alerts': performance_alerts,
 
352
  'alert_logs': alert_logs,
353
  'top_performing_logs': top_performing_logs,
354
  'vendor_logs': vendor_logs,
355
+ 'sorted_logs': sorted_logs,
356
+ 'vendor_names': [log.vendorLogName for log in vendor_logs_list],
357
+ 'report_month': 'May, 2025'
358
  }
359
 
 
360
  return render_template('dashboard.html', **template_data)
361
  except Exception as e:
362
+ logger.error(f"Error in / endpoint: {str(e)}")
 
 
363
  template_data = {
364
  'total_vendors': 0,
365
  'performance_alerts': 0,
 
372
  'alert_logs': [],
373
  'top_performing_logs': [],
374
  'vendor_logs': [],
375
+ 'sorted_logs': [],
376
+ 'vendor_names': [],
377
+ 'report_month': 'May, 2025'
378
  }
 
379
  return render_template('dashboard.html', **template_data)
380
 
381
  @app.route('/document', methods=['GET'])
382
  def get_document():
383
+ """Render the document page."""
384
  try:
385
  return render_template('document.html')
386
  except Exception as e:
387
+ logger.error(f"Error in /document endpoint: {str(e)}")
388
+ return jsonify({'error': str(e)}), 500
 
389
 
390
  if __name__ == "__main__":
391
  app.run(host="0.0.0.0", port=7860, debug=True)