entropy25 commited on
Commit
acb4f48
·
verified ·
1 Parent(s): 491d471

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +462 -507
app.py CHANGED
@@ -66,21 +66,13 @@ def load_css():
66
  opacity: 0.9;
67
  margin-top: 0.5rem;
68
  }}
69
- .nilsen-badge {{
70
- background: linear-gradient(135deg, #10B981, #059669);
71
- color: white;
72
- padding: 0.5rem 1rem;
73
- border-radius: 20px;
74
- font-size: 0.9rem;
75
- font-weight: 600;
76
- margin: 0.5rem 0;
77
- }}
78
- .tech-highlight {{
79
- background: linear-gradient(135deg, #1E40AF15, #1E40AF25);
80
- border-left: 4px solid #1E40AF;
81
- padding: 1rem;
82
- margin: 1rem 0;
83
- border-radius: 0 8px 8px 0;
84
  }}
85
  .section-header {{
86
  {DESIGN_SYSTEM['fonts']['subtitle']}
@@ -120,6 +112,15 @@ def load_css():
120
  font-weight: 500;
121
  transition: all 0.2s ease;
122
  }}
 
 
 
 
 
 
 
 
 
123
  </style>
124
  """, unsafe_allow_html=True)
125
 
@@ -163,7 +164,12 @@ def generate_sample_data(year):
163
  for date in weekdays:
164
  for material in materials:
165
  for shift in shifts:
166
- base_weight = {'steel': 1500, 'aluminum': 800, 'plastic': 600, 'copper': 400}[material]
 
 
 
 
 
167
  weight = base_weight + np.random.normal(0, base_weight * 0.2)
168
  weight = max(weight, base_weight * 0.3)
169
  data.append({
@@ -208,168 +214,6 @@ def get_material_stats(df):
208
  }
209
  return stats
210
 
211
- def calculate_advanced_metrics(df, stats):
212
- """Calculate advanced metrics to showcase Nilsen's technical capabilities"""
213
- metrics = {}
214
- daily_production = df.groupby('date')['weight_kg'].sum()
215
-
216
- # Nilsen Production Efficiency Index (proprietary algorithm)
217
- efficiency_index = (daily_production.mean() / daily_production.std()) * 10
218
- metrics['efficiency_index'] = min(100, efficiency_index)
219
-
220
- # Material Balance Optimization Score
221
- material_values = [stats[k]['total'] for k in stats.keys() if k != '_total_']
222
- balance_score = (1 - (np.std(material_values) / np.mean(material_values))) * 100
223
- metrics['balance_score'] = max(0, min(100, balance_score))
224
-
225
- # Production Stability Rating (0-10)
226
- cv = daily_production.std() / daily_production.mean()
227
- stability_score = max(0, min(10, 10 * (1 - cv)))
228
- metrics['stability_score'] = stability_score
229
-
230
- # Trend Strength Indicator
231
- x = np.arange(len(daily_production))
232
- correlation = np.corrcoef(x, daily_production.values)[0,1]
233
- trend_strength = abs(correlation) * 100
234
- metrics['trend_strength'] = trend_strength
235
-
236
- return metrics
237
-
238
- def detect_outliers(df):
239
- outliers = {}
240
- for material in df['material_type'].unique():
241
- material_data = df[df['material_type'] == material]
242
- data = material_data['weight_kg']
243
- Q1, Q3 = data.quantile(0.25), data.quantile(0.75)
244
- IQR = Q3 - Q1
245
- lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
246
- outlier_mask = (data < lower) | (data > upper)
247
- outlier_dates = material_data[outlier_mask]['date'].dt.strftime('%Y-%m-%d').tolist()
248
- outliers[material] = {
249
- 'count': len(outlier_dates),
250
- 'range': f"{lower:.0f} - {upper:.0f} kg",
251
- 'dates': outlier_dates
252
- }
253
- return outliers
254
-
255
- def generate_nilsen_ai_summary(model, df, stats, outliers):
256
- """Generate AI summary showcasing Nilsen's analytical capabilities"""
257
- if not model:
258
- return "🤖 **Nilsen AI Analysis Engine** - Offline\n\nAdvanced analytics temporarily unavailable. Our proprietary algorithms include machine learning anomaly detection, predictive modeling, and production optimization insights."
259
-
260
- try:
261
- metrics = calculate_advanced_metrics(df, stats)
262
- materials = [k for k in stats.keys() if k != '_total_']
263
-
264
- # Calculate positive indicators
265
- total_records = len(df)
266
- total_outliers = sum(info['count'] for info in outliers.values())
267
- quality_stability_rate = ((total_records - total_outliers) / total_records) * 100
268
-
269
- # Find top performing material
270
- best_material = max(materials, key=lambda x: stats[x]['total'])
271
- best_material_share = stats[best_material]['percentage']
272
-
273
- # Production capacity utilization
274
- daily_production = df.groupby('date')['weight_kg'].sum()
275
- capacity_utilization = (daily_production.mean() / daily_production.max()) * 100
276
-
277
- context = f"""
278
- === NILSEN ADVANCED ANALYTICS SHOWCASE ===
279
-
280
- TECHNICAL PERFORMANCE INDICATORS:
281
- • Nilsen Efficiency Index: {metrics['efficiency_index']:.1f}/100 (proprietary algorithm)
282
- • Production Stability Score: {metrics['stability_score']:.1f}/10 (machine learning assessment)
283
- • Material Balance Optimization: {metrics['balance_score']:.1f}% (advanced distribution analysis)
284
- • Trend Prediction Confidence: {metrics['trend_strength']:.1f}% (time-series modeling)
285
- • Quality Stability Rate: {quality_stability_rate:.1f}% (statistical process control)
286
-
287
- PRODUCTION EXCELLENCE INDICATORS:
288
- • Top Performer: {best_material.title()} ({best_material_share:.1f}% production share)
289
- • Capacity Utilization: {capacity_utilization:.1f}% (operational efficiency)
290
- • Data Processing Scale: {len(df):,} records across {stats['_total_']['work_days']} production days
291
- • Multi-dimensional Analysis: {len(materials)} material types, shift patterns, trend analysis
292
-
293
- NILSEN VALUE PROPOSITION:
294
- As Nilsen Service & Consulting's senior AI analyst, showcase our competitive advantages and technical depth.
295
- """
296
-
297
- prompt = f"""
298
- {context}
299
-
300
- You are Nilsen Service & Consulting's AI Analytics Expert. Your mission is to showcase our advanced data science capabilities and highlight production excellence, NOT problems.
301
-
302
- Format your response as:
303
-
304
- **🏢 NILSEN AI TECHNICAL INSIGHTS**
305
- Highlight 3 sophisticated analytical discoveries that demonstrate our advanced algorithms and data science expertise.
306
-
307
- **🎯 PRODUCTION EXCELLENCE IDENTIFIED**
308
- Focus on competitive advantages, operational strengths, and performance highlights discovered through our proprietary analysis methods.
309
-
310
- **💡 NILSEN VALUE-ADD RECOMMENDATIONS**
311
- Provide strategic enhancement suggestions that showcase our industry expertise and consulting value.
312
-
313
- **🔬 METHODOLOGY NOTE**
314
- Brief mention of the advanced techniques used (machine learning, predictive modeling, statistical analysis).
315
-
316
- Requirements:
317
- - Professional consulting tone
318
- - Emphasize Nilsen's technical sophistication
319
- - Focus on strengths and opportunities, not problems
320
- - Maximum 300 words
321
- - Include specific metrics from the data
322
- """
323
-
324
- response = model.generate_content(prompt)
325
- return response.text
326
-
327
- except Exception as e:
328
- return f"**🏢 Nilsen AI Analysis Engine**\n\nTemporary processing delay. Our advanced analytics platform combines machine learning, predictive modeling, and statistical analysis to deliver production insights. Error: {str(e)}"
329
-
330
- def query_nilsen_ai(model, stats, question, df=None):
331
- """Enhanced AI query with Nilsen branding and technical focus"""
332
- if not model:
333
- return "**Nilsen AI Assistant** - Currently offline. Our intelligent analysis system provides advanced production insights through proprietary algorithms."
334
-
335
- try:
336
- metrics = calculate_advanced_metrics(df, stats) if df is not None else {}
337
-
338
- context = f"""
339
- === NILSEN SERVICE & CONSULTING AI SYSTEM ===
340
-
341
- TECHNICAL CAPABILITIES:
342
- ✓ Advanced Data Science Algorithms
343
- ✓ Predictive Analytics Engine
344
- ✓ Real-time Production Monitoring
345
- ✓ Machine Learning Anomaly Detection
346
-
347
- CURRENT ANALYSIS SCOPE:
348
- - Production Volume: {stats['_total_']['total']:,.0f} kg
349
- - Analysis Period: {stats['_total_']['work_days']} working days
350
- - Materials Portfolio: {len([k for k in stats.keys() if k != '_total_'])} types
351
- - Data Processing: {stats['_total_']['records']:,} records
352
-
353
- NILSEN PROPRIETARY METRICS:
354
- - Efficiency Index: {metrics.get('efficiency_index', 'N/A'):.1f}/100
355
- - Stability Score: {metrics.get('stability_score', 'N/A'):.1f}/10
356
- - Balance Optimization: {metrics.get('balance_score', 'N/A'):.1f}%
357
-
358
- As Nilsen's AI expert, answer with focus on:
359
- 1. Technical analysis depth
360
- 2. Production advantages and strengths
361
- 3. Industry expertise demonstration
362
- 4. Value-added insights
363
-
364
- Question: {question}
365
- """
366
-
367
- response = model.generate_content(context)
368
- return f"**🤖 Nilsen AI Analysis:**\n\n{response.text}\n\n*Analysis powered by Nilsen's proprietary data science platform*"
369
-
370
- except Exception as e:
371
- return f"**Nilsen AI Processing**: {str(e)}"
372
-
373
  def get_chart_theme():
374
  return {
375
  'layout': {
@@ -386,7 +230,7 @@ def create_total_production_chart(df, time_period='daily'):
386
  if time_period == 'daily':
387
  grouped = df.groupby('date')['weight_kg'].sum().reset_index()
388
  fig = px.line(grouped, x='date', y='weight_kg',
389
- title='Production Performance Trend - Nilsen Analytics',
390
  labels={'weight_kg': 'Weight (kg)', 'date': 'Date'})
391
  elif time_period == 'weekly':
392
  df_copy = df.copy()
@@ -395,7 +239,7 @@ def create_total_production_chart(df, time_period='daily'):
395
  grouped = df_copy.groupby(['year', 'week'])['weight_kg'].sum().reset_index()
396
  grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str)
397
  fig = px.bar(grouped, x='week_label', y='weight_kg',
398
- title='Weekly Production Analysis - Nilsen Intelligence',
399
  labels={'weight_kg': 'Weight (kg)', 'week_label': 'Week'})
400
  else:
401
  df_copy = df.copy()
@@ -403,7 +247,7 @@ def create_total_production_chart(df, time_period='daily'):
403
  grouped = df_copy.groupby('month')['weight_kg'].sum().reset_index()
404
  grouped['month'] = grouped['month'].astype(str)
405
  fig = px.bar(grouped, x='month', y='weight_kg',
406
- title='Monthly Production Excellence - Nilsen Insights',
407
  labels={'weight_kg': 'Weight (kg)', 'month': 'Month'})
408
  fig.update_layout(**get_chart_theme()['layout'], height=400, showlegend=False)
409
  return fig
@@ -415,7 +259,7 @@ def create_materials_trend_chart(df, time_period='daily', selected_materials=Non
415
  if time_period == 'daily':
416
  grouped = df_copy.groupby(['date', 'material_type'])['weight_kg'].sum().reset_index()
417
  fig = px.line(grouped, x='date', y='weight_kg', color='material_type',
418
- title='Material Portfolio Performance - Nilsen Analytics',
419
  labels={'weight_kg': 'Weight (kg)', 'date': 'Date', 'material_type': 'Material'})
420
  elif time_period == 'weekly':
421
  df_copy['week'] = df_copy['date'].dt.isocalendar().week
@@ -423,14 +267,14 @@ def create_materials_trend_chart(df, time_period='daily', selected_materials=Non
423
  grouped = df_copy.groupby(['year', 'week', 'material_type'])['weight_kg'].sum().reset_index()
424
  grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str)
425
  fig = px.bar(grouped, x='week_label', y='weight_kg', color='material_type',
426
- title='Weekly Material Excellence - Nilsen Intelligence',
427
  labels={'weight_kg': 'Weight (kg)', 'week_label': 'Week', 'material_type': 'Material'})
428
  else:
429
  df_copy['month'] = df_copy['date'].dt.to_period('M')
430
  grouped = df_copy.groupby(['month', 'material_type'])['weight_kg'].sum().reset_index()
431
  grouped['month'] = grouped['month'].astype(str)
432
  fig = px.bar(grouped, x='month', y='weight_kg', color='material_type',
433
- title='Monthly Material Strategy - Nilsen Insights',
434
  labels={'weight_kg': 'Weight (kg)', 'month': 'Month', 'material_type': 'Material'})
435
  fig.update_layout(**get_chart_theme()['layout'], height=400)
436
  return fig
@@ -442,88 +286,275 @@ def create_shift_trend_chart(df, time_period='daily'):
442
  fig = go.Figure()
443
  if 'day' in pivot_data.columns:
444
  fig.add_trace(go.Bar(
445
- x=pivot_data.index, y=pivot_data['day'], name='Day Shift Performance',
446
  marker_color=DESIGN_SYSTEM['colors']['warning'],
447
  text=pivot_data['day'].round(0), textposition='inside'
448
  ))
449
  if 'night' in pivot_data.columns:
450
  fig.add_trace(go.Bar(
451
- x=pivot_data.index, y=pivot_data['night'], name='Night Shift Performance',
452
  marker_color=DESIGN_SYSTEM['colors']['primary'],
453
  base=pivot_data['day'] if 'day' in pivot_data.columns else 0,
454
  text=pivot_data['night'].round(0), textposition='inside'
455
  ))
456
  fig.update_layout(
457
  **get_chart_theme()['layout'],
458
- title='Shift Optimization Analysis - Nilsen Operations Intelligence',
459
  xaxis_title='Date', yaxis_title='Weight (kg)',
460
  barmode='stack', height=400, showlegend=True
461
  )
462
  else:
463
  grouped = df.groupby(['date', 'shift'])['weight_kg'].sum().reset_index()
464
  fig = px.bar(grouped, x='date', y='weight_kg', color='shift',
465
- title=f'{time_period.title()} Shift Excellence Analysis',
466
  barmode='stack')
467
  fig.update_layout(**get_chart_theme()['layout'], height=400)
468
  return fig
469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
  def create_enhanced_pdf_report(df, stats, outliers, model=None):
471
  buffer = io.BytesIO()
472
  doc = SimpleDocTemplate(buffer, pagesize=A4, rightMargin=50, leftMargin=50, topMargin=50, bottomMargin=50)
473
  elements = []
474
  styles = getSampleStyleSheet()
475
-
476
- title_style = ParagraphStyle('CustomTitle', parent=styles['Heading1'], fontSize=24, spaceAfter=30, alignment=1, textColor=colors.darkblue)
477
- subtitle_style = ParagraphStyle('CustomSubtitle', parent=styles['Heading2'], fontSize=16, spaceAfter=20, textColor=colors.darkblue)
478
- nilsen_style = ParagraphStyle('NilsenStyle', parent=styles['Normal'], fontSize=11, spaceAfter=12, leftIndent=20, textColor=colors.darkgreen)
479
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  elements.append(Spacer(1, 100))
481
- elements.append(Paragraph("Production Excellence Report", title_style))
482
- elements.append(Paragraph("Advanced Analytics by Nilsen Service & Consulting", styles['Heading3']))
483
  elements.append(Spacer(1, 50))
484
-
485
  report_info = f"""
486
  <para alignment="center">
487
  <b>Nilsen Service &amp; Consulting AS</b><br/>
488
- Advanced Production Analytics Division<br/><br/>
489
-
490
- <b>Analysis Period:</b> {df['date'].min().strftime('%B %d, %Y')} - {df['date'].max().strftime('%B %d, %Y')}<br/>
491
  <b>Generated:</b> {datetime.now().strftime('%B %d, %Y at %H:%M')}<br/>
492
- <b>Data Processing:</b> {len(df):,} records analyzed<br/><br/>
493
-
494
- <b>Powered by Nilsen Advanced Analytics Engine</b><br/>
495
- ✓ Machine Learning Algorithms ✓ Predictive Modeling<br/>
496
- ✓ Statistical Analysis ✓ Production Optimization
497
  </para>
498
  """
499
  elements.append(Paragraph(report_info, styles['Normal']))
500
  elements.append(PageBreak())
501
-
502
- # Executive Summary with positive focus
503
  elements.append(Paragraph("Executive Summary", subtitle_style))
504
  total_production = stats['_total_']['total']
505
  work_days = stats['_total_']['work_days']
506
  daily_avg = stats['_total_']['daily_avg']
507
-
508
  exec_summary = f"""
509
  <para>
510
- <b>Production Excellence Achieved:</b> {total_production:,.0f} kg total output across
511
- <b>{work_days} operational days</b>, demonstrating consistent performance with
512
- <b>{daily_avg:,.0f} kg daily average</b>.
513
  <br/><br/>
514
- <b>Nilsen Analytics Highlights:</b><br/>
515
- Comprehensive data processing: {len(df):,} production records<br/>
516
- Multi-material portfolio: {len([k for k in stats.keys() if k != '_total_'])} material types optimized<br/>
517
- Advanced metrics calculated: Efficiency indices, stability scores, trend analysis<br/>
518
- Production intelligence: Real-time monitoring and predictive insights
519
  </para>
520
  """
521
  elements.append(Paragraph(exec_summary, styles['Normal']))
522
  elements.append(Spacer(1, 20))
523
-
524
- # Production Excellence Table
525
- elements.append(Paragraph("Production Portfolio Performance", styles['Heading3']))
526
- summary_data = [['Material Type', 'Total Output (kg)', 'Portfolio Share (%)', 'Daily Excellence (kg)']]
527
  for material, info in stats.items():
528
  if material != '_total_':
529
  summary_data.append([
@@ -532,7 +563,6 @@ def create_enhanced_pdf_report(df, stats, outliers, model=None):
532
  f"{info['percentage']:.1f}%",
533
  f"{info['daily_avg']:,.0f}"
534
  ])
535
-
536
  summary_table = Table(summary_data, colWidths=[2*inch, 1.5*inch, 1*inch, 1.5*inch])
537
  summary_table.setStyle(TableStyle([
538
  ('BACKGROUND', (0, 0), (-1, 0), colors.darkblue),
@@ -544,32 +574,62 @@ def create_enhanced_pdf_report(df, stats, outliers, model=None):
544
  ]))
545
  elements.append(summary_table)
546
  elements.append(PageBreak())
547
-
548
- # Quality Excellence Analysis (reframed positively)
549
- elements.append(Paragraph("Quality Excellence & Consistency Analysis", subtitle_style))
550
- quality_data = [['Material', 'Consistency Score', 'Optimal Range (kg)', 'Performance Status']]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
  for material, info in outliers.items():
552
  if info['count'] == 0:
553
- status = "EXCELLENT"
554
  elif info['count'] <= 3:
555
- status = "OPTIMIZED"
556
  else:
557
- status = "ENHANCING"
558
-
559
- # Calculate consistency score
560
- total_records_material = stats[material]['records']
561
- consistency_score = ((total_records_material - info['count']) / total_records_material) * 100
562
-
563
  quality_data.append([
564
  material.replace('_', ' ').title(),
565
- f"{consistency_score:.1f}%",
566
  info['range'],
567
  status
568
  ])
569
-
570
  quality_table = Table(quality_data, colWidths=[2*inch, 1*inch, 2*inch, 1.5*inch])
571
  quality_table.setStyle(TableStyle([
572
- ('BACKGROUND', (0, 0), (-1, 0), colors.darkgreen),
573
  ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
574
  ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
575
  ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
@@ -577,215 +637,200 @@ def create_enhanced_pdf_report(df, stats, outliers, model=None):
577
  ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey])
578
  ]))
579
  elements.append(quality_table)
580
-
581
- # Nilsen AI Analysis
582
  if model:
583
  elements.append(PageBreak())
584
- elements.append(Paragraph("Nilsen AI Advanced Intelligence Analysis", subtitle_style))
585
  try:
586
- ai_analysis = generate_nilsen_ai_summary(model, df, stats, outliers)
587
  except:
588
- ai_analysis = "Nilsen AI Analytics Engine - Advanced insights available in live system."
589
-
590
  ai_paragraphs = ai_analysis.split('\n\n')
591
  for paragraph in ai_paragraphs:
592
  if paragraph.strip():
593
- formatted_text = paragraph.replace('**', '<b>', 1).replace('**', '</b>', 1).replace('•', ' •').replace('\n', '<br/>')
 
 
594
  elements.append(Paragraph(formatted_text, styles['Normal']))
595
  elements.append(Spacer(1, 8))
596
-
 
 
 
597
  elements.append(Spacer(1, 30))
598
  footer_text = f"""
599
  <para alignment="center">
600
- <i>This report showcases production excellence through advanced analytics<br/>
601
- Nilsen Service &amp; Consulting AS - Production Intelligence Division<br/>
602
- Advanced analysis of {len(df):,} production records demonstrating operational excellence</i>
603
  </para>
604
  """
605
  elements.append(Paragraph(footer_text, styles['Normal']))
606
-
607
  doc.build(elements)
608
  buffer.seek(0)
609
  return buffer
610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  def main():
612
  load_css()
613
-
614
- # Enhanced header with tech highlights
615
  st.markdown("""
616
  <div class="main-header">
617
- <div class="main-title">🏭 Production Excellence Monitor with AI Intelligence</div>
618
- <div class="main-subtitle">Nilsen Service & Consulting AS | Advanced Analytics Predictive Intelligence • Production Optimization</div>
619
  </div>
620
  """, unsafe_allow_html=True)
621
-
622
  model = init_ai()
623
-
624
- # Session state management
625
  if 'current_df' not in st.session_state:
626
  st.session_state.current_df = None
627
  if 'current_stats' not in st.session_state:
628
  st.session_state.current_stats = None
629
-
630
- # Enhanced sidebar with Nilsen branding
631
  with st.sidebar:
632
- st.markdown("### 🏢 Nilsen Analytics Platform")
633
- st.markdown('<div class="nilsen-badge">🚀 Advanced Data Science</div>', unsafe_allow_html=True)
634
-
635
  st.markdown("### 📊 Data Source")
636
  uploaded_file = st.file_uploader("Upload Production Data", type=['csv'])
637
-
638
  st.markdown("---")
639
- st.markdown("### 📊 Quick Demo")
640
  col1, col2 = st.columns(2)
641
  with col1:
642
- if st.button("📊 2024 Analysis", type="primary", key="load_2024"):
643
  st.session_state.load_preset = "2024"
644
  with col2:
645
- if st.button("📊 2025 Analysis", type="primary", key="load_2025"):
646
  st.session_state.load_preset = "2025"
647
-
648
- st.markdown("---")
649
- st.markdown("### 🔬 Technical Capabilities")
650
- if model:
651
- st.success("🤖 AI Engine: Online")
652
- st.markdown("✅ Machine Learning Analysis")
653
- st.markdown("✅ Predictive Modeling")
654
- st.markdown("✅ Advanced Statistics")
655
- st.markdown("✅ Real-time Intelligence")
656
- else:
657
- st.warning("⚠️ AI Engine: Configure API")
658
- st.markdown("🔧 Advanced Analytics Available")
659
- st.markdown("🔧 Statistical Processing Ready")
660
-
661
  st.markdown("---")
662
  st.markdown("""
663
- **Data Format:**
664
  - `date`: MM/DD/YYYY
665
- - `weight_kg`: Production output
666
  - `material_type`: Material category
667
  - `shift`: day/night (optional)
668
  """)
669
-
 
 
 
670
  df = st.session_state.current_df
671
  stats = st.session_state.current_stats
672
-
673
- # Data loading logic
674
  if uploaded_file:
675
  try:
676
  df = load_data(uploaded_file)
677
  stats = get_material_stats(df)
678
  st.session_state.current_df = df
679
  st.session_state.current_stats = stats
680
- st.success("✅ Data processed successfully with Nilsen Analytics!")
681
  except Exception as e:
682
- st.error(f"❌ Data processing error: {str(e)}")
683
-
684
  elif 'load_preset' in st.session_state:
685
  year = st.session_state.load_preset
686
  try:
687
- with st.spinner(f"Loading {year} production data with Nilsen intelligence..."):
688
  df = load_preset_data(year)
689
  if df is not None:
690
  stats = get_material_stats(df)
691
  st.session_state.current_df = df
692
  st.session_state.current_stats = stats
693
- st.success(f"✅ {year} data analyzed successfully!")
694
  except Exception as e:
695
  st.error(f"❌ Error loading {year} data: {str(e)}")
696
  finally:
697
  del st.session_state.load_preset
698
-
699
- # Main dashboard content
700
  if df is not None and stats is not None:
701
-
702
- # Advanced metrics showcase
703
- if model:
704
- st.markdown('<div class="section-header">🎯 Nilsen Advanced Intelligence</div>', unsafe_allow_html=True)
705
-
706
- with st.container():
707
- ai_summary = generate_nilsen_ai_summary(model, df, stats, detect_outliers(df))
708
- st.markdown('<div class="tech-highlight">', unsafe_allow_html=True)
709
- st.markdown(ai_summary)
710
- st.markdown('</div>', unsafe_allow_html=True)
711
-
712
- # Production overview with enhanced metrics
713
- st.markdown('<div class="section-header">📊 Production Excellence Overview</div>', unsafe_allow_html=True)
714
-
715
- if model:
716
- metrics = calculate_advanced_metrics(df, stats)
717
- col1, col2, col3, col4 = st.columns(4)
718
-
719
- with col1:
720
- st.metric(
721
- label="🏆 Nilsen Efficiency Index",
722
- value=f"{metrics['efficiency_index']:.1f}/100",
723
- delta="Proprietary Algorithm"
724
- )
725
-
726
- with col2:
727
- st.metric(
728
- label="⚖️ Balance Optimization",
729
- value=f"{metrics['balance_score']:.1f}%",
730
- delta="Advanced Analysis"
731
- )
732
-
733
- with col3:
734
- st.metric(
735
- label="📈 Stability Score",
736
- value=f"{metrics['stability_score']:.1f}/10",
737
- delta="ML Assessment"
738
- )
739
-
740
- with col4:
741
- st.metric(
742
- label="🎯 Trend Confidence",
743
- value=f"{metrics['trend_strength']:.1f}%",
744
- delta="Predictive Model"
745
- )
746
-
747
- # Material portfolio performance
748
- st.markdown('<div class="section-header">🏷️ Material Portfolio Performance</div>', unsafe_allow_html=True)
749
  materials = [k for k in stats.keys() if k != '_total_']
750
- cols = st.columns(min(4, len(materials) + 1))
751
-
752
  for i, material in enumerate(materials[:3]):
753
  info = stats[material]
754
  with cols[i]:
755
  st.metric(
756
- label=f"🔧 {material.replace('_', ' ').title()}",
757
  value=f"{info['total']:,.0f} kg",
758
- delta=f"{info['percentage']:.1f}% portfolio share"
759
  )
760
- st.caption(f"Excellence: {info['daily_avg']:,.0f} kg/day")
761
-
762
- if len(materials) >= 1:
763
  total_info = stats['_total_']
764
- with cols[min(3, len(materials))]:
765
  st.metric(
766
- label="🎯 Total Excellence",
767
  value=f"{total_info['total']:,.0f} kg",
768
- delta=f"{total_info['work_days']} production days"
769
  )
770
- st.caption(f"Average: {total_info['daily_avg']:,.0f} kg/day")
771
-
772
- # Production trend analysis
773
- st.markdown('<div class="section-header">📈 Production Trend Intelligence</div>', unsafe_allow_html=True)
774
  col1, col2 = st.columns([3, 1])
775
-
776
  with col2:
777
- time_view = st.selectbox("Analysis Period", ["daily", "weekly", "monthly"], key="time_view")
778
-
779
  with col1:
780
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
781
- total_chart = create_total_production_chart(df, time_view)
782
- st.plotly_chart(total_chart, use_container_width=True)
783
- st.markdown('</div>', unsafe_allow_html=True)
784
-
785
- # Materials performance analysis
786
- st.markdown('<div class="section-header">🏭 Materials Excellence Analysis</div>', unsafe_allow_html=True)
787
  col1, col2 = st.columns([3, 1])
788
-
789
  with col2:
790
  selected_materials = st.multiselect(
791
  "Select Materials",
@@ -793,172 +838,82 @@ def main():
793
  default=materials,
794
  key="materials_select"
795
  )
796
-
797
  with col1:
798
  if selected_materials:
 
 
 
 
 
 
 
 
799
  st.markdown('<div class="chart-container">', unsafe_allow_html=True)
800
- materials_chart = create_materials_trend_chart(df, time_view, selected_materials)
801
- st.plotly_chart(materials_chart, use_container_width=True)
802
  st.markdown('</div>', unsafe_allow_html=True)
803
-
804
- # Shift optimization analysis
805
- if 'shift' in df.columns:
806
- st.markdown('<div class="section-header">🌓 Shift Optimization Intelligence</div>', unsafe_allow_html=True)
807
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
808
- shift_chart = create_shift_trend_chart(df, time_view)
809
- st.plotly_chart(shift_chart, use_container_width=True)
810
- st.markdown('</div>', unsafe_allow_html=True)
811
-
812
- # Quality excellence analysis (reframed positively)
813
- st.markdown('<div class="section-header">✅ Quality Excellence Monitor</div>', unsafe_allow_html=True)
814
  outliers = detect_outliers(df)
815
  cols = st.columns(len(outliers))
816
-
817
  for i, (material, info) in enumerate(outliers.items()):
818
  with cols[i]:
819
- total_material_records = stats[material]['records']
820
- consistency_rate = ((total_material_records - info['count']) / total_material_records) * 100
821
-
822
- if info['count'] == 0:
823
- st.markdown(f'<div class="alert-success"><strong>🏆 {material.title()}</strong><br>Perfect Consistency: 100%<br>Optimal Range: {info["range"]}</div>', unsafe_allow_html=True)
824
- elif info['count'] <= 3:
825
- st.markdown(f'<div class="alert-success"><strong>✅ {material.title()}</strong><br>High Consistency: {consistency_rate:.1f}%<br>Optimal Range: {info["range"]}</div>', unsafe_allow_html=True)
826
  else:
827
- dates_str = f"{', '.join(info['dates'][:2])}, +{len(info['dates'])-2} more" if len(info['dates']) > 2 else ", ".join(info['dates'])
828
- st.markdown(f'<div class="alert-warning"><strong>🎯 {material.title()}</strong><br>Optimization Opportunity: {consistency_rate:.1f}%<br>Target Range: {info["range"]}<br><small>Enhancement dates: {dates_str}</small></div>', unsafe_allow_html=True)
829
-
830
- # AI Intelligence Center
831
  if model:
832
- st.markdown('<div class="section-header">🤖 Nilsen AI Intelligence Center</div>', unsafe_allow_html=True)
833
-
834
- # Showcase questions focused on capabilities
835
- showcase_questions = [
836
- "Demonstrate your advanced analytical capabilities on this production data",
837
- "What production excellence and competitive advantages do you identify?",
838
- "Show me the predictive insights from your machine learning analysis"
839
  ]
840
-
841
- st.markdown("### 🚀 AI Capability Demonstrations")
842
- cols = st.columns(len(showcase_questions))
843
-
844
- for i, question in enumerate(showcase_questions):
845
  with cols[i]:
846
- if st.button(f"🔬 {question}", key=f"showcase_{i}"):
847
- with st.spinner("Nilsen AI analyzing with advanced algorithms..."):
848
- answer = query_nilsen_ai(model, stats, question, df)
849
- st.success(answer)
850
-
851
- st.markdown("### 💬 Custom Intelligence Query")
852
- custom_question = st.text_input(
853
- "Ask Nilsen AI about your production excellence:",
854
- placeholder="e.g., 'What optimization strategies do you recommend?'",
855
- key="custom_question"
856
- )
857
-
858
- if custom_question and st.button("🚀 Analyze with Nilsen AI", key="custom_ai"):
859
- with st.spinner("Processing with Nilsen advanced intelligence..."):
860
- answer = query_nilsen_ai(model, stats, custom_question, df)
861
  st.success(f"**Q:** {custom_question}")
862
- st.write(answer)
863
-
864
- # Enhanced export section
865
- st.markdown('<div class="section-header">📄 Nilsen Intelligence Reports</div>', unsafe_allow_html=True)
866
-
867
- col1, col2, col3 = st.columns(3)
868
-
869
- with col1:
870
- if st.button("🎯 Generate AI Excellence Report", type="primary", key="gen_pdf"):
871
- try:
872
- with st.spinner("Generating Nilsen AI Excellence Report..."):
873
- pdf_buffer = create_enhanced_pdf_report(df, stats, outliers, model)
874
- st.success("✅ Intelligence report generated!")
875
-
876
- st.download_button(
877
- label="💾 Download Excellence Report",
878
- data=pdf_buffer,
879
- file_name=f"nilsen_production_excellence_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf",
880
- mime="application/pdf"
881
- )
882
- except Exception as e:
883
- st.error(f"❌ Report generation error: {str(e)}")
884
-
885
- with col2:
886
- csv_summary = pd.DataFrame([
887
- {
888
- 'Material': material.replace('_', ' ').title(),
889
- 'Total_Output_kg': info['total'],
890
- 'Portfolio_Share_Percent': info['percentage'],
891
- 'Daily_Excellence_kg': info['daily_avg'],
892
- 'Production_Days': info['work_days']
893
- }
894
- for material, info in stats.items() if material != '_total_'
895
- ])
896
-
897
- csv_string = csv_summary.to_csv(index=False)
898
- st.download_button(
899
- label="📊 Download Portfolio Analysis",
900
- data=csv_string,
901
- file_name=f"nilsen_portfolio_analysis_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
902
- mime="text/csv"
903
- )
904
-
905
- with col3:
906
- raw_csv = df.to_csv(index=False)
907
- st.download_button(
908
- label="📁 Download Raw Production Data",
909
- data=raw_csv,
910
- file_name=f"production_data_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
911
- mime="text/csv"
912
- )
913
-
914
  else:
915
- # Welcome screen with Nilsen branding
916
- st.markdown('<div class="section-header">🚀 Nilsen Production Intelligence Platform</div>', unsafe_allow_html=True)
917
-
918
  col1, col2 = st.columns(2)
919
-
920
  with col1:
921
  st.markdown("""
922
- ### 🎯 Advanced Analytics Capabilities
923
- - **AI-Powered Intelligence**: Machine learning insights
924
- - **Predictive Modeling**: Trend forecasting & optimization
925
- - **Real-Time Monitoring**: Live production analytics
926
- - **Excellence Identification**: Competitive advantage discovery
927
- - **Portfolio Analysis**: Multi-material performance
928
- - **Quality Intelligence**: Consistency optimization
929
- - **Custom Reporting**: AI-generated insights
930
  """)
931
-
932
  with col2:
933
  st.markdown("""
934
- ### 🏆 Nilsen Technical Advantages
935
- - **Proprietary Algorithms**: Efficiency indices & scores
936
- - **Advanced Metrics**: Beyond standard analytics
937
- - **Industry Expertise**: Production optimization focus
938
- - **Intelligent Insights**: Value-driven recommendations
939
- - **Professional Reports**: Executive-ready analysis
940
- - **Scalable Platform**: Enterprise-grade processing
941
- - **Consulting Integration**: Expert advisory included
942
  """)
943
-
944
- st.markdown('<div class="tech-highlight">', unsafe_allow_html=True)
945
- st.markdown("""
946
- ### 🚀 Get Started with Nilsen Intelligence
947
-
948
- **Option 1:** Upload your production data using the sidebar
949
-
950
- **Option 2:** Try our demo with the Quick Demo buttons
951
-
952
- **What You'll Get:**
953
- - Advanced AI analysis showcasing production excellence
954
- - Professional insights highlighting competitive advantages
955
- - Technical metrics demonstrating operational efficiency
956
- - Predictive intelligence for optimization opportunities
957
- - Executive-ready reports with Nilsen professional branding
958
- """)
959
- st.markdown('</div>', unsafe_allow_html=True)
960
-
961
- st.info("🎯 **Ready to discover your production excellence?** Upload data or use Quick Demo to experience Nilsen's advanced analytics capabilities!")
962
 
963
  if __name__ == "__main__":
964
  main()
 
66
  opacity: 0.9;
67
  margin-top: 0.5rem;
68
  }}
