SHELLAPANDIANGANHUNGING commited on
Commit
c1edd4a
·
verified ·
1 Parent(s): 6b58028

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +264 -113
app.py CHANGED
@@ -1451,128 +1451,279 @@ else:
1451
  </div>
1452
  """, unsafe_allow_html=True)
1453
 
1454
- # ===============================================================
1455
- # PLOT FUNCTION — UPDATED: color for slope=0 is now #FFD700
1456
- # ===============================================================
1457
- def plot_chart(data, title):
1458
- if data.empty:
1459
- fig = go.Figure()
1460
- fig.add_annotation(
1461
- text="No Data",
1462
- x=0.5, y=0.5,
1463
- showarrow=False,
1464
- font_size=16
1465
- )
1466
- fig.update_layout(height=350, title=dict(text=title, x=0.5))
1467
- return fig
1468
-
1469
- data_sorted = data.sort_values('weekly_avg', ascending=False)
1470
-
1471
- def get_color(slope):
1472
- if slope == 0:
1473
- return "#FFD700" # ✅ Kuning untuk One Time Event
1474
- elif slope > 0:
1475
- if slope < 0.5:
1476
- return "#ffcdd2"
1477
- elif slope < 1.0:
1478
- return "#ef9a9a"
1479
- elif slope < 1.5:
1480
- return "#e57373"
1481
- else:
1482
- return "#d32f2f"
1483
- else: # slope < 0
1484
- if slope > -0.5:
1485
- return "#c8e6c9"
1486
- elif slope > -1.0:
1487
- return "#a5d6a7"
1488
- elif slope > -1.5:
1489
- return "#81c784"
1490
- else:
1491
- return "#388e3c"
1492
-
1493
- colors = [get_color(s) for s in data_sorted["slope"]]
1494
-
1495
- bar_trace = go.Bar(
1496
- x=data_sorted[col_operator].astype(str),
1497
- y=data_sorted["weekly_avg"],
1498
- marker=dict(
1499
- color=colors,
1500
- line=dict(width=2, color="rgba(0,0,0,0.2)")
1501
- ),
1502
- text=[f"{v:.1f}" for v in data_sorted["weekly_avg"]],
1503
- textposition="outside",
1504
- hovertemplate=(
1505
- "<b>%{x}</b><br>" +
1506
- "Weekly Avg: %{y:.2f}<br>" +
1507
- "Trend Slope: %{customdata[0]:+.3f}<br>" +
1508
- "Total Events: %{customdata[1]}<br>" +
1509
- "Weeks Active: %{customdata[2]}<br>" +
1510
- "<extra></extra>"
1511
- ),
1512
- customdata=np.stack([data_sorted["slope"], data_sorted["total_events"], data_sorted["n_weeks"]], axis=-1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1513
  )
 
 
1514
 
1515
- fig = go.Figure(bar_trace)
1516
- fig.update_layout(
1517
- title=dict(text=f"<b>{title}</b>", x=0.5),
1518
- height=450,
1519
- margin=dict(l=50, r=20, t=60, b=120),
1520
- xaxis_title="<b>Operator Name</b>",
1521
- yaxis_title="<b>Weekly Avg Events</b>",
1522
- font=dict(family="Segoe UI", size=12),
1523
- bargap=0.3,
1524
- plot_bgcolor="rgba(0,0,0,0)",
1525
- paper_bgcolor="rgba(0,0,0,0)",
1526
- xaxis=dict(tickangle=45)
 
 
 
 
 
 
 
 
 
 
 
1527
  )
1528
- return fig
1529
- col_insight1, col_insight2 = st.columns(2)
1530
- with col_insight1:
1531
- if not top_ob.empty:
1532
- st.markdown("### OB HAULER Hazard Analysis")
1533
- ob_worsening = len(top_ob[top_ob['slope'] > 0])
1534
- ob_improving = len(top_ob[top_ob['slope'] < 0])
1535
- ob_one_time = len(top_ob[top_ob['slope'] == 0])
1536
- ob_avg_risk = top_ob['weekly_avg'].mean()
1537
- ob_max_risk = top_ob['weekly_avg'].max()
1538
-
1539
- # Buat list 3 poin yang selalu ada, tanpa kondisional untuk jumlah poin
1540
- ob_insights = [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1541
  f"{ob_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1542
  f"{ob_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1543
  f"Average hazard: {ob_avg_risk:.2f} events/week (max: {ob_max_risk:.2f})."
1544
  ]
1545
- for insight in ob_insights:
1546
- st.markdown(f"""
1547
- <div class="ai-insight-box">
1548
- <div class="ai-insight-title">Hazard Summary</div>
1549
- <p>{insight}</p>
1550
- </div>
1551
- """, unsafe_allow_html=True)
 
 
 
1552
  else:
1553
  st.info("No OB HAULER data for analysis.")
1554
- with col_insight2:
1555
- if not top_coal.empty:
1556
- st.markdown("### HAULING COAL Hazard Analysis")
1557
- coal_worsening = len(top_coal[top_coal['slope'] > 0])
1558
- coal_improving = len(top_coal[top_coal['slope'] < 0])
1559
- coal_one_time = len(top_coal[top_coal['slope'] == 0])
1560
- coal_avg_risk = top_coal['weekly_avg'].mean()
1561
- coal_max_risk = top_coal['weekly_avg'].max()
1562
- coal_insights = [f"{coal_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1563
- f"{coal_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1564
- f"Average hazard: {coal_avg_risk:.2f} events/week (max: {coal_max_risk:.2f})."]
1565
- for insight in coal_insights:
1566
- st.markdown(f"""
1567
- <div class="ai-insight-box">
1568
- <div class="ai-insight-title">Hazard Summary</div>
1569
- <p>{insight}</p>
1570
- </div>
1571
- """, unsafe_allow_html=True)
1572
- else:
1573
- st.info("No HAULING COAL data for analysis.")
1574
- # ===============================================================
1575
- # RECOMMENDATIONS
 
 
 
 
 
1576
  # ===============================================================
1577
  col_rec1, col_rec2 = st.columns(2)
1578
 
 
1451
  </div>
1452
  """, unsafe_allow_html=True)
