MaheshP98 commited on
Commit
1f2a07b
·
verified ·
1 Parent(s): c528749

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -27
app.py CHANGED
@@ -13,6 +13,102 @@ import tempfile
13
  # Configure logging to match the log format
14
  logging.basicConfig(level=logging.INFO, format='%(asctime)s,%(msecs)03d - %(levelname)s - %(message)s')
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def validate_csv(df):
17
  """
18
  Validate that the CSV has the required columns.
@@ -67,37 +163,57 @@ def generate_summary(combined_df, anomaly_df, amc_df, plot_path, pdf_path):
67
  for _, row in amc_df.iterrows():
68
  days_until_expiry = (row['amc_expiry'] - datetime(2025, 6, 5)).days
69
  urgency = "Urgent" if days_until_expiry <= 3 else "Upcoming"
70
- summary.append(f"- **{row['equipment']}**: Due on {row['amc_expiry'].strftime('%Y-%m-%d')} ({urgency}, {days_until_expiry} days left)")
71
- summary.append("Please schedule maintenance to avoid downtime.")
 
72
  else:
73
  summary.append("No devices need maintenance within the next 7 days.")
74
  summary.append("\n")
75
 
76
  # Generated Reports
77
  summary.append("## Generated Reports")
78
- summary.append("- **Usage Chart**: A bar chart showing how much each device was used, grouped by status (e.g., Active, Inactive).")
79
- summary.append("- **PDF Report**: Download the detailed report below for a full analysis, including a table of all records and a flowchart of our process.")
80
 
81
  return "\n".join(summary)
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  def process_files(uploaded_files):
84
  """
85
  Process uploaded CSV files, generate usage plots, detect anomalies, and process AMC expiries.
86
- Returns a dataframe, plot path, PDF path, AMC expiry message, and summary.
87
  """
88
  # Log received files
89
  logging.info(f"Received uploaded files: {uploaded_files}")
90
 
91
  if not uploaded_files:
92
  logging.warning("No files uploaded.")
93
- return None, None, None, "Please upload at least one valid CSV file.", "## Summary\nNo files uploaded."
94
 
95
  valid_files = [f for f in uploaded_files if f.name.endswith('.csv')]
96
  logging.info(f"Processing {len(valid_files)} valid files: {valid_files}")
97
 
98
  if not valid_files:
99
  logging.warning("No valid CSV files uploaded.")
100
- return None, None, None, "Please upload at least one valid CSV file.", "## Summary\nNo valid CSV files uploaded."
101
 
102
  logging.info("Loading logs from uploaded files...")
103
  all_data = []
@@ -111,15 +227,15 @@ def process_files(uploaded_files):
111
  is_valid, error_msg = validate_csv(df)
112
  if not is_valid:
113
  logging.error(f"Failed to load {file.name}: {error_msg}")
114
- return None, None, None, f"Error loading {file.name}: {error_msg}", f"## Summary\nError: {error_msg}"
115
  all_data.append(df)
116
  except Exception as e:
117
  logging.error(f"Failed to load {file.name}: {str(e)}")
118
- return None, None, None, f"Error loading {file.name}: {str(e)}", f"## Summary\nError: {str(e)}"
119
 
120
  if not all_data:
121
  logging.warning("No data loaded from uploaded files.")
122
- return None, None, None, "No valid data found in uploaded files.", "## Summary\nNo data loaded."
123
 
124
  combined_df = pd.concat(all_data, ignore_index=True)
125
  logging.info(f"Combined {len(combined_df)} total records.")
@@ -132,7 +248,7 @@ def process_files(uploaded_files):
132
  logging.info("Usage plot generated successfully.")
133
  else:
134
  logging.error("Failed to generate usage plot.")
135
- return combined_df, None, None, "Failed to generate usage plot.", "## Summary\nUsage plot generation failed."
136
 
137
  # Detect anomalies using Local Outlier Factor
138
  logging.info("Detecting anomalies using Local Outlier Factor...")
@@ -159,12 +275,17 @@ def process_files(uploaded_files):
159
  summary = generate_summary(combined_df, anomaly_df, amc_df, plot_path, pdf_path)
160
  logging.info("Summary generated successfully.")
161
 
 
 
 
 
 
162
  # Prepare output dataframe (combine original data with anomalies)
163
  output_df = combined_df.copy()
164
  if anomaly_df is not None:
165
  output_df['anomaly'] = anomaly_df['anomaly'].map({1: "Normal", -1: "Unusual"})
166
 
167
- return output_df, plot_path, pdf_path, amc_message, summary
168
 
169
  def generate_usage_plot(df):
170
  """