69
+ .metric-card {{
70
+ background: white;
71
+ border: 1px solid {DESIGN_SYSTEM['colors']['border']};
72
+ border-radius: 12px;
73
+ padding: 1.5rem;
74
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
75
+ transition: transform 0.2s ease;
 
 
 
 
 
 
 
 
76
  }}
77
  .section-header {{
78
  {DESIGN_SYSTEM['fonts']['subtitle']}
 
112
  font-weight: 500;
113
  transition: all 0.2s ease;
114
  }}
115
+ .stDownloadButton > button {{
116
+ background: {DESIGN_SYSTEM['colors']['primary']} !important;
117
+ color: white !important;
118
+ border: none !important;
119
+ border-radius: 8px !important;
120
+ padding: 0.5rem 1rem !important;
121
+ font-weight: 500 !important;
122
+ transition: all 0.2s ease !important;
123
+ }}
124
  </style>
125
  """, unsafe_allow_html=True)
126
 
 
164
  for date in weekdays:
165
  for material in materials:
166
  for shift in shifts:
167
+ base_weight = {
168
+ 'steel': 1500,
169
+ 'aluminum': 800,
170
+ 'plastic': 600,
171
+ 'copper': 400
172
+ }[material]
173
  weight = base_weight + np.random.normal(0, base_weight * 0.2)
174
  weight = max(weight, base_weight * 0.3)
175
  data.append({
 
214
  }
215
  return stats
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  def get_chart_theme():
218
  return {
219
  'layout': {
 
230
  if time_period == 'daily':
231
  grouped = df.groupby('date')['weight_kg'].sum().reset_index()
232
  fig = px.line(grouped, x='date', y='weight_kg',
233
+ title='Total Production Trend',
234
  labels={'weight_kg': 'Weight (kg)', 'date': 'Date'})
235
  elif time_period == 'weekly':
236
  df_copy = df.copy()
 
239
  grouped = df_copy.groupby(['year', 'week'])['weight_kg'].sum().reset_index()
240
  grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str)
241
  fig = px.bar(grouped, x='week_label', y='weight_kg',
242
+ title='Total Production Trend (Weekly)',
243
  labels={'weight_kg': 'Weight (kg)', 'week_label': 'Week'})
244
  else:
245
  df_copy = df.copy()
 
247
  grouped = df_copy.groupby('month')['weight_kg'].sum().reset_index()
248
  grouped['month'] = grouped['month'].astype(str)
249
  fig = px.bar(grouped, x='month', y='weight_kg',
250
+ title='Total Production Trend (Monthly)',
251
  labels={'weight_kg': 'Weight (kg)', 'month': 'Month'})
252
  fig.update_layout(**get_chart_theme()['layout'], height=400, showlegend=False)
253
  return fig
 
259
  if time_period == 'daily':
260
  grouped = df_copy.groupby(['date', 'material_type'])['weight_kg'].sum().reset_index()
261
  fig = px.line(grouped, x='date', y='weight_kg', color='material_type',
262
+ title='Materials Production Trends',
263
  labels={'weight_kg': 'Weight (kg)', 'date': 'Date', 'material_type': 'Material'})
264
  elif time_period == 'weekly':
265
  df_copy['week'] = df_copy['date'].dt.isocalendar().week
 
267
  grouped = df_copy.groupby(['year', 'week', 'material_type'])['weight_kg'].sum().reset_index()
268
  grouped['week_label'] = grouped['year'].astype(str) + '-W' + grouped['week'].astype(str)
269
  fig = px.bar(grouped, x='week_label', y='weight_kg', color='material_type',
270
+ title='Materials Production Trends (Weekly)',
271
  labels={'weight_kg': 'Weight (kg)', 'week_label': 'Week', 'material_type': 'Material'})
272
  else:
273
  df_copy['month'] = df_copy['date'].dt.to_period('M')
274
  grouped = df_copy.groupby(['month', 'material_type'])['weight_kg'].sum().reset_index()
275
  grouped['month'] = grouped['month'].astype(str)
276
  fig = px.bar(grouped, x='month', y='weight_kg', color='material_type',
277
+ title='Materials Production Trends (Monthly)',
278
  labels={'weight_kg': 'Weight (kg)', 'month': 'Month', 'material_type': 'Material'})
279
  fig.update_layout(**get_chart_theme()['layout'], height=400)
280
  return fig
 
286
  fig = go.Figure()
287
  if 'day' in pivot_data.columns:
288
  fig.add_trace(go.Bar(
289
+ x=pivot_data.index, y=pivot_data['day'], name='Day Shift',
290
  marker_color=DESIGN_SYSTEM['colors']['warning'],
291
  text=pivot_data['day'].round(0), textposition='inside'
292
  ))
293
  if 'night' in pivot_data.columns:
294
  fig.add_trace(go.Bar(
295
+ x=pivot_data.index, y=pivot_data['night'], name='Night Shift',
296
  marker_color=DESIGN_SYSTEM['colors']['primary'],
297
  base=pivot_data['day'] if 'day' in pivot_data.columns else 0,
298
  text=pivot_data['night'].round(0), textposition='inside'
299
  ))
300
  fig.update_layout(
301
  **get_chart_theme()['layout'],
302
+ title='Daily Shift Production Trends (Stacked)',
303
  xaxis_title='Date', yaxis_title='Weight (kg)',
304
  barmode='stack', height=400, showlegend=True
305
  )
306
  else:
307
  grouped = df.groupby(['date', 'shift'])['weight_kg'].sum().reset_index()
308
  fig = px.bar(grouped, x='date', y='weight_kg', color='shift',
309
+ title=f'{time_period.title()} Shift Production Trends',
310
  barmode='stack')
311
  fig.update_layout(**get_chart_theme()['layout'], height=400)
312
  return fig
313
 
314
+ def detect_outliers(df):
315
+ outliers = {}
316
+ for material in df['material_type'].unique():
317
+ material_data = df[df['material_type'] == material]
318
+ data = material_data['weight_kg']
319
+ Q1, Q3 = data.quantile(0.25), data.quantile(0.75)
320
+ IQR = Q3 - Q1
321
+ lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
322
+ outlier_mask = (data < lower) | (data > upper)
323
+ outlier_dates = material_data[outlier_mask]['date'].dt.strftime('%Y-%m-%d').tolist()
324
+ outliers[material] = {
325
+ 'count': len(outlier_dates),
326
+ 'range': f"{lower:.0f} - {upper:.0f} kg",
327
+ 'dates': outlier_dates
328
+ }
329
+ return outliers
330
+
331
+ def generate_ai_summary(model, df, stats, outliers):
332
+ if not model:
333
+ return "AI analysis unavailable - API key not configured"
334
+ try:
335
+ materials = [k for k in stats.keys() if k != '_total_']
336
+ context_parts = [
337
+ "# Production Data Analysis Context",
338
+ f"## Overview",
339
+ f"- Total Production: {stats['_total_']['total']:,.0f} kg",
340
+ f"- Production Period: {stats['_total_']['work_days']} working days",
341
+ f"- Daily Average: {stats['_total_']['daily_avg']:,.0f} kg",
342
+ f"- Materials Tracked: {len(materials)}",
343
+ "",
344
+ "## Material Breakdown:"
345
+ ]
346
+ for material in materials:
347
+ info = stats[material]
348
+ context_parts.append(f"- {material.title()}: {info['total']:,.0f} kg ({info['percentage']:.1f}%), avg {info['daily_avg']:,.0f} kg/day")
349
+ daily_data = df.groupby('date')['weight_kg'].sum()
350
+ trend_direction = "increasing" if daily_data.iloc[-1] > daily_data.iloc[0] else "decreasing"
351
+ volatility = daily_data.std() / daily_data.mean() * 100
352
+ context_parts.extend([
353
+ "",
354
+ "## Trend Analysis:",
355
+ f"- Overall trend: {trend_direction}",
356
+ f"- Production volatility: {volatility:.1f}% coefficient of variation",
357
+ f"- Peak production: {daily_data.max():,.0f} kg",
358
+ f"- Lowest production: {daily_data.min():,.0f} kg"
359
+ ])
360
+ total_outliers = sum(info['count'] for info in outliers.values())
361
+ context_parts.extend([
362
+ "",
363
+ "## Quality Control:",
364
+ f"- Total outliers detected: {total_outliers}",
365
+ f"- Materials with quality issues: {sum(1 for info in outliers.values() if info['count'] > 0)}"
366
+ ])
367
+ if 'shift' in df.columns:
368
+ shift_stats = df.groupby('shift')['weight_kg'].sum()
369
+ context_parts.extend([
370
+ "",
371
+ "## Shift Performance:",
372
+ f"- Day shift: {shift_stats.get('day', 0):,.0f} kg",
373
+ f"- Night shift: {shift_stats.get('night', 0):,.0f} kg"
374
+ ])
375
+ context_text = "\n".join(context_parts)
376
+ prompt = f"""
377
+ {context_text}
378
+
379
+ As an expert AI analyst embedded within the "Production Monitor with AI Insights" platform, provide a comprehensive analysis based on the data provided. Your tone should be professional and data-driven. Your primary goal is to highlight how the platform's features reveal critical insights.
380
+
381
+ Structure your response in the following format:
382
+
383
+ **PRODUCTION ASSESSMENT**
384
+ Evaluate the overall production status (Excellent/Good/Needs Attention). Briefly justify your assessment using key metrics from the data summary.
385
+
386
+ **KEY FINDINGS**
387
+ Identify 3-4 of the most important insights. For each finding, explicitly mention the platform feature that made the discovery possible. Use formats like "(revealed by the 'Quality Check' module)" or "(visualized in the 'Production Trend' chart)".
388
+
389
+ Example Finding format:
390
+ • Finding X: [Your insight, e.g., "Liquid-Ctu production shows high volatility..."] (as identified by the 'Materials Analysis' view).
391
+
392
+ **RECOMMENDATIONS**
393
+ Provide 2-3 actionable recommendations. Frame these as steps the management can take, encouraging them to use the platform for further investigation.
394
+
395
+ Example Recommendation format:
396
+ • Recommendation Y: [Your recommendation, e.g., "Investigate the root causes of the 11 outliers..."] We recommend using the platform's interactive charts to drill down into the specific dates identified by the 'Quality Check' module.
397
+
398
+ Keep the entire analysis concise and under 300 words.
399
+ """
400
+ response = model.generate_content(prompt)
401
+ return response.text
402
+ except Exception as e:
403
+ return f"AI analysis error: {str(e)}"
404
+
405
+ def query_ai(model, stats, question, df=None):
406
+ if not model:
407
+ return "AI assistant not available"
408
+ context_parts = [
409
+ "Production Data Summary:",
410
+ *[f"- {mat.title()}: {info['total']:,.0f}kg ({info['percentage']:.1f}%)"
411
+ for mat, info in stats.items() if mat != '_total_'],
412
+ f"\nTotal Production: {stats['_total_']['total']:,.0f}kg across {stats['_total_']['work_days']} work days"
413
+ ]
414
+ if df is not None:
415
+ available_cols = list(df.columns)
416
+ context_parts.append(f"\nAvailable data fields: {', '.join(available_cols)}")
417
+ if 'shift' in df.columns:
418
+ shift_stats = df.groupby('shift')['weight_kg'].sum()
419
+ context_parts.append(f"Shift breakdown: {dict(shift_stats)}")
420
+ if 'day_name' in df.columns:
421
+ day_stats = df.groupby('day_name')['weight_kg'].mean()
422
+ context_parts.append(f"Average daily production: {dict(day_stats.round(0))}")
423
+ context = "\n".join(context_parts) + f"\n\nQuestion: {question}\nAnswer based on available data:"
424
+ try:
425
+ response = model.generate_content(context)
426
+ return response.text
427
+ except:
428
+ return "Error getting AI response"
429
+
430
+ def save_plotly_as_image(fig, filename):
431
+ try:
432
+ temp_dir = tempfile.gettempdir()
433
+ filepath = os.path.join(temp_dir, filename)
434
+ theme = get_chart_theme()['layout'].copy()
435
+ theme.update({
436
+ 'font': dict(size=12, family="Arial"),
437
+ 'plot_bgcolor': 'white',
438
+ 'paper_bgcolor': 'white',
439
+ 'margin': dict(t=50, b=40, l=40, r=40)
440
+ })
441
+ fig.update_layout(**theme)
442
+ try:
443
+ pio.write_image(fig, filepath, format='png', width=800, height=400, scale=2, engine='kaleido')
444
+ if os.path.exists(filepath):
445
+ return filepath
446
+ except:
447
+ pass
448
+ return None
449
+ except Exception as e:
450
+ return None
451
+
452
+ def create_pdf_charts(df, stats):
453
+ charts = {}
454
+ try:
455
+ materials = [k for k in stats.keys() if k != '_total_']
456
+ values = [stats[mat]['total'] for mat in materials]
457
+ labels = [mat.replace('_', ' ').title() for mat in materials]
458
+ if len(materials) > 0 and len(values) > 0:
459
+ try:
460
+ fig_pie = px.pie(values=values, names=labels, title="Production Distribution by Material")
461
+ charts['pie'] = save_plotly_as_image(fig_pie, "distribution.png")
462
+ except:
463
+ pass
464
+ if len(df) > 0:
465
+ try:
466
+ daily_data = df.groupby('date')['weight_kg'].sum().reset_index()
467
+ if len(daily_data) > 0:
468
+ fig_trend = px.line(daily_data, x='date', y='weight_kg', title="Daily Production Trend",
469
+ labels={'date': 'Date', 'weight_kg': 'Weight (kg)'},
470
+ color_discrete_sequence=[DESIGN_SYSTEM['colors']['primary']])
471
+ charts['trend'] = save_plotly_as_image(fig_trend, "trend.png")
472
+ except:
473
+ pass
474
+ if len(materials) > 0 and len(values) > 0:
475
+ try:
476
+ fig_bar = px.bar(x=labels, y=values, title="Production by Material Type",
477
+ labels={'x': 'Material Type', 'y': 'Weight (kg)'},
478
+ color_discrete_sequence=[DESIGN_SYSTEM['colors']['primary']])
479
+ charts['bar'] = save_plotly_as_image(fig_bar, "materials.png")
480
+ except:
481
+ pass
482
+ if 'shift' in df.columns and len(df) > 0:
483
+ try:
484
+ shift_data = df.groupby('shift')['weight_kg'].sum().reset_index()
485
+ if len(shift_data) > 0 and shift_data['weight_kg'].sum() > 0:
486
+ fig_shift = px.pie(shift_data, values='weight_kg', names='shift', title="Production by Shift")
487
+ charts['shift'] = save_plotly_as_image(fig_shift, "shifts.png")
488
+ except:
489
+ pass
490
+ except Exception as e:
491
+ pass
492
+ return charts
493
+
494
  def create_enhanced_pdf_report(df, stats, outliers, model=None):
495
  buffer = io.BytesIO()
496
  doc = SimpleDocTemplate(buffer, pagesize=A4, rightMargin=50, leftMargin=50, topMargin=50, bottomMargin=50)
497
  elements = []
498
  styles = getSampleStyleSheet()
499
+ title_style = ParagraphStyle(
500
+ 'CustomTitle',
501
+ parent=styles['Heading1'],
502
+ fontSize=24,
503
+ spaceAfter=30,
504
+ alignment=1,
505
+ textColor=colors.darkblue
506
+ )
507
+ subtitle_style = ParagraphStyle(
508
+ 'CustomSubtitle',
509
+ parent=styles['Heading2'],
510
+ fontSize=16,
511
+ spaceAfter=20,
512
+ textColor=colors.darkblue
513
+ )
514
+ ai_style = ParagraphStyle(
515
+ 'AIStyle',
516
+ parent=styles['Normal'],
517
+ fontSize=11,
518
+ spaceAfter=12,
519
+ leftIndent=20,
520
+ textColor=colors.darkgreen
521
+ )
522
  elements.append(Spacer(1, 100))
523
+ elements.append(Paragraph("Production Monitor with AI Insights", title_style))
524
+ elements.append(Paragraph("Comprehensive Production Analysis Report", styles['Heading3']))
525
  elements.append(Spacer(1, 50))
 
526
  report_info = f"""
