Beispiel #1
0
def test_cbva002_callback_return_validation():
    app = Dash(__name__)
    app.layout = html.Div([
        html.Div(id="a"),
        html.Div(id="b"),
        html.Div(id="c"),
        html.Div(id="d"),
        html.Div(id="e"),
        html.Div(id="f"),
    ])

    @app.callback(Output("b", "children"), [Input("a", "children")])
    def single(a):
        return set([1])

    single_wrapped = app.callback_map["b.children"]["callback"]

    with pytest.raises(InvalidCallbackReturnValue):
        # outputs_list (normally callback_context.outputs_list) is provided
        # by the dispatcher from the request.
        single_wrapped("aaa", outputs_list={"id": "b", "property": "children"})
        pytest.fail("not serializable")

    @app.callback([Output("c", "children"),
                   Output("d", "children")], [Input("a", "children")])
    def multi(a):
        return [1, set([2])]

    multi_wrapped = app.callback_map["..c.children...d.children.."]["callback"]

    with pytest.raises(InvalidCallbackReturnValue):
        outputs_list = [
            {
                "id": "c",
                "property": "children"
            },
            {
                "id": "d",
                "property": "children"
            },
        ]
        multi_wrapped("aaa", outputs_list=outputs_list)
        pytest.fail("nested non-serializable")

    @app.callback([Output("e", "children"),
                   Output("f", "children")], [Input("a", "children")])
    def multi2(a):
        return ["abc"]

    multi2_wrapped = app.callback_map["..e.children...f.children.."][
        "callback"]

    with pytest.raises(InvalidCallbackReturnValue):
        outputs_list = [
            {
                "id": "e",
                "property": "children"
            },
            {
                "id": "f",
                "property": "children"
            },
        ]
        multi2_wrapped("aaa", outputs_list=outputs_list)
        pytest.fail("wrong-length list")
def test_full_configuration(dash_duo, app, testdata_folder) -> None:
    webviz_settings = WebvizSettings(
        shared_settings={
            "scratch_ensembles": {
                "iter-0":
                str(testdata_folder / "01_drogon_ahm/realization-*/iter-0"),
            }
        },
        theme=default_theme,
    )
    plugin = StructuralUncertainty(
        app,
        webviz_settings,
        ensembles=["iter-0"],
        surface_attributes=["ds_extract_postprocess"],
        surface_name_filter=[
            "topvolon", "toptherys", "topvolantis", "basevolantis"
        ],
        wellfolder=testdata_folder / "observed_data" / "wells",
        wellsuffix=".rmswell",
        zonelog="Zone",
        initial_settings={
            "intersection_data": {
                "surface_names": ["topvolon", "toptherys", "topvolantis"],
                "surface_attribute": "ds_extract_postprocess",
                "ensembles": [
                    "iter-0",
                ],
                "calculation": ["Mean", "Min", "Max"],
                # - Uncertainty envelope
                "well": "55_33-1",
                "realizations": [0, 1],
                "colors": {
                    "topvolon": {
                        "iter-0": "#2C82C9"
                    },
                    "toptherys": {
                        "iter-0": "#512E34",
                    },
                    "topvolantis": {
                        "iter-0": "#EEE657",
                    },
                },
            },
            "intersection_layout": {
                "yaxis": {
                    "range": [1700, 1550],
                    "title": "True vertical depth [m]",
                },
                "xaxis": {
                    "title": "Lateral distance [m]"
                },
            },
        },
    )

    app.layout = plugin.layout

    # Injecting a div that will be updated when the plot data stores are
    # changed. Since the plot data are stored in LocalStorage and Selenium
    # has no functionality to wait for LocalStorage to equal some value we
    # instead populate this injected div with some data before we check the content
    # of Localstorage.
    @app.callback(
        Output(plugin.uuid("layout"), "children"),
        Input(plugin.uuid("intersection-graph-layout"), "data"),
        State(plugin.uuid("layout"), "children"),
    )
    def _add_or_update_div(data, children):
        plot_is_updated = html.Div(id=plugin.uuid("plot_is_updated"),
                                   children=data.get("title"))
        if len(children) == 6:
            children[5] = plot_is_updated
        else:
            children.append(plot_is_updated)

        return children

    dash_duo.start_server(app)

    intersection_data_id = plugin.uuid("intersection-data")

    # Check some initialization
    # Check dropdowns
    for element, return_val in zip(["well", "surface_attribute"],
                                   ["55_33-1", "ds_extract_postprocess"]):
        uuid = stringify_object_id(uuid={
            "element": element,
            "id": intersection_data_id
        })
        assert dash_duo.wait_for_text_to_equal(f"#\\{uuid} .Select-value",
                                               return_val)

    # Wait for the callbacks to execute
    dash_duo.wait_for_text_to_equal(
        f'#{plugin.uuid("plot_is_updated")}',
        "Intersection along well: 55_33-1",
        timeout=30,
    )

    # Check that graph data is stored
    graph_data = dash_duo.get_session_storage(
        plugin.uuid("intersection-graph-data"))
    assert len(graph_data) == 14
    graph_layout = dash_duo.get_session_storage(
        plugin.uuid("intersection-graph-layout"))
    assert isinstance(graph_layout, dict)
    assert graph_layout.get("title") == "Intersection along well: 55_33-1"

    ### Change well and check graph
    well_uuid = stringify_object_id(uuid={
        "element": "well",
        "id": intersection_data_id
    })

    apply_btn = dash_duo.wait_for_element_by_id(
        plugin.uuid("apply-intersection-data-selections"))
    well_dropdown = dash_duo.wait_for_element_by_id(well_uuid)
    dash_duo.select_dcc_dropdown(well_dropdown, value="55_33-2")
    apply_btn.click()

    # dash_duo.wait_for_text_to_equal(
    #     f'#{plugin.uuid("plot_is_updated")}',
    #     "Intersection along well: 55_33-1",
    #     timeout=100,
    # )
    graph_layout = dash_duo.get_session_storage(
        plugin.uuid("intersection-graph-layout"))
def register_callbacks(dash_app: CustomDashView):
    @dash_app.callback(
        Output(component_id='get_athlete_output',
               component_property='children'),
        Output('confirm', 'displayed'),
        [Input(component_id='btn_get_athlete', component_property='n_clicks')],
        prevent_initial_call=True)
    def get_athlete(_):
        athlete = strava_swagger.get_athlete()
        if athlete:
            return athlete, False
        else:
            return dash_external_redirect.redirect(
                url_for('users.strava_login')), True

    @dash_app.callback([
        Output(component_id="page_content", component_property="children"),
        Output(component_id="configuration_modal_centered",
               component_property="is_open"),
        Output(component_id="user_config_store", component_property="data")
    ], [
        Input(component_id="btn_open_configuration",
              component_property="n_clicks"),
        Input(component_id="btn_save_configuration",
              component_property="n_clicks"),
        Input(component_id="btn_close_configuration",
              component_property="n_clicks"),
    ], [
        State(component_id="charts_config_switches",
              component_property="value"),
        State(component_id="configuration_modal_centered",
              component_property="is_open"),
        State(component_id="current_activity_store", component_property="data")
    ],
                       prevent_initial_callbacks=True)
    def toggle_configuration_modal(btn_open_config, btn_save_config,
                                   btn_close_config, switches_value, is_open,
                                   current_activity):
        if btn_save_config:
            dash_app.context = {
                'user': current_user.id,
                'activity': current_activity,
                'switches': switches_value
            }
            page, config = dash_app.presenter.save_config_and_update_page()
            return page, not is_open, config.to_json()
        if btn_open_config or btn_close_config:
            return dash.no_update, not is_open, dash.no_update
        return dash.no_update, is_open, dash.no_update

    @dash_app.callback(
        Output(component_id='get_activities_output',
               component_property='children'),
        # Output('confirm', 'displayed'),
        [
            Input(component_id='btn_get_activities',
                  component_property='n_clicks')
        ],
        prevent_initial_call=True)
    def get_activities(_):
        activities = IO(current_user.id).get_activities_from_strava()

        if activities:
            return test_strava_methods_page.make_table(activities), False
        else:
            return dash_external_redirect.redirect(
                url_for('users.strava_login')), True
Beispiel #4
0
        dbc.Row(
            [
                dbc.Col(controls, md=4),
                dbc.Col(dbc.Card(dbc.CardImg(id="wordcloud")), md=8),
            ],
            align="center",
        ),
    ],
    fluid=True,
)


@app.callback(
    Output("wordcloud", "src"),
    [
        Input("book", "value"),
        Input("min-freq-slider", "value"),
        Input("max-vocab-slider", "value"),
    ],
)
def make_wordcloud(book, min_freq, max_vocab):
    # filter frequencies based on min_freq and max_vocab
    sorted_frequencies = sorted(load_word_frequencies(book).items(),
                                key=lambda x: x[1],
                                reverse=True)
    frequencies = {
        k: v
        for k, v in sorted_frequencies[:max_vocab] if v >= min_freq
    }

    wc = WordCloud(
Beispiel #5
0
def test_tabs_render_without_selected(dash_dcc, is_eager):
    app = Dash(__name__, eager_loading=is_eager)

    menu = html.Div([html.Div("one", id="one"), html.Div("two", id="two")])

    tabs_one = html.Div(
        [dcc.Tabs([dcc.Tab(dcc.Graph(id="graph-one"), label="tab-one-one")])],
        id="tabs-one",
        style={"display": "none"},
    )

    tabs_two = html.Div(
        [dcc.Tabs([dcc.Tab(dcc.Graph(id="graph-two"), label="tab-two-one")])],
        id="tabs-two",
        style={"display": "none"},
    )

    app.layout = html.Div([menu, tabs_one, tabs_two])

    for i in ("one", "two"):

        @app.callback(Output(f"tabs-{i}", "style"), [Input(i, "n_clicks")])
        def on_click_update_tabs(n_clicks):
            if n_clicks is None:
                raise PreventUpdate

            if n_clicks % 2 == 1:
                return {"display": "block"}
            return {"display": "none"}

        @app.callback(Output(f"graph-{i}", "figure"), [Input(i, "n_clicks")])
        def on_click_update_graph(n_clicks):
            if n_clicks is None:
                raise PreventUpdate

            return {
                "data": [{"x": [1, 2, 3, 4], "y": [4, 3, 2, 1]}],
                "layout": {"width": 700, "height": 450},
            }

    dash_dcc.start_server(app)

    button_one = dash_dcc.wait_for_element("#one")
    button_two = dash_dcc.wait_for_element("#two")

    button_one.click()

    # wait for tabs to be loaded after clicking
    WebDriverWait(dash_dcc.driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#graph-one .main-svg"))
    )

    is_eager = "eager" if is_eager else "lazy"

    time.sleep(1)
    dash_dcc.percy_snapshot(f"Tabs-1 rendered ({is_eager})")

    button_two.click()

    # wait for tabs to be loaded after clicking
    WebDriverWait(dash_dcc.driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#graph-two .main-svg"))
    )

    time.sleep(1)
    dash_dcc.percy_snapshot(f"Tabs-2 rendered ({is_eager})")

    # do some extra tests while we're here
    # and have access to Graph and plotly.js
    check_graph_config_shape(dash_dcc)

    assert dash_dcc.get_logs() == []
                                     } for x in data.area_list],
                                     value="London"),
                        html.Br(),
                        html.Div(id="stats-card"),
                    ]),
            dbc.Col(width=9,
                    children=[
                        html.H2('Recycling'),
                        dcc.Graph(id='recycle-chart', figure=fig_rc),
                    ]),
        ]),
    ])


@app.callback(Output("stats-card", "children"),
              [Input("area-select", "value")])
