RathodHarish commited on
Commit
6b41ed3
·
verified ·
1 Parent(s): a6bde15

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +266 -214
app.py CHANGED
@@ -1,303 +1,355 @@
1
- from flask import Flask, request, jsonify
2
- from simple_salesforce import Salesforce
3
  import pandas as pd
4
  from datetime import datetime
5
  import logging
6
- from sklearn.ensemble import IsolationForest
 
7
  from transformers import pipeline
8
- import torch
9
- import os
10
- import time
11
- import sys
12
- import requests
13
- from requests.exceptions import Timeout
14
 
15
- # Configure logging to console first
16
- logging.basicConfig(
17
- level=logging.INFO,
18
- format='%(asctime)s - %(levelname)s - %(message)s',
19
- handlers=[
20
- logging.StreamHandler(sys.stdout)
21
- ]
22
- )
23
 
24
- # Add file handler for logging
 
 
 
 
 
 
 
25
  try:
26
- file_handler = logging.FileHandler('app.log')
27
- file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
28
- logging.getLogger().addHandler(file_handler)
29
- logging.info("File logging enabled successfully")
30
  except Exception as e:
31
- logging.warning(f"Failed to enable file logging: {str(e)}. Continuing with console logging only.")
32
-
33
- # Initialize Flask app
34
- app = Flask(__name__)
35
- logging.info("Flask app initialized")
36
-
37
- # Salesforce credentials (hardcoded for testing)
38
- SF_USERNAME = "multi-devicelabopsdashboard@sathkrutha.com"
39
- SF_PASSWORD = "Team@1234"
40
- SF_SECURITY_TOKEN = "BXgWWNXjvc3zJmVv2O7JfBqCc"
41
- SF_INSTANCE_URL = "https://multi-devicelabopsdashboard-dev-ed.develop.my.salesforce.com"
42
-
43
- # Global variable for Salesforce connection
44
- sf = None
45
-
46
- # Global variable for Hugging Face model (lazy initialization)
47
- summarizer = None
48
- logging.info("Hugging Face model set to lazy initialization")
49
 
50
- # Health check endpoint
51
- @app.route('/health', methods=['GET'])
52
- def health_check():
53
- return jsonify({"status": "App is running"}), 200
54
-
55
- # Connect to Salesforce with a timeout
56
  def connect_to_salesforce():
57
- global sf
58
- logging.info("Attempting to connect to Salesforce...")
59
- start_time = time.time()
60
  try:
61
- session = requests.Session()
62
- adapter = requests.adapters.HTTPAdapter(max_retries=3)
63
- session.mount('https://', adapter)
64
- session.request('GET', SF_INSTANCE_URL, timeout=10)
65
  sf = Salesforce(
66
- username=SF_USERNAME,
67
- password=SF_PASSWORD,
68
- security_token=SF_SECURITY_TOKEN,
69
- instance_url=SF_INSTANCE_URL,
70
- session=session
71
  )
72
- logging.info(f"Connected to Salesforce successfully in {time.time() - start_time:.2f} seconds")
73
- except Timeout:
74
- logging.error("Salesforce connection timed out after 10 seconds")
75
- sf = None
76
  except Exception as e:
77
  logging.error(f"Failed to connect to Salesforce: {str(e)}")
78
- sf = None
79
-
80
- # Lazy load the Hugging Face model
81
- def load_huggingface_model():
82
- global summarizer
83
- if summarizer is None:
84
- logging.info("Loading Hugging Face model (t5-small)...")
85
- start_time = time.time()
86
- try:
87
- device = 0 if torch.cuda.is_available() else -1
88
- summarizer = pipeline("summarization", model="t5-small", device=device)
89
- logging.info(f"Hugging Face model loaded successfully in {time.time() - start_time:.2f} seconds on device: {'GPU' if device == 0 else 'CPU'}")
90
- except Exception as e:
91
- logging.error(f"Failed to load Hugging Face model: {str(e)}")
92
- summarizer = None
93
 
