Example #1
0
def test_incs001_csp_hashes_inline_scripts(dash_duo, add_hashes,
                                           hash_algorithm, expectation):
    app = Dash(__name__)

    app.layout = html.Div([
        dcc.Input(id="input_element", type="text"),
        html.Div(id="output_element")
    ])

    app.clientside_callback(
        """
        function(input) {
            return input;
        }
        """,
        Output("output_element", "children"),
        [Input("input_element", "value")],
    )

    with expectation:
        csp = {
            "default-src":
            "'self'",
            "script-src": ["'self'"] +
            (app.csp_hashes(hash_algorithm) if add_hashes else []),
        }

        flask_talisman.Talisman(app.server,
                                content_security_policy=csp,
                                force_https=False)

        dash_duo.start_server(app)

        dash_duo.find_element("#input_element").send_keys("xyz")
        assert dash_duo.wait_for_element("#output_element").text == "xyz"
def test_clsd001_simple_clientside_serverside_callback(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        dcc.Input(id="input"),
        html.Div(id="output-clientside"),
        html.Div(id="output-serverside"),
    ])

    @app.callback(Output("output-serverside", "children"),
                  [Input("input", "value")])
    def update_output(value):
        return 'Server says "{}"'.format(value)

    app.clientside_callback(
        ClientsideFunction(namespace="clientside", function_name="display"),
        Output("output-clientside", "children"),
        [Input("input", "value")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    dash_duo.wait_for_text_to_equal("#output-serverside", 'Server says "None"')
    dash_duo.wait_for_text_to_equal("#output-clientside",
                                    'Client says "undefined"')

    dash_duo.find_element("#input").send_keys("hello world")
    dash_duo.wait_for_text_to_equal("#output-serverside",
                                    'Server says "hello world"')
    dash_duo.wait_for_text_to_equal("#output-clientside",
                                    'Client says "hello world"')
Example #3
0
def test_clsd006_no_update(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        dcc.Input(id="first", value=1),
        dcc.Input(id="second", value=1),
        dcc.Input(id="third", value=1)
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="add1_no_update_at_11"),
        [Output("second", "value"),
         Output("third", "value")], [Input("first", "value")],
        [State("second", "value"),
         State("third", "value")])

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#first", '1')
    dash_duo.wait_for_text_to_equal("#second", '2')
    dash_duo.wait_for_text_to_equal("#third", '2')

    dash_duo.find_element("#first").send_keys("1")

    dash_duo.wait_for_text_to_equal("#first", '11')
    dash_duo.wait_for_text_to_equal("#second", '2')
    dash_duo.wait_for_text_to_equal("#third", '3')

    dash_duo.find_element("#first").send_keys("1")

    dash_duo.wait_for_text_to_equal("#first", '111')
    dash_duo.wait_for_text_to_equal("#second", '3')
    dash_duo.wait_for_text_to_equal("#third", '4')
Example #4
0
def test_clsd017_clientside_serverside_shared_input_with_promise(dash_duo):
    lock = Lock()
    lock.acquire()

    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Div(id="input", children=["initial"]),
        html.Div(id="clientside-div"),
        html.Div(id="serverside-div"),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="non_delayed_promise"),
        Output("clientside-div", "children"),
        Input("input", "children"),
    )

    @app.callback(Output("serverside-div", "children"),
                  Input("input", "children"))
    def callback(value):
        with lock:
            return "serverside-" + value[0] + "-deferred"

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#clientside-div", "clientside-initial")
    lock.release()
    dash_duo.wait_for_text_to_equal("#serverside-div",
                                    "serverside-initial-deferred")
Example #5
0
def setup_callbacks(app: Dash):
    @app.callback(Output("nav-list", "children"),
                  [Input('url', 'href')],
                  [State("report-query", "data")],
                  prevent_initial_call=True)
    def update_nav_links(_, query_string):
        return generate_nav_links(query_string)

    # app.clientside_callback(
    #     ClientsideFunction(
    #         namespace="compliance",
    #         function_name="download_pdf"
    #     ),
    #     Output("report-download", "data-download"),
    #     [Input("report-download", "n_clicks")],
    #     prevent_initial_call=True
    # )

    app.clientside_callback(
        ClientsideFunction(
            namespace="compliance",
            function_name="download_png"
        ),
        Output("report-download-png", "data-download"),
        [Input("report-download-png", "n_clicks")],
        prevent_initial_call=True
    )
Example #6
0
def test_clsd014_input_output_callback(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Div(id="input-text"),
        dcc.Input(id="input", type="number", value=0)
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="input_output_callback"),
        Output("input", "value"),
        Input("input", "value"),
    )

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="input_output_follower"),
        Output("input-text", "children"),
        Input("input", "value"),
    )

    dash_duo.start_server(app)

    dash_duo.find_element("#input").send_keys("2")
    dash_duo.wait_for_text_to_equal("#input-text", "3")
    call_count = dash_duo.driver.execute_script("return window.callCount;")

    assert call_count == 2, "initial + changed once"

    assert dash_duo.get_logs() == []
Example #7
0
def test_clsd016_serverside_clientside_shared_input_with_promise(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Div(id="input", children=["initial"]),
        html.Div(id="clientside-div"),
        html.Div(id="serverside-div"),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="delayed_promise"),
        Output("clientside-div", "children"),
        Input("input", "children"),
    )

    @app.callback(Output("serverside-div", "children"),
                  Input("input", "children"))
    def callback(value):
        return "serverside-" + value[0]

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#serverside-div", "serverside-initial")
    dash_duo.driver.execute_script("window.callbackDone('deferred')")
    dash_duo.wait_for_text_to_equal("#clientside-div",
                                    "clientside-initial-deferred")