1453
 
1454
+ # ===============================================================
1455
+ # PLOT FUNCTION — UPDATED: color for slope=0 is now #FFD700
1456
+ # ===============================================================
1457
+ def plot_chart(data, title):
1458
+ if data.empty:
1459
+ fig = go.Figure()
1460
+ fig.add_annotation(
1461
+ text="No Data",
1462
+ x=0.5, y=0.5,
1463
+ showarrow=False,
1464
+ font_size=16
1465
+ )
1466
+ fig.update_layout(height=350, title=dict(text=title, x=0.5))
1467
+ return fig
1468
+
1469
+ data_sorted = data.sort_values('weekly_avg', ascending=False)
1470
+
1471
+ def get_color(slope):
1472
+ if slope == 0:
1473
+ return "#FFD700" # ✅ Kuning untuk One Time Event
1474
+ elif slope > 0:
1475
+ if slope < 0.5:
1476
+ return "#ffcdd2"
1477
+ elif slope < 1.0:
1478
+ return "#ef9a9a"
1479
+ elif slope < 1.5:
1480
+ return "#e57373"
1481
+ else:
1482
+ return "#d32f2f"
1483
+ else: # slope < 0
1484
+ if slope > -0.5:
1485
+ return "#c8e6c9"
1486
+ elif slope > -1.0:
1487
+ return "#a5d6a7"
1488
+ elif slope > -1.5:
1489
+ return "#81c784"
1490
+ else:
1491
+ return "#388e3c"
1492
+
1493
+ colors = [get_color(s) for s in data_sorted["slope"]]
1494
+
1495
+ bar_trace = go.Bar(
1496
+ x=data_sorted[col_operator].astype(str),
1497
+ y=data_sorted["weekly_avg"],
1498
+ marker=dict(
1499
+ color=colors,
1500
+ line=dict(width=2, color="rgba(0,0,0,0.2)")
1501
+ ),
1502
+ text=[f"{v:.1f}" for v in data_sorted["weekly_avg"]],
1503
+ textposition="outside",
1504
+ hovertemplate=(
1505
+ "<b>%{x}</b><br>" +
1506
+ "Weekly Avg: %{y:.2f}<br>" +
1507
+ "Trend Slope: %{customdata[0]:+.3f}<br>" +
1508
+ "Total Events: %{customdata[1]}<br>" +
1509
+ "Weeks Active: %{customdata[2]}<br>" +
1510
+ "<extra></extra>"
1511
+ ),
1512
+ customdata=np.stack([data_sorted["slope"], data_sorted["total_events"], data_sorted["n_weeks"]], axis=-1)
1513
+ )
1514
+
1515
+ fig = go.Figure(bar_trace)
1516
+ fig.update_layout(
1517
+ title=dict(text=f"<b>{title}</b>", x=0.5),
1518
+ height=450,
1519
+ margin=dict(l=50, r=20, t=60, b=120),
1520
+ xaxis_title="<b>Operator Name</b>",
1521
+ yaxis_title="<b>Weekly Avg Events</b>",
1522
+ font=dict(family="Segoe UI", size=12),
1523
+ bargap=0.3,
1524
+ plot_bgcolor="rgba(0,0,0,0)",
1525
+ paper_bgcolor="rgba(0,0,0,0)",
1526
+ xaxis=dict(tickangle=45)
1527
+ )
1528
+ return fig
1529
+
1530
+
1531
+ # ==========================
1532
+ # INSIGHT DISPLAY SECTION
1533
+ # ==========================
1534
+ col_insight1, col_insight2 = st.columns(2)
1535
+
1536
+ with col_insight1:
1537
+ if not top_ob.empty:
1538
+ st.markdown("### OB HAULER Hazard Analysis")
1539
+ ob_worsening = len(top_ob[top_ob['slope'] > 0])
1540
+ ob_improving = len(top_ob[top_ob['slope'] < 0])
1541
+ ob_one_time = len(top_ob[top_ob['slope'] == 0])
1542
+ ob_avg_risk = top_ob['weekly_avg'].mean()
1543
+ ob_max_risk = top_ob['weekly_avg'].max()
1544
+
1545
+ # Always show 3 insights
1546
+ ob_insights = [
1547
+ f"{ob_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1548
+ f"{ob_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1549
+ f"Average hazard: {ob_avg_risk:.2f} events/week (max: {ob_max_risk:.2f})."
1550
+ ]
1551
+ for insight in ob_insights:
1552
+ st.markdown(
1553
+ f"""
1554
+ <div class="ai-insight-box">
1555
+ <div class="ai-insight-title">Hazard Summary</div>
1556
+ <p>{insight}</p>
1557
+ </div>
1558
+ """,
1559
+ unsafe_allow_html=True
1560
  )
