Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -46,7 +46,11 @@ def connect_to_salesforce():
|
|
| 46 |
logger.error(f"Salesforce connection failed: {e}")
|
| 47 |
raise
|
| 48 |
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
# Choices
|
| 52 |
equipment_choices = [
|
|
@@ -77,66 +81,104 @@ def call_ai_model(usage, idle, freq, cost, last):
|
|
| 77 |
|
| 78 |
# Core processing
|
| 79 |
def process_equipment_utilization(equip, proj, use_h, idle_h, move_f, cost_h, last_maint, ai_sug):
|
| 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 |
# Format output
|
| 142 |
def format_output(result):
|
|
@@ -172,24 +214,52 @@ def format_output(result):
|
|
| 172 |
|
| 173 |
# Gradio callbacks
|
| 174 |
def manual_input(equipment, project, usage, idle, freq, cost, last, ai_suggestion):
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
def batch_upload(csv_file):
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
# Interface
|
| 195 |
with gr.Blocks() as app:
|
|
|
|
| 46 |
logger.error(f"Salesforce connection failed: {e}")
|
| 47 |
raise
|
| 48 |
|
| 49 |
+
try:
|
| 50 |
+
sf = connect_to_salesforce()
|
| 51 |
+
except Exception as e:
|
| 52 |
+
logger.error(f"Failed to initialize Salesforce connection: {e}")
|
| 53 |
+
sf = None
|
| 54 |
|
| 55 |
# Choices
|
| 56 |
equipment_choices = [
|
|
|
|
| 81 |
|
| 82 |
# Core processing
|
| 83 |
def process_equipment_utilization(equip, proj, use_h, idle_h, move_f, cost_h, last_maint, ai_sug):
|
| 84 |
+
try:
|
| 85 |
+
if not sf:
|
| 86 |
+
raise ValueError("Salesforce connection is not initialized. Please check credentials and try again.")
|
| 87 |
+
|
| 88 |
+
if not ai_sug:
|
| 89 |
+
ai_sug, conf, score = call_ai_model(use_h, idle_h, move_f, cost_h, last_maint)
|
| 90 |
+
else:
|
| 91 |
+
conf, score = 0.9, 85.0
|
| 92 |
+
summary = {
|
| 93 |
+
"Equipment Name": equip,
|
| 94 |
+
"Project": proj,
|
| 95 |
+
"Usage Hours": use_h,
|
| 96 |
+
"Idle Hours": idle_h,
|
| 97 |
+
"Suggestion": ai_sug,
|
| 98 |
+
"Confidence": conf,
|
| 99 |
+
"Utilization Score": score,
|
| 100 |
+
"Cost per Hour": cost_h,
|
| 101 |
+
"Last Maintenance": last_maint or "N/A"
|
| 102 |
+
}
|
| 103 |
+
record_data = {
|
| 104 |
+
"Equipment_Name__c": equip,
|
| 105 |
+
"Project_Name__c": proj,
|
| 106 |
+
"Usage_Hours__c": use_h,
|
| 107 |
+
"Idle_Hours__c": idle_h,
|
| 108 |
+
"AI_Suggestion__c": ai_sug,
|
| 109 |
+
"Suggestion_Confidence__c": conf * 100,
|
| 110 |
+
"Utilization_Score__c": score,
|
| 111 |
+
"Cost_per_Hour__c": cost_h,
|
| 112 |
+
"Report_Link__c": "Pending",
|
| 113 |
+
"Last_Maintenance__c": last_maint if last_maint != "N/A" else None,
|
| 114 |
+
"Dashboard_Flag__c": False
|
| 115 |
+
}
|
| 116 |
+
resp = sf.Equipment_Utilization_Record__c.create(record_data)
|
| 117 |
+
if not resp.get("success"):
|
| 118 |
+
raise ValueError(f"Failed to create Salesforce record: {resp}")
|
| 119 |
+
rec_id = resp.get("id")
|
| 120 |
+
if not rec_id:
|
| 121 |
+
raise ValueError("Salesforce record creation succeeded, but no record ID was returned.")
|
| 122 |
+
|
| 123 |
+
# Prepare paths
|
| 124 |
+
uid = uuid.uuid4().hex[:8]
|
| 125 |
+
pdf_path = Path(f"static/reports/report_{uid}.pdf")
|
| 126 |
+
csv_path = Path(f"static/reports/report_{uid}.csv")
|
| 127 |
+
try:
|
| 128 |
+
pdf_path.parent.mkdir(parents=True, exist_ok=True)
|
| 129 |
+
except Exception as e:
|
| 130 |
+
raise ValueError(f"Failed to create directory {pdf_path.parent}: {e}")
|
| 131 |
+
|
| 132 |
+
# Generate PDF
|
| 133 |
+
try:
|
| 134 |
+
c = canvas.Canvas(str(pdf_path), pagesize=letter)
|
| 135 |
+
c.setFont("Helvetica", 12)
|
| 136 |
+
c.drawString(100, 750, "Equipment Utilization Report")
|
| 137 |
+
c.drawString(100, 735, f"Record ID: {rec_id}")
|
| 138 |
+
y = 710
|
| 139 |
+
for k, v in summary.items():
|
| 140 |
+
c.drawString(100, y, f"{k}: {v}")
|
| 141 |
+
y -= 20
|
| 142 |
+
c.save()
|
| 143 |
+
except Exception as e:
|
| 144 |
+
raise ValueError(f"Failed to generate PDF: {e}")
|
| 145 |
+
|
| 146 |
+
# Generate CSV
|
| 147 |
+
try:
|
| 148 |
+
with open(csv_path, "w", newline="") as f:
|
| 149 |
+
w = csv.writer(f)
|
| 150 |
+
w.writerow(["Field", "Value"])
|
| 151 |
+
w.writerow(["Record ID", rec_id])
|
| 152 |
+
[w.writerow([k, v]) for k, v in summary.items()]
|
| 153 |
+
except Exception as e:
|
| 154 |
+
raise ValueError(f"Failed to generate CSV: {e}")
|
| 155 |
+
|
| 156 |
+
# Upload PDF to Salesforce
|
| 157 |
+
try:
|
| 158 |
+
encoded = base64.b64encode(pdf_path.read_bytes()).decode()
|
| 159 |
+
cv = sf.ContentVersion.create({
|
| 160 |
+
"Title": "UtilReport",
|
| 161 |
+
"PathOnClient": os.path.basename(str(pdf_path)),
|
| 162 |
+
"VersionData": encoded,
|
| 163 |
+
"FirstPublishLocationId": rec_id
|
| 164 |
+
})
|
| 165 |
+
if not cv.get("success"):
|
| 166 |
+
raise ValueError(f"Failed to upload PDF to Salesforce: {cv}")
|
| 167 |
+
pdf_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{cv['id']}"
|
| 168 |
+
sf.Equipment_Utilization_Record__c.update(rec_id, {"Report_Link__c": pdf_url})
|
| 169 |
+
except Exception as e:
|
| 170 |
+
raise ValueError(f"Failed to upload PDF to Salesforce: {e}")
|
| 171 |
+
|
| 172 |
+
return {
|
| 173 |
+
"Salesforce_Record_Id": rec_id,
|
| 174 |
+
"Summary": summary,
|
| 175 |
+
"Report_Link": pdf_url,
|
| 176 |
+
"CSV_Report_Link": str(csv_path),
|
| 177 |
+
"Report_File_Path": str(pdf_path)
|
| 178 |
+
}
|
| 179 |
+
except Exception as e:
|
| 180 |
+
logger.error(f"Error in process_equipment_utilization: {e}")
|
| 181 |
+
raise ValueError(f"Processing failed: {str(e)}")
|
| 182 |
|
| 183 |
# Format output
|
| 184 |
def format_output(result):
|
|
|
|
| 214 |
|
| 215 |
# Gradio callbacks
|
| 216 |
def manual_input(equipment, project, usage, idle, freq, cost, last, ai_suggestion):
|
| 217 |
+
try:
|
| 218 |
+
# Validate inputs
|
| 219 |
+
if not equipment or equipment not in equipment_choices:
|
| 220 |
+
raise ValueError("Please select a valid Equipment Name.")
|
| 221 |
+
if not project or project not in project_choices:
|
| 222 |
+
raise ValueError("Please select a valid Project Name.")
|
| 223 |
+
if usage is None or usage < 0:
|
| 224 |
+
raise ValueError("Usage Hours must be a non-negative number.")
|
| 225 |
+
if idle is None or idle < 0:
|
| 226 |
+
raise ValueError("Idle Hours must be a non-negative number.")
|
| 227 |
+
if freq is None or freq < 0:
|
| 228 |
+
raise ValueError("Movement Frequency must be a non-negative number.")
|
| 229 |
+
if cost is None or cost < 0:
|
| 230 |
+
raise ValueError("Cost per Hour must be a non-negative number.")
|
| 231 |
+
|
| 232 |
+
last_val = last or "N/A"
|
| 233 |
+
res = process_equipment_utilization(equipment, project, usage, idle, freq, cost, last_val, ai_suggestion)
|
| 234 |
+
formatted = format_output(res)
|
| 235 |
+
return formatted, res.get("Report_File_Path")
|
| 236 |
+
except Exception as e:
|
| 237 |
+
logger.error(f"Error in manual_input: {e}")
|
| 238 |
+
return f"Error: {str(e)}", None
|
| 239 |
|
| 240 |
def batch_upload(csv_file):
|
| 241 |
+
try:
|
| 242 |
+
if not csv_file:
|
| 243 |
+
raise ValueError("Please upload a CSV file.")
|
| 244 |
+
df = pd.read_csv(csv_file.name)
|
| 245 |
+
required_columns = ['equipment_name', 'project_name', 'usage_hours', 'idle_hours', 'movement_frequency', 'cost_per_hour']
|
| 246 |
+
missing_columns = [col for col in required_columns if col not in df.columns]
|
| 247 |
+
if missing_columns:
|
| 248 |
+
raise ValueError(f"CSV file is missing required columns: {', '.join(missing_columns)}")
|
| 249 |
+
|
| 250 |
+
ids = []
|
| 251 |
+
for _, row in df.iterrows():
|
| 252 |
+
rec = process_equipment_utilization(
|
| 253 |
+
row['equipment_name'], row['project_name'],
|
| 254 |
+
float(row['usage_hours']), float(row['idle_hours']),
|
| 255 |
+
float(row['movement_frequency']), float(row['cost_per_hour']),
|
| 256 |
+
row.get('last_maintenance', 'N/A'), row.get('ai_suggestion', '')
|
| 257 |
+
)
|
| 258 |
+
ids.append(rec['Salesforce_Record_Id'])
|
| 259 |
+
return {"records": ids}
|
| 260 |
+
except Exception as e:
|
| 261 |
+
logger.error(f"Error in batch_upload: {e}")
|
| 262 |
+
return {"error": str(e)}
|
| 263 |
|
| 264 |
# Interface
|
| 265 |
with gr.Blocks() as app:
|