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) | |