DEV Community

Armaan Khan
Armaan Khan

Posted on Edited on

daboard3

import streamlit as st
import pandas as pd
import json
from datetime import datetime
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
from typing import Dict, List, Any, Optional

# Configure page
st.set_page_config(
    page_title="Snowflake Query Optimizer",
    page_icon="❄️",
    layout="wide",
    initial_sidebar_state="collapsed"
)

# Custom CSS for beautiful UI
st.markdown("""
<style>
    /* Import Google Fonts */
    @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

    /* Global Styles */
    * {
        font-family: 'Inter', sans-serif;
    }

    .main {
        padding: 1rem 2rem;
        background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
        min-height: 100vh;
    }

    /* Header Styles */
    .header-container {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        padding: 3rem 2rem;
        border-radius: 20px;
        margin-bottom: 2rem;
        color: white;
        text-align: center;
        box-shadow: 0 20px 60px rgba(102, 126, 234, 0.3);
        position: relative;
        overflow: hidden;
    }

    .header-container::before {
        content: '';
        position: absolute;
        top: -50%;
        left: -50%;
        width: 200%;
        height: 200%;
        background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
        animation: rotate 20s linear infinite;
    }

    @keyframes rotate {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }

    .header-title {
        font-size: 3rem;
        font-weight: 700;
        margin-bottom: 1rem;
        text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        position: relative;
        z-index: 1;
    }

    .header-subtitle {
        font-size: 1.3rem;
        opacity: 0.95;
        font-weight: 400;
        position: relative;
        z-index: 1;
    }

    /* Metrics Cards */
    .metrics-container {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
        gap: 1.5rem;
        margin-bottom: 2rem;
    }

    .metric-card {
        background: white;
        padding: 2rem;
        border-radius: 16px;
        box-shadow: 0 8px 32px rgba(0,0,0,0.1);
        border: 1px solid rgba(255,255,255,0.2);
        backdrop-filter: blur(10px);
        transition: all 0.3s ease;
        position: relative;
        overflow: hidden;
    }

    .metric-card::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 4px;
        background: linear-gradient(90deg, #667eea, #764ba2);
    }

    .metric-card:hover {
        transform: translateY(-8px);
        box-shadow: 0 16px 48px rgba(0,0,0,0.15);
    }

    .metric-value {
        font-size: 2.5rem;
        font-weight: 700;
        color: #2c3e50;
        margin-bottom: 0.5rem;
        background: linear-gradient(135deg, #667eea, #764ba2);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
    }

    .metric-label {
        color: #7f8c8d;
        font-size: 1rem;
        text-transform: uppercase;
        letter-spacing: 1px;
        font-weight: 500;
    }

    /* Table Styles */
    .table-container {
        background: white;
        border-radius: 20px;
        overflow: hidden;
        box-shadow: 0 20px 60px rgba(0,0,0,0.1);
        margin: 2rem 0;
        border: 1px solid rgba(255,255,255,0.2);
    }

    .table-header {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 2rem;
        text-align: center;
        position: relative;
    }

    .table-title {
        font-size: 1.8rem;
        font-weight: 600;
        margin-bottom: 0.5rem;
    }

    .table-subtitle {
        font-size: 1rem;
        opacity: 0.9;
        font-weight: 400;
    }

    /* Streamlit Table Row Styles */
    .table-row {
        display: flex;
        align-items: center;
        padding: 0.5rem 0;
        border-bottom: 1px solid #f1f3f4;
        transition: all 0.3s ease;
    }

    .table-row:hover {
        background: linear-gradient(135deg, #f8f9ff 0%, #f0f4ff 100%);
        border-radius: 8px;
        margin: 0 0.5rem;
    }

    .table-header-row {
        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
        font-weight: 600;
        color: #495057;
        border-bottom: 2px solid #dee2e6;
        font-size: 0.9rem;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        padding: 1rem 0;
        margin-bottom: 0.5rem;
        border-radius: 8px;
    }

    /* User cell styling */
    .user-cell-style {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
        color: white !important;
        font-weight: 700 !important;
        border-radius: 12px !important;
        border: none !important;
        box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
        height: 50px !important;
    }

    /* Clickable metric button styles */
    .metric-button {
        background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%) !important;
        color: #1565c0 !important;
        font-weight: 600 !important;
        border-radius: 8px !important;
        border: 2px solid transparent !important;
        transition: all 0.3s ease !important;
        height: 45px !important;
    }

    .metric-button:hover {
        background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%) !important;
        color: white !important;
        transform: scale(1.05) !important;
        box-shadow: 0 4px 15px rgba(25, 118, 210, 0.3) !important;
    }

    .zero-button {
        background: #f8f9fa !important;
        color: #6c757d !important;
        font-weight: 500 !important;
        border-radius: 8px !important;
        border: none !important;
        cursor: default !important;
        height: 45px !important;
    }

    .zero-button:hover {
        background: #f8f9fa !important;
        color: #6c757d !important;
        transform: none !important;
        box-shadow: none !important;
    }

    /* Status button styles */
    .status-high {
        background: linear-gradient(135deg, #ff5722 0%, #d32f2f 100%) !important;
        color: white !important;
        border-radius: 20px !important;
        font-weight: 600 !important;
        font-size: 0.85rem !important;
        box-shadow: 0 4px 15px rgba(255, 87, 34, 0.3) !important;
        border: none !important;
        height: 45px !important;
    }

    .status-normal {
        background: linear-gradient(135deg, #4caf50 0%, #388e3c 100%) !important;
        color: white !important;
        border-radius: 20px !important;
        font-weight: 600 !important;
        font-size: 0.85rem !important;
        box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3) !important;
        border: none !important;
        height: 45px !important;
    }

    /* Query Details Styles */
    .query-details-container {
        background: white;
        border-radius: 20px;
        padding: 2rem;
        margin: 2rem 0;
        box-shadow: 0 20px 60px rgba(0,0,0,0.1);
        border: 1px solid rgba(255,255,255,0.2);
    }

    .query-details-header {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 2rem;
        border-radius: 16px;
        margin-bottom: 2rem;
        text-align: center;
        position: relative;
        overflow: hidden;
    }

    .query-details-title {
        font-size: 2rem;
        font-weight: 700;
        margin-bottom: 0.5rem;
    }

    .query-details-subtitle {
        font-size: 1.1rem;
        opacity: 0.9;
        font-weight: 400;
    }

    .query-box {
        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
        border: 2px solid #dee2e6;
        border-radius: 12px;
        padding: 1.5rem;
        margin: 1rem 0;
        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
        font-size: 0.9rem;
        line-height: 1.6;
        white-space: pre-wrap;
        max-height: 400px;
        overflow-y: auto;
        box-shadow: inset 0 2px 8px rgba(0,0,0,0.05);
        position: relative;
    }

    .query-box::before {
        content: 'SQL';
        position: absolute;
        top: 10px;
        right: 15px;
        background: #667eea;
        color: white;
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 0.7rem;
        font-weight: 600;
    }

    /* Button Styles */
    .stButton > button {
        transition: all 0.3s ease !important;
    }

    /* Dropdown Styles */
    .stSelectbox > div > div {
        background: white;
        border: 2px solid #e9ecef;
        border-radius: 12px;
        box-shadow: 0 4px 15px rgba(0,0,0,0.1);
    }

    /* Metric Cards in Details */
    .detail-metrics {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 1rem;
        margin: 2rem 0;
    }

    .detail-metric-card {
        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
        padding: 1.5rem;
        border-radius: 12px;
        text-align: center;
        border: 1px solid #dee2e6;
        transition: all 0.3s ease;
    }

    .detail-metric-card:hover {
        transform: translateY(-4px);
        box-shadow: 0 8px 25px rgba(0,0,0,0.1);
    }

    .detail-metric-value {
        font-size: 1.8rem;
        font-weight: 700;
        color: #495057;
        margin-bottom: 0.5rem;
    }

    .detail-metric-label {
        color: #6c757d;
        font-size: 0.9rem;
        font-weight: 500;
        text-transform: uppercase;
        letter-spacing: 0.5px;
    }

    /* Animation Classes */
    .fade-in {
        animation: fadeIn 0.6s ease-out;
    }

    @keyframes fadeIn {
        from {
            opacity: 0;
            transform: translateY(20px);
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    .slide-in {
        animation: slideIn 0.8s ease-out;
    }

    @keyframes slideIn {
        from {
            opacity: 0;
            transform: translateX(-30px);
        }
        to {
            opacity: 1;
            transform: translateX(0);
        }
    }

    /* Scrollbar Styles */
    ::-webkit-scrollbar {
        width: 8px;
    }

    ::-webkit-scrollbar-track {
        background: #f1f1f1;
        border-radius: 4px;
    }

    ::-webkit-scrollbar-thumb {
        background: linear-gradient(135deg, #667eea, #764ba2);
        border-radius: 4px;
    }

    ::-webkit-scrollbar-thumb:hover {
        background: linear-gradient(135deg, #5a6fd8, #6a42a0);
    }

    /* Loading Animation */
    .loading {
        display: inline-block;
        width: 20px;
        height: 20px;
        border: 3px solid rgba(102, 126, 234, 0.3);
        border-radius: 50%;
        border-top-color: #667eea;
        animation: spin 1s ease-in-out infinite;
    }

    @keyframes spin {
        to { transform: rotate(360deg); }
    }

    /* Hide default streamlit button styling for table */
    .element-container:has(.user-cell-style) .stButton > button {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
        color: white !important;
        font-weight: 700 !important;
        border-radius: 12px !important;
        border: none !important;
        box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
    }
</style>
""", unsafe_allow_html=True)