Example #8
0
def test_clsd008_clientside_inline_source(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div(
        [
            dcc.Input(id="input"),
            html.Div(id="output-clientside"),
            html.Div(id="output-serverside"),
        ]
    )

    @app.callback(Output("output-serverside", "children"), [Input("input", "value")])
    def update_output(value):
        return 'Server says "{}"'.format(value)

    app.clientside_callback(
        """
        function (value) {
            return 'Client says "' + value + '"';
        }
        """,
        Output("output-clientside", "children"),
        [Input("input", "value")],
    )

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#output-serverside", 'Server says "None"')
    dash_duo.wait_for_text_to_equal("#output-clientside", 'Client says "undefined"')

    dash_duo.find_element("#input").send_keys("hello world")
    dash_duo.wait_for_text_to_equal("#output-serverside", 'Server says "hello world"')
    dash_duo.wait_for_text_to_equal("#output-clientside", 'Client says "hello world"')
Example #9
0
def test_clsd011_clientside_callback_context_inputs_list(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Button("btn0", id="btn0"),
        html.Button("btn1:0", id={"btn1": 0}),
        html.Button("btn1:1", id={"btn1": 1}),
        html.Button("btn1:2", id={"btn1": 2}),
        html.Div(id="output-clientside", style={"font-family": "monospace"}),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="inputs_list_to_str"),
        Output("output-clientside", "children"),
        [Input("btn0", "n_clicks"),
         Input({"btn1": ALL}, "n_clicks")],
    )

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ('[{"id":"btn0","property":"n_clicks"},'
         '[{"id":{"btn1":0},"property":"n_clicks"},'
         '{"id":{"btn1":1},"property":"n_clicks"},'
         '{"id":{"btn1":2},"property":"n_clicks"}]]'),
    )

    dash_duo.find_element("#btn0").click()

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ('[{"id":"btn0","property":"n_clicks","value":1},'
         '[{"id":{"btn1":0},"property":"n_clicks"},'
         '{"id":{"btn1":1},"property":"n_clicks"},'
         '{"id":{"btn1":2},"property":"n_clicks"}]]'),
    )

    dash_duo.find_element("button[id*='btn1\":0']").click()
    dash_duo.find_element("button[id*='btn1\":0']").click()

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ('[{"id":"btn0","property":"n_clicks","value":1},'
         '[{"id":{"btn1":0},"property":"n_clicks","value":2},'
         '{"id":{"btn1":1},"property":"n_clicks"},'
         '{"id":{"btn1":2},"property":"n_clicks"}]]'),
    )

    dash_duo.find_element("button[id*='btn1\":2']").click()

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ('[{"id":"btn0","property":"n_clicks","value":1},'
         '[{"id":{"btn1":0},"property":"n_clicks","value":2},'
         '{"id":{"btn1":1},"property":"n_clicks"},'
         '{"id":{"btn1":2},"property":"n_clicks","value":1}]]'),
    )
Example #10
0
def test_grva008_shapes_not_lost(dash_dcc):
    # See issue #879 and pr #905
    app = Dash(__name__)

    fig = {"data": [], "layout": {"dragmode": "drawrect"}}
    graph = dcc.Graph(id="graph", figure=fig, style={"height": "400px"})

    app.layout = html.Div(
        [
            graph,
            html.Br(),
            html.Button(id="button", children="Clone figure"),
            html.Div(id="output", children=""),
        ]
    )

    app.clientside_callback(
        """
        function clone_figure(_, figure) {
            const new_figure = {...figure};
            const shapes = new_figure.layout.shapes || [];
            return [new_figure, shapes.length];
        }
        """,
        Output("graph", "figure"),
        Output("output", "children"),
        Input("button", "n_clicks"),
        State("graph", "figure"),
    )

    dash_dcc.start_server(app)
    button = dash_dcc.wait_for_element("#button")
    dash_dcc.wait_for_text_to_equal("#output", "0")

    # Draw a shape
    dash_dcc.click_and_hold_at_coord_fractions("#graph", 0.25, 0.25)
    dash_dcc.move_to_coord_fractions("#graph", 0.35, 0.75)
    dash_dcc.release()

    # Click to trigger an update of the output, the shape should survive
    dash_dcc.wait_for_text_to_equal("#output", "0")
    button.click()
    dash_dcc.wait_for_text_to_equal("#output", "1")

    # Draw another shape
    dash_dcc.click_and_hold_at_coord_fractions("#graph", 0.75, 0.25)
    dash_dcc.move_to_coord_fractions("#graph", 0.85, 0.75)
    dash_dcc.release()

    # Click to trigger an update of the output, the shape should survive
    dash_dcc.wait_for_text_to_equal("#output", "1")
    button.click()
    dash_dcc.wait_for_text_to_equal("#output", "2")

    assert dash_dcc.get_logs() == []
def test_clsd004_clientside_multiple_outputs(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        dcc.Input(id="input", value=1),
        dcc.Input(id="output-1"),
        dcc.Input(id="output-2"),
        dcc.Input(id="output-3"),
        dcc.Input(id="output-4"),
    ])

    app.clientside_callback(
        ClientsideFunction("clientside", "add_to_four_outputs"),
        [
            Output("output-1", "value"),
            Output("output-2", "value"),
            Output("output-3", "value"),
            Output("output-4", "value"),
        ],
        [Input("input", "value")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    for selector, expected in [
        ["#input", "1"],
        ["#output-1", "2"],
        ["#output-2", "3"],
        ["#output-3", "4"],
        ["#output-4", "5"],
    ]:
        dash_duo.wait_for_text_to_equal(selector, expected)

    dash_duo.wait_for_element("#input").send_keys("1")

    for selector, expected in [
        ["#input", "11"],
        ["#output-1", "12"],
        ["#output-2", "13"],
        ["#output-3", "14"],
        ["#output-4", "15"],
    ]:
        dash_duo.wait_for_text_to_equal(selector, expected)
def test_clsd006_PreventUpdate(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        dcc.Input(id="first", value=1),
        dcc.Input(id="second", value=1),
        dcc.Input(id="third", value=1),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="add1_prevent_at_11"),
        Output("second", "value"),
        [Input("first", "value")],
        [State("second", "value")],
    )

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="add1_prevent_at_11"),
        Output("third", "value"),
        [Input("second", "value")],
        [State("third", "value")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    dash_duo.wait_for_text_to_equal("#first", "1")
    dash_duo.wait_for_text_to_equal("#second", "2")
    dash_duo.wait_for_text_to_equal("#third", "2")

    dash_duo.find_element("#first").send_keys("1")

    dash_duo.wait_for_text_to_equal("#first", "11")
    dash_duo.wait_for_text_to_equal("#second", "2")
    dash_duo.wait_for_text_to_equal("#third", "2")

    dash_duo.find_element("#first").send_keys("1")

    dash_duo.wait_for_text_to_equal("#first", "111")
    dash_duo.wait_for_text_to_equal("#second", "3")
    dash_duo.wait_for_text_to_equal("#third", "3")
def test_clsd009_clientside_callback_context_triggered(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        html.Button("btn0", id="btn0"),
        html.Button("btn1:0", id={"btn1": 0}),
        html.Button("btn1:1", id={"btn1": 1}),
        html.Button("btn1:2", id={"btn1": 2}),
        html.Div(id="output-clientside", style={"font-family": "monospace"}),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="triggered_to_str"),
        Output("output-clientside", "children"),
        [Input("btn0", "n_clicks"),
         Input({"btn1": ALL}, "n_clicks")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    dash_duo.wait_for_text_to_equal("#output-clientside", "")

    dash_duo.find_element("#btn0").click()

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        "btn0.n_clicks = 1",
    )

    dash_duo.find_element("button[id*='btn1\":0']").click()
    dash_duo.find_element("button[id*='btn1\":0']").click()

    dash_duo.wait_for_text_to_equal("#output-clientside",
                                    '{"btn1":0}.n_clicks = 2')

    dash_duo.find_element("button[id*='btn1\":2']").click()

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        '{"btn1":2}.n_clicks = 1',
    )