94
- # Fetch SmartLog records from Salesforce
95
- def fetch_smartlog_records(lab_site, start_date, end_date, equipment_type):
96
- if sf is None:
97
- raise Exception("Salesforce connection not established")
98
  try:
99
- logging.info("Fetching SmartLog records from Salesforce...")
100
- query = "SELECT Device_Id__c, Log_Type__c, Status__c, Timestamp__c, Usage_Hours__c, Downtime__c, AMC_Date__c FROM SmartLog__c WHERE "
101
- conditions = []
102
- params = {}
103
- if lab_site:
104
- conditions.append("Lab_Site__c = :lab_site")
105
- params['lab_site'] = lab_site
106
- if start_date:
107
- conditions.append("Timestamp__c >= :start_date")
108
- params['start_date'] = start_date
109
- if end_date:
110
- conditions.append("Timestamp__c <= :end_date")
111
- params['end_date'] = end_date
112
- if equipment_type:
113
- conditions.append("Log_Type__c = :equipment_type")
114
- params['equipment_type'] = equipment_type
115
-
116
- if not conditions:
117
- query = query.replace(" WHERE ", "")
118
- else:
119
- query += " AND ".join(conditions)
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- result = sf.query_all(query, **params)
122
- records = result['records']
 
 
 
 
 
 
 
 
123
 
124
- data = []
125
- for record in records:
126
- data.append({
127
- 'device_id': record['Device_Id__c'],
128
- 'log_type': record['Log_Type__c'],
129
- 'status': record['Status__c'],
130
- 'timestamp': record['Timestamp__c'],
131
- 'usage_hours': record['Usage_Hours__c'],
132
- 'downtime': record['Downtime__c'],
133
- 'amc_date': record['AMC_Date__c']
134
- })
135
- df = pd.DataFrame(data)
136
- df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce')
137
- df['amc_date'] = pd.to_datetime(df['amc_date'], errors='coerce')
138
- logging.info(f"Fetched {len(df)} SmartLog records")
 
139
  return df
140
  except Exception as e:
141
- logging.error(f"Failed to fetch SmartLog records: {str(e)}")
142
  raise e
143
 
144
  # Format summary prompt and generate report
145
- def summarize_logs(df):
146
- load_huggingface_model()
147
- if summarizer is None:
148
- return "Failed to load summarization model."
149
  try:
150
  total_devices = df["device_id"].nunique()
151
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
 
152
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
153
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
 
154
  return summary
155
  except Exception as e:
156
  logging.error(f"Summary generation failed: {str(e)}")
157
  return "Failed to generate summary."
158
 
159
- # Anomaly Detection
160
- def detect_anomalies(df):
 
161
  try:
162
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
163
- return None, "Anomaly detection requires 'usage_hours' and 'downtime' columns."
 
 
164
  if len(df) > 5000:
165
  df = df.sample(n=5000, random_state=42)
166
  logging.info("Sampled data for anomaly detection to 5,000 rows")
 
167
  features = df[["usage_hours", "downtime"]].fillna(0)
168
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
169
  df["anomaly"] = iso_forest.fit_predict(features)
 
170
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
171
  if anomalies.empty:
172
- return None, "No anomalies detected."
173
- anomaly_lines = []
 
174
  for idx, row in anomalies.head(5).iterrows():
175
- anomaly_lines.append({
176
- "device_id": row['device_id'],
177
- "usage_hours": float(row['usage_hours']),
178
- "downtime": float(row['downtime']),
179
- "timestamp": row['timestamp'].isoformat()
180
- })
181
- return anomaly_lines, None
182
  except Exception as e:
183
  logging.error(f"Anomaly detection failed: {str(e)}")
184
- return None, f"Anomaly detection failed: {str(e)}"
185
 
186
- # AMC Reminders
187
- def check_amc_reminders(df, current_date):
 
188
  try:
189
  if "device_id" not in df.columns or "amc_date" not in df.columns:
190
- return None, "AMC reminders require 'device_id' and 'amc_date' columns."
 
 
 
191
  current_date = pd.to_datetime(current_date)
 
192
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
193
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
 