# Initialize session state
def init_session_state():
    """Initialize session state variables"""
    if 'page' not in st.session_state:
        st.session_state.page = 'overview'
    if 'selected_user' not in st.session_state:
        st.session_state.selected_user = None
    if 'selected_metric' not in st.session_state:
        st.session_state.selected_metric = None
    if 'selected_count' not in st.session_state:
        st.session_state.selected_count = 0
    if 'selected_query_id' not in st.session_state:
        st.session_state.selected_query_id = None
    if 'sample_queries' not in st.session_state:
        st.session_state.sample_queries = []

# Sample data generator (replace with your actual data loading function)
@st.cache_data
def load_sample_data():
    """Load sample data that matches your Snowflake query output"""
    np.random.seed(42)

    users = ['john_doe', 'jane_smith', 'mike_wilson', 'sarah_jones', 'alex_brown', 
             'chris_taylor', 'emma_davis', 'ryan_clark', 'lisa_white', 'david_hall']

    data = []
    for user in users:
        # Generate realistic sample queries for each metric type
        sample_queries = {
            'spilled': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT * FROM large_table_{user} WHERE date > \'2024-01-01\' ORDER BY timestamp, amount DESC',
                    'execution_time_ms': np.random.randint(30000, 120000),
                    'bytes_spilled_to_local_storage': np.random.randint(500000, 5000000),
                    'bytes_spilled_to_remote_storage': np.random.randint(0, 1000000),
                    'warehouse_size': np.random.choice(['MEDIUM', 'LARGE', 'X-LARGE']),
                    'start_time': '2024-01-15 14:30:00'
                } for _ in range(np.random.randint(0, 5))
            ],
            'over_provisioned': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT COUNT(*) FROM small_table_{user}',
                    'execution_time_ms': np.random.randint(1000, 5000),
                    'bytes_scanned': np.random.randint(1000, 100000),
                    'warehouse_size': 'X-LARGE',
                    'start_time': '2024-01-15 10:15:00'
                } for _ in range(np.random.randint(0, 8))
            ],
            'peak_hour_long_running': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT a.*, b.* FROM big_table_a_{user} a JOIN big_table_b_{user} b ON a.id = b.id WHERE a.status = \'active\'',
                    'execution_time_ms': np.random.randint(300000, 600000),
                    'bytes_scanned': np.random.randint(10000, 50000),
                    'warehouse_size': np.random.choice(['LARGE', 'X-LARGE', '2X-LARGE']),
                    'start_time': '2024-01-15 14:30:00'
                } for _ in range(np.random.randint(0, 6))
            ],
            'select_star': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT * FROM user_data_{user} LIMIT 1000',
                    'execution_time_ms': np.random.randint(5000, 30000),
                    'bytes_scanned': np.random.randint(1000, 10000),
                    'warehouse_size': np.random.choice(['SMALL', 'MEDIUM', 'LARGE']),
                    'start_time': '2024-01-15 11:20:00'
                } for _ in range(np.random.randint(0, 10))
            ],
            'unpartitioned_scan': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT * FROM events_{user} WHERE event_date BETWEEN \'2024-01-01\' AND \'2024-12-31\'',
                    'execution_time_ms': np.random.randint(60000, 180000),
                    'bytes_scanned': np.random.randint(5000, 30000),
                    'partitions_scanned': 365,
                    'partitions_total': 365,
                    'start_time': '2024-01-15 16:45:00'
                } for _ in range(np.random.randint(0, 7))
            ],
            'zero_result': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT * FROM transactions_{user} WHERE amount < 0 AND status = \'invalid\'',
                    'execution_time_ms': np.random.randint(10000, 60000),
                    'bytes_scanned': np.random.randint(500, 5000),
                    'rows_produced': 0,
                    'start_time': '2024-01-15 09:30:00'
                } for _ in range(np.random.randint(0, 4))
            ],
            'slow': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT customer_id, SUM(amount) FROM transactions_{user} GROUP BY customer_id HAVING SUM(amount) > 10000 ORDER BY SUM(amount) DESC',
                    'execution_time_ms': np.random.randint(120000, 300000),
                    'bytes_scanned': np.random.randint(10000, 50000),
                    'warehouse_size': np.random.choice(['MEDIUM', 'LARGE', 'X-LARGE']),
                    'start_time': '2024-01-15 13:15:00'
                } for _ in range(np.random.randint(0, 6))
            ],
            'cartesian_join': [
                {
                    'query_id': f'Q{np.random.randint(1000, 9999)}',
                    'query_text': f'SELECT * FROM table_a_{user} CROSS JOIN table_b_{user}',
                    'execution_time_ms': np.random.randint(180000, 600000),
                    'bytes_scanned': np.random.randint(20000, 100000),
                    'rows_produced': np.random.randint(100, 10000),
                    'start_time': '2024-01-15 15:20:00'
                } for _ in range(np.random.randint(0, 2))
            ]
        }

        user_data = {
            'USER_NAME': user,
            'TOTAL_QUERIES': np.random.randint(500, 3000),
            'WAREHOUSES_USED': np.random.randint(2, 8),
            'DATABASES_ACCESSED': np.random.randint(3, 10),
            'TOTAL_CREDITS': round(np.random.uniform(50.0, 500.0), 2),
            'AVG_EXECUTION_TIME_MS': np.random.randint(1000, 8000),
            'AVG_BYTES_PER_ROW': round(np.random.uniform(100.0, 2000.0), 2),
            'TOTAL_DATA_SCANNED_GB': round(np.random.uniform(100.0, 2000.0), 2),
            'FAILURE_CANCELLATION_RATE_PCT': round(np.random.uniform(0.5, 8.0), 2),
            'SPILLED_QUERIES': len(sample_queries['spilled']),
            'OVER_PROVISIONED_QUERIES': len(sample_queries['over_provisioned']),
            'PEAK_HOUR_LONG_RUNNING_QUERIES': len(sample_queries['peak_hour_long_running']),
            'SELECT_STAR_QUERIES': len(sample_queries['select_star']),
            'UNPARTITIONED_SCAN_QUERIES': len(sample_queries['unpartitioned_scan']),
            'REPEATED_QUERIES': np.random.randint(50, 500),
            'COMPLEX_JOIN_QUERIES': np.random.randint(10, 100),
            'ZERO_RESULT_QUERIES': len(sample_queries['zero_result']),
            'HIGH_COMPILE_QUERIES': np.random.randint(5, 80),
            'UNTAGGED_QUERIES': np.random.randint(20, 200),
            'UNLIMITED_ORDER_BY_QUERIES': np.random.randint(5, 50),
            'LARGE_GROUP_BY_QUERIES': np.random.randint(3, 30),
            'SLOW_QUERIES': len(sample_queries['slow']),
            'EXPENSIVE_DISTINCT_QUERIES': np.random.randint(2, 25),
            'INEFFICIENT_LIKE_QUERIES': np.random.randint(10, 80),
            'NO_RESULTS_WITH_SCAN_QUERIES': np.random.randint(5, 40),
            'CARTESIAN_JOIN_QUERIES': len(sample_queries['cartesian_join']),
            'HIGH_COMPILE_RATIO_QUERIES': np.random.randint(3, 30),
            'WEIGHTED_SCORE': round(np.random.uniform(500.0, 3000.0), 2),
            'COST_STATUS': np.random.choice(['Normal', 'High Cost'], p=[0.7, 0.3]),
            'QUERY_SAMPLES': sample_queries
        }
        data.append(user_data)

    return pd.DataFrame(data)