Example #14
0
    def apply(cls, app: Dash) -> None:

        for cb in cls.REGISTRY:
            try:
                if isinstance(cb, deferred_callback):
                    LOG.info(f"Applying deferred callback {cb.f.__name__}")
                    app.callback(*cb.args, **cb.kwargs)(cb.f)
                else:
                    LOG.info(
                        f"Applying deferred clientside callback {cb.name}")
                    app.clientside_callback(*cb.args, **cb.kwargs)
            except Exception as e:
                LOG.error(f"Callback {type(cb)=}, {cb=}, "
                          f"{getattr(cb, 'name', None)=}"
                          f"{getattr(cb, 'f', None)=}"
                          f"failed")
                LOG.error(e)
                continue
Example #15
0
def test_rdls004_update_title_chained_callbacks(dash_duo, update_title):
    initial_title = "Initial Title"
    app = Dash("Dash", title=initial_title, update_title=update_title)
    lock = Lock()

    app.layout = html.Div(children=[
        html.Button(id="page-title", n_clicks=0, children="Page Title"),
        html.Div(id="page-output"),
        html.Div(id="final-output"),
    ])
    app.clientside_callback(
        """
        function(n_clicks) {
            if (n_clicks > 0) {
                document.title = 'Page ' + n_clicks;
            }
            return n_clicks;
        }
        """,
        Output("page-output", "children"),
        [Input("page-title", "n_clicks")],
    )

    @app.callback(Output("final-output", "children"),
                  [Input("page-output", "children")])
    def update(n):
        with lock:
            return n

    # check for original title after loading
    dash_duo.start_server(app)
    dash_duo.wait_for_text_to_equal("#final-output", "0")
    until(lambda: dash_duo.driver.title == initial_title, timeout=1)

    with lock:
        dash_duo.find_element("#page-title").click()
        # check for update-title while processing the serverside callback
        if update_title:
            until(lambda: dash_duo.driver.title == update_title, timeout=1)
        else:
            until(lambda: dash_duo.driver.title == "Page 1", timeout=1)

    dash_duo.wait_for_text_to_equal("#final-output", "1")
    until(lambda: dash_duo.driver.title == "Page 1", timeout=1)
def test_clsd003_clientside_exceptions_halt_subsequent_updates(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        dcc.Input(id="first", value=1),
        dcc.Input(id="second"),
        dcc.Input(id="third")
    ])

    app.clientside_callback(
        ClientsideFunction("clientside", "add1_break_at_11"),
        Output("second", "value"),
        [Input("first", "value")],
    )

    app.clientside_callback(
        ClientsideFunction("clientside", "add1_break_at_11"),
        Output("third", "value"),
        [Input("second", "value")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    test_cases = [["#first", "1"], ["#second", "2"], ["#third", "3"]]
    for selector, expected in test_cases:
        dash_duo.wait_for_text_to_equal(selector, expected)

    first_input = dash_duo.wait_for_element("#first")
    first_input.send_keys("1")
    # clientside code will prevent the update from occurring
    test_cases = [["#first", "11"], ["#second", "2"], ["#third", "3"]]
    for selector, expected in test_cases:
        dash_duo.wait_for_text_to_equal(selector, expected)

    first_input.send_keys("1")

    # the previous clientside code error should not be fatal:
    # subsequent updates should still be able to occur
    test_cases = [["#first", "111"], ["#second", "112"], ["#third", "113"]]
    for selector, expected in test_cases:
        dash_duo.wait_for_text_to_equal(selector, expected)
Example #17
0
def test_clsd018_clientside_inline_async_function(dash_duo):
    app = Dash(__name__)

    app.layout = html.Div([
        html.Div(id="input", children=["initial"]),
        html.Div(id="output-div"),
    ])

    app.clientside_callback(
        """
        async function(input) {
            return input + "-inline";
        }
        """,
        Output("output-div", "children"),
        Input("input", "children"),
    )

    dash_duo.start_server(app)
    dash_duo.wait_for_text_to_equal("#output-div", "initial-inline")
Example #18
0
def test_clsd005_clientside_fails_when_returning_a_promise(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Div(id="input", children="hello"),
        html.Div(id="side-effect"),
        html.Div(id="output", children="output"),
    ])

    app.clientside_callback(
        ClientsideFunction("clientside", "side_effect_and_return_a_promise"),
        Output("output", "children"),
        [Input("input", "children")],
    )

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#input", "hello")
    dash_duo.wait_for_text_to_equal("#side-effect", "side effect")
    dash_duo.wait_for_text_to_equal("#output", "output")
Example #19
0
def test_clsd019_clientside_inline_promise(dash_duo):
    app = Dash(__name__)

    app.layout = html.Div([
        html.Div(id="input", children=["initial"]),
        html.Div(id="output-div"),
    ])

    app.clientside_callback(
        """
        function(inputValue) {
            return new Promise(function (resolve) {
                resolve(inputValue + "-inline");
            });
        }
        """,
        Output("output-div", "children"),
        Input("input", "children"),
    )

    dash_duo.start_server(app)
    dash_duo.wait_for_text_to_equal("#output-div", "initial-inline")
def setup_callbacks(app: dash.Dash, cache: cache_int.CacheInterface) -> None:
    create_upload_callback(app, "compliance-report", cache)
    create_upload_callback(app, "training-report", cache)
    create_button_callback(app, "button", "compliance-report", "training-report", cache)

    # Show a waiting spinner on clicking the button
    app.clientside_callback(
        ClientsideFunction(
            namespace="compliance",
            function_name="new_dashboard_spinner"
        ),
        Output("button", "data-popup"),
        [Input("button", "n_clicks")]
    )

    # If components are missing, re-hide the spinner
    app.clientside_callback(
        ClientsideFunction(
            namespace="compliance",
            function_name="new_dashboard_spinner_hide"
        ),
        Output("button", "data-popup-hide"),
        [Input("button", "children")]
    )
Example #21
0
def test_clsd015_clientside_chained_callbacks_returning_promise(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div([
        html.Div(id="input", children=["initial"]),
        html.Div(id="div-1"),
        html.Div(id="div-2"),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="chained_promise"),
        Output("div-1", "children"),
        Input("input", "children"),
    )

    @app.callback(Output("div-2", "children"), Input("div-1", "children"))
    def callback(value):
        return value + "-twice"

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#div-1", "initial-chained")
    dash_duo.wait_for_text_to_equal("#div-2", "initial-chained-twice")
def update_figure_clientside(app: Dash, get_uuid: Callable) -> None:
    @app.callback(
        Output(
            {
                "id": get_uuid("clientside"),
                "plotly_attribute": "update_layout"
            }, "data"),
        Input({
            "id": get_uuid("plotly_layout"),
            "layout_attribute": ALL
        }, "value"),
        Input(get_uuid("plotly_uirevision"), "value"),
    )
    def _update_layout(layout_attributes: Optional[List],
                       uirevision: str) -> Dict:
        """Store plotly layout options from user selections in a dcc.Store"""
        if layout_attributes is None:
            return {}
        layout = {}
        for ctx in callback_context.inputs_list[0]:
            layout[ctx["id"]["layout_attribute"]] = ctx.get("value", None)
        layout["uirevision"] = str(uirevision) if uirevision else None
        return layout

    @app.callback(
        Output(
            {
                "id": get_uuid("clientside"),
                "plotly_attribute": "plotly_layout"
            }, "data"),
        Input(
            {
                "id": get_uuid("clientside"),
                "plotly_attribute": "initial_layout"
            }, "data"),
        Input(
            {
                "id": get_uuid("clientside"),
                "plotly_attribute": "update_layout"
            }, "data"),
    )
    def _update_plot_layout(initial_layout: dict, update_layout: dict) -> Dict:
        if initial_layout is None:
            raise PreventUpdate
        fig = Figure({"layout": initial_layout})
        if update_layout is not None:
            fig.update_layout(update_layout)
        return fig["layout"]

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="set_dcc_figure"),
        Output(get_uuid("graph"), "figure"),
        Input(
            {
                "id": get_uuid("clientside"),
                "plotly_attribute": "plotly_layout"
            }, "data"),
        Input({
            "id": get_uuid("clientside"),
            "plotly_attribute": "plotly_data"
        }, "data"),
    )
