RathodHarish commited on
Commit
95fa040
·
verified ·
1 Parent(s): 007a560

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -59
app.py CHANGED
@@ -3,6 +3,7 @@ import pandas as pd
3
  import matplotlib.pyplot as plt
4
  from fpdf import FPDF
5
  import io
 
6
 
7
  # Global DataFrame to store the CSV data
8
  df = pd.DataFrame()
@@ -10,106 +11,128 @@ df = pd.DataFrame()
10
  def upload_csv(file):
11
  global df
12
  try:
13
- # Check if a file was actually uploaded
14
  if file is None:
15
- return ["All"], ["All"], "No file uploaded. Please upload a CSV file.", "All", "All"
16
 
17
- # Read the CSV file directly from the file object
18
  df = pd.read_csv(file)
19
 
20
- # Check if the DataFrame is empty
21
  if df.empty:
22
- return ["All"], ["All"], "The uploaded CSV file is empty.", "All", "All"
23
 
24
  # Define required columns
25
- required_columns = {'DeviceID', 'Lab', 'Type', 'Timestamp', 'Status', 'UsageCount'}
26
-
27
- # Check if all required columns are present
28
  if not required_columns.issubset(df.columns):
29
  missing_cols = required_columns - set(df.columns)
30
- return ["All"], ["All"], f"CSV is missing required columns: {', '.join(missing_cols)}", "All", "All"
 
 
 
31
 
32
- # Extract unique values for dropdowns, ensuring no empty or invalid entries
33
  labs = ['All'] + sorted([str(lab) for lab in df['Lab'].dropna().unique() if str(lab).strip()])
34
  types = ['All'] + sorted([str(type_) for type_ in df['Type'].dropna().unique() if str(type_).strip()])
35
 
36
- # If no valid labs or types are found, return default values
37
- if len(labs) == 1: # Only "All" is present
38
- labs = ["All"]
39
- if len(types) == 1: # Only "All" is present
40
- types = ["All"]
41
 
42
- return labs, types, "", "All", "All"
43
  except Exception as e:
44
- # Handle any other errors (e.g., file not readable, invalid CSV format)
45
- return ["All"], ["All"], f"Failed to load CSV: {str(e)}", "All", "All"
46
 
47
- def filter_and_plot(selected_lab, selected_type):
48
  global df
49
- # If no data is loaded, return None (no plot)
50
  if df.empty:
51
- return None
52
 
53
- # Create a copy of the DataFrame for filtering
54
  filtered_df = df.copy()
55
-
56
- # Apply filters based on dropdown selections
57
  if selected_lab != "All":
58
  filtered_df = filtered_df[filtered_df["Lab"] == selected_lab]
59
  if selected_type != "All":
60
  filtered_df = filtered_df[filtered_df["Type"] == selected_type]
 
 
 
 
 
61
 
62
- # If no data remains after filtering, return None
63
  if filtered_df.empty:
64
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- # Prepare the plot
 
 
 
 
67
  plt.figure(figsize=(8, 4))
68
- status_counts = filtered_df["Status"].value_counts()
69
- status_counts.plot(kind="bar", color=["green", "red"])
70
- plt.title("Status Counts")
71
- plt.xlabel("Status")
72
- plt.ylabel("Count")
 
 
 
 
73
 
74
- # Save the plot to a buffer
75
- buf = io.BytesIO()
76
- plt.savefig(buf, format="png")
77
- plt.close() # Close the plot to free memory
78
- buf.seek(0)
79
- return buf
 
 
80
 
81
- def download_pdf(selected_lab, selected_type):
82
  global df
83
- # If no data is loaded, return None (no PDF)
84
  if df.empty:
85
  return None
86
 
87
- # Create a copy of the DataFrame for filtering
88
  filtered_df = df.copy()
89
-
90
- # Apply filters based on dropdown selections
91
  if selected_lab != "All":
92
  filtered_df = filtered_df[filtered_df["Lab"] == selected_lab]
93
  if selected_type != "All":
94
  filtered_df = filtered_df[filtered_df["Type"] == selected_type]
 
 
 
 
 
95
 