def render_header():
    """Render the beautiful header section"""
    st.markdown("""
    <div class="header-container fade-in">
        <div class="header-title">❄️ Snowflake Query Optimizer</div>
        <div class="header-subtitle">Advanced Query Performance Analysis & Optimization Dashboard</div>
    </div>
    """, unsafe_allow_html=True)

def render_overview_metrics(df):
    """Render overview metrics cards"""
    total_users = len(df)
    total_queries = df['TOTAL_QUERIES'].sum()
    total_credits = df['TOTAL_CREDITS'].sum()
    high_cost_users = len(df[df['COST_STATUS'] == 'High Cost'])

    metrics_html = f"""
    <div class="metrics-container fade-in">
        <div class="metric-card">
            <div class="metric-value">{total_users:,}</div>
            <div class="metric-label">Total Users</div>
        </div>
        <div class="metric-card">
            <div class="metric-value">{total_queries:,}</div>
            <div class="metric-label">Total Queries</div>
        </div>
        <div class="metric-card">
            <div class="metric-value">${total_credits:,.2f}</div>
            <div class="metric-label">Total Credits</div>
        </div>
        <div class="metric-card">
            <div class="metric-value">{high_cost_users}</div>
            <div class="metric-label">High Cost Users</div>
        </div>
    </div>
    """

    st.markdown(metrics_html, unsafe_allow_html=True)

