Spaces:
Sleeping
Sleeping
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) | |