RathodHarish commited on
Commit
c9d5c6c
·
verified ·
1 Parent(s): 199d16a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -20
app.py CHANGED
@@ -69,15 +69,27 @@ def detect_anomalies(df):
69
  # AMC reminders
70
  def check_amc_reminders(df, current_date):
71
  try:
 
72
  if "device_id" not in df.columns or "amc_date" not in df.columns:
 
73
  return "AMC reminders require 'device_id' and 'amc_date' columns.", pd.DataFrame()
 
74
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
75
- current_date = pd.to_datetime(current_date)
 
 
 
 
 
 
76
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
 
 
77
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]]
78
  if reminders.empty:
79
  logging.info("No AMC reminders found within the next 30 days.")
80
  return "No AMC reminders due within the next 30 days.", reminders
 
81
  reminder_lines = ["Upcoming AMC Reminders:"]
82
  for _, row in reminders.head(5).iterrows():
83
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
@@ -175,10 +187,10 @@ def create_downtime_chart(agg_data):
175
  color_discrete_map={"green": "#96CEB4", "red": "#FF0000"}
176
  )
177
  fig.update_traces(
178
- marker_line_color='#333333',
179
- marker_line_width=1.5,
180
- opacity=0.9
181
- )
182
  fig.update_layout(
183
  title_font=dict(size=18, family="Arial", color="#333333"),
184
  font=dict(family="Arial", size=12, color="#333333"),
@@ -207,7 +219,7 @@ def create_downtime_chart(agg_data):
207
  logging.error(f"Failed to create downtime chart: {str(e)}")
208
  return None
209
 
210
- # Create Daily Log Trends chart
211
  def create_daily_log_trends_chart(df):
212
  try:
213
  if df.empty or 'timestamp' not in df.columns:
@@ -218,16 +230,21 @@ def create_daily_log_trends_chart(df):
218
  df['date'] = df['timestamp'].dt.date
219
  log_counts = df.groupby('date').size().reset_index(name='log_count')
220
 
221
- fig = px.line(
222
  log_counts,
223
  x='date',
224
  y='log_count',
225
  title="Daily Log Trends",
226
  labels={"date": "Date", "log_count": "Number of Logs"}
227
  )
 
228
  fig.update_traces(
 
229
  line_color='#4ECDC4',
230
- line_width=2
 
 
 
231
  )
232
  fig.update_layout(
233
  title_font=dict(size=18, family="Arial", color="#333333"),
@@ -261,18 +278,31 @@ def create_weekly_uptime_chart(df):
261
  logging.warning("DataFrame is empty or missing required columns for Weekly Uptime Percentage.")
262
  return None
263
 
264
- # Group by week
265
- df['week'] = df['timestamp'].dt.isocalendar().week
 
 
 
 
 
 
266
  df['year'] = df['timestamp'].dt.year
267
  weekly_data = df.groupby(['year', 'week']).agg({
268
  'downtime': 'sum'
269
  }).reset_index()
270
 
 
 
271
  # Calculate uptime percentage (assuming 24*7 = 168 hours per week)
272
  total_hours_per_week = 168
273
  weekly_data['uptime_percentage'] = ((total_hours_per_week - weekly_data['downtime']) / total_hours_per_week) * 100
 
274
  weekly_data['week_label'] = weekly_data.apply(lambda x: f"{x['year']}-W{x['week']:02d}", axis=1)
275
 
 
 
 
 
276
  fig = px.bar(
277
  weekly_data,
278
  x='week_label',
@@ -315,14 +345,14 @@ def create_weekly_uptime_chart(df):
315
  logging.error(f"Failed to create Weekly Uptime Percentage chart: {str(e)}")
316
  return None
317
 
318
- # Create Anomaly Alerts chart
319
  def create_anomaly_alerts_chart(df, anomalies_df):
320
  try:
321
  if df.empty or anomalies_df.empty:
322
  logging.warning("DataFrame or anomalies DataFrame is empty for Anomaly Alerts chart.")
323
  return None
324
 
325
- # Prepare data for scatter plot
326
  df['is_anomaly'] = df.index.isin(anomalies_df.index)
327
  df['color'] = df['is_anomaly'].map({True: 'red', False: 'blue'})
328
 
@@ -330,13 +360,18 @@ def create_anomaly_alerts_chart(df, anomalies_df):
330
  df,
331
  x='usage_hours',
332
  y='downtime',
 
333
  color='color',
334
  title="Anomaly Alerts (Red = Anomaly)",
335
  labels={"usage_hours": "Usage Hours", "downtime": "Downtime (Hours)"},
336
  color_discrete_map={'blue': '#4ECDC4', 'red': '#FF0000'}
337
  )
338
  fig.update_traces(
339
- marker=dict(size=8, line=dict(width=1, color='#333333')),
 
 
 
 
340
  opacity=0.7
341
  )
342
  fig.update_layout(
@@ -614,9 +649,9 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
614
  }
615
  df = pd.read_csv(file_path, dtype=dtypes)
616
  # Downsample early if dataset is too large
617
- if len(df) > 10000:
618
- df = df.sample(n=10000, random_state=42)
619
- logging.info(f"Downsampled DataFrame to 10,000 rows immediately after loading.")
620
  missing_columns = [col for col in required_columns if col not in df.columns]
621
  if missing_columns:
622
  return f"Missing columns: {missing_columns}", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, f"Missing required columns: {missing_columns}"
@@ -664,11 +699,11 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
664
  logging.warning("Filtered DataFrame is empty after applying filters.")
665
  return "No data after applying filters.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, "No data available after applying filters."
666
 
667
- logging.info(f"Filtered DataFrame:\n{filtered_df.head().to_string()}")
668
 
669
- if len(filtered_df) > 10000:
670
- filtered_df = filtered_df.sample(n=10000, random_state=42)
671
- logging.info(f"Downsampled DataFrame to 10,000 rows for chart generation.")
672
 
673
  # Pre-aggregate data for charts
674
  agg_data = {
 
69
  # AMC reminders
70
  def check_amc_reminders(df, current_date):
71
  try:
72
+ logging.info(f"Input DataFrame for AMC reminders:\n{df.head().to_string()}")
73
  if "device_id" not in df.columns or "amc_date" not in df.columns:
74
+ logging.warning("Missing 'device_id' or 'amc_date' columns for AMC reminders.")
75
  return "AMC reminders require 'device_id' and 'amc_date' columns.", pd.DataFrame()
76
+
77
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
78
+ if df["amc_date"].dt.tz is None:
79
+ logging.info("Localizing naive AMC dates to IST")
80
+ df["amc_date"] = df["amc_date"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
81
+
82
+ current_date = pd.to_datetime(current_date).tz_localize('Asia/Kolkata')
83
+ logging.info(f"Current date for AMC check: {current_date}")
84
+
85
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
86
+ logging.info(f"Days to AMC:\n{df[['device_id', 'amc_date', 'days_to_amc']].to_string()}")
87
+
88
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]]
89
  if reminders.empty:
90
  logging.info("No AMC reminders found within the next 30 days.")
91
  return "No AMC reminders due within the next 30 days.", reminders
92
+
93
  reminder_lines = ["Upcoming AMC Reminders:"]
94
  for _, row in reminders.head(5).iterrows():
95
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
 
187
  color_discrete_map={"green": "#96CEB4", "red": "#FF0000"}
188
  )
189
  fig.update_traces(
190
+
191
+ marker_line_color='#333333',
192
+ marker_line_width=1.5,
193
+ opacity=0.9
194
  fig.update_layout(
195
  title_font=dict(size=18, family="Arial", color="#333333"),
196
  font=dict(family="Arial", size=12, color="#333333"),
 
219
  logging.error(f"Failed to create downtime chart: {str(e)}")
220
  return None
221
 
222
+ # Create Daily Log Trends chart (changed to area chart with markers)
223
  def create_daily_log_trends_chart(df):
224
  try:
225
  if df.empty or 'timestamp' not in df.columns:
 
230
  df['date'] = df['timestamp'].dt.date
231
  log_counts = df.groupby('date').size().reset_index(name='log_count')
232
 
233
+ fig = px.area(
234
  log_counts,
235
  x='date',
236
  y='log_count',
237
  title="Daily Log Trends",
238
  labels={"date": "Date", "log_count": "Number of Logs"}
239
  )
240
+ # Add markers
241
  fig.update_traces(
242
+ fill='tozeroy',
243
  line_color='#4ECDC4',
244
+ line_width=2,
245
+ mode='lines+markers',
246
+ marker=dict(size=8, color='#4ECDC4', line=dict(width=1, color='#333333')),
247
+ fillcolor='rgba(78, 205, 196, 0.3)' # Gradient fill with transparency
248
  )
249
  fig.update_layout(
250
  title_font=dict(size=18, family="Arial", color="#333333"),
 
278
  logging.warning("DataFrame is empty or missing required columns for Weekly Uptime Percentage.")
279
  return None
280
 
281
+ logging.info(f"DataFrame for Weekly Uptime:\n{df[['timestamp', 'downtime']].to_string()}")
282
+
283
+ # Group by week (handle pandas 2.x compatibility)
284
+ try:
285
+ df['week'] = df['timestamp'].dt.isocalendar().week
286
+ except AttributeError:
287
+ # For pandas 2.x, use .dt.weekofyear or manual calculation
288
+ df['week'] = df['timestamp'].dt.isocalendar()['week']
289
  df['year'] = df['timestamp'].dt.year
290
  weekly_data = df.groupby(['year', 'week']).agg({
291
  'downtime': 'sum'
292
  }).reset_index()
293
 
294
+ logging.info(f"Weekly data:\n{weekly_data.to_string()}")
295
+
296
  # Calculate uptime percentage (assuming 24*7 = 168 hours per week)
297
  total_hours_per_week = 168
298
  weekly_data['uptime_percentage'] = ((total_hours_per_week - weekly_data['downtime']) / total_hours_per_week) * 100
299
+ weekly_data['uptime_percentage'] = weekly_data['uptime_percentage'].clip(0, 100) # Ensure percentage is between 0 and 100
300
  weekly_data['week_label'] = weekly_data.apply(lambda x: f"{x['year']}-W{x['week']:02d}", axis=1)
301
 
302
+ if weekly_data.empty:
303
+ logging.warning("No weekly data available for Weekly Uptime Percentage chart.")
304
+ return None
305
+
306
  fig = px.bar(
307
  weekly_data,
308
  x='week_label',
 
345
  logging.error(f"Failed to create Weekly Uptime Percentage chart: {str(e)}")
346
  return None
347
 
348
+ # Create Anomaly Alerts chart (changed to bubble chart)
349
  def create_anomaly_alerts_chart(df, anomalies_df):
350
  try:
351
  if df.empty or anomalies_df.empty:
352
  logging.warning("DataFrame or anomalies DataFrame is empty for Anomaly Alerts chart.")
353
  return None
354
 
355
+ # Prepare data for bubble chart
356
  df['is_anomaly'] = df.index.isin(anomalies_df.index)
357
  df['color'] = df['is_anomaly'].map({True: 'red', False: 'blue'})
358
 
 
360
  df,
361
  x='usage_hours',
362
  y='downtime',
363
+ size='usage_hours', # Bubble size based on usage hours
364
  color='color',
365
  title="Anomaly Alerts (Red = Anomaly)",
366
  labels={"usage_hours": "Usage Hours", "downtime": "Downtime (Hours)"},
367
  color_discrete_map={'blue': '#4ECDC4', 'red': '#FF0000'}
368
  )
369
  fig.update_traces(
370
+ marker=dict(
371
+ sizemode='area',
372
+ sizeref=0.1, # Adjust bubble size scaling
373
+ line=dict(width=1, color='#333333')
374
+ ),
375
  opacity=0.7
376
  )
377
  fig.update_layout(
 
649
  }
650
  df = pd.read_csv(file_path, dtype=dtypes)
651
  # Downsample early if dataset is too large
652
+ if len(df) > 5000:
653
+ df = df.sample(n=5000, random_state=42)
654
+ logging.info(f"Downsampled DataFrame to 5,000 rows immediately after loading.")
655
  missing_columns = [col for col in required_columns if col not in df.columns]
656
  if missing_columns:
657
  return f"Missing columns: {missing_columns}", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, f"Missing required columns: {missing_columns}"
 
699
  logging.warning("Filtered DataFrame is empty after applying filters.")
700
  return "No data after applying filters.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, "No data available after applying filters."
701
 
702
+ logging.info(f"Filtered DataFrame before AMC check:\n{filtered_df[['device_id', 'amc_date']].to_string()}")
703
 
704
+ if len(filtered_df) > 1000:
705
+ filtered_df = filtered_df.sample(n=1000, random_state=42)
706
+ logging.info(f"Downsampled filtered DataFrame to 1,000 rows for chart generation.")
707
 
708
  # Pre-aggregate data for charts
709
  agg_data = {