Skip to content

Commit 725453d

Browse files
sayalaruanoenryH
andauthored
✨Factory function for building plotly plot functions (#34)
* ♻️ Refactor(vuecore/engines/plotly/theming.py): create helper functions for common theming to simplify functions and avoid redundant code * ✨ Feat(vuecore/engines/plotly/plot_builder.py): Add build_plot factory function to encapsulate common code to build all plots * ♻️ Refactor build plotly plot functions for bar, box, line, and scatter plots by using the plot_builder factory function * 🐛 Remove unused import and variables, suggested by ruff * Style(vuecore/engines/plotly/plot_builder.py): Add Optional for preprocess variable Co-authored-by: Henry Webel <heweb@dtu.dk> * 🐛 Fix(src/vuecore/engines/plotly/plot_builder.py): add Optional missing import and add validation of preprocess to be callable --------- Co-authored-by: Henry Webel <heweb@dtu.dk>
1 parent fd0c619 commit 725453d

File tree

7 files changed

+307
-240
lines changed

7 files changed

+307
-240
lines changed

src/vuecore/engines/plotly/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from .box import build as build_box
88
from .saver import save
99

10+
# Import build_utils to ensure it's available
11+
from . import plot_builder # noqa: F401
12+
1013
# Register the functions with the central dispatcher
1114
register_builder(
1215
plot_type=PlotType.SCATTER, engine=EngineType.PLOTLY, func=build_scatter

src/vuecore/engines/plotly/bar.py

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66

77
from vuecore.schemas.basic.bar import BarConfig
88
from .theming import apply_bar_theme
9+
from .plot_builder import build_plot
10+
11+
# Define parameters handled by the theme script
12+
THEMING_PARAMS = [
13+
"opacity",
14+
"barmode",
15+
"log_x",
16+
"log_y",
17+
"range_x",
18+
"range_y",
19+
"title",
20+
"x_title",
21+
"y_title",
22+
"subtitle",
23+
"template",
24+
"width",
25+
"height",
26+
]
927

1028

1129
def build(data: pd.DataFrame, config: BarConfig) -> go.Figure:
@@ -31,37 +49,10 @@ def build(data: pd.DataFrame, config: BarConfig) -> go.Figure:
3149
go.Figure
3250
A `plotly.graph_objects.Figure` object representing the bar plot.
3351
"""
34-
# Get all parameters from the config model, including extras
35-
all_config_params = config.model_dump()
36-
37-
# Define parameters handled by the theme script
38-
theming_params = [
39-
"opacity",
40-
"barmode",
41-
"log_x",
42-
"log_y",
43-
"range_x",
44-
"range_y",
45-
"title",
46-
"x_title",
47-
"y_title",
48-
"subtitle",
49-
"template",
50-
"width",
51-
"height",
52-
]
53-
54-
# Create the dictionary of arguments for px.bar
55-
plot_args = {
56-
k: v
57-
for k, v in all_config_params.items()
58-
if k not in theming_params and v is not None
59-
}
60-
61-
# Create the base figure using only the arguments relevant to px.bar
62-
fig = px.bar(data, **plot_args)
63-
64-
# Apply theme and additional styling to the generated figure.
65-
fig = apply_bar_theme(fig, config)
66-
67-
return fig
52+
return build_plot(
53+
data=data,
54+
config=config,
55+
px_function=px.bar,
56+
theming_function=apply_bar_theme,
57+
theming_params=THEMING_PARAMS,
58+
)

src/vuecore/engines/plotly/box.py

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@
66

77
from vuecore.schemas.basic.box import BoxConfig
88
from .theming import apply_box_theme
9+
from .plot_builder import build_plot
10+
11+
# Define parameters handled by the theme script
12+
THEMING_PARAMS = [
13+
"boxmode",
14+
"log_x",
15+
"log_y",
16+
"range_x",
17+
"range_y",
18+
"notched",
19+
"points",
20+
"title",
21+
"x_title",
22+
"y_title",
23+
"subtitle",
24+
"template",
25+
"width",
26+
"height",
27+
]
928

1029

1130
def build(data: pd.DataFrame, config: BoxConfig) -> go.Figure:
@@ -31,38 +50,10 @@ def build(data: pd.DataFrame, config: BoxConfig) -> go.Figure:
3150
go.Figure
3251
A `plotly.graph_objects.Figure` object representing the box plot.
3352
"""
34-
# Get all parameters from the config model, including extras
35-
all_config_params = config.model_dump()
36-
37-
# Define parameters handled by the theme script
38-
theming_params = [
39-
"boxmode",
40-
"log_x",
41-
"log_y",
42-
"range_x",
43-
"range_y",
44-
"notched",
45-
"points",
46-
"title",
47-
"x_title",
48-
"y_title",
49-
"subtitle",
50-
"template",
51-
"width",
52-
"height",
53-
]
54-
55-
# Create the dictionary of arguments for px.box
56-
plot_args = {
57-
k: v
58-
for k, v in all_config_params.items()
59-
if k not in theming_params and v is not None
60-
}
61-
62-
# Create the base figure using only the arguments relevant to px.box
63-
fig = px.box(data, **plot_args)
64-
65-
# Apply theme and additional styling to the generated figure.
66-
fig = apply_box_theme(fig, config)
67-
68-
return fig
53+
return build_plot(
54+
data=data,
55+
config=config,
56+
px_function=px.box,
57+
theming_function=apply_box_theme,
58+
theming_params=THEMING_PARAMS,
59+
)

src/vuecore/engines/plotly/line.py

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66

77
from vuecore.schemas.basic.line import LineConfig
88
from .theming import apply_line_theme
9+
from .plot_builder import build_plot
10+
11+
# Define parameters handled by the theme script
12+
THEMING_PARAMS = [
13+
"markers",
14+
"log_x",
15+
"log_y",
16+
"range_x",
17+
"range_y",
18+
"line_shape",
19+
"title",
20+
"x_title",
21+
"y_title",
22+
"subtitle",
23+
"template",
24+
"width",
25+
"height",
26+
]
927

1028

1129
def build(data: pd.DataFrame, config: LineConfig) -> go.Figure:
@@ -31,37 +49,10 @@ def build(data: pd.DataFrame, config: LineConfig) -> go.Figure:
3149
go.Figure
3250
A `plotly.graph_objects.Figure` object representing the line plot.
3351
"""
34-
# Get all parameters from the config model, including extras
35-
all_config_params = config.model_dump()
36-
37-
# Define parameters handled by the theme script
38-
theming_params = [
39-
"markers",
40-
"log_x",
41-
"log_y",
42-
"range_x",
43-
"range_y",
44-
"line_shape",
45-
"title",
46-
"x_title",
47-
"y_title",
48-
"subtitle",
49-
"template",
50-
"width",
51-
"height",
52-
]
53-
54-
# Create the dictionary of arguments for px.line
55-
plot_args = {
56-
k: v
57-
for k, v in all_config_params.items()
58-
if k not in theming_params and v is not None
59-
}
60-
61-
# Create the base figure using only the arguments for px.line
62-
fig = px.line(data, **plot_args)
63-
64-
# Apply theme and additional styling
65-
fig = apply_line_theme(fig, config)
66-
67-
return fig
52+
return build_plot(
53+
data=data,
54+
config=config,
55+
px_function=px.line,
56+
theming_function=apply_line_theme,
57+
theming_params=THEMING_PARAMS,
58+
)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# vuecore/engines/plotly/plot_builder.py
2+
from typing import Any, Optional, List, Callable
3+
import pandas as pd
4+
import plotly.graph_objects as go
5+
6+
7+
def build_plot(
8+
data: pd.DataFrame,
9+
config: Any,
10+
px_function: Callable,
11+
theming_function: Callable,
12+
theming_params: List[str],
13+
preprocess: Optional[Callable] = None,
14+
) -> go.Figure:
15+
"""
16+
Base function to build Plotly figures with common patterns.
17+
18+
The function follows these steps:
19+
1. Get all parameters from the config model
20+
2. Create the dictionary of arguments for the plot function
21+
3. Apply preprocessing
22+
4. Create the base figure
23+
5. Apply theme and additional styling
24+
25+
Parameters
26+
----------
27+
data : pd.DataFrame
28+
The DataFrame containing the plot data.
29+
config : Any
30+
The Pydantic model with all plot configurations.
31+
px_function : Callable
32+
The Plotly Express function to use (e.g., px.bar, px.scatter, etc).
33+
theming_function : Callable
34+
The theming function to apply to the figure.
35+
theming_params : List[str]
36+
List of parameter names handled by the theming function.
37+
preprocess : Callable, Optional
38+
Optional preprocessing function for special features.
39+
40+
Returns
41+
-------
42+
go.Figure
43+
A styled Plotly figure object.
44+
"""
45+
# Get all parameters from the config model
46+
all_config_params = config.model_dump()
47+
48+
# Create the dictionary of arguments for the plot function
49+
plot_args = {
50+
k: v
51+
for k, v in all_config_params.items()
52+
if k not in theming_params and v is not None
53+
}
54+
55+
# Apply preprocessing if provided
56+
if preprocess and callable(preprocess):
57+
data, plot_args = preprocess(data, plot_args, config)
58+
59+
# Create the base figure
60+
fig = px_function(data, **plot_args)
61+
62+
# Apply theme and additional styling
63+
fig = theming_function(fig, config)
64+
65+
return fig

0 commit comments

Comments
 (0)