Update app.py
Browse files
app.py
CHANGED
@@ -3,220 +3,331 @@ import pandas as pd
|
|
3 |
import numpy as np
|
4 |
import plotly.express as px
|
5 |
from sklearn.ensemble import IsolationForest
|
6 |
-
import
|
7 |
-
from
|
8 |
-
import
|
9 |
-
import
|
10 |
-
import
|
11 |
-
import
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
> "Innovation distinguishes between a leader and a follower." β *Steve Jobs*
|
20 |
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
st.markdown("""
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
try:
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
except Exception as e:
|
58 |
-
st.error("Error
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
# Replace this dummy call with an actual Groq API call as needed.
|
80 |
-
df = run_local_anomaly_detection(df)
|
81 |
-
return df
|
82 |
-
|
83 |
-
def generate_plots(df):
|
84 |
-
# Generate 2D and 3D plots from the first numeric columns
|
85 |
-
numeric_cols = df.select_dtypes(include=[np.number]).columns
|
86 |
-
fig2d, fig3d = None, None
|
87 |
-
if len(numeric_cols) >= 2:
|
88 |
-
fig2d = px.scatter(df, x=numeric_cols[0], y=numeric_cols[1],
|
89 |
-
color='anomaly_flag',
|
90 |
-
title="π 2D Anomaly Detection Plot")
|
91 |
-
if len(numeric_cols) >= 3:
|
92 |
-
fig3d = px.scatter_3d(df, x=numeric_cols[0], y=numeric_cols[1], z=numeric_cols[2],
|
93 |
-
color='anomaly_flag',
|
94 |
-
title="π 3D Anomaly Detection Plot")
|
95 |
-
return fig2d, fig3d
|
96 |
-
|
97 |
-
def generate_pdf_report(summary_text, fig2d, fig3d):
|
98 |
-
pdf = FPDF()
|
99 |
-
pdf.add_page()
|
100 |
-
pdf.set_font("Arial", 'B', 16)
|
101 |
-
pdf.cell(0, 10, "WiFi Anomaly Detection Report", ln=True)
|
102 |
-
pdf.ln(10)
|
103 |
-
pdf.set_font("Arial", size=12)
|
104 |
-
pdf.multi_cell(0, 10, summary_text)
|
105 |
-
pdf.ln(10)
|
106 |
|
107 |
-
#
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
-
|
119 |
-
|
120 |
-
pdf.image(image, w=pdf.w - 40)
|
121 |
-
pdf.ln(10)
|
122 |
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
if
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
#
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
st.
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import numpy as np
|
4 |
import plotly.express as px
|
5 |
from sklearn.ensemble import IsolationForest
|
6 |
+
from io import BytesIO
|
7 |
+
from reportlab.lib.pagesizes import letter
|
8 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, PageBreak
|
9 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
10 |
+
from reportlab.lib.units import inch
|
11 |
+
from reportlab.lib import colors
|
12 |
+
import pdfplumber
|
13 |
+
import base64
|
14 |
+
import random
|
15 |
+
import plotly.io as pio
|
16 |
+
|
17 |
+
# Fix for Kaleido
|
18 |
+
pio.kaleido.scope.mathjax = None
|
|
|
19 |
|
20 |
+
# App Configuration
|
21 |
+
st.set_page_config(
|
22 |
+
page_title="WiFi Guardian π‘οΈ",
|
23 |
+
page_icon="πΆ",
|
24 |
+
layout="wide"
|
25 |
+
)
|
26 |
+
|
27 |
+
# Custom CSS for a polished interface
|
28 |
st.markdown("""
|
29 |
+
<style>
|
30 |
+
.st-emotion-cache-1kyxreq {
|
31 |
+
display: flex;
|
32 |
+
flex-flow: wrap;
|
33 |
+
gap: 2rem;
|
34 |
+
}
|
35 |
+
.reportview-container .main .block-container{
|
36 |
+
padding-top: 2rem;
|
37 |
+
}
|
38 |
+
.sidebar .sidebar-content {
|
39 |
+
background: linear-gradient(180deg, #2e3b4e, #1a2639);
|
40 |
+
}
|
41 |
+
.stButton>button {
|
42 |
+
width: 100%;
|
43 |
+
margin: 5px 0;
|
44 |
+
transition: all 0.3s;
|
45 |
+
}
|
46 |
+
.stButton>button:hover {
|
47 |
+
transform: scale(1.05);
|
48 |
+
}
|
49 |
+
.summary-box {
|
50 |
+
padding: 20px;
|
51 |
+
border-radius: 10px;
|
52 |
+
background-color: #2e3b4e;
|
53 |
+
margin: 10px 0;
|
54 |
+
}
|
55 |
+
</style>
|
56 |
+
""", unsafe_allow_html=True)
|
57 |
+
|
58 |
+
# Motivational Quotes
|
59 |
+
QUOTES = [
|
60 |
+
"π‘οΈ Cybersecurity is not a product, but a process!",
|
61 |
+
"π Better safe than hacked!",
|
62 |
+
"πΆ A secure network is a happy network!",
|
63 |
+
"π€ AI guards while you sleep!",
|
64 |
+
"π¨ Detect before you regret!",
|
65 |
+
"π» Security is always worth the investment!",
|
66 |
+
"π Stay vigilant, stay secure!"
|
67 |
+
]
|
68 |
+
|
69 |
+
def show_quote():
|
70 |
+
st.markdown(f"<h3 style='text-align: center; color: #4CAF50;'>{random.choice(QUOTES)}</h3>",
|
71 |
+
unsafe_allow_html=True)
|
72 |
+
|
73 |
+
# Main App Function
|
74 |
+
def main():
|
75 |
+
# Initialize session state variables
|
76 |
+
if 'current_step' not in st.session_state:
|
77 |
+
st.session_state.current_step = 1
|
78 |
+
if 'file_uploaded' not in st.session_state:
|
79 |
+
st.session_state.file_uploaded = False
|
80 |
+
if 'df' not in st.session_state:
|
81 |
+
st.session_state.df = None
|
82 |
+
|
83 |
+
# Sidebar Navigation
|
84 |
+
with st.sidebar:
|
85 |
+
st.title("π Navigation")
|
86 |
+
st.markdown("---")
|
87 |
+
|
88 |
+
if st.button("π€ 1. Upload File", help="Upload your network logs"):
|
89 |
+
st.session_state.current_step = 1
|
90 |
+
if st.button("π 2. Data Visualization", disabled=not st.session_state.file_uploaded):
|
91 |
+
st.session_state.current_step = 2
|
92 |
+
if st.button("π 3. Statistics Analysis", disabled=not st.session_state.file_uploaded):
|
93 |
+
st.session_state.current_step = 3
|
94 |
+
if st.button("π₯ 4. Download Report", disabled=not st.session_state.file_uploaded):
|
95 |
+
st.session_state.current_step = 4
|
96 |
+
|
97 |
+
# Main Content Area
|
98 |
+
if st.session_state.current_step == 1:
|
99 |
+
upload_file_section()
|
100 |
+
elif st.session_state.current_step == 2:
|
101 |
+
visualization_section()
|
102 |
+
elif st.session_state.current_step == 3:
|
103 |
+
statistics_section()
|
104 |
+
elif st.session_state.current_step == 4:
|
105 |
+
download_section()
|
106 |
+
|
107 |
+
def upload_file_section():
|
108 |
+
st.title("π€ Upload Network Logs")
|
109 |
+
st.markdown("---")
|
110 |
+
|
111 |
+
if not st.session_state.file_uploaded:
|
112 |
+
show_quote()
|
113 |
+
st.markdown("""
|
114 |
+
### Welcome to WiFi Guardian! π€
|
115 |
+
**Protect your network with AI-powered anomaly detection**
|
116 |
+
1. Upload network logs π€
|
117 |
+
2. Visualize patterns π
|
118 |
+
3. Generate reports π
|
119 |
+
""")
|
120 |
+
|
121 |
+
uploaded_file = st.file_uploader(
|
122 |
+
"Choose network logs (CSV/TXT/PDF)",
|
123 |
+
type=["csv", "txt", "pdf"],
|
124 |
+
label_visibility="collapsed"
|
125 |
+
)
|
126 |
+
|
127 |
+
if uploaded_file:
|
128 |
try:
|
129 |
+
process_file(uploaded_file)
|
130 |
+
st.session_state.file_uploaded = True
|
131 |
+
st.success("β
File processed successfully!")
|
132 |
+
|
133 |
+
# Show file summary
|
134 |
+
st.subheader("π Upload Summary")
|
135 |
+
col1, col2, col3 = st.columns(3)
|
136 |
+
with col1:
|
137 |
+
st.metric("Total Records", len(st.session_state.df))
|
138 |
+
with col2:
|
139 |
+
anomalies = sum(st.session_state.df['anomaly'] == -1)
|
140 |
+
st.metric("Anomalies Detected", f"{anomalies} ({anomalies/len(st.session_state.df)*100:.1f}%)")
|
141 |
+
with col3:
|
142 |
+
st.metric("Max Traffic", f"{st.session_state.df['traffic'].max():.2f} Mbps")
|
143 |
+
|
144 |
except Exception as e:
|
145 |
+
st.error(f"Error processing file: {str(e)}")
|
146 |
+
|
147 |
+
def visualization_section():
|
148 |
+
st.title("π Data Visualization")
|
149 |
+
st.markdown("---")
|
150 |
+
|
151 |
+
# 2D Visualization
|
152 |
+
st.subheader("2D Traffic Analysis π")
|
153 |
+
# Use 'timestamp' if available; if not, generate a dummy one
|
154 |
+
df = st.session_state.df.copy()
|
155 |
+
if 'timestamp' not in df.columns:
|
156 |
+
df['timestamp'] = pd.date_range(start="2021-01-01", periods=len(df), freq="T")
|
157 |
+
fig2d = px.scatter(
|
158 |
+
df,
|
159 |
+
x='timestamp',
|
160 |
+
y='traffic',
|
161 |
+
color='anomaly',
|
162 |
+
color_discrete_map={-1: 'orange', 1: 'blue'},
|
163 |
+
title="2D Traffic Analysis"
|
164 |
+
)
|
165 |
+
st.plotly_chart(fig2d, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
|
167 |
+
# 3D Visualization
|
168 |
+
st.subheader("3D Network Health π")
|
169 |
+
fig3d = px.scatter_3d(
|
170 |
+
df,
|
171 |
+
x='latency',
|
172 |
+
y='packet_loss',
|
173 |
+
z='traffic',
|
174 |
+
color='anomaly',
|
175 |
+
color_discrete_map={-1: 'orange', 1: 'blue'},
|
176 |
+
title="3D Network Analysis"
|
177 |
+
)
|
178 |
+
st.plotly_chart(fig3d, use_container_width=True)
|
179 |
+
|
180 |
+
def statistics_section():
|
181 |
+
st.title("π Statistical Analysis")
|
182 |
+
st.markdown("---")
|
183 |
|
184 |
+
st.subheader("Data Summary π")
|
185 |
+
st.dataframe(st.session_state.df.describe(), use_container_width=True)
|
|
|
|
|
186 |
|
187 |
+
st.subheader("Anomaly Distribution π")
|
188 |
+
anomaly_counts = st.session_state.df['anomaly'].value_counts()
|
189 |
+
fig = px.pie(
|
190 |
+
names=['Normal', 'Anomaly'],
|
191 |
+
values=[anomaly_counts.get(1, 0), anomaly_counts.get(-1, 0)],
|
192 |
+
hole=0.4,
|
193 |
+
color_discrete_sequence=['blue', 'orange'],
|
194 |
+
title="Anomaly Distribution"
|
195 |
+
)
|
196 |
+
st.plotly_chart(fig, use_container_width=True)
|
197 |
+
|
198 |
+
def download_section():
|
199 |
+
st.title("π₯ Download Report")
|
200 |
+
st.markdown("---")
|
201 |
|
202 |
+
if st.button("π¨οΈ Generate Full Report"):
|
203 |
+
with st.spinner("Generating PDF report..."):
|
204 |
+
generate_pdf_report()
|
205 |
+
st.success("Report generated successfully!")
|
206 |
+
|
207 |
+
if 'pdf_report' in st.session_state:
|
208 |
+
st.markdown("---")
|
209 |
+
b64 = base64.b64encode(st.session_state.pdf_report).decode()
|
210 |
+
href = f'<a href="data:application/octet-stream;base64,{b64}" download="wifi_report.pdf">π₯ Download Full Report</a>'
|
211 |
+
st.markdown(href, unsafe_allow_html=True)
|
212 |
+
|
213 |
+
def process_file(uploaded_file):
|
214 |
+
try:
|
215 |
+
# Process CSV files
|
216 |
+
if uploaded_file.name.endswith('.csv'):
|
217 |
+
df = pd.read_csv(uploaded_file)
|
218 |
+
# Process TXT files
|
219 |
+
elif uploaded_file.name.endswith('.txt'):
|
220 |
+
lines = [line.decode().strip().split(',') for line in uploaded_file.readlines()]
|
221 |
+
df = pd.DataFrame(lines[1:], columns=lines[0])
|
222 |
+
# Process PDF files using pdfplumber
|
223 |
+
elif uploaded_file.name.endswith('.pdf'):
|
224 |
+
with pdfplumber.open(uploaded_file) as pdf:
|
225 |
+
text = '\n'.join([page.extract_text() for page in pdf.pages])
|
226 |
+
lines = [line.split(',') for line in text.split('\n') if line]
|
227 |
+
df = pd.DataFrame(lines[1:], columns=lines[0])
|
228 |
+
else:
|
229 |
+
raise ValueError("Unsupported file type.")
|
230 |
+
|
231 |
+
# Ensure required numeric columns exist and convert them
|
232 |
+
numeric_cols = ['traffic', 'latency', 'packet_loss']
|
233 |
+
for col in numeric_cols:
|
234 |
+
if col not in df.columns:
|
235 |
+
raise ValueError(f"Column '{col}' not found in data.")
|
236 |
+
df[col] = pd.to_numeric(df[col], errors='coerce')
|
237 |
+
|
238 |
+
# Run anomaly detection using IsolationForest with 40% contamination
|
239 |
+
clf = IsolationForest(contamination=0.4, random_state=42)
|
240 |
+
df['anomaly'] = clf.fit_predict(df[numeric_cols])
|
241 |
+
|
242 |
+
st.session_state.df = df
|
243 |
+
|
244 |
+
except Exception as e:
|
245 |
+
st.error(f"Error processing file: {str(e)}")
|
246 |
+
raise
|
247 |
+
|
248 |
+
def generate_pdf_report():
|
249 |
+
try:
|
250 |
+
buffer = BytesIO()
|
251 |
+
doc = SimpleDocTemplate(buffer, pagesize=letter)
|
252 |
+
styles = getSampleStyleSheet()
|
253 |
+
elements = []
|
254 |
+
|
255 |
+
# Custom Title Style
|
256 |
+
title_style = ParagraphStyle(
|
257 |
+
name='Title',
|
258 |
+
parent=styles['Heading1'],
|
259 |
+
fontSize=18,
|
260 |
+
textColor=colors.darkblue,
|
261 |
+
spaceAfter=14
|
262 |
+
)
|
263 |
+
|
264 |
+
# Add Title
|
265 |
+
elements.append(Paragraph("WiFi Network Anomaly Detection", title_style))
|
266 |
+
elements.append(Spacer(1, 12))
|
267 |
+
|
268 |
+
# Add Summary Section
|
269 |
+
elements.append(Paragraph("<b>Detection Summary:</b>", styles['Heading2']))
|
270 |
+
summary_text = f"""
|
271 |
+
β’ Total Data Points: {len(st.session_state.df)}<br/>
|
272 |
+
β’ Anomalies Detected: {sum(st.session_state.df['anomaly'] == -1)}<br/>
|
273 |
+
β’ Maximum Traffic: {st.session_state.df['traffic'].max():.2f} Mbps<br/>
|
274 |
+
β’ Average Latency: {st.session_state.df['latency'].mean():.2f} ms<br/>
|
275 |
+
β’ Peak Packet Loss: {st.session_state.df['packet_loss'].max():.2f}%<br/>
|
276 |
+
"""
|
277 |
+
elements.append(Paragraph(summary_text, styles['BodyText']))
|
278 |
+
elements.append(PageBreak())
|
279 |
+
|
280 |
+
# Generate and embed plots in memory using BytesIO
|
281 |
+
|
282 |
+
# 2D Plot
|
283 |
+
df = st.session_state.df.copy()
|
284 |
+
if 'timestamp' not in df.columns:
|
285 |
+
df['timestamp'] = pd.date_range(start="2021-01-01", periods=len(df), freq="T")
|
286 |
+
fig2d = px.scatter(df, x='timestamp', y='traffic',
|
287 |
+
color='anomaly', title="2D Traffic Analysis",
|
288 |
+
color_discrete_map={-1: 'orange', 1: 'blue'})
|
289 |
+
img_bytes_2d = fig2d.to_image(format="png", engine="kaleido")
|
290 |
+
img2d_io = BytesIO(img_bytes_2d)
|
291 |
+
|
292 |
+
# 3D Plot
|
293 |
+
fig3d = px.scatter_3d(df, x='latency', y='packet_loss',
|
294 |
+
z='traffic', color='anomaly', title="3D Network Analysis",
|
295 |
+
color_discrete_map={-1: 'orange', 1: 'blue'})
|
296 |
+
img_bytes_3d = fig3d.to_image(format="png", engine="kaleido")
|
297 |
+
img3d_io = BytesIO(img_bytes_3d)
|
298 |
+
|
299 |
+
# Add 2D Plot
|
300 |
+
elements.append(Paragraph("<b>2D Traffic Analysis</b>", styles['Heading2']))
|
301 |
+
elements.append(Image(img2d_io, width=6*inch, height=4*inch))
|
302 |
+
elements.append(Spacer(1, 12))
|
303 |
+
|
304 |
+
# Add 3D Plot
|
305 |
+
elements.append(Paragraph("<b>3D Network Analysis</b>", styles['Heading2']))
|
306 |
+
elements.append(Image(img3d_io, width=6*inch, height=4*inch))
|
307 |
+
elements.append(PageBreak())
|
308 |
+
|
309 |
+
# Add Statistics Section
|
310 |
+
elements.append(Paragraph("<b>Statistical Report</b>", styles['Heading1']))
|
311 |
+
stats = st.session_state.df.describe()
|
312 |
+
for col in ['traffic', 'latency', 'packet_loss']:
|
313 |
+
elements.append(Paragraph(f"<b>{col.capitalize()} Statistics:</b>", styles['Heading3']))
|
314 |
+
stats_text = f"""
|
315 |
+
β’ Mean: {stats[col]['mean']:.2f}<br/>
|
316 |
+
β’ Std Dev: {stats[col]['std']:.2f}<br/>
|
317 |
+
β’ Min: {stats[col]['min']:.2f}<br/>
|
318 |
+
β’ 25%: {stats[col]['25%']:.2f}<br/>
|
319 |
+
β’ 50%: {stats[col]['50%']:.2f}<br/>
|
320 |
+
β’ 75%: {stats[col]['75%']:.2f}<br/>
|
321 |
+
β’ Max: {stats[col]['max']:.2f}<br/>
|
322 |
+
"""
|
323 |
+
elements.append(Paragraph(stats_text, styles['BodyText']))
|
324 |
+
elements.append(Spacer(1, 12))
|
325 |
+
|
326 |
+
doc.build(elements)
|
327 |
+
st.session_state.pdf_report = buffer.getvalue()
|
328 |
+
|
329 |
+
except Exception as e:
|
330 |
+
st.error(f"Error generating report: {str(e)}")
|
331 |
+
|
332 |
+
if __name__ == "__main__":
|
333 |
+
main()
|