194
  if reminders.empty:
195
- return None, "No AMC reminders due within the next 30 days."
196
- reminder_lines = []
 
197
  for idx, row in reminders.head(5).iterrows():
198
- reminder_lines.append({
199
- "device_id": row['device_id'],
200
- "amc_date": row['amc_date'].isoformat()
201
- })
202
- return reminder_lines, None
203
  except Exception as e:
204
  logging.error(f"AMC reminder generation failed: {str(e)}")
205
- return None, f"AMC reminder generation failed: {str(e)}"
206
 
207
- # Dashboard Insights
208
- def generate_dashboard_insights(df):
209
- load_huggingface_model()
210
- if summarizer is None:
211
- return "Failed to load summarization model."
212
  try:
213
  total_devices = df["device_id"].nunique()
214
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
215
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
216
  insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
 
217
  return insights
218
  except Exception as e:
219
  logging.error(f"Dashboard insights generation failed: {str(e)}")
220
  return f"Dashboard insights generation failed: {str(e)}"
221
 
222
- # Create usage chart data for LaTeX table
223
- def create_usage_chart_data(df):
 
224
  try:
225
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
226
  if len(usage_data) > 5:
227
  usage_data = usage_data.nlargest(5, "usage_hours")
228
  logging.info("Limited chart data to top 5 devices")
229
- chart_lines = []
230
- for idx, row in usage_data.iterrows():
231
- chart_lines.append({
232
- "device_id": row['device_id'],
233
- "usage_hours": float(row['usage_hours'])
234
- })
235
- return chart_lines
 
 
 
 
 
 
 
 
 
 
 
 
236
  except Exception as e:
237
- logging.error(f"Failed to create usage chart data: {str(e)}")
238
- return []
239
 
240
- # HTTP endpoint to process logs
241
- @app.route('/process_logs', methods=['POST'])
242
- def process_logs():
243
  try:
244
- if sf is None:
245
- return jsonify({"error": "Salesforce connection not established. Check server logs for details."}), 500
246
-
247
- data = request.get_json()
248
- lab_site = data.get('lab_site')
249
- start_date = data.get('start_date')
250
- end_date = data.get('end_date')
251
- equipment_type = data.get('equipment_type')
252
- amc_threshold = data.get('amc_threshold', 30)
253
 
254
- df = fetch_smartlog_records(lab_site, start_date, end_date, equipment_type)
255
  if df.empty:
256
- return jsonify({"error": "No data available in SmartLog__c."}), 400
 
257
 
258
- summary = summarize_logs(df)
 
 
259
 
260
- preview_lines = []
261
- for idx, row in df.head().iterrows():
262
- preview_lines.append({
263
- "row": idx + 1,
264
- "device_id": row['device_id'],
265
- "timestamp": row['timestamp'].isoformat() if pd.notnull(row['timestamp']) else None,
266
- "usage_hours": float(row['usage_hours']) if pd.notnull(row['usage_hours']) else 0,
267
- "downtime": float(row['downtime']) if pd.notnull(row['downtime']) else 0,
268
- "amc_date": row['amc_date'].isoformat() if pd.notnull(row['amc_date']) else None
269
- })
270
 
271
- chart_data = create_usage_chart_data(df)
 
272
 
273
- anomaly_lines, anomaly_error = detect_anomalies(df)
274
- if anomaly_error:
275
- anomaly_lines = [{"error": anomaly_error}]
276
 
277
- reminder_lines, reminder_error = check_amc_reminders(df, datetime.now())
278
- if reminder_error:
279
- reminder_lines = [{"error": reminder_error}]
280
 
281
- insights = generate_dashboard_insights(df)
 
282
 
283
- response = {
284
- "summary": summary,
285
- "log_preview": preview_lines,
286
- "usage_chart": chart_data,
287
- "anomalies": anomaly_lines,
288
- "amc_reminders": reminder_lines,
289
- "insights": insights
290
- }
291
 
