Update app/streamlit_app.py
Browse files- app/streamlit_app.py +2 -422
    	
        app/streamlit_app.py
    CHANGED
    
    | @@ -279,116 +279,6 @@ class StreamlitAppManager: | |
| 279 | 
             
                        logger.warning(f"Could not fetch traffic status: {e}")
         | 
| 280 | 
             
                        return None
         | 
| 281 |  | 
| 282 | 
            -
                def trigger_manual_operation(self, operation_type: str, parameters: dict = None, async_execution: bool = True) -> dict:
         | 
| 283 | 
            -
                    """Trigger a manual operation via API"""
         | 
| 284 | 
            -
                    try:
         | 
| 285 | 
            -
                        if not self.api_available:
         | 
| 286 | 
            -
                            return {'error': 'API not available'}
         | 
| 287 | 
            -
                        
         | 
| 288 | 
            -
                        request_data = {
         | 
| 289 | 
            -
                            'operation_type': operation_type,
         | 
| 290 | 
            -
                            'parameters': parameters or {},
         | 
| 291 | 
            -
                            'async_execution': async_execution,
         | 
| 292 | 
            -
                            'priority': 5,
         | 
| 293 | 
            -
                            'timeout_seconds': 300
         | 
| 294 | 
            -
                        }
         | 
| 295 | 
            -
                        
         | 
| 296 | 
            -
                        response = self.session.post(
         | 
| 297 | 
            -
                            f"{self.config['api_url']}/operations/trigger",
         | 
| 298 | 
            -
                            json=request_data,
         | 
| 299 | 
            -
                            timeout=30
         | 
| 300 | 
            -
                        )
         | 
| 301 | 
            -
                        
         | 
| 302 | 
            -
                        if response.status_code == 200:
         | 
| 303 | 
            -
                            return response.json()
         | 
| 304 | 
            -
                        else:
         | 
| 305 | 
            -
                            return {'error': f'API Error: {response.status_code} - {response.text}'}
         | 
| 306 | 
            -
                    
         | 
| 307 | 
            -
                    except Exception as e:
         | 
| 308 | 
            -
                        return {'error': f'Request failed: {str(e)}'}
         | 
| 309 | 
            -
                
         | 
| 310 | 
            -
                def get_operation_status(self, operation_id: str) -> dict:
         | 
| 311 | 
            -
                    """Get status of a triggered operation"""
         | 
| 312 | 
            -
                    try:
         | 
| 313 | 
            -
                        if not self.api_available:
         | 
| 314 | 
            -
                            return {'error': 'API not available'}
         | 
| 315 | 
            -
                        
         | 
| 316 | 
            -
                        response = self.session.get(
         | 
| 317 | 
            -
                            f"{self.config['api_url']}/operations/{operation_id}/status",
         | 
| 318 | 
            -
                            timeout=10
         | 
| 319 | 
            -
                        )
         | 
| 320 | 
            -
                        
         | 
| 321 | 
            -
                        if response.status_code == 200:
         | 
| 322 | 
            -
                            return response.json()
         | 
| 323 | 
            -
                        else:
         | 
| 324 | 
            -
                            return {'error': f'API Error: {response.status_code}'}
         | 
| 325 | 
            -
                    
         | 
| 326 | 
            -
                    except Exception as e:
         | 
| 327 | 
            -
                        return {'error': f'Request failed: {str(e)}'}
         | 
| 328 | 
            -
                
         | 
| 329 | 
            -
                def list_operations(self, status_filter: str = None, limit: int = 20) -> dict:
         | 
| 330 | 
            -
                    """List recent operations"""
         | 
| 331 | 
            -
                    try:
         | 
| 332 | 
            -
                        if not self.api_available:
         | 
| 333 | 
            -
                            return {'error': 'API not available'}
         | 
| 334 | 
            -
                        
         | 
| 335 | 
            -
                        params = {'limit': limit}
         | 
| 336 | 
            -
                        if status_filter:
         | 
| 337 | 
            -
                            params['status'] = status_filter
         | 
| 338 | 
            -
                        
         | 
| 339 | 
            -
                        response = self.session.get(
         | 
| 340 | 
            -
                            f"{self.config['api_url']}/operations",
         | 
| 341 | 
            -
                            params=params,
         | 
| 342 | 
            -
                            timeout=10
         | 
| 343 | 
            -
                        )
         | 