@@ -237,7 +358,7 @@ def process_amc_expiries(df):
237
 
238
  def generate_pdf_report(original_df, anomaly_df, amc_df):
239
  """
240
- Generate a professionally formatted PDF report with necessary fields and a flowchart.
241
  Returns the path to the saved PDF.
242
  """
243
  try:
@@ -337,11 +458,26 @@ def generate_pdf_report(original_df, anomaly_df, amc_df):
337
  if amc_df is not None and not amc_df.empty:
338
  c.drawString(50, y, f"Devices Needing Maintenance Soon: {len(amc_df['equipment'].unique())}")
339
  y -= 20
 
 
 
 
 
 
 
 
 
 
340
  c.setFont("Helvetica", 10)
341
  for _, row in amc_df.iterrows():
342
  days_until_expiry = (row['amc_expiry'] - datetime(2025, 6, 5)).days
343
  urgency = "Urgent" if days_until_expiry <= 3 else "Upcoming"
344
- c.drawString(50, y, f"{row['equipment']}: {row['amc_expiry'].strftime('%Y-%m-%d')} ({urgency}, {days_until_expiry} days left)")
 
 
 
 
 
345
  y -= 20
346
  if y < 50:
347
  c.showPage()
@@ -349,7 +485,7 @@ def generate_pdf_report(original_df, anomaly_df, amc_df):
349
  draw_header()
350
  c.setFont("Helvetica", 10)
351
  c.setFont("Helvetica-Oblique", 10)
352
- c.drawString(50, y, "Recommendation: Schedule maintenance to prevent downtime.")
353
  y -= 20
354
  else:
355
  c.drawString(50, y, "No devices need maintenance within the next 7 days.")
@@ -360,16 +496,20 @@ def generate_pdf_report(original_df, anomaly_df, amc_df):
360
  y = draw_section_title("Processing Pipeline Flowchart", y)
361
  c.setFont("Helvetica", 10)
362
  flowchart = [
363
- "1. Upload CSV File(s)",
364
- "2. Validate Data (Check for required columns and data types)",
365
- "3. Generate Usage Chart (Bar chart of usage by device and status)",
366
- "4. Detect Unusual Activity (Using Local Outlier Factor)",
367
- "5. Check Maintenance Dates (Identify AMC expiries within 7 days)",
368
- "6. Create PDF Report (Detailed analysis with tables and insights)"
369
  ]
370
- for step in flowchart:
371
  c.drawString(50, y, step)
372
- y -= 20
 
 
 
 
373
  if y < 50:
374
  c.showPage()
375
  y = height - 100
@@ -384,24 +524,30 @@ def generate_pdf_report(original_df, anomaly_df, amc_df):
384
  return None
385
 
386
  # Gradio interface
387
- with gr.Blocks() as demo:
388
  gr.Markdown("# Equipment Log Analysis")
389
  with gr.Row():
390
  file_input = gr.File(file_count="multiple", label="Upload CSV Files")
391
  process_button = gr.Button("Process Files")
392
  with gr.Row():
393
- output_summary = gr.Markdown(label="Summary of Results")
394
  with gr.Row():
395
  output_df = gr.Dataframe(label="Processed Data")
396
  output_plot = gr.Image(label="Usage Chart")
397
  with gr.Row():
398
- output_message = gr.Textbox(label="Maintenance Alerts")
399
  output_pdf = gr.File(label="Download Detailed PDF Report")
 
 
 
 
 
 
400
 
401
  process_button.click(
402
  fn=process_files,
403
  inputs=[file_input],
404
- outputs=[output_df, output_plot, output_pdf, output_message, output_summary]
405
  )
406
 
407
  if __name__ == "__main__":
 
13
  # Configure logging to match the log format
14
  logging.basicConfig(level=logging.INFO, format='%(asctime)s,%(msecs)03d - %(levelname)s - %(message)s')
15
 