292
- return jsonify(response), 200
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
- except Exception as e:
295
- logging.error(f"Failed to process logs: {str(e)}")
296
- return jsonify({"error": str(e)}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
  if __name__ == "__main__":
299
- logging.info("Starting Flask application...")
300
- start_time = time.time()
301
- connect_to_salesforce()
302
- logging.info(f"Flask application startup completed in {time.time() - start_time:.2f} seconds")
303
- app.run(host="0.0.0.0", port=5000, debug=True)
 
 
 
 
1
+ import gradio as gr
 
2
  import pandas as pd
3
  from datetime import datetime
4
  import logging
5
+ import plotly.express as px
6
+ from sklearn.ensemble import IsolationForest # For anomaly detection
7
  from transformers import pipeline
8
+ import torch # For GPU availability check
9
+ from simple_salesforce import Salesforce # For Salesforce connection
 
 
 
 
10
 
11
+ # Configure logging for debugging
12
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
 
 
 
 
 
13
 
14
+ # Salesforce credentials (replace with your actual credentials or use environment variables)
15
+ SALESFORCE_USERNAME = "your_username"
16
+ SALESFORCE_PASSWORD = "your_password"
17
+ SALESFORCE_SECURITY_TOKEN = "your_security_token"
18
+ SALESFORCE_DOMAIN = "login" # Use "test" for sandbox, "login" for production
19
+
20
+ # Preload Hugging Face summarization model at startup
21
+ logging.info("Preloading Hugging Face model...")
22
  try:
23
+ device = 0 if torch.cuda.is_available() else -1
24
+ summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=device) # Lighter model
25
+ logging.info(f"Hugging Face model preloaded successfully on device: {'GPU' if device == 0 else 'CPU'}")
 
26
  except Exception as e:
27
+ logging.error(f"Failed to preload model: {str(e)}")
28
+ raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ # Connect to Salesforce
 
 
 
 
 
31
  def connect_to_salesforce():
 
 
 
32
  try:
 
 
 
 
33
  sf = Salesforce(
34
+ username=SALESFORCE_USERNAME,
35
+ password=SALESFORCE_PASSWORD,
36
+ security_token=SALESFORCE_SECURITY_TOKEN,
37
+ domain=SALESFORCE_DOMAIN
 
38
  )
39
+ logging.info("Successfully connected to Salesforce")
40
+ return sf
 
 
41
  except Exception as e:
42
  logging.error(f"Failed to connect to Salesforce: {str(e)}")
43
+ raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ # Fetch data from Salesforce
46
+ def fetch_salesforce_data(sf, row_limit=10000, progress=gr.Progress()):
47
+ progress(0.05, "Fetching data from Salesforce...")
 
48
  try:
49
+ # Query Salesforce for LabEquipmentLog__c object
50
+ query = """
51
+ SELECT Device_ID__c, Log_Type__c, Status__c, Timestamp__c,
52
+ Usage_Hours__c, Downtime__c, AMC_Date__c
53
+ FROM LabEquipmentLog__c
54
+ LIMIT {}
55
+ """.format(row_limit)
56
+ result = sf.query_all(query)
57
+ records = result["records"]
58
+
59
+ # Convert to DataFrame
60
+ df = pd.DataFrame(records)
61
+ df = df.rename(columns={
62
+ "Device_ID__c": "device_id",
63
+ "Log_Type__c": "log_type",
64
+ "Status__c": "status",
65
+ "Timestamp__c": "timestamp",
66
+ "Usage_Hours__c": "usage_hours",
67
+ "Downtime__c": "downtime",
68
+ "AMC_Date__c": "amc_date"
69
+ })
70
+
71
+ # Ensure proper data types
72
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
73
+ df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
74
+ df["usage_hours"] = df["usage_hours"].astype("float32", errors='ignore')
75
+ df["downtime"] = df["downtime"].astype("float32", errors='ignore')
76
+ df["device_id"] = df["device_id"].astype("string")
77
+
78
+ logging.info(f"Fetched {len(df)} records from Salesforce")
79
+ return df
80
+ except Exception as e:
81
+ logging.error(f"Failed to fetch Salesforce data: {str(e)}")
82
+ raise e
83
 