def handle_cell_click(user_name: str, metric_name: str, count: int, sample_queries: List[Dict]):
    """Handle cell click and navigate to details page"""
    st.session_state.page = 'query_list'
    st.session_state.selected_user = user_name
    st.session_state.selected_metric = metric_name
    st.session_state.selected_count = count
    st.session_state.sample_queries = sample_queries
    st.session_state.selected_query_id = None

def render_interactive_table(df):
    """Render the beautiful interactive table using Streamlit components"""
    st.markdown("""
    <div class="table-container slide-in">
        <div class="table-header">
            <div class="table-title">Query Performance Analysis</div>
            <div class="table-subtitle">Click on any query count to view detailed analysis</div>
        </div>
    </div>
    """, unsafe_allow_html=True)

    # Define metrics to display (excluding recommendations and query samples)
    metrics_config = [
        ('SPILLED_QUERIES', 'Spilled', 'spilled'),
        ('OVER_PROVISIONED_QUERIES', 'Over Provisioned', 'over_provisioned'),
        ('PEAK_HOUR_LONG_RUNNING_QUERIES', 'Peak Long Running', 'peak_hour_long_running'),
        ('SELECT_STAR_QUERIES', 'Select *', 'select_star'),
        ('UNPARTITIONED_SCAN_QUERIES', 'Unpartitioned Scan', 'unpartitioned_scan'),
        ('ZERO_RESULT_QUERIES', 'Zero Results', 'zero_result'),
        ('SLOW_QUERIES', 'Slow Queries', 'slow'),
        ('CARTESIAN_JOIN_QUERIES', 'Cartesian Joins', 'cartesian_join')
    ]

    # Create table header using columns
    header_cols = st.columns([2] + [1.2] * len(metrics_config) + [1.5])

    # Style the header row
    st.markdown('<div class="table-header-row">', unsafe_allow_html=True)
    header_cols[0].markdown("**User**")
    for i, (_, label, _) in enumerate(metrics_config):
        header_cols[i+1].markdown(f"**{label}**")
    header_cols[-1].markdown("**Cost Status**")
    st.markdown('</div>', unsafe_allow_html=True)

    # Create table rows
    for idx, row in df.iterrows():
        cols = st.columns([2] + [1.2] * len(metrics_config) + [1.5])

        # User column - non-clickable display
        with cols[0]:
            st.button(
                row['USER_NAME'], 
                key=f"user_{idx}",
                disabled=True,
                help=f"User: {row['USER_NAME']}",
                use_container_width=True
            )
            # Apply custom styling
            st.markdown(f"""
            <style>
            div[data-testid="stButton"]:has(button[key="user_{idx}"]) button {{
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
                color: white !important;
                font-weight: 700 !important;
                border-radius: 12px !important;
                border: none !important;
                box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
                cursor: default !important;
            }}
            </style>
            """, unsafe_allow_html=True)

        # Metric columns with clickable buttons
        for i, (col_name, _, sample_key) in enumerate(metrics_config):
            value = int(row[col_name])
            sample_queries = row['QUERY_SAMPLES'].get(sample_key, []) if isinstance(row['QUERY_SAMPLES'], dict) else []

            with cols[i+1]:
                if value == 0:
                    # Zero value - non-clickable
                    st.button(
                        str(value),
                        key=f"zero_{row['USER_NAME']}_{col_name}_{idx}",
                        disabled=True,
                        use_container_width=True
                    )
                    # Apply zero styling
                    st.markdown(f"""
                    <style>
                    div[data-testid="stButton"]:has(button[key="zero_{row['USER_NAME']}_{col_name}_{idx}"]) button {{
                        background: #f8f9fa !important;
                        color: #6c757d !important;
                        font-weight: 500 !important;
                        border-radius: 8px !important;
                        border: none !important;
                        cursor: default !important;
                    }}
                    </style>
                    """, unsafe_allow_html=True)
                else:
                    # Clickable metric button
                    if st.button(
                        str(value),
                        key=f"metric_{row['USER_NAME']}_{col_name}_{idx}",
                        help=f"View {value} {col_name.replace('_', ' ').lower()}",
                        use_container_width=True
                    ):
                        handle_cell_click(row['USER_NAME'], col_name, value, sample_queries)
                        st.rerun()

                    # Apply metric button styling
                    st.markdown(f"""
                    <style>
                    div[data-testid="stButton"]:has(button[key="metric_{row['USER_NAME']}_{col_name}_{idx}"]) button {{
                        background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%) !important;
                        color: #1565c0 !important;
                        font-weight: 600 !important;
                        border-radius: 8px !important;
                        border: 2px solid transparent !important;
                        transition: all 0.3s ease !important;
                    }}
                    div[data-testid="stButton"]:has(button[key="metric_{row['USER_NAME']}_{col_name}_{idx}"]) button:hover {{
                        background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%) !important;
                        color: white !important;
                        transform: scale(1.05) !important;
                        box-shadow: 0 4px 15px rgba(25, 118, 210, 0.3) !important;
                    }}
                    </style>
                    """, unsafe_allow_html=True)

        # Cost status column
        with cols[-1]:
            status_text = row['COST_STATUS']
            st.button(
                status_text,
                key=f"status_{row['USER_NAME']}_{idx}",
                disabled=True,
                use_container_width=True
            )

            # Apply status styling
            status_class = "status-high" if status_text == 'High Cost' else "status-normal"
            bg_color = "linear-gradient(135deg, #ff5722 0%, #d32f2f 100%)" if status_text == 'High Cost' else "linear-gradient(135deg, #4caf50 0%, #388e3c 100%)"

            st.markdown(f"""
            <style>
            div[data-testid="stButton"]:has(button[key="status_{row['USER_NAME']}_{idx}"]) button {{
                background: {bg_color} !important;
                color: white !important;
                border-radius: 20px !important;
                font-weight: 600 !important;
                font-size: 0.85rem !important;
                box-shadow: 0 4px 15px rgba(255, 87, 34, 0.3) !important;
                border: none !important;
                cursor: default !important;
            }}
            </style>
            """, unsafe_allow_html=True)