16
+ # CSS styling for the Gradio interface
17
+ css = """
18
+ body {
19
+ font-family: Arial, sans-serif;
20
+ background-color: #F3F4F6;
21
+ color: #1E3A8A;
22
+ }
23
+
24
+ h1 {
25
+ color: #1E3A8A;
26
+ text-align: center;
27
+ margin-bottom: 20px;
28
+ }
29
+
30
+ .gr-button {
31
+ background-color: #1E3A8A;
32
+ color: white;
33
+ border: none;
34
+ border-radius: 5px;
35
+ padding: 10px 20px;
36
+ }
37
+
38
+ .gr-button:hover {
39
+ background-color: #2B4C9B;
40
+ }
41
+
42
+ .summary-card {
43
+ background-color: white;
44
+ border-radius: 10px;
45
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
46
+ padding: 20px;
47
+ margin: 20px 0;
48
+ }
49
+
50
+ .summary-card h2 {
51
+ color: #1E3A8A;
52
+ margin-top: 0;
53
+ }
54
+
55
+ .maintenance-alert {
56
+ background-color: white;
57
+ border-radius: 10px;
58
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
59
+ padding: 15px;
60
+ margin: 10px 0;
61
+ }
62
+
63
+ .alert-urgent {
64
+ color: #DC2626;
65
+ font-weight: bold;
66
+ }
67
+
68
+ .alert-upcoming {
69
+ color: #F59E0B;
70
+ font-weight: bold;
71
+ }
72
+
73
+ .recommendation {
74
+ font-style: italic;
75
+ color: #4B5563;
76
+ }
77
+
78
+ .flowchart {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 10px;
82
+ margin: 20px 0;
83
+ }
84
+
85
+ .flowchart-step {
86
+ background-color: #E5E7EB;
87
+ border-left: 5px solid #1E3A8A;
88
+ padding: 10px;
89
+ border-radius: 5px;
90
+ position: relative;
91
+ }
92
+
93
+ .flowchart-step:not(:last-child):after {
94
+ content: '↓';
95
+ position: absolute;
96
+ bottom: -20px;
97
+ left: 50%;
98
+ transform: translateX(-50%);
99
+ font-size: 20px;
100
+ color: #1E3A8A;
101
+ }
102
+
103
+ .report-preview {
104
+ background-color: white;
105
+ border-radius: 10px;
106
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
107
+ padding: 15px;
108
+ margin: 10px 0;
109
+ }
110
+ """
111
+
112
  def validate_csv(df):
113
  """
114
  Validate that the CSV has the required columns.
 
163
  for _, row in amc_df.iterrows():
164
  days_until_expiry = (row['amc_expiry'] - datetime(2025, 6, 5)).days
165
  urgency = "Urgent" if days_until_expiry <= 3 else "Upcoming"
166
+ urgency_class = "alert-urgent" if urgency == "Urgent" else "alert-upcoming"
167
+ summary.append(f"- <span class='{urgency_class}'>⚠️ {urgency}</span>: **{row['equipment']}** - Due on {row['amc_expiry'].strftime('%Y-%m-%d')} ({days_until_expiry} days left)")
168
+ summary.append("\n<div class='recommendation'>Recommendation: Contact the maintenance team within 24 hours for urgent alerts at support@company.com.</div>")
169
  else:
170
  summary.append("No devices need maintenance within the next 7 days.")
171
  summary.append("\n")
172
 
173
  # Generated Reports
174
  summary.append("## Generated Reports")
175
+ summary.append("- **Usage Chart**: Visualizes usage patterns across devices, helping identify overworked or underused equipment. See below for the chart.")
176
+ summary.append("- **PDF Report**: A comprehensive report including a full data table, unusual activity details, maintenance alerts, and a detailed flowchart of our process. Download it below.")
177
 
178
  return "\n".join(summary)
179
 
180
+ def generate_flowchart_html():
181
+ """
182
+ Generate an HTML representation of the flowchart for the Gradio interface.
183
+ Returns an HTML string.
184
+ """
185
+ steps = [
186
+ ("Upload CSV File(s)", "User uploads log files in CSV format."),
187
+ ("Validate Data", "Checks for required columns (equipment, usage_count, status, amc_expiry) and correct data types."),
188
+ ("Generate Usage Chart", "Creates a bar chart showing usage counts by device and status (e.g., Active, Inactive)."),
189
+ ("Detect Unusual Activity", "Uses Local Outlier Factor to identify devices with unusual usage patterns (e.g., too high or too low)."),
190
+ ("Check Maintenance Dates", "Identifies devices with AMC expiries within 7 days from 2025-06-05."),
191
+ ("Create PDF Report", "Generates a detailed PDF with data tables, insights, and this flowchart.")
192
+ ]
193
+ html = ["<div class='flowchart'>"]
194
+ for step, description in steps:
195
+ html.append(f"<div class='flowchart-step'><strong>{step}</strong><br>{description}</div>")
196
+ html.append("</div>")
197
+ return "\n".join(html)
198
+
199
  def process_files(uploaded_files):
200
  """