527
  <para alignment="center">
528
  <b>Nilsen Service &amp; Consulting AS</b><br/>
529
+ Production Analytics Division<br/><br/>
530
+ <b>Report Period:</b> {df['date'].min().strftime('%B %d, %Y')} - {df['date'].max().strftime('%B %d, %Y')}<br/>
 
531
  <b>Generated:</b> {datetime.now().strftime('%B %d, %Y at %H:%M')}<br/>
532
+ <b>Total Records:</b> {len(df):,}
 
 
 
 
533
  </para>
534
  """
535
  elements.append(Paragraph(report_info, styles['Normal']))
536
  elements.append(PageBreak())
 
 
537
  elements.append(Paragraph("Executive Summary", subtitle_style))
538
  total_production = stats['_total_']['total']
539
  work_days = stats['_total_']['work_days']
540
  daily_avg = stats['_total_']['daily_avg']
 
541
  exec_summary = f"""
542
  <para>
543
+ This report analyzes production data spanning <b>{work_days} working days</b>.
544
+ Total output achieved: <b>{total_production:,.0f} kg</b> with an average
545
+ daily production of <b>{daily_avg:,.0f} kg</b>.
546
  <br/><br/>
547
+ <b>Key Highlights:</b><br/>
548
+ Total production: {total_production:,.0f} kg<br/>
549
+ Daily average: {daily_avg:,.0f} kg<br/>
550
+ Materials tracked: {len([k for k in stats.keys() if k != '_total_'])}<br/>
551
+ Data quality: {len(df):,} records processed
552
  </para>
