import gradio as gr
import os
import base64
import datetime
from dotenv import load_dotenv
from fpdf import FPDF
import shutil
import html
import io
import matplotlib.pyplot as plt
import numpy as np
from simple_salesforce import Salesforce
# Load environment variables
load_dotenv()
# Required env vars check
required_env_vars = ['SF_USERNAME', 'SF_PASSWORD', 'SF_SECURITY_TOKEN']
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
if missing_vars:
raise EnvironmentError(f"Missing required environment variables: {missing_vars}")
# Defaults
KPI_FLAG_DEFAULT = os.getenv('KPI_FLAG', 'True') == 'True'
ENGAGEMENT_SCORE_DEFAULT = float(os.getenv('ENGAGEMENT_SCORE', '85.0'))
# Function to generate progress chart
def show_dashboard_chart(start_date, end_date, tasks_completed):
completed_tasks = list(tasks_completed.values())
labels = list(tasks_completed.keys())
remaining_tasks = [5 - task for task in completed_tasks] # Assuming 5 tasks per date
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(labels, completed_tasks, color='green', label="Completed")
ax.bar(labels, remaining_tasks, bottom=completed_tasks, color='gray', label="Remaining")
ax.set_xlabel("Dates")
ax.set_ylabel("Task Progress")
ax.set_title(f"Task Completion Progress from {start_date} to {end_date}")
ax.legend()
plt.xticks(rotation=45)
chart_image = io.BytesIO()
plt.savefig(chart_image, format='png')
chart_image.seek(0)
return chart_image
# Function to get the data from Salesforce
def get_dashboard_data_from_salesforce(supervisor_name, project_id):
try:
sf = Salesforce(
username=os.getenv('SF_USERNAME'),
password=os.getenv('SF_PASSWORD'),
security_token=os.getenv('SF_SECURITY_TOKEN'),
domain=os.getenv('SF_DOMAIN', 'login')
)
query = sf.query(f"SELECT Start_Date__c, End_Date__c FROM Project__c WHERE Name = '{project_id}' LIMIT 1")
if query['totalSize'] == 0:
return "", "", None, "Project not found"
start_date_str = query['records'][0]['Start_Date__c']
end_date_str = query['records'][0]['End_Date__c']
start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d")
end_date = datetime.datetime.strptime(end_date_str, "%Y-%m-%d")
task_dates = [start_date + datetime.timedelta(days=i) for i in range((end_date - start_date).days + 1)]
tasks_completed = {str(task_dates[i].date()): np.random.randint(1, 6) for i in range(len(task_dates))}
chart_image = show_dashboard_chart(start_date, end_date, tasks_completed)
return start_date, end_date, chart_image, f"Project {project_id} Task Progress"
except Exception as e:
return "", "", None, f"Error: {str(e)}"
# Clean text for PDF generation
def clean_text_for_pdf(text):
return html.unescape(text).encode('latin-1', 'replace').decode('latin-1')
# Function to save report as PDF
def save_report_as_pdf(role, supervisor_name, project_id, checklist, suggestions):
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
filename = f"report_{supervisor_name}_{project_id}_{now}.pdf"
file_path = f"./reports/{filename}"
os.makedirs("reports", exist_ok=True)
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", 'B', 14)
pdf.cell(200, 10, txt="Supervisor Daily Report", ln=True, align="C")
pdf.set_font("Arial", size=12)
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Role: {role}"), ln=True)
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Supervisor: {supervisor_name}"), ln=True)
pdf.cell(200, 10, txt=clean_text_for_pdf(f"Project ID: {project_id}"), ln=True)
pdf.ln(5)
pdf.set_font("Arial", 'B', 12)
pdf.cell(200, 10, txt="Daily Checklist", ln=True)
pdf.set_font("Arial", size=12)
for line in checklist.split("\n"):
pdf.multi_cell(0, 10, clean_text_for_pdf(line))
pdf.ln(5)
pdf.set_font("Arial", 'B', 12)
pdf.cell(200, 10, txt="Focus Suggestions", ln=True)
pdf.set_font("Arial", size=12)
for line in suggestions.split("\n"):
pdf.multi_cell(0, 10, clean_text_for_pdf(line))
pdf.output(file_path)
temp_pdf_path = "/tmp/" + os.path.basename(file_path)
shutil.copy(file_path, temp_pdf_path)
return temp_pdf_path, filename
# Function to get roles from Salesforce
def get_roles_from_salesforce():
try:
sf = Salesforce(
username=os.getenv('SF_USERNAME'),
password=os.getenv('SF_PASSWORD'),
security_token=os.getenv('SF_SECURITY_TOKEN'),
domain=os.getenv('SF_DOMAIN', 'login')
)
result = sf.query("SELECT Role__c FROM Supervisor__c WHERE Role__c != NULL")
return list(set(record['Role__c'] for record in result['records']))
except Exception as e:
return []
# Function to get supervisor names based on role
def get_supervisor_name_by_role(role):
try:
sf = Salesforce(
username=os.getenv('SF_USERNAME'),
password=os.getenv('SF_PASSWORD'),
security_token=os.getenv('SF_SECURITY_TOKEN'),
domain=os.getenv('SF_DOMAIN', 'login')
)
result = sf.query(f"SELECT Name FROM Supervisor__c WHERE Role__c = '{role}'")
return [record['Name'] for record in result['records']]
except Exception as e:
return []
# Function to get the project name for a supervisor
def get_projects_for_supervisor(supervisor_name):
try:
sf = Salesforce(
username=os.getenv('SF_USERNAME'),
password=os.getenv('SF_PASSWORD'),
security_token=os.getenv('SF_SECURITY_TOKEN'),
domain=os.getenv('SF_DOMAIN', 'login')
)
result = sf.query(f"SELECT Id FROM Supervisor__c WHERE Name = '{supervisor_name}' LIMIT 1")
if result['totalSize'] == 0:
return ""
supervisor_id = result['records'][0]['Id']
project_result = sf.query(f"SELECT Name FROM Project__c WHERE Supervisor_ID__c = '{supervisor_id}' LIMIT 1")
return project_result['records'][0]['Name'] if project_result['totalSize'] > 0 else ""
except Exception as e:
return ""
# Function to generate daily checklist and focus suggestions
def generate_checklist_and_suggestions(role, project_id, milestones, reflection):
if not all([role, project_id, milestones, reflection]):
return "Please fill all fields.", ""
# Generate Checklist
checklist_items = []
if role.lower() == "site supervisor":
checklist_items.append("- Conduct daily site safety briefing")
checklist_items.append("- Monitor progress against project timeline")
milestones_lower = milestones.lower()
if "excavation" in milestones_lower:
checklist_items.append("- Verify excavation progress and safety measures")
if "foundation work" in milestones_lower:
checklist_items.append("- Inspect foundation work for quality")
if "pour the foundation concrete" in milestones_lower:
checklist_items.append("- Ensure proper concrete pouring and curing")
if "set up reinforcement bars" in milestones_lower:
checklist_items.append("- Check reinforcement bar installation")
if "test soil compaction" in milestones_lower:
checklist_items.append("- Conduct soil compaction tests")
if not checklist_items:
checklist_items.append("- Perform general site inspection")
checklist = "\n".join(checklist_items)
# Generate Suggestions
suggestions_items = []
reflection_lower = reflection.lower()
if "weather conditions" in reflection_lower or "rainfall" in reflection_lower:
suggestions_items.append("- Adjust schedule for weather delays")
suggestions_items.append("- Implement weather protection measures")
if "delays" in reflection_lower:
suggestions_items.append("- Identify causes of delays and mitigate")
if "leveling" in reflection_lower or "base" in reflection_lower:
suggestions_items.append("- Verify leveling accuracy and resolve issues")
if not suggestions_items:
suggestions_items.append("- Monitor team performance")
suggestions_items.append("- Review safety protocols")
suggestions = "\n".join(suggestions_items)
return checklist, suggestions
# Function to upload the report and create the Supervisor AI Coaching record in Salesforce
def upload_report_and_create_supervisor_ai_coaching(supervisor_name, project_id, checklist, suggestions, pdf_path, pdf_name):
try:
sf = Salesforce(
username=os.getenv('SF_USERNAME'),
password=os.getenv('SF_PASSWORD'),
security_token=os.getenv('SF_SECURITY_TOKEN'),
domain=os.getenv('SF_DOMAIN', 'login')
)
with open(pdf_path, "rb") as f:
encoded = base64.b64encode(f.read()).decode()
content = sf.ContentVersion.create({
'Title': pdf_name,
'PathOnClient': pdf_name,
'VersionData': encoded
})
content_id = content['id']
download_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{content_id}"
query = sf.query(f"SELECT Id FROM Supervisor__c WHERE Name = '{supervisor_name}' LIMIT 1")
supervisor_id = query['records'][0]['Id'] if query['totalSize'] > 0 else None
if not supervisor_id:
return "Supervisor not found."
project_query = sf.query(f"SELECT Id FROM Project__c WHERE Name = '{project_id}' LIMIT 1")
project_id_sf = project_query['records'][0]['Id'] if project_query['totalSize'] > 0 else None
if not project_id_sf:
return "Project not found."
sf.Supervisor_AI_Coaching__c.create({
'Project_ID__c': project_id_sf,
'Supervisor_ID__c': supervisor_id,
'Daily_Checklist__c': checklist,
'Suggested_Tips__c': suggestions,
'Download_Link__c': download_url
})
return "Supervisor AI Coaching record created and report uploaded successfully."
except Exception as e:
return f"Error: {str(e)}"
# Gradio interface
def create_interface():
roles = get_roles_from_salesforce()
with gr.Blocks(theme="soft", css=".footer { display: none; }") as demo:
gr.Markdown("## 🧠AI-Powered Supervisor Assistant")
with gr.Row():
role = gr.Dropdown(choices=roles, label="Role", value="Site Supervisor")
supervisor_name = gr.Dropdown(choices=["SUP-056"], label="Supervisor Name", value="SUP-056")
project_id = gr.Textbox(label="Project ID", value="PROJ-078", interactive=False)
milestones = gr.Textbox(label="Milestones (comma-separated KPIs)", value="Excavation and Foundation Work, Pour the foundation concrete, Set up reinforcement bars for the foundation, Test soil compaction")
reflection = gr.Textbox(label="Reflection Log", lines=4, value="The excavation and foundation work was slower than expected due to unexpected weather conditions. We faced some delays because of heavy rainfall. The foundation team had some minor issues with leveling the base but they managed to resolve it in the end.")
with gr.Row():
generate = gr.Button("Generate")
clear = gr.Button("Clear")
refresh = gr.Button("🔄 Refresh Roles")
checklist_output = gr.Textbox(label="✅ Daily Checklist")
suggestions_output = gr.Textbox(label="💡 Focus Suggestions")
download_button = gr.File(label="⬇ Download Report")
pdf_link = gr.HTML()
role.change(fn=lambda r: gr.update(choices=get_supervisor_name_by_role(r)), inputs=role, outputs=supervisor_name)
supervisor_name.change(fn=get_projects_for_supervisor, inputs=supervisor_name, outputs=project_id)
def handle_generate(role, supervisor_name, project_id, milestones, reflection):
checklist, suggestions = generate_checklist_and_suggestions(role, project_id, milestones, reflection)
if not checklist or not suggestions:
return "Error generating checklist or suggestions.", "Error generating checklist or suggestions.", None, None, "Error: Generation failed."
pdf_path, pdf_name = save_report_as_pdf(role, supervisor_name, project_id, checklist, suggestions)
supervisor_ai_coaching_response = upload_report_and_create_supervisor_ai_coaching(supervisor_name, project_id, checklist, suggestions, pdf_path, pdf_name)
pdf_link_content = f'Download PDF'
return checklist, suggestions, pdf_path, pdf_link_content, supervisor_ai_coaching_response
generate.click(fn=handle_generate,
inputs=[role, supervisor_name, project_id, milestones, reflection],
outputs=[checklist_output, suggestions_output, download_button, pdf_link, gr.HTML()])
clear.click(fn=lambda: ("", "", "", "", ""),
inputs=None,
outputs=[role, supervisor_name, project_id, milestones, reflection])
refresh.click(fn=lambda: gr.update(choices=get_roles_from_salesforce()), outputs=role)
with gr.Tab("📊 Supervisor Dashboard"):
dash_supervisor = gr.Textbox(label="Supervisor Name", placeholder="e.g., SUP-056")
dash_project = gr.Textbox(label="Project ID", placeholder="e.g., PROJ-078")
load_dash = gr.Button("📥 Load Dashboard")
dash_output = gr.HTML()
def show_dashboard_html(sup_name, proj_id):
start_date, end_date, chart_image, chart_title = get_dashboard_data_from_salesforce(sup_name, proj_id)
if chart_image:
chart_html = f""
return f"
From {start_date} to {end_date}
{chart_html}" else: return f"Error: {chart_title}" load_dash.click(fn=show_dashboard_html, inputs=[dash_supervisor, dash_project], outputs=dash_output) return demo if __name__ == "__main__": app = create_interface() app.launch()