def render_output_panel(area_select):
    data.process_data_for_area(area_select)
    card = html.Div([
        dbc.Card(className="bg-dark text-light",
                 children=[
                     dbc.CardBody([
                         html.H4(area_select,
                                 id="card-name",
                                 className="card-title"),
                         html.Br(),
                         html.H6("Compared to England:",
                                 className="card-title"),
                         html.H4("{:,.0f}%".format(data.compare_to_eng),
                                 className="card-text text-light"),
                         html.Br(),
Beispiel #7
0
                                            ]
                                            )
                            ],
                    ),

                    html.Br(),
                    html.Div(id='my-output'),
                    ]
    )

@app.callback(
    Output(component_id='my-output', component_property='children'),
    Output(component_id="price-chart",  component_property='figure'),

    Input (component_id='my-input', component_property='value'    ),
    Input (component_id="metal-filter", component_property='value'    ),

    )

# def update_output_div(input_value):
#     return 'Output: {}'.format(input_value)

def update_output(input_value, metal):
    fig = px.line(df,x="DateTime", 
                 y=[metal],
                 
                 color_discrete_map={
                    "Platinum": "#E5E4E2",
                    "Gold": "gold",
                    "Silver": "silver",
Beispiel #8
0
def plugin_callbacks(
    get_uuid: Callable,
    ensemble_surface_providers: Dict[str, EnsembleSurfaceProvider],
    surface_server: SurfaceServer,
    ensemble_fault_polygons_providers: Dict[str,
                                            EnsembleFaultPolygonsProvider],
    fault_polygons_server: FaultPolygonsServer,
    map_surface_names_to_fault_polygons: Dict[str, str],
    well_picks_provider: Optional[WellPickProvider],
    fault_polygon_attribute: Optional[str],
) -> None:
    def selections(tab: str, colorselector: bool = False) -> Dict[str, str]:
        uuid = get_uuid(LayoutElements.SELECTIONS if not colorselector else
                        LayoutElements.COLORSELECTIONS)
        return {"view": ALL, "id": uuid, "tab": tab, "selector": ALL}

    def selector_wrapper(tab: str,
                         colorselector: bool = False) -> Dict[str, str]:
        uuid = get_uuid(LayoutElements.WRAPPER
                        if not colorselector else LayoutElements.COLORWRAPPER)
        return {"id": uuid, "tab": tab, "selector": ALL}

    def links(tab: str, colorselector: bool = False) -> Dict[str, str]:
        uuid = get_uuid(LayoutElements.LINK
                        if not colorselector else LayoutElements.COLORLINK)
        return {"id": uuid, "tab": tab, "selector": ALL}

    @callback(
        Output(get_uuid(LayoutElements.OPTIONS_DIALOG), "open"),
        Input({
            "id": get_uuid("Button"),
            "tab": ALL
        }, "n_clicks"),
        State(get_uuid(LayoutElements.OPTIONS_DIALOG), "open"),
    )
    def open_close_options_dialog(_n_click: list, is_open: bool) -> bool:
        if any(click is not None for click in _n_click):
            return not is_open
        raise PreventUpdate

    # 2nd callback
    @callback(
        Output({
            "id": get_uuid(LayoutElements.LINKED_VIEW_DATA),
            "tab": MATCH
        }, "data"),
        Output(selector_wrapper(MATCH), "children"),
        Input(selections(MATCH), "value"),
        Input({
            "id": get_uuid(LayoutElements.VIEWS),
            "tab": MATCH
        }, "value"),
        Input({
            "id": get_uuid(LayoutElements.MULTI),
            "tab": MATCH
        }, "value"),
        Input(links(MATCH), "value"),
        Input(get_uuid(LayoutElements.REALIZATIONS_FILTER), "value"),
        Input(get_uuid("tabs"), "value"),
        State(selections(MATCH), "id"),
        State(selector_wrapper(MATCH), "id"),
    )
    def _update_components_and_selected_data(
        mapselector_values: List[Dict[str, Any]],
        number_of_views: int,
        multi_selector: str,
        selectorlinks: List[List[str]],
        filtered_reals: List[int],
        tab_name: str,
        selector_ids: List[dict],
        wrapper_ids: List[dict],
    ) -> Tuple[List[dict], list]:
        """Reads stored raw selections, stores valid selections as a dcc.Store
        and updates visible and valid selections in layout"""

        # Prevent update if the pattern matched components does not match the current tab
        datatab = wrapper_ids[0]["tab"]
        if datatab != tab_name or number_of_views is None:
            raise PreventUpdate

        ctx = callback_context.triggered[0]["prop_id"]

        selector_values = []
        for idx in range(number_of_views):
            view_selections = combine_selector_values_and_name(
                mapselector_values, selector_ids, view=idx)
            view_selections["multi"] = multi_selector
            if tab_name == Tabs.STATS:
                view_selections[
                    "mode"] = DefaultSettings.VIEW_LAYOUT_STATISTICS_TAB[idx]
            selector_values.append(view_selections)

        linked_selector_names = [l[0] for l in selectorlinks if l]

        component_properties = _update_selector_component_properties_from_provider(
            selector_values=selector_values,
            linked_selectors=linked_selector_names,
            multi=multi_selector,
            multi_in_ctx=get_uuid(LayoutElements.MULTI) in ctx,
            filtered_realizations=filtered_reals,
        )
        # retrive the updated selector values from the component properties
        selector_values = [{key: val["value"]
                            for key, val in data.items()}
                           for idx, data in enumerate(component_properties)]

        if multi_selector is not None:
            selector_values = update_selections_with_multi(
                selector_values, multi_selector)
        selector_values = remove_data_if_not_valid(selector_values)
        if tab_name == Tabs.DIFF and len(selector_values) == 2:
            selector_values = add_diff_surface_to_values(selector_values)

        return (
            selector_values,
            [
                SideBySideSelectorFlex(
                    tab_name,
                    get_uuid,
                    selector=id_val["selector"],
                    view_data=[
                        data[id_val["selector"]]
                        for data in component_properties
                    ],
                    link=id_val["selector"] in linked_selector_names,
                    dropdown=id_val["selector"] in ["ensemble", "mode"],
                ) for id_val in wrapper_ids
            ],
        )

    # 3rd callback
    @callback(
        Output(
            {
                "id": get_uuid(LayoutElements.VERIFIED_VIEW_DATA),
                "tab": MATCH
            }, "data"),
        Output(selector_wrapper(MATCH, colorselector=True), "children"),
        Input({
            "id": get_uuid(LayoutElements.LINKED_VIEW_DATA),
            "tab": MATCH
        }, "data"),
        Input(selections(MATCH, colorselector=True), "value"),
        Input(
            {
                "view": ALL,
                "id": get_uuid(LayoutElements.RANGE_RESET),
                "tab": MATCH
            },
            "n_clicks",
        ),
        Input(links(MATCH, colorselector=True), "value"),
        State({
            "id": get_uuid(LayoutElements.MULTI),
            "tab": MATCH
        }, "value"),
        State(selector_wrapper(MATCH, colorselector=True), "id"),
        State(get_uuid(LayoutElements.STORED_COLOR_SETTINGS), "data"),
        State(get_uuid("tabs"), "value"),
        State(selections(MATCH, colorselector=True), "id"),
    )
    def _update_color_components_and_value(
        selector_values: List[dict],
        colorvalues: List[Dict[str, Any]],
        _n_click: int,
        colorlinks: List[List[str]],
        multi: str,
        color_wrapper_ids: List[dict],
        stored_color_settings: Dict,
        tab: str,
        colorval_ids: List[dict],
    ) -> Tuple[List[dict], list]:
        """Adds color settings to validated stored selections, updates color component in layout
        and writes validated selectors with colors to a dcc.Store"""
        ctx = callback_context.triggered[0]["prop_id"]

        if selector_values is None:
            raise PreventUpdate

        reset_color_index = (json.loads(ctx.split(".")[0])["view"] if get_uuid(
            LayoutElements.RANGE_RESET) in ctx else None)
        color_update_index = (json.loads(ctx.split(".")[0]).get("view") if
                              LayoutElements.COLORSELECTIONS in ctx else None)

        # if a selector is set as multi in the "Maps per Selector" tab
        # color_range should be linked and the min and max surface ranges
        # from all the views should be used as range
        use_range_from_all = tab == Tabs.SPLIT and multi != "attribute"

        links = [l[0] for l in colorlinks if l]
        links = links if not use_range_from_all else links + ["color_range"]

        # update selector_values with current values from the color components
        for idx, data in enumerate(selector_values):
            data.update(
                combine_selector_values_and_name(colorvalues,
                                                 colorval_ids,
                                                 view=idx))
        color_component_properties = _update_color_component_properties(
            values=selector_values,
            links=links if not use_range_from_all else links + ["color_range"],
            stored_color_settings=stored_color_settings,
            reset_color_index=reset_color_index,
            color_update_index=color_update_index,
        )

        if use_range_from_all:
            ranges = [data["surface_range"] for data in selector_values]
            min_max_for_all = [
                min(r[0] for r in ranges),
                max(r[1] for r in ranges)
            ]
            for data in color_component_properties:
                data["color_range"]["range"] = min_max_for_all
                if reset_color_index is not None:
                    data["color_range"]["value"] = min_max_for_all

        for idx, data in enumerate(color_component_properties):
            for key, val in data.items():
                selector_values[idx][key] = val["value"]

        return (
            selector_values,
            [
                SideBySideSelectorFlex(
                    tab,
                    get_uuid,
                    selector=id_val["selector"],
                    view_data=[
                        data[id_val["selector"]]
                        for data in color_component_properties
                    ],
                    link=id_val["selector"] in links,
                    dropdown=id_val["selector"] in ["colormap"],
                ) for id_val in color_wrapper_ids
            ],
        )

    # 4th callback
    @callback(
        Output(get_uuid(LayoutElements.STORED_COLOR_SETTINGS), "data"),
        Input({
            "id": get_uuid(LayoutElements.VERIFIED_VIEW_DATA),
            "tab": ALL
        }, "data"),
        State(get_uuid("tabs"), "value"),
        State(get_uuid(LayoutElements.STORED_COLOR_SETTINGS), "data"),
        State({
            "id": get_uuid(LayoutElements.VERIFIED_VIEW_DATA),
            "tab": ALL
        }, "id"),
    )
    def _update_color_store(
        selector_values: List[List[dict]],
        tab: str,
        stored_color_settings: Dict[str, dict],
        data_id: List[dict],
    ) -> Dict[str, dict]:
        """Update the color store with chosen color range and colormap for surfaces"""
        if selector_values is None:
            raise PreventUpdate
        index = [x["tab"] for x in data_id].index(tab)

        stored_color_settings = (stored_color_settings
                                 if stored_color_settings is not None else {})
        for data in selector_values[index]:
            surfaceid = (get_surface_id_for_diff_surf(selector_values[index])
                         if data.get("surf_type") == "diff" else
                         get_surface_id_from_data(data))
            stored_color_settings[surfaceid] = {
                "colormap": data["colormap"],
                "color_range": data["color_range"],
            }

        return stored_color_settings

    # 5th callback
    @callback(
        Output({
            "id": get_uuid(LayoutElements.DECKGLMAP),
            "tab": MATCH
        }, "layers"),
        Output({
            "id": get_uuid(LayoutElements.DECKGLMAP),
            "tab": MATCH
        }, "bounds"),
        Output({
            "id": get_uuid(LayoutElements.DECKGLMAP),
            "tab": MATCH
        }, "views"),
        Input({
            "id": get_uuid(LayoutElements.VERIFIED_VIEW_DATA),
            "tab": MATCH
        }, "data"),
        Input(get_uuid(LayoutElements.WELLS), "value"),
        Input(get_uuid(LayoutElements.VIEW_COLUMNS), "value"),
        Input(get_uuid(LayoutElements.OPTIONS), "value"),
        State(get_uuid("tabs"), "value"),
        State({
            "id": get_uuid(LayoutElements.MULTI),
            "tab": MATCH
        }, "value"),
    )
    def _update_map(
        surface_elements: List[dict],
        selected_wells: List[str],
        view_columns: Optional[int],
        options: List[str],
        tab_name: str,
        multi: str,
    ) -> tuple:
        """Updates the map component with the stored, validated selections"""

        # Prevent update if the pattern matched components does not match the current tab
        state_list = callback_context.states_list
        if state_list[1]["id"]["tab"] != tab_name or surface_elements is None:
            raise PreventUpdate

        if tab_name == Tabs.DIFF:
            view_columns = 3 if view_columns is None else view_columns

        layers = update_map_layers(
            views=len(surface_elements),
            include_well_layer=well_picks_provider is not None,
            visible_well_layer=LayoutLabels.SHOW_WELLS in options,
            visible_fault_polygons_layer=LayoutLabels.SHOW_FAULTPOLYGONS
            in options,
            visible_hillshading_layer=LayoutLabels.SHOW_HILLSHADING in options,
        )
        layer_model = DeckGLMapLayersModel(layers)

        for idx, data in enumerate(surface_elements):
            diff_surf = data.get("surf_type") == "diff"
            surf_meta, img_url = (
                get_surface_metadata_and_image(data) if not diff_surf else
                get_surface_metadata_and_image_for_diff_surface(
                    surface_elements))
            viewport_bounds = [
                surf_meta.x_min,
                surf_meta.y_min,
                surf_meta.x_max,
                surf_meta.y_max,
            ]

            # Map3DLayer currently not implemented. Will replace
            # ColormapLayer and HillshadingLayer
            # layer_data = {
            #     "mesh": img_url,
            #     "propertyTexture": img_url,
            #     "bounds": surf_meta.deckgl_bounds,
            #     "rotDeg": surf_meta.deckgl_rot_deg,
            #     "meshValueRange": [surf_meta.val_min, surf_meta.val_max],
            #     "propertyValueRange": [surf_meta.val_min, surf_meta.val_max],
            #     "colorMapName": data["colormap"],
            #     "colorMapRange": data["color_range"],
            # }

            # layer_model.update_layer_by_id(
            #     layer_id=f"{LayoutElements.MAP3D_LAYER}-{idx}",
            #     layer_data=layer_data,
            # )
            layer_data = {
                "image": img_url,
                "bounds": surf_meta.deckgl_bounds,
                "rotDeg": surf_meta.deckgl_rot_deg,
                "valueRange": [surf_meta.val_min, surf_meta.val_max],
            }
            layer_model.update_layer_by_id(
                layer_id=f"{LayoutElements.COLORMAP_LAYER}-{idx}",
                layer_data=layer_data,
            )

            layer_model.update_layer_by_id(
                layer_id=f"{LayoutElements.HILLSHADING_LAYER}-{idx}",
                layer_data=layer_data,
            )
            layer_model.update_layer_by_id(
                layer_id=f"{LayoutElements.COLORMAP_LAYER}-{idx}",
                layer_data={
                    "colorMapName": data["colormap"],
                    "colorMapRange": data["color_range"],
                },
            )
            if (LayoutLabels.SHOW_FAULTPOLYGONS in options
                    and fault_polygon_attribute is not None):
                # if diff surface use polygons from first view
                data_for_faultpolygons = data if not diff_surf else surface_elements[
                    0]
                fault_polygons_provider = ensemble_fault_polygons_providers[
                    data_for_faultpolygons["ensemble"][0]]
                horizon_name = data_for_faultpolygons["name"][0]
                fault_polygons_address = SimulatedFaultPolygonsAddress(
                    attribute=fault_polygon_attribute,
                    name=map_surface_names_to_fault_polygons.get(
                        horizon_name, horizon_name),
                    realization=int(data_for_faultpolygons["realizations"][0]),
                )
                layer_model.update_layer_by_id(
                    layer_id=f"{LayoutElements.FAULTPOLYGONS_LAYER}-{idx}",
                    layer_data={
                        "data":
                        fault_polygons_server.encode_partial_url(
                            provider_id=fault_polygons_provider.provider_id(),
                            fault_polygons_address=fault_polygons_address,
                        ),
                    },
                )
            if LayoutLabels.SHOW_WELLS in options and well_picks_provider is not None:
                horizon_name = (data["name"][0] if not diff_surf else
                                surface_elements[0]["name"][0])
                layer_model.update_layer_by_id(
                    layer_id=f"{LayoutElements.WELLS_LAYER}-{idx}",
                    layer_data={
                        "data":
                        well_picks_provider.get_geojson(
                            selected_wells, horizon_name)
                    },
                )
        return (
            layer_model.layers,
            viewport_bounds if surface_elements else no_update,
            {
                "layout":
                view_layout(len(surface_elements), view_columns),
                "showLabel":
                True,
                "viewports": [
                    {
                        "id":
                        f"{view}_view",
                        "show3D":
                        False,
                        "layerIds": [
                            # f"{LayoutElements.MAP3D_LAYER}-{view}",
                            f"{LayoutElements.COLORMAP_LAYER}-{view}",
                            f"{LayoutElements.HILLSHADING_LAYER}-{view}",
                            f"{LayoutElements.FAULTPOLYGONS_LAYER}-{view}",
                            f"{LayoutElements.WELLS_LAYER}-{view}",
                        ],
                        "name":
                        make_viewport_label(surface_elements[view], tab_name,
                                            multi),
                    } for view in range(len(surface_elements))
                ],
            },
        )

    def make_viewport_label(data: dict, tab: str, multi: Optional[str]) -> str:
        """Return text-label for each viewport based on which tab is selected"""
        # For the difference view
        if tab == Tabs.DIFF and data.get("surf_type") == "diff":
            return "Difference Map (View1 - View2)"

        # For the statistics view 'mode' is used as label
        if tab == Tabs.STATS:
            if data["mode"] == SurfaceMode.REALIZATION:
                return f"REAL {data['realizations'][0]}"
            return data["mode"]

        # For the "map per selector" the chosen multi selector is used as label
        if tab == Tabs.SPLIT:
            if multi == "realizations":
                return f"REAL {data['realizations'][0]}"
            return data[multi][0] if multi == "mode" else data[multi]

        return general_label(data)

    def general_label(data: dict) -> str:
        """Create a general map label with all available information"""
        surfaceid = [
            data["ensemble"][0], data["attribute"][0], data["name"][0]
        ]
        if data["date"]:
            surfaceid.append(data["date"][0])
        if data["mode"] != SurfaceMode.REALIZATION:
            surfaceid.append(data["mode"])
        else:
            surfaceid.append(f"REAL {data['realizations'][0]}")
        return " ".join(surfaceid)

    # pylint: disable=too-many-branches
    def _update_selector_component_properties_from_provider(
        selector_values: List[dict],
        linked_selectors: List[str],
        multi: str,
        multi_in_ctx: bool,
        filtered_realizations: List[int],
    ) -> List[Dict[str, dict]]:
        """Return updated options and values for the different selector components using
        the provider. If current selected value for a selector is in the options it will
        be used as the new value"""
        view_data = []
        for idx, data in enumerate(selector_values):

            if not ("ensemble" in linked_selectors and idx > 0):
                ensembles = list(ensemble_surface_providers.keys())
                ensemble = data.get("ensemble", [])
                ensemble = [ensemble] if isinstance(ensemble,
                                                    str) else ensemble
                if not ensemble or multi_in_ctx:
                    ensemble = ensembles if multi == "ensemble" else ensembles[:
                                                                               1]

            if not ("attribute" in linked_selectors and idx > 0):
                attributes = []
                for ens in ensemble:
                    provider = ensemble_surface_providers[ens]
                    attributes.extend([
                        x for x in provider.attributes() if x not in attributes
                    ])
                    # only show attributes with date when multi is set to date
                    if multi == "date":
                        attributes = [
                            x for x in attributes
                            if attribute_has_date(x, provider)
                        ]

                attribute = [
                    x for x in data.get("attribute", []) if x in attributes
                ]
                if not attribute or multi_in_ctx:
                    attribute = attributes if multi == "attribute" else attributes[:
                                                                                   1]

            if not ("name" in linked_selectors and idx > 0):
                names = []
                for ens in ensemble:
                    provider = ensemble_surface_providers[ens]
                    for attr in attribute:
                        attr_names = provider.surface_names_for_attribute(attr)
                        names.extend([x for x in attr_names if x not in names])

                name = [x for x in data.get("name", []) if x in names]
                if not name or multi_in_ctx:
                    name = names if multi == "name" else names[:1]

            if not ("date" in linked_selectors and idx > 0):
                dates = []
                for ens in ensemble:
                    provider = ensemble_surface_providers[ens]
                    for attr in attribute:
                        attr_dates = provider.surface_dates_for_attribute(attr)

                        if attr_dates is not None:
                            dates.extend(
                                [x for x in attr_dates if x not in dates])

                interval_dates = [x for x in dates if "_" in x]
                dates = [x for x in dates if x not in interval_dates
                         ] + interval_dates

                date = [x for x in data.get("date", []) if x in dates]
                if not date or multi_in_ctx:
                    date = dates if multi == "date" else dates[:1]

            if not ("mode" in linked_selectors and idx > 0):
                modes = list(SurfaceMode)
                mode = data.get("mode", SurfaceMode.REALIZATION)

            if not ("realizations" in linked_selectors and idx > 0):
                if mode == SurfaceMode.REALIZATION:
                    real = data.get("realizations", [])
                    if not real or real[0] not in filtered_realizations:
                        real = filtered_realizations[:1]
                else:
                    real = filtered_realizations

            view_data.append({
                "ensemble": {
                    "value": ensemble,
                    "options": ensembles,
                    "multi": multi == "ensemble",
                },
                "attribute": {
                    "value": attribute,
                    "options": attributes,
                    "multi": multi == "attribute",
                },
                "name": {
                    "value": name,
                    "options": names,
                    "multi": multi == "name"
                },
                "date": {
                    "value": date,
                    "options": dates,
                    "multi": multi == "date"
                },
                "mode": {
                    "value": mode,
                    "options": modes
                },
                "realizations": {
                    "value":
                    real,
                    "disabled":
                    mode != SurfaceMode.REALIZATION,
                    "options":
                    filtered_realizations,
                    "multi":
                    mode != SurfaceMode.REALIZATION or multi == "realizations",
                },
            })

        return view_data

    def _update_color_component_properties(
        values: List[dict],
        links: List[str],
        stored_color_settings: dict,
        reset_color_index: Optional[int],
        color_update_index: Optional[int],
    ) -> List[dict]:
        """Return updated options and values for the different color selector components.
        If previous color settings are found it will be used, or set value to min and max
        surface range unless the user has updated the component through interaction."""
        stored_color_settings = (stored_color_settings
                                 if stored_color_settings is not None else {})
        colormaps = DefaultSettings.COLORMAP_OPTIONS

        surfids: List[str] = []
        color_data: List[dict] = []
        for idx, data in enumerate(values):
            surfaceid = (get_surface_id_for_diff_surf(values)
                         if data.get("surf_type") == "diff" else
                         get_surface_id_from_data(data))
            # if surfaceid exist in another view use the color settings
            # from that view and disable the color component
            if surfaceid in surfids:
                index_of_first = surfids.index(surfaceid)
                surfids.append(surfaceid)
                view_data = deepcopy(color_data[index_of_first])
                view_data["colormap"].update(disabled=True)
                view_data["color_range"].update(disabled=True)
                color_data.append(view_data)
                continue

            surfids.append(surfaceid)

            use_stored_color = (surfaceid in stored_color_settings
                                and not color_update_index == idx)
            if not ("colormap" in links and idx > 0):
                colormap = (stored_color_settings[surfaceid]["colormap"]
                            if use_stored_color else data.get(
                                "colormap", colormaps[0]))

            if not ("color_range" in links and idx > 0):
                value_range = data["surface_range"]
                if data.get("colormap_keep_range", False):
                    color_range = data["color_range"]
                elif reset_color_index == idx or surfaceid not in stored_color_settings:
                    color_range = value_range
                else:
                    color_range = (
                        stored_color_settings[surfaceid]["color_range"]
                        if use_stored_color else data.get(
                            "color_range", value_range))

            color_data.append({
                "colormap": {
                    "value": colormap,
                    "options": colormaps
                },
                "color_range": {
                    "value":
                    color_range,
                    "step":
                    calculate_slider_step(
                        min_value=value_range[0],
                        max_value=value_range[1],
                        steps=100,
                    ) if value_range[0] != value_range[1] else 0,
                    "range":
                    value_range,
                },
            })

        return color_data

    def get_surface_address_from_data(
        data: dict,
    ) -> Union[SimulatedSurfaceAddress, ObservedSurfaceAddress,
               StatisticalSurfaceAddress]:
        """Return the SurfaceAddress based on view selection"""
        has_date = (ensemble_surface_providers[
            data["ensemble"][0]].surface_dates_for_attribute(
                data["attribute"][0]) is not None)

        if data["mode"] == SurfaceMode.REALIZATION:
            return SimulatedSurfaceAddress(
                attribute=data["attribute"][0],
                name=data["name"][0],
                datestr=data["date"][0] if has_date else None,
                realization=int(data["realizations"][0]),
            )
        if data["mode"] == SurfaceMode.OBSERVED:
            return ObservedSurfaceAddress(
                attribute=data["attribute"][0],
                name=data["name"][0],
                datestr=data["date"][0] if has_date else None,
            )
        return StatisticalSurfaceAddress(
            attribute=data["attribute"][0],
            name=data["name"][0],
            datestr=data["date"][0] if has_date else None,
            realizations=[int(real) for real in data["realizations"]],
            statistic=data["mode"],
        )

    def publish_and_get_surface_metadata(
            surface_provider: EnsembleSurfaceProvider,
            surface_address: SurfaceAddress) -> Tuple:
        provider_id: str = surface_provider.provider_id()
        qualified_address = QualifiedSurfaceAddress(provider_id,
                                                    surface_address)
        surf_meta = surface_server.get_surface_metadata(qualified_address)
        if not surf_meta:
            # This means we need to compute the surface
            surface = surface_provider.get_surface(address=surface_address)
            if not surface:
                raise ValueError(
                    f"Could not get surface for address: {surface_address}")
            surface_server.publish_surface(qualified_address, surface)
            surf_meta = surface_server.get_surface_metadata(qualified_address)
        return surf_meta, surface_server.encode_partial_url(qualified_address)

    def publish_and_get_diff_surface_metadata(
        surface_provider: EnsembleSurfaceProvider,
        surface_address: SurfaceAddress,
        sub_surface_provider: EnsembleSurfaceProvider,
        sub_surface_address: SurfaceAddress,
    ) -> Tuple:
        provider_id: str = surface_provider.provider_id()
        subprovider_id = sub_surface_provider.provider_id()
        qualified_address: Union[QualifiedSurfaceAddress,
                                 QualifiedDiffSurfaceAddress]
        qualified_address = QualifiedDiffSurfaceAddress(
            provider_id, surface_address, subprovider_id, sub_surface_address)

        surf_meta = surface_server.get_surface_metadata(qualified_address)
        if not surf_meta:
            surface_a = surface_provider.get_surface(address=surface_address)
            surface_b = sub_surface_provider.get_surface(
                address=sub_surface_address)
            if surface_a is not None and surface_b is not None:
                surface = surface_a - surface_b
            surface_server.publish_surface(qualified_address, surface)
            surf_meta = surface_server.get_surface_metadata(qualified_address)
        return surf_meta, surface_server.encode_partial_url(qualified_address)

    def get_surface_id_from_data(data: dict) -> str:
        """Retrieve surfaceid used for the colorstore"""
        surfaceid = data["attribute"][0] + data["name"][0]
        if data["date"]:
            surfaceid += data["date"][0]
        if data["mode"] == SurfaceMode.STDDEV:
            surfaceid += data["mode"]
        return surfaceid

    def get_surface_id_for_diff_surf(values: List[dict]) -> str:
        """Retrieve surfaceid used for the colorstore, for the diff surface
        this needs to be a combination of the two surfaces subtracted"""
        return get_surface_id_from_data(values[0]) + get_surface_id_from_data(
            values[1])

    def update_selections_with_multi(values: List[dict],
                                     multi: str) -> List[dict]:
        """If a selector has been set as multi, the values selected in that component needs
        to be divided between the views so that there is only one unique value in each"""
        multi_values = values[0][multi]
        new_values = []
        for val in multi_values:
            updated_values = deepcopy(values[0])
            updated_values[multi] = [val]
            new_values.append(updated_values)
        return new_values

    def attribute_has_date(attribute: str,
                           provider: EnsembleSurfaceProvider) -> bool:
        """Check if an attribute has any dates"""
        return provider.surface_dates_for_attribute(attribute) is not None

    def remove_data_if_not_valid(values: List[dict]) -> List[dict]:
        """Checks if surfaces can be provided from the selections.
        Any invalid selections are removed."""
        updated_values = []
        for data in values:
            surface_address = get_surface_address_from_data(data)
            try:
                provider = ensemble_surface_providers[data["ensemble"][0]]
                surf_meta, _ = publish_and_get_surface_metadata(
                    surface_address=surface_address,
                    surface_provider=provider,
                )
            except ValueError:
                continue
            if not isinstance(surf_meta.val_min,
                              np.ma.core.MaskedConstant) and not isinstance(
                                  surf_meta.val_max,
                                  np.ma.core.MaskedConstant):
                data["surface_range"] = [surf_meta.val_min, surf_meta.val_max]
                updated_values.append(data)

        return updated_values

    def get_surface_metadata_and_image(data: dict) -> Tuple:
        surface_address = get_surface_address_from_data(data)
        provider = ensemble_surface_providers[data["ensemble"][0]]
        return publish_and_get_surface_metadata(
            surface_address=surface_address, surface_provider=provider)

    def get_surface_metadata_and_image_for_diff_surface(
        selector_values: List[dict], ) -> Tuple:
        surface_address = get_surface_address_from_data(selector_values[0])
        sub_surface_address = get_surface_address_from_data(selector_values[1])
        provider = ensemble_surface_providers[selector_values[0]["ensemble"]
                                              [0]]
        sub_provider = ensemble_surface_providers[selector_values[1]
                                                  ["ensemble"][0]]
        return publish_and_get_diff_surface_metadata(
            surface_address=surface_address,
            surface_provider=provider,
            sub_surface_address=sub_surface_address,
            sub_surface_provider=sub_provider,
        )

    def add_diff_surface_to_values(selector_values: List[dict]) -> List[dict]:
        surf_meta, _ = get_surface_metadata_and_image_for_diff_surface(
            selector_values)
        selector_values.append({
            "surface_range": [surf_meta.val_min, surf_meta.val_max],
            "surf_type":
            "diff",
        })
        return selector_values

    def combine_selector_values_and_name(values: list, id_list: list,
                                         view: int) -> dict:
        """Combine selector values with selector name for given view"""
        return {
            id_values["selector"]: values
            for values, id_values in zip(values, id_list)
            if id_values["view"] == view
        }
Beispiel #9
0
def well_overview_callbacks(
    app: Dash,
    get_uuid: Callable,
    data_models: Dict[str, EnsembleWellAnalysisData],
    theme: WebvizConfigTheme,
) -> None:
    @app.callback(
        Output(get_uuid(ClientsideStoreElements.WELL_OVERVIEW_CHART_SELECTED),
               "data"),
        Input(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_BUTTON),
                "button": ALL,
            },
            "n_clicks",
        ),
        State(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_BUTTON),
                "button": ALL,
            },
            "id",
        ),
    )
    def _update_chart_selected(_apply_click: int, button_ids: list) -> str:
        """Stores the selected chart type in ClientsideStoreElements.WELL_OVERVIEW_CHART_SELECTED"""
        ctx = callback_context.triggered[0]

        # handle initial callback
        if ctx["prop_id"] == ".":
            return "bar"

        for button_id in button_ids:
            if button_id["button"] in ctx["prop_id"]:
                return button_id["button"]
        raise ValueError("Id not found")

    @callback(
        Output(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_BUTTON),
                "button": ALL,
            },
            "style",
        ),
        Input(get_uuid(ClientsideStoreElements.WELL_OVERVIEW_CHART_SELECTED),
              "data"),
        State(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_BUTTON),
                "button": ALL,
            },
            "id",
        ),
    )
    def _update_button_style(chart_selected: str, button_ids: list) -> list:
        """Updates the styling of the chart type buttons, showing which chart type
        is currently selected.
        """
        button_styles = {
            button["button"]: {
                "background-color": "#E8E8E8"
            }
            for button in button_ids
        }
        button_styles[chart_selected] = {
            "background-color": "#7393B3",
            "color": "#fff"
        }

        return update_relevant_components(
            id_list=button_ids,
            update_info=[{
                "new_value": style,
                "conditions": {
                    "button": button
                },
            } for button, style in button_styles.items()],
        )

    @callback(
        Output(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_SETTINGS),
                "charttype": ALL,
            },
            "style",
        ),
        Input(get_uuid(ClientsideStoreElements.WELL_OVERVIEW_CHART_SELECTED),
              "data"),
        State(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_SETTINGS),
                "charttype": ALL,
            },
            "id",
        ),
    )
    def _display_charttype_settings(chart_selected: str,
                                    charttype_settings_ids: list) -> list:
        """Display only the settings relevant for the currently selected chart type."""
        return [{
            "display": "block"
        } if settings_id["charttype"] == chart_selected else {
            "display": "none"
        } for settings_id in charttype_settings_ids]

    @app.callback(
        Output(get_uuid(WellOverviewLayoutElements.GRAPH_FRAME), "children"),
        Input(get_uuid(WellOverviewLayoutElements.ENSEMBLES), "value"),
        Input(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_CHECKLIST),
                "charttype": ALL,
            },
            "value",
        ),
        Input(get_uuid(WellOverviewLayoutElements.SUMVEC), "value"),
        Input(get_uuid(ClientsideStoreElements.WELL_OVERVIEW_CHART_SELECTED),
              "data"),
        Input(get_uuid(WellOverviewLayoutElements.WELL_FILTER), "value"),
        State(
            {
                "id": get_uuid(WellOverviewLayoutElements.CHARTTYPE_CHECKLIST),
                "charttype": ALL,
            },
            "id",
        ),
        State(get_uuid(WellOverviewLayoutElements.GRAPH), "figure"),
    )
    def _update_graph(
        ensembles: List[str],
        checklist_values: List[List[str]],
        sumvec: str,
        chart_selected: str,
        wells_selected: List[str],
        checklist_ids: List[Dict[str, str]],
        current_fig_dict: dict,
    ) -> List[wcc.Graph]:
        """Updates the well overview graph with selected input (f.ex chart type)"""
        ctx = callback_context.triggered[0]["prop_id"].split(".")[0]

        settings = {
            checklist_id["charttype"]: checklist_values[i]
            for i, checklist_id in enumerate(checklist_ids)
        }

        # If the event is a plot settings event, then we only update the formatting
        # and not the figure data
        if current_fig_dict is not None and is_plot_settings_event(
                ctx, get_uuid):
            fig_dict = format_well_overview_figure(
                go.Figure(current_fig_dict),
                chart_selected,
                settings[chart_selected],
                sumvec,
            )
        else:
            figure = WellOverviewFigure(
                ensembles,
                data_models,
                sumvec,
                chart_selected,
                wells_selected,
                theme,
            )

            fig_dict = format_well_overview_figure(figure.figure,
                                                   chart_selected,
                                                   settings[chart_selected],
                                                   sumvec)

        return [
            wcc.Graph(
                id=get_uuid(WellOverviewLayoutElements.GRAPH),
                style={"height": "87vh"},
                figure=fig_dict,
            )
        ]