84
+ # Load data from CSV file
85
+ def load_csv_data(file_obj, row_limit=10000, progress=gr.Progress()):
86
+ progress(0.05, "Loading CSV file...")
87
+ try:
88
+ file_name = file_obj.name if hasattr(file_obj, 'name') else file_obj
89
+ logging.info(f"Processing CSV file: {file_name}")
90
+
91
+ if not file_name.endswith(".csv"):
92
+ logging.error("Unsupported file format")
93
+ raise ValueError("Unsupported file format. Please upload a CSV file.")
94
 
95
+ usecols = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
96
+ dtypes = {
97
+ "device_id": "string",
98
+ "log_type": "string",
99
+ "status": "string",
100
+ "usage_hours": "float32",
101
+ "downtime": "float32",
102
+ "amc_date": "string"
103
+ }
104
+ df = pd.read_csv(file_name, usecols=usecols, dtype=dtypes, nrows=row_limit)
105
+
106
+ # Convert timestamps
107
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
108
+ df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
109
+
110
+ logging.info(f"File loaded successfully with {len(df)} rows (limited to {row_limit} rows)")
111
  return df
112
  except Exception as e:
113
+ logging.error(f"Failed to load CSV: {str(e)}")
114
  raise e
115
 
116
  # Format summary prompt and generate report
117
+ def summarize_logs(df, progress=gr.Progress()):
118
+ progress(0.1, "Generating summary report...")
 
 
119
  try:
120
  total_devices = df["device_id"].nunique()
121
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
122
+
123
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
124
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
125
+ logging.info("Summary generated successfully")
126
  return summary
127
  except Exception as e:
128
  logging.error(f"Summary generation failed: {str(e)}")
129
  return "Failed to generate summary."
130
 
131
+ # Anomaly Detection using Isolation Forest with sampling for large datasets
132
+ def detect_anomalies(df, progress=gr.Progress()):
133
+ progress(0.4, "Detecting anomalies...")
134
  try:
135
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
136
+ logging.warning("Required columns for anomaly detection not found")
137
+ return "Anomaly detection requires 'usage_hours' and 'downtime' columns."
138
+
139
  if len(df) > 5000:
140
  df = df.sample(n=5000, random_state=42)
141
  logging.info("Sampled data for anomaly detection to 5,000 rows")
142
+
143
  features = df[["usage_hours", "downtime"]].fillna(0)
144
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
145
  df["anomaly"] = iso_forest.fit_predict(features)
146
+
147
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
148
  if anomalies.empty:
149
+ return "No anomalies detected."
150
+
151
+ anomaly_lines = ["**Detected Anomalies:**"]
152
  for idx, row in anomalies.head(5).iterrows():
153
+ anomaly_lines.append(f"- Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}")
154
+ anomaly_list = "\n".join(anomaly_lines)
155
+ logging.info("Anomalies detected successfully")
156
+ return anomaly_list
 
 
 
157
  except Exception as e:
158
  logging.error(f"Anomaly detection failed: {str(e)}")
159
+ return f"Anomaly detection failed: {str(e)}"
160
 
161
+ # AMC Reminders based on device and AMC date
162
+ def check_amc_reminders(df, current_date, progress=gr.Progress()):
163
+ progress(0.6, "Checking AMC reminders...")
164
  try:
165
  if "device_id" not in df.columns or "amc_date" not in df.columns:
166
+ logging.warning("Required columns for AMC reminders not found")
167
+ return "AMC reminders require 'device_id' and 'amc_date' columns."
168
+
169
+ df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
170
  current_date = pd.to_datetime(current_date)
171
+
172
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
173
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
174
+
175
  if reminders.empty:
176
+ return "No AMC reminders due within the next 30 days."
177
+
178
+ reminder_lines = ["**Upcoming AMC Reminders:**"]
179
  for idx, row in reminders.head(5).iterrows():
180
+ reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
181
+ reminder_list = "\n".join(reminder_lines)
182
+ logging.info("AMC reminders generated successfully")
183
+ return reminder_list
 
