geethareddy commited on
Commit
4bc0541
·
verified ·
1 Parent(s): 1c68d9d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +416 -159
app.py CHANGED
@@ -1,185 +1,442 @@
1
- import requests
2
- import json
3
  import os
4
- import logging
5
  from datetime import datetime
6
  from dotenv import load_dotenv
7
- from simple_salesforce import Salesforce
8
- from flask import Flask, jsonify, request, render_template, redirect, url_for
9
-
10
- # Configure logging
11
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
12
- logger = logging.getLogger(__name__)
13
 
14
  # Load environment variables
15
  load_dotenv()
16
 
17
- # Hugging Face API configuration
18
- HUGGING_FACE_API_URL = os.getenv("HUGGING_FACE_API_URL", "https://api-inference.huggingface.co/models/distilgpt2")
19
- HUGGING_FACE_API_TOKEN = os.getenv("HUGGING_FACE_API_TOKEN")
20
-
21
- # Salesforce configuration
22
- SALESFORCE_USERNAME = os.getenv("SALESFORCE_USERNAME")
23
- SALESFORCE_PASSWORD = os.getenv("SALESFORCE_PASSWORD")
24
- SALESFORCE_SECURITY_TOKEN = os.getenv("SALESFORCE_SECURITY_TOKEN")
25
- SALESFORCE_DOMAIN = os.getenv("SALESFORCE_DOMAIN", "login")
26
-
27
- # Validate environment variables
28
- if not HUGGING_FACE_API_TOKEN:
29
- logger.error("HUGGING_FACE_API_TOKEN is not set")
30
- raise ValueError("HUGGING_FACE_API_TOKEN environment variable is not set")
31
- if not HUGGING_FACE_API_URL.startswith("https://api-inference.huggingface.co/models/"):
32
- logger.error("Invalid HUGGING_FACE_API_URL: %s", HUGGING_FACE_API_URL)
33
- raise ValueError("HUGGING_FACE_API_URL must point to a valid Hugging Face model")
34
- if not all([SALESFORCE_USERNAME, SALESFORCE_PASSWORD, SALESFORCE_SECURITY_TOKEN]):
35
- logger.error("Salesforce credentials are incomplete")
36
- raise ValueError("Salesforce credentials must be set")
37
-
38
- # Initialize Flask app
39
  app = Flask(__name__)
 
40
 
41
- def generate_coaching_output(data):
42
- """
43
- Generate daily checklist and tips using Hugging Face LLM.
44
- """
45
- logger.info("Generating coaching output for supervisor %s", data['supervisor_id'])
46
- milestones_json = json.dumps(data['milestones'], indent=2)
47
- prompt = f"""
48
- You are an AI Coach for construction site supervisors. Based on the following data, generate a daily checklist, three focus tips, and a motivational quote. Ensure outputs are concise, actionable, and tailored to the supervisor's role, project status, and reflection log.
49
-
50
- Supervisor Role: {data['role']}
51
- Project Milestones: {milestones_json}
52
- Reflection Log: {data['reflection_log']}
53
- Weather: {data['weather']}
54
-
55
- Format the response as JSON:
56
- {{
57
- "checklist": ["item1", "item2", ...],
58
- "tips": ["tip1", "tip2", "tip3"],
59
- "quote": "motivational quote"
60
- }}
61
- """
62
-
63
- headers = {
64
- "Authorization": f"Bearer {HUGGING_FACE_API_TOKEN}",
65
- "Content-Type": "application/json"
66
- }
67
- payload = {
68
- "inputs": prompt,
69
- "parameters": {
70
- "max_length": 200,
71
- "temperature": 0.7,
72
- "top_p": 0.9
73
- }
74
- }
75
 
 
 
76
  try:
