Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,301 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import torch
|
3 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
4 |
+
from simple_salesforce import Salesforce
|
5 |
+
import os
|
6 |
+
import base64
|
7 |
+
import datetime
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
from fpdf import FPDF
|
10 |
+
import shutil
|
11 |
+
import html
|
12 |
+
import io
|
13 |
+
import matplotlib.pyplot as plt
|
14 |
+
import numpy as np
|
15 |
+
|
16 |
+
# Load environment variables
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
# Required env vars check
|
20 |
+
required_env_vars = ['SF_USERNAME', 'SF_PASSWORD', 'SF_SECURITY_TOKEN']
|
21 |
+
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
|
22 |
+
if missing_vars:
|
23 |
+
raise EnvironmentError(f"Missing required environment variables: {missing_vars}")
|
24 |
+
|
25 |
+
# Load model and tokenizer
|
26 |
+
model_name = "distilgpt2"
|
27 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
|
28 |
+
model = AutoModelForCausalLM.from_pretrained(model_name, low_cpu_mem_usage=True)
|
29 |
+
|
30 |
+
if tokenizer.pad_token is None:
|
31 |
+
tokenizer.pad_token = tokenizer.eos_token
|
32 |
+
tokenizer.pad_token_id = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)
|
33 |
+
model.config.pad_token_id = tokenizer.pad_token_id
|
34 |
+
|
35 |
+
# Function to generate progress chart
|
36 |
+
def show_dashboard_chart(start_date, end_date, tasks_completed):
|
37 |
+
completed_tasks = list(tasks_completed.values())
|
38 |
+
labels = list(tasks_completed.keys())
|
39 |
+
remaining_tasks = [5 - task for task in completed_tasks] # Assuming 5 tasks per date
|
40 |
+
|
41 |
+
# Create the bar chart with completed and remaining tasks
|
42 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
43 |
+
ax.bar(labels, completed_tasks, color='green', label="Completed")
|
44 |
+
ax.bar(labels, remaining_tasks, bottom=completed_tasks, color='gray', label="Remaining")
|
45 |
+
|
46 |
+
ax.set_xlabel("Dates")
|
47 |
+
ax.set_ylabel("Task Progress")
|
48 |
+
ax.set_title(f"Task Completion Progress from {start_date} to {end_date}")
|
49 |
+
ax.legend()
|
50 |
+
plt.xticks(rotation=45)
|
51 |
+
chart_image = io.BytesIO()
|
52 |
+
plt.savefig(chart_image, format='png')
|
53 |
+
chart_image.seek(0)
|
54 |
+
return chart_image
|
55 |
+
|
56 |
+
# Function to get the data from Salesforce
|
57 |
+
def get_dashboard_data_from_salesforce(supervisor_name, project_id):
|
58 |
+
try:
|
59 |
+
sf = Salesforce(
|
60 |
+
username=os.getenv('SF_USERNAME'),
|
61 |
+
password=os.getenv('SF_PASSWORD'),
|
62 |
+
security_token=os.getenv('SF_SECURITY_TOKEN'),
|
63 |
+
domain=os.getenv('SF_DOMAIN', 'login')
|
64 |
+
)
|
65 |
+
|
66 |
+
# Get the start and end date from Salesforce
|
67 |
+
query = sf.query(f"SELECT Start_Date__c, End_Date__c FROM Project__c WHERE Name = '{project_id}' LIMIT 1")
|
68 |
+
if query['totalSize'] == 0:
|
69 |
+
return "", "", None, "Project not found"
|
70 |
+
|
71 |
+
start_date_str = query['records'][0]['Start_Date__c']
|
72 |
+
end_date_str = query['records'][0]['End_Date__c']
|
73 |
+
|
74 |
+
# Convert the string dates to datetime objects
|
75 |
+
start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d")
|
76 |
+
end_date = datetime.datetime.strptime(end_date_str, "%Y-%m-%d")
|
77 |
+
|
78 |
+
# Generate task dates and simulated completion data
|
79 |
+
task_dates = [start_date + datetime.timedelta(days=i) for i in range((end_date - start_date).days + 1)]
|
80 |
+
tasks_completed = {str(task_dates[i].date()): np.random.randint(1, 6) for i in range(len(task_dates))}
|
81 |
+
|
82 |
+
chart_image = show_dashboard_chart(start_date, end_date, tasks_completed)
|
83 |
+
return start_date, end_date, chart_image, f"Project {project_id} Task Progress"
|
84 |
+
except Exception as e:
|
85 |
+
return "", "", None, f"Error: {str(e)}"
|
86 |
+
|
87 |
+
# Clean text for PDF generation
|
88 |
+
def clean_text_for_pdf(text):
|
89 |
+
return html.unescape(text).encode('latin-1', 'replace').decode('latin-1')
|
90 |
+
|
91 |
+
# Function to save report as PDF
|
92 |
+
def save_report_as_pdf(role, supervisor_name, project_id, checklist, suggestions):
|
93 |
+
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
94 |
+
filename = f"report_{supervisor_name}_{project_id}_{now}.pdf"
|
95 |
+
file_path = f"./reports/{filename}"
|
96 |
+
os.makedirs("reports", exist_ok=True)
|
97 |
+
|
98 |
+
pdf = FPDF()
|
99 |
+
pdf.add_page()
|
100 |
+
pdf.set_font("Arial", 'B', 14)
|
101 |
+
pdf.cell(200, 10, txt="Supervisor Daily Report", ln=True, align="C")
|
102 |
+
pdf.set_font("Arial", size=12)
|
103 |
+
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Role: {role}"), ln=True)
|
104 |
+
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Supervisor: {supervisor_name}"), ln=True)
|
105 |
+
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Project ID: {project_id}"), ln=True)
|
106 |
+
pdf.ln(5)
|
107 |
+
pdf.set_font("Arial", 'B', 12)
|
108 |
+
pdf.cell(200, 10, txt="Daily Checklist", ln=True)
|
109 |
+
pdf.set_font("Arial", size=12)
|
110 |
+
for line in checklist.split("\n"):
|
111 |
+
pdf.multi_cell(0, 10, clean_text_for_pdf(line))
|
112 |
+
pdf.ln(5)
|
113 |
+
pdf.set_font("Arial", 'B', 12)
|
114 |
+
pdf.cell(200, 10, txt="Focus Suggestions", ln=True)
|
115 |
+
pdf.set_font("Arial", size=12)
|
116 |
+
for line in suggestions.split("\n"):
|
117 |
+
pdf.multi_cell(0, 10, clean_text_for_pdf(line))
|
118 |
+
pdf.output(file_path)
|
119 |
+
|
120 |
+
temp_pdf_path = "/tmp/" + os.path.basename(file_path)
|
121 |
+
shutil.copy(file_path, temp_pdf_path)
|
122 |
+
return temp_pdf_path, filename
|
123 |
+
|
124 |
+
# Function to get roles from Salesforce
|
125 |
+
def get_roles_from_salesforce():
|
126 |
+
try:
|
127 |
+
sf = Salesforce(
|
128 |
+
username=os.getenv('SF_USERNAME'),
|
129 |
+
password=os.getenv('SF_PASSWORD'),
|
130 |
+
security_token=os.getenv('SF_SECURITY_TOKEN'),
|
131 |
+
domain=os.getenv('SF_DOMAIN', 'login')
|
132 |
+
)
|
133 |
+
result = sf.query("SELECT Role__c FROM Supervisor__c WHERE Role__c != NULL")
|
134 |
+
return list(set(record['Role__c'] for record in result['records']))
|
135 |
+
except Exception as e:
|
136 |
+
return []
|
137 |
+
|
138 |
+
# Function to get supervisor names based on role
|
139 |
+
def get_supervisor_name_by_role(role):
|
140 |
+
try:
|
141 |
+
sf = Salesforce(
|
142 |
+
username=os.getenv('SF_USERNAME'),
|
143 |
+
password=os.getenv('SF_PASSWORD'),
|
144 |
+
security_token=os.getenv('SF_SECURITY_TOKEN'),
|
145 |
+
domain=os.getenv('SF_DOMAIN', 'login')
|
146 |
+
)
|
147 |
+
result = sf.query(f"SELECT Name FROM Supervisor__c WHERE Role__c = '{role}'")
|
148 |
+
return [record['Name'] for record in result['records']]
|
149 |
+
except Exception as e:
|
150 |
+
return []
|
151 |
+
|
152 |
+
# Function to get the project name for a supervisor
|
153 |
+
def get_projects_for_supervisor(supervisor_name):
|
154 |
+
try:
|
155 |
+
sf = Salesforce(
|
156 |
+
username=os.getenv('SF_USERNAME'),
|
157 |
+
password=os.getenv('SF_PASSWORD'),
|
158 |
+
security_token=os.getenv('SF_SECURITY_TOKEN'),
|
159 |
+
domain=os.getenv('SF_DOMAIN', 'login')
|
160 |
+
)
|
161 |
+
result = sf.query(f"SELECT Id FROM Supervisor__c WHERE Name = '{supervisor_name}' LIMIT 1")
|
162 |
+
if result['totalSize'] == 0:
|
163 |
+
return ""
|
164 |
+
supervisor_id = result['records'][0]['Id']
|
165 |
+
project_result = sf.query(f"SELECT Name FROM Project__c WHERE Supervisor_ID__c = '{supervisor_id}' LIMIT 1")
|
166 |
+
return project_result['records'][0]['Name'] if project_result['totalSize'] > 0 else ""
|
167 |
+
except Exception as e:
|
168 |
+
return ""
|
169 |
+
|
170 |
+
# Function to generate daily checklist and focus suggestions
|
171 |
+
def generate_checklist_and_suggestions(role, project_id, milestones, reflection):
|
172 |
+
prompt = f"""
|
173 |
+
You are a supervisor assistant. Given the role {role}, project {project_id}, milestones {milestones}, and reflection log {reflection}, generate:
|
174 |
+
|
175 |
+
1. A Daily Checklist with clear and concise tasks.
|
176 |
+
2. Focus Suggestions based on concerns or keywords from the reflection log.
|
177 |
+
"""
|
178 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
179 |
+
outputs = model.generate(inputs['input_ids'], max_length=200, num_return_sequences=1)
|
180 |
+
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
181 |
+
|
182 |
+
# Split generated text into checklist and suggestions
|
183 |
+
parts = generated_text.split("\n")
|
184 |
+
checklist = "\n".join(parts[:len(parts)//2])
|
185 |
+
suggestions = "\n".join(parts[len(parts)//2:])
|
186 |
+
|
187 |
+
return checklist, suggestions
|
188 |
+
|
189 |
+
# Function to upload the report and create the Supervisor AI Coaching record in Salesforce
|
190 |
+
def upload_report_and_create_supervisor_ai_coaching(supervisor_name, project_id, checklist, suggestions, pdf_path, pdf_name):
|
191 |
+
try:
|
192 |
+
sf = Salesforce(
|
193 |
+
username=os.getenv('SF_USERNAME'),
|
194 |
+
password=os.getenv('SF_PASSWORD'),
|
195 |
+
security_token=os.getenv('SF_SECURITY_TOKEN'),
|
196 |
+
domain=os.getenv('SF_DOMAIN', 'login')
|
197 |
+
)
|
198 |
+
|
199 |
+
# Upload the PDF file to Salesforce as Content Version
|
200 |
+
with open(pdf_path, "rb") as f:
|
201 |
+
encoded = base64.b64encode(f.read()).decode()
|
202 |
+
|
203 |
+
content = sf.ContentVersion.create({
|
204 |
+
'Title': pdf_name,
|
205 |
+
'PathOnClient': pdf_name,
|
206 |
+
'VersionData': encoded
|
207 |
+
})
|
208 |
+
|
209 |
+
content_id = content['id']
|
210 |
+
download_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{content_id}"
|
211 |
+
|
212 |
+
# Create a Supervisor AI Coaching record
|
213 |
+
query = sf.query(f"SELECT Id FROM Supervisor__c WHERE Name = '{supervisor_name}' LIMIT 1")
|
214 |
+
supervisor_id = query['records'][0]['Id'] if query['totalSize'] > 0 else None
|
215 |
+
if not supervisor_id:
|
216 |
+
return "Supervisor not found."
|
217 |
+
|
218 |
+
project_query = sf.query(f"SELECT Id FROM Project__c WHERE Name = '{project_id}' LIMIT 1")
|
219 |
+
project_id_sf = project_query['records'][0]['Id'] if project_query['totalSize'] > 0 else None
|
220 |
+
if not project_id_sf:
|
221 |
+
return "Project not found."
|
222 |
+
|
223 |
+
# Create Supervisor AI Coaching record with all necessary fields
|
224 |
+
sf.Supervisor_AI_Coaching__c.create({
|
225 |
+
'Project_ID__c': project_id_sf,
|
226 |
+
'Supervisor_ID__c': supervisor_id,
|
227 |
+
'Daily_Checklist__c': checklist,
|
228 |
+
'Suggested_Tips__c': suggestions,
|
229 |
+
'Download_Link__c': download_url
|
230 |
+
})
|
231 |
+
|
232 |
+
return "Supervisor AI Coaching record created and report uploaded successfully."
|
233 |
+
|
234 |
+
except Exception as e:
|
235 |
+
return f"Error: {str(e)}"
|
236 |
+
|
237 |
+
# Gradio interface
|
238 |
+
def create_interface():
|
239 |
+
roles = get_roles_from_salesforce() # Get roles from Salesforce dynamically
|
240 |
+
with gr.Blocks(theme="soft", css=".footer { display: none; }") as demo:
|
241 |
+
gr.Markdown("## π§ AI-Powered Supervisor Assistant")
|
242 |
+
|
243 |
+
with gr.Row():
|
244 |
+
role = gr.Dropdown(choices=roles, label="Role")
|
245 |
+
supervisor_name = gr.Dropdown(choices=[], label="Supervisor Name")
|
246 |
+
project_id = gr.Textbox(label="Project ID", interactive=False)
|
247 |
+
|
248 |
+
milestones = gr.Textbox(label="Milestones (comma-separated KPIs)")
|
249 |
+
reflection = gr.Textbox(label="Reflection Log", lines=4)
|
250 |
+
|
251 |
+
with gr.Row():
|
252 |
+
generate = gr.Button("Generate")
|
253 |
+
clear = gr.Button("Clear")
|
254 |
+
refresh = gr.Button("π Refresh Roles")
|
255 |
+
|
256 |
+
checklist_output = gr.Textbox(label="β
Daily Checklist")
|
257 |
+
suggestions_output = gr.Textbox(label="π‘ Focus Suggestions")
|
258 |
+
download_button = gr.File(label="β¬ Download Report")
|
259 |
+
pdf_link = gr.HTML()
|
260 |
+
|
261 |
+
role.change(fn=lambda r: gr.update(choices=get_supervisor_name_by_role(r)), inputs=role, outputs=supervisor_name)
|
262 |
+
supervisor_name.change(fn=get_projects_for_supervisor, inputs=supervisor_name, outputs=project_id)
|
263 |
+
|
264 |
+
def handle_generate(role, supervisor_name, project_id, milestones, reflection):
|
265 |
+
checklist, suggestions = generate_checklist_and_suggestions(role, project_id, milestones, reflection)
|
266 |
+
pdf_path, pdf_name = save_report_as_pdf(role, supervisor_name, project_id, checklist, suggestions)
|
267 |
+
supervisor_ai_coaching_response = upload_report_and_create_supervisor_ai_coaching(supervisor_name, project_id, checklist, suggestions, pdf_path, pdf_name)
|
268 |
+
return checklist, suggestions, pdf_path, pdf_name, supervisor_ai_coaching_response
|
269 |
+
|
270 |
+
generate.click(fn=handle_generate,
|
271 |
+
inputs=[role, supervisor_name, project_id, milestones, reflection],
|
272 |
+
outputs=[checklist_output, suggestions_output, download_button, pdf_link, gr.HTML()])
|
273 |
+
|
274 |
+
clear.click(fn=lambda: ("", "", "", "", ""),
|
275 |
+
inputs=None,
|
276 |
+
outputs=[role, supervisor_name, project_id, milestones, reflection])
|
277 |
+
|
278 |
+
refresh.click(fn=lambda: gr.update(choices=get_roles_from_salesforce()), outputs=role)
|
279 |
+
|
280 |
+
# Supervisor Dashboard Tab
|
281 |
+
with gr.Tab("π Supervisor Dashboard"):
|
282 |
+
dash_supervisor = gr.Textbox(label="Supervisor Name", placeholder="e.g., SUP-056")
|
283 |
+
dash_project = gr.Textbox(label="Project ID", placeholder="e.g., PROJ-078")
|
284 |
+
load_dash = gr.Button("π₯ Load Dashboard")
|
285 |
+
dash_output = gr.HTML()
|
286 |
+
|
287 |
+
def show_dashboard_html(sup_name, proj_id):
|
288 |
+
start_date, end_date, chart_image, chart_title = get_dashboard_data_from_salesforce(sup_name, proj_id)
|
289 |
+
if chart_image:
|
290 |
+
chart_html = f"<img src='data:image/png;base64,{base64.b64encode(chart_image.read()).decode()}' />"
|
291 |
+
return f"<h3>{chart_title}</h3><p>From {start_date} to {end_date}</p>{chart_html}"
|
292 |
+
else:
|
293 |
+
return f"Error: {chart_title}"
|
294 |
+
|
295 |
+
load_dash.click(fn=show_dashboard_html, inputs=[dash_supervisor, dash_project], outputs=dash_output)
|
296 |
+
|
297 |
+
return demo
|
298 |
+
|
299 |
+
if __name__ == "__main__":
|
300 |
+
app = create_interface()
|
301 |
+
app.launch()
|