184
  except Exception as e:
185
  logging.error(f"AMC reminder generation failed: {str(e)}")
186
+ return f"AMC reminder generation failed: {str(e)}"
187
 
188
+ # Dashboard Insights (AI-generated executive-level insights)
189
+ def generate_dashboard_insights(df, progress=gr.Progress()):
190
+ progress(0.8, "Generating dashboard insights...")
 
 
191
  try:
192
  total_devices = df["device_id"].nunique()
193
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
194
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
195
  insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
196
+ logging.info("Dashboard insights generated successfully")
197
  return insights
198
  except Exception as e:
199
  logging.error(f"Dashboard insights generation failed: {str(e)}")
200
  return f"Dashboard insights generation failed: {str(e)}"
201
 
202
+ # Create a bar chart for usage hours per device
203
+ def create_usage_chart(df, progress=gr.Progress()):
204
+ progress(0.9, "Creating usage chart...")
205
  try:
206
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
207
  if len(usage_data) > 5:
208
  usage_data = usage_data.nlargest(5, "usage_hours")
209
  logging.info("Limited chart data to top 5 devices")
210
+
211
+ custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
212
+ fig = px.bar(
213
+ usage_data,
214
+ x="device_id",
215
+ y="usage_hours",
216
+ title="Usage Hours per Device",
217
+ labels={"device_id": "Device ID", "usage_hours": "Usage Hours"},
218
+ color="device_id",
219
+ color_discrete_sequence=custom_colors
220
+ )
221
+ fig.update_layout(
222
+ title_font_size=16,
223
+ margin=dict(l=20, r=20, t=40, b=20),
224
+ plot_bgcolor="white",
225
+ paper_bgcolor="white",
226
+ font=dict(size=12)
227
+ )
228
+ return fig
229
  except Exception as e:
230
+ logging.error(f"Failed to create usage chart: {str(e)}")
231
+ return None
232
 
233
+ # Main Gradio function
234
+ async def process_logs(file_obj=None, progress=gr.Progress()):
 
235
  try:
236
+ progress(0, "Starting data processing...")
237
+
238
+ # Load data: prioritize CSV if uploaded, otherwise fetch from Salesforce
239
+ if file_obj:
240
+ df = load_csv_data(file_obj, row_limit=10000, progress=progress)
241
+ else:
242
+ progress(0.05, "No CSV uploaded, fetching from Salesforce...")
243
+ sf = connect_to_salesforce()
244
+ df = fetch_salesforce_data(sf, row_limit=10000, progress=progress)
245
 
 
246
  if df.empty:
247
+ logging.warning("No data available")
248
+ return "No data available.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated."
249
 
250
+ # Step 1: Summary Report
251
+ progress(0.2, "Generating summary...")
252
+ summary = f"**Step 1: Summary Report** \n{summarize_logs(df, progress)}"
253
 
254
+ # Step 2: Log Preview
255
+ progress(0.3, "Previewing logs...")
256
+ if not df.empty:
257
+ preview_lines = ["**Step 2: Log Preview (First 5 Rows)**"]
258
+ for idx, row in df.head().iterrows():
259
+ preview_lines.append(f"**Row {idx + 1}:** Device ID: {row['device_id']}, Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, AMC Date: {row['amc_date']}, Log Type: {row['log_type']}, Status: {row['status']}")
260
+ preview = "\n".join(preview_lines)
261
+ else:
262
+ preview = "**Step 2: Log Preview** \nNo data available."
 
263
 
264
+ # Step 3: Usage Chart
265
+ chart = create_usage_chart(df, progress)
266
 
267
+ # Step 4: Anomaly Detection
268
+ anomalies = f"**Step 3: Anomaly Detection** \n{detect_anomalies(df, progress)}"
 
269
 
270
+ # Step 5: AMC Reminders
271
+ amc_reminders = f"**Step 4: AMC Reminders** \n{check_amc_reminders(df, datetime.now(), progress)}"
 
272
 