| 344 | 
            -
                        
         | 
| 345 | 
            -
                        if response.status_code == 200:
         | 
| 346 | 
            -
                            return {'operations': response.json()}
         | 
| 347 | 
            -
                        else:
         | 
| 348 | 
            -
                            return {'error': f'API Error: {response.status_code}'}
         | 
| 349 | 
            -
                    
         | 
| 350 | 
            -
                    except Exception as e:
         | 
| 351 | 
            -
                        return {'error': f'Request failed: {str(e)}'}
         | 
| 352 | 
            -
                
         | 
| 353 | 
            -
                def get_available_operation_types(self) -> dict:
         | 
| 354 | 
            -
                    """Get available operation types"""
         | 
| 355 | 
            -
                    try:
         | 
| 356 | 
            -
                        if not self.api_available:
         | 
| 357 | 
            -
                            return {'error': 'API not available'}
         | 
| 358 | 
            -
                        
         | 
| 359 | 
            -
                        response = self.session.get(
         | 
| 360 | 
            -
                            f"{self.config['api_url']}/operations/types",
         | 
| 361 | 
            -
                            timeout=10
         | 
| 362 | 
            -
                        )
         | 
| 363 | 
            -
                        
         | 
| 364 | 
            -
                        if response.status_code == 200:
         | 
| 365 | 
            -
                            return response.json()
         | 
| 366 | 
            -
                        else:
         | 
| 367 | 
            -
                            return {'error': f'API Error: {response.status_code}'}
         | 
| 368 | 
            -
                    
         | 
| 369 | 
            -
                    except Exception as e:
         | 
| 370 | 
            -
                        return {'error': f'Request failed: {str(e)}'}
         | 
| 371 | 
            -
                
         | 
| 372 | 
            -
                def cancel_operation(self, operation_id: str) -> dict:
         | 
| 373 | 
            -
                    """Cancel a pending operation"""
         | 
| 374 | 
            -
                    try:
         | 
| 375 | 
            -
                        if not self.api_available:
         | 
| 376 | 
            -
                            return {'error': 'API not available'}
         | 
| 377 | 
            -
                        
         | 
| 378 | 
            -
                        response = self.session.post(
         | 
| 379 | 
            -
                            f"{self.config['api_url']}/operations/{operation_id}/cancel",
         | 
| 380 | 
            -
                            timeout=10
         | 
| 381 | 
            -
                        )
         | 
| 382 | 
            -
                        
         | 
| 383 | 
            -
                        if response.status_code == 200:
         | 
| 384 | 
            -
                            return response.json()
         | 
| 385 | 
            -
                        else:
         | 
| 386 | 
            -
                            return {'error': f'API Error: {response.status_code}'}
         | 
| 387 | 
            -
                    
         | 
| 388 | 
            -
                    except Exception as e:
         | 
| 389 | 
            -
                        return {'error': f'Request failed: {str(e)}'}
         | 
| 390 | 
            -
             | 
| 391 | 
            -
             | 
| 392 |  | 
| 393 | 
             
            # Initialize app manager
         | 
| 394 | 
             
            app_manager = StreamlitAppManager()
         | 
| @@ -1165,15 +1055,14 @@ def main(): | |
| 1165 | 
             
                            '<div class="error-message">🔴 API Service: Offline</div>', unsafe_allow_html=True)
         | 
| 1166 |  | 
| 1167 | 
             
                # Main content area
         | 
| 1168 | 
            -
                tab1, tab2, tab3, tab4, tab5, tab6, tab7 | 
| 1169 | 
             
                    "🔍 Prediction",
         | 
| 1170 | 
             
                    "📊 Batch Analysis", 
         | 
| 1171 | 
             
                    "📈 Analytics",
         | 
| 1172 | 
             
                    "🎯 Model Training",
         | 
| 1173 | 
             
                    "📋 Logs",
         | 
| 1174 | 
             
                    "⚙️ System Status",
         | 
| 1175 | 
            -
                    "📊 Monitoring" | 
| 1176 | 
            -
                    "⚙️ Manual Triggers"
         | 
| 1177 | 
             
                ])
         | 
| 1178 |  | 
| 1179 |  | 
| @@ -1704,11 +1593,6 @@ def main(): | |
| 1704 | 
             
                    st.divider()
         | 
| 1705 | 
             
                    render_deployment_status()
         | 