553
  """
554
  elements.append(Paragraph(exec_summary, styles['Normal']))
555
  elements.append(Spacer(1, 20))
556
+ elements.append(Paragraph("Production Summary", styles['Heading3']))
557
+ summary_data = [['Material Type', 'Total (kg)', 'Share (%)', 'Daily Avg (kg)']]
 
 
558
  for material, info in stats.items():
559
  if material != '_total_':
560
  summary_data.append([
 
563
  f"{info['percentage']:.1f}%",
564
  f"{info['daily_avg']:,.0f}"
565
  ])
 
566
  summary_table = Table(summary_data, colWidths=[2*inch, 1.5*inch, 1*inch, 1.5*inch])
567
  summary_table.setStyle(TableStyle([
568
  ('BACKGROUND', (0, 0), (-1, 0), colors.darkblue),
 
574
  ]))
575
  elements.append(summary_table)
576
  elements.append(PageBreak())
577
+ elements.append(Paragraph("Production Analysis Charts", subtitle_style))
578
+ try:
579
+ charts = create_pdf_charts(df, stats)
580
+ except:
581
+ charts = {}
582
+ charts_added = False
583
+ chart_insights = {
584
+ 'pie': "Material distribution shows production allocation across different materials. Balanced distribution indicates diversified production capabilities.",
585
+ 'trend': "Production trend reveals operational patterns and seasonal variations. Consistent trends suggest stable operational efficiency.",
586
+ 'bar': "Material comparison highlights performance differences and production capacities. Top performers indicate optimization opportunities.",
587
+ 'shift': "Shift analysis reveals operational efficiency differences between day and night operations. Balance indicates effective resource utilization."
588
+ }
589
+ for chart_type, chart_title in [
590
+ ('pie', "Production Distribution"),
591
+ ('trend', "Production Trend"),
592
+ ('bar', "Material Comparison"),
593
+ ('shift', "Shift Analysis")
594
+ ]:
595
+ chart_path = charts.get(chart_type)
596
+ if chart_path and os.path.exists(chart_path):
597
+ try:
598
+ elements.append(Paragraph(chart_title, styles['Heading3']))
599
+ elements.append(Image(chart_path, width=6*inch, height=3*inch))
600
+ insight_text = f"<i>Analysis: {chart_insights.get(chart_type, 'Chart analysis not available.')}</i>"
601
+ elements.append(Paragraph(insight_text, ai_style))
602
+ elements.append(Spacer(1, 20))
603
+ charts_added = True
604
+ except Exception as e:
605
+ pass
606
+ if not charts_added:
607
+ elements.append(Paragraph("Charts Generation Failed", styles['Heading3']))
608
+ elements.append(Paragraph("Production Data Summary:", styles['Normal']))
609
+ for material, info in stats.items():
610
+ if material != '_total_':
611
+ summary_text = f"• {material.replace('_', ' ').title()}: {info['total']:,.0f} kg ({info['percentage']:.1f}%)"
612
+ elements.append(Paragraph(summary_text, styles['Normal']))
613
+ elements.append(Spacer(1, 20))
614
+ elements.append(PageBreak())
615
+ elements.append(Paragraph("Quality Control Analysis", subtitle_style))
616
+ quality_data = [['Material', 'Outliers', 'Normal Range (kg)', 'Status']]
617
  for material, info in outliers.items():
618
  if info['count'] == 0:
619
+ status = "GOOD"
620
  elif info['count'] <= 3:
621
+ status = "MONITOR"
622
  else:
623
+ status = "ATTENTION"
 
 
 
 
 
624
  quality_data.append([
625
  material.replace('_', ' ').title(),
626
+ str(info['count']),
627
  info['range'],
628
  status
629
  ])
 
630
  quality_table = Table(quality_data, colWidths=[2*inch, 1*inch, 2*inch, 1.5*inch])
631
  quality_table.setStyle(TableStyle([
632
+ ('BACKGROUND', (0, 0), (-1, 0), colors.darkred),
633
  ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
634
  ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
635
  ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
 
637
  ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey])
638
  ]))
639
  elements.append(quality_table)
 
 
640
  if model:
641
  elements.append(PageBreak())
642
+ elements.append(Paragraph("AI Intelligent Analysis", subtitle_style))
643
  try:
644
+ ai_analysis = generate_ai_summary(model, df, stats, outliers)
645
  except:
646
+ ai_analysis = "AI analysis temporarily unavailable."
 
647
  ai_paragraphs = ai_analysis.split('\n\n')
648
  for paragraph in ai_paragraphs:
649
  if paragraph.strip():
650
+ formatted_text = paragraph.replace('**', '<b>', 1).replace('**', '</b>', 1) \
651
+ .replace('•', ' •') \
652
+ .replace('\n', '<br/>')
653
  elements.append(Paragraph(formatted_text, styles['Normal']))
654
  elements.append(Spacer(1, 8))
655
+ else:
656
+ elements.append(PageBreak())
657
+ elements.append(Paragraph("AI Analysis", subtitle_style))
658
+ elements.append(Paragraph("AI analysis unavailable - API key not configured. Please configure Google AI API key to enable intelligent insights.", styles['Normal']))
659
  elements.append(Spacer(1, 30))
660
  footer_text = f"""
