""" File upload component """ import dash from dash import dcc, html import dash_bootstrap_components as dbc def create_upload_component(): """ Create the file upload component with drag-and-drop Returns: Dash component """ return dbc.Card([ dbc.CardHeader(html.H5("Data Input", className="mb-0")), dbc.CardBody([ dcc.Upload( id='upload-data', children=html.Div([ html.I(className="fas fa-cloud-upload-alt fa-3x mb-3"), html.H5('Drag and Drop or Click to Select File'), html.P('Supported formats: CSV, Excel (max 100MB)', className='text-muted') ]), style={ 'width': '100%', 'height': '150px', 'lineHeight': '150px', 'borderWidth': '2px', 'borderStyle': 'dashed', 'borderRadius': '10px', 'textAlign': 'center', 'backgroundColor': '#f8f9fa' }, multiple=False ), html.Div(id='upload-status', className='mt-3'), ]) ], className='mb-4') def create_column_selector(): """ Create column mapping dropdowns with support for multivariate and covariate-informed forecasting Returns: Dash component """ return dbc.Card([ dbc.CardHeader(html.H5("Data Configuration", className="mb-0")), dbc.CardBody([ # Forecasting Mode Selector dbc.Row([ dbc.Col([ dbc.Label("Forecasting Mode", className="fw-bold"), dcc.RadioItems( id='forecasting-mode', options=[ {'label': ' Univariate (Single target)', 'value': 'univariate'}, {'label': ' Multivariate (Multiple targets)', 'value': 'multivariate'}, {'label': ' Covariate-informed (With external variables)', 'value': 'covariate'} ], value='univariate', className='mt-2', labelStyle={'display': 'block', 'marginBottom': '8px'} ), html.Small("Chronos-2 supports all three modes with zero-shot learning", className='text-muted') ], md=12), ], className='mb-3'), html.Hr(), # Column Selection dbc.Row([ dbc.Col([ dbc.Label("Date/Timestamp Column"), dcc.Dropdown( id='date-column-dropdown', placeholder='Select date column...', clearable=False ) ], md=4), dbc.Col([ dbc.Label("Target Variable(s)"), dcc.Dropdown( id='target-column-dropdown', placeholder='Select target column(s)...', clearable=False, multi=True # Enable multi-select ), html.Small(id='target-help-text', className='text-muted') ], md=4), dbc.Col([ dbc.Label("ID Column (Optional)"), dcc.Dropdown( id='id-column-dropdown', placeholder='Select ID column (optional)...', clearable=True ), html.Small("For multiple time series", className='text-muted') ], md=4), ], className='mb-3'), # Covariate Selection (shown only for covariate-informed mode) html.Div([ dbc.Row([ dbc.Col([ dbc.Label("Covariate Columns"), dcc.Dropdown( id='covariate-columns-dropdown', placeholder='Select covariate column(s)...', clearable=True, multi=True ), html.Small("External variables that may influence the forecast", className='text-muted') ], md=12), ], className='mb-3'), ], id='covariate-section', style={'display': 'none'}), html.Hr(), html.Div(id='data-preview-container', className='mt-3'), html.Div(id='data-quality-report', className='mt-3') ]) ], className='mb-4', id='column-selector-card', style={'display': 'none'}) def create_sample_data_loader(): """ Create sample data loader component Returns: Dash component """ return dbc.Card([ dbc.CardBody([ html.H6("Quick Start with Sample Data"), dbc.Row([ dbc.Col([ dbc.Button( "Weather Stations", id='load-weather', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), dbc.Col([ dbc.Button( "Air Quality UCI", id='load-airquality', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), dbc.Col([ dbc.Button( "Bitcoin Price", id='load-bitcoin', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), ]), dbc.Row([ dbc.Col([ dbc.Button( "S&P 500 Stock", id='load-stock', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), dbc.Col([ dbc.Button( "Traffic Speeds", id='load-traffic', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), dbc.Col([ dbc.Button( "Electricity Consumption", id='load-electricity', color='outline-primary', size='sm', className='w-100 mb-2' ), ], md=4), ]), ]) ], className='mb-4') def format_upload_status(status: str, message: str, is_error: bool = False): """ Format upload status message Args: status: Status type ('success', 'error', 'info') message: Message to display is_error: Whether this is an error message Returns: Dash component """ if is_error or status == 'error': return dbc.Alert([ html.I(className="fas fa-exclamation-circle me-2"), message ], color='danger', dismissable=True) elif status == 'success': return dbc.Alert([ html.I(className="fas fa-check-circle me-2"), message ], color='success', dismissable=True) elif status == 'warning': return dbc.Alert([ html.I(className="fas fa-exclamation-triangle me-2"), message ], color='warning', dismissable=True) else: return dbc.Alert([ html.I(className="fas fa-info-circle me-2"), message ], color='info', dismissable=True) def create_data_preview_table(df, n_rows=10): """ Create a data preview table Args: df: DataFrame to preview n_rows: Number of rows to show Returns: Dash component """ if df is None or df.empty: return html.Div() return html.Div([ html.H6("Data Preview"), dbc.Table.from_dataframe( df.head(n_rows), striped=True, bordered=True, hover=True, responsive=True, size='sm' ), html.P( f"Showing first {min(n_rows, len(df))} of {len(df)} rows", className='text-muted small' ) ]) def create_quality_report(report: dict): """ Create a data quality report display Args: report: Quality report dictionary Returns: Dash component """ if not report: return html.Div() # Build warning messages if needed warnings = [] if report.get('sampled', False): warnings.append( dbc.Alert( f"⚠️ Large dataset detected: Sampled from {report.get('original_points', 0):,} to {report.get('total_points', 0):,} rows (most recent data retained)", color="warning", className="mb-2" ) ) if report.get('duplicates_removed', 0) > 0: warnings.append( dbc.Alert( f"⚠️ Removed {report.get('duplicates_removed', 0):,} duplicate timestamps", color="info", className="mb-2" ) ) return dbc.Card([ dbc.CardHeader(html.H6("Data Quality Report", className="mb-0")), dbc.CardBody([ html.Div(warnings) if warnings else None, dbc.Row([ dbc.Col([ html.Small("Total Points", className='text-muted'), html.H6(f"{report.get('total_points', 0):,}") ], md=3), dbc.Col([ html.Small("Date Range", className='text-muted'), html.H6(f"{report.get('date_range', {}).get('start', 'N/A')} to {report.get('date_range', {}).get('end', 'N/A')}", style={'fontSize': '0.9rem'}) ], md=3), dbc.Col([ html.Small("Frequency", className='text-muted'), html.H6(report.get('frequency', 'Unknown')) ], md=2), dbc.Col([ html.Small("Missing Filled", className='text-muted'), html.H6(str(report.get('missing_filled', 0))) ], md=2), dbc.Col([ html.Small("Outliers", className='text-muted'), html.H6(str(report.get('outliers_detected', 0))) ], md=2), ]), html.Hr(), dbc.Row([ dbc.Col([ html.Small("Mean", className='text-muted'), html.P(f"{report.get('statistics', {}).get('mean', 0):.2f}") ], md=3), dbc.Col([ html.Small("Std Dev", className='text-muted'), html.P(f"{report.get('statistics', {}).get('std', 0):.2f}") ], md=3), dbc.Col([ html.Small("Min", className='text-muted'), html.P(f"{report.get('statistics', {}).get('min', 0):.2f}") ], md=3), dbc.Col([ html.Small("Max", className='text-muted'), html.P(f"{report.get('statistics', {}).get('max', 0):.2f}") ], md=3), ]) ]) ], className='mt-3')