| 1706 |  | 
| 1707 | 
            -
                
         | 
| 1708 | 
            -
                # Tab 8: Manual Triggers
         | 
| 1709 | 
            -
                 render_manual_triggers_tab()
         | 
| 1710 | 
            -
             | 
| 1711 | 
            -
             | 
| 1712 | 
             
            def render_system_status():
         | 
| 1713 | 
             
                """Render system status tab"""
         | 
| 1714 | 
             
                st.header("System Status & Monitoring")
         | 
| @@ -1933,310 +1817,6 @@ def render_deployment_status(): | |
| 1933 | 
             
                else:
         | 
| 1934 | 
             
                    st.warning("Deployment status not available")
         | 
| 1935 |  | 
| 1936 | 
            -
             | 
| 1937 | 
            -
            # Manual Triggering Endpoint UI
         | 
| 1938 | 
            -
            def render_manual_triggers_tab():
         | 
| 1939 | 
            -
                """Render the manual triggers tab"""
         | 
| 1940 | 
            -
                st.header("⚙️ Manual System Operations")
         | 
| 1941 | 
            -
                
         | 
| 1942 | 
            -
                # Quick status check
         | 
| 1943 | 
            -
                col1, col2 = st.columns([3, 1])
         | 
| 1944 | 
            -
                
         | 
| 1945 | 
            -
                with col1:
         | 
| 1946 | 
            -
                    if st.session_state.get('last_operation_check'):
         | 
| 1947 | 
            -
                        last_check = st.session_state.last_operation_check
         | 
| 1948 | 
            -
                        st.info(f"Last operation check: {last_check}")
         | 
| 1949 | 
            -
                    else:
         | 
| 1950 | 
            -
                        st.info("No recent operations")
         | 
| 1951 | 
            -
                
         | 
| 1952 | 
            -
                with col2:
         | 
| 1953 | 
            -
                    if st.button("🔄 Refresh Status", use_container_width=True):
         | 
| 1954 | 
            -
                        st.session_state.last_operation_check = datetime.now().strftime('%H:%M:%S')
         | 
| 1955 | 
            -
                        st.rerun()
         | 
| 1956 | 
            -
                
         | 
| 1957 | 
            -
                # Initialize session state for operations
         | 
| 1958 | 
            -
                if 'triggered_operations' not in st.session_state:
         | 
| 1959 | 
            -
                    st.session_state.triggered_operations = {}
         | 
| 1960 | 
            -
                
         | 
| 1961 | 
            -
                # Operation trigger section
         | 
| 1962 | 
            -
                st.subheader("🚀 Available Operations")
         | 
| 1963 | 
            -
                
         | 
| 1964 | 
            -
                # Get available operation types
         | 
| 1965 | 
            -
                operation_types_data = app_manager.get_available_operation_types()
         | 
| 1966 | 
            -
                
         | 
| 1967 | 
            -
                if 'error' in operation_types_data:
         | 
| 1968 | 
            -
                    st.error(f"Could not load operation types: {operation_types_data['error']}")
         | 
| 1969 | 
            -
                    return
         | 
| 1970 | 
            -
                
         | 
| 1971 | 
            -
                available_ops = operation_types_data.get('available_operations', {})
         | 
| 1972 | 
            -
                
         | 
| 1973 | 
            -
                # Create operation trigger interface
         | 
| 1974 | 
            -
                col1, col2 = st.columns([2, 1])
         | 
| 1975 | 
            -
                
         | 
| 1976 | 
            -
                with col1:
         | 
| 1977 | 
            -
                    # Operation selection
         | 
| 1978 | 
            -
                    operation_names = {
         | 
| 1979 | 
            -
                        'data_validation': 'Data Validation',
         | 
| 1980 | 
            -
                        'model_health_check': 'Model Health Check',
         | 
| 1981 | 
            -
                        'system_cleanup': 'System Cleanup',
         | 
| 1982 | 
            -
                        'metrics_export': 'Metrics Export',
         | 
| 1983 | 
            -
                        'log_rotation': 'Log Rotation',
         | 
| 1984 | 
            -
                        'cache_refresh': 'Cache Refresh',
         | 
| 1985 | 
            -
                        'data_quality_audit': 'Data Quality Audit',
         | 
| 1986 | 
            -
                        'prediction_batch_test': 'Prediction Batch Test'
         | 
| 1987 | 
            -
                    }
         | 