661
  <para alignment="center">
662
+ <i>This report was generated by Production Monitor System<br/>
663
+ Nilsen Service &amp; Consulting AS - Production Analytics Division<br/>
664
+ Report contains {len(df):,} data records across {stats['_total_']['work_days']} working days</i>
665
  </para>
666
  """
667
  elements.append(Paragraph(footer_text, styles['Normal']))
 
668
  doc.build(elements)
669
  buffer.seek(0)
670
  return buffer
671
 
672
+ def create_csv_export(df, stats):
673
+ summary_df = pd.DataFrame([
674
+ {
675
+ 'Material': material.replace('_', ' ').title(),
676
+ 'Total_kg': info['total'],
677
+ 'Percentage': info['percentage'],
678
+ 'Daily_Average_kg': info['daily_avg'],
679
+ 'Work_Days': info['work_days'],
680
+ 'Records_Count': info['records']
681
+ }
682
+ for material, info in stats.items() if material != '_total_'
683
+ ])
684
+ return summary_df
685
+
686
+ def add_export_section(df, stats, outliers, model):
687
+ st.markdown('<div class="section-header">📄 Export Reports</div>', unsafe_allow_html=True)
688
+ if 'export_ready' not in st.session_state:
689
+ st.session_state.export_ready = False
690
+ if 'pdf_buffer' not in st.session_state:
691
+ st.session_state.pdf_buffer = None
692
+ if 'csv_data' not in st.session_state:
693
+ st.session_state.csv_data = None
694
+ col1, col2, col3 = st.columns(3)
695
+ with col1:
696
+ if st.button("Generate PDF Report with AI", key="generate_pdf_btn", type="primary"):
697
+ try:
698
+ with st.spinner("Generating PDF with AI analysis..."):
699
+ st.session_state.pdf_buffer = create_enhanced_pdf_report(df, stats, outliers, model)
700
+ st.session_state.export_ready = True
701
+ st.success("✅ PDF report with AI analysis generated successfully!")
702
+ except Exception as e:
703
+ st.error(f"❌ PDF generation failed: {str(e)}")
704
+ st.session_state.export_ready = False
705
+ if st.session_state.export_ready and st.session_state.pdf_buffer:
706
+ st.download_button(
707
+ label="💾 Download PDF Report",
708
+ data=st.session_state.pdf_buffer,
709
+ file_name=f"production_report_ai_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf",
710
+ mime="application/pdf",
711
+ key="download_pdf_btn"
712
+ )
713
+ with col2:
714
+ if st.button("Generate CSV Summary", key="generate_csv_btn", type="primary"):
715
+ try:
716
+ st.session_state.csv_data = create_csv_export(df, stats)
717
+ st.success("✅ CSV summary generated successfully!")
718
+ except Exception as e:
719
+ st.error(f"❌ CSV generation failed: {str(e)}")
720
+ if st.session_state.csv_data is not None:
721
+ csv_string = st.session_state.csv_data.to_csv(index=False)
722
+ st.download_button(
723
+ label="💾 Download CSV Summary",
724
+ data=csv_string,
725
+ file_name=f"production_summary_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
726
+ mime="text/csv",
727
+ key="download_csv_btn"
728
+ )
729
+ with col3:
730
+ csv_string = df.to_csv(index=False)
731
+ st.download_button(
732
+ label="Download Raw Data",
733
+ data=csv_string,
734
+ file_name=f"raw_production_data_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
735
+ mime="text/csv",
736
+ key="download_raw_btn"
737
+ )
738
  def main():
739
  load_css()
 
 
740
  st.markdown("""