def test_clsd012_clientside_callback_context_states(dash_duo):
    app = Dash(__name__, assets_folder="clientside_assets")

    app.layout = html.Div([
        dcc.Input(id="in0"),
        dcc.Input(id={"in1": 0}),
        dcc.Input(id={"in1": 1}),
        dcc.Input(id={"in1": 2}),
        html.Div(id="output-clientside", style={"font-family": "monospace"}),
    ])

    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="states_to_str"),
        Output("output-clientside", "children"),
        [Input("in0", "n_submit"),
         Input({"in1": ALL}, "n_submit")],
        [State("in0", "value"),
         State({"in1": ALL}, "value")],
    )

    class DashView(BaseDashView):
        dash = app

    dash_duo.start_server(DashView)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ("in0.value = null, "
         '{"in1":0}.value = null, '
         '{"in1":1}.value = null, '
         '{"in1":2}.value = null'),
    )

    dash_duo.find_element("#in0").send_keys("test 0" + Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ("in0.value = test 0, "
         '{"in1":0}.value = null, '
         '{"in1":1}.value = null, '
         '{"in1":2}.value = null'),
    )

    dash_duo.find_element("input[id*='in1\":0']").send_keys("test 1" +
                                                            Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ("in0.value = test 0, "
         '{"in1":0}.value = test 1, '
         '{"in1":1}.value = null, '
         '{"in1":2}.value = null'),
    )

    dash_duo.find_element("input[id*='in1\":2']").send_keys("test 2" +
                                                            Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ("in0.value = test 0, "
         '{"in1":0}.value = test 1, '
         '{"in1":1}.value = null, '
         '{"in1":2}.value = test 2'),
    )
