Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -11,6 +11,18 @@ import json
|
|
11 |
import os
|
12 |
import sqlite3
|
13 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
class DataAnalysisChatbot:
|
16 |
def __init__(self):
|
@@ -1275,6 +1287,291 @@ class DataAnalysisChatbot:
|
|
1275 |
help_text += " correlate temperature humidity pressure\n"
|
1276 |
help_text += " visualize scatter temperature humidity\n"
|
1277 |
help_text += " trend sales date\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1278 |
|
1279 |
return help_text
|
1280 |
|
|
|
11 |
import os
|
12 |
import sqlite3
|
13 |
from datetime import datetime
|
14 |
+
import streamlit as st
|
15 |
+
import pandas as pd
|
16 |
+
import matplotlib.pyplot as plt
|
17 |
+
import seaborn as sns
|
18 |
+
import os
|
19 |
+
import io
|
20 |
+
from datetime import datetime
|
21 |
+
import base64
|
22 |
+
from PIL import Image
|
23 |
+
|
24 |
+
# Import the DataAnalysisChatbot class
|
25 |
+
from paste import DataAnalysisChatbot
|
26 |
|
27 |
class DataAnalysisChatbot:
|
28 |
def __init__(self):
|
|
|
1287 |
help_text += " correlate temperature humidity pressure\n"
|
1288 |
help_text += " visualize scatter temperature humidity\n"
|
1289 |
help_text += " trend sales date\n"
|
1290 |
+
|
1291 |
+
|
1292 |
+
# Page configuration
|
1293 |
+
st.set_page_config(
|
1294 |
+
page_title="Data Analysis Assistant",
|
1295 |
+
page_icon="π",
|
1296 |
+
layout="wide",
|
1297 |
+
initial_sidebar_state="expanded"
|
1298 |
+
)
|
1299 |
+
|
1300 |
+
# Initialize session state variables if they don't exist
|
1301 |
+
if 'chatbot' not in st.session_state:
|
1302 |
+
st.session_state.chatbot = DataAnalysisChatbot()
|
1303 |
+
if 'conversation' not in st.session_state:
|
1304 |
+
st.session_state.conversation = []
|
1305 |
+
if 'data_loaded' not in st.session_state:
|
1306 |
+
st.session_state.data_loaded = False
|
1307 |
+
if 'current_file' not in st.session_state:
|
1308 |
+
st.session_state.current_file = None
|
1309 |
+
if 'data_preview' not in st.session_state:
|
1310 |
+
st.session_state.data_preview = None
|
1311 |
+
|
1312 |
+
# Function to get a download link for a file
|
1313 |
+
def get_download_link(file_path, link_text):
|
1314 |
+
with open(file_path, 'rb') as f:
|
1315 |
+
data = f.read()
|
1316 |
+
b64 = base64.b64encode(data).decode()
|
1317 |
+
href = f'<a href="data:file/txt;base64,{b64}" download="{os.path.basename(file_path)}">{link_text}</a>'
|
1318 |
+
return href
|
1319 |
+
|
1320 |
+
# Function to convert matplotlib figure to Streamlit-compatible format
|
1321 |
+
def plt_to_streamlit():
|
1322 |
+
buf = io.BytesIO()
|
1323 |
+
plt.savefig(buf, format='png')
|
1324 |
+
buf.seek(0)
|
1325 |
+
return buf
|
1326 |
+
|
1327 |
+
# Custom CSS
|
1328 |
+
st.markdown("""
|
1329 |
+
<style>
|
1330 |
+
.main-header {
|
1331 |
+
font-size: 2.5rem;
|
1332 |
+
font-weight: 700;
|
1333 |
+
color: #1E88E5;
|
1334 |
+
margin-bottom: 1rem;
|
1335 |
+
}
|
1336 |
+
.sub-header {
|
1337 |
+
font-size: 1.5rem;
|
1338 |
+
font-weight: 600;
|
1339 |
+
color: #333;
|
1340 |
+
margin-bottom: 1rem;
|
1341 |
+
}
|
1342 |
+
.chat-user {
|
1343 |
+
background-color: #E3F2FD;
|
1344 |
+
padding: 10px 15px;
|
1345 |
+
border-radius: 15px;
|
1346 |
+
margin-bottom: 10px;
|
1347 |
+
font-size: 1rem;
|
1348 |
+
}
|
1349 |
+
.chat-bot {
|
1350 |
+
background-color: #F5F5F5;
|
1351 |
+
padding: 10px 15px;
|
1352 |
+
border-radius: 15px;
|
1353 |
+
margin-bottom: 10px;
|
1354 |
+
font-size: 1rem;
|
1355 |
+
}
|
1356 |
+
.file-info {
|
1357 |
+
padding: 10px;
|
1358 |
+
background-color: #E8F5E9;
|
1359 |
+
border-radius: 5px;
|
1360 |
+
margin-bottom: 10px;
|
1361 |
+
}
|
1362 |
+
.sidebar-content {
|
1363 |
+
padding: 10px;
|
1364 |
+
}
|
1365 |
+
.highlight-text {
|
1366 |
+
color: #1E88E5;
|
1367 |
+
font-weight: bold;
|
1368 |
+
}
|
1369 |
+
.stButton>button {
|
1370 |
+
width: 100%;
|
1371 |
+
}
|
1372 |
+
</style>
|
1373 |
+
""", unsafe_allow_html=True)
|
1374 |
+
|
1375 |
+
# Sidebar for data loading and information
|
1376 |
+
with st.sidebar:
|
1377 |
+
st.markdown('<div class="sidebar-content">', unsafe_allow_html=True)
|
1378 |
+
st.markdown('<p class="sub-header">π Data Loading</p>', unsafe_allow_html=True)
|
1379 |
+
|
1380 |
+
# File uploader
|
1381 |
+
uploaded_file = st.file_uploader("Upload your data file", type=['csv', 'xlsx', 'json', 'db', 'sqlite'])
|
1382 |
+
|
1383 |
+
# Load data button (only show if file is uploaded)
|
1384 |
+
if uploaded_file is not None:
|
1385 |
+
file_type = uploaded_file.name.split('.')[-1].lower()
|
1386 |
+
|
1387 |
+
# Save the uploaded file to a temporary location
|
1388 |
+
temp_file_path = f"temp_upload_{datetime.now().strftime('%Y%m%d%H%M%S')}.{file_type}"
|
1389 |
+
with open(temp_file_path, "wb") as f:
|
1390 |
+
f.write(uploaded_file.getbuffer())
|
1391 |
+
|
1392 |
+
# Load data based on file type
|
1393 |
+
if st.button("Load Data"):
|
1394 |
+
try:
|
1395 |
+
if file_type == 'csv':
|
1396 |
+
response = st.session_state.chatbot.process_query(f"load csv {temp_file_path}")
|
1397 |
+
elif file_type in ['xlsx', 'xls']:
|
1398 |
+
response = st.session_state.chatbot.process_query(f"load excel {temp_file_path}")
|
1399 |
+
elif file_type == 'json':
|
1400 |
+
response = st.session_state.chatbot.process_query(f"load json {temp_file_path}")
|
1401 |
+
elif file_type in ['db', 'sqlite']:
|
1402 |
+
# For SQL databases, we need to prompt for a query
|
1403 |
+
st.session_state.current_file = temp_file_path
|
1404 |
+
st.session_state.data_loaded = False
|
1405 |
+
response = "SQL database loaded. Please enter a query in the main chat."
|
1406 |
+
else:
|
1407 |
+
response = "Unsupported file format. Please upload CSV, Excel, JSON, or SQLite files."
|
1408 |
+
|
1409 |
+
st.session_state.conversation.append({"role": "user", "message": f"Loading {uploaded_file.name}"})
|
1410 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1411 |
+
|
1412 |
+
if "Successfully loaded data" in response:
|
1413 |
+
st.session_state.data_loaded = True
|
1414 |
+
st.session_state.current_file = temp_file_path
|
1415 |
+
|
1416 |
+
# Get data preview
|
1417 |
+
if st.session_state.chatbot.data is not None:
|
1418 |
+
st.session_state.data_preview = st.session_state.chatbot.data.head()
|
1419 |
+
except Exception as e:
|
1420 |
+
st.error(f"Error loading data: {str(e)}")
|
1421 |
+
|
1422 |
+
# Display data information if data is loaded
|
1423 |
+
if st.session_state.data_loaded and st.session_state.chatbot.data is not None:
|
1424 |
+
st.markdown('<p class="sub-header">π Data Information</p>', unsafe_allow_html=True)
|
1425 |
+
|
1426 |
+
# Display basic info
|
1427 |
+
st.markdown('<div class="file-info">', unsafe_allow_html=True)
|
1428 |
+
st.write(f"**Rows:** {len(st.session_state.chatbot.data)}")
|
1429 |
+
st.write(f"**Columns:** {len(st.session_state.chatbot.data.columns)}")
|
1430 |
+
st.write(f"**Data Source:** {st.session_state.chatbot.data_source}")
|
1431 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
1432 |
+
|
1433 |
+
# Quick actions
|
1434 |
+
st.markdown('<p class="sub-header">β‘ Quick Actions</p>', unsafe_allow_html=True)
|
1435 |
+
|
1436 |
+
col1, col2 = st.columns(2)
|
1437 |
+
with col1:
|
1438 |
+
if st.button("Describe Data"):
|
1439 |
+
response = st.session_state.chatbot.process_query("describe")
|
1440 |
+
st.session_state.conversation.append({"role": "user", "message": "Describe data"})
|
1441 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1442 |
+
|
1443 |
+
with col2:
|
1444 |
+
if st.button("Check Missing"):
|
1445 |
+
response = st.session_state.chatbot.process_query("missing")
|
1446 |
+
st.session_state.conversation.append({"role": "user", "message": "Check missing values"})
|
1447 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1448 |
+
|
1449 |
+
col1, col2 = st.columns(2)
|
1450 |
+
with col1:
|
1451 |
+
if st.button("Correlations"):
|
1452 |
+
response = st.session_state.chatbot.process_query("correlate")
|
1453 |
+
st.session_state.conversation.append({"role": "user", "message": "Show correlations"})
|
1454 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1455 |
+
|
1456 |
+
with col2:
|
1457 |
+
if st.button("Generate Report"):
|
1458 |
+
response = st.session_state.chatbot.process_query("report")
|
1459 |
+
st.session_state.conversation.append({"role": "user", "message": "Generate report"})
|
1460 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1461 |
+
|
1462 |
+
# If report was generated, provide download link
|
1463 |
+
if "Report generated and saved as" in response:
|
1464 |
+
report_filename = response.split("Report generated and saved as ")[-1].strip()
|
1465 |
+
st.markdown(
|
1466 |
+
get_download_link(report_filename, "π₯ Download Report"),
|
1467 |
+
unsafe_allow_html=True
|
1468 |
+
)
|
1469 |
+
|
1470 |
+
# Help section
|
1471 |
+
st.markdown('<p class="sub-header">β Help</p>', unsafe_allow_html=True)
|
1472 |
+
if st.button("Show Commands"):
|
1473 |
+
response = st.session_state.chatbot.process_query("help")
|
1474 |
+
st.session_state.conversation.append({"role": "user", "message": "Show available commands"})
|
1475 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1476 |
+
|
1477 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
1478 |
+
|
1479 |
+
# Main area
|
1480 |
+
st.markdown('<h1 class="main-header">π Data Analysis Assistant</h1>', unsafe_allow_html=True)
|
1481 |
+
|
1482 |
+
# Show data preview if data is loaded
|
1483 |
+
if st.session_state.data_loaded and st.session_state.data_preview is not None:
|
1484 |
+
st.markdown('<p class="sub-header">Data Preview</p>', unsafe_allow_html=True)
|
1485 |
+
st.dataframe(st.session_state.data_preview, use_container_width=True)
|
1486 |
+
|
1487 |
+
# Display conversation history
|
1488 |
+
st.markdown('<p class="sub-header">Chat History</p>', unsafe_allow_html=True)
|
1489 |
+
chat_container = st.container()
|
1490 |
+
|
1491 |
+
with chat_container:
|
1492 |
+
for message in st.session_state.conversation:
|
1493 |
+
if message["role"] == "user":
|
1494 |
+
st.markdown(f'<div class="chat-user">π€ <b>You:</b> {message["message"]}</div>', unsafe_allow_html=True)
|
1495 |
+
else:
|
1496 |
+
# Process bot messages for special content
|
1497 |
+
bot_message = message["message"]
|
1498 |
+
|
1499 |
+
# Check if it's a visualization result
|
1500 |
+
if "Visualization created and saved as" in bot_message:
|
1501 |
+
# Extract the filename and load the image
|
1502 |
+
img_file = bot_message.split("Visualization created and saved as ")[-1].strip()
|
1503 |
+
if os.path.exists(img_file):
|
1504 |
+
st.markdown(f'<div class="chat-bot">π€ <b>Assistant:</b></div>', unsafe_allow_html=True)
|
1505 |
+
try:
|
1506 |
+
img = Image.open(img_file)
|
1507 |
+
st.image(img, caption="Generated Visualization", use_column_width=True)
|
1508 |
+
except Exception as e:
|
1509 |
+
st.error(f"Error displaying visualization: {str(e)}")
|
1510 |
+
st.markdown(f'<div class="chat-bot">π€ <b>Assistant:</b> {bot_message}</div>', unsafe_allow_html=True)
|
1511 |
+
else:
|
1512 |
+
st.markdown(f'<div class="chat-bot">π€ <b>Assistant:</b> {bot_message}</div>', unsafe_allow_html=True)
|
1513 |
+
|
1514 |
+
# Check if it's a report result
|
1515 |
+
elif "Report generated and saved as" in bot_message:
|
1516 |
+
report_filename = bot_message.split("Report generated and saved as ")[-1].strip()
|
1517 |
+
st.markdown(
|
1518 |
+
f'<div class="chat-bot">π€ <b>Assistant:</b> {bot_message}<br/>{get_download_link(report_filename, "π₯ Download Report")}</div>',
|
1519 |
+
unsafe_allow_html=True
|
1520 |
+
)
|
1521 |
+
|
1522 |
+
# Regular message
|
1523 |
+
else:
|
1524 |
+
# Format code blocks
|
1525 |
+
if "```" in bot_message:
|
1526 |
+
parts = bot_message.split("```")
|
1527 |
+
formatted_message = ""
|
1528 |
+
for i, part in enumerate(parts):
|
1529 |
+
if i % 2 == 0: # Outside code block
|
1530 |
+
formatted_message += part
|
1531 |
+
else: # Inside code block
|
1532 |
+
formatted_message += f"<pre style='background-color: #f0f0f0; padding: 10px; border-radius: 5px; overflow-x: auto;'>{part}</pre>"
|
1533 |
+
st.markdown(f'<div class="chat-bot">π€ <b>Assistant:</b> {formatted_message}</div>', unsafe_allow_html=True)
|
1534 |
+
else:
|
1535 |
+
st.markdown(f'<div class="chat-bot">π€ <b>Assistant:</b> {bot_message}</div>', unsafe_allow_html=True)
|
1536 |
+
|
1537 |
+
# User input
|
1538 |
+
st.markdown('<p class="sub-header">Ask a Question</p>', unsafe_allow_html=True)
|
1539 |
+
user_input = st.text_area("Enter your query", height=100, key="user_query")
|
1540 |
+
|
1541 |
+
# Handle SQL query case
|
1542 |
+
if st.session_state.current_file is not None and not st.session_state.data_loaded and st.session_state.current_file.endswith(('db', 'sqlite')):
|
1543 |
+
sql_query = st.text_area("Enter SQL query", height=100, key="sql_query")
|
1544 |
+
if st.button("Run SQL Query") and sql_query:
|
1545 |
+
response = st.session_state.chatbot.process_query(f"load sql {st.session_state.current_file} query {sql_query}")
|
1546 |
+
st.session_state.conversation.append({"role": "user", "message": f"SQL query: {sql_query}"})
|
1547 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1548 |
+
|
1549 |
+
if "Successfully loaded data" in response:
|
1550 |
+
st.session_state.data_loaded = True
|
1551 |
+
if st.session_state.chatbot.data is not None:
|
1552 |
+
st.session_state.data_preview = st.session_state.chatbot.data.head()
|
1553 |
+
|
1554 |
+
# Submit button for regular queries
|
1555 |
+
if st.button("Submit") and user_input:
|
1556 |
+
# Add user message to conversation
|
1557 |
+
st.session_state.conversation.append({"role": "user", "message": user_input})
|
1558 |
+
|
1559 |
+
# Process query
|
1560 |
+
response = st.session_state.chatbot.process_query(user_input)
|
1561 |
+
|
1562 |
+
# Add bot response to conversation
|
1563 |
+
st.session_state.conversation.append({"role": "bot", "message": response})
|
1564 |
+
|
1565 |
+
# Clear input
|
1566 |
+
st.session_state.user_query = ""
|
1567 |
+
|
1568 |
+
# Add warning for demo mode
|
1569 |
+
st.markdown("---")
|
1570 |
+
st.markdown("**Note:** File uploads and data processing are handled locally. Make sure you have the necessary dependencies installed.", unsafe_allow_html=True)
|
1571 |
+
|
1572 |
+
# Footer
|
1573 |
+
st.markdown("---")
|
1574 |
+
st.markdown("Β© 2025 Data Analysis Assistant | Built with Streamlit")
|
1575 |
|
1576 |
return help_text
|
1577 |
|