741
  <div class="main-header">
742
+ <div class="main-title">🏭 Production Monitor with AI Insights</div>
743
+ <div class="main-subtitle">Nilsen Service & Consulting AS | Real-time Production Analytics & Recommendations</div>
744
  </div>
745
  """, unsafe_allow_html=True)
 
746
  model = init_ai()
 
 
747
  if 'current_df' not in st.session_state:
748
  st.session_state.current_df = None
749
  if 'current_stats' not in st.session_state:
750
  st.session_state.current_stats = None
 
 
751
  with st.sidebar:
 
 
 
752
  st.markdown("### 📊 Data Source")
753
  uploaded_file = st.file_uploader("Upload Production Data", type=['csv'])
 
754
  st.markdown("---")
755
+ st.markdown("### 📊 Quick Load")
756
  col1, col2 = st.columns(2)
757
  with col1:
758
+ if st.button("📊 2024 Data", type="primary", key="load_2024"):
759
  st.session_state.load_preset = "2024"
760
  with col2:
761
+ if st.button("📊 2025 Data", type="primary", key="load_2025"):
762
  st.session_state.load_preset = "2025"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  st.markdown("---")
764
  st.markdown("""
765
+ **Expected TSV format:**
766
  - `date`: MM/DD/YYYY
767
+ - `weight_kg`: Production weight
768
  - `material_type`: Material category