96
- # If no data remains after filtering, return None
97
  if filtered_df.empty:
98
  return None
99
 
100
- # Generate the PDF
101
  pdf = FPDF()
102
  pdf.add_page()
103
  pdf.set_font("Arial", size=12)
104
- pdf.cell(200, 10, txt="LabOps Summary Report", ln=True, align='C')
105
  pdf.ln(10)
106
 
107
- # Add data to the PDF
108
  for index, row in filtered_df.iterrows():
109
- line = f"{row['Timestamp']} | {row['DeviceID']} | {row['Lab']} | {row['Type']} | {row['Status']} | {row['UsageCount']}"
110
  pdf.multi_cell(0, 10, txt=line)
111
 
112
- # Save the PDF to a buffer
113
  output = io.BytesIO()
114
  pdf.output(output)
115
  output.seek(0)
@@ -117,27 +140,50 @@ def download_pdf(selected_lab, selected_type):
117
 
118
  # Build the Gradio interface
119
  with gr.Blocks() as demo:
120
- gr.Markdown("🧪 **LabOps Dashboard with Filters**\nUpload lab device logs, filter by Lab and Equipment Type, visualize uptime/downtime & generate PDF reports.")
121
 
122
  with gr.Row():
123
- csv_input = gr.File(label="Upload Log CSV", file_types=[".csv"])
124
 
125
  with gr.Row():
126
- lab_dropdown = gr.Dropdown(label="Select Lab", choices=["All"], value="All")
127
- type_dropdown = gr.Dropdown(label="Select Equipment Type", choices=["All"], value="All")
 
 
 
 
 
 
128
 
129
- plot_output = gr.Image(label="Plot")
130
- download_btn = gr.Button("Download PDF Summary")
131
  error_box = gr.Textbox(label="Status/Error Message", visible=True, interactive=False)
132
 
133
  # Connect the components
134
  csv_input.change(
135
  fn=upload_csv,
136
  inputs=csv_input,
137
- outputs=[lab_dropdown, type_dropdown, error_box, lab_dropdown, type_dropdown]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  )
139
- lab_dropdown.change(fn=filter_and_plot, inputs=[lab_dropdown, type_dropdown], outputs=plot_output)
140
- type_dropdown.change(fn=filter_and_plot, inputs=[lab_dropdown, type_dropdown], outputs=plot_output)
141
- download_btn.click(fn=download_pdf, inputs=[lab_dropdown, type_dropdown], outputs=gr.File(label="Download PDF"))
142
 
143
  demo.launch()
 
3
  import matplotlib.pyplot as plt
4
  from fpdf import FPDF
5
  import io
6
+ from datetime import datetime, timedelta
7
 
8
  # Global DataFrame to store the CSV data
9
  df = pd.DataFrame()
 
11
  def upload_csv(file):
12
  global df
13
  try:
 
14
  if file is None:
15
+ return ["All"], ["All"], ["All"], "No file uploaded. Please upload a CSV file.", "All", "All", "All"
16
 
17
+ # Read the CSV file
18
  df = pd.read_csv(file)
19
 
 
20
  if df.empty:
21
+ return ["All"], ["All"], ["All"], "The uploaded CSV file is empty.", "All", "All", "All"
22
 
23
  # Define required columns
24
+ required_columns = {'DeviceID', 'Lab', 'Type', 'Timestamp', 'Status', 'UsageCount', 'Health'}
 
 
25
  if not required_columns.issubset(df.columns):
26
  missing_cols = required_columns - set(df.columns)
27
+ return ["All"], ["All"], ["All"], f"CSV is missing required columns: {', '.join(missing_cols)}", "All", "All", "All"
28
+
29
+ # Convert Timestamp to datetime
30
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'])
31
 
32
+ # Extract unique values for dropdowns
33
  labs = ['All'] + sorted([str(lab) for lab in df['Lab'].dropna().unique() if str(lab).strip()])
34
  types = ['All'] + sorted([str(type_) for type_ in df['Type'].dropna().unique() if str(type_).strip()])
35
 
