Spaces:
Runtime error
Runtime error
| """ | |
| Page Manager for Pharmaceutical Analytics Application | |
| This module manages the different pages/views of the application, | |
| enabling a multi-page structure without bloating the main app.py. | |
| """ | |
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from datetime import datetime | |
| import time | |
| import threading | |
| import queue | |
| import sys | |
| import traceback | |
| import json | |
| # Import diagnostics module | |
| from updated_diagnostics import render_diagnostics_tab | |
| def run_workflow_in_thread(workflow, alert): | |
| """Run the workflow in a separate thread and update session state""" | |
| try: | |
| # Update status | |
| st.session_state.status_queue.put(("info", "Planning analysis approach...")) | |
| st.session_state.current_step = "planning" | |
| # Run the workflow | |
| print(f"Starting workflow with alert: {alert}") | |
| result = workflow.run_workflow(alert) | |
| print("Workflow completed successfully") | |
| # Store the result | |
| st.session_state.workflow_state = result | |
| # Update status | |
| st.session_state.status_queue.put(("success", "Analysis complete!")) | |
| st.session_state.current_step = "complete" | |
| except Exception as e: | |
| error_details = f"Error in workflow: {str(e)}\n{traceback.format_exc()}" | |
| print(error_details) | |
| st.session_state.status_queue.put(("error", f"Error: {str(e)}")) | |
| st.session_state.current_step = "error" | |
| # Store error details | |
| if "error_details" not in st.session_state: | |
| st.session_state.error_details = [] | |
| st.session_state.error_details.append(error_details) | |
| def run_workflow_directly(alert_text): | |
| """Run the workflow directly (not in a thread) for debugging""" | |
| try: | |
| # Create the workflow | |
| SalesAnalysisWorkflow = st.session_state.agents["SalesAnalysisWorkflow"] | |
| workflow = SalesAnalysisWorkflow(db_path="data/pharma_db.sqlite") | |
| # Update status | |
| st.session_state.current_step = "planning" | |
| # Run the workflow with verbose debug output | |
| st.info("Running workflow directly...") | |
| # Add detailed debugging information at each step | |
| st.write("1. Creating workflow instance") | |
| # Store the alert in session state | |
| st.session_state.alert = alert_text | |
| # Run the workflow step by step | |
| st.write("2. Starting workflow execution") | |
| result = workflow.run_workflow(alert_text) | |
| # Print workflow state | |
| st.write("3. Workflow execution completed") | |
| st.write(f"Status: {result.get('status', 'unknown')}") | |
| st.write(f"Plan created: {'Yes' if result.get('plan') else 'No'}") | |
| st.write(f"Data sources: {len(result.get('data_sources', {}))}") | |
| st.write(f"Analysis results: {len(result.get('analysis_results', {}))}") | |
| st.write(f"Validation results: {len(result.get('validation_results', {}))}") | |
| st.write(f"Insight requests: {len(result.get('insight_requests', []))}") | |
| st.write(f"Insight cards: {len(result.get('insight_cards', {}))}") | |
| # Store the result | |
| st.session_state.workflow_state = result | |
| st.session_state.current_step = "complete" | |
| # Add debug output | |
| if "insight_requests" in result and result["insight_requests"]: | |
| st.write("Insight Requests Details:") | |
| for i, req in enumerate(result["insight_requests"]): | |
| st.write(f"Request {i+1}: ID={req.request_id}") | |
| return True | |
| except Exception as e: | |
| st.error(f"Error in direct workflow execution: {str(e)}") | |
| st.code(traceback.format_exc()) | |
| return False | |
| def render_main_page(): | |
| """Render the main page with alert input and sample dashboard""" | |
| st.header("📱 Incoming Alert") | |
| col1, col2 = st.columns([3, 1]) | |
| with col1: | |
| # Dropdown for alert selection | |
| alert_option = st.selectbox( | |
| "Select Alert Type:", | |
| [ | |
| "Sales Decline", | |
| "Market Share Loss", | |
| "Inventory Issue", | |
| "Competitor Launch", | |
| "Custom Alert" | |
| ] | |
| ) | |
| # Alert templates | |
| alert_templates = { | |
| "Sales Decline": "Sales of DrugX down 15% in Northeast region over past 30 days compared to forecast.", | |
| "Market Share Loss": "Market share of DrugX decreased by 8 percentage points in the Southeast region over the last quarter.", | |
| "Inventory Issue": "Multiple stockouts of DrugX reported in Midwest distribution centers affecting 25% of pharmacies.", | |
| "Competitor Launch": "Competitor MedCorp launched similar product at 20% lower price point in Western territories.", | |
| "Custom Alert": "" | |
| } | |
| # Alert description | |
| alert_text = st.text_area( | |
| "Alert Description:", | |
| value=alert_templates[alert_option], | |
| height=100 | |
| ) | |
| # Submit button (only enabled if agents are loaded) | |
| submit_button = st.button("Analyze Alert", disabled=not st.session_state.get("agents_loaded", False)) | |
| if submit_button and alert_text: | |
| try: | |
| # Initialize queue if not exists | |
| if "status_queue" not in st.session_state: | |
| st.session_state.status_queue = queue.Queue() | |
| # Create and start the workflow | |
| SalesAnalysisWorkflow = st.session_state.agents["SalesAnalysisWorkflow"] | |
| workflow = SalesAnalysisWorkflow(db_path="data/pharma_db.sqlite") | |
| # Show initial status | |
| st.info("Starting analysis workflow...") | |
| st.session_state.logs.append({ | |
| "timestamp": datetime.now().isoformat(), | |
| "type": "info", | |
| "message": "Starting analysis workflow..." | |
| }) | |
| # Store the alert in session state | |
| st.session_state.alert = alert_text | |
| # Start the workflow in a separate thread | |
| thread = threading.Thread( | |
| target=run_workflow_in_thread, | |
| args=(workflow, alert_text), | |
| daemon=True | |
| ) | |
| thread.start() | |
| # Store the thread and mark alert as submitted | |
| st.session_state.workflow_thread = thread | |
| st.session_state.alert_submitted = True | |
| # Change active page to analysis viewer | |
| st.session_state.active_page = "analysis" | |
| # Force page refresh | |
| st.rerun() | |
| except Exception as e: | |
| st.error(f"Error starting workflow: {str(e)}") | |
| st.code(traceback.format_exc()) | |
| # Add fallback direct execution button | |
| if alert_text and st.session_state.get("agents_loaded", False): | |
| if st.button("Debug: Run Directly (No Thread)"): | |
| if run_workflow_directly(alert_text): | |
| st.success("Workflow completed! Check the Analysis Viewer page.") | |
| st.session_state.active_page = "analysis" | |
| st.rerun() | |
| with col2: | |
| st.image("https://img.icons8.com/fluency/240/000000/notification-center.png", width=150) | |
| st.markdown("**Source:** Mobile Alert System") | |
| st.markdown("**Priority:** High") | |
| st.markdown("**Time:** " + datetime.now().strftime("%Y-%m-%d %H:%M")) | |
| # Show example dashboard | |
| st.markdown("---") | |
| st.header("📊 Example Dashboard Preview") | |
| # Create sample visualization | |
| fig = px.line( | |
| pd.DataFrame({ | |
| 'Month': pd.date_range(start='2023-01-01', periods=12, freq='M'), | |
| 'Sales': [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150], | |
| 'Target': [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] | |
| }), | |
| x='Month', | |
| y=['Sales', 'Target'], | |
| title="DrugX Performance (Sample Data)", | |
| labels={"value": "Sales ($K)", "variable": "Metric"} | |
| ) | |
| fig.update_layout(height=400) | |
| st.plotly_chart(fig, use_container_width=True) | |
| def render_page(): | |
| """Render the current active page based on session state""" | |
| # Initialize session state if needed | |
| initialize_session() | |
| # Process status updates from thread if workflow is running | |
| if st.session_state.get("workflow_thread") and st.session_state.workflow_thread.is_alive(): | |
| while not st.session_state.status_queue.empty(): | |
| status_type, status_message = st.session_state.status_queue.get() | |
| if status_type == "info": | |
| st.info(status_message) | |
| elif status_type == "success": | |
| st.success(status_message) | |
| elif status_type == "error": | |
| st.error(status_message) | |
| # Add to logs | |
| st.session_state.logs.append({ | |
| "timestamp": datetime.now().isoformat(), | |
| "type": status_type, | |
| "message": status_message | |
| }) | |
| # Create navigation in sidebar | |
| with st.sidebar: | |
| st.header("Navigation") | |
| # Only show navigation options if environment is ready | |
| if st.session_state.get("environment_ready", False): | |
| pages = { | |
| "main": "Home", | |
| "analysis": "Analysis Viewer", | |
| "diagnostics": "Diagnostics" | |
| } | |
| # Only show Analysis Viewer option if an analysis exists | |
| if st.session_state.get("workflow_state") is None and "analysis" in pages: | |
| pages = {k: v for k, v in pages.items() if k != "analysis"} | |
| # Radio buttons for navigation | |
| selected_page = st.radio("Go to", list(pages.values())) | |
| # Map selection back to page key | |
| reverse_pages = {v: k for k, v in pages.items()} | |
| st.session_state.active_page = reverse_pages.get(selected_page, "main") | |
| # Render the active page | |
| active_page = st.session_state.get("active_page", "main") | |
| if active_page == "main": | |
| render_main_page() | |
| elif active_page == "analysis": | |
| workflow_state = st.session_state.get("workflow_state") | |
| if workflow_state: | |
| if workflow_state.get("status") == "complete": | |
| render_analysis_results(workflow_state) | |
| else: | |
| render_analysis_progress() | |
| else: | |
| st.info("No analysis is currently running. Start an analysis from the Home page.") | |
| st.session_state.active_page = "main" | |
| st.rerun() | |
| elif active_page == "diagnostics": | |
| render_diagnostics_tab() | |
| else: | |
| st.error(f"Unknown page: {active_page}") | |
| st.session_state.active_page = "main" | |
| def render_analysis_progress(): | |
| """Render the analysis progress view""" | |
| # Show progress | |
| current_step = st.session_state.current_step | |
| # Progress indicators | |
| st.markdown("### 🔄 Analysis in Progress") | |
| # Progress bar | |
| steps = ["planning", "data_collection", "analysis", "validation", "insights", "complete"] | |
| step_idx = steps.index(current_step) if current_step in steps else 0 | |
| progress = (step_idx + 1) / len(steps) | |
| progress_bar = st.progress(progress) | |
| # Step indicators | |
| col1, col2, col3, col4, col5 = st.columns(5) | |
| with col1: | |
| check = "✅" if step_idx >= 0 else "🔄" | |
| check = "🔄" if step_idx == 0 else check | |
| st.markdown(f"{check} **Planning**") | |
| with col2: | |
| check = "✅" if step_idx >= 1 else "⏳" | |
| check = "🔄" if step_idx == 1 else check | |
| st.markdown(f"{check} **Data Collection**") | |
| with col3: | |
| check = "✅" if step_idx >= 2 else "⏳" | |
| check = "🔄" if step_idx == 2 else check | |
| st.markdown(f"{check} **Analysis**") | |
| with col4: | |
| check = "✅" if step_idx >= 3 else "⏳" | |
| check = "🔄" if step_idx == 3 else check | |
| st.markdown(f"{check} **Validation**") | |
| with col5: | |
| check = "✅" if step_idx >= 4 else "⏳" | |
| check = "🔄" if step_idx == 4 else check | |
| st.markdown(f"{check} **Insights**") | |
| # Show current activity based on step | |
| st.markdown("---") | |
| # Display the alert being analyzed | |
| alert_container = st.container(border=True) | |
| with alert_container: | |
| st.markdown("**Alert Being Analyzed:**") | |
| st.info(st.session_state.get("alert", "Unknown alert")) | |
| # Show thinking animation or appropriate step visualization | |
| thinking_container = st.container() | |
| with thinking_container: | |
| if current_step == "planning": | |
| render_planning_activity() | |
| elif current_step == "data_collection": | |
| render_data_collection_activity() | |
| elif current_step == "analysis": | |
| render_analysis_activity() | |
| elif current_step == "validation": | |
| render_validation_activity() | |
| elif current_step == "insights": | |
| render_insights_activity() | |
| # Placeholder for log console | |
| with st.expander("View Process Log"): | |
| for log in st.session_state.logs: | |
| timestamp = log.get("timestamp", "") | |
| message = log.get("message", "") | |
| log_type = log.get("type", "info") | |
| if log_type == "error": | |
| st.error(f"{timestamp}: {message}") | |
| else: | |
| st.text(f"{timestamp}: {message}") | |
| # Display any error details if available | |
| if "error_details" in st.session_state and st.session_state.error_details: | |
| with st.expander("Error Details", expanded=True): | |
| for i, error in enumerate(st.session_state.error_details): | |
| st.error(f"Error {i+1}:") | |
| st.code(error) | |
| # Auto refresh only if thread is still running | |
| if st.session_state.get("workflow_thread") and st.session_state.workflow_thread.is_alive(): | |
| time.sleep(0.5) | |
| st.rerun() | |
| def render_planning_activity(): | |
| """Render Planning Agent activity visualization""" | |
| st.markdown("### 🧠 Planning Agent at Work") | |
| st.markdown("The Planning Agent is decomposing the problem and creating an analysis plan.") | |
| # Show thinking animation | |
| thinking_messages = [ | |
| "Identifying required data sources...", | |
| "Determining appropriate analytical approaches...", | |
| "Creating task dependency graph...", | |
| "Designing validation strategy..." | |
| ] | |
| thinking_placeholder = st.empty() | |
| for i in range(len(thinking_messages)): | |
| thinking_placeholder.info(thinking_messages[i % len(thinking_messages)]) | |
| time.sleep(0.5) | |
| def render_data_collection_activity(): | |
| """Render Data Agent activity visualization""" | |
| st.markdown("### 🗃️ Data Agent at Work") | |
| st.markdown("The Data Agent is translating requirements into SQL queries and collecting data.") | |
| # Show SQL generation animation | |
| if st.session_state.get("show_sql", False): | |
| sql_placeholder = st.empty() | |
| example_sql = """ | |
| -- Retrieving DrugX sales data by region | |
| SELECT | |
| r.region_name, | |
| strftime('%Y-%m', s.sale_date) as month, | |
| SUM(s.units_sold) as total_units, | |
| SUM(s.revenue) as total_revenue, | |
| SUM(s.margin) as total_margin | |
| FROM | |
| sales s | |
| JOIN | |
| regions r ON s.region_id = r.region_id | |
| WHERE | |
| s.product_id = 'DRX' | |
| AND s.sale_date >= date('now', '-90 days') | |
| GROUP BY | |
| r.region_name, strftime('%Y-%m', s.sale_date) | |
| ORDER BY | |
| r.region_name, month; | |
| """ | |
| for i in range(0, min(len(example_sql), 100), 10): | |
| sql_placeholder.code(example_sql[:i] + "▌", language="sql") | |
| time.sleep(0.02) | |
| sql_placeholder.code(example_sql, language="sql") | |
| def render_analysis_activity(): | |
| """Render Analytics Agent activity visualization""" | |
| st.markdown("### 📊 Analytics Agent at Work") | |
| st.markdown("The Analytics Agent is performing statistical analysis and modeling.") | |
| # Show Python code generation animation | |
| if st.session_state.get("show_code", False): | |
| code_placeholder = st.empty() | |
| example_code = """ | |
| import pandas as pd | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from statsmodels.tsa.seasonal import seasonal_decompose | |
| def analyze_sales_decline(sales_df): | |
| # Convert date column to datetime | |
| sales_df['date'] = pd.to_datetime(sales_df['date']) | |
| # Focus on Northeast region | |
| ne_sales = sales_df[sales_df['region'] == 'Northeast'] | |
| # Perform time series decomposition | |
| result = seasonal_decompose(ne_sales['sales'], | |
| model='multiplicative', | |
| period=12) | |
| # Calculate percentage decline | |
| latest_month = ne_sales['date'].max() | |
| prev_month = latest_month - pd.DateOffset(months=1) | |
| latest_sales = ne_sales[ne_sales['date'] == latest_month]['sales'].values[0] | |
| prev_sales = ne_sales[ne_sales['date'] == prev_month]['sales'].values[0] | |
| pct_decline = (prev_sales - latest_sales) / prev_sales * 100 | |
| """ | |
| for i in range(0, min(len(example_code), 200), 20): | |
| code_placeholder.code(example_code[:i] + "▌", language="python") | |
| time.sleep(0.01) | |
| code_placeholder.code(example_code, language="python") | |
| def render_validation_activity(): | |
| """Render QA Agent activity visualization""" | |
| st.markdown("### 🔍 QA Agent at Work") | |
| st.markdown("The QA Agent is validating the analysis results for accuracy and completeness.") | |
| # Show quality checks | |
| checks = [ | |
| {"check": "Data completeness", "status": "Running..."}, | |
| {"check": "Statistical significance", "status": "Waiting..."}, | |
| {"check": "Alternative hypotheses", "status": "Waiting..."}, | |
| {"check": "Insight relevance", "status": "Waiting..."} | |
| ] | |
| check_placeholder = st.empty() | |
| for i in range(min(len(checks), 4)): | |
| checks[i]["status"] = "Passed ✅" | |
| check_table = pd.DataFrame(checks) | |
| check_placeholder.table(check_table) | |
| time.sleep(0.5) | |
| def render_insights_activity(): | |
| """Render Insights Agent activity visualization""" | |
| st.markdown("### 💡 Insights Agent at Work") | |
| st.markdown("The Insights Agent is generating visualizations and actionable recommendations.") | |
| # Show visualization generation | |
| viz_placeholder = st.empty() | |
| # Create sample visualization progressively | |
| for i in range(1, 5): | |
| fig = go.Figure() | |
| months = pd.date_range(start='2023-01-01', periods=12, freq='M') | |
| sales = [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150] | |
| targets = [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] | |
| if i >= 1: | |
| fig.add_trace(go.Scatter( | |
| x=months[:8+i], | |
| y=targets[:8+i], | |
| mode='lines+markers', | |
| name='Target', | |
| line=dict(color='green', width=2) | |
| )) | |
| if i >= 2: | |
| fig.add_trace(go.Scatter( | |
| x=months[:8+i], | |
| y=sales[:8+i], | |
| mode='lines+markers', | |
| name='Actual Sales', | |
| line=dict(color='blue', width=2) | |
| )) | |
| if i >= 3: | |
| # Add competitor launch annotation | |
| fig.add_vline( | |
| x=months[9], | |
| line_dash="dash", | |
| line_color="red", | |
| annotation_text="Competitor Launch" if i >= 4 else "" | |
| ) | |
| fig.update_layout( | |
| title="DrugX Performance Analysis", | |
| xaxis_title="Month", | |
| yaxis_title="Sales ($K)", | |
| height=400 | |
| ) | |
| viz_placeholder.plotly_chart(fig, use_container_width=True) | |
| time.sleep(0.5) | |
| def render_analysis_results(workflow_state): | |
| """Render completed analysis results""" | |
| st.markdown("### ✅ Analysis Complete") | |
| # Display debug info at the top if there were any issues | |
| if "error_details" in st.session_state and st.session_state.error_details: | |
| with st.expander("Debug Information", expanded=False): | |
| for i, error in enumerate(st.session_state.error_details): | |
| st.error(f"Error {i+1}:") | |
| st.code(error) | |
| # Extract plan and insights | |
| plan = workflow_state.get("plan") | |
| insight_cards = workflow_state.get("insight_cards", {}) | |
| visualizations = workflow_state.get("visualizations", []) | |
| # Display the alert | |
| st.markdown("#### 📱 Alert Analyzed") | |
| st.info(workflow_state.get("alert", "")) | |
| # Display the problem statement | |
| if plan: | |
| st.markdown("#### 🎯 Problem Analysis") | |
| st.markdown(plan.problem_statement) | |
| # Display insight cards | |
| st.markdown("---") | |
| st.markdown("### 💡 Key Insights") | |
| if insight_cards: | |
| for card_id, card in insight_cards.items(): | |
| # Create a card-like container | |
| with st.container(): | |
| st.subheader(card.title) | |
| st.markdown(card.description) | |
| # Display visualizations if available | |
| if hasattr(card, 'charts') and card.charts and len(visualizations) > 0: | |
| # Generate sample visualizations | |
| cols = st.columns(min(len(card.charts), 2)) | |
| for i, chart_name in enumerate(card.charts[:2]): | |
| with cols[i % 2]: | |
| # Create appropriate chart based on name | |
| if "trend" in chart_name.lower(): | |
| # Create sample time series | |
| months = pd.date_range(start='2023-01-01', periods=12, freq='M') | |
| sales = [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150] | |
| targets = [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter( | |
| x=months, y=sales, mode='lines+markers', name='Actual Sales', | |
| line=dict(color='blue', width=2) | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=months, y=targets, mode='lines+markers', name='Target', | |
| line=dict(color='green', width=2, dash='dash') | |
| )) | |
| # Add competitor launch annotation | |
| fig.add_vline( | |
| x=months[9], line_dash="dash", line_color="red", | |
| annotation_text="Competitor Launch" | |
| ) | |
| fig.update_layout( | |
| title="DrugX Sales Trend", | |
| xaxis_title="Month", | |
| yaxis_title="Sales ($K)", | |
| height=300 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| elif "competitor" in chart_name.lower(): | |
| # Create sample competitor comparison | |
| fig = go.Figure() | |
| # Market share data | |
| months = pd.date_range(start='2023-01-01', periods=12, freq='M') | |
| drugx_share = [65, 64, 66, 67, 68, 67, 66, 65, 64, 58, 54, 50] | |
| competitor_share = [0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15] | |
| other_share = [35, 36, 34, 33, 32, 33, 34, 35, 36, 37, 36, 35] | |
| fig.add_trace(go.Bar( | |
| x=months, y=drugx_share, name='DrugX', | |
| marker_color='blue' | |
| )) | |
| fig.add_trace(go.Bar( | |
| x=months, y=competitor_share, name='CompDrug2', | |
| marker_color='red' | |
| )) | |
| fig.add_trace(go.Bar( | |
| x=months, y=other_share, name='Others', | |
| marker_color='gray' | |
| )) | |
| fig.update_layout( | |
| title="Market Share Comparison", | |
| xaxis_title="Month", | |
| yaxis_title="Market Share (%)", | |
| barmode='stack', | |
| height=300 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| elif "supply" in chart_name.lower(): | |
| # Create sample supply chain visualization | |
| fig = go.Figure() | |
| # Inventory data | |
| months = pd.date_range(start='2023-01-01', periods=12, freq='M') | |
| inventory = [40, 38, 42, 45, 43, 41, 39, 37, 35, 25, 20, 18] | |
| stockouts = [0, 0, 0, 0, 0, 0, 0, 0, 5, 15, 22, 25] | |
| fig.add_trace(go.Scatter( | |
| x=months, y=inventory, mode='lines+markers', name='Inventory Days', | |
| line=dict(color='blue', width=2) | |
| )) | |
| fig.add_trace(go.Bar( | |
| x=months, y=stockouts, name='Stockout %', | |
| marker_color='red' | |
| )) | |
| fig.update_layout( | |
| title="Supply Chain Metrics", | |
| xaxis_title="Month", | |
| yaxis_title="Inventory Days / Stockout %", | |
| height=300 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| else: | |
| # Create a generic chart | |
| st.image("https://img.icons8.com/fluency/240/000000/graph.png", width=100) | |
| st.markdown(f"*{chart_name}*") | |
| # Display key findings | |
| if hasattr(card, 'key_findings') and card.key_findings: | |
| st.markdown("#### Key Findings") | |
| for i, finding in enumerate(card.key_findings): | |
| expander = st.expander(f"{finding.get('finding', 'Finding')}") | |
| with expander: | |
| st.markdown(f"**Details:** {finding.get('details', '')}") | |
| if 'evidence' in finding: | |
| st.markdown(f"**Evidence:** {finding.get('evidence', '')}") | |
| if 'impact' in finding: | |
| st.markdown(f"**Impact:** {finding.get('impact', '')}") | |
| # Display metrics | |
| if hasattr(card, 'metrics') and card.metrics: | |
| st.markdown("#### Key Metrics") | |
| metric_cols = st.columns(min(len(card.metrics), 4)) | |
| for i, (metric_name, metric_value) in enumerate(card.metrics.items()): | |
| with metric_cols[i % len(metric_cols)]: | |
| st.metric( | |
| label=metric_name.replace('_', ' ').title(), | |
| value=metric_value | |
| ) | |
| # Display action items | |
| if hasattr(card, 'action_items') and card.action_items: | |
| st.markdown("#### Recommended Actions") | |
| for i, action in enumerate(card.action_items): | |
| priority = action.get('priority', 'Medium') | |
| priority_color = { | |
| 'High': 'red', | |
| 'Medium': 'orange', | |
| 'Low': 'blue' | |
| }.get(priority, 'gray') | |
| st.markdown(f"**{i+1}. {action.get('action', 'Action')}** " | |
| f"<span style='color:{priority_color};'>[{priority}]</span>", | |
| unsafe_allow_html=True) | |
| action_cols = st.columns(3) | |
| with action_cols[0]: | |
| st.markdown(f"**Owner:** {action.get('owner', 'TBD')}") | |
| with action_cols[1]: | |
| st.markdown(f"**Timeline:** {action.get('timeline', 'TBD')}") | |
| with action_cols[2]: | |
| st.markdown(f"**Expected Impact:** {action.get('expected_impact', 'TBD')}") | |
| st.markdown("---") | |
| else: | |
| st.warning("No insight cards were generated. There might have been an error in the analysis.") | |
| # Show detailed workflow state information for debugging | |
| with st.expander("Debug: Workflow State", expanded=True): | |
| # Basic workflow information | |
| st.write("### Basic Workflow Information") | |
| st.write(f"Alert: {workflow_state.get('alert', 'Not available')}") | |
| st.write(f"Status: {workflow_state.get('status', 'Unknown')}") | |
| st.write(f"Plan created: {'Yes' if workflow_state.get('plan') else 'No'}") | |
| # Check data sources | |
| data_sources = workflow_state.get('data_sources', {}) | |
| st.write(f"Data sources: {len(data_sources)} found") | |
| if data_sources: | |
| st.write("Data source IDs: " + ", ".join(data_sources.keys())) | |
| # Check analysis results | |
| analysis_results = workflow_state.get('analysis_results', {}) | |
| st.write(f"Analysis results: {len(analysis_results)} found") | |
| if analysis_results: | |
| for key, result in analysis_results.items(): | |
| st.write(f"- Analysis {key}: {result.name if hasattr(result, 'name') else 'Unnamed'}") | |
| if hasattr(result, 'insights') and result.insights: | |
| st.write(f" Insights: {len(result.insights)}") | |
| if hasattr(result, 'attribution') and result.attribution: | |
| st.write(f" Attribution factors: {len(result.attribution)}") | |
| # Check validation results | |
| validation_results = workflow_state.get('validation_results', {}) | |
| st.write(f"Validation results: {len(validation_results)} found") | |
| # Check insight requests | |
| insight_requests = workflow_state.get('insight_requests', []) | |
| st.write(f"Insight requests: {len(insight_requests)} found") | |
| if insight_requests: | |
| for i, req in enumerate(insight_requests): | |
| st.write(f"- Request {i+1}: ID={req.request_id if hasattr(req, 'request_id') else 'Unknown'}") | |
| # Check logs | |
| logs = workflow_state.get('logs', []) | |
| if logs: | |
| st.write("### Workflow Logs") | |
| log_df = pd.DataFrame(logs) | |
| st.dataframe(log_df) | |
| # Show data sources (simplified) | |
| with st.expander("View Data Sources"): | |
| for source_id, source in workflow_state.get("data_sources", {}).items(): | |
| st.markdown(f"**{source.name}**") | |
| st.dataframe(source.content.head(5)) | |
| # Show code details if enabled | |
| if st.session_state.get("show_code", False): | |
| with st.expander("View Generated Code"): | |
| for analysis_id, analysis in workflow_state.get("analysis_results", {}).items(): | |
| st.markdown(f"#### Analysis: {analysis.name}") | |
| st.code(analysis.code, language="python") | |
| # Debug information for insight requests | |
| with st.expander("Debug Insight Requests/Results", expanded=True): | |
| insight_requests = workflow_state.get("insight_requests", []) | |
| insight_cards = workflow_state.get("insight_cards", {}) | |
| # Show insight requests | |
| st.write(f"### Insight Requests: {len(insight_requests)}") | |
| for i, request in enumerate(insight_requests): | |
| st.write(f"#### Request {i+1}: {request.request_id if hasattr(request, 'request_id') else 'Unknown'}") | |
| try: | |
| # Try to display request details | |
| if hasattr(request, 'original_problem'): | |
| st.write(f"Problem: {request.original_problem}") | |
| # Show truncated analysis results | |
| if hasattr(request, 'analysis_results'): | |
| st.write("Analysis Results Keys:") | |
| st.json({k: type(v).__name__ for k, v in request.analysis_results.items()}) | |
| # Show truncated validation results | |
| if hasattr(request, 'validation_results'): | |
| st.write("Validation Results Keys:") | |
| st.json({k: type(v).__name__ for k, v in request.validation_results.items()}) | |
| except Exception as e: | |
| st.error(f"Error displaying request details: {str(e)}") | |
| # Show insight cards | |
| st.write(f"### Insight Cards: {len(insight_cards)}") | |
| for card_id, card in insight_cards.items(): | |
| st.write(f"Card ID: {card_id}") | |
| try: | |
| st.json({ | |
| "title": card.title if hasattr(card, 'title') else "N/A", | |
| "description": card.description[:100] + "..." if hasattr(card, 'description') else "N/A", | |
| "key_findings": f"{len(card.key_findings)} findings" if hasattr(card, 'key_findings') else "N/A", | |
| "charts": card.charts if hasattr(card, 'charts') else "N/A", | |
| }) | |
| except Exception as e: | |
| st.error(f"Error displaying card details: {str(e)}") | |
| def initialize_session(): | |
| """Initialize session state variables if they don't exist""" | |
| if "active_page" not in st.session_state: | |
| st.session_state.active_page = "main" | |
| if "workflow_state" not in st.session_state: | |
| st.session_state.workflow_state = None | |
| if "workflow_thread" not in st.session_state: | |
| st.session_state.workflow_thread = None | |
| if "status_queue" not in st.session_state: | |
| st.session_state.status_queue = queue.Queue() | |
| if "logs" not in st.session_state: | |
| st.session_state.logs = [] | |
| if "current_step" not in st.session_state: | |
| st.session_state.current_step = None | |
| if "alert_submitted" not in st.session_state: | |
| st.session_state.alert_submitted = False | |
| if "show_sql" not in st.session_state: | |
| st.session_state.show_sql = False | |
| if "show_code" not in st.session_state: | |
| st.session_state.show_code = False | |
| if "alert" not in st.session_state: | |
| st.session_state.alert = "" | |
| if "error_details" not in st.session_state: | |
| st.session_state.error_details = [] |