inspekta_deck / src /streamlit_app.py
amiguel's picture
Rename src/app.py to src/streamlit_app.py
badee10 verified
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import math
import matplotlib.transforms as transforms
import sqlite3
# Import FPSO-specific modules
from reparos.clv import *
from reparos.paz import *
from reparos.dal import *
from reparos.gir import *
# --- UI CONFIG & STYLE ---
st.set_page_config(page_title="Inspekta Deck - FPSO Notifications", layout="wide")
st.markdown("""
<style>
@import url('https://fonts.cdnfonts.com/css/tw-cen-mt');
* {
font-family: 'Tw Cen MT', sans-serif !important;
}
/* Sidebar arrow fix */
section[data-testid="stSidebar"] [data-testid="stSidebarNav"]::before {
content: "β–Ά";
font-size: 1.3rem;
margin-right: 0.4rem;
}
/* Fix sidebar expander layout */
section[data-testid="stSidebar"] [data-testid="stExpander"] {
margin-bottom: 1rem;
}
section[data-testid="stSidebar"] [data-testid="stExpander"] [data-testid="stExpanderHeader"] {
padding: 0.5rem 0.75rem;
font-size: 0.9rem;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
}
section[data-testid="stSidebar"] [data-testid="stExpander"] [data-testid="stExpanderContent"] {
padding: 0.5rem 0.75rem;
}
/* Ensure proper spacing for sidebar elements */
section[data-testid="stSidebar"] .stMarkdown {
margin-bottom: 0.5rem;
}
section[data-testid="stSidebar"] .stButton {
margin-top: 0.5rem;
}
/* Ensure sidebar has proper width */
section[data-testid="stSidebar"] {
min-width: 300px;
}
/* Improve expander content readability */
section[data-testid="stSidebar"] [data-testid="stExpander"] .stMarkdown {
font-size: 0.85rem;
line-height: 1.3;
}
section[data-testid="stSidebar"] [data-testid="stExpander"] .stMarkdown p {
margin-bottom: 0.25rem;
}
/* Top-right logo placement - responsive to scrolling */
.logo-container {
position: absolute;
top: 1rem;
right: 2rem;
z-index: 1000;
transition: all 0.3s ease;
}
/* Adjust logo position when scrolling */
.logo-container.scrolled {
position: fixed;
top: 0.5rem;
right: 1rem;
transform: scale(0.8);
}
/* Ensure main content doesn't overlap with logo */
.main .block-container {
padding-top: 2rem !important;
}
/* Smooth transitions for logo */
.logo-container img {
transition: all 0.3s ease;
}
/* Custom styling for better UX */
.stButton > button {
border-radius: 8px;
border: 1px solid #e0e0e0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: 500;
transition: all 0.3s ease;
}
.stButton > button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* Custom styling for metrics */
.metric-container {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
padding: 1rem;
border-radius: 10px;
color: white;
text-align: center;
margin: 0.5rem 0;
}
/* Custom styling for charts */
.chart-container {
background: white;
padding: 1rem;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin: 1rem 0;
}
/* Responsive design */
@media (max-width: 768px) {
.logo-container {
position: relative;
top: auto;
right: auto;
text-align: center;
margin-bottom: 1rem;
}
.main .block-container {
padding-top: 1rem !important;
}
}
</style>
""", unsafe_allow_html=True)
# Add logo and title
st.markdown("""
<div class="logo-container">
<h1 style="color: #1f77b4; margin: 0; font-size: 2rem;">🚒 Inspekta Deck</h1>
<p style="color: #666; margin: 0; font-size: 1rem;">FPSO Notifications Analysis</p>
</div>
""", unsafe_allow_html=True)
# Main title
st.title("🚒 FPSO Notifications Analysis Platform")
st.markdown("---")
# Initialize session state
if 'data_loaded' not in st.session_state:
st.session_state.data_loaded = False
if 'df' not in st.session_state:
st.session_state.df = None
if 'fpsos' not in st.session_state:
st.session_state.fpsos = []
# Sidebar
with st.sidebar:
st.header("πŸ“Š Data Management")
# File upload
uploaded_file = st.file_uploader("Upload Excel file", type=['xlsx', 'xls'])
if uploaded_file is not None:
try:
# Load data
df = pd.read_excel(uploaded_file)
st.session_state.df = df
st.session_state.data_loaded = True
st.session_state.fpsos = df['FPSO'].unique().tolist()
st.success("Data loaded successfully!")
except Exception as e:
st.error(f"Error loading file: {str(e)}")
# Database connection (if available)
if st.button("Load from Database"):
try:
# Try multiple possible database paths
db_paths = [
'reparos/notifs_data.db',
'notifs_data.db',
'./reparos/notifs_data.db',
'../reparos/notifs_data.db'
]
df = None
for db_path in db_paths:
try:
conn = sqlite3.connect(db_path)
df = pd.read_sql_query("SELECT * FROM notifications", conn)
conn.close()
st.success(f"Data loaded from database: {db_path}")
break
except Exception:
continue
if df is not None:
st.session_state.df = df
st.session_state.data_loaded = True
st.session_state.fpsos = df['FPSO'].unique().tolist()
else:
st.error("Database file not found. Please upload an Excel file instead.")
except Exception as e:
st.error(f"Error loading from database: {str(e)}")
if st.session_state.data_loaded:
st.markdown("---")
st.header("🎯 Analysis Options")
# FPSO selection
selected_fpsos = st.multiselect(
"Select FPSOs to analyze",
st.session_state.fpsos,
default=st.session_state.fpsos[:3] if len(st.session_state.fpsos) >= 3 else st.session_state.fpsos
)
# Date range
if 'Date' in st.session_state.df.columns:
st.session_state.df['Date'] = pd.to_datetime(st.session_state.df['Date'])
min_date = st.session_state.df['Date'].min()
max_date = st.session_state.df['Date'].max()
date_range = st.date_input(
"Select date range",
value=(min_date, max_date),
min_value=min_date,
max_value=max_date
)
# Main content
if st.session_state.data_loaded:
# Create tabs
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([
"πŸ“ˆ Overview", "πŸ” CLV Analysis", "⚑ PAZ Analysis",
"πŸ—οΈ DAL Analysis", "βš™οΈ GIR Analysis", "πŸ€– RAG Assistant"
])
with tab1:
st.header("πŸ“Š Overview Dashboard")
# Key metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Notifications", len(st.session_state.df))
with col2:
st.metric("Unique FPSOs", len(st.session_state.fpsos))
with col3:
if 'Priority' in st.session_state.df.columns:
high_priority = len(st.session_state.df[st.session_state.df['Priority'] == 'High'])
st.metric("High Priority", high_priority)
with col4:
if 'Status' in st.session_state.df.columns:
open_notifications = len(st.session_state.df[st.session_state.df['Status'] == 'Open'])
st.metric("Open Notifications", open_notifications)
# Charts
col1, col2 = st.columns(2)
with col1:
st.subheader("Notifications by FPSO")
fpso_counts = st.session_state.df['FPSO'].value_counts()
fig, ax = plt.subplots(figsize=(10, 6))
fpso_counts.plot(kind='bar', ax=ax)
plt.xticks(rotation=45)
plt.tight_layout()
st.pyplot(fig)
with col2:
if 'Priority' in st.session_state.df.columns:
st.subheader("Notifications by Priority")
priority_counts = st.session_state.df['Priority'].value_counts()
fig, ax = plt.subplots(figsize=(10, 6))
priority_counts.plot(kind='pie', autopct='%1.1f%%', ax=ax)
plt.tight_layout()
st.pyplot(fig)
with tab2:
st.header("πŸ” CLV Analysis")
# CLV-specific analysis would go here
st.info("CLV analysis features will be implemented here")
with tab3:
st.header("⚑ PAZ Analysis")
# PAZ-specific analysis would go here
st.info("PAZ analysis features will be implemented here")
with tab4:
st.header("πŸ—οΈ DAL Analysis")
# DAL-specific analysis would go here
st.info("DAL analysis features will be implemented here")
with tab5:
st.header("βš™οΈ GIR Analysis")
# GIR-specific analysis would go here
st.info("GIR analysis features will be implemented here")
with tab6:
st.header("πŸ€– RAG Assistant")
try:
# Import RAG chatbot
from reparos.rag_chatbot import DigiTwinRAG
# Initialize RAG system
if 'rag_system' not in st.session_state:
st.session_state.rag_system = DigiTwinRAG()
# Load data into RAG system
if st.session_state.df is not None:
st.session_state.rag_system.load_notifications_data(st.session_state.df)
# Render chat interface
st.session_state.rag_system.render_chat_interface()
except ImportError:
st.error("RAG module not available. Please install the required dependencies.")
st.code("pip install -r reparos/requirements_rag.txt")
except Exception as e:
st.error(f"Error initializing RAG system: {str(e)}")
else:
st.info("πŸ‘† Please upload an Excel file or load data from database to begin analysis.")
# Show sample data structure
st.subheader("πŸ“‹ Expected Data Structure")
st.markdown("""
Your Excel file should contain the following columns:
- **FPSO**: FPSO identifier
- **Date**: Notification date
- **Priority**: Notification priority (High, Medium, Low)
- **Status**: Notification status (Open, Closed, etc.)
- **Description**: Notification description
- **Category**: Notification category
""")
# Show sample data
sample_data = pd.DataFrame({
'FPSO': ['CLV', 'PAZ', 'DAL', 'GIR'],
'Date': ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'],
'Priority': ['High', 'Medium', 'Low', 'High'],
'Status': ['Open', 'Closed', 'Open', 'Closed'],
'Description': ['Sample notification 1', 'Sample notification 2', 'Sample notification 3', 'Sample notification 4'],
'Category': ['Safety', 'Maintenance', 'Operations', 'Safety']
})
st.dataframe(sample_data)
# Footer
st.markdown("---")
st.markdown("""
<div style="text-align: center; color: #666; padding: 1rem;">
<p>🚒 <strong>Inspekta Deck</strong> - FPSO Notifications Analysis Platform</p>
<p>Built with ❀️ by ValonyLabs | Powered by Streamlit</p>
</div>
""", unsafe_allow_html=True)