36
+ # Extract date range for filter
37
+ min_date = df['Timestamp'].min().strftime('%Y-%m-%d')
38
+ max_date = df['Timestamp'].max().strftime('%Y-%m-%d')
39
+ date_ranges = ['All', f"{min_date} to {max_date}"]
 
40
 
41
+ return labs, types, date_ranges, "", "All", "All", "All"
42
  except Exception as e:
43
+ return ["All"], ["All"], ["All"], f"Failed to load CSV: {str(e)}", "All", "All", "All"
 
44
 
45
+ def filter_and_visualize(selected_lab, selected_type, selected_date_range):
46
  global df
 
47
  if df.empty:
48
+ return None, None, None, None, "No data available."
49
 
50
+ # Filter the DataFrame
51
  filtered_df = df.copy()
 
 
52
  if selected_lab != "All":
53
  filtered_df = filtered_df[filtered_df["Lab"] == selected_lab]
54
  if selected_type != "All":
55
  filtered_df = filtered_df[filtered_df["Type"] == selected_type]
56
+ if selected_date_range != "All" and selected_date_range != "No data available.":
57
+ start_date, end_date = selected_date_range.split(" to ")
58
+ start_date = pd.to_datetime(start_date)
59
+ end_date = pd.to_datetime(end_date) + timedelta(days=1) # Include end date
60
+ filtered_df = filtered_df[(filtered_df["Timestamp"] >= start_date) & (filtered_df["Timestamp"] < end_date)]
61
 
 
62
  if filtered_df.empty:
63
+ return None, None, None, None, "No data matches the selected filters."
64
+
65
+ # Device Cards (as a table)
66
+ device_cards = filtered_df[['DeviceID', 'Lab', 'Type', 'Health', 'UsageCount', 'Timestamp']].sort_values(by='Timestamp', ascending=False)
67
+
68
+ # Daily Log Trends (Line Chart)
69
+ daily_logs = filtered_df.groupby(filtered_df['Timestamp'].dt.date).size()
70
+ plt.figure(figsize=(8, 4))
71
+ daily_logs.plot(kind='line', marker='o', color='blue')
72
+ plt.title("Daily Log Trends")
73
+ plt.xlabel("Date")
74
+ plt.ylabel("Number of Logs")
75
+ plt.xticks(rotation=45)
76
+ buf1 = io.BytesIO()
77
+ plt.savefig(buf1, format="png", bbox_inches="tight")
78
+ plt.close()
79
+ buf1.seek(0)
80
 
81
+ # Weekly Uptime % (Bar Chart)
82
+ end_date = filtered_df['Timestamp'].max()
83
+ start_date = end_date - timedelta(days=7)
84
+ weekly_df = filtered_df[(filtered_df['Timestamp'] >= start_date) & (filtered_df['Timestamp'] <= end_date)]
85
+ uptime = weekly_df.groupby(weekly_df['Timestamp'].dt.date)['Status'].apply(lambda x: (x == 'Up').mean() * 100)
86
  plt.figure(figsize=(8, 4))
87
+ uptime.plot(kind='bar', color='green')
88
+ plt.title("Weekly Uptime %")
89
+ plt.xlabel("Date")
90
+ plt.ylabel("Uptime %")
91
+ plt.xticks(rotation=45)
92
+ buf2 = io.BytesIO()
93
+ plt.savefig(buf2, format="png", bbox_inches="tight")
94
+ plt.close()
95
+ buf2.seek(0)
96
 
97
+ # Anomaly Alerts (Text)
98
+ anomalies = filtered_df[(filtered_df['UsageCount'] > 80) | (filtered_df['Status'] == 'Down')]
99
+ if anomalies.empty:
100
+ anomaly_text = "No anomalies detected."
101
+ else:
102
+ anomaly_text = "Anomalies Detected:\n" + anomalies[['DeviceID', 'Lab', 'Type', 'Status', 'UsageCount']].to_string(index=False)
103
+
104
+ return device_cards, buf1, buf2, anomaly_text, ""
105
 
106
+ def download_pdf(selected_lab, selected_type, selected_date_range):
107
  global df
 
108
  if df.empty:
109
  return None
110
 
 
111
  filtered_df = df.copy()
 
 
