Source code for dtale.dash_application.extended_aggregations

import dash_bootstrap_components as dbc

from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

from dtale.charts.utils import AGGS, NON_EXT_AGGREGATION
from dtale.dash_application import dcc, html
from dtale.dash_application.layout.utils import (
    build_hoverable,
    build_input,
    build_option,
    show_style,
)
from dtale.translations import text
from dtale.utils import make_list

MAX_INPUTS = 10
INPUT_IDS = list(range(1, MAX_INPUTS + 1))


[docs]def build_error(error): return html.Div( [ html.I(className="ico-error"), html.Span(error), ], className="dtale-alert alert alert-danger", )
[docs]def build_extended_agg_desc(ext_agg): desc = [] col, agg, window, comp = (ext_agg.get(p) for p in ["col", "agg", "window", "comp"]) desc += [html.Span("Col: "), html.B(col)] desc += [html.Span(", Agg: "), html.B(agg)] if window: desc += [html.Span(", Win: "), html.B(window)] if comp: desc += [html.Span(", Roll Comp: "), html.B(comp)] return desc
[docs]def build_body(ext_aggs): col_inputs = [html.Div(id="extended-agg-errors")] for i in INPUT_IDS: ext_agg = ext_aggs[i - 1] if len(ext_aggs) >= i else {} col_inputs.append( html.Div( [ html.Span( "{}.".format(i), className="col-auto pr-0 mt-auto mb-auto ext-agg-id", ), build_input( text("Col"), dcc.Dropdown( id="col-dropdown-{}".format(i), placeholder=text("Select"), style=dict(width="inherit"), value=ext_agg.get("col"), ), className="col-md-3", ), build_input( text("Agg"), dcc.Dropdown( id="agg-dropdown-{}".format(i), options=[ build_option(v, text(AGGS[v])) for v in [ "count", "nunique", "sum", "mean", "rolling", "corr", "first", "last", # "drop_duplicates", "median", "min", "max", "std", "var", "mad", "prod", "pctsum", "pctct", ] ], placeholder=text("Select"), style=dict(width="inherit"), value=ext_agg.get("agg"), ), className="col-md-3", ), html.Div( [ build_input( text("Window"), dcc.Input( id="window-input-{}".format(i), type="number", placeholder=text("Enter Days"), className="form-control text-center", style={"lineHeight": "inherit"}, value=ext_agg.get("window"), ), className="col-md-6", ), build_input( text("Computation"), dcc.Dropdown( id="rolling-comp-dropdown-{}".format(i), options=[ build_option("corr", text("Correlation")), build_option("count", text("Count")), build_option("cov", text("Covariance")), build_option("kurt", text("Kurtosis")), build_option("max", text("Maximum")), build_option("mean", text("Mean")), build_option("median", text("Median")), build_option("min", text("Minimum")), build_option("skew", text("Skew")), build_option( "std", text("Standard Deviation"), ), build_option("sum", text("Sum")), build_option("var", text("Variance")), ], placeholder=text("Select"), style=dict(width="inherit"), value=ext_agg.get("rolling_comp"), ), className="col-md-6 pl-0", ), ], id="rolling-inputs-{}".format(i), style=show_style( ext_agg.get("agg") == "rolling", display_style="inherit" ), className="col-md-6 row p-0", ), ], className="row pb-3", ) ) return html.Div(col_inputs, id="extended-agg-body")
[docs]def build_modal(ext_aggs, chart_type, y): return [ build_hoverable( html.I( className="ico-settings pointer", id="open-extended-agg-modal", style=show_style(chart_type not in NON_EXT_AGGREGATION and len(y)), ), html.Div( html.Span(text("ext_agg_desc")), id="extended-aggregation-tooltip" ), hover_class="saved-chart-config", top="100%", additional_classes="mb-auto mt-auto", ), dcc.Store(id="extended-aggregations", data=ext_aggs), dcc.Store(id="prev-open-extended-agg-modal", data=0), dcc.Store(id="prev-close-extended-agg-modal", data=0), dcc.Store(id="prev-clear-extended-agg-modal", data=0), dcc.Store(id="prev-apply-extended-agg-modal", data=0), dbc.Modal( [ dbc.ModalHeader( html.Div( [ html.Div( text("Extended Aggregations"), className="col mt-auto mb-auto", ), html.Button( html.Span("X"), className="close mr-5", id="close-extended-agg-modal", ), ], className="row", ) ), dbc.ModalBody(build_body(ext_aggs)), dbc.ModalFooter( [ dbc.Button( text("Clear"), id="clear-extended-agg-modal", className="ml-auto", ), dbc.Button( text("Apply"), id="apply-extended-agg-modal", ), ] ), ], id="extended-agg-modal", size="lg", centered=True, ), ]
[docs]def init_callbacks(dash_app): @dash_app.callback( [ Output("extended-agg-modal", "is_open"), Output("extended-aggregations", "data"), Output("prev-open-extended-agg-modal", "data"), Output("prev-apply-extended-agg-modal", "data"), Output("prev-close-extended-agg-modal", "data"), Output("prev-clear-extended-agg-modal", "data"), Output("extended-agg-errors", "children"), ] + [Output("col-dropdown-{}".format(i), "value") for i in INPUT_IDS] + [Output("agg-dropdown-{}".format(i), "value") for i in INPUT_IDS], [ Input("open-extended-agg-modal", "n_clicks"), Input("apply-extended-agg-modal", "n_clicks"), Input("close-extended-agg-modal", "n_clicks"), Input("clear-extended-agg-modal", "n_clicks"), ], [ State("extended-agg-modal", "is_open"), State("extended-aggregations", "data"), State("input-data", "data"), State("prev-open-extended-agg-modal", "data"), State("prev-apply-extended-agg-modal", "data"), State("prev-close-extended-agg-modal", "data"), State("prev-clear-extended-agg-modal", "data"), ] + [State("col-dropdown-{}".format(i), "value") for i in INPUT_IDS] + [State("agg-dropdown-{}".format(i), "value") for i in INPUT_IDS] + [State("window-input-{}".format(i), "value") for i in INPUT_IDS] + [State("rolling-comp-dropdown-{}".format(i), "value") for i in INPUT_IDS], ) def toggle_modal( open_clicks, apply_clicks, close_clicks, clear_clicks, is_modal_open, curr_ext_aggs, inputs, prev_open_clicks, prev_apply_clicks, prev_close_clicks, prev_clear_clicks, *agg_inputs ): open_clicks = open_clicks or 0 apply_clicks = apply_clicks or 0 close_clicks = close_clicks or 0 clear_clicks = clear_clicks or 0 is_open = open_clicks > prev_open_clicks is_apply = apply_clicks > prev_apply_clicks is_close = close_clicks > prev_close_clicks is_clear = clear_clicks > prev_clear_clicks agg_inputs = list(agg_inputs) col_values = [agg_inputs.pop(0) for _ in INPUT_IDS] agg_values = [agg_inputs.pop(0) for _ in INPUT_IDS] window_values = [agg_inputs.pop(0) for _ in INPUT_IDS] rolling_comp_values = [agg_inputs.pop(0) for _ in INPUT_IDS] if is_open or is_apply or is_close or is_clear: errors = [] ext_aggs = curr_ext_aggs if is_close or is_open else [] final_is_modal_open = not is_modal_open if is_open: curr_col, curr_agg = (inputs.get(prop) for prop in ["col", "agg"]) if curr_agg != "raw": for i, sub_col in enumerate(make_list(curr_col)): col_values[i] = sub_col agg_values[i] = curr_agg ext_aggs.append( dict( col=sub_col, agg=curr_agg, window=None, rolling_comp=None, ) ) if is_apply: agg_input_iterable = enumerate( zip(col_values, agg_values, window_values, rolling_comp_values), 1 ) for i, (col, agg, window, rolling_comp) in agg_input_iterable: if col is None: continue if agg is None: errors.append( "Entry {} is missing an aggregation selection!".format(i) ) continue if agg == "rolling": if not window: errors.append( "Entry {} is missing a rolling window!".format(i) ) continue if not rolling_comp: errors.append( "Entry {} is missing a rolling computation!".format(i) ) continue ext_aggs.append( dict( col=col, agg=agg, window=window, rolling_comp=rolling_comp, ) ) if len(errors): errors.append( 'If you wish to not use an extended aggregation please click "Clear".' ) errors = build_error(" ".join(errors)) final_is_modal_open = True else: errors = None return ( final_is_modal_open, ext_aggs, open_clicks, apply_clicks, close_clicks, clear_clicks, errors, ) + tuple(col_values + agg_values) return ( is_modal_open, curr_ext_aggs, open_clicks, apply_clicks, close_clicks, clear_clicks, None, ) + tuple(col_values + agg_values) @dash_app.callback( [Output("col-dropdown-{}".format(i), "options") for i in INPUT_IDS], [Input("extended-agg-modal", "is_open")], [State("input-data", "data")], ) def populate_col_dropdowns(is_open, input_data): if not is_open: raise PreventUpdate y = make_list(input_data.get("y")) z = make_list(input_data.get("z")) col_options = [build_option(sub_col) for sub_col in (y if not len(z) else z)] return [col_options for _ in range(10)] def toggle_rolling_style(agg): return show_style(agg == "rolling", display_style="inherit") for i in INPUT_IDS: dash_app.callback( Output("rolling-inputs-{}".format(i), "style"), [Input("agg-dropdown-{}".format(i), "value")], )(toggle_rolling_style)