Beispiel #10
0
    def set_callbacks(self, app):
        @app.callback(
            Output(self.uuid("parcoords"), "figure"),
            self.parcoord_inputs,
        )
        def _update_parcoord(ens, exc_inc, parameter_list, *opt_args):
            """Updates parallel coordinates plot
            Filter dataframe for chosen ensembles and parameters
            Call render_parcoord to render new figure
            """
            # Ensure selected ensembles is a list
            ens = ens if isinstance(ens, list) else [ens]
            # Ensure selected parameters is a list
            parameter_list = (parameter_list if isinstance(
                parameter_list, list) else [parameter_list])
            special_columns = ["ENSEMBLE", "REAL"]
            if exc_inc == "exc":
                parameterdf = self.parameterdf.drop(parameter_list, axis=1)
            elif exc_inc == "inc":
                parameterdf = self.parameterdf[special_columns +
                                               parameter_list]
            params = [
                param for param in parameterdf.columns
                if param not in special_columns
                and param in self.parameter_columns
            ]

            mode = opt_args[0] if opt_args else "ensemble"
            # Need a default response
            response = ""

            if mode == "response":
                if len(ens) != 1:
                    # Need to wait for update of ensemble selector to multi=False
                    raise PreventUpdate
                df = parameterdf.loc[self.parameterdf["ENSEMBLE"] == ens[0]]
                response = opt_args[1]
                response_filter_values = opt_args[2:] if len(
                    opt_args) > 2 else {}
                filteroptions = parresp.make_response_filters(
                    response_filters=self.response_filters,
                    response_filter_values=response_filter_values,
                )
                responsedf = parresp.filter_and_sum_responses(
                    self.responsedf,
                    ens[0],
                    response,
                    filteroptions=filteroptions,
                    aggregation=self.aggregation,
                )

                # Renaming to make it clear in plot.
                responsedf.rename(columns={response: f"Response: {response}"},
                                  inplace=True)
                df = pd.merge(responsedf, df,
                              on=["REAL"]).drop(columns=special_columns)
                df[self.uuid("COLOR")] = df.apply(
                    lambda row: self.ensembles.index(ens[0]), axis=1)
            else:
                # Filter on ensembles (ens) and active parameters (params),
                # adding the COLOR column to the columns to keep
                df = self.parameterdf[self.parameterdf["ENSEMBLE"].isin(ens)][
                    params + ["ENSEMBLE"]]
                df[self.uuid("COLOR")] = df.apply(
                    lambda row: self.ensembles.index(row["ENSEMBLE"]), axis=1)
            return render_parcoord(
                df,
                self.theme,
                self.ens_colormap,
                self.uuid("COLOR"),
                self.ensembles,
                mode,
                params,
                response,
            )

        @app.callback(
            [
                Output(self.uuid("ensembles"), "multi"),
                Output(self.uuid("ensembles"), "value"),
                Output(self.uuid("view_response"), "style"),
            ],
            [Input(self.uuid("mode"), "value")],
        )
        def _update_mode(mode: str):
            if mode == "ensemble":
                return True, self.ensembles, {"display": "none"}
            if mode == "response":
                return False, self.ensembles[0], {"display": "block"}
            # The error should never occur
            raise ValueError("ensemble and response are the only valid modes.")