112
  if selected_lab != "All":
113
  filtered_df = filtered_df[filtered_df["Lab"] == selected_lab]
114
  if selected_type != "All":
115
  filtered_df = filtered_df[filtered_df["Type"] == selected_type]
116
+ if selected_date_range != "All" and selected_date_range != "No data available.":
117
+ start_date, end_date = selected_date_range.split(" to ")
118
+ start_date = pd.to_datetime(start_date)
119
+ end_date = pd.to_datetime(end_date) + timedelta(days=1)
120
+ filtered_df = filtered_df[(filtered_df["Timestamp"] >= start_date) & (filtered_df["Timestamp"] < end_date)]
121
 
 
122
  if filtered_df.empty:
123
  return None
124
 
 
125
  pdf = FPDF()
126
  pdf.add_page()
127
  pdf.set_font("Arial", size=12)
128
+ pdf.cell(200, 10, txt="LabOps Dashboard Report", ln=True, align='C')
129
  pdf.ln(10)
130
 
131
+ # Add filtered data
132
  for index, row in filtered_df.iterrows():
133
+ line = f"{row['Timestamp']} | {row['DeviceID']} | {row['Lab']} | {row['Type']} | {row['Status']} | {row['UsageCount']} | {row['Health']}"
134
  pdf.multi_cell(0, 10, txt=line)
135
 
 
136
  output = io.BytesIO()
137
  pdf.output(output)
138
  output.seek(0)
 
140
 
141
  # Build the Gradio interface
142
  with gr.Blocks() as demo:
143
+ gr.Markdown("🧪 **Multi-Device LabOps Dashboard**\nMonitor smart lab devices, visualize logs, and generate PDF reports.")
144
 
145
  with gr.Row():
146
+ csv_input = gr.File(label="Upload Device Logs CSV", file_types=[".csv"])
147
 
148
  with gr.Row():
149
+ lab_dropdown = gr.Dropdown(label="Filter by Lab", choices=["All"], value="All")
150
+ type_dropdown = gr.Dropdown(label="Filter by Equipment Type", choices=["All"], value="All")
151
+ date_dropdown = gr.Dropdown(label="Filter by Date Range", choices=["All"], value="All")
152
+
153
+ with gr.Row():
154
+ device_table = gr.DataFrame(label="Device Cards (Health, Usage, Last Log)")
155
+ plot_daily = gr.Image(label="Daily Log Trends")
156
+ plot_uptime = gr.Image(label="Weekly Uptime %")
157
 
158
+ anomaly_output = gr.Textbox(label="Anomaly Alerts")
159
+ download_btn = gr.Button("Download PDF Report")
160
  error_box = gr.Textbox(label="Status/Error Message", visible=True, interactive=False)
161
 
162
  # Connect the components
163
  csv_input.change(
164
  fn=upload_csv,
165
  inputs=csv_input,
166
+ outputs=[lab_dropdown, type_dropdown, date_dropdown, error_box, lab_dropdown, type_dropdown, date_dropdown]
167
+ )
168
+ lab_dropdown.change(
169
+ fn=filter_and_visualize,
170
+ inputs=[lab_dropdown, type_dropdown, date_dropdown],
171
+ outputs=[device_table, plot_daily, plot_uptime, anomaly_output, error_box]
172
+ )
173
+ type_dropdown.change(
174
+ fn=filter_and_visualize,
175
+ inputs=[lab_dropdown, type_dropdown, date_dropdown],
176
+ outputs=[device_table, plot_daily, plot_uptime, anomaly_output, error_box]
177
+ )
178
+ date_dropdown.change(
179
+ fn=filter_and_visualize,
180
+ inputs=[lab_dropdown, type_dropdown, date_dropdown],
181
+ outputs=[device_table, plot_daily, plot_uptime, anomaly_output, error_box]
182
+ )
183
+ download_btn.click(
184
+ fn=download_pdf,
185
+ inputs=[lab_dropdown, type_dropdown, date_dropdown],
186
+ outputs=gr.File(label="Download PDF")
187
  )
 
 
 
188
 
189
  demo.launch()