201
  Process uploaded CSV files, generate usage plots, detect anomalies, and process AMC expiries.
202
+ Returns a dataframe, plot path, PDF path, AMC expiry message, summary, and flowchart HTML.
203
  """
204
  # Log received files
205
  logging.info(f"Received uploaded files: {uploaded_files}")
206
 
207
  if not uploaded_files:
208
  logging.warning("No files uploaded.")
209
+ return None, None, None, "Please upload at least one valid CSV file.", "## Summary\nNo files uploaded.", ""
210
 
211
  valid_files = [f for f in uploaded_files if f.name.endswith('.csv')]
212
  logging.info(f"Processing {len(valid_files)} valid files: {valid_files}")
213
 
214
  if not valid_files:
215
  logging.warning("No valid CSV files uploaded.")
216
+ return None, None, None, "Please upload at least one valid CSV file.", "## Summary\nNo valid CSV files uploaded.", ""
217
 
218
  logging.info("Loading logs from uploaded files...")
219
  all_data = []
 
227
  is_valid, error_msg = validate_csv(df)
228
  if not is_valid:
229
  logging.error(f"Failed to load {file.name}: {error_msg}")
230
+ return None, None, None, f"Error loading {file.name}: {error_msg}", f"## Summary\nError: {error_msg}", ""
231
  all_data.append(df)
232
  except Exception as e:
233
  logging.error(f"Failed to load {file.name}: {str(e)}")
234
+ return None, None, None, f"Error loading {file.name}: {str(e)}", f"## Summary\nError: {str(e)}", ""
235
 
236
  if not all_data:
237
  logging.warning("No data loaded from uploaded files.")
238
+ return None, None, None, "No valid data found in uploaded files.", "## Summary\nNo data loaded.", ""
239
 
240
  combined_df = pd.concat(all_data, ignore_index=True)
241
  logging.info(f"Combined {len(combined_df)} total records.")
 
248
  logging.info("Usage plot generated successfully.")
249
  else:
250
  logging.error("Failed to generate usage plot.")
251
+ return combined_df, None, None, "Failed to generate usage plot.", "## Summary\nUsage plot generation failed.", ""
252
 
253
  # Detect anomalies using Local Outlier Factor
254
  logging.info("Detecting anomalies using Local Outlier Factor...")
 
275
  summary = generate_summary(combined_df, anomaly_df, amc_df, plot_path, pdf_path)
276
  logging.info("Summary generated successfully.")
277
 
278
+ # Generate flowchart HTML
279
+ logging.info("Generating flowchart HTML...")
280
+ flowchart_html = generate_flowchart_html()
281
+ logging.info("Flowchart HTML generated successfully.")
282
+
283
  # Prepare output dataframe (combine original data with anomalies)
284
  output_df = combined_df.copy()
285
  if anomaly_df is not None:
286
  output_df['anomaly'] = anomaly_df['anomaly'].map({1: "Normal", -1: "Unusual"})
287
 
288
+ return output_df, plot_path, pdf_path, amc_message, summary, flowchart_html
289
 
290
  def generate_usage_plot(df):
291
  """
 
358
 
359
  def generate_pdf_report(original_df, anomaly_df, amc_df):
360
  """
361
+ Generate a professionally formatted PDF report with necessary fields and a detailed flowchart.
362
  Returns the path to the saved PDF.
363
  """
364
  try:
 
458
  if amc_df is not None and not amc_df.empty:
459
  c.drawString(50, y, f"Devices Needing Maintenance Soon: {len(amc_df['equipment'].unique())}")
460
  y -= 20
461
+ # Table headers
462
+ c.setFont("Helvetica-Bold", 10)
463
+ headers = ["Device", "Expiry Date", "Urgency", "Days Left", "Action"]
464
+ x_positions = [50, 150, 250, 350, 450]
465
+ for i, header in enumerate(headers):
466
+ c.drawString(x_positions[i], y, header)
467
+ c.line(50, y - 5, width - 50, y - 5)
468
+ y -= 20
469
+
470
+ # Table rows
471
  c.setFont("Helvetica", 10)