| 1988 | 
            -
                    
         | 
| 1989 | 
            -
                    selected_operation = st.selectbox(
         | 
| 1990 | 
            -
                        "Select Operation:",
         | 
| 1991 | 
            -
                        options=list(operation_names.keys()),
         | 
| 1992 | 
            -
                        format_func=lambda x: operation_names[x],
         | 
| 1993 | 
            -
                        index=0
         | 
| 1994 | 
            -
                    )
         | 
| 1995 | 
            -
                    
         | 
| 1996 | 
            -
                    # Show operation description
         | 
| 1997 | 
            -
                    if selected_operation in available_ops:
         | 
| 1998 | 
            -
                        op_info = available_ops[selected_operation]
         | 
| 1999 | 
            -
                        st.info(f"**Description:** {op_info['description']}")
         | 
| 2000 | 
            -
                        st.caption(f"**Typical Duration:** {op_info['typical_duration']}")
         | 
| 2001 | 
            -
                
         | 
| 2002 | 
            -
                with col2:
         | 
| 2003 | 
            -
                    # Execution mode
         | 
| 2004 | 
            -
                    async_mode = st.checkbox("Async Execution", value=True, 
         | 
| 2005 | 
            -
                                            help="Run operation in background (recommended)")
         | 
| 2006 | 
            -
                    
         | 
| 2007 | 
            -
                    # Priority setting
         | 
| 2008 | 
            -
                    priority = st.selectbox("Priority", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 
         | 
| 2009 | 
            -
                                           index=4, help="1 = Highest, 10 = Lowest")
         | 
| 2010 | 
            -
                
         | 
| 2011 | 
            -
                # Operation-specific parameters
         | 
| 2012 | 
            -
                st.subheader("⚙️ Operation Parameters")
         | 
| 2013 | 
            -
                parameters = {}
         | 
| 2014 | 
            -
                
         | 
| 2015 | 
            -
                if selected_operation == 'data_validation':
         | 
| 2016 | 
            -
                    parameters['include_integrity_check'] = st.checkbox("Include Integrity Check", value=True)
         | 
| 2017 | 
            -
                
         | 
| 2018 | 
            -
                elif selected_operation == 'model_health_check':
         | 
| 2019 | 
            -
                    parameters['test_count'] = st.number_input("Number of Test Predictions", 
         | 
| 2020 | 
            -
                                                              min_value=1, max_value=20, value=4)
         | 
| 2021 | 
            -
                
         | 
| 2022 | 
            -
                elif selected_operation == 'system_cleanup':
         | 
| 2023 | 
            -
                    parameters['clean_cache'] = st.checkbox("Clean Cache Files", value=False)
         | 
| 2024 | 
            -
                
         | 
| 2025 | 
            -
                elif selected_operation == 'metrics_export':
         | 
| 2026 | 
            -
                    parameters['format'] = st.selectbox("Export Format", options=['json'], index=0)
         | 
| 2027 | 
            -
                    parameters['include_raw_data'] = st.checkbox("Include Raw Data", value=False)
         | 
| 2028 | 
            -
                
         | 
| 2029 | 
            -
                elif selected_operation == 'log_rotation':
         | 
| 2030 | 
            -
                    col_a, col_b = st.columns(2)
         | 
| 2031 | 
            -
                    with col_a:
         | 
| 2032 | 
            -
                        parameters['max_file_size_mb'] = st.number_input("Max File Size (MB)", 
         | 
| 2033 | 
            -
                                                                       min_value=1, max_value=100, value=10)
         | 
| 2034 | 
            -
                    with col_b:
         | 
| 2035 | 
            -
                        parameters['max_files_per_type'] = st.number_input("Max Files to Keep", 
         | 
| 2036 | 
            -
                                                                         min_value=1, max_value=20, value=5)
         | 
| 2037 | 
            -
                
         | 
| 2038 | 
            -
                elif selected_operation == 'cache_refresh':
         | 
| 2039 | 
            -
                    parameters['regenerate_cache'] = st.checkbox("Regenerate Cache", value=True)
         | 
| 2040 | 
            -
                
         | 
| 2041 | 
            -
                elif selected_operation == 'prediction_batch_test':
         | 
| 2042 | 
            -
                    col_a, col_b = st.columns(2)
         | 
