Spaces:
Sleeping
Sleeping
| from flask import Flask, request, jsonify, session, redirect, url_for, render_template, send_file | |
| from simple_salesforce import Salesforce, SalesforceAuthenticationFailed, SalesforceError | |
| import os | |
| from datetime import datetime | |
| from dotenv import load_dotenv | |
| import logging | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.pdfgen import canvas | |
| import io | |
| import tempfile | |
| # Load environment variables | |
| load_dotenv() | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(levelname)s - %(message)s', | |
| handlers=[ | |
| logging.FileHandler('app.log'), | |
| logging.StreamHandler() | |
| ] | |
| ) | |
| logger = logging.getLogger(__name__) | |
| app = Flask(__name__) | |
| app.secret_key = os.getenv('FLASK_SECRET_KEY', 'your-secret-key') | |
| # Salesforce mock data for guest users | |
| MOCK_DATA = { | |
| "supervisor_name": "GUEST", | |
| "project_id": "PROJ_001", | |
| "daily_checklist": "Inspect safety equipment\nReview team assignments\nCheck project timeline", | |
| "suggested_tips": "Prioritize safety checks\nCommunicate clearly with the team\nMonitor resource usage", | |
| "reflection_log": "", | |
| "engagement_score": 85, | |
| "kpi_flag": False, | |
| "download_link": "", | |
| "last_login": str(datetime.now()) | |
| } | |
| def get_salesforce_connection(): | |
| """Establish a Salesforce connection with error handling.""" | |
| try: | |
| sf = Salesforce( | |
| username=os.getenv('SALESFORCE_USERNAME'), | |
| password=os.getenv('SALESFORCE_PASSWORD'), | |
| security_token=os.getenv('SALESFORCE_SECURITY_TOKEN'), | |
| domain=os.getenv('SALESFORCE_DOMAIN', 'test') # 'login' for production | |
| ) | |
| logger.info("Successfully connected to Salesforce") | |
| return sf | |
| except SalesforceAuthenticationFailed as e: | |
| logger.error(f"Salesforce authentication failed: {str(e)}") | |
| raise Exception("Salesforce authentication failed. Check your credentials.") | |
| except Exception as e: | |
| logger.error(f"Error connecting to Salesforce: {str(e)}") | |
| raise Exception("Unable to connect to Salesforce. Please try again later.") | |
| def index(): | |
| if 'supervisor_name' not in session: | |
| logger.info("User not logged in, redirecting to login page") | |
| return redirect(url_for('login_page')) | |
| return render_template('index.html') | |
| def login_page(): | |
| return render_template('login.html') | |
| def signup_page(): | |
| return render_template('signup.html') | |
| def login(): | |
| data = request.get_json() | |
| username = data.get('username') # Now using Name field | |
| password = data.get('password') | |
| if username == 'GUEST': | |
| session['supervisor_name'] = 'GUEST' | |
| logger.info("Guest login successful") | |
| return jsonify({"status": "success", "message": "Logged in as guest"}) | |
| try: | |
| sf = get_salesforce_connection() | |
| # Query Supervisor_AI_Coaching__c for Name and Password__c | |
| query = f"SELECT Id, Name, Password__c FROM Supervisor_AI_Coaching__c WHERE Name = '{username}' LIMIT 1" | |
| result = sf.query(query) | |
| if not result['records']: | |
| logger.warning(f"Invalid username: {username}") | |
| return jsonify({"status": "error", "message": "Invalid username"}), 401 | |
| record = result['records'][0] | |
| stored_password = record['Password__c'] | |
| if stored_password != password: | |
| logger.warning(f"Invalid password for username: {username}") | |
| return jsonify({"status": "error", "message": "Invalid password"}), 401 | |
| session['supervisor_name'] = username | |
| logger.info(f"Login successful for {username}") | |
| return jsonify({"status": "success", "message": "Login successful"}) | |
| except Exception as e: | |
| logger.error(f"Login error: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| def signup(): | |
| data = request.get_json() | |
| username = data.get('username') # Name field | |
| password = data.get('password') # Password__c field | |
| project_id = data.get('project_id', 'PROJ_001') # Project_ID__c | |
| engagement_score = float(data.get('engagement_score', 85)) # Engagement_Score__c | |
| kpi_flag = data.get('kpi_flag', False) # KPI_Flag__c | |
| if not username or not password: | |
| logger.warning("Signup failed: Username and password are required") | |
| return jsonify({"status": "error", "message": "Username and password are required"}), 400 | |
| try: | |
| sf = get_salesforce_connection() | |
| # Check if username already exists | |
| query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{username}' LIMIT 1" | |
| result = sf.query(query) | |
| if result['records']: | |
| logger.warning(f"Signup failed: Username {username} already exists") | |
| return jsonify({"status": "error", "message": "Username already exists"}), 400 | |
| # Create new Supervisor_AI_Coaching__c record | |
| new_record = { | |
| 'Name': username, | |
| 'Password__c': password, | |
| 'Project_ID__c': project_id, | |
| 'Engagement_Score__c': engagement_score, | |
| 'KPI_Flag__c': kpi_flag, | |
| 'Daily_Checklist__c': '', | |
| 'Suggested_Tips__c': '', | |
| 'Reflection_Log__c': '', | |
| 'Download_Link__c': '' | |
| } | |
| sf.Supervisor_AI_Coaching__c.create(new_record) | |
| logger.info(f"Signup successful for {username}") | |
| # Automatically log in the user after signup | |
| session['supervisor_name'] = username | |
| return jsonify({"status": "success", "message": "Signup successful, you are now logged in"}) | |
| except SalesforceError as e: | |
| logger.error(f"Salesforce API error during signup: {str(e)}") | |
| return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500 | |
| except Exception as e: | |
| logger.error(f"Signup error: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| def logout(): | |
| supervisor_name = session.get('supervisor_name', 'Unknown') | |
| session.pop('supervisor_name', None) | |
| logger.info(f"User {supervisor_name} logged out") | |
| return jsonify({"status": "success", "message": "Logged out successfully"}) | |
| def get_supervisor_data(): | |
| supervisor_name = session.get('supervisor_name', 'GUEST') | |
| if supervisor_name == 'GUEST': | |
| logger.info("Returning mock data for guest user") | |
| return jsonify({"status": "success", "data": MOCK_DATA}) | |
| try: | |
| sf = get_salesforce_connection() | |
| query = f""" | |
| SELECT Name, Project_ID__c, Daily_Checklist__c, Suggested_Tips__c, | |
| Reflection_Log__c, Engagement_Score__c, KPI_Flag__c, Download_Link__c | |
| FROM Supervisor_AI_Coaching__c | |
| WHERE Name = '{supervisor_name}' | |
| LIMIT 1 | |
| """ | |
| result = sf.query(query) | |
| if result['records']: | |
| record = result['records'][0] | |
| data = { | |
| "supervisor_name": record['Name'], | |
| "project_id": record['Project_ID__c'], | |
| "daily_checklist": record['Daily_Checklist__c'] or "", | |
| "suggested_tips": record['Suggested_Tips__c'] or "", | |
| "reflection_log": record['Reflection_Log__c'] or "", | |
| "engagement_score": record['Engagement_Score__c'] or 0, | |
| "kpi_flag": record['KPI_Flag__c'], | |
| "download_link": record['Download_Link__c'] or "", | |
| "last_login": str(datetime.now()) | |
| } | |
| logger.info(f"Fetched data for supervisor {supervisor_name}") | |
| return jsonify({"status": "success", "data": data}) | |
| else: | |
| logger.warning(f"No data found for supervisor {supervisor_name}") | |
| return jsonify({"status": "error", "message": "No data found for this supervisor"}) | |
| except SalesforceError as e: | |
| logger.error(f"Salesforce API error while fetching data: {str(e)}") | |
| return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500 | |
| except Exception as e: | |
| logger.error(f"Error fetching supervisor data: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| def submit_reflection(): | |
| supervisor_name = session.get('supervisor_name', 'GUEST') | |
| if supervisor_name == 'GUEST': | |
| MOCK_DATA['reflection_log'] = request.get_json().get('reflection') | |
| logger.info("Reflection submitted for guest user") | |
| return jsonify({"status": "success", "message": "Reflection submitted successfully (guest mode)"}) | |
| data = request.get_json() | |
| reflection = data.get('reflection') | |
| try: | |
| sf = get_salesforce_connection() | |
| query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{supervisor_name}' LIMIT 1" | |
| result = sf.query(query) | |
| if not result['records']: | |
| logger.warning(f"No record found for supervisor {supervisor_name}") | |
| return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404 | |
| record_id = result['records'][0]['Id'] | |
| sf.Supervisor_AI_Coaching__c.update(record_id, {'Reflection_Log__c': reflection}) | |
| logger.info(f"Reflection updated for supervisor {supervisor_name}") | |
| return jsonify({"status": "success", "message": "Reflection submitted successfully"}) | |
| except SalesforceError as e: | |
| logger.error(f"Salesforce API error while submitting reflection: {str(e)}") | |
| return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500 | |
| except Exception as e: | |
| logger.error(f"Error submitting reflection: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| def generate(): | |
| supervisor_name = session.get('supervisor_name', 'GUEST') | |
| data = request.get_json() | |
| # Generate checklist and tips (simplified logic) | |
| checklist = [ | |
| "Inspect safety equipment", | |
| "Review team assignments", | |
| "Check project timeline" | |
| ] | |
| tips = [ | |
| "Prioritize safety checks", | |
| "Communicate clearly with the team", | |
| "Monitor resource usage" | |
| ] | |
| if supervisor_name == 'GUEST': | |
| MOCK_DATA['daily_checklist'] = '\n'.join(checklist) | |
| MOCK_DATA['suggested_tips'] = '\n'.join(tips) | |
| logger.info("Generated coaching output for guest user") | |
| return jsonify({ | |
| "status": "success", | |
| "output": { | |
| "checklist": checklist, | |
| "tips": tips | |
| } | |
| }) | |
| try: | |
| sf = get_salesforce_connection() | |
| query = f"SELECT Id FROM Supervisor_AI_Coaching__c WHERE Name = '{supervisor_name}' LIMIT 1" | |
| result = sf.query(query) | |
| if not result['records']: | |
| logger.warning(f"No record found for supervisor {supervisor_name}") | |
| return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404 | |
| record_id = result['records'][0]['Id'] | |
| sf.Supervisor_AI_Coaching__c.update(record_id, { | |
| 'Daily_Checklist__c': '\n'.join(checklist), | |
| 'Suggested_Tips__c': '\n'.join(tips) | |
| }) | |
| logger.info(f"Generated and updated coaching output for supervisor {supervisor_name}") | |
| return jsonify({ | |
| "status": "success", | |
| "output": { | |
| "checklist": checklist, | |
| "tips": tips | |
| } | |
| }) | |
| except SalesforceError as e: | |
| logger.error(f"Salesforce API error while generating output: {str(e)}") | |
| return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500 | |
| except Exception as e: | |
| logger.error(f"Error generating coaching output: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| def generate_pdf_summary(data): | |
| """Generate a PDF summary and return the file path.""" | |
| try: | |
| with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf', dir='/tmp') as tmp_file: | |
| pdf_path = tmp_file.name | |
| c = canvas.Canvas(pdf_path, pagesize=letter) | |
| width, height = letter | |
| # Title | |
| c.setFont("Helvetica-Bold", 16) | |
| c.drawString(50, height - 50, "Supervisor AI Coaching Summary") | |
| # Supervisor Data | |
| c.setFont("Helvetica", 12) | |
| y_position = height - 100 | |
| c.drawString(50, y_position, f"Supervisor Name: {data.get('supervisor_name', 'N/A')}") | |
| y_position -= 20 | |
| c.drawString(50, y_position, f"Project ID: {data.get('project_id', 'N/A')}") | |
| y_position -= 20 | |
| c.drawString(50, y_position, f"Last Login: {data.get('last_login', 'N/A')}") | |
| y_position -= 40 | |
| # Daily Checklist | |
| c.setFont("Helvetica-Bold", 14) | |
| c.drawString(50, y_position, "Daily Checklist") | |
| c.setFont("Helvetica", 12) | |
| y_position -= 20 | |
| checklist = data.get('daily_checklist', '').split('\n') | |
| for item in checklist: | |
| if item.strip(): | |
| c.drawString(70, y_position, f"- {item}") | |
| y_position -= 20 | |
| if y_position < 50: | |
| c.showPage() | |
| y_position = height - 50 | |
| # Suggested Tips | |
| y_position -= 20 | |
| c.setFont("Helvetica-Bold", 14) | |
| c.drawString(50, y_position, "Suggested Tips") | |
| c.setFont("Helvetica", 12) | |
| y_position -= 20 | |
| tips = data.get('suggested_tips', '').split('\n') | |
| for tip in tips: | |
| if tip.strip(): | |
| c.drawString(70, y_position, f"- {tip}") | |
| y_position -= 20 | |
| if y_position < 50: | |
| c.showPage() | |
| y_position = height - 50 | |
| # Reflection Log | |
| y_position -= 20 | |
| c.setFont("Helvetica-Bold", 14) | |
| c.drawString(50, y_position, "Reflection Log") | |
| c.setFont("Helvetica", 12) | |
| y_position -= 20 | |
| reflection = data.get('reflection_log', 'No reflection available.') | |
| lines = reflection.split('\n') | |
| for line in lines: | |
| c.drawString(70, y_position, line) | |
| y_position -= 20 | |
| if y_position < 50: | |
| c.showPage() | |
| y_position = height - 50 | |
| # KPIs | |
| y_position -= 20 | |
| c.setFont("Helvetica-Bold", 14) | |
| c.drawString(50, y_position, "KPIs") | |
| c.setFont("Helvetica", 12) | |
| y_position -= 20 | |
| c.drawString(70, y_position, f"Engagement Score: {data.get('engagement_score', 0)}%") | |
| y_position -= 20 | |
| c.drawString(70, y_position, f"KPI Flag: {'Active' if data.get('kpi_flag', False) else 'Inactive'}") | |
| y_position -= 20 | |
| c.save() | |
| logger.info(f"Generated PDF at {pdf_path}") | |
| return pdf_path | |
| except Exception as e: | |
| logger.error(f"Error generating PDF: {str(e)}") | |
| raise | |
| def download_pdf(): | |
| supervisor_name = session.get('supervisor_name', 'GUEST') | |
| if supervisor_name == 'GUEST': | |
| logger.info("Download not available for guest user") | |
| return jsonify({"status": "success", "message": "Download not available in guest mode"}) | |
| try: | |
| sf = get_salesforce_connection() | |
| query = f""" | |
| SELECT Id, Name, Project_ID__c, Daily_Checklist__c, Suggested_Tips__c, | |
| Reflection_Log__c, Engagement_Score__c, KPI_Flag__c, Download_Link__c | |
| FROM Supervisor_AI_Coaching__c | |
| WHERE Name = '{supervisor_name}' | |
| LIMIT 1 | |
| """ | |
| result = sf.query(query) | |
| if not result['records']: | |
| logger.warning(f"No record found for supervisor {supervisor_name}") | |
| return jsonify({"status": "error", "message": "No record found for this supervisor"}), 404 | |
| record = result['records'][0] | |
| record_id = record['Id'] | |
| data = { | |
| "supervisor_name": record['Name'], | |
| "project_id": record['Project_ID__c'], | |
| "daily_checklist": record['Daily_Checklist__c'] or "", | |
| "suggested_tips": record['Suggested_Tips__c'] or "", | |
| "reflection_log": record['Reflection_Log__c'] or "", | |
| "engagement_score": record['Engagement_Score__c'] or 0, | |
| "kpi_flag": record['KPI_Flag__c'], | |
| "download_link": record['Download_Link__c'] or "", | |
| "last_login": str(datetime.now()) | |
| } | |
| # Generate PDF | |
| pdf_path = generate_pdf_summary(data) | |
| # Placeholder for Download_Link__c (requires actual storage solution in production) | |
| download_link = "https://example.com/report.pdf" | |
| sf.Supervisor_AI_Coaching__c.update(record_id, {'Download_Link__c': download_link}) | |
| logger.info(f"Updated Download_Link__c for supervisor {supervisor_name}") | |
| # Serve the PDF file | |
| return send_file( | |
| pdf_path, | |
| as_attachment=True, | |
| download_name=f"supervisor_report_{supervisor_name}.pdf", | |
| mimetype='application/pdf' | |
| ) | |
| except SalesforceError as e: | |
| logger.error(f"Salesforce API error while downloading PDF: {str(e)}") | |
| return jsonify({"status": "error", "message": "Salesforce API error. Please try again later."}), 500 | |
| except Exception as e: | |
| logger.error(f"Error downloading PDF: {str(e)}") | |
| return jsonify({"status": "error", "message": str(e)}), 500 | |
| finally: | |
| if 'pdf_path' in locals(): | |
| try: | |
| os.remove(pdf_path) | |
| logger.info(f"Cleaned up temporary PDF file: {pdf_path}") | |
| except Exception as e: | |
| logger.warning(f"Failed to clean up PDF file: {str(e)}") | |
| if __name__ == '__main__': | |
| port = int(os.getenv('PORT', 5000)) | |
| app.run(host='0.0.0.0', port=port, debug=True) |