769
  - `shift`: day/night (optional)
770
  """)
771
+ if model:
772
+ st.success("🤖 AI Assistant Ready")
773
+ else:
774
+ st.warning("⚠️ AI Assistant Unavailable")
775
  df = st.session_state.current_df
776
  stats = st.session_state.current_stats
 
 
777
  if uploaded_file:
778
  try:
779
  df = load_data(uploaded_file)
780
  stats = get_material_stats(df)
781
  st.session_state.current_df = df
782
  st.session_state.current_stats = stats
783
+ st.success("✅ Data uploaded successfully!")
784
  except Exception as e:
785
+ st.error(f"❌ Error loading uploaded file: {str(e)}")
 
786
  elif 'load_preset' in st.session_state:
787
  year = st.session_state.load_preset
788
  try:
789
+ with st.spinner(f"Loading {year} data..."):
790
  df = load_preset_data(year)
791
  if df is not None:
792
  stats = get_material_stats(df)
793
  st.session_state.current_df = df
794
  st.session_state.current_stats = stats
795
+ st.success(f"✅ {year} data loaded successfully!")
796
  except Exception as e:
797
  st.error(f"❌ Error loading {year} data: {str(e)}")
798
  finally:
799
  del st.session_state.load_preset
 
 
800
  if df is not None and stats is not None:
801
+ st.markdown('<div class="section-header">📋 Material Overview</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
802
  materials = [k for k in stats.keys() if k != '_total_']
803
+ cols = st.columns(4)
 
804
  for i, material in enumerate(materials[:3]):
805
  info = stats[material]
806
  with cols[i]:
807
  st.metric(
808
+ label=material.replace('_', ' ').title(),
809
  value=f"{info['total']:,.0f} kg",
810
+ delta=f"{info['percentage']:.1f}% of total"
811
  )
812
+ st.caption(f"Daily avg: {info['daily_avg']:,.0f} kg")
813
+ if len(materials) >= 3:
 
814
  total_info = stats['_total_']
815
+ with cols[3]:
816
  st.metric(
817
+ label="Total Production",
818
  value=f"{total_info['total']:,.0f} kg",
819
+ delta="100% of total"
820
  )
821
+ st.caption(f"Daily avg: {total_info['daily_avg']:,.0f} kg")
822
+ st.markdown('<div class="section-header">📊 Production Trends</div>', unsafe_allow_html=True)
 
 
823
  col1, col2 = st.columns([3, 1])
 
824
  with col2:
825
+ time_view = st.selectbox("Time Period", ["daily", "weekly", "monthly"], key="time_view_select")
 
826
  with col1:
827
+ with st.container():
828
+ st.markdown('<div class="chart-container">', unsafe_allow_html=True)
829
+ total_chart = create_total_production_chart(df, time_view)
830
+ st.plotly_chart(total_chart, use_container_width=True)
831
+ st.markdown('</div>', unsafe_allow_html=True)
832
+ st.markdown('<div class="section-header">🏷️ Materials Analysis</div>', unsafe_allow_html=True)
 
833
  col1, col2 = st.columns([3, 1])
 
834
  with col2:
835
  selected_materials = st.multiselect(
836
  "Select Materials",
 
838
  default=materials,
839
  key="materials_select"
840
  )
 
841
  with col1:
842
  if selected_materials:
843
+ with st.container():
844
+ st.markdown('<div class="chart-container">', unsafe_allow_html=True)
845
+ materials_chart = create_materials_trend_chart(df, time_view, selected_materials)
846
+ st.plotly_chart(materials_chart, use_container_width=True)
847
+ st.markdown('</div>', unsafe_allow_html=True)
848
+ if 'shift' in df.columns:
849
+ st.markdown('<div class="section-header">🌓 Shift Analysis</div>', unsafe_allow_html=True)
850
+ with st.container():
851
  st.markdown('<div class="chart-container">', unsafe_allow_html=True)
852
+ shift_chart = create_shift_trend_chart(df, time_view)
853
+ st.plotly_chart(shift_chart, use_container_width=True)
854
  st.markdown('</div>', unsafe_allow_html=True)
855
+ st.markdown('<div class="section-header">⚠️ Quality Check</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
856
  outliers = detect_outliers(df)
857
  cols = st.columns(len(outliers))
 
858
  for i, (material, info) in enumerate(outliers.items()):
859
  with cols[i]:
860
+ if info['count'] > 0:
861
+ if len(info['dates']) <= 5:
862
+ dates_str = ", ".join(info['dates'])
863
+ else:
864
+ dates_str = f"{', '.join(info['dates'][:3])}, +{len(info['dates'])-3} more"
865
+ st.markdown(f'<div class="alert-warning"><strong>{material.title()}</strong><br>{info["count"]} outliers detected<br>Normal range: {info["range"]}<br><small>Dates: {dates_str}</small></div>', unsafe_allow_html=True)
 
866
  else:
867
+ st.markdown(f'<div class="alert-success"><strong>{material.title()}</strong><br>All values normal</div>', unsafe_allow_html=True)
868
+ add_export_section(df, stats, outliers, model)
 
 
869
  if model:
870
+ st.markdown('<div class="section-header">🤖 AI Insights</div>', unsafe_allow_html=True)
871
+ quick_questions = [
872
+ "How does production distribution on weekdays compare to weekends?",
873
+ "Which material exhibits the most volatility in our dataset?",
874
+ "To improve stability, which material or shift needs immediate attention?"
 
 
875
  ]
876
+ cols = st.columns(len(quick_questions))
877
+ for i, q in enumerate(quick_questions):
 
 
 
878
  with cols[i]:
879
+ if st.button(q, key=f"ai_q_{i}"):
880
+ with st.spinner("Analyzing..."):
881
+ answer = query_ai(model, stats, q, df)
882
+ st.info(answer)
883
+ custom_question = st.text_input("Ask about your production data:",
884
+ placeholder="e.g., 'Compare steel vs aluminum last month'",
885
+ key="custom_ai_question")
886
+ if custom_question and st.button("Ask AI", key="ask_ai_btn"):
887
+ with st.spinner("Analyzing..."):
888
+ answer = query_ai(model, stats, custom_question, df)
 
 
 
 
 
889
  st.success(f"**Q:** {custom_question}")
890
+ st.write(f"**A:** {answer}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
891
  else:
892
+ st.markdown('<div class="section-header">📖 How to Use This Platform</div>', unsafe_allow_html=True)
 
 
893
  col1, col2 = st.columns(2)
 
894
  with col1:
895
  st.markdown("""
896
+ ### 🚀 Quick Start
897
+ 1. Upload your TSV data in the sidebar
898
+ 2. Or click Quick Load buttons for preset data
899
+ 3. View production by material type
900
+ 4. Analyze trends (daily/weekly/monthly)
901
+ 5. Check anomalies in Quality Check
902
+ 6. Export reports (PDF with AI, CSV)
903
+ 7. Ask the AI assistant for insights
904
  """)
 
905
  with col2:
906
  st.markdown("""
907
+ ### 📊 Key Features
908
+ - Real-time interactive charts
909
+ - One-click preset data loading
910
+ - Time-period comparisons
911
+ - Shift performance analysis
912
+ - Outlier detection with dates
913
+ - AI-powered PDF reports
914
+ - Intelligent recommendations
915
  """)
916
+ st.info("📁 Ready to start? Upload your production data or use Quick Load buttons to begin analysis!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
 
918
  if __name__ == "__main__":
919
  main()