| 2043 | 
            -
                    with col_a:
         | 
| 2044 | 
            -
                        parameters['num_tests'] = st.number_input("Number of Tests", 
         | 
| 2045 | 
            -
                                                                min_value=1, max_value=50, value=10)
         | 
| 2046 | 
            -
                    with col_b:
         | 
| 2047 | 
            -
                        parameters['test_timeout'] = st.number_input("Test Timeout (seconds)", 
         | 
| 2048 | 
            -
                                                                   min_value=10, max_value=120, value=30)
         | 
| 2049 | 
            -
                
         | 
| 2050 | 
            -
                # Trigger button
         | 
| 2051 | 
            -
                st.divider()
         | 
| 2052 | 
            -
                
         | 
| 2053 | 
            -
                col1, col2, col3 = st.columns([2, 1, 1])
         | 
| 2054 | 
            -
                
         | 
| 2055 | 
            -
                with col1:
         | 
| 2056 | 
            -
                    if st.button(f"▶️ Execute {operation_names[selected_operation]}", 
         | 
| 2057 | 
            -
                                 type="primary", use_container_width=True):
         | 
| 2058 | 
            -
                        
         | 
| 2059 | 
            -
                        with st.spinner(f"Triggering {operation_names[selected_operation]}..."):
         | 
| 2060 | 
            -
                            result = app_manager.trigger_manual_operation(
         | 
| 2061 | 
            -
                                operation_type=selected_operation,
         | 
| 2062 | 
            -
                                parameters=parameters,
         | 
| 2063 | 
            -
                                async_execution=async_mode
         | 
| 2064 | 
            -
                            )
         | 
| 2065 | 
            -
                        
         | 
| 2066 | 
            -
                        if 'error' in result:
         | 
| 2067 | 
            -
                            st.error(f"Failed to trigger operation: {result['error']}")
         | 
| 2068 | 
            -
                        else:
         | 
| 2069 | 
            -
                            operation_id = result['operation_id']
         | 
| 2070 | 
            -
                            st.session_state.triggered_operations[operation_id] = {
         | 
| 2071 | 
            -
                                'operation_type': selected_operation,
         | 
| 2072 | 
            -
                                'operation_name': operation_names[selected_operation],
         | 
| 2073 | 
            -
                                'triggered_at': datetime.now().isoformat(),
         | 
| 2074 | 
            -
                                'status': result['status'],
         | 
| 2075 | 
            -
                                'async_mode': async_mode
         | 
| 2076 | 
            -
                            }
         | 
| 2077 | 
            -
                            
         | 
| 2078 | 
            -
                            st.success(f"✅ Operation triggered successfully!")
         | 
| 2079 | 
            -
                            st.info(f"**Operation ID:** {operation_id}")
         | 
| 2080 | 
            -
                            st.info(f"**Status:** {result['status']}")
         | 
| 2081 | 
            -
                            st.info(f"**Estimated Duration:** {result['estimated_duration']}")
         | 
| 2082 | 
            -
                            
         | 
| 2083 | 
            -
                            if async_mode:
         | 
| 2084 | 
            -
                                st.info("Operation is running in the background. Check status below.")
         | 
| 2085 | 
            -
                
         | 
| 2086 | 
            -
                with col2:
         | 
| 2087 | 
            -
                    # Quick operation buttons
         | 
| 2088 | 
            -
                    st.write("**Quick Actions:**")
         | 
| 2089 | 
            -
                    if st.button("🔍 Health Check", use_container_width=True):
         | 
| 2090 | 
            -
                        result = app_manager.trigger_manual_operation('model_health_check', {}, True)
         | 
| 2091 | 
            -
                        if 'operation_id' in result:
         | 
| 2092 | 
            -
                            st.success(f"Health check started: {result['operation_id'][:8]}...")
         | 
| 2093 | 
            -
                
         | 
| 2094 | 
            -
                with col3:
         | 
| 2095 | 
            -
                    if st.button("🧹 Quick Cleanup", use_container_width=True):
         | 
| 2096 | 
            -
                        result = app_manager.trigger_manual_operation('system_cleanup', {'clean_cache': False}, True)
         | 
| 2097 | 
            -
                        if 'operation_id' in result:
         | 
| 2098 | 
            -
                            st.success(f"Cleanup started: {result['operation_id'][:8]}...")
         | 
