geethareddy commited on
Commit
3c6b86c
·
verified ·
1 Parent(s): 5123b13

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -0
app.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import uvicorn
4
+ from fastapi import FastAPI, HTTPException
5
+ from fastapi.responses import FileResponse
6
+ from pydantic import BaseModel
7
+ from simple_salesforce import Salesforce
8
+ from contextlib import asynccontextmanager
9
+ import requests
10
+ from typing import Optional
11
+
12
+ # Set up logging early
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+ logger.info("Starting application initialization")
16
+
17
+ # Load environment variables
18
+ SF_USERNAME = os.getenv('SF_USERNAME')
19
+ SF_PASSWORD = os.getenv('SF_PASSWORD')
20
+ SF_SECURITY_TOKEN = os.getenv('SF_SECURITY_TOKEN')
21
+
22
+ # Validate environment variables
23
+ required_vars = {
24
+ 'SF_USERNAME': SF_USERNAME,
25
+ 'SF_PASSWORD': SF_PASSWORD,
26
+ 'SF_SECURITY_TOKEN': SF_SECURITY_TOKEN
27
+ }
28
+ missing_vars = [var for var, value in required_vars.items() if not value]
29
+ if missing_vars:
30
+ logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
31
+ raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}")
32
+
33
+ # Global Salesforce connection
34
+ sf = None
35
+
36
+ @asynccontextmanager
37
+ async def lifespan(app: FastAPI):
38
+ """Manage application lifecycle."""
39
+ global sf
40
+ logger.info("Starting application lifecycle")
41
+
42
+ # Initialize Salesforce connection
43
+ try:
44
+ sf = Salesforce(
45
+ username=SF_USERNAME,
46
+ password=SF_PASSWORD,
47
+ security_token=SF_SECURITY_TOKEN,
48
+ instance_url='https://aicoachforsitesupervisors-dev-ed.develop.my.salesforce.com',
49
+ version='63.0'
50
+ )
51
+ logger.info("Successfully connected to Salesforce")
52
+ except Exception as e:
53
+ logger.error(f"Failed to connect to Salesforce: {str(e)}")
54
+ sf = None
55
+
56
+ logger.info("Application initialized successfully")
57
+ yield
58
+ logger.info("Shutting down application")
59
+
60
+ # Initialize FastAPI app with lifespan
61
+ app = FastAPI(lifespan=lifespan)
62
+
63
+ # OpenWeatherMap API key (hardcoded as it works and doesn't cause issues)
64
+ WEATHER_API_KEY = "60c00e1b8293d3c0482f8d6ca1a37003"
65
+
66
+ # Pydantic model for request body
67
+ class ProjectRequest(BaseModel):
68
+ projectId: str
69
+ projectName: str
70
+ milestones: Optional[str] = None
71
+ weatherLogs: Optional[str] = None
72
+ safetyLogs: Optional[str] = None
73
+ role: str
74
+
75
+ def fetch_weather_data(location: str) -> str:
76
+ """Fetch weather data for a given location."""
77
+ try:
78
+ url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={WEATHER_API_KEY}&units=metric"
79
+ response = requests.get(url)
80
+ response.raise_for_status()
81
+ data = response.json()
82
+ weather = data['weather'][0]['description']
83
+ temp = data['main']['temp']
84
+ return f"{weather}, {temp}°C"
85
+ except Exception as e:
86
+ logger.error(f"Failed to fetch weather data: {str(e)}")
87
+ return "Weather data unavailable"
88
+
89
+ def generate_coaching_data(project: dict, role: str) -> dict:
90
+ """Mock AI model to generate checklist, tips, and engagement score."""
91
+ logger.info(f"Generating coaching data for project {project.get('Name')}")
92
+ checklist = f"1. Review safety protocols for {project.get('Project_Name__c', 'project')}\n2. Check {role} tasks\n3. Update milestones"
93
+ tips = f"1. Prioritize safety due to {project.get('Weather_Logs__c', 'conditions')}\n2. Focus on {role} responsibilities\n3. Communicate progress"
94
+ engagement_score = 85.0 # Mock score; replace with real AI model
95
+ return {
96
+ "checklist": checklist,
97
+ "tips": tips,
98
+ "engagementScore": engagement_score
99
+ }
100
+
101
+ @app.get("/api/latest-project")
102
+ async def get_latest_project():
103
+ """Fetch the latest Project__c record based on CreatedDate."""
104
+ if sf is None:
105
+ logger.error("Salesforce connection not initialized")
106
+ raise HTTPException(status_code=500, detail="Salesforce connection not initialized")
107
+
108
+ try:
109
+ query = """
110
+ SELECT Id, Name, Project_Name__c, Milestones__c, Weather_Logs__c, Safety_Logs__c, Location__c, Supervisor_ID__c
111
+ FROM Project__c
112
+ ORDER BY CreatedDate DESC
113
+ LIMIT 1
114
+ """
115
+ logger.info(f"Executing SOQL query for latest project: {query}")
116
+ result = sf.query(query)
117
+
118
+ if result['totalSize'] == 0:
119
+ logger.warning("No Project__c records found")
120
+ raise HTTPException(status_code=404, detail="No projects found")
121
+
122
+ project = result['records'][0]
123
+ logger.info(f"Fetched latest project: {project['Name']}")
124
+
125
+ # Prepare response with extracted fields
126
+ response = {
127
+ "projectId": project['Name'],
128
+ "projectName": project['Project_Name__c'],
129
+ "milestones": project.get('Milestones__c', ''),
130
+ "weatherLogs": project.get('Weather_Logs__c', ''),
131
+ "safetyLogs": project.get('Safety_Logs__c', ''),
132
+ "role": "Site Manager" # Default role; can be updated based on Supervisor_ID__c if needed
133
+ }
134
+
135
+ return response
136
+ except Exception as e:
137
+ logger.error(f"Error fetching latest project: {str(e)}")
138
+ raise HTTPException(status_code=500, detail=str(e))
139
+
140
+ @app.post("/api/generate")
141
+ async def generate_coaching(request: ProjectRequest):
142
+ if sf is None:
143
+ logger.error("Salesforce connection not initialized")
144
+ raise HTTPException(status_code=500, detail="Salesforce connection not initialized")
145
+
146
+ try:
147
+ # Query Project__c by projectId to get the full record
148
+ query = f"SELECT Id, Name, Project_Name__c, Milestones__c, Weather_Logs__c, Safety_Logs__c, Location__c, Supervisor_ID__c FROM Project__c WHERE Name = '{request.projectId}' LIMIT 1"
149
+ logger.info(f"Executing SOQL query: {query}")
150
+ result = sf.query(query)
151
+
152
+ if result['totalSize'] == 0:
153
+ logger.warning(f"Project {request.projectId} not found")
154
+ raise HTTPException(status_code=404, detail="Project not found")
155
+
156
+ project = result['records'][0]
157
+ logger.info(f"Retrieved project: {project['Name']}")
158
+
159
+ # Update weather logs if location available
160
+ if project.get('Location__c'):
161
+ project['Weather_Logs__c'] = fetch_weather_data(project['Location__c'])
162
+
163
+ # Generate coaching data
164
+ coaching_data = generate_coaching_data(project, request.role)
165
+
166
+ # Prepare response with extracted fields and generated results
167
+ response = {
168
+ "projectId": project['Name'],
169
+ "projectName": project['Project_Name__c'],
170
+ "milestones": project.get('Milestones__c', ''),
171
+ "weatherLogs": project.get('Weather_Logs__c', ''),
172
+ "safetyLogs": project.get('Safety_Logs__c', ''),
173
+ "role": request.role,
174
+ "checklist": coaching_data['checklist'],
175
+ "tips": coaching_data['tips'],
176
+ "engagementScore": coaching_data['engagementScore']
177
+ }
178
+
179
+ # Insert into Supervisor_AI_Coaching__c
180
+ try:
181
+ coaching_record = {
182
+ 'Project_ID__c': project['Id'], # Updated to correct API name for Lookup field
183
+ 'Checklist__c': coaching_data['checklist'],
184
+ 'Tips__c': coaching_data['tips'],
185
+ 'Engagement_Score__c': coaching_data['engagementScore'],
186
+ 'Role__c': request.role,
187
+ 'Project_Name__c': project['Project_Name__c'],
188
+ 'Milestones__c': project.get('Milestones__c', ''),
189
+ 'Weather_Logs__c': project.get('Weather_Logs__c', ''),
190
+ 'Safety_Logs__c': project.get('Safety_Logs__c', '')
191
+ }
192
+ sf.Supervisor_AI_Coaching__c.create(coaching_record)
193
+ logger.info(f"Inserted coaching data into Supervisor_AI_Coaching__c for project {project['Name']}")
194
+ except Exception as e:
195
+ logger.error(f"Failed to insert into Supervisor_AI_Coaching__c: {str(e)}")
196
+ raise HTTPException(status_code=500, detail=f"Failed to save coaching data: {str(e)}")
197
+
198
+ logger.info(f"Generated coaching response: {response}")
199
+ return response
200
+ except Exception as e:
201
+ logger.error(f"Error generating coaching data: {str(e)}")
202
+ raise HTTPException(status_code=500, detail=str(e))
203
+
204
+ @app.get("/")
205
+ async def root():
206
+ logger.info("Serving index.html from root directory")
207
+ return FileResponse("index.html")
208
+
209
+ @app.get("/styles.css")
210
+ async def serve_styles():
211
+ logger.info("Serving styles.css from root directory")
212
+ return FileResponse("styles.css", media_type="text/css")
213
+
214
+ # Start Uvicorn server for Hugging Face Spaces
215
+ logger.info("Starting Uvicorn server for Hugging Face Spaces")
216
+ uvicorn.run(app, host="0.0.0.0", port=7860)