1561
+ else:
1562
+ st.info("No OB HAULER data for analysis.")
1563
 
1564
+ with col_insight2:
1565
+ if not top_coal.empty:
1566
+ st.markdown("### HAULING COAL Hazard Analysis")
1567
+ coal_worsening = len(top_coal[top_coal['slope'] > 0])
1568
+ coal_improving = len(top_coal[top_coal['slope'] < 0])
1569
+ coal_one_time = len(top_coal[top_coal['slope'] == 0])
1570
+ coal_avg_risk = top_coal['weekly_avg'].mean()
1571
+ coal_max_risk = top_coal['weekly_avg'].max()
1572
+
1573
+ coal_insights = [
1574
+ f"{coal_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1575
+ f"{coal_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1576
+ f"Average hazard: {coal_avg_risk:.2f} events/week (max: {coal_max_risk:.2f})."
1577
+ ]
1578
+ for insight in coal_insights:
1579
+ st.markdown(
1580
+ f"""
1581
+ <div class="ai-insight-box">
1582
+ <div class="ai-insight-title">Hazard Summary</div>
1583
+ <p>{insight}</p>
1584
+ </div>
1585
+ """,
1586
+ unsafe_allow_html=True
1587
  )
1588
+ else:
1589
+ st.info("No HAULING COAL data for analysis.")
1590
+ # ===============================================================
1591
+ # RECOMMENDATIONS# ===============================================================
1592
+ # PLOT FUNCTION UPDATED: color for slope=0 is now #FFD700
1593
+ # ===============================================================
1594
+ def plot_chart(data, title):
1595
+ if data.empty:
1596
+ fig = go.Figure()
1597
+ fig.add_annotation(
1598
+ text="No Data",
1599
+ x=0.5, y=0.5,
1600
+ showarrow=False,
1601
+ font_size=16
1602
+ )
1603
+ fig.update_layout(height=350, title=dict(text=title, x=0.5))
1604
+ return fig
1605
+
1606
+ data_sorted = data.sort_values('weekly_avg', ascending=False)
1607
+
1608
+ def get_color(slope):
1609
+ if slope == 0:
1610
+ return "#FFD700" # ✅ Kuning untuk One Time Event
1611
+ elif slope > 0:
1612
+ if slope < 0.5:
1613
+ return "#ffcdd2"
1614
+ elif slope < 1.0:
1615
+ return "#ef9a9a"
1616
+ elif slope < 1.5:
1617
+ return "#e57373"
1618
+ else:
1619
+ return "#d32f2f"
1620
+ else: # slope < 0
1621
+ if slope > -0.5:
1622
+ return "#c8e6c9"
1623
+ elif slope > -1.0:
1624
+ return "#a5d6a7"
1625
+ elif slope > -1.5:
1626
+ return "#81c784"
1627
+ else:
1628
+ return "#388e3c"
1629
+
1630
+ colors = [get_color(s) for s in data_sorted["slope"]]
1631
+
1632
+ bar_trace = go.Bar(
1633
+ x=data_sorted[col_operator].astype(str),
1634
+ y=data_sorted["weekly_avg"],
1635
+ marker=dict(
1636
+ color=colors,
1637
+ line=dict(width=2, color="rgba(0,0,0,0.2)")
1638
+ ),
1639
+ text=[f"{v:.1f}" for v in data_sorted["weekly_avg"]],
1640
+ textposition="outside",
1641
+ hovertemplate=(
1642
+ "<b>%{x}</b><br>" +
1643
+ "Weekly Avg: %{y:.2f}<br>" +
1644
+ "Trend Slope: %{customdata[0]:+.3f}<br>" +
1645
+ "Total Events: %{customdata[1]}<br>" +
1646
+ "Weeks Active: %{customdata[2]}<br>" +
1647
+ "<extra></extra>"
1648
+ ),
1649
+ customdata=np.stack([data_sorted["slope"], data_sorted["total_events"], data_sorted["n_weeks"]], axis=-1)
1650
+ )
1651
+
1652
+ fig = go.Figure(bar_trace)
1653
+ fig.update_layout(
1654
+ title=dict(text=f"<b>{title}</b>", x=0.5),
1655
+ height=450,
1656
+ margin=dict(l=50, r=20, t=60, b=120),
1657
+ xaxis_title="<b>Operator Name</b>",
1658
+ yaxis_title="<b>Weekly Avg Events</b>",
1659
+ font=dict(family="Segoe UI", size=12),
1660
+ bargap=0.3,
1661
+ plot_bgcolor="rgba(0,0,0,0)",
1662
+ paper_bgcolor="rgba(0,0,0,0)",
1663
+ xaxis=dict(tickangle=45)
1664
+ )
1665
+ return fig
1666
+
1667
+
1668
+ # ==========================
1669
+ # INSIGHT DISPLAY SECTION
1670
+ # ==========================
1671
+ col_insight1, col_insight2 = st.columns(2)
1672
+
1673
+ with col_insight1:
1674
+ if not top_ob.empty:
1675
+ st.markdown("### OB HAULER Hazard Analysis")
1676
+ ob_worsening = len(top_ob[top_ob['slope'] > 0])
1677
+ ob_improving = len(top_ob[top_ob['slope'] < 0])
1678
+ ob_one_time = len(top_ob[top_ob['slope'] == 0])
1679
+ ob_avg_risk = top_ob['weekly_avg'].mean()
1680
+ ob_max_risk = top_ob['weekly_avg'].max()
1681
+
1682
+ # Always show 3 insights
1683
+ ob_insights = [
1684
  f"{ob_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1685
  f"{ob_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1686
  f"Average hazard: {ob_avg_risk:.2f} events/week (max: {ob_max_risk:.2f})."
1687
  ]
