Core Module API
This section documents the core components that form the foundation of dashboard_lego.
DataSource
Location: dashboard_lego.core.datasource
Hierarchy: [Core | DataSources | DataSource]
Purpose: Concrete data source with a 2-stage processing pipeline using composition.
Pipeline Architecture (v0.15)
Data flows through a 2-stage pipeline with independent caching at each stage. This is a shift from the previous 3-stage model.
┌─────────────┐ ┌────────────────┐ ┌──────────┐
│ DataBuilder │ --> │ DataTransformer│ --> │ Output │
│ (Build) │ │ (Transform) │ │ (Blocks) │
└─────────────┘ └────────────────┘ └──────────┘
↑ ↑
│ │
build transform
params params
Contract:
:contract:
- pre: data_builder and data_transformer are provided to the constructor via composition.
- post: Data flows through the 2-stage pipeline: build → transform.
- invariant: get_processed_data() always runs the pipeline, leveraging the cache at each stage.
- no_abstract_methods: DataSource is fully concrete and should not be subclassed for data loading.
Key Architectural Changes (v0.15)
Simpler 2-Stage Pipeline: The previous
Load → Preprocess → Filterpipeline is nowBuild → Transform.Semantic Clarity:
Buildclearly means “construct the complete dataset from a source”.Transformmeans “apply any df→df transformation” (filtering, aggregation, etc.).Composition over Inheritance:
DataSourceis now a concrete class. You no longer subclass it. Instead, you provideDataBuilderandDataTransformerinstances to its constructor.Staged Caching: Changing
transformparameters (e.g., from a filter control) will only re-run the second stage, reusing the cached result from thebuildstage for better performance.
Constructor
def __init__(
self,
data_builder: Optional[DataBuilder] = None,
data_transformer: Optional[DataTransformer] = None,
param_classifier: Optional[Callable[[str], str]] = None,
cache_dir: Optional[str] = None,
cache_ttl: int = 300,
build_fn: Optional[Callable[[Dict[str, Any]], pd.DataFrame]] = None,
transform_fn: Optional[Callable[[pd.DataFrame], pd.DataFrame]] = None,
cache_prewarm_params: Optional[List[Dict[str, Any]]] = None,
**kwargs
)
Pipeline Parameters:
data_builder: ADataBuilderinstance for Stage 1 (loading and initial processing).data_transformer: ADataTransformerinstance for Stage 2 (filtering, aggregation, reshaping).param_classifier: A function that routes parameters from controls to the correct pipeline stage. It must return either'build'or'transform'.build_fn: Optional lambda function for simple data building. Signature:lambda params: DataFrame. If provided, automatically creates aLambdaBuilder.transform_fn: Optional lambda function for simple data transformation. Signature:lambda df: DataFrame. If provided, automatically creates aLambdaTransformer.cache_prewarm_params: Optional list of parameter dictionaries to prewarm cache during initialization.
Simple Lambda Function Usage:
For simple use cases, you can use lambda functions instead of creating full DataBuilder and DataTransformer classes:
# Simple datasource with lambda functions
datasource = DataSource(
build_fn=lambda params: pd.read_csv(params.get('file', 'default.csv')),
transform_fn=lambda df: df.groupby('category').sum().reset_index()
)
# With cache prewarming
datasource = DataSource(
build_fn=lambda params: pd.read_csv(params.get('file', 'default.csv')),
transform_fn=lambda df: df.groupby('category').sum().reset_index(),
cache_prewarm_params=[
{'file': 'sales.csv', 'category': 'Electronics'},
{'file': 'sales.csv', 'category': 'Home'}
]
)
Param Classifier Example:
def classify_params(param_key: str) -> str:
"""Route params to the correct pipeline stage."""
# Params from filter controls go to the transform stage.
if param_key.startswith('filters-'):
return 'transform'
# All other params go to the build stage.
return 'build'
datasource = DataSource(
data_builder=MyDataBuilder(),
data_transformer=MyDataTransformer(),
param_classifier=classify_params
)
Public Methods
def get_processed_data(self, params: Optional[Dict[str, Any]] = None) -> pd.DataFrame:
"""
Runs the 2-stage pipeline and returns the final, transformed data.
Args:
params: A dictionary of parameters for both build and transform stages.
Returns:
A transformed DataFrame.
Pipeline Stages:
1. Classify params into 'build' vs 'transform' categories.
2. Build data (cached by build params).
3. Transform data (cached by transform params).
"""
def with_transform_fn(self, transform_fn: Callable[[pd.DataFrame], pd.DataFrame]) -> "DataSource":
"""
Creates a new, specialized datasource instance with an additional transformation.
This is the key to block-level transformations.
"""
Implementation Example (v0.15 Pattern)
from dashboard_lego.core import DataSource, DataBuilder, DataTransformer
from dashboard_lego.blocks import get_metric_row
import pandas as pd
# Step 1: Define a DataBuilder (combines loading and initial processing)
class SalesDataBuilder(DataBuilder):
def __init__(self, file_path: str, **kwargs):
super().__init__(**kwargs)
self.file_path = file_path
def build(self, params: dict) -> pd.DataFrame:
# Load AND process in one method
df = pd.read_csv(self.file_path)
df['Revenue'] = df['Price'] * df['Quantity']
df['Date'] = pd.to_datetime(df['Date'])
return df
# Step 2: Define a DataTransformer (for filtering, aggregation, etc.)
class SalesTransformer(DataTransformer):
def transform(self, data: pd.DataFrame, params: dict) -> pd.DataFrame:
df = data.copy()
if 'filters-category' in params:
category = params['filters-category']
if category and category != 'All':
df = df[df['Category'] == category]
# ... other transformations
return df
# Step 3: Define a parameter classifier
def param_classifier(key: str) -> str:
return 'transform' if key.startswith('filters-') else 'build'
# Step 4: Create the DataSource instance using composition
datasource = DataSource(
data_builder=SalesDataBuilder("sales.csv"),
data_transformer=SalesTransformer(),
param_classifier=param_classifier,
cache_ttl=600
)
# Step 5: Use get_metric_row() factory to create metric blocks (v0.15+)
# The get_kpis() method is removed from the datasource.
metrics, row_opts = get_metric_row(
metrics_spec={
'total_revenue': {
'column': 'Revenue',
'agg': 'sum',
'title': 'Total Revenue',
'color': 'success'
},
'avg_price': {
'column': 'Price',
'agg': 'mean',
'title': 'Avg Price',
'color': 'info'
}
},
datasource=datasource,
subscribes_to=['filters-category']
)
DashboardPage
Location: dashboard_lego.core.page
Hierarchy: [Core | Orchestration | DashboardPage]
Purpose: Main orchestrator for dashboard lifecycle and layout
Contract:
:contract:
- pre: Blocks must be provided via 'blocks' param or navigation sections
- post: Complete Dash layout with registered callbacks
- lifecycle: instantiate → build_layout() → register_callbacks() → serve
Constructor
def __init__(
self,
title: str = "Dashboard",
blocks: Optional[LayoutSpec] = None,
navigation: Optional[NavigationConfig] = None,
theme: Optional[str] = None, # dbc.themes.* URL
theme_config: Optional[ThemeConfig] = None,
**kwargs
)
# LayoutSpec format:
# Single block: BaseBlock
# Row: [block1, block2, ...] or [(block1, col_opts), ...]
# Multiple rows: [[row1_blocks], [row2_blocks], ...]
# With row options: [([blocks], row_opts), ...]
Column Options:
col_opts = {
"xs": int, # Width on extra-small screens (1-12)
"sm": int, # Width on small screens (1-12)
"md": int, # Width on medium screens (1-12)
"lg": int, # Width on large screens (1-12)
"xl": int, # Width on extra-large screens (1-12)
"offset": int, # Column offset (0-11)
"align": str, # Vertical align: "start"|"center"|"end"
"className": str,
"style": dict,
}
Row Options:
row_opts = {
"align": str, # Vertical alignment: "start"|"center"|"end"
"justify": str, # Horizontal alignment: "start"|"center"|"end"|"between"|"around"
"g": int, # Gutter size (0-5)
"className": str,
"style": dict,
}
Public Methods
def build_layout(self) -> Component:
"""
Constructs complete Dash layout from blocks.
Returns:
Dash Component tree ready for app.layout assignment
Side effects:
- Creates StateManager
- Registers all blocks with StateManager
- Injects theme_config into blocks
"""
def register_callbacks(self, app: Dash) -> None:
"""
Registers all callbacks with Dash app.
Args:
app: Dash application instance
Side effects:
- Calls state_manager.bind_callbacks() for block-centric callbacks
- Registers navigation callbacks if navigation is enabled
"""
Layout Examples
# Simple one-column layout
page = DashboardPage(
title="My Dashboard",
blocks=one_column([kpi_block, chart_block]),
theme=dbc.themes.LUX
)
# Two-column layout with custom column widths
page = DashboardPage(
title="My Dashboard",
blocks=[
[(kpi_block, {"md": 8}), (sidebar_block, {"md": 4})]
]
)
# Complex multi-row layout
page = DashboardPage(
title="My Dashboard",
blocks=[
# KPI row (full width)
[(kpi_block, {"md": 12})],
# Two charts side-by-side
[(chart1, {"md": 6}), (chart2, {"md": 6})],
# Three cards in a row
[(card1, {"md": 4}), (card2, {"md": 4}), (card3, {"md": 4})]
]
)
For more core components (NavigationConfig, StateManager, ThemeConfig, DataBuilder, DataTransformer, DataProcessingContext, SidebarConfig), see the API documentation.