def update_intersection(
    app: Dash,
    get_uuid: Callable,
    surface_set_models: Dict[str, SurfaceSetModel],
    well_set_model: WellSetModel,
    color_picker: ColorPicker,
    zonelog: Optional[str] = None,
) -> None:
    @app.callback(
        Output(get_uuid("intersection-graph-data"), "data"),
        Input(get_uuid("apply-intersection-data-selections"), "n_clicks"),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "element": "source"
            },
            "value",
        ),
        Input({
            "id": get_uuid("map"),
            "element": "stored_polyline"
        }, "data"),
        Input({
            "id": get_uuid("map"),
            "element": "stored_xline"
        }, "data"),
        Input({
            "id": get_uuid("map"),
            "element": "stored_yline"
        }, "data"),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "well"
        }, "value"),
        Input(get_uuid("realization-store"), "data"),
        State(
            {
                "id": get_uuid("intersection-data"),
                "element": "surface_attribute"
            },
            "value",
        ),
        State({
            "id": get_uuid("intersection-data"),
            "element": "surface_names"
        }, "value"),
        State(
            {
                "id": get_uuid("intersection-data"),
                "element": "calculation"
            },
            "value",
        ),
        State({
            "id": get_uuid("intersection-data"),
            "element": "ensembles"
        }, "value"),
        State({
            "id": get_uuid("intersection-data"),
            "element": "resolution"
        }, "value"),
        State({
            "id": get_uuid("intersection-data"),
            "element": "extension"
        }, "value"),
        State(color_picker.color_store_id, "data"),
    )
    # pylint: disable=too-many-arguments: disable=too-many-branches, too-many-locals
    def _store_intersection_traces(
        _apply_click: Optional[int],
        intersection_source: str,
        polyline: Optional[List],
        xline: Optional[List],
        yline: Optional[List],
        wellname: str,
        realizations: List[int],
        surfaceattribute: str,
        surfacenames: List[str],
        statistics: List[str],
        ensembles: str,
        resolution: float,
        extension: int,
        color_list: List[str],
    ) -> List:
        """Generate plotly traces for intersection figure and store clientside"""

        # TODO(Sigurd) Can we prohibit clearing of the sampling and extension input
        # fields (dcc.Input) in the client? Until we can, we must guard against sampling
        # and extension being None. This happens when the user clears the input field and we
        # have not yet found a solution that prohibits the input field from being cleared.
        # The situation can be slightly remedied by setting required=True which will highlight
        # the missing value with a red rectangle.
        if any(val is None for val in [resolution, extension]):
            raise PreventUpdate
        traces = []

        if intersection_source == "polyline":
            if polyline is None:
                return []
            fence_spec = get_fencespec_from_polyline(polyline,
                                                     distance=resolution,
                                                     atleast=5,
                                                     nextend=extension /
                                                     resolution)
        elif intersection_source == "xline":
            if xline is None:
                return []
            fence_spec = get_fencespec_from_polyline(xline,
                                                     distance=resolution,
                                                     atleast=5,
                                                     nextend=extension /
                                                     resolution)
        elif intersection_source == "yline":
            if yline is None:
                return []
            fence_spec = get_fencespec_from_polyline(yline,
                                                     distance=resolution,
                                                     atleast=5,
                                                     nextend=extension /
                                                     resolution)
        else:
            fence_spec = well_set_model.get_fence(
                well_name=wellname,
                distance=resolution,
                atleast=5,
                nextend=extension / resolution,
            )

        realizations = [int(real) for real in realizations]
        for ensemble in ensembles:
            surfset = surface_set_models[ensemble]
            for surfacename in surfacenames:
                color = color_picker.get_color(
                    color_list=color_list,
                    filter_query={
                        "surfacename": surfacename,
                        "ensemble": ensemble,
                    },
                )
                showlegend = True

                if statistics is not None:
                    for stat in ["Mean", "Min", "Max"]:
                        if stat in statistics:
                            trace = get_plotly_trace_statistical_surface(
                                surfaceset=surfset,
                                fence_spec=fence_spec,
                                calculation=stat,
                                legendname=f"{surfacename}({ensemble})",
                                name=surfacename,
                                attribute=surfaceattribute,
                                realizations=realizations,
                                showlegend=showlegend,
                                color=color,
                            )
                            traces.append(trace)
                            showlegend = False
                    if "Uncertainty envelope" in statistics:
                        envelope_traces = get_plotly_traces_uncertainty_envelope(
                            surfaceset=surfset,
                            fence_spec=fence_spec,
                            legendname=f"{surfacename}({ensemble})",
                            name=surfacename,
                            attribute=surfaceattribute,
                            realizations=realizations,
                            showlegend=showlegend,
                            color=color,
                        )
                        traces.extend(envelope_traces)
                        showlegend = False
                    if "Realizations" in statistics:
                        for real in realizations:
                            trace = get_plotly_trace_realization_surface(
                                surfaceset=surfset,
                                fence_spec=fence_spec,
                                legendname=f"{surfacename}({ensemble})",
                                name=surfacename,
                                attribute=surfaceattribute,
                                realization=real,
                                color=color,
                                showlegend=showlegend,
                            )
                            traces.append(trace)
                            showlegend = False
        if intersection_source == "well":
            well = well_set_model.get_well(wellname)
            traces.append(get_plotly_trace_well_trajectory(well))
            if well.zonelogname is not None:
                traces.extend(get_plotly_zonelog_trace(well, zonelog))

        return traces

    @app.callback(
        Output(get_uuid("intersection-graph-layout"), "data"),
        Input(get_uuid("intersection-graph-data"), "data"),
        Input(get_uuid("initial-intersection-graph-layout"), "data"),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "element": "source"
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_locks"
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_min"
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_max"
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "ui_options"
            },
            "value",
        ),
        State(get_uuid("leaflet-map1"), "polyline_points"),
        State({
            "id": get_uuid("intersection-data"),
            "element": "well"
        }, "value"),
    )
    # pylint: disable=too-many-arguments, too-many-branches
    def _store_intersection_layout(
        data: List,
        initial_layout: Optional[dict],
        intersection_source: str,
        zrange_locks: str,
        zmin: Optional[float],
        zmax: Optional[float],
        ui_options: List[str],
        polyline: Optional[List],
        wellname: str,
    ) -> Dict:
        """Store intersection layout configuration clientside"""
        ctx = callback_context.triggered[0]
        if "ui_options" in ctx["prop_id"]:
            raise PreventUpdate

        # Set default layout
        layout: Dict = {
            "hovermode": "closest",
            "yaxis": {
                "autorange": "reversed",
                "showgrid": False,
                "zeroline": False,
                "title": "True vertical depth",
            },
            "xaxis": {
                "showgrid": False,
                "zeroline": False,
                "title": "Lateral resolution",
            },
            "plot_bgcolor": "rgba(0, 0, 0, 0)",
            "paper_bgcolor": "rgba(0, 0, 0, 0)",
        }

        # Update title to reflect source of cross-section calculation
        annotation_title = ["A", "A'"]
        if intersection_source in ["polyline", "xline", "yline"]:
            layout.update({
                "title":
                f"Intersection along {intersection_source} shown in Surface A"
            })
            layout.get("xaxis", {}).update({"autorange": True})
            annotation_title = ["B", "B'"]
        if intersection_source == "well":
            layout["title"] = f"Intersection along well: {wellname}"

        # Set A-B annotations on plot
        layout["annotations"] = [
            {
                "x": 0,
                "y": 1,
                "xref": "paper",
                "yref": "paper",
                "text": f"<b>{annotation_title[0]}</b>",
                "font": {
                    "size": 40
                },
                "showarrow": False,
            },
            {
                "x": 1,
                "y": 1,
                "xref": "paper",
                "yref": "paper",
                "text": f"<b>{annotation_title[1]}</b>",
                "font": {
                    "size": 40
                },
                "showarrow": False,
            },
        ]
        # Update layout with any values provided from yaml configuration
        if initial_layout is not None:
            layout.update(initial_layout)

        # Return emptly plot layout if surface is source but no polyline is drawn
        if intersection_source == "polyline" and polyline is None:
            layout.update({
                "title":
                "Draw a random line from the toolbar on Surface A",
            })
            return layout

        # Add any interactivily set range options
        if ui_options:
            if "uirevision" in ui_options:
                layout.update({"uirevision": "keep"})

        user_range = []
        if not (zmax is None and zmin is None):
            if "lock" in zrange_locks:
                if zmax is None:
                    zmax = max(
                        max(x for x in item["y"] if x is not None)
                        for item in data)
                if zmin is None:
                    zmin = min(
                        min(x for x in item["y"] if x is not None)
                        for item in data)
                user_range = [zmax, zmin]

            if "truncate" in zrange_locks:
                zmin_data = min(
                    min(x for x in item["y"] if x is not None)
                    for item in data)
                zmax_data = max(
                    max(x for x in item["y"] if x is not None)
                    for item in data)
                zmax = zmax if zmax is not None else zmax_data
                zmin = zmin if zmin is not None else zmin_data

                user_range = [min(zmax, zmax_data), max(zmin, zmin_data)]

        # Set y-axis range from depth range input if specified
        if user_range:
            layout.get("yaxis", {}).update({"autorange": False})
            layout.get("yaxis", {}).update(range=user_range)
        # Else autocalculate range if not intersecting a well
        elif intersection_source != "well":
            if "range" in layout.get("yaxis", {}):
                del layout["yaxis"]["range"]
            layout.get("yaxis", {}).update({"autorange": "reversed"})

        # Remove xaxis zero line
        layout.get("xaxis", {}).update({"zeroline": False, "showline": False})
        return layout

    # Store intersection data and layout to the plotly figure
    # Done clientside for performance
    app.clientside_callback(
        ClientsideFunction(namespace="clientside",
                           function_name="set_dcc_figure"),
        Output(get_uuid("intersection-graph"), "figure"),
        Input(get_uuid("intersection-graph-layout"), "data"),
        State(get_uuid("intersection-graph-data"), "data"),
    )

    @app.callback(
        Output(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_min"
            },
            "max",
        ),
        Output(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_max"
            },
            "min",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_min"
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "settings": "zrange_max"
            },
            "value",
        ),
    )
    def _set_min_max_for_range_input(
        zmin: Optional[float],
        zmax: Optional[float],
    ) -> Tuple[Optional[float], Optional[float]]:
        ctx = callback_context.triggered[0]
        if ctx["prop_id"] == ".":
            raise PreventUpdate

        return zmax, zmin

    @app.callback(
        Output(get_uuid("apply-intersection-data-selections"), "style"),
        Output(
            {
                "id": get_uuid("intersection-data"),
                "element": "stored_manual_update_options",
            },
            "data",
        ),
        Input(get_uuid("apply-intersection-data-selections"), "n_clicks"),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "element": "surface_attribute"
            },
            "value",
        ),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "surface_names"
        }, "value"),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "element": "calculation"
            },
            "value",
        ),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "ensembles"
        }, "value"),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "resolution"
        }, "value"),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "extension"
        }, "value"),
        Input(color_picker.color_store_id, "data"),
        State(
            {
                "id": get_uuid("intersection-data"),
                "element": "stored_manual_update_options",
            },
            "data",
        ),
    )
    def _update_apply_button(
        _apply_click: Optional[int],
        surfaceattribute: str,
        surfacenames: List[str],
        statistics: List[str],
        ensembles: str,
        resolution: float,
        extension: int,
        color_list: List[str],
        previous_settings: Dict,
    ) -> Tuple[Dict, Dict]:

        ctx = callback_context.triggered[0]

        new_settings = {
            "surface_attribute": surfaceattribute,
            "surface_names": surfacenames,
            "calculation": statistics,
            "ensembles": ensembles,
            "resolution": resolution,
            "extension": extension,
            "colors": color_list,
        }
        # store selected settings if initial callback or apply button is pressed
        if ("apply-intersection-data-selections" in ctx["prop_id"]
                or ctx["prop_id"] == "."):
            return {"background-color": "#E8E8E8"}, new_settings

        element = ("colors" if "colorpicker" in ctx["prop_id"] else json.loads(
            ctx["prop_id"].replace(".value", "")).get("element"))
        if new_settings[element] != previous_settings[element]:
            return {
                "background-color": "#7393B3",
                "color": "#fff"
            }, previous_settings
        return {"background-color": "#E8E8E8"}, previous_settings