| 2099 | 
            -
                
         | 
| 2100 | 
            -
                # Operation status monitoring
         | 
| 2101 | 
            -
                st.divider()
         | 
| 2102 | 
            -
                st.subheader("📊 Operation Status Monitor")
         | 
| 2103 | 
            -
                
         | 
| 2104 | 
            -
                # Recent operations from session state
         | 
| 2105 | 
            -
                if st.session_state.triggered_operations:
         | 
| 2106 | 
            -
                    st.write("**Recent Operations (This Session):**")
         | 
| 2107 | 
            -
                    
         | 
| 2108 | 
            -
                    for op_id, op_info in list(st.session_state.triggered_operations.items())[-5:]:
         | 
| 2109 | 
            -
                        with st.expander(f"{op_info['operation_name']} - {op_id[:8]}... ({op_info['status']})"):
         | 
| 2110 | 
            -
                            col1, col2 = st.columns([3, 1])
         | 
| 2111 | 
            -
                            
         | 
| 2112 | 
            -
                            with col1:
         | 
| 2113 | 
            -
                                st.write(f"**Operation:** {op_info['operation_name']}")
         | 
| 2114 | 
            -
                                st.write(f"**ID:** {op_id}")
         | 
| 2115 | 
            -
                                st.write(f"**Triggered:** {op_info['triggered_at']}")
         | 
| 2116 | 
            -
                                st.write(f"**Mode:** {'Async' if op_info['async_mode'] else 'Sync'}")
         | 
| 2117 | 
            -
                            
         | 
| 2118 | 
            -
                            with col2:
         | 
| 2119 | 
            -
                                if st.button(f"🔄 Check Status", key=f"check_{op_id}"):
         | 
| 2120 | 
            -
                                    status_result = app_manager.get_operation_status(op_id)
         | 
| 2121 | 
            -
                                    
         | 
| 2122 | 
            -
                                    if 'error' in status_result:
         | 
| 2123 | 
            -
                                        st.error(f"Status check failed: {status_result['error']}")
         | 
| 2124 | 
            -
                                    else:
         | 
| 2125 | 
            -
                                        # Update session state
         | 
| 2126 | 
            -
                                        st.session_state.triggered_operations[op_id]['status'] = status_result['status']
         | 
| 2127 | 
            -
                                        
         | 
| 2128 | 
            -
                                        # Display detailed status
         | 
| 2129 | 
            -
                                        st.json(status_result)
         | 
| 2130 | 
            -
                                        
         | 
| 2131 | 
            -
                                        # Show results if completed
         | 
| 2132 | 
            -
                                        if status_result['status'] == 'completed' and status_result.get('result_data'):
         | 
| 2133 | 
            -
                                            st.success("✅ Operation completed successfully!")
         | 
| 2134 | 
            -
                                            with st.expander("📋 Results"):
         | 
| 2135 | 
            -
                                                st.json(status_result['result_data'])
         | 
| 2136 | 
            -
                                        
         | 
| 2137 | 
            -
                                        elif status_result['status'] == 'failed':
         | 
| 2138 | 
            -
                                            st.error("❌ Operation failed!")
         | 
| 2139 | 
            -
                                            if status_result.get('error_message'):
         | 
| 2140 | 
            -
                                                st.error(f"Error: {status_result['error_message']}")
         | 
| 2141 | 
            -
                
         | 
| 2142 | 
            -
                # All operations from API
         | 
| 2143 | 
            -
                st.subheader("📋 All Recent Operations")
         | 
| 2144 | 
            -
                
         | 
| 2145 | 
            -
                col1, col2, col3 = st.columns(3)
         | 
| 2146 | 
            -
                
         | 
| 2147 | 
            -
                with col1:
         | 
| 2148 | 
            -
                    status_filter = st.selectbox(
         | 
| 2149 | 
            -
                        "Filter by Status:",
         | 
| 2150 | 
            -
                        options=[None, "pending", "running", "completed", "failed", "cancelled"],
         | 
| 2151 | 
            -
                        format_func=lambda x: "All" if x is None else x.title()
         | 
| 2152 | 
            -
                    )
         | 
| 2153 | 
            -
                
         | 
| 2154 | 
            -
                with col2:
         | 
| 2155 | 
            -
                    limit = st.number_input("Max Results", min_value=5, max_value=50, value=20)
         | 
| 2156 | 
            -
                
         | 