1688
+ for insight in ob_insights:
1689
+ st.markdown(
1690
+ f"""
1691
+ <div class="ai-insight-box">
1692
+ <div class="ai-insight-title">Hazard Summary</div>
1693
+ <p>{insight}</p>
1694
+ </div>
1695
+ """,
1696
+ unsafe_allow_html=True
1697
+ )
1698
  else:
1699
  st.info("No OB HAULER data for analysis.")
1700
+
1701
+ with col_insight2:
1702
+ if not top_coal.empty:
1703
+ st.markdown("### HAULING COAL Hazard Analysis")
1704
+ coal_worsening = len(top_coal[top_coal['slope'] > 0])
1705
+ coal_improving = len(top_coal[top_coal['slope'] < 0])
1706
+ coal_one_time = len(top_coal[top_coal['slope'] == 0])
1707
+ coal_avg_risk = top_coal['weekly_avg'].mean()
1708
+ coal_max_risk = top_coal['weekly_avg'].max()
1709
+
1710
+ coal_insights = [
1711
+ f"{coal_worsening} out of 10 top hazard operators are showing <span class='trend-up'>worsening</span> trends.",
1712
+ f"{coal_one_time} operators are classified as <b>One Time Event</b> (single-week activity).",
1713
+ f"Average hazard: {coal_avg_risk:.2f} events/week (max: {coal_max_risk:.2f})."
1714
+ ]
1715
+ for insight in coal_insights:
1716
+ st.markdown(
1717
+ f"""
1718
+ <div class="ai-insight-box">
1719
+ <div class="ai-insight-title">Hazard Summary</div>
1720
+ <p>{insight}</p>
1721
+ </div>
1722
+ """,
1723
+ unsafe_allow_html=True
1724
+ )
1725
+ else:
1726
+ st.info("No HAULING COAL data for analysis.")
1727
  # ===============================================================
1728
  col_rec1, col_rec2 = st.columns(2)
1729