Beispiel #11
0
    def set_callbacks(self, app: Dash) -> None:
        @app.callback(
            Output(self.uuid("graph"), "figure"),
            Input(self.uuid("ensemble"), "value"),
            Input(self.uuid("plot_type"), "value"),
            Input(self.uuid("n_wells"), "value"),
            Input(self.uuid("wells"), "value"),
            Input(self.uuid("sort_by"), "value"),
            Input(self.uuid("stat_bars"), "value"),
            Input(self.uuid("ascending"), "value"),
        )
        def _update_graph(
            ensemble: str,
            plot_type: str,
            n_wells: int,
            wells: Union[str, List[str]],
            sort_by: str,
            stat_bars: Union[str, List[str]],
            ascending: bool,
        ) -> dict:
            wells = wells if isinstance(wells, list) else [wells]
            stat_bars = stat_bars if isinstance(stat_bars,
                                                list) else [stat_bars]
            df = filter_df(df=self.smry, ensemble=ensemble, wells=wells)
            stat_df = (calc_statistics(df).sort_values(
                sort_by, ascending=ascending).iloc[0:n_wells, :])
            traces = []
            if plot_type == "Fan chart":
                traces.extend(
                    _get_fanchart_traces(
                        ens_stat_df=stat_df,
                        color=self.ens_colors[ensemble],
                        legend_group=ensemble,
                    ))
            elif plot_type in ["Bar chart", "Line chart"]:
                for stat in stat_bars:
                    yaxis = "y2" if stat == "count" else "y"

                    if plot_type == "Bar chart":

                        traces.append({
                            "x":
                            [vec[5:] for vec in stat_df.index],  # strip WBHP:
                            "y":
                            stat_df[stat],
                            "name": [
                                key for key, value in self.label_map.items()
                                if value == stat
                            ][0],
                            "yaxis":
                            yaxis,
                            "type":
                            "bar",
                            "offsetgroup":
                            stat,
                            "showlegend":
                            True,
                        })
                    elif plot_type == "Line chart":
                        traces.append({
                            "x":
                            [vec[5:] for vec in stat_df.index],  # strip WBHP:
                            "y":
                            stat_df[stat],
                            "name": [
                                key for key, value in self.label_map.items()
                                if value == stat
                            ][0],
                            "yaxis":
                            yaxis,
                            "type":
                            "line",
                            "offsetgroup":
                            stat,
                            "showlegend":
                            True,
                        })
                    else:
                        raise ValueError("Invalid plot type.")

            layout = self.theme.create_themed_layout({
                "yaxis": {
                    "side": "left",
                    "title": "Bottom hole pressure",
                    "showgrid": False,
                },
                "yaxis2": {
                    "side": "right",
                    "overlaying": "y",
                    "title": "Count (data points)",
                    "showgrid": False,
                },
                "xaxis": {
                    "showgrid": False
                },
                "barmode": "group",
                "legend": {
                    "x": 1.05
                },
            })
            return {"data": traces, "layout": layout}

        @app.callback(
            Output(self.uuid("select_stat"), "style"),
            Input(self.uuid("plot_type"), "value"),
        )
        def _update_stat_selector(plot_type: str) -> dict:
            return ({
                "display": "none"
            } if plot_type == "Fan chart" else {
                "display": "block"
            })