def render_query_list_page():
    """Render the query list page with dropdown selection"""
    # Back button
    col1, col2 = st.columns([1, 4])
    with col1:
        if st.button("← Back to Overview", key="back_to_overview", help="Return to main table"):
            st.session_state.page = 'overview'
            st.rerun()

    # Page header
    metric_display = st.session_state.selected_metric.replace('_', ' ').title()
    st.markdown(f"""
    <div class="query-details-container fade-in">
        <div class="query-details-header">
            <div class="query-details-title">{st.session_state.selected_user}</div>
            <div class="query-details-subtitle">{metric_display} - {st.session_state.selected_count} queries found</div>
        </div>
    </div>
    """, unsafe_allow_html=True)

    # Show sample queries if available
    if st.session_state.sample_queries:
        st.markdown("### 📋 Sample Queries")

        # Create dropdown for query selection
        query_options = []
        for i, query in enumerate(st.session_state.sample_queries):
            query_preview = query.get('query_text', 'No query text')[:50] + "..."
            query_options.append(f"Query {i+1}: {query.get('query_id', 'Unknown')} - {query_preview}")

        selected_query_idx = st.selectbox(
            "Select a query to view details:",
            range(len(query_options)),
            format_func=lambda x: query_options[x],
            key="query_selector"
        )

        if st.button("View Query Details", key="view_details", type="primary"):
            st.session_state.selected_query_id = selected_query_idx
            st.session_state.page = 'query_details'
            st.rerun()

        # Show preview of selected query
        if selected_query_idx is not None:
            selected_query = st.session_state.sample_queries[selected_query_idx]

            col1, col2 = st.columns([2, 1])

            with col1:
                st.markdown("**Query Preview:**")
                query_text = selected_query.get('query_text', 'No query text available')
                st.markdown(f'<div class="query-box">{query_text}</div>', unsafe_allow_html=True)

            with col2:
                st.markdown("**Quick Metrics:**")

                metrics_html = '<div class="detail-metrics">'

                if 'execution_time_ms' in selected_query:
                    metrics_html += f'''
                    <div class="detail-metric-card">
                        <div class="detail-metric-value">{selected_query["execution_time_ms"]:,}ms</div>
                        <div class="detail-metric-label">Execution Time</div>
                    </div>
                    '''

                if 'bytes_scanned' in selected_query:
                    bytes_gb = selected_query['bytes_scanned'] / (1024**3)
                    metrics_html += f'''
                    <div class="detail-metric-card">
                        <div class="detail-metric-value">{bytes_gb:.2f}GB</div>
                        <div class="detail-metric-label">Data Scanned</div>
                    </div>
                    '''

                if 'warehouse_size' in selected_query:
                    metrics_html += f'''
                    <div class="detail-metric-card">
                        <div class="detail-metric-value">{selected_query["warehouse_size"]}</div>
                        <div class="detail-metric-label">Warehouse</div>
                    </div>
                    '''

                metrics_html += '</div>'
                st.markdown(metrics_html, unsafe_allow_html=True)

    else:
        st.info("No sample queries available for this metric.")