Example #25
0
    def set_callbacks(self, app: Dash) -> None:
        @app.callback(
            Output(self.ids("label"), "disabled"),
            Input(self.ids("plot-options"), "value"),
        )
        def _disable_label(plot_options: List) -> bool:
            return "Show realization points" in plot_options

        @app.callback(
            Output(self.ids("graph-wrapper"), "style"),
            Output(self.ids("table-wrapper"), "style"),
            Input(self.ids("plot-or-table"), "value"),
            State(self.ids("graph-wrapper"), "style"),
            State(self.ids("table-wrapper"), "style"),
        )
        def _set_visualization(
            viz_type: str, graph_style: dict, table_style: dict
        ) -> Tuple[Dict[str, str], Dict[str, str]]:
            if viz_type == "bars":
                graph_style.update({"display": "inline"})
                table_style.update({"display": "none"})
            if viz_type == "table":
                graph_style.update({"display": "none"})
                table_style.update({"display": "inline"})
            return graph_style, table_style

        app.clientside_callback(
            ClientsideFunction(
                namespace="clientside", function_name="get_client_height"
            ),
            Output(self.ids("client-height-pixels"), "data"),
            Input(self.ids("plot-options"), "value"),
        )

        @app.callback(
            Output(self.ids("tornado-graph"), "figure"),
            Output(self.ids("tornado-table"), "data"),
            Output(self.ids("tornado-table"), "columns"),
            Output(self.ids("high-low-storage"), "data"),
            Input(self.ids("reference"), "value"),
            Input(self.ids("scale"), "value"),
            Input(self.ids("plot-options"), "value"),
            Input(self.ids("label"), "value"),
            Input(self.ids("storage"), "data"),
            Input(self.ids("sens_filter"), "value"),
            State(self.ids("client-height-pixels"), "data"),
        )
        def _calc_tornado(
            reference: str,
            scale: str,
            plot_options: List,
            label_option: str,
            data: Union[str, bytes, bytearray],
            sens_filter: List[str],
            client_height: Optional[int],
        ) -> Tuple[dict, dict]:
            if not data:
                raise PreventUpdate
            plot_options = plot_options if plot_options else []
            data = json.loads(data)
            if not isinstance(data, dict):
                raise PreventUpdate
            values = pd.DataFrame(data["data"], columns=["REAL", "VALUE"])
            realizations = self.realizations.loc[
                self.realizations["ENSEMBLE"] == data["ENSEMBLE"]
            ]

            design_and_responses = pd.merge(values, realizations, on="REAL")
            if sens_filter is not None:
                if reference not in sens_filter:
                    sens_filter.append(reference)
                design_and_responses = design_and_responses.loc[
                    design_and_responses["SENSNAME"].isin(sens_filter)
                ]
            tornado_data = TornadoData(
                dframe=design_and_responses,
                response_name=data.get("response_name"),
                reference=reference,
                scale="Percentage" if scale == "Relative value (%)" else "Absolute",
                cutbyref="Remove sensitivites with no impact" in plot_options,
            )

            figure_height = (
                client_height * 0.59
                if "Fit all bars in figure" in plot_options
                and client_height is not None
                else max(100 * len(tornado_data.tornadotable["sensname"].unique()), 200)
            )
            tornado_figure = TornadoBarChart(
                tornado_data=tornado_data,
                plotly_theme=self.plotly_theme,
                figure_height=figure_height,
                label_options=label_option,
                number_format=data.get("number_format", ""),
                unit=data.get("unit", ""),
                spaced=data.get("spaced", True),
                locked_si_prefix=data.get("locked_si_prefix", None),
                use_true_base=scale == "True value",
                show_realization_points="Show realization points" in plot_options,
                color_by_sensitivity="Color bars by sensitivity" in plot_options,
            )
            tornado_table = TornadoTable(tornado_data=tornado_data)
            return (
                tornado_figure.figure,
                tornado_table.as_plotly_table,
                tornado_table.columns,
                tornado_data.low_high_realizations_list,
            )

        if self.allow_click:

            @app.callback(
                Output(self.ids("click-store"), "data"),
                [
                    Input(self.ids("tornado-graph"), "clickData"),
                    Input(self.ids("reset"), "n_clicks"),
                    State(self.ids("high-low-storage"), "data"),
                ],
            )
            def _save_click_data(
                data: dict, nclicks: Optional[int], sens_reals: dict
            ) -> str:
                if (
                    callback_context.triggered is None
                    or sens_reals is None
                    or data is None
                ):
                    raise PreventUpdate
                ctx = callback_context.triggered[0]["prop_id"].split(".")[0]
                if ctx == self.ids("reset") and nclicks:
                    return json.dumps(
                        {
                            "real_low": [],
                            "real_high": [],
                            "sens_name": None,
                        }
                    )
                sensname = data["points"][0]["y"]
                real_high = sens_reals[sensname]["real_high"]
                real_low = sens_reals[sensname]["real_low"]
                return json.dumps(
                    {
                        "real_low": real_low,
                        "real_high": real_high,
                        "sens_name": sensname,
                    }
                )