472
  for _, row in amc_df.iterrows():
473
  days_until_expiry = (row['amc_expiry'] - datetime(2025, 6, 5)).days
474
  urgency = "Urgent" if days_until_expiry <= 3 else "Upcoming"
475
+ action = "Contact maintenance team within 24 hours" if urgency == "Urgent" else "Schedule maintenance this week"
476
+ c.drawString(50, y, str(row['equipment']))
477
+ c.drawString(150, y, str(row['amc_expiry'].strftime('%Y-%m-%d')))
478
+ c.drawString(250, y, urgency)
479
+ c.drawString(350, y, str(days_until_expiry))
480
+ c.drawString(450, y, action)
481
  y -= 20
482
  if y < 50:
483
  c.showPage()
 
485
  draw_header()
486
  c.setFont("Helvetica", 10)
487
  c.setFont("Helvetica-Oblique", 10)
488
+ c.drawString(50, y, "Contact: Email the maintenance team at support@company.com for scheduling.")
489
  y -= 20
490
  else:
491
  c.drawString(50, y, "No devices need maintenance within the next 7 days.")
 
496
  y = draw_section_title("Processing Pipeline Flowchart", y)
497
  c.setFont("Helvetica", 10)
498
  flowchart = [
499
+ ("1. Upload CSV File(s)", "User uploads log files in CSV format containing device usage data."),
500
+ ("2. Validate Data", "Ensures all required columns (equipment, usage_count, status, amc_expiry) are present and data types are correct (e.g., usage_count as numeric, amc_expiry as date)."),
501
+ ("3. Generate Usage Chart", "Creates a bar chart showing usage counts by device and status (e.g., Active, Inactive) to visualize usage patterns."),
502
+ ("4. Detect Unusual Activity", "Uses Local Outlier Factor (LOF) algorithm to identify devices with unusual usage patterns by comparing local density of usage counts (contamination=0.1, n_neighbors=5)."),
503
+ ("5. Check Maintenance Dates", "Identifies devices with AMC expiries within 7 days from 2025-06-05, calculating days left and urgency (urgent if ≤3 days)."),
504
+ ("6. Create PDF Report", "Generates this PDF with a data table, unusual activity details, maintenance alerts, and this detailed flowchart.")
505
  ]
506
+ for step, description in flowchart:
507
  c.drawString(50, y, step)
508
+ y -= 15
509
+ c.setFont("Helvetica-Oblique", 9)
510
+ c.drawString(70, y, description)
511
+ c.setFont("Helvetica", 10)
512
+ y -= 25
513
  if y < 50:
514
  c.showPage()
515
  y = height - 100
 
524
  return None
525
 
526
  # Gradio interface
527
+ with gr.Blocks(css=css) as demo:
528
  gr.Markdown("# Equipment Log Analysis")
529
  with gr.Row():
530
  file_input = gr.File(file_count="multiple", label="Upload CSV Files")
531
  process_button = gr.Button("Process Files")
532
  with gr.Row():
533
+ output_summary = gr.Markdown(label="Summary of Results", elem_classes=["summary-card"])
534
  with gr.Row():
535
  output_df = gr.Dataframe(label="Processed Data")
536
  output_plot = gr.Image(label="Usage Chart")
537
  with gr.Row():
538
+ output_message = gr.Textbox(label="Maintenance Alerts", elem_classes=["maintenance-alert"])
539
  output_pdf = gr.File(label="Download Detailed PDF Report")
540
+ with gr.Row():
541
+ gr.HTML(generate_flowchart_html(), label="Processing Flowchart")
542
+ with gr.Row():
543
+ gr.Markdown("## Report Previews", elem_classes=["report-preview"])
544
+ gr.Markdown("- **Usage Chart**: See the bar chart above for a visual of device usage by status.")
545
+ gr.Markdown("- **PDF Report**: Download the PDF above for a full analysis, including data tables, unusual activity, maintenance alerts, and a detailed flowchart.")
546
 
547
  process_button.click(
548
  fn=process_files,
549
  inputs=[file_input],
550
+ outputs=[output_df, output_plot, output_pdf, output_message, output_summary, gr.HTML]
551
  )
552
 
553
  if __name__ == "__main__":