def render_query_details_page():
    """Render detailed view of a specific query"""
    # Back button
    col1, col2 = st.columns([1, 4])
    with col1:
        if st.button("← Back to Query List", key="back_to_list", help="Return to query list"):
            st.session_state.page = 'query_list'
            st.rerun()

    # Get selected query
    if (st.session_state.selected_query_id is not None and 
        st.session_state.selected_query_id < len(st.session_state.sample_queries)):

        query = st.session_state.sample_queries[st.session_state.selected_query_id]

        # Page header
        st.markdown(f"""
        <div class="query-details-container fade-in">
            <div class="query-details-header">
                <div class="query-details-title">Query Details: {query.get('query_id', 'Unknown')}</div>
                <div class="query-details-subtitle">{st.session_state.selected_user} - {st.session_state.selected_metric.replace('_', ' ').title()}</div>
            </div>
        </div>
        """, unsafe_allow_html=True)

        # Query metrics
        st.markdown("### 📊 Query Metrics")

        # Create metrics grid
        metrics_data = []
        if 'execution_time_ms' in query:
            metrics_data.append(("Execution Time", f"{query['execution_time_ms']:,} ms"))
        if 'bytes_scanned' in query:
            bytes_gb = query['bytes_scanned'] / (1024**3)
            metrics_data.append(("Data Scanned", f"{bytes_gb:.2f} GB"))
        if 'warehouse_size' in query:
            metrics_data.append(("Warehouse Size", query['warehouse_size']))
        if 'rows_produced' in query:
            metrics_data.append(("Rows Produced", f"{query['rows_produced']:,}"))
        if 'bytes_spilled_to_local_storage' in query:
            spilled_mb = query['bytes_spilled_to_local_storage'] / (1024**2)
            metrics_data.append(("Local Spill", f"{spilled_mb:.2f} MB"))
        if 'bytes_spilled_to_remote_storage' in query:
            spilled_mb = query['bytes_spilled_to_remote_storage'] / (1024**2)
            metrics_data.append(("Remote Spill", f"{spilled_mb:.2f} MB"))
        if 'partitions_scanned' in query and 'partitions_total' in query:
            metrics_data.append(("Partitions", f"{query['partitions_scanned']}/{query['partitions_total']}"))
        if 'start_time' in query:
            metrics_data.append(("Start Time", query['start_time']))

        # Display metrics in a grid
        cols = st.columns(min(4, len(metrics_data)))
        for i, (label, value) in enumerate(metrics_data):
            with cols[i % 4]:
                st.metric(label, value)

        # Query text
        st.markdown("### 📝 Query Text")
        query_text = query.get('query_text', 'No query text available')
        st.markdown(f'<div class="query-box">{query_text}</div>', unsafe_allow_html=True)

        # Performance analysis
        st.markdown("### 🔍 Performance Analysis")

        analysis_points = []

        # Add analysis based on query type and metrics
        if 'execution_time_ms' in query and query['execution_time_ms'] > 60000:
            analysis_points.append("⚠️ **Long execution time detected** - Consider optimizing query logic or adding appropriate indexes")

        if 'bytes_scanned' in query and query['bytes_scanned'] > 10000:  # > 1GB
            analysis_points.append("📊 **High data scan volume** - Consider using clustering keys or more selective WHERE clauses")

        if 'bytes_spilled_to_local_storage' in query and query['bytes_spilled_to_local_storage'] > 0:
            analysis_points.append("💾 **Memory spilling detected** - Consider increasing warehouse size or optimizing query complexity")

        if 'warehouse_size' in query and query['warehouse_size'] in ['X-LARGE', '2X-LARGE', '3X-LARGE', '4X-LARGE']:
            if 'execution_time_ms' in query and query['execution_time_ms'] < 10000:
                analysis_points.append("💰 **Potential over-provisioning** - Query completed quickly on large warehouse")

        if 'partitions_scanned' in query and 'partitions_total' in query:
            if query['partitions_scanned'] == query['partitions_total']:
                analysis_points.append("🔄 **Full table scan** - Consider adding partition pruning conditions")

        if query.get('rows_produced', 1) == 0:
            analysis_points.append("❌ **Zero results returned** - Review query logic and data availability")

        if analysis_points:
            for point in analysis_points:
                st.markdown(point)
        else:
            st.success("✅ No major performance issues detected for this query")

        # Recommendations
        st.markdown("### 💡 Optimization Recommendations")

        recommendations = []

        # Generate recommendations based on query characteristics
        if 'SELECT *' in query_text.upper():
            recommendations.append("🎯 **Specify columns explicitly** instead of using SELECT * to reduce data transfer")

        if 'ORDER BY' in query_text.upper() and 'LIMIT' not in query_text.upper():
            recommendations.append("📏 **Add LIMIT clause** to ORDER BY queries to prevent unnecessary sorting of large result sets")

        if 'JOIN' in query_text.upper() and 'ON' not in query_text.upper():
            recommendations.append("⚠️ **Review JOIN conditions** to ensure proper join predicates are specified")

        if 'GROUP BY' in query_text.upper():
            recommendations.append("📊 **Consider pre-aggregation** for frequently used GROUP BY operations")

        if 'DISTINCT' in query_text.upper():
            recommendations.append("🔄 **Evaluate DISTINCT usage** - consider if GROUP BY might be more efficient")

        if recommendations:
            for rec in recommendations:
                st.markdown(rec)
        else:
            st.info("No specific recommendations available for this query pattern")

    else:
        st.error("Query not found or invalid selection")

