-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add choropleth chart to plotly, refactor, add more tests
- Loading branch information
Showing
44 changed files
with
3,553 additions
and
1,161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from __future__ import annotations | ||
|
||
from .base import PlotlyBuilder # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
import plotly.express as px | ||
|
||
from .base import PlotlyBuilder, BasePlotlyForm | ||
|
||
|
||
class PlotlyBarBuilder(PlotlyBuilder): | ||
def to_json(self) -> str: | ||
return self.build_bar_chart() | ||
|
||
def build_bar_chart(self) -> Any: | ||
if self.settings.get("skip_null_values"): | ||
self.df = self.df[self.df[self.settings["y"]].notna()] | ||
|
||
fig = px.bar( | ||
data_frame=self.df, | ||
x=self.settings["x"], | ||
y=self.settings["y"], | ||
log_x=self.settings.get("log_x", False), | ||
log_y=self.settings.get("log_y", False), | ||
opacity=self.settings.get("opacity"), | ||
animation_frame=self.settings.get("animation_frame"), | ||
color=self.settings.get("color"), | ||
) | ||
|
||
fig.update_xaxes( | ||
type="category", | ||
) | ||
|
||
if chart_title := self.settings.get("chart_title"): | ||
fig.update_layout(title_text=chart_title) | ||
|
||
if x_axis_label := self.settings.get("x_axis_label"): | ||
fig.update_xaxes(title_text=x_axis_label) | ||
else: | ||
fig.update_xaxes(title_text=self.settings["x"]) | ||
|
||
if y_axis_label := self.settings.get("y_axis_label"): | ||
fig.update_yaxes(title_text=y_axis_label) | ||
else: | ||
fig.update_yaxes(title_text=self.settings["y"][0]) | ||
|
||
return fig.to_json() | ||
|
||
|
||
class PlotlyBarForm(BasePlotlyForm): | ||
name = "Bar" | ||
builder = PlotlyBarBuilder | ||
|
||
def get_form_fields(self): | ||
"""Get the form fields for the Plotly bar chart.""" | ||
columns = [{"value": col, "label": col} for col in self.df.columns] | ||
chart_types = [ | ||
{"value": form.name, "label": form.name} | ||
for form in self.builder.get_supported_forms() | ||
] | ||
|
||
return [ | ||
self.title_field(), | ||
self.description_field(), | ||
self.engine_field(), | ||
self.type_field(chart_types), | ||
self.engine_details_field(), | ||
self.x_axis_field(columns), | ||
self.y_axis_field(columns), | ||
self.more_info_button_field(), | ||
self.log_x_field(), | ||
self.log_y_field(), | ||
self.sort_x_field(), | ||
self.sort_y_field(), | ||
self.skip_null_values_field(), | ||
self.limit_field(maximum=1000000), | ||
self.chart_title_field(), | ||
self.x_axis_label_field(), | ||
self.y_axis_label_field(), | ||
self.color_field(columns), | ||
self.animation_frame_field(columns), | ||
self.opacity_field(), | ||
self.filter_field(columns), | ||
] | ||
|
||
|
||
class PlotlyHorizontalBarBuilder(PlotlyBuilder): | ||
def to_json(self) -> Any: | ||
return self.build_horizontal_bar_chart() | ||
|
||
def build_horizontal_bar_chart(self) -> Any: | ||
if self.settings.get("skip_null_values"): | ||
self.df = self.df[self.df[self.settings["y"]].notna()] | ||
|
||
fig = px.bar( | ||
data_frame=self.df, | ||
x=self.settings["y"], | ||
y=self.settings["x"], | ||
log_x=self.settings.get("log_y", False), | ||
log_y=self.settings.get("log_x", False), | ||
opacity=self.settings.get("opacity"), | ||
animation_frame=self.settings.get("animation_frame"), | ||
color=self.settings.get("color"), | ||
orientation="h", | ||
) | ||
|
||
fig.update_yaxes( | ||
type="category", | ||
) | ||
|
||
if chart_title := self.settings.get("chart_title"): | ||
fig.update_layout(title_text=chart_title) | ||
|
||
if x_axis_label := self.settings.get("x_axis_label"): | ||
fig.update_xaxes(title_text=x_axis_label) | ||
else: | ||
fig.update_xaxes(title_text=self.settings["y"]) | ||
|
||
if y_axis_label := self.settings.get("y_axis_label"): | ||
fig.update_yaxes(title_text=y_axis_label) | ||
else: | ||
fig.update_yaxes(title_text=self.settings["x"]) | ||
|
||
return fig.to_json() | ||
|
||
|
||
class PlotlyHorizontalBarForm(PlotlyBarForm): | ||
name = "Horizontal Bar" | ||
builder = PlotlyHorizontalBarBuilder |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
from ckanext.charts.chart_builders.base import BaseChartBuilder, BaseChartForm | ||
|
||
|
||
class PlotlyBuilder(BaseChartBuilder): | ||
"""Base class for Plotly chart builders. | ||
Defines supported chart types for Plotly engine. | ||
""" | ||
|
||
DEFAULT_NAN_FILL_VALUE = 0 | ||
|
||
@classmethod | ||
def get_supported_forms(cls) -> list[type[Any]]: | ||
from ckanext.charts.chart_builders.plotly.choropleth import PlotlyChoroplethForm | ||
from ckanext.charts.chart_builders.plotly.line import PlotlyLineForm | ||
from ckanext.charts.chart_builders.plotly.pie import PlotlyPieForm | ||
from ckanext.charts.chart_builders.plotly.scatter import PlotlyScatterForm | ||
from ckanext.charts.chart_builders.plotly.bar import ( | ||
PlotlyBarForm, | ||
PlotlyHorizontalBarForm, | ||
) | ||
|
||
return [ | ||
PlotlyBarForm, | ||
PlotlyHorizontalBarForm, | ||
PlotlyPieForm, | ||
PlotlyLineForm, | ||
PlotlyScatterForm, | ||
PlotlyChoroplethForm, | ||
] | ||
|
||
|
||
class BasePlotlyForm(BaseChartForm): | ||
pass |
Oops, something went wrong.