diff --git a/dtale/app.py b/dtale/app.py index b7579315..0ee31530 100644 --- a/dtale/app.py +++ b/dtale/app.py @@ -955,6 +955,7 @@ def get_instance(data_id): def offline_chart( df, + return_object=False, chart_type=None, query=None, x=None, @@ -976,6 +977,8 @@ def offline_chart( :param df: integer string identifier for a D-Tale process's data :type df: :class:`pandas:pandas.DataFrame` + :param return_object: if True, return the plotly graph object for this chart + :type return_object: bool :param chart_type: type of chart, possible options are line|bar|pie|scatter|3d_scatter|surface|heatmap :type chart_type: str :param query: pandas dataframe query string @@ -1014,6 +1017,7 @@ def offline_chart( """ instance = startup(url=None, data=df, data_id=999, is_proxy=JUPYTER_SERVER_PROXY) output = instance.offline_chart( + return_object=return_object, chart_type=chart_type, query=query, x=x, diff --git a/dtale/config.py b/dtale/config.py index 8cd97db8..43beb817 100644 --- a/dtale/config.py +++ b/dtale/config.py @@ -87,11 +87,7 @@ def load_app_settings(config): getter="getboolean", ) hide_main_menu = get_config_val( - config, - curr_app_settings, - "hide_main_menu", - section="app", - getter="getboolean", + config, curr_app_settings, "hide_main_menu", section="app", getter="getboolean" ) hide_column_menus = get_config_val( config, diff --git a/dtale/dash_application/charts.py b/dtale/dash_application/charts.py index 29afe0c7..67c893a9 100644 --- a/dtale/dash_application/charts.py +++ b/dtale/dash_application/charts.py @@ -612,12 +612,7 @@ def build_url(path, query): export_png_link, export_csv_link, ], - style={ - "position": "absolute", - "zIndex": 5, - "left": 5, - "top": 2, - }, + style={"position": "absolute", "zIndex": 5, "left": 5, "top": 2}, ) return html.Div( [links] + make_list(chart), style={"position": "relative", "height": "100%"} @@ -4010,25 +4005,26 @@ def clean_output(output): find_figures(output, formatted_output) output = formatted_output if isinstance(output, dcc.Graph): - output = output.figure if inputs.get("title"): - output["layout"]["title"] = dict(text=inputs.get("title")) - output["layout"]["colorway"] = px.colors.qualitative.D3 - output["layout"]["plot_bgcolor"] = "white" - output["layout"]["xaxis"].update( + output.figure["layout"]["title"] = dict(text=inputs.get("title")) + output.figure["layout"]["colorway"] = px.colors.qualitative.D3 + output.figure["layout"]["plot_bgcolor"] = "white" + output.figure["layout"]["xaxis"].update( mirror=True, ticks="outside", showline=True, linecolor="black", gridcolor="lightgrey", ) - output["layout"]["yaxis"].update( + output.figure["layout"]["yaxis"].update( mirror=True, ticks="outside", showline=True, linecolor="black", gridcolor="lightgrey", ) + if inputs.get("return_object") is not True: + output = output.figure return output def _raw_chart_builder(): diff --git a/dtale/views.py b/dtale/views.py index db2c7da3..7d09e4ff 100644 --- a/dtale/views.py +++ b/dtale/views.py @@ -584,6 +584,7 @@ def notebook_charts( def offline_chart( self, + return_object=False, chart_type=None, query=None, x=None, @@ -605,6 +606,8 @@ def offline_chart( """ Builds the HTML for a plotly chart figure to saved to a file or output to a jupyter notebook + :param return_object: if True, return the plotly graph object for this chart + :type return_object: bool :param chart_type: type of chart, possible options are line|bar|pie|scatter|3d_scatter|surface|heatmap :type chart_type: str :param query: pandas dataframe query string @@ -645,6 +648,7 @@ def offline_chart( - otherwise it will return the HTML output as a string """ params = dict( + return_object=return_object, chart_type=chart_type, query=query, x=x, @@ -672,6 +676,9 @@ def offline_chart( iplot(chart) return + if return_object: + return build_raw_chart(self._data_id, export=True, **params) + html_str = export_chart(self._data_id, params) if filepath is None: return html_str diff --git a/tests/dtale/test_offline_chart.py b/tests/dtale/test_offline_chart.py index 0dbcb8aa..a5bdd972 100644 --- a/tests/dtale/test_offline_chart.py +++ b/tests/dtale/test_offline_chart.py @@ -105,3 +105,23 @@ def test_build_notebook(test_data): assert output is None init_notebook_mode_mock.assert_called_once() iplot_mock.assert_called_once() + + +@pytest.mark.unit +def test_output_object(test_data): + from dtale import offline_chart + from dtale.dash_application import dcc + + with ExitStack() as stack: + stack.enter_context( + mock.patch("dtale.views.in_ipython_frontend", mock.Mock(return_value=False)) + ) + output = offline_chart( + test_data, + return_object=True, + chart_type="bar", + x="date", + y="foo", + agg="sum", + ) + assert isinstance(output, dcc.Graph) diff --git a/tests/dtale/test_views.py b/tests/dtale/test_views.py index b5ad343c..1c1e3839 100644 --- a/tests/dtale/test_views.py +++ b/tests/dtale/test_views.py @@ -752,8 +752,7 @@ def test_duplicate_cols(unittest): build_dtypes(dtypes) resp = c.get( - "/dtale/duplicate-col/{}".format(c.port), - query_string=dict(col="b"), + "/dtale/duplicate-col/{}".format(c.port), query_string=dict(col="b") ) unittest.assertEquals( list(global_state.get_data(c.port).columns), ["a", "b", "b_2", "c"]