| 2157 | 
            -
                with col3:
         | 
| 2158 | 
            -
                    if st.button("🔍 Load Operations", use_container_width=True):
         | 
| 2159 | 
            -
                        operations_result = app_manager.list_operations(status_filter, limit)
         | 
| 2160 | 
            -
                        
         | 
| 2161 | 
            -
                        if 'error' in operations_result:
         | 
| 2162 | 
            -
                            st.error(f"Failed to load operations: {operations_result['error']}")
         | 
| 2163 | 
            -
                        else:
         | 
| 2164 | 
            -
                            operations = operations_result.get('operations', [])
         | 
| 2165 | 
            -
                            
         | 
| 2166 | 
            -
                            if operations:
         | 
| 2167 | 
            -
                                st.success(f"Loaded {len(operations)} operations")
         | 
| 2168 | 
            -
                                
         | 
| 2169 | 
            -
                                # Create operations dataframe
         | 
| 2170 | 
            -
                                ops_data = []
         | 
| 2171 | 
            -
                                for op in operations:
         | 
| 2172 | 
            -
                                    ops_data.append({
         | 
| 2173 | 
            -
                                        'ID': op['operation_id'][:8] + '...',
         | 
| 2174 | 
            -
                                        'Type': op['operation_type'].replace('_', ' ').title(),
         | 
| 2175 | 
            -
                                        'Status': op['status'].title(),
         | 
| 2176 | 
            -
                                        'Started': op['started_at'][:19] if op['started_at'] else 'N/A',
         | 
| 2177 | 
            -
                                        'Duration': f"{op['duration_seconds']:.1f}s" if op.get('duration_seconds') else 'N/A',
         | 
| 2178 | 
            -
                                        'Full ID': op['operation_id']
         | 
| 2179 | 
            -
                                    })
         | 
| 2180 | 
            -
                                
         | 
| 2181 | 
            -
                                df = pd.DataFrame(ops_data)
         | 
| 2182 | 
            -
                                
         | 
| 2183 | 
            -
                                # Display operations table
         | 
| 2184 | 
            -
                                st.dataframe(df[['ID', 'Type', 'Status', 'Started', 'Duration']], 
         | 
| 2185 | 
            -
                                           use_container_width=True)
         | 
| 2186 | 
            -
                                
         | 
| 2187 | 
            -
                                # Detailed view for selected operation
         | 
| 2188 | 
            -
                                selected_short_id = st.selectbox(
         | 
| 2189 | 
            -
                                    "View Details for Operation:",
         | 
| 2190 | 
            -
                                    options=df['ID'].tolist(),
         | 
| 2191 | 
            -
                                    index=0 if df['ID'].tolist() else None
         | 
| 2192 | 
            -
                                )
         | 
| 2193 | 
            -
                                
         | 
| 2194 | 
            -
                                if selected_short_id:
         | 
| 2195 | 
            -
                                    selected_full_id = df[df['ID'] == selected_short_id]['Full ID'].iloc[0]
         | 
| 2196 | 
            -
                                    selected_op = next(op for op in operations if op['operation_id'] == selected_full_id)
         | 
| 2197 | 
            -
                                    
         | 
| 2198 | 
            -
                                    st.subheader(f"📄 Operation Details: {selected_short_id}")
         | 
| 2199 | 
            -
                                    
         | 
| 2200 | 
            -
                                    col_detail1, col_detail2 = st.columns(2)
         | 
| 2201 | 
            -
                                    
         | 
| 2202 | 
            -
                                    with col_detail1:
         | 
| 2203 | 
            -
                                        st.write(f"**Full ID:** {selected_op['operation_id']}")
         | 
| 2204 | 
            -
                                        st.write(f"**Type:** {selected_op['operation_type']}")
         | 
| 2205 | 
            -
                                        st.write(f"**Status:** {selected_op['status']}")
         | 
| 2206 | 
            -
                                        st.write(f"**Started:** {selected_op['started_at']}")
         | 
| 2207 | 
            -
                                    
         | 
| 2208 | 
            -
                                    with col_detail2:
         | 
| 2209 | 
            -
                                        if selected_op.get('completed_at'):
         | 
| 2210 | 
            -
                                            st.write(f"**Completed:** {selected_op['completed_at']}")
         | 
| 2211 | 
            -
                                        if selected_op.get('duration_seconds'):
         | 