273
+ # Step 6: Dashboard Insights
274
+ insights = f"**Step 5: Dashboard Insights (AI)** \n{generate_dashboard_insights(df, progress)}"
275
 
276
+ progress(1.0, "Processing complete!")
277
+ return summary, preview, chart, anomalies, amc_reminders, insights
278
+ except Exception as e:
279
+ logging.error(f"Failed to process data: {str(e)}")
280
+ return f"Failed to process data: {str(e)}", None, None, None, None, None
 
 
 
281
 
282
+ # Gradio Interface with Step-by-Step Layout
283
+ try:
284
+ logging.info("Initializing Gradio Blocks interface...")
285
+ with gr.Blocks(css="""
286
+ .dashboard-container {border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px; background-color: #f9f9f9;}
287
+ .dashboard-title {font-size: 24px; font-weight: bold; margin-bottom: 5px;}
288
+ .dashboard-section {margin-bottom: 5px;}
289
+ .dashboard-section h3 {font-size: 18px; margin-bottom: 2px;}
290
+ .dashboard-section p {margin: 1px 0; line-height: 1.2;}
291
+ .dashboard-section li {margin: 1px 0; line-height: 1.2;}
292
+ .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
293
+ """) as iface:
294
+ gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Salesforce + Hugging Face AI)</h1>")
295
+ gr.Markdown("Upload a CSV file or fetch lab equipment logs from Salesforce to analyze usage.")
296
 
297
+ with gr.Row():
298
+ with gr.Column(scale=1):
299
+ file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
300
+ submit_button = gr.Button("Analyze Data", variant="primary")
301
+
302
+ with gr.Column(scale=2):
303
+ with gr.Group(elem_classes="dashboard-container"):
304
+ gr.Markdown("<div class='dashboard-title'>Analysis Results (Step-by-Step)</div>")
305
+
306
+ # Step 1: Summary Report
307
+ with gr.Group(elem_classes="dashboard-section"):
308
+ gr.Markdown("### Step 1: Summary Report")
309
+ summary_output = gr.Markdown()
310
+
311
+ # Step 2: Log Preview
312
+ with gr.Group(elem_classes="dashboard-section"):
313
+ gr.Markdown("### Step 2: Log Preview")
314
+ preview_output = gr.Markdown()
315
+
316
+ # Step 3: Usage Chart
317
+ with gr.Group(elem_classes="dashboard-section"):
318
+ gr.Markdown("### Step 3: Usage Chart")
319
+ chart_output = gr.Plot()
320
+
321
+ # Step 4: Anomaly Detection
322
+ with gr.Group(elem_classes="dashboard-section"):
323
+ gr.Markdown("### Step 4: Anomaly Detection")
324
+ anomaly_output = gr.Markdown()
325
+
326
+ # Step 5: AMC Reminders
327
+ with gr.Group(elem_classes="dashboard-section"):
328
+ gr.Markdown("### Step 5: AMC Reminders")
329
+ amc_output = gr.Markdown()
330
+
331
+ # Step 6: Dashboard Insights
332
+ with gr.Group(elem_classes="dashboard-section"):
333
+ gr.Markdown("### Step 6: Dashboard Insights (AI)")
334
+ insights_output = gr.Markdown()
335
+
336
+ submit_button.click(
337
+ fn=process_logs,
338
+ inputs=[file_input],
339
+ outputs=[summary_output, preview_output, chart_output, anomaly_output, amc_output, insights_output]
340
+ )
341
+
342
+ logging.info("Gradio interface initialized successfully")
343
+ except Exception as e:
344
+ logging.error(f"Failed to initialize Gradio interface: {str(e)}")
345
+ raise e
346
 
347
  if __name__ == "__main__":
348
+ try:
349
+ logging.info("Launching Gradio interface...")
350
+ iface.launch(server_name="0.0.0.0", server_port=7860, debug=True, share=False)
351
+ logging.info("Gradio interface launched successfully")
352
+ except Exception as e:
353
+ logging.error(f"Failed to launch Gradio interface: {str(e)}")
354
+ print(f"Error launching app: {str(e)}")
355
+ raise e