File size: 14,606 Bytes
eba0723 5a6aa4a 02615cd eba0723 d85f039 02615cd eba0723 a65ab2b 1799b50 eba0723 a65ab2b eba0723 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd d85f039 02615cd eba0723 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a 02615cd 5a6aa4a a65ab2b 02615cd eba0723 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
from flask import Flask, request, jsonify, session, redirect, url_for, render_template
from simple_salesforce import Salesforce, SalesforceAuthenticationFailed, SalesforceError
import os
from dotenv import load_dotenv
import logging
import bcrypt
from urllib.parse import quote
# Load environment variables
load_dotenv()
# Configure logging
logging.basicConfig(
level=logging.DEBUG, # Set to DEBUG for detailed logs
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', 'tTPLQduw8wDpxdKOMJ3d9dM3o')
# Salesforce mock data for guest users
MOCK_DATA = {
"supervisor_id": "GUEST",
"project_id": "PROJ_001",
"last_login": "Guest Mode"
}
def get_salesforce_connection():
"""Establish a Salesforce connection with detailed error handling."""
try:
# Ensure you are passing the correct environment variables
sf = Salesforce(
username=os.getenv('SALESFORCE_USERNAME'),
password=os.getenv('SALESFORCE_PASSWORD'),
security_token=os.getenv('SALESFORCE_SECURITY_TOKEN'),
domain=os.getenv('SALESFORCE_DOMAIN', 'login'), # 'test' is for sandbox, 'login' is for production
version='60.0' # Specify Salesforce API version
)
logger.info("Successfully connected to Salesforce")
# Test connection
sf.query("SELECT Id FROM Supervisor__c LIMIT 1")
logger.debug("Salesforce connection test query successful")
return sf
except SalesforceAuthenticationFailed as e:
logger.error(f"Salesforce authentication failed: {str(e)}")
raise Exception(f"Salesforce authentication failed: {str(e)}. Check your credentials.")
except SalesforceError as e:
logger.error(f"Salesforce error during connection: {str(e)}")
raise Exception(f"Salesforce error: {str(e)}. Check object permissions and API access.")
except Exception as e:
logger.error(f"Unexpected error connecting to Salesforce: {str(e)}")
raise Exception(f"Unable to connect to Salesforce: {str(e)}. Please check your configuration.")
def hash_password(password):
"""Hash a password using bcrypt."""
try:
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
except Exception as e:
logger.error(f"Password hashing failed: {str(e)}")
raise
def verify_password(password, hashed_password):
"""Verify a password against its hash."""
try:
return bcrypt.checkpw(password.encode('utf-8'), hashed_password.encode('utf-8'))
except Exception as e:
logger.error(f"Password verification failed: {str(e)}")
return False
@app.route('/')
def index():
if 'supervisor_id' 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')
from flask import redirect, url_for
from flask import Flask, request, session, redirect, render_template, url_for, jsonify
from urllib.parse import quote
@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
supervisor_id = data.get('supervisor_id')
password = data.get('password')
if not supervisor_id or not password:
logger.warning("Login failed: Supervisor ID and password are required")
return jsonify({"status": "error", "message": "Supervisor ID and password are required"}), 400
if supervisor_id == 'GUEST':
session['supervisor_id'] = 'GUEST'
logger.info("Guest login successful")
return jsonify({"status": "success", "message": "Logged in as guest", "redirect": "/dashboard"})
try:
sf = get_salesforce_connection()
logger.debug(f"Querying Salesforce for Supervisor_ID__c: {supervisor_id}")
supervisor_id_escaped = quote(supervisor_id, safe='')
query = f"SELECT Id, Name, Password__c FROM Supervisor__c WHERE Name = '{supervisor_id_escaped}' LIMIT 1"
result = sf.query(query)
logger.debug(f"Salesforce query result: {result}")
if not result['records']:
logger.warning(f"Invalid Supervisor ID: {supervisor_id}")
return jsonify({"status": "error", "message": "Invalid Supervisor ID"}), 401
record = result['records'][0]
stored_password = record['Password__c']
if not stored_password:
logger.warning(f"No password set for Supervisor ID: {supervisor_id}")
return jsonify({"status": "error", "message": "No password set for this Supervisor ID"}), 401
if not verify_password(password, stored_password):
logger.warning(f"Invalid password for Supervisor ID: {supervisor_id}")
return jsonify({"status": "error", "message": "Invalid password"}), 401
session['supervisor_id'] = supervisor_id
logger.info(f"Login successful for {supervisor_id}")
return jsonify({"status": "success", "message": "Logged in as guest", "redirect": "/dashboard"})
except Exception as e:
logger.error(f"Login error: {str(e)}")
return jsonify({"status": "error", "message": str(e)}), 500
@app.route('/dashboard', methods=['GET'])
def dashboard():
"""
Serve the dashboard page after successful login.
"""
return render_template('dashboard.html')
@app.route('/home')
def home():
return render_template('home.html')
@app.route('/signup', methods=['POST'])
def signup():
data = request.get_json()
supervisor_id = data.get('supervisor_id')
password = data.get('password')
if not supervisor_id or not password:
logger.warning("Signup failed: Supervisor ID and password are required")
return jsonify({"status": "error", "message": "Supervisor ID and password are required"}), 400
try:
sf = get_salesforce_connection()
logger.debug(f"Checking if Supervisor_ID__c {supervisor_id} already exists")
supervisor_id_escaped = quote(supervisor_id, safe='')
query = f"SELECT Id FROM Supervisor__c WHERE Name = '{supervisor_id_escaped}' LIMIT 1"
result = sf.query(query)
if result['records']:
logger.warning(f"Signup failed: Supervisor ID {supervisor_id} already exists")
return jsonify({"status": "error", "message": "Supervisor ID already exists"}), 400
hashed_password = hash_password(password)
logger.debug(f"Creating new Supervisor__c record for {supervisor_id}")
new_record = {
'Name': supervisor_id,
'Password__c': hashed_password
}
response = sf.Supervisor__c.create(new_record)
logger.debug(f"Salesforce create response: {response}")
if not response.get('success'):
logger.error(f"Failed to create Supervisor record: {response.get('errors')}")
return jsonify({"status": "error", "message": f"Failed to create record in Salesforce: {response.get('errors')}"}), 500
session['supervisor_id'] = supervisor_id
logger.info(f"Signup successful for {supervisor_id}")
return jsonify({"status": "success", "message": "Signup successful, you are now logged in"})
except Exception as e:
logger.error(f"Signup error: {str(e)}")
return jsonify({"status": "error", "message": str(e)}), 500
def generate_coaching_output(data):
"""
Generate daily checklist and tips using Hugging Face LLM.
"""
logger.info("Generating coaching output for supervisor %s", data['supervisor_id'])
milestones_json = json.dumps(data['milestones'], indent=2)
prompt = f"""
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.
Supervisor Role: {data['role']}
Project Milestones: {milestones_json}
Reflection Log: {data['reflection_log']}
Weather: {data['weather']}
Format the response as JSON:
{{
"checklist": ["item1", "item2", ...],
"tips": ["tip1", "tip2", "tip3"],
"quote": "motivational quote"
}}
"""
headers = {
"Authorization": f"Bearer {HUGGING_FACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"inputs": prompt,
"parameters": {
"max_length": 200,
"temperature": 0.7,
"top_p": 0.9
}
}
try:
response = requests.post(HUGGING_FACE_API_URL, headers=headers, json=payload, timeout=5)
response.raise_for_status()
result = response.json()
generated_text = result[0]["generated_text"] if isinstance(result, list) else result["generated_text"]
start_idx = generated_text.find('{')
end_idx = generated_text.rfind('}') + 1
if start_idx == -1 or end_idx == 0:
logger.error("No valid JSON found in LLM output")
raise ValueError("No valid JSON found in LLM output")
json_str = generated_text[start_idx:end_idx]
output = json.loads(json_str)
logger.info("Successfully generated coaching output")
return output
except requests.exceptions.HTTPError as e:
logger.error("Hugging Face API HTTP error: %s", e)
return None
except (json.JSONDecodeError, ValueError) as e:
logger.error("Error parsing LLM output: %s", e)
return None
except Exception as e:
logger.error("Unexpected error in Hugging Face API call: %s", e)
return None
def save_to_salesforce(output, supervisor_id, project_id):
"""
Save coaching output to Salesforce Supervisor_AI_Coaching__c object.
"""
if not output:
logger.error("No coaching output to save")
return False
try:
sf = Salesforce(
username=SALESFORCE_USERNAME,
password=SALESFORCE_PASSWORD,
security_token=SALESFORCE_SECURITY_TOKEN,
domain=SALESFORCE_DOMAIN
)
logger.info("Connected to Salesforce")
coaching_record = {
"Supervisor_ID__c": supervisor_id,
"Project_ID__c": project_id,
"Daily_Checklist__c": "\n".join(output["checklist"]),
"Suggested_Tips__c": "\n".join(output["tips"]),
"Quote__c": output["quote"],
"Generated_Date__c": datetime.now().strftime("%Y-%m-%d")
}
sf.Supervisor_AI_Coaching__c.upsert(
f"Supervisor_ID__c/{supervisor_id}_{datetime.now().strftime('%Y-%m-%d')}",
coaching_record
)
logger.info("Successfully saved coaching record to Salesforce for supervisor %s", supervisor_id)
return True
except Exception as e:
logger.error("Salesforce error: %s", e)
return False
@app.route('/', methods=['GET'])
def redirect_to_ui():
"""
Redirect root URL to the UI.
"""
return redirect(url_for('ui'))
@app.route('/ui', methods=['GET'])
def ui():
"""
Serve the HTML user interface.
"""
return render_template('home.html')
@app.route('/generate', methods=['POST'])
def generate_endpoint():
"""
Endpoint to generate coaching output based on supervisor data.
"""
try:
data = request.get_json()
if not data or not all(key in data for key in ['supervisor_id', 'role', 'project_id', 'milestones', 'reflection_log', 'weather']):
return jsonify({"status": "error", "message": "Invalid or missing supervisor data"}), 400
coaching_output = generate_coaching_output(data)
if coaching_output:
success = save_to_salesforce(coaching_output, data["supervisor_id"], data["project_id"])
if success:
return jsonify({"status": "success", "output": coaching_output}), 200
else:
return jsonify({"status": "error", "message": "Failed to save to Salesforce"}), 500
else:
return jsonify({"status": "error", "message": "Failed to generate coaching output"}), 500
except Exception as e:
logger.error("Error in generate endpoint: %s", e)
return jsonify({"status": "error", "message": str(e)}), 500
@app.route('/health', methods=['GET'])
def health_check():
"""
Health check endpoint.
"""
return jsonify({"status": "healthy", "message": "Application is running"}), 200
@app.route('/logout', methods=['POST'])
def logout():
supervisor_id = session.get('supervisor_id', 'Unknown')
session.pop('supervisor_id', None)
logger.info(f"User {supervisor_id} logged out")
return jsonify({"status": "success", "message": "Logged out successfully"})
@app.route('/get_supervisor_data')
def get_supervisor_data():
supervisor_id = session.get('supervisor_id', 'GUEST')
if supervisor_id == 'GUEST':
logger.info("Returning mock data for guest user")
return jsonify({"status": "success", "data": MOCK_DATA})
try:
sf = get_salesforce_connection()
supervisor_id_escaped = quote(supervisor_id, safe='')
query = f"""
SELECT Supervisor_ID__c, Project_ID__c
FROM Supervisor__c
WHERE Supervisor_ID__c = '{supervisor_id_escaped}'
LIMIT 1
"""
result = sf.query(query)
if result['records']:
record = result['records'][0]
data = {
"supervisor_id": record['Supervisor_ID__c'],
"project_id": record['Project_ID__c'],
"last_login": str(datetime.now())
}
logger.info(f"Fetched data for supervisor {supervisor_id}")
return jsonify({"status": "success", "data": data})
else:
logger.warning(f"No data found for supervisor {supervisor_id}")
return jsonify({"status": "error", "message": "No data found for this supervisor"}), 404
except Exception as e:
logger.error(f"Error fetching supervisor data: {str(e)}")
return jsonify({"status": "error", "message": str(e)}), 500
if __name__ == '__main__':
port = int(os.getenv('PORT', 5000))
app.run(host='0.0.0.0', port=port, debug=True) |