| 2212 | 
            -
                                            st.write(f"**Duration:** {selected_op['duration_seconds']:.2f} seconds")
         | 
| 2213 | 
            -
                                        if selected_op.get('error_message'):
         | 
| 2214 | 
            -
                                            st.error(f"**Error:** {selected_op['error_message']}")
         | 
| 2215 | 
            -
                                    
         | 
| 2216 | 
            -
                                    # Show logs
         | 
| 2217 | 
            -
                                    if selected_op.get('logs'):
         | 
| 2218 | 
            -
                                        with st.expander("📜 Operation Logs"):
         | 
| 2219 | 
            -
                                            for log_entry in selected_op['logs']:
         | 
| 2220 | 
            -
                                                st.text(log_entry)
         | 
| 2221 | 
            -
                                    
         | 
| 2222 | 
            -
                                    # Show results
         | 
| 2223 | 
            -
                                    if selected_op.get('result_data'):
         | 
| 2224 | 
            -
                                        with st.expander("📊 Results Data"):
         | 
| 2225 | 
            -
                                            st.json(selected_op['result_data'])
         | 
| 2226 | 
            -
                                    
         | 
| 2227 | 
            -
                                    # Action buttons
         | 
| 2228 | 
            -
                                    if selected_op['status'] == 'pending':
         | 
| 2229 | 
            -
                                        if st.button(f"❌ Cancel Operation", key=f"cancel_{selected_full_id}"):
         | 
| 2230 | 
            -
                                            cancel_result = app_manager.cancel_operation(selected_full_id)
         | 
| 2231 | 
            -
                                            if 'error' in cancel_result:
         | 
| 2232 | 
            -
                                                st.error(f"Cancel failed: {cancel_result['error']}")
         | 
| 2233 | 
            -
                                            else:
         | 
| 2234 | 
            -
                                                st.success("Operation cancelled successfully!")
         | 
| 2235 | 
            -
                                                st.rerun()
         | 
| 2236 | 
            -
                            else:
         | 
| 2237 | 
            -
                                st.info("No operations found")
         | 
| 2238 | 
            -
             | 
| 2239 | 
            -
             | 
| 2240 | 
             
            # Auto-refresh logic
         | 
| 2241 | 
             
            if st.session_state.auto_refresh:
         | 
| 2242 | 
             
                time_since_refresh = datetime.now() - st.session_state.last_refresh
         | 
|  | |
| 279 | 
             
                        logger.warning(f"Could not fetch traffic status: {e}")
         | 
| 280 | 
             
                        return None
         | 
| 281 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 282 |  | 
| 283 | 
             
            # Initialize app manager
         | 
| 284 | 
             
            app_manager = StreamlitAppManager()
         | 
|  | |
| 1055 | 
             
                            '<div class="error-message">🔴 API Service: Offline</div>', unsafe_allow_html=True)
         | 
| 1056 |  | 
| 1057 | 
             
                # Main content area
         | 
| 1058 | 
            +
                tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs([
         | 
| 1059 | 
             
                    "🔍 Prediction",
         | 
| 1060 | 
             
                    "📊 Batch Analysis", 
         | 
| 1061 | 
             
                    "📈 Analytics",
         | 
| 1062 | 
             
                    "🎯 Model Training",
         | 
| 1063 | 
             
                    "📋 Logs",
         | 
| 1064 | 
             
                    "⚙️ System Status",
         | 
| 1065 | 
            +
                    "📊 Monitoring"  # New monitoring tab
         | 
|  | |
| 1066 | 
             
                ])
         | 
| 1067 |  | 
| 1068 |  | 
|  | |
| 1593 | 
             
                    st.divider()
         | 
| 1594 | 
             
                    render_deployment_status()
         | 
| 1595 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 1596 | 
             
            def render_system_status():
         | 
| 1597 | 
             
                """Render system status tab"""
         | 
| 1598 | 
             
                st.header("System Status & Monitoring")
         | 
|  | |
| 1817 | 
             
                else:
         | 
| 1818 | 
             
                    st.warning("Deployment status not available")
         | 
| 1819 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1820 | 
             
            # Auto-refresh logic
         | 
| 1821 | 
             
            if st.session_state.auto_refresh:
         | 
| 1822 | 
             
                time_since_refresh = datetime.now() - st.session_state.last_refresh
         |