Beispiel #12
0
    ctl.H2("Boolean input"),
    bool_input,
    ctl.H2("Matrix input"),
    matrix_input,
    ctl.H2("Slider input"),
    slider_input,
    ctl.H2("Dynamic inputs"),
    ctl.Button("Generate inputs", id="generate-inputs"),
    html.Div(id="dynamic-inputs"),
    ctl.H1("Output"),
    html.Span(id="output"),
])


@app.callback(Output("output", "children"),
              Input(your_component.get_all_kwargs_id(), "value"))
def show_outputs(*args):

    kwargs = your_component.reconstruct_kwargs_from_state()

    return str(kwargs)


@app.callback(Output("dynamic-inputs", "children"),
              Input("generate-inputs", "n_clicks"))
def add_inputs(n_clicks):

    if not n_clicks:
        raise PreventUpdate

    element = random.choice(["Li", "Na", "K"])
Beispiel #13
0
 def osi_update_notebook(self):
     return (
         Output(self.notebook_home.notebook_div, "style"),
         Output(self.notebook_home.notebook, "data"),
         Input(self.notebook_home.update_notebook_button, "n_clicks"),
     )
Beispiel #14
0
            ],
            className="ms-1",
        ),
        dbc.Row(dbc.Col(footer)),
    ],
    fluid=True,
)
"""
==========================================================================
Callbacks
"""


@app.callback(
    Output("allocation_pie_chart", "figure"),
    Input("stock_bond", "value"),
    Input("cash", "value"),
)
def update_pie(stocks, cash):
    bonds = 100 - stocks - cash
    slider_input = [cash, bonds, stocks]

    if stocks >= 70:
        investment_style = "Aggressive"
    elif stocks <= 30:
        investment_style = "Conservative"
    else:
        investment_style = "Moderate"
    figure = make_pie(slider_input, investment_style + " Asset Allocation")
    return figure