77
- response = requests.post(HUGGING_FACE_API_URL, headers=headers, json=payload, timeout=5)
78
- response.raise_for_status()
79
- result = response.json()
80
- generated_text = result[0]["generated_text"] if isinstance(result, list) else result["generated_text"]
81
-
82
- start_idx = generated_text.find('{')
83
- end_idx = generated_text.rfind('}') + 1
84
- if start_idx == -1 or end_idx == 0:
85
- logger.error("No valid JSON found in LLM output")
86
- raise ValueError("No valid JSON found in LLM output")
87
-
88
- json_str = generated_text[start_idx:end_idx]
89
- output = json.loads(json_str)
90
- logger.info("Successfully generated coaching output")
91
- return output
92
-
93
- except requests.exceptions.HTTPError as e:
94
- logger.error("Hugging Face API HTTP error: %s", e)
95
- return None
96
- except (json.JSONDecodeError, ValueError) as e:
97
- logger.error("Error parsing LLM output: %s", e)
98
- return None
99
  except Exception as e:
100
- logger.error("Unexpected error in Hugging Face API call: %s", e)
101
- return None
 
 
 
 
 
 
 
102
 
103
- def save_to_salesforce(output, supervisor_id, project_id):
104
- """
105
- Save coaching output to Salesforce Supervisor_AI_Coaching__c object.
106
- """
107
- if not output:
108
- logger.error("No coaching output to save")
109
- return False
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  try:
112
- sf = Salesforce(
113
- username=SALESFORCE_USERNAME,
114
- password=SALESFORCE_PASSWORD,
115
- security_token=SALESFORCE_SECURITY_TOKEN,
116
- domain=SALESFORCE_DOMAIN
117
- )
118
- logger.info("Connected to Salesforce")
119
-
120
- coaching_record = {
121
- "Supervisor_ID__c": supervisor_id,
122
- "Project_ID__c": project_id,
123
- "Daily_Checklist__c": "\n".join(output["checklist"]),
124
- "Suggested_Tips__c": "\n".join(output["tips"]),
125
- "Quote__c": output["quote"],
126
- "Generated_Date__c": datetime.now().strftime("%Y-%m-%d")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
 
 
128
 
129
- sf.Supervisor_AI_Coaching__c.upsert(
130
- f"Supervisor_ID__c/{supervisor_id}_{datetime.now().strftime('%Y-%m-%d')}",
131
- coaching_record
132
- )
133
- logger.info("Successfully saved coaching record to Salesforce for supervisor %s", supervisor_id)
134
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  except Exception as e:
137
- logger.error("Salesforce error: %s", e)
138
- return False
139
-
140
- @app.route('/', methods=['GET'])
141
- def redirect_to_ui():
142
- """
143
- Redirect root URL to the UI.
144
- """
145
- return redirect(url_for('ui'))
146
-
147
- @app.route('/ui', methods=['GET'])
148
- def ui():
149
- """
150
- Serve the HTML user interface.
151
- """
152
- return render_template('index.html')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  @app.route('/generate', methods=['POST'])
155
- def generate_endpoint():
156
- """
157
- Endpoint to generate coaching output based on supervisor data.
158
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  try:
160
- data = request.get_json()
161
- if not data or not all(key in data for key in ['supervisor_id', 'role', 'project_id', 'milestones', 'reflection_log', 'weather']):
162
- return jsonify({"status": "error", "message": "Invalid or missing supervisor data"}), 400
163
-
164
- coaching_output = generate_coaching_output(data)
165
- if coaching_output:
166
- success = save_to_salesforce(coaching_output, data["supervisor_id"], data["project_id"])
167
- if success:
168
- return jsonify({"status": "success", "output": coaching_output}), 200
169
- else:
170
- return jsonify({"status": "error", "message": "Failed to save to Salesforce"}), 500
171
- else:
172
- return jsonify({"status": "error", "message": "Failed to generate coaching output"}), 500
 
 
 
 
 
 
 
 
 
 
173
  except Exception as e:
174
- logger.error("Error in generate endpoint: %s", e)
175
  return jsonify({"status": "error", "message": str(e)}), 500
176
 
177
- @app.route('/health', methods=['GET'])
178
- def health_check():
179
- """
180
- Health check endpoint.
181
- """
182
- return jsonify({"status": "healthy", "message": "Application is running"}), 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- if __name__ == "__main__":
185
- app.run(host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
 
 
1
+ from flask import Flask, request, jsonify, session, redirect, url_for, render_template, send_file
2
+ from simple_salesforce import Salesforce, SalesforceAuthenticationFailed, SalesforceError
3
  import os
 
4
  from datetime import datetime
5
  from dotenv import load_dotenv
6
+ import logging
7
+ from reportlab.lib.pagesizes import letter
8
+ from reportlab.pdfgen import canvas
9
+ import io
10
+ import tempfile
 
11
 
12
  # Load environment variables
13
  load_dotenv()
14
 
15
+ # Configure logging
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format='%(asctime)s - %(levelname)s - %(message)s',
19
+ handlers=[
20
+ logging.FileHandler('app.log'),
21
+ logging.StreamHandler()
22
+ ]
23
+ )
24
+ logger = logging.getLogger(__name__)
25
+
 
 
 
 
 
 
 
 
 
 
 
26
  app = Flask(__name__)
27
+ app.secret_key = os.getenv('FLASK_SECRET_KEY', 'your-secret-key')
28
 
29
+ # Salesforce mock data for guest users
30
+ MOCK_DATA = {
31
+ "supervisor_name": "GUEST",
32
+ "project_id": "PROJ_001",
33
+ "daily_checklist": "Inspect safety equipment\nReview team assignments\nCheck project timeline",
34
+ "suggested_tips": "Prioritize safety checks\nCommunicate clearly with the team\nMonitor resource usage",
35
+ "reflection_log": "",
36
+ "engagement_score": 85,
37
+ "kpi_flag": False,
38
+ "download_link": "",
39
+ "last_login": str(datetime.now())
40
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ def get_salesforce_connection():
43
+ """Establish a Salesforce connection with error handling."""
44
  try:
45
+ sf = Salesforce(
46
+ username=os.getenv('SALESFORCE_USERNAME'),
47
+ password=os.getenv('SALESFORCE_PASSWORD'),
48
+ security_token=os.getenv('SALESFORCE_SECURITY_TOKEN'),
49
+ domain=os.getenv('SALESFORCE_DOMAIN', 'test') # 'login' for production
50
+ )
51
+ logger.info("Successfully connected to Salesforce")
52
+ return sf
53
+ except SalesforceAuthenticationFailed as e:
54
+ logger.error(f"Salesforce authentication failed: {str(e)}")
55
+ raise Exception("Salesforce authentication failed. Check your credentials.")
 
 
 
 
 
 
 
 
 
 
 
56
  except Exception as e:
57
+ logger.error(f"Error connecting to Salesforce: {str(e)}")
58
+ raise Exception("Unable to connect to Salesforce. Please try again later.")
59
+
60
+ @app.route('/')
61
+ def index():
62
+ if 'supervisor_name' not in session:
63
+ logger.info("User not logged in, redirecting to login page")
64
+ return redirect(url_for('login_page'))
65
+ return render_template('index.html')
66
 
67
+ @app.route('/login', methods=['GET'])
68
+ def login_page():
69
+ return render_template('login.html')
70
+
71
+ @app.route('/signup', methods=['GET'])
72
+ def signup_page():
73
+ return render_template('signup.html')
74
+
75
+ @app.route('/login', methods=['POST'])
76
+ def login():
77
+ data = request.get_json()
78
+ username = data.get('username') # Now using Name field
79
+ password = data.get('password')
80
+
81
+ if username == 'GUEST':
82
+ session['supervisor_name'] = 'GUEST'
83
+ logger.info("Guest login successful")
84
+ return jsonify({"status": "success", "message": "Logged in as guest"})
85
 
86
  try:
87
+ sf = get_salesforce_connection()
88
+ # Query Supervisor_AI_Coaching__c for Name and Password__c
89
+ query = f"SELECT Id, Name, Password__c FROM Supervisor_AI_Coaching__c WHERE Name = '{username}' LIMIT 1"
90
+ result = sf.query(query)
91
+ if not result['records']:
92
+ logger.warning(f"Invalid username: {username}")
93
+ return jsonify({"status": "error", "message": "Invalid username"}), 401
94
+
95
+ record = result['records'][0]
96
+ stored_password = record['Password__c']
97
+ if stored_password != password:
98
+ logger.warning(f"Invalid password for username: {username}")
99
+ return jsonify({"status": "error", "message": "Invalid password"}), 401
100
+
101
+ session['supervisor_name'] = username
102
+ logger.info(f"Login successful for {username}")
103
+ return jsonify({"status": "success", "message": "Login successful"})
104
+ except Exception as e:
105
+ logger.error(f"Login error: {str(e)}")
106
+ return jsonify({"status": "error", "message": str(e)}), 500
107
+
108
+ @app.route('/signup', methods=['POST'])
109
+ def signup():
110
+ data = request.get_json()
111
+ username = data.get('username') # Name field
112
+ password = data.get('password') # Password__c field
113
+ project_id = data.get('project_id', 'PROJ_001') # Project_ID__c
114
+ engagement_score = float(data.get('engagement_score', 85)) # Engagement_Score__c
115
+ kpi_flag = data.get('kpi_flag', False) # KPI_Flag__c
116
+
117
+ if not username or not password:
118
+ logger.warning("Signup failed: Username and password are required")
119
+ return jsonify({"status": "error", "message": "Username and password are required"}), 400
120
+
121
+ try:
122
+ sf = get_salesforce_connection()
123
+ # Check if username already exists
124
+ query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{username}' LIMIT 1"
125
+ result = sf.query(query)
126
+ if result['records']:
127
+ logger.warning(f"Signup failed: Username {username} already exists")
128
+ return jsonify({"status": "error", "message": "Username already exists"}), 400
129
+
130
+ # Create new Supervisor_AI_Coaching__c record
131
+ new_record = {
132
+ 'Name': username,
133
+ 'Password__c': password,
134
+ 'Project_ID__c': project_id,
135
+ 'Engagement_Score__c': engagement_score,
136
+ 'KPI_Flag__c': kpi_flag,
137
+ 'Daily_Checklist__c': '',
138
+ 'Suggested_Tips__c': '',
139
+ 'Reflection_Log__c': '',
140
+ 'Download_Link__c': ''
141
  }
142
+ sf.Supervisor_AI_Coaching__c.create(new_record)
143
+ logger.info(f"Signup successful for {username}")
144
 
145
+ # Automatically log in the user after signup
146
+ session['supervisor_name'] = username
147
+ return jsonify({"status": "success", "message": "Signup successful, you are now logged in"})
148
+ except SalesforceError as e:
149
+ logger.error(f"Salesforce API error during signup: {str(e)}")
150
+ return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500
151
+ except Exception as e:
152
+ logger.error(f"Signup error: {str(e)}")
153
+ return jsonify({"status": "error", "message": str(e)}), 500
154
+
155
+ @app.route('/logout', methods=['POST'])
156
+ def logout():
157
+ supervisor_name = session.get('supervisor_name', 'Unknown')
158
+ session.pop('supervisor_name', None)
159
+ logger.info(f"User {supervisor_name} logged out")
160
+ return jsonify({"status": "success", "message": "Logged out successfully"})
161
+
162
+ @app.route('/get_supervisor_data')
163
+ def get_supervisor_data():
164
+ supervisor_name = session.get('supervisor_name', 'GUEST')
165
+ if supervisor_name == 'GUEST':
166
+ logger.info("Returning mock data for guest user")
167
+ return jsonify({"status": "success", "data": MOCK_DATA})
168
+
169
+ try:
170
+ sf = get_salesforce_connection()
171
+ query = f"""
172
+ SELECT Name, Project_ID__c, Daily_Checklist__c, Suggested_Tips__c,
173
+ Reflection_Log__c, Engagement_Score__c, KPI_Flag__c, Download_Link__c
174
+ FROM Supervisor_AI_Coaching__c
175
+ WHERE Name = '{supervisor_name}'
176
+ LIMIT 1
177
+ """
178
+ result = sf.query(query)
179
 
180
+ if result['records']:
181
+ record = result['records'][0]
182
+ data = {
183
+ "supervisor_name": record['Name'],
184
+ "project_id": record['Project_ID__c'],
185
+ "daily_checklist": record['Daily_Checklist__c'] or "",
186
+ "suggested_tips": record['Suggested_Tips__c'] or "",
187
+ "reflection_log": record['Reflection_Log__c'] or "",
188
+ "engagement_score": record['Engagement_Score__c'] or 0,
189
+ "kpi_flag": record['KPI_Flag__c'],
190
+ "download_link": record['Download_Link__c'] or "",
191
+ "last_login": str(datetime.now())
192
+ }
193
+ logger.info(f"Fetched data for supervisor {supervisor_name}")
194
+ return jsonify({"status": "success", "data": data})
195
+ else:
196
+ logger.warning(f"No data found for supervisor {supervisor_name}")
197
+ return jsonify({"status": "error", "message": "No data found for this supervisor"})
198
+ except SalesforceError as e:
199
+ logger.error(f"Salesforce API error while fetching data: {str(e)}")
200
+ return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500
201
  except Exception as e:
202
+ logger.error(f"Error fetching supervisor data: {str(e)}")
203
+ return jsonify({"status": "error", "message": str(e)}), 500
204
+
205
+ @app.route('/submit_reflection', methods=['POST'])
206
+ def submit_reflection():
207
+ supervisor_name = session.get('supervisor_name', 'GUEST')
208
+ if supervisor_name == 'GUEST':
209
+ MOCK_DATA['reflection_log'] = request.get_json().get('reflection')
210
+ logger.info("Reflection submitted for guest user")
211
+ return jsonify({"status": "success", "message": "Reflection submitted successfully (guest mode)"})
212
+
213
+ data = request.get_json()
214
+ reflection = data.get('reflection')
215
+
216
+ try:
217
+ sf = get_salesforce_connection()
218
+ query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{supervisor_name}' LIMIT 1"
219
+ result = sf.query(query)
220
+ if not result['records']:
221
+ logger.warning(f"No record found for supervisor {supervisor_name}")
222
+ return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404
223
+
224
+ record_id = result['records'][0]['Id']
225
+ sf.Supervisor_AI_Coaching__c.update(record_id, {'Reflection_Log__c': reflection})
226
+ logger.info(f"Reflection updated for supervisor {supervisor_name}")
227
+ return jsonify({"status": "success", "message": "Reflection submitted successfully"})
228
+ except SalesforceError as e:
229
+ logger.error(f"Salesforce API error while submitting reflection: {str(e)}")
230
+ return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500
231
+ except Exception as e:
232
+ logger.error(f"Error submitting reflection: {str(e)}")
233
+ return jsonify({"status": "error", "message": str(e)}), 500
234
 
235
  @app.route('/generate', methods=['POST'])
236
+ def generate():
237
+ supervisor_name = session.get('supervisor_name', 'GUEST')
238
+ data = request.get_json()
239
+
240
+ # Generate checklist and tips (simplified logic)
241
+ checklist = [
242
+ "Inspect safety equipment",
243
+ "Review team assignments",
244
+ "Check project timeline"
245
+ ]
246
+ tips = [
247
+ "Prioritize safety checks",
248
+ "Communicate clearly with the team",
249
+ "Monitor resource usage"
250
+ ]
251
+
252
+ if supervisor_name == 'GUEST':
253
+ MOCK_DATA['daily_checklist'] = '\n'.join(checklist)
254
+ MOCK_DATA['suggested_tips'] = '\n'.join(tips)
255
+ logger.info("Generated coaching output for guest user")
256
+ return jsonify({
257
+ "status": "success",
258
+ "output": {
259
+ "checklist": checklist,
260
+ "tips": tips
261
+ }
262
+ })
263
+
264
  try:
265
+ sf = get_salesforce_connection()
266
+ query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{supervisor_name}' LIMIT 1"
267
+ result = sf.query(query)
268
+ if not result['records']:
269
+ logger.warning(f"No record found for supervisor {supervisor_name}")
270
+ return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404
271
+
272
+ record_id = result['records'][0]['Id']
273
+ sf.Supervisor_AI_Coaching__c.update(record_id, {
274
+ 'Daily_Checklist__c': '\n'.join(checklist),
275
+ 'Suggested_Tips__c': '\n'.join(tips)
276
+ })
277
+ logger.info(f"Generated and updated coaching output for supervisor {supervisor_name}")
278
+ return jsonify({
279
+ "status": "success",
280
+ "output": {
281
+ "checklist": checklist,
282
+ "tips": tips
283
+ }
284
+ })
285
+ except SalesforceError as e:
286
+ logger.error(f"Salesforce API error while generating output: {str(e)}")
287
+ return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500
288
  except Exception as e:
289
+ logger.error(f"Error generating coaching output: {str(e)}")
290
  return jsonify({"status": "error", "message": str(e)}), 500
291
 
292
+ def generate_pdf_summary(data):
293
+ """Generate a PDF summary and return the file path."""
294
+ try:
295
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf', dir='/tmp') as tmp_file:
296
+ pdf_path = tmp_file.name
297
+ c = canvas.Canvas(pdf_path, pagesize=letter)
298
+ width, height = letter
299
+
300
+ # Title
301
+ c.setFont("Helvetica-Bold", 16)
302
+ c.drawString(50, height - 50, "Supervisor AI Coaching Summary")
303
+
304
+ # Supervisor Data
305
+ c.setFont("Helvetica", 12)
306
+ y_position = height - 100
307
+ c.drawString(50, y_position, f"Supervisor Name: {data.get('supervisor_name', 'N/A')}")
308
+ y_position -= 20
309
+ c.drawString(50, y_position, f"Project ID: {data.get('project_id', 'N/A')}")
310
+ y_position -= 20
311
+ c.drawString(50, y_position, f"Last Login: {data.get('last_login', 'N/A')}")
312
+ y_position -= 40
313
+
314
+ # Daily Checklist
315
+ c.setFont("Helvetica-Bold", 14)
316
+ c.drawString(50, y_position, "Daily Checklist")
317
+ c.setFont("Helvetica", 12)
318
+ y_position -= 20
319
+ checklist = data.get('daily_checklist', '').split('\n')
320
+ for item in checklist:
321
+ if item.strip():
322
+ c.drawString(70, y_position, f"- {item}")
323
+ y_position -= 20
324
+ if y_position < 50:
325
+ c.showPage()
326
+ y_position = height - 50
327
+
328
+ # Suggested Tips
329
+ y_position -= 20
330
+ c.setFont("Helvetica-Bold", 14)
331
+ c.drawString(50, y_position, "Suggested Tips")
332
+ c.setFont("Helvetica", 12)
333
+ y_position -= 20
334
+ tips = data.get('suggested_tips', '').split('\n')
335
+ for tip in tips:
336
+ if tip.strip():
337
+ c.drawString(70, y_position, f"- {tip}")
338
+ y_position -= 20
339
+ if y_position < 50:
340
+ c.showPage()
341
+ y_position = height - 50
342
+
343
+ # Reflection Log
344
+ y_position -= 20
345
+ c.setFont("Helvetica-Bold", 14)
346
+ c.drawString(50, y_position, "Reflection Log")
347
+ c.setFont("Helvetica", 12)
348
+ y_position -= 20
349
+ reflection = data.get('reflection_log', 'No reflection available.')
350
+ lines = reflection.split('\n')
351
+ for line in lines:
352
+ c.drawString(70, y_position, line)
353
+ y_position -= 20
354
+ if y_position < 50:
355
+ c.showPage()
356
+ y_position = height - 50
357
+
358
+ # KPIs
359
+ y_position -= 20
360
+ c.setFont("Helvetica-Bold", 14)
361
+ c.drawString(50, y_position, "KPIs")
362
+ c.setFont("Helvetica", 12)
363
+ y_position -= 20
364
+ c.drawString(70, y_position, f"Engagement Score: {data.get('engagement_score', 0)}%")
365
+ y_position -= 20
366
+ c.drawString(70, y_position, f"KPI Flag: {'Active' if data.get('kpi_flag', False) else 'Inactive'}")
367
+ y_position -= 20
368
+
369
+ c.save()
370
+ logger.info(f"Generated PDF at {pdf_path}")
371
+ return pdf_path
372
+ except Exception as e:
373
+ logger.error(f"Error generating PDF: {str(e)}")
374
+ raise
375
+
376
+ @app.route('/download_pdf', methods=['GET'])
377
+ def download_pdf():
378
+ supervisor_name = session.get('supervisor_name', 'GUEST')
379
+ if supervisor_name == 'GUEST':
380
+ logger.info("Download not available for guest user")
381
+ return jsonify({"status": "success", "message": "Download not available in guest mode"})
382
+
383
+ try:
384
+ sf = get_salesforce_connection()
385
+ query = f"""
386
+ SELECT Id, Name, Project_ID__c, Daily_Checklist__c, Suggested_Tips__c,
387
+ Reflection_Log__c, Engagement_Score__c, KPI_Flag__c, Download_Link__c
388
+ FROM Supervisor_AI_Coaching__c
389
+ WHERE Name = '{supervisor_name}'
390
+ LIMIT 1
391
+ """
392
+ result = sf.query(query)
393
+ if not result['records']:
394
+ logger.warning(f"No record found for supervisor {supervisor_name}")
395
+ return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404
396
+
397
+ record = result['records'][0]
398
+ record_id = record['Id']
399
+ data = {
400
+ "supervisor_name": record['Name'],
401
+ "project_id": record['Project_ID__c'],
402
+ "daily_checklist": record['Daily_Checklist__c'] or "",
403
+ "suggested_tips": record['Suggested_Tips__c'] or "",
404
+ "reflection_log": record['Reflection_Log__c'] or "",
405
+ "engagement_score": record['Engagement_Score__c'] or 0,
406
+ "kpi_flag": record['KPI_Flag__c'],
407
+ "download_link": record['Download_Link__c'] or "",
408
+ "last_login": str(datetime.now())
409
+ }
410
+
411
+ # Generate PDF
412
+ pdf_path = generate_pdf_summary(data)
413
+
414
+ # Placeholder for Download_Link__c (requires actual storage solution in production)
415
+ download_link = "https://example.com/report.pdf"
416
+ sf.Supervisor_AI_Coaching__c.update(record_id, {'Download_Link__c': download_link})
417
+ logger.info(f"Updated Download_Link__c for supervisor {supervisor_name}")
418
+
419
+ # Serve the PDF file
420
+ return send_file(
421
+ pdf_path,
422
+ as_attachment=True,
423
+ download_name=f"supervisor_report_{supervisor_name}.pdf",
424
+ mimetype='application/pdf'
425
+ )
426
+ except SalesforceError as e:
427
+ logger.error(f"Salesforce API error while downloading PDF: {str(e)}")
428
+ return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500
429
+ except Exception as e:
430
+ logger.error(f"Error downloading PDF: {str(e)}")
431
+ return jsonify({"status": "error", "message": str(e)}), 500
432
+ finally:
433
+ if 'pdf_path' in locals():
434
+ try:
435
+ os.remove(pdf_path)
436
+ logger.info(f"Cleaned up temporary PDF file: {pdf_path}")
437
+ except Exception as e:
438
+ logger.warning(f"Failed to clean up PDF file: {str(e)}")
439
 
440
+ if __name__ == '__main__':
441
+ port = int(os.getenv('PORT', 5000))
442
+ app.run(host='0.0.0.0', port=port, debug=True)