|
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_dotenv() |
|
|
|
|
|
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') |
|
|
|
|
|
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') |
|
) |
|
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.") |
|
|
|
@app.route('/') |
|
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') |
|
|
|
@app.route('/login', methods=['GET']) |
|
def login_page(): |
|
return render_template('login.html') |
|
|
|
@app.route('/signup', methods=['GET']) |
|
def signup_page(): |
|
return render_template('signup.html') |
|
|
|
@app.route('/login', methods=['POST']) |
|
def login(): |
|
data = request.get_json() |
|
username = data.get('username') |
|
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 = 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 |
|
|
|
@app.route('/signup', methods=['POST']) |
|
def signup(): |
|
data = request.get_json() |
|
username = data.get('username') |
|
password = data.get('password') |
|
project_id = data.get('project_id', 'PROJ_001') |
|
engagement_score = float(data.get('engagement_score', 85)) |
|
kpi_flag = data.get('kpi_flag', False) |
|
|
|
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() |
|
|
|
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 |
|
|
|
|
|
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}") |
|
|
|
|
|
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 |
|
|
|
@app.route('/logout', methods=['POST']) |
|
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"}) |
|
|
|
@app.route('/get_supervisor_data') |
|
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 |
|
|
|
@app.route('/submit_reflection', methods=['POST']) |
|
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 |
|
|
|
@app.route('/generate', methods=['POST']) |
|
def generate(): |
|
supervisor_name = session.get('supervisor_name', 'GUEST') |
|
data = request.get_json() |
|
|
|
|
|
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 |
|
|
|
|
|
c.setFont("Helvetica-Bold", 16) |
|
c.drawString(50, height - 50, "Supervisor AI Coaching Summary") |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
@app.route('/download_pdf', methods=['GET']) |
|
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()) |
|
} |
|
|
|
|
|
pdf_path = generate_pdf_summary(data) |
|
|
|
|
|
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}") |
|
|
|
|
|
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) |