Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import plotly.express as px
|
4 |
+
import numpy as np
|
5 |
+
import time
|
6 |
+
|
7 |
+
# Simulated data (you can replace this with data from salesforce_integration.py or simulator.py)
|
8 |
+
def generate_mock_data(n=100):
|
9 |
+
np.random.seed(42)
|
10 |
+
data = {
|
11 |
+
"PoleID": [f"Pole_{i:05}" for i in range(n)],
|
12 |
+
"Site": np.random.choice(["Site 1", "Site 2", "Site 3", "Site 4"], n),
|
13 |
+
"SolarGen(kWh)": np.random.uniform(2.0, 6.0, n),
|
14 |
+
"WindGen(kWh)": np.random.uniform(0.5, 2.0, n),
|
15 |
+
"Tilt(°)": np.random.uniform(0, 15, n),
|
16 |
+
"Vibration(g)": np.random.uniform(0, 3, n),
|
17 |
+
"CameraStatus": np.random.choice(["Online", "Offline"], n),
|
18 |
+
"PowerSufficient": np.random.choice(["Yes", "No"], n),
|
19 |
+
}
|
20 |
+
df = pd.DataFrame(data)
|
21 |
+
|
22 |
+
# Rule-based alert level
|
23 |
+
df["Anomalies"] = df.apply(lambda row: [
|
24 |
+
"LowSolarOutput" if row["SolarGen(kWh)"] < 4.0 else "",
|
25 |
+
"LowWindOutput" if row["WindGen(kWh)"] < 0.7 else "",
|
26 |
+
"PoleTiltRisk" if row["Tilt(°)"] > 10 else "",
|
27 |
+
"VibrationAlert" if row["Vibration(g)"] > 2.0 else "",
|
28 |
+
"CameraOffline" if row["CameraStatus"] == "Offline" else "",
|
29 |
+
"PowerInsufficient" if row["PowerSufficient"] == "No" else "",
|
30 |
+
], axis=1)
|
31 |
+
|
32 |
+
df["Anomalies"] = df["Anomalies"].apply(lambda x: [a for a in x if a])
|
33 |
+
df["AlertLevel"] = df["Anomalies"].apply(lambda x: "Green" if len(x) == 0 else "Yellow" if len(x) == 1 else "Red")
|
34 |
+
return df
|
35 |
+
|
36 |
+
# Visuals
|
37 |
+
def show_heatmap(df):
|
38 |
+
st.subheader("🌡️ Fault Distribution Heatmap")
|
39 |
+
|
40 |
+
map_data = df.groupby(['Site', 'AlertLevel']).size().reset_index(name="Count")
|
41 |
+
fig = px.density_heatmap(
|
42 |
+
map_data, x="Site", y="AlertLevel", z="Count", color_continuous_scale="Reds", title="Alerts per Site"
|
43 |
+
)
|
44 |
+
st.plotly_chart(fig, use_container_width=True)
|
45 |
+
|
46 |
+
def show_red_alerts(df):
|
47 |
+
st.subheader("🚨 Blinking Red Alert Poles")
|
48 |
+
red_df = df[df["AlertLevel"] == "Red"]
|
49 |
+
if red_df.empty:
|
50 |
+
st.success("No red alerts right now!")
|
51 |
+
return
|
52 |
+
|
53 |
+
for _, row in red_df.iterrows():
|
54 |
+
with st.container():
|
55 |
+
st.markdown(
|
56 |
+
f"<div style='padding:8px; background-color:#ffcccc; animation: blink 1s infinite;'>"
|
57 |
+
f"<strong>{row['PoleID']}</strong>: {', '.join(row['Anomalies'])}</div>",
|
58 |
+
unsafe_allow_html=True
|
59 |
+
)
|
60 |
+
|
61 |
+
st.markdown(
|
62 |
+
"""
|
63 |
+
<style>
|
64 |
+
@keyframes blink {
|
65 |
+
50% { background-color: #ff4d4d; }
|
66 |
+
}
|
67 |
+
</style>
|
68 |
+
""",
|
69 |
+
unsafe_allow_html=True
|
70 |
+
)
|
71 |
+
|
72 |
+
# Main
|
73 |
+
st.set_page_config("VIEP Heatmap Dashboard", layout="wide")
|
74 |
+
st.title("🌍 Vedavathi Smart Pole Monitoring Dashboard")
|
75 |
+
|
76 |
+
df = generate_mock_data()
|
77 |
+
|
78 |
+
# Filters
|
79 |
+
alert_filter = st.selectbox("Filter by Alert Level", ["All", "Green", "Yellow", "Red"])
|
80 |
+
if alert_filter != "All":
|
81 |
+
df = df[df["AlertLevel"] == alert_filter]
|
82 |
+
|
83 |
+
# Views
|
84 |
+
show_heatmap(df)
|
85 |
+
show_red_alerts(df)
|