def distribution_controllers(get_uuid: Callable,
                             volumemodel: InplaceVolumesModel) -> None:
    @callback(
        Output({
            "id": get_uuid("main-voldist"),
            "page": "custom"
        }, "children"),
        Input(get_uuid("selections"), "data"),
        State(get_uuid("page-selected"), "data"),
    )
    def _update_page_custom(selections: dict, page_selected: str) -> tuple:

        if page_selected != "custom":
            raise PreventUpdate

        selections = selections[page_selected]
        if not selections["update"]:
            raise PreventUpdate

        selected_data = [
            selections[x]
            for x in ["Subplots", "Color by", "X Response", "Y Response"]
            if selections[x] is not None
        ]
        groups = ["REAL"]
        parameters = []
        for item in selected_data:
            if item in volumemodel.selectors and item not in groups:
                groups.append(item)
            if item in volumemodel.parameters and item not in parameters:
                parameters.append(item)

        # for bo/bg the data should be grouped on fluid zone
        if any(x in selected_data
               for x in ["BO", "BG"]) and "FLUID_ZONE" not in groups:
            if "BO" in selected_data and "BG" in selected_data:
                return html.Div("Can't plot BO against BG",
                                style={"margin-top": "40px"})
            selections["filters"]["FLUID_ZONE"] = [
                "oil" if "BO" in selected_data else "gas"
            ]

        dframe = volumemodel.get_df(filters=selections["filters"],
                                    groups=groups,
                                    parameters=parameters)

        if dframe.empty:
            return html.Div("No data left after filtering",
                            style={"margin-top": "40px"})

        df_for_figure = (dframe if not (selections["Plot type"] == "bar"
                                        and not "REAL" in selected_data) else
                         dframe.groupby([x for x in groups
                                         if x != "REAL"]).mean().reset_index())
        figure = (
            create_figure(
                plot_type=selections["Plot type"],
                data_frame=df_for_figure,
                x=selections["X Response"],
                y=selections["Y Response"],
                nbins=selections["hist_bins"],
                facet_col=selections["Subplots"],
                color=selections["Color by"],
                color_discrete_sequence=selections["Colorscale"],
                color_continuous_scale=selections["Colorscale"],
                barmode=selections["barmode"],
                boxmode=selections["barmode"],
                layout=dict(title=dict(
                    text=(f"{volume_description(selections['X Response'])}" +
                          (f" [{volume_unit(selections['X Response'])}]"
                           if selections["X Response"]
                           in volumemodel.volume_columns else "")),
                    x=0.5,
                    xref="paper",
                    font=dict(size=18),
                ), ),
                yaxis=dict(showticklabels=True),
            ).add_annotation(
                fluid_annotation(selections)).update_xaxes({
                    "matches": None
                } if not selections["X axis matches"] else {}).update_yaxes({
                    "matches":
                    None
                } if not selections["Y axis matches"] else {}).update_xaxes(
                    {
                        "type": "category",
                        "tickangle": 45,
                        "tickfont_size": 12
                    } if selections["X Response"] in
                    volumemodel.selectors else {}))

        return custom_plotting_layout(
            figure=figure,
            tables=make_tables(
                dframe=dframe,
                responses=list(
                    {selections["X Response"], selections["Y Response"]}),
                groups=groups,
                volumemodel=volumemodel,
                page_selected=page_selected,
                selections=selections,
                table_type="Statistics table",
                view_height=37,
            ) if selections["bottom_viz"] == "table" else None,
        )

    @callback(
        Output({
            "id": get_uuid("main-table"),
            "page": "table"
        }, "children"),
        Input(get_uuid("selections"), "data"),
        State(get_uuid("page-selected"), "data"),
    )
    def _update_page_tables(
        selections: dict,
        page_selected: str,
    ) -> list:

        if page_selected != "table":
            raise PreventUpdate

        selections = selections[page_selected]
        if not selections["update"]:
            raise PreventUpdate

        table_groups = (["ENSEMBLE", "REAL"] if selections["Table type"]
                        == "Statistics table" else ["ENSEMBLE"])
        if selections["Group by"] is not None:
            table_groups.extend(
                [x for x in selections["Group by"] if x not in table_groups])
        dframe = volumemodel.get_df(filters=selections["filters"],
                                    groups=table_groups)

        return make_tables(
            dframe=dframe,
            responses=selections["table_responses"],
            groups=selections["Group by"],
            view_height=85,
            table_type=selections["Table type"],
            volumemodel=volumemodel,
            page_selected=page_selected,
            selections=selections,
        )

    @callback(
        Output({
            "id": get_uuid("main-voldist"),
            "page": "per_zr"
        }, "children"),
        Input(get_uuid("selections"), "data"),
        State(get_uuid("page-selected"), "data"),
    )
    def _update_page_per_zr(selections: dict, page_selected: str) -> list:
        if page_selected != "per_zr":
            raise PreventUpdate

        selections = selections[page_selected]
        if not selections["update"]:
            raise PreventUpdate

        figs = []
        selectors = [
            x for x in ["ZONE", "REGION", "FACIES", "FIPNUM", "SET"]
            if x in volumemodel.selectors
        ]
        color = selections["Color by"] is not None
        for selector in selectors:
            groups = list({selector, selections["Color by"]
                           }) if color else [selector]
            dframe = volumemodel.get_df(filters=selections["filters"],
                                        groups=groups)
            piefig = ((create_figure(
                plot_type="pie",
                data_frame=dframe,
                values=selections["X Response"],
                names=selector,
                color_discrete_sequence=selections["Colorscale"],
                color=selector,
            ).update_traces(
                marker_line=dict(color="#000000", width=1)).update_layout(
                    margin=dict(l=10, b=10))) if not color else [])
            barfig = create_figure(
                plot_type="bar",
                data_frame=dframe,
                x=selector,
                y=selections["X Response"],
                title=f"{selections['X Response']} per {selector}",
                barmode="overlay"
                if selector == selections["Color by"] else "group",
                layout={
                    "bargap": 0.05
                },
                color_discrete_sequence=selections["Colorscale"],
                color=selections["Color by"],
                text=selections["X Response"],
                xaxis=dict(type="category",
                           tickangle=45,
                           tickfont_size=17,
                           title=None),
            ).update_traces(
                texttemplate=("%{text:.3s}" if selections["X Response"]
                              in volumemodel.volume_columns else
                              "%{text:.3g}"),
                textposition="auto",
            )

            if selections["X Response"] not in volumemodel.hc_responses:
                barfig.add_annotation(fluid_annotation(selections))
            figs.append([piefig, barfig])
        return plots_per_zone_region_layout(figs)

    @callback(
        Output({
            "id": get_uuid("main-voldist"),
            "page": "conv"
        }, "children"),
        Input(get_uuid("selections"), "data"),
        State(get_uuid("page-selected"), "data"),
    )
    def _update_page_conv(selections: dict, page_selected: str) -> go.Figure:
        if page_selected != "conv":
            raise PreventUpdate

        selections = selections[page_selected]
        if not selections["update"]:
            raise PreventUpdate

        subplots = selections["Subplots"] if selections[
            "Subplots"] is not None else []
        groups = ["REAL"]
        if subplots and subplots not in groups:
            groups.append(subplots)

        dframe = volumemodel.get_df(filters=selections["filters"],
                                    groups=groups)
        dframe = dframe.sort_values(by=["REAL"])

        if dframe.empty:
            return html.Div("No data left after filtering",
                            style={"margin-top": "40px"})

        dfs = []
        df_groups = dframe.groupby(subplots) if subplots else [(None, dframe)]
        for _, df in df_groups:
            for calculation in ["mean", "p10", "p90"]:
                df_stat = df.reset_index(drop=True).copy()
                df_stat[selections["X Response"]] = (
                    (df_stat[selections["X Response"]].expanding().mean())
                    if calculation == "mean" else
                    df_stat[selections["X Response"]].expanding().quantile(
                        0.1 if calculation == "p90" else 0.9))
                df_stat["calculation"] = calculation
                df_stat["index"] = df_stat.index + 1
                dfs.append(df_stat)
        if dfs:
            dframe = pd.concat(dfs)

        title = (
            f"<b>Convergence plot of mean/p10/p90 for {selections['X Response']} </b>"
            "  -  shaded areas indicates failed/filtered out realizations")

        figure = (create_figure(
            plot_type="line",
            data_frame=dframe,
            x="REAL",
            y=selections["X Response"],
            facet_col=selections["Subplots"],
            color="calculation",
            custom_data=["calculation", "index"],
            title=title,
            yaxis=dict(showticklabels=True),
        ).update_traces(
            hovertemplate=
            (f"{selections['X Response']} %{{y}} <br>"
             f"%{{customdata[0]}} for realizations {dframe['REAL'].min()}-%{{x}}<br>"
             "Realization count: %{customdata[1]} <extra></extra>"),
            line_width=3.5,
        ).update_traces(line_color="black", selector={
            "name": "mean"
        }).update_traces(
            line=dict(color="firebrick", dash="dash"),
            selector={
                "name": "p10"
            }).update_traces(
                line=dict(color="royalblue", dash="dash"),
                selector={
                    "name": "p90"
                }
            ).update_xaxes({
                "matches": None
            } if not selections["X axis matches"] else {}).update_yaxes(
                {"matches": None} if not selections["Y axis matches"] else {}))
        if selections["X Response"] not in volumemodel.hc_responses:
            figure.add_annotation(fluid_annotation(selections))

        missing_reals = [
            x for x in range(dframe["REAL"].min(), dframe["REAL"].max())
            if x not in dframe["REAL"].unique()
        ]
        if missing_reals:
            for real_range in to_ranges(missing_reals):
                figure.add_vrect(
                    x0=real_range[0] - 0.5,
                    x1=real_range[1] + 0.5,
                    fillcolor="gainsboro",
                    layer="below",
                    opacity=0.4,
                    line_width=0,
                )

        return convergence_plot_layout(figure=figure)
def update_intersection_source(app: Dash, get_uuid: Callable,
                               surface_geometry: Dict) -> None:
    @app.callback(
        Output({
            "id": get_uuid("intersection-data"),
            "element": "well-wrapper"
        }, "style"),
        Output(
            {
                "id": get_uuid("intersection-data"),
                "element": "xline-wrapper"
            }, "style"),
        Output(
            {
                "id": get_uuid("intersection-data"),
                "element": "yline-wrapper"
            }, "style"),
        Input({
            "id": get_uuid("intersection-data"),
            "element": "source"
        }, "value"),
    )
    def _show_source_details(source: str) -> Tuple[Dict, Dict, Dict]:
        hide = {"display": "none"}
        show = {"display": "inline"}
        if source == "well":
            return show, hide, hide
        if source == "xline":
            return hide, show, hide
        if source == "yline":
            return hide, hide, show
        return hide, hide, hide

    @app.callback(
        Output(
            {
                "id": get_uuid("map"),
                "element": "stored_xline"
            },
            "data",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "cross-section": "xline",
                "element": "value",
            },
            "value",
        ),
    )
    def _store_xline(x_value: Union[float, int]) -> List:

        return [
            [x_value, surface_geometry["ymin"]],
            [x_value, surface_geometry["ymax"]],
        ]

    @app.callback(
        Output(
            {
                "id": get_uuid("map"),
                "element": "stored_yline"
            },
            "data",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "cross-section": "yline",
                "element": "value",
            },
            "value",
        ),
    )
    def _store_yline(y_value: Union[float, int]) -> List:

        return [
            [surface_geometry["xmin"], y_value],
            [surface_geometry["xmax"], y_value],
        ]

    @app.callback(
        Output(
            {
                "id": get_uuid("intersection-data"),
                "cross-section": MATCH,
                "element": "value",
            },
            "step",
        ),
        Input(
            {
                "id": get_uuid("intersection-data"),
                "cross-section": MATCH,
                "element": "step",
            },
            "value",
        ),
    )
    def _set_cross_section_step(step: Union[float, int]) -> Union[float, int]:
        return step
import dash_bootstrap_components as dbc
from dash import Input, Output, html, no_update

toast = html.Div([
    dbc.Button(
        "Open toast",
        id="simple-toast-toggle",
        color="primary",
        className="mb-3",
        n_clicks=0,
    ),
    dbc.Toast(
        [html.P("This is the content of the toast", className="mb-0")],
        id="simple-toast",
        header="This is the header",
        icon="primary",
        dismissable=True,
        is_open=False,
    ),
])


@app.callback(
    Output("simple-toast", "is_open"),
    [Input("simple-toast-toggle", "n_clicks")],
)
def open_toast(n):
    if n == 0:
        return no_update
    return True
Beispiel #18
0
def test_dvcv004_duplicate_outputs_across_callbacks(dash_duo):
    app = Dash(__name__)
    app.layout = html.Div(
        [html.Div(id="a"),
         html.Div(id="b"),
         html.Div(id="c")])

    @app.callback([Output("a", "children"),
                   Output("a", "style")], [Input("b", "children")])
    def x(b):
        return b, b

    @app.callback(Output("b", "children"), [Input("b", "style")])
    def y(b):
        return b

    @app.callback(Output("a", "children"), [Input("b", "children")])
    def x2(b):
        return b

    @app.callback([Output("b", "children"),
                   Output("b", "style")], [Input("c", "children")])
    def y2(c):
        return c

    @app.callback(
        [Output({"a": 1}, "children"),
         Output({
             "b": ALL,
             "c": 1
         }, "children")],
        [Input("b", "children")],
    )
    def z(b):
        return b, b

    @app.callback(
        [
            Output({"a": ALL}, "children"),
            Output({
                "b": 1,
                "c": ALL
            }, "children")
        ],
        [Input("b", "children")],
    )
    def z2(b):
        return b, b

    dash_duo.start_server(app, **debugging)

    specs = [
        [
            "Overlapping wildcard callback outputs",
            [
                # depending on the order callbacks get reported to the
                # front end, either of these could have been registered first.
                # so we use this oder-independent form that just checks for
                # both prop_id's and the string "overlaps another output"
                '({"b":1,"c":ALL}.children)',
                "overlaps another output",
                '({"b":ALL,"c":1}.children)',
                "used in a different callback.",
            ],
        ],
        [
            "Overlapping wildcard callback outputs",
            [
                '({"a":ALL}.children)',
                "overlaps another output",
                '({"a":1}.children)',
                "used in a different callback.",
            ],
        ],
        [
            "Duplicate callback outputs",
            ["Output 0 (b.children) is already in use."]
        ],
        [
            "Duplicate callback outputs",
            ["Output 0 (a.children) is already in use."]
        ],
    ]
    check_errors(dash_duo, specs)