def render_analytics_charts(df):
    """Render analytics charts"""
    st.markdown("### 📊 Analytics Overview")

    col1, col2 = st.columns(2)

    with col1:
        # Top users by weighted score
        top_users = df.nlargest(10, 'WEIGHTED_SCORE')
        fig = px.bar(
            top_users,
            x='USER_NAME',
            y='WEIGHTED_SCORE',
            title='Top Users by Weighted Score',
            color='WEIGHTED_SCORE',
            color_continuous_scale='Viridis',
            template='plotly_white'
        )
        fig.update_layout(
            showlegend=False,
            title_font_size=16,
            title_font_color='#2c3e50'
        )
        st.plotly_chart(fig, use_container_width=True)

    with col2:
        # Cost status distribution
        cost_dist = df['COST_STATUS'].value_counts()
        fig = px.pie(
            values=cost_dist.values,
            names=cost_dist.index,
            title='Cost Status Distribution',
            color_discrete_map={'Normal': '#4caf50', 'High Cost': '#f44336'},
            template='plotly_white'
        )
        fig.update_layout(
            title_font_size=16,
            title_font_color='#2c3e50'
        )
        st.plotly_chart(fig, use_container_width=True)

def main():
    """Main application function"""
    # Initialize session state
    init_session_state()

    # Load data (replace this with your actual data loading function)
    df = load_sample_data()

    # Render header
    render_header()

    # Route to appropriate page
    if st.session_state.page == 'overview':
        render_overview_metrics(df)
        render_interactive_table(df)
        render_analytics_charts(df)

    elif st.session_state.page == 'query_list':
        render_query_list_page()

    elif st.session_state.page == 'query_details':
        render_query_details_page()

# Function to replace load_sample_data with your actual data
def load_your_data(df_from_snowflake):
    """
    Replace this function with your actual data loading logic

    Args:
        df_from_snowflake: DataFrame from your Snowflake query

    Returns:
        Processed DataFrame ready for the dashboard
    """
    # Your data processing logic here
    # Make sure the DataFrame has all required columns
    return df_from_snowflake

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Top comments (0)