Example #26
0
                html.Div(id='wordcloud-div', children=[dcc.Loading(children=[
                    html.Img(id="wordcloud-img", style={
                        "max-width": "80%", "height": "auto", "display": "block",
                        "margin-left": "auto", "margin-right": "auto"})])]),
            ]
        ),
        html.Div(children=[html.P("Created with ❤️ and Python")], className="footer",
                    style={"text-align": "center", "height": "50px"})
])

# Get screen resolution on client side
app.clientside_callback(
    """
    function(url) {
        return "".concat(window.innerWidth, "x", window.innerHeight);
    }
    """,
    Output('size', 'children'),
    [Input('url', 'href')]
)

# Update Upload Text
@app.callback(
    Output(component_id='upload-text', component_property='children'),
    [Input('upload-text', 'filename'),],
    [State('upload-text', 'last_modified')])
def update_upload(file_name, last_modified):
    if file_name is None:
        return html.Div([
            html.A('Upload'),
            ' Your Own Book'
Example #27
0
def init_graphing(server):

    external_stylesheets = [themes.BOOTSTRAP]

    app = Dash('__main__',
               server=server,
               url_base_pathname=url_base,
               assets_folder='static',
               external_stylesheets=external_stylesheets)

    data = pd.read_csv('static/Sun.txt')

    df = data[data['ACCEPT'] == True]

    rv_figure = scatter(df, x="MJD", y="V")
    rv_figure.update_layout(clickmode='event')

    app.layout = html.Div([
        dcc.Loading([
            dcc.Graph(id='rv-plot',
                      className="pt-5",
                      config={
                          "displaylogo":
                          False,
                          'modeBarButtonsToRemove':
                          ['pan2d', 'lasso2d', 'autoscale']
                      }),
            dcc.Input(id="x1", type="number", placeholder="", debounce=True),
            dcc.Input(id="x2", type="number", placeholder="", debounce=True),
            html.Br(),
            dcc.Input(id="y1", type="number", placeholder="", debounce=True),
            dcc.Input(id="y2", type="number", placeholder="", debounce=True),
        ]),
        html.Div(id='data', children=df.to_json(), className='d-none'),
        html.Div([
            dcc.Markdown("""
                **Click Data**
                Click on points in the graph.
            """),
            html.Pre(id='click-data'),
        ]),
        html.Div([], id='spec-container'),
    ])

    app.clientside_callback(
        output=Output('click-data', 'children'),
        inputs=[Input('rv-plot', 'clickData'),
                State('data', 'children')],
        clientside_function=ClientsideFunction(namespace='graphing',
                                               function_name='clickData'))

    app.clientside_callback(
        output=Output('rv-plot', 'figure'),
        inputs=[
            Input('x1', 'value'),
            Input('x2', 'value'),
            Input('y1', 'value'),
            Input('y2', 'value'),
            State('data', 'children')
        ],
        clientside_function=ClientsideFunction(namespace='graphing',
                                               function_name='zoomfunc'))

    @app.callback(Output('spec-container', 'children'),
                  Input('click-data', 'children'))
    def getGraph(children):
        if (children == None):
            raise PreventUpdate

        data = mongo.db.onespectrum.find({"FILENAME": "Sun_200911.1062"}, {
            "_id": 0,
            "# WAVE": 1,
            "FLUX": 1
        })

        data_dict = {'wave': [], 'flux': []}
        count = 0
        realCount = 0
        print(data[0]["# WAVE"])
        for x in data:
            count += 1
            if (x["# WAVE"] != '' and count % 10 == 0):
                data_dict['wave'].append(float(x["# WAVE"]))
                data_dict['flux'].append(float(x["FLUX"]))
                realCount += 1
        print("Get")
        spec = line(data_dict, x="wave", y="flux", render_mode="webgl")
        print(realCount)

        return [
            children,
            dcc.Graph(id='spec-plot', figure=rv_figure, className="pt-5")
        ]

    with server.test_client() as client:
        client.get('/')
        app.index_string = render_template('data_page.html')
Example #28
0
def test_clsd002_chained_serverside_clientside_callbacks(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div(
        [
            html.Label("x"),
            dcc.Input(id="x", value=3),
            html.Label("y"),
            dcc.Input(id="y", value=6),
            # clientside
            html.Label("x + y (clientside)"),
            dcc.Input(id="x-plus-y"),
            # server-side
            html.Label("x+y / 2 (serverside)"),
            dcc.Input(id="x-plus-y-div-2"),
            # server-side
            html.Div(
                [
                    html.Label("Display x, y, x+y/2 (serverside)"),
                    dcc.Textarea(id="display-all-of-the-values"),
                ]
            ),
            # clientside
            html.Label("Mean(x, y, x+y, x+y/2) (clientside)"),
            dcc.Input(id="mean-of-all-values"),
        ]
    )

    app.clientside_callback(
        ClientsideFunction("clientside", "add"),
        Output("x-plus-y", "value"),
        [Input("x", "value"), Input("y", "value")],
    )

    call_counts = {"divide": Value("i", 0), "display": Value("i", 0)}

    @app.callback(Output("x-plus-y-div-2", "value"), [Input("x-plus-y", "value")])
    def divide_by_two(value):
        call_counts["divide"].value += 1
        return float(value) / 2.0

    @app.callback(
        Output("display-all-of-the-values", "value"),
        [
            Input("x", "value"),
            Input("y", "value"),
            Input("x-plus-y", "value"),
            Input("x-plus-y-div-2", "value"),
        ],
    )
    def display_all(*args):
        call_counts["display"].value += 1
        return "\n".join([str(a) for a in args])

    app.clientside_callback(
        ClientsideFunction("clientside", "mean"),
        Output("mean-of-all-values", "value"),
        [
            Input("x", "value"),
            Input("y", "value"),
            Input("x-plus-y", "value"),
            Input("x-plus-y-div-2", "value"),
        ],
    )

    dash_duo.start_server(app)

    test_cases = [
        ["#x", "3"],
        ["#y", "6"],
        ["#x-plus-y", "9"],
        ["#x-plus-y-div-2", "4.5"],
        ["#display-all-of-the-values", "3\n6\n9\n4.5"],
        ["#mean-of-all-values", str((3 + 6 + 9 + 4.5) / 4.0)],
    ]
    for selector, expected in test_cases:
        dash_duo.wait_for_text_to_equal(selector, expected)

    assert call_counts["display"].value == 1
    assert call_counts["divide"].value == 1

    x_input = dash_duo.wait_for_element_by_css_selector("#x")
    x_input.send_keys("1")

    test_cases = [
        ["#x", "31"],
        ["#y", "6"],
        ["#x-plus-y", "37"],
        ["#x-plus-y-div-2", "18.5"],
        ["#display-all-of-the-values", "31\n6\n37\n18.5"],
        ["#mean-of-all-values", str((31 + 6 + 37 + 18.5) / 4.0)],
    ]
    for selector, expected in test_cases:
        dash_duo.wait_for_text_to_equal(selector, expected)

    assert call_counts["display"].value == 2
    assert call_counts["divide"].value == 2
Example #29
0
def test_clsd013_clientside_callback_context_states_list(dash_duo):
    app = Dash(__name__, assets_folder="assets")

    app.layout = html.Div(
        [
            dcc.Input(id="in0"),
            dcc.Input(id={"in1": 0}),
            dcc.Input(id={"in1": 1}),
            dcc.Input(id={"in1": 2}),
            html.Div(id="output-clientside", style={"font-family": "monospace"}),
        ]
    )

    app.clientside_callback(
        ClientsideFunction(namespace="clientside", function_name="states_list_to_str"),
        Output("output-clientside", "children"),
        [Input("in0", "n_submit"), Input({"in1": ALL}, "n_submit")],
        [State("in0", "value"), State({"in1": ALL}, "value")],
    )

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        (
            '[{"id":"in0","property":"value"},'
            '[{"id":{"in1":0},"property":"value"},'
            '{"id":{"in1":1},"property":"value"},'
            '{"id":{"in1":2},"property":"value"}]]'
        ),
    )

    dash_duo.find_element("#in0").send_keys("test 0" + Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        (
            '[{"id":"in0","property":"value","value":"test 0"},'
            '[{"id":{"in1":0},"property":"value"},'
            '{"id":{"in1":1},"property":"value"},'
            '{"id":{"in1":2},"property":"value"}]]'
        ),
    )

    dash_duo.find_element("input[id*='in1\":0']").send_keys("test 1" + Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        (
            '[{"id":"in0","property":"value","value":"test 0"},'
            '[{"id":{"in1":0},"property":"value","value":"test 1"},'
            '{"id":{"in1":1},"property":"value"},'
            '{"id":{"in1":2},"property":"value"}]]'
        ),
    )

    dash_duo.find_element("input[id*='in1\":2']").send_keys("test 2" + Keys.RETURN)

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        (
            '[{"id":"in0","property":"value","value":"test 0"},'
            '[{"id":{"in1":0},"property":"value","value":"test 1"},'
            '{"id":{"in1":1},"property":"value"},'
            '{"id":{"in1":2},"property":"value","value":"test 2"}]]'
        ),
    )
Example #30
0
def init_graphing(server):

    external_stylesheets = [themes.BOOTSTRAP]

    app = Dash('__main__',
               server=server,
               url_base_pathname=url_base,
               assets_folder='static',
               external_stylesheets=external_stylesheets)

    data = pd.read_csv('static/Sun.txt')

    df = data[data['ACCEPT'] == True]

    rv_figure = scatter(df, x="MJD", y="V")
    rv_figure.update_layout(clickmode='event')

    app.layout = html.Div([
        dcc.Loading([
            dcc.Graph(id='rv-plot',
                      className="pt-5",
                      config={
                          "displaylogo":
                          False,
                          'modeBarButtonsToRemove':
                          ['pan2d', 'lasso2d', 'autoscale']
                      }),
            dcc.Input(id="x1", type="number", placeholder="", debounce=True),
            dcc.Input(id="x2", type="number", placeholder="", debounce=True),
            html.Br(),
            dcc.Input(id="y1", type="number", placeholder="", debounce=True),
            dcc.Input(id="y2", type="number", placeholder="", debounce=True),
        ]),
        html.Div(id='data', children=df.to_json(), className='d-none'),
        html.Div([
            dcc.Markdown("""
                **Click Data**
                Click on points in the graph.
            """),
            html.Pre(id='click-data'),
        ]),
        html.Div([], id='spec-container'),
    ])

    app.clientside_callback(
        output=Output('click-data', 'children'),
        inputs=[Input('rv-plot', 'clickData'),
                State('data', 'children')],
        clientside_function=ClientsideFunction(namespace='graphing',
                                               function_name='clickData'))

    app.clientside_callback(
        output=Output('rv-plot', 'figure'),
        inputs=[
            Input('x1', 'value'),
            Input('x2', 'value'),
            Input('y1', 'value'),
            Input('y2', 'value'),
            State('data', 'children')
        ],
        clientside_function=ClientsideFunction(namespace='graphing',
                                               function_name='zoomfunc'))

    @app.callback(Output('spec-container', 'children'),
                  Input('click-data', 'children'))
    def getGraph(children):
        if (children == None):
            raise PreventUpdate
        return [
            children,
            dcc.Graph(id='spec-plot', figure=rv_figure, className="pt-5")
        ]

    with server.test_client() as client:
        client.get('/')
        app.index_string = render_template('data_page.html')