def parameter_qc_controller(app: Dash, get_uuid: Callable,
                            parametermodel: ParametersModel):
    @app.callback(
        Output(get_uuid("property-qc-wrapper"), "children"),
        Input({
            "id": get_uuid("ensemble-selector"),
            "tab": "qc"
        }, "value"),
        Input({
            "id": get_uuid("delta-ensemble-selector"),
            "tab": "qc"
        }, "value"),
        Input(
            {
                "id": get_uuid("filter-parameter"),
                "tab": "qc"
            },
            "value",
        ),
        Input(get_uuid("property-qc-plot-type"), "value"),
    )
    def _update_bars(ensemble, delta_ensemble, parameters, plot_type):
        """Callback to switch visualization between table and distribution plots"""
        parameters = parameters if isinstance(parameters,
                                              list) else [parameters]
        ensembles = [ensemble, delta_ensemble]

        if plot_type == "table":
            columns, dframe = parametermodel.make_statistics_table(
                ensembles=ensembles, parameters=parameters)
            return dash_table.DataTable(
                style_table={
                    "height": "75vh",
                    "overflow": "auto",
                    "fontSize": 15,
                },
                style_cell={"textAlign": "center"},
                style_cell_conditional=[{
                    "if": {
                        "column_id": "PARAMETER|"
                    },
                    "textAlign": "left"
                }],
                columns=columns,
                data=dframe,
                sort_action="native",
                filter_action="native",
                merge_duplicate_headers=True,
            )
        return wcc.Graph(
            id=get_uuid("property-qc-graph"),
            config={"displayModeBar": False},
            style={"height": "75vh"},
            figure=parametermodel.make_grouped_plot(
                ensembles=ensembles,
                parameters=parameters,
                plot_type=plot_type,
            ),
        )

    @app.callback(
        Output(
            {
                "id": get_uuid("filter-parameter"),
                "tab": "qc"
            },
            "options",
        ),
        Output(
            {
                "id": get_uuid("filter-parameter"),
                "tab": "qc"
            },
            "value",
        ),
        Input(get_uuid("delta-sort"), "value"),
        Input({
            "id": get_uuid("ensemble-selector"),
            "tab": "qc"
        }, "value"),
        Input({
            "id": get_uuid("delta-ensemble-selector"),
            "tab": "qc"
        }, "value"),
        State(
            {
                "id": get_uuid("filter-parameter"),
                "tab": "qc"
            },
            "value",
        ),
    )
    def _update_parameters(sortby, ensemble, delta_ensemble, current_params):
        """Callback to sort parameters based on selection"""
        parametermodel.sort_parameters(
            ensemble=ensemble,
            delta_ensemble=delta_ensemble,
            sortby=sortby,
        )
        return [{
            "label": i,
            "value": i
        } for i in parametermodel.parameters], current_params
Beispiel #20
0
def test_dvcv006_inconsistent_wildcards(dash_duo):
    app = Dash(__name__)
    app.layout = html.Div()

    @app.callback(
        [
            Output({"b": MATCH}, "children"),
            Output({
                "b": ALL,
                "c": 1
            }, "children")
        ],
        [Input({
            "b": MATCH,
            "c": 2
        }, "children")],
    )
    def x(c):
        return c, [c]

    @app.callback(
        [Output({"a": MATCH}, "children")],
        [
            Input({"b": MATCH}, "children"),
            Input({"c": ALLSMALLER}, "children")
        ],
        [
            State({
                "d": MATCH,
                "dd": MATCH
            }, "children"),
            State({"e": ALL}, "children")
        ],
    )
    def y(b, c, d, e):
        return b + c + d + e

    dash_duo.start_server(app, **debugging)

    specs = [
        [
            "`Input` / `State` wildcards not in `Output`s",
            [
                'State 0 ({"d":MATCH,"dd":MATCH}.children)',
                "has MATCH or ALLSMALLER on key(s) d, dd",
                'where Output 0 ({"a":MATCH}.children)',
            ],
        ],
        [
            "`Input` / `State` wildcards not in `Output`s",
            [
                'Input 1 ({"c":ALLSMALLER}.children)',
                "has MATCH or ALLSMALLER on key(s) c",
                'where Output 0 ({"a":MATCH}.children)',
            ],
        ],
        [
            "`Input` / `State` wildcards not in `Output`s",
            [
                'Input 0 ({"b":MATCH}.children)',
                "has MATCH or ALLSMALLER on key(s) b",
                'where Output 0 ({"a":MATCH}.children)',
            ],
        ],
        [
            "Mismatched `MATCH` wildcards across `Output`s",
            [
                'Output 1 ({"b":ALL,"c":1}.children)',
                "does not have MATCH wildcards on the same keys as",
                'Output 0 ({"b":MATCH}.children).',
            ],
        ],
    ]
    check_errors(dash_duo, specs)
Beispiel #21
0
import dash_bootstrap_components as dbc
from dash import Input, Output, html

button_group = html.Div(
    [
        dbc.RadioItems(
            id="radios",
            className="btn-group",
            inputClassName="btn-check",
            labelClassName="btn btn-outline-primary",
            labelCheckedClassName="active",
            options=[
                {"label": "Option 1", "value": 1},
                {"label": "Option 2", "value": 2},
                {"label": "Option 3", "value": 3},
            ],
            value=1,
        ),
        html.Div(id="output"),
    ],
    className="radio-group",
)


@app.callback(Output("output", "children"), [Input("radios", "value")])
def display_value(value):
    return f"Selected value: {value}"
Beispiel #22
0
def test_dvcv010_bad_props(dash_duo):
    app = Dash(__name__)
    app.layout = html.Div(
        [
            html.Div([html.Div(id="inner-div"),
                      dcc.Input(id="inner-input")],
                     id="outer-div"),
            dcc.Input(id={"a": 1}),
        ],
        id="main",
    )

    @app.callback(
        Output("inner-div", "xyz"),
        # "data-xyz" is OK, does not give an error
        [Input("inner-input", "pdq"),
         Input("inner-div", "data-xyz")],
        [State("inner-div", "value")],
    )
    def xyz(a, b, c):
        a if b else c

    @app.callback(
        Output({"a": MATCH}, "no"),
        [Input({"a": MATCH}, "never")],
        # "boo" will not error because we don't check State MATCH/ALLSMALLER
        [State({"a": MATCH}, "boo"),
         State({"a": ALL}, "nope")],
    )
    def f(a, b, c):
        return a if b else c

    dash_duo.start_server(app, **debugging)

    specs = [
        [
            "Invalid prop for this component",
            [
                'Property "never" was used with component ID:',
                '{"a":1}',
                "in one of the Input items of a callback.",
                "This ID is assigned to a dash_core_components.Input component",
                "in the layout, which does not support this property.",
                "This ID was used in the callback(s) for Output(s):",
                '{"a":MATCH}.no',
            ],
        ],
        [
            "Invalid prop for this component",
            [
                'Property "nope" was used with component ID:',
                '{"a":1}',
                "in one of the State items of a callback.",
                "This ID is assigned to a dash_core_components.Input component",
                '{"a":MATCH}.no',
            ],
        ],
        [
            "Invalid prop for this component",
            [
                'Property "no" was used with component ID:',
                '{"a":1}',
                "in one of the Output items of a callback.",
                "This ID is assigned to a dash_core_components.Input component",
                '{"a":MATCH}.no',
            ],
        ],
        [
            "Invalid prop for this component",
            [
                'Property "pdq" was used with component ID:',
                '"inner-input"',
                "in one of the Input items of a callback.",
                "This ID is assigned to a dash_core_components.Input component",
                "inner-div.xyz",
            ],
        ],
        [
            "Invalid prop for this component",
            [
                'Property "value" was used with component ID:',
                '"inner-div"',
                "in one of the State items of a callback.",
                "This ID is assigned to a dash_html_components.Div component",
                "inner-div.xyz",
            ],
        ],
        [
            "Invalid prop for this component",
            [
                'Property "xyz" was used with component ID:',
                '"inner-div"',
                "in one of the Output items of a callback.",
                "This ID is assigned to a dash_html_components.Div component",
                "inner-div.xyz",
            ],
        ],
    ]
    check_errors(dash_duo, specs)
Beispiel #23
0
    def set_callbacks(self, app):
        @app.callback(
            Output(self.ids("map-view"), "layers"),
            [
                Input(self.ids("surface"), "value"),
                Input(self.ids("surface-type"), "value"),
                Input(self.ids("gridparameter"), "value"),
                Input(self.ids("color-values"), "value"),
                Input(self.ids("map-view"), "switch"),
            ],
        )
        def _render_surface(
            surfacepath,
            surface_type,
            gridparameter,
            color_values,
            hillshade,
        ):

            surface = xtgeo.surface_from_file(get_path(surfacepath))
            min_val = None
            max_val = None

            if surface_type == "attribute":
                min_val = color_values[0] if color_values else None
                max_val = color_values[1] if color_values else None
                grid = load_grid(get_path(self.gridfile))
                gridparameter = load_grid_parameter(grid,
                                                    get_path(gridparameter))
                surface.slice_grid3d(grid, gridparameter)

            return [
                SurfaceLeafletModel(
                    surface,
                    name="surface",
                    clip_min=min_val,
                    clip_max=max_val,
                    apply_shading=hillshade.get("value", False),
                ).layer
            ]

        @app.callback(
            Output(self.ids("fence-view"), "figure"),
            [
                Input(self.ids("map-view"), "polyline_points"),
                Input(self.ids("gridparameter"), "value"),
                Input(self.ids("surface"), "value"),
                Input(self.ids("color-values"), "value"),
                Input(self.ids("color-scale"), "colorscale"),
            ],
        )
        def _render_fence(coords, gridparameter, surfacepath, color_values,
                          colorscale):
            if not coords:
                raise PreventUpdate
            grid = load_grid(get_path(self.gridfile))
            if (grid.subgrids is not None and
                    sum([len(subgrid)
                         for subgrid in grid.subgrids.values()]) != grid.nlay):
                warnings.warn(
                    (f"Subgrid information in {self.gridfile} does not correspond "
                     "with number of grid layers. Subgrid information will be removed. "
                     "This is a bug in xtgeo==2.15.2 for grids exported from RMS using Xtgeo. "
                     "Export the grid with xtgeo>2.15.2 to remove this warning. "
                     ),
                    FutureWarning,
                )
                grid.subgrids = None
            gridparameter = load_grid_parameter(grid, get_path(gridparameter))
            fence = get_fencespec(coords)
            hmin, hmax, vmin, vmax, values = grid.get_randomline(
                fence, gridparameter, zincrement=0.5)

            surface = xtgeo.surface_from_file(get_path(surfacepath))
            s_arr = get_surface_fence(fence, surface)
            return make_heatmap(
                values,
                s_arr=s_arr,
                theme=self.plotly_theme,
                s_name=self.surfacenames[self.surfacefiles.index(surfacepath)],
                colorscale=colorscale,
                xmin=hmin,
                xmax=hmax,
                ymin=vmin,
                ymax=vmax,
                zmin=color_values[0],
                zmax=color_values[1],
                xaxis_title="Distance along polyline",
                yaxis_title=self.zunit,
            )

        @app.callback(
            [
                Output(self.ids("color-values"), "min"),
                Output(self.ids("color-values"), "max"),
                Output(self.ids("color-values"), "value"),
                Output(self.ids("color-values"), "step"),
            ],
            [Input(self.ids("color-range-btn"), "n_clicks")],
            [State(self.ids("gridparameter"), "value")],
        )
        def _update_color_slider(_clicks, gridparameter):
            grid = load_grid(get_path(self.gridfile))
            gridparameter = load_grid_parameter(grid, get_path(gridparameter))

            minv = float(f"{gridparameter.values.min():2f}")
            maxv = float(f"{gridparameter.values.max():2f}")
            value = [minv, maxv]
            step = calculate_slider_step(minv, maxv, steps=100)
            return minv, maxv, value, step
Beispiel #24
0
def multipage_app(validation=False):
    app = Dash(__name__,
               suppress_callback_exceptions=(validation == "suppress"))

    skeleton = html.Div(
        [dcc.Location(id="url", refresh=False),
         html.Div(id="page-content")])

    layout_index = html.Div([
        dcc.Link('Navigate to "/page-1"', id="index_p1", href="/page-1"),
        dcc.Link('Navigate to "/page-2"', id="index_p2", href="/page-2"),
    ])

    layout_page_1 = html.Div([
        html.H2("Page 1"),
        dcc.Input(id="input-1-state", type="text", value="Montreal"),
        dcc.Input(id="input-2-state", type="text", value="Canada"),
        html.Button(id="submit-button", n_clicks=0, children="Submit"),
        html.Div(id="output-state"),
        html.Br(),
        dcc.Link('Navigate to "/"', id="p1_index", href="/"),
        dcc.Link('Navigate to "/page-2"', id="p1_p2", href="/page-2"),
    ])

    layout_page_2 = html.Div([
        html.H2("Page 2"),
        dcc.Input(id="page-2-input", value="LA"),
        html.Div(id="page-2-display-value"),
        html.Br(),
        dcc.Link('Navigate to "/"', id="p2_index", href="/"),
        dcc.Link('Navigate to "/page-1"', id="p2_p1", href="/page-1"),
    ])

    validation_layout = html.Div(
        [skeleton, layout_index, layout_page_1, layout_page_2])

    def validation_function():
        return skeleton if flask.has_request_context() else validation_layout

    app.layout = validation_function if validation == "function" else skeleton
    if validation == "attribute":
        app.validation_layout = validation_layout

    # Index callbacks
    @app.callback(Output("page-content", "children"),
                  [Input("url", "pathname")])
    def display_page(pathname):
        if pathname == "/page-1":
            return layout_page_1
        elif pathname == "/page-2":
            return layout_page_2
        else:
            return layout_index

    # Page 1 callbacks
    @app.callback(
        Output("output-state", "children"),
        [Input("submit-button", "n_clicks")],
        [State("input-1-state", "value"),
         State("input-2-state", "value")],
    )
    def update_output(n_clicks, input1, input2):
        return ("The Button has been pressed {} times,"
                'Input 1 is "{}",'
                'and Input 2 is "{}"').format(n_clicks, input1, input2)

    # Page 2 callbacks
    @app.callback(Output("page-2-display-value", "children"),
                  [Input("page-2-input", "value")])
    def display_value(value):
        print("display_value")
        return 'You have selected "{}"'.format(value)

    return app
Beispiel #25
0
def test_graph_does_not_resize_in_tabs(dash_dcc, is_eager):
    app = Dash(__name__, eager_loading=is_eager)
    app.layout = html.Div(
        [
            html.H1("Dash Tabs component demo"),
            dcc.Tabs(
                id="tabs-example",
                value="tab-1-example",
                children=[
                    dcc.Tab(label="Tab One", value="tab-1-example", id="tab-1"),
                    dcc.Tab(label="Tab Two", value="tab-2-example", id="tab-2"),
                    dcc.Tab(
                        label="Tab Three",
                        value="tab-3-example",
                        id="tab-3",
                        disabled=True,
                        disabled_className="disabled-tab",
                    ),
                ],
            ),
            html.Div(id="tabs-content-example"),
        ]
    )

    @app.callback(
        Output("tabs-content-example", "children"),
        [Input("tabs-example", "value")],
    )
    def render_content(tab):
        if tab == "tab-1-example":
            return html.Div(
                [
                    html.H3("Tab content 1"),
                    dcc.Graph(
                        id="graph-1-tabs",
                        figure={
                            "data": [{"x": [1, 2, 3], "y": [3, 1, 2], "type": "bar"}]
                        },
                    ),
                ]
            )
        elif tab == "tab-2-example":
            return html.Div(
                [
                    html.H3("Tab content 2"),
                    dcc.Graph(
                        id="graph-2-tabs",
                        figure={
                            "data": [{"x": [1, 2, 3], "y": [5, 10, 6], "type": "bar"}]
                        },
                    ),
                ]
            )

    dash_dcc.start_server(app)

    tab_one = dash_dcc.wait_for_element("#tab-1")
    tab_two = dash_dcc.wait_for_element("#tab-2")

    # wait for disabled tab with custom className
    dash_dcc.wait_for_element("#tab-3.disabled-tab")

    WebDriverWait(dash_dcc.driver, 10).until(
        EC.element_to_be_clickable((By.ID, "tab-2"))
    )

    # wait for Graph to be ready
    WebDriverWait(dash_dcc.driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#graph-1-tabs .main-svg"))
    )

    is_eager = "eager" if is_eager else "lazy"

    dash_dcc.percy_snapshot(
        f"Tabs with Graph - initial (graph should not resize) ({is_eager})"
    )
    tab_two.click()

    # wait for Graph to be ready
    WebDriverWait(dash_dcc.driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#graph-2-tabs .main-svg"))
    )

    dash_dcc.percy_snapshot(
        f"Tabs with Graph - clicked tab 2 (graph should not resize) ({is_eager})"
    )

    WebDriverWait(dash_dcc.driver, 10).until(
        EC.element_to_be_clickable((By.ID, "tab-1"))
    )

    tab_one.click()

    # wait for Graph to be loaded after clicking
    WebDriverWait(dash_dcc.driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#graph-1-tabs .main-svg"))
    )

    dash_dcc.percy_snapshot(
        f"Tabs with Graph - clicked tab 1 (graph should not resize) ({is_eager})"
    )

    assert dash_dcc.get_logs() == []
Beispiel #26
0
import dash_bootstrap_components as dbc
from dash import Input, Output, html

list_group = html.Div([
    dbc.ListGroup([
        dbc.ListGroupItem("Internal link", href="/l/components/list_group"),
        dbc.ListGroupItem("External link", href="https://google.com"),
        dbc.ListGroupItem("Disabled link",
                          href="https://google.com",
                          disabled=True),
        dbc.ListGroupItem("Button", id="button-item", n_clicks=0, action=True),
    ]),
    html.P(id="counter"),
])


@app.callback(Output("counter", "children"),
              [Input("button-item", "n_clicks")])
def count_clicks(n):
    return f"Button clicked {n} times"
Beispiel #27
0
        dbc.Button(
            "Open Offcanvas", id="open-offcanvas-placement", n_clicks=0
        ),
        dbc.Offcanvas(
            html.P("Some offcanvas content..."),
            id="offcanvas-placement",
            title="Placement",
            is_open=False,
        ),
    ]
)


@app.callback(
    Output("offcanvas-placement", "is_open"),
    Input("open-offcanvas-placement", "n_clicks"),
    [State("offcanvas-placement", "is_open")],
)
def toggle_offcanvas(n1, is_open):
    if n1:
        return not is_open
    return is_open


@app.callback(
    Output("offcanvas-placement", "placement"),
    Input("offcanvas-placement-selector", "value"),
)
def toggle_placement(placement):
    return placement
Beispiel #28
0
    html.P("Mean:"),
    dcc.Slider(id="histograms-mean",
               min=-3,
               max=3,
               value=0,
               marks={
                   -3: "-3",
                   3: "3"
               }),
    html.P("Standard Deviation:"),
    dcc.Slider(id="histograms-std",
               min=1,
               max=3,
               value=1,
               marks={
                   1: "1",
                   3: "3"
               }),
])


@callback(
    Output("histograms-graph", "figure"),
    Input("histograms-mean", "value"),
    Input("histograms-std", "value"),
)
def display_color(mean, std):
    data = np.random.normal(mean, std, size=500)
    fig = px.histogram(data, nbins=30, range_x=[-10, 10])
    return fig
Beispiel #29
0
def test_ldcp006_children_identity(dash_dcc):
    lock = Lock()

    app = Dash(__name__)
    app.layout = html.Div([
        html.Button("click", id="btn"),
        dcc.Loading(dcc.Graph(id="graph"), className="loading"),
    ])

    @app.callback(Output("graph", "figure"), [Input("btn", "n_clicks")])
    def update_graph(n):
        with lock:
            bars = list(range(2, (n or 0) + 5))
            return {
                "data": [{
                    "type": "bar",
                    "x": bars,
                    "y": bars
                }],
                "layout": {
                    "width": 400,
                    "height": 400
                },
            }

    def get_graph_visibility():
        return dash_dcc.driver.execute_script(
            "var gd_ = document.querySelector('.js-plotly-plot');"
            "return getComputedStyle(gd_).visibility;")

    with lock:
        dash_dcc.start_server(app)
        dash_dcc.find_element(".loading .dash-spinner")
        dash_dcc.find_element("#graph .js-plotly-plot")
        dash_dcc.driver.execute_script(
            "window.gd = document.querySelector('.js-plotly-plot');"
            "window.gd.__test__ = 'boo';")
        assert get_graph_visibility() == "hidden"

    test_identity = ("var gd_ = document.querySelector('.js-plotly-plot');"
                     "return gd_ === window.gd && gd_.__test__ === 'boo';")

    wait.until(
        lambda: len(dash_dcc.find_elements(".js-plotly-plot .bars path")) == 3,
        3)
    assert dash_dcc.driver.execute_script(test_identity)
    assert get_graph_visibility() == "visible"

    with lock:
        dash_dcc.find_element("#btn").click()
        dash_dcc.find_element(".loading .dash-spinner")
        assert len(dash_dcc.find_elements(".js-plotly-plot .bars path")) == 3
        assert dash_dcc.driver.execute_script(test_identity)
        assert get_graph_visibility() == "hidden"

    wait.until(
        lambda: len(dash_dcc.find_elements(".js-plotly-plot .bars path")) == 4,
        3)
    assert dash_dcc.driver.execute_script(test_identity)
    assert get_graph_visibility() == "visible"

    assert dash_dcc.get_logs() == []
Beispiel #30
0
        [
            dbc.AccordionItem(
                "This is the content of the first section. It has a "
                "default ID of item-0.",
                title="Item 1: item-0",
            ),
            dbc.AccordionItem(
                "This is the content of the second section. It has a "
                "default ID of item-1.",
                title="Item 2: item-1",
            ),
            dbc.AccordionItem(
                "This is the content of the third section. It has a "
                "default ID of item-2.",
                title="Item 3: item-2",
            ),
        ],
        id="accordion-always-open",
        always_open=True,
    ),
    html.Div(id="accordion-contents-open-ids", className="mt-3"),
])


@app.callback(
    Output("accordion-contents-open-ids", "children"),
    [Input("accordion-always-open", "active_item")],
)
def change_item(item):
    return f"Item(s) selected: {item}"