import time

import dash_bootstrap_components as dbc
from dash import Input, Output, html

loading_spinner = html.Div(
    [
        dbc.Button("Load", id="loading-button", n_clicks=0),
        dbc.Spinner(html.Div(id="loading-output")),
    ]
)


@app.callback(
    Output("loading-output", "children"), [Input("loading-button", "n_clicks")]
)
def load_output(n):
    if n:
        time.sleep(1)
        return f"Output loaded {n} times"
    return "Output not reloaded yet"
示例#2
0
                dbc.NavLink("Page 1", href="/page-1", active="exact"),
                dbc.NavLink("Page 2", href="/page-2", active="exact"),
            ],
            vertical=True,
            pills=True,
        ),
    ],
    style=SIDEBAR_STYLE,
)

content = html.Div(id="page-content", style=CONTENT_STYLE)

app.layout = html.Div([dcc.Location(id="url"), sidebar, content])


@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
    if pathname == "/":
        return html.P("This is the content of the home page!")
    elif pathname == "/page-1":
        return html.P("This is the content of page 1. Yay!")
    elif pathname == "/page-2":
        return html.P("Oh cool, this is page 2!")
    # If the user tries to reach a different page, return a 404 message
    return html.Div(
        [
            html.H1("404: Not found", className="text-danger"),
            html.Hr(),
            html.P(f"The pathname {pathname} was not recognised..."),
        ],
        className="p-3 bg-light rounded-3",
示例#3
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"]: val
            for val, id_values in zip(values, id_list)
            if id_values["view"] == view
        }
示例#4
0
                        H5("We are also always on the lookout for donations of medical equipment and supplies. If you can help in this area please contact us at [email protected]"
                           ),
                    ],
                    width=7,
                ),
            ],
            justify="center",
            className="mt-5 mb-2",
        ),
    ],
    fluid=True,
)


# top left bar graph ******************************************************
@callback(Output("bar-col", "children"), Input("bar-data", "value"))
def create_bar_graph(data_column):
    bar_grpah = dcc.Graph(
        figure=px.bar(dfc,
                      x=data_column,
                      y="Patients turned away",
                      hover_data=["Activity Date"]).update_xaxes(
                          categoryorder="total descending"))
    return bar_grpah


# top right map ***********************************************************
@callback(Output("map-conslt-col", "children"),
          Input("map1-conslt-data", "value"))
def create_map(data_column):
    map1 = dcc.Graph(
示例#5
0
# Copied from the Dash documetation sample code at https://github.com/plotly/dash-recipes/tree/master/multi-page-app
from dash import Input, Output, html, dcc
import dash_bootstrap_components as dbc
from multi_page_app.app import app

layout = dbc.Container(
    fluid=True,
    children=[
        html.Br(),
        html.H1('Fruit Selector'),
        dcc.Dropdown(id='app-2-dropdown',
                     options=[{
                         'label': '{}'.format(i),
                         'value': i
                     } for i in ['Apple', 'Banana', 'Coconut', 'Date']]),
        html.Div(id='app-2-display-value')
    ])


@app.callback(Output('app-2-display-value', 'children'),
              Input('app-2-dropdown', 'value'))
def display_value(value):
    return 'You have selected "{}"'.format(value)
示例#6
0
app.layout = html.Div([
    dcc.Input(id="input1", value="AAA"),
    html.Button(id="run-button1", children="Run"),
    html.Div(id="status1", children="Finished"),
    html.Div(id="result1", children="No results"),
    html.Hr(),
    dcc.Input(id="input2", value="aaa"),
    html.Button(id="run-button2", children="Run"),
    html.Div(id="status2", children="Finished"),
    html.Div(id="result2", children="No results"),
])


@app.long_callback(
    Output("result1", "children"),
    [Input("run-button1", "n_clicks"),
     State("input1", "value")],
    progress=Output("status1", "children"),
    progress_default="Finished",
    interval=500,
    cache_args_to_ignore=[0],
)
def update_output1(set_progress, _n_clicks, value):
    for i in range(4):
        set_progress(f"Progress {i}/4")
        time.sleep(2)
    return f"Result for '{value}'"


@app.long_callback(
    Output("result2", "children"),
示例#7
0
def test_clsd013_clientside_callback_context_states_list(dash_duo):
    app = Dash(__name__, assets_folder="assets")

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

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

    dash_duo.start_server(app)

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

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

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

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

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

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

    dash_duo.wait_for_text_to_equal(
        "#output-clientside",
        ('[{"id":"in0","property":"value","value":"test 0"},'
         '[{"id":{"in1":0},"property":"value","value":"test 1"},'
         '{"id":{"in1":1},"property":"value"},'
         '{"id":{"in1":2},"property":"value","value":"test 2"}]]'),
    )
示例#8
0
def test_cdpr002_updatemodes(dash_dcc):
    app = Dash(__name__)

    app.layout = html.Div(
        [
            dcc.DatePickerRange(
                id="date-picker-range",
                start_date_id="startDate",
                end_date_id="endDate",
                start_date_placeholder_text="Select a start date!",
                end_date_placeholder_text="Select an end date!",
                updatemode="bothdates",
            ),
            html.Div(id="date-picker-range-output"),
        ]
    )

    @app.callback(
        Output("date-picker-range-output", "children"),
        [
            Input("date-picker-range", "start_date"),
            Input("date-picker-range", "end_date"),
        ],
    )
    def update_output(start_date, end_date):
        return "{} - {}".format(start_date, end_date)

    dash_dcc.start_server(app=app)

    start_date = dash_dcc.find_element("#startDate")
    start_date.click()

    end_date = dash_dcc.find_element("#endDate")
    end_date.click()

    assert (
        dash_dcc.find_element("#date-picker-range-output").text == "None - None"
    ), "the output should not update when both clicked but no selection happen"

    start_date.click()

    dash_dcc.find_elements(dash_dcc.date_picker_day_locator)[4].click()
    assert (
        dash_dcc.find_element("#date-picker-range-output").text == "None - None"
    ), "the output should not update when only one is selected"

    eday = dash_dcc.find_elements(dash_dcc.date_picker_day_locator)[-4]
    wait.until(lambda: eday.is_displayed() and eday.is_enabled(), timeout=2)
    eday.click()

    date_tokens = set(start_date.get_attribute("value").split("/"))
    date_tokens.update(end_date.get_attribute("value").split("/"))

    assert (
        set(
            itertools.chain(
                *[
                    _.split("-")
                    for _ in dash_dcc.find_element(
                        "#date-picker-range-output"
                    ).text.split(" - ")
                ]
            )
        )
        == date_tokens
    ), "date should match the callback output"

    assert dash_dcc.get_logs() == []
示例#9
0
import dash

dash.register_page(__name__)

from dash import Dash, dcc, html, Input, Output, callback
import plotly.express as px

df = px.data.medals_wide(indexed=True)

layout = html.Div(
    [
        html.P("Medals included:"),
        dcc.Checklist(
            id="heatmaps-medals",
            options=[{"label": x, "value": x} for x in df.columns],
            value=df.columns.tolist(),
        ),
        dcc.Graph(id="heatmaps-graph"),
    ]
)


@callback(Output("heatmaps-graph", "figure"), Input("heatmaps-medals", "value"))
def filter_heatmap(cols):
    fig = px.imshow(df[cols])
    return fig
示例#10
0
def test_clsd002_chained_serverside_clientside_callbacks(dash_duo):
    app = Dash(__name__, assets_folder="assets")

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

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

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

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

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

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

    dash_duo.start_server(app)

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

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

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

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

    assert call_counts["display"].value == 2
    assert call_counts["divide"].value == 2
示例#11
0
            style={'textAlign': 'center'}),
    # interval activated once/week or when page refreshed
    dcc.Interval(id='interval_db', interval=86400000 * 7, n_intervals=0),
    html.Div(id='mongo-datatable', children=[]),
    html.Div([
        html.Div(id='pie-graph', className='five columns'),
        html.Div(id='hist-graph', className='six columns'),
    ],
             className='row'),
    dcc.Store(id='changed-cell')
])


# Display Datatable with data from Mongo database
@app.callback(Output('mongo-datatable', component_property='children'),
              Input('interval_db', component_property='n_intervals'))
def populate_datatable(n_intervals):
    # Convert the Collection (table) date to a pandas DataFrame
    df = pd.DataFrame(list(collection.find()))
    # Convert id from ObjectId to string so it can be read by DataTable
    df['_id'] = df['_id'].astype(str)
    print(df.head(20))

    return [
        dash_table.DataTable(
            id='our-table',
            data=df.to_dict('records'),
            columns=[{
                'id': p,
                'name': p,
                'editable': False
    def set_callbacks(self, app: Dash) -> None:
        @app.callback(
            [
                Output(self.tornadoplot.storage_id, "data"),
                Output(self.uuid("table"), "data"),
                Output(self.uuid("table"), "columns"),
            ],
            [
                Input(i, "value")
                for sublist in [
                    [
                        self.uuid("ensemble"),
                        self.uuid("response"),
                        self.uuid("source"),
                    ],
                    list(self.selectors_id.values()),
                ]
                for i in sublist
            ],
        )
        def _render_table_and_tornado(
            ensemble: str, response: str, source: str, *filters: Union[str, List[str]]
        ) -> Tuple[str, List[dict], List[dict]]:
            # Filter data
            data = filter_dataframe(
                self.volumes, self.selectors, ensemble, source, filters
            )

            # Table data
            table, columns = calculate_table(data, response)

            # TornadoPlot input
            tornado = json.dumps(
                {
                    "ENSEMBLE": ensemble,
                    "data": data.groupby("REAL")
                    .sum()
                    .reset_index()[["REAL", response]]
                    .values.tolist(),
                    "response_name": response,
                    "number_format": "#.4g",
                    "unit": volume_unit(response),
                }
            )
            return tornado, table, columns

        @app.callback(
            [
                Output(self.uuid("graph-wrapper"), "children"),
                Output(self.uuid("volume_title"), "children"),
            ],
            [
                Input(self.tornadoplot.click_id, "data"),
                Input(self.tornadoplot.high_low_storage_id, "data"),
                Input(self.uuid("plot-type"), "value"),
            ],
            [
                State(i, "value")
                for sublist in [
                    [
                        self.uuid("ensemble"),
                        self.uuid("response"),
                        self.uuid("source"),
                    ],
                    list(self.selectors_id.values()),
                ]
                for i in sublist
            ],
        )
        def _render_chart(
            tornado_click_data_str: Union[str, None],
            high_low_storage: dict,
            plot_type: str,
            ensemble: str,
            response: str,
            source: str,
            *filters: Union[str, List[str]],
        ) -> Tuple[Union[wcc.Graph, html.Div], str]:
            if callback_context.triggered is None:
                raise PreventUpdate

            tornado_click: Union[dict, None] = (
                json.loads(tornado_click_data_str) if tornado_click_data_str else None
            )

            ctx = callback_context.triggered[0]["prop_id"].split(".")[0]
            if tornado_click:
                if (
                    high_low_storage is not None
                    and ctx == self.tornadoplot.high_low_storage_id
                ):
                    # Tornado plot is updated without a click in the plot, updating reals:
                    if tornado_click["sens_name"] in high_low_storage:
                        tornado_click["real_low"] = high_low_storage[
                            tornado_click["sens_name"]
                        ].get("real_low")
                        tornado_click["real_high"] = high_low_storage[
                            tornado_click["sens_name"]
                        ].get("real_high")
                    else:
                        # fallback in a case where chosen sens_name does not exist in updated
                        # tornado plot.
                        # (can this ever occur except when sens_name already is None?)
                        tornado_click["sens_name"] = None
                        tornado_click["real_low"] = []
                        tornado_click["real_high"] = []

            # Filter data
            data = filter_dataframe(
                self.volumes, self.selectors, ensemble, source, filters
            )

            # Volume title:
            volume_title = f"{volume_description(response)} [{volume_unit(response)}]"

            # Make Plotly figure
            layout = {}
            layout.update({"height": 600, "margin": {"l": 100, "b": 100}})
            if plot_type == "Per realization":
                # One bar per realization
                layout.update(
                    {
                        "title": "Response per realization",
                        "xaxis": {"title": "Realizations"},
                        "yaxis": {"title": volume_title},
                    }
                )
                plot_data = data.groupby("REAL").sum().reset_index()

                if tornado_click:
                    figure_data = [
                        {
                            "y": plot_data[
                                plot_data["REAL"].isin(tornado_click["real_low"])
                            ][response],
                            "x": plot_data[
                                plot_data["REAL"].isin(tornado_click["real_low"])
                            ]["REAL"],
                            "tickformat": ".4s",
                            "type": "bar",
                            "showlegend": False,
                            "name": "",
                        },
                        {
                            "y": plot_data[
                                plot_data["REAL"].isin(tornado_click["real_high"])
                            ][response],
                            "x": plot_data[
                                plot_data["REAL"].isin(tornado_click["real_high"])
                            ]["REAL"],
                            "tickformat": ".4s",
                            "type": "bar",
                            "showlegend": False,
                            "name": "",
                        },
                        {
                            "y": plot_data[
                                ~plot_data["REAL"].isin(
                                    tornado_click["real_low"]
                                    + tornado_click["real_high"]
                                )
                            ][response],
                            "x": plot_data[
                                ~plot_data["REAL"].isin(
                                    tornado_click["real_low"]
                                    + tornado_click["real_high"]
                                )
                            ]["REAL"],
                            "tickformat": ".4s",
                            "type": "bar",
                            "marker": {"color": "grey"},
                            "showlegend": False,
                            "name": "",
                        },
                    ]
                else:
                    figure_data = [
                        {
                            "y": plot_data[response],
                            "x": plot_data["REAL"],
                            "tickformat": ".4s",
                            "type": "bar",
                            "marker": {"color": "grey"},
                        },
                    ]
                figure = wcc.Graph(
                    config={"displayModeBar": False},
                    id=self.uuid("graph"),
                    figure={
                        "data": figure_data,
                        "layout": self.theme.create_themed_layout(layout),
                    },
                )
            elif plot_type == "Per sensitivity case":
                # One box per sensitivity name
                layout.update(
                    {
                        "title": "Distribution for each sensitivity case",
                        "yaxis": {"title": volume_title},
                    }
                )
                figure = wcc.Graph(
                    config={"displayModeBar": False},
                    id=self.uuid("graph"),
                    figure={
                        "data": [
                            {
                                "y": senscase_df.groupby("REAL")
                                .sum()
                                .reset_index()[response],
                                "name": f"{sensname} ({senscase})",
                                "type": "box",
                            }
                            for sensname, sensname_df in data.groupby(["SENSNAME"])
                            for senscase, senscase_df in sensname_df.groupby(
                                ["SENSCASE"]
                            )
                        ],
                        "layout": self.theme.create_themed_layout(layout),
                    },
                )
            elif plot_type == "Per sensitivity name":
                # One box per sensitivity name
                layout.update(
                    {
                        "title": "Distribution for each sensitivity name",
                        "yaxis": {"title": volume_title},
                    }
                )
                figure = wcc.Graph(
                    config={"displayModeBar": False},
                    id=self.uuid("graph"),
                    figure={
                        "data": [
                            {
                                "y": sensname_df.groupby("REAL")
                                .sum()
                                .reset_index()[response],
                                "name": f"{sensname}",
                                "type": "box",
                            }
                            for sensname, sensname_df in data.groupby(["SENSNAME"])
                        ],
                        "layout": self.theme.create_themed_layout(layout),
                    },
                )
            else:
                # Should not occur unless plot_type options are changed
                figure = html.Div("Invalid plot type")

            return figure, volume_title
示例#13
0
def update_maps(
    app: Dash,
    get_uuid: Callable,
    surface_set_models: Dict[str, SurfaceSetModel],
    well_set_model: WellSetModel,
) -> None:
    @app.callback(
        Output({"id": get_uuid("map"), "element": "label"}, "children"),
        Output(get_uuid("leaflet-map1"), "layers"),
        Output({"id": get_uuid("map2"), "element": "label"}, "children"),
        Output(get_uuid("leaflet-map2"), "layers"),
        Output({"id": get_uuid("map3"), "element": "label"}, "children"),
        Output(get_uuid("leaflet-map3"), "layers"),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map1",
                "element": "surfaceattribute",
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map2",
                "element": "surfaceattribute",
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map1",
                "element": "surfacename",
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map2",
                "element": "surfacename",
            },
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "map_id": "map1", "element": "ensemble"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "map_id": "map2", "element": "ensemble"},
            "value",
        ),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map1",
                "element": "calculation",
            },
            "value",
        ),
        Input(
            {
                "id": get_uuid("map-settings"),
                "map_id": "map2",
                "element": "calculation",
            },
            "value",
        ),
        Input(get_uuid("leaflet-map1"), "switch"),
        Input(get_uuid("leaflet-map2"), "switch"),
        Input(get_uuid("leaflet-map3"), "switch"),
        Input(
            {"id": get_uuid("map-settings"), "map_id": "map1", "element": "options"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "map_id": "map2", "element": "options"},
            "value",
        ),
        Input(get_uuid("map-color-ranges"), "data"),
        Input(
            {"id": get_uuid("map-settings"), "settings": "compute_diff"},
            "value",
        ),
        Input(get_uuid("realization-store"), "data"),
        Input({"id": get_uuid("intersection-data"), "element": "well"}, "value"),
        Input({"id": get_uuid("map"), "element": "stored_polyline"}, "data"),
        Input({"id": get_uuid("map"), "element": "stored_xline"}, "data"),
        Input({"id": get_uuid("map"), "element": "stored_yline"}, "data"),
        Input({"id": get_uuid("intersection-data"), "element": "source"}, "value"),
        State(get_uuid("leaflet-map1"), "layers"),
        State(get_uuid("leaflet-map2"), "layers"),
        State(get_uuid("leaflet-map3"), "layers"),
    )
    # pylint: disable=too-many-arguments, too-many-locals, too-many-branches
    def _update_maps(
        surfattr_map: str,
        surfattr_map2: str,
        surfname_map: str,
        surfname_map2: str,
        ensemble_map: str,
        ensemble_map2: str,
        calc_map: str,
        calc_map2: str,
        shade_map: Dict[str, bool],
        shade_map2: Dict[str, bool],
        shade_map3: Dict[str, bool],
        options: List[str],
        options2: List[str],
        color_range_settings: Dict,
        compute_diff: List[str],
        real_list: List[str],
        wellname: Optional[str],
        polyline: Optional[List],
        xline: Optional[List],
        yline: Optional[List],
        source: str,
        current_map: List,
        current_map2: List,
        current_map3: List,
    ) -> Tuple[str, List, str, List, str, List]:
        """Generate Leaflet layers for the three map views"""
        realizations = [int(real) for real in real_list]
        ctx = callback_context.triggered[0]
        if "compute_diff" in ctx["prop_id"]:
            if not compute_diff:
                return (
                    no_update,
                    no_update,
                    no_update,
                    no_update,
                    no_update,
                    [],
                )

        # Check if map is already generated and should just be updated with polylines
        update_poly_only = bool(
            current_map
            and (
                "stored_polyline" in ctx["prop_id"]
                or "stored_yline" in ctx["prop_id"]
                or "stored_xline" in ctx["prop_id"]
            )
        )
        if polyline is not None:
            poly_layer = create_leaflet_polyline_layer(
                polyline, name="Polyline", poly_id="random_line"
            )
            for map_layers in [current_map, current_map2, current_map3]:
                map_layers = replace_or_add_map_layer(
                    map_layers, "Polyline", poly_layer
                )
        if xline is not None and source == "xline":
            xline_layer = create_leaflet_polyline_layer(
                xline, name="Xline", poly_id="x_line"
            )
            for map_layers in [current_map, current_map2, current_map3]:
                map_layers = replace_or_add_map_layer(map_layers, "Xline", xline_layer)
        if yline is not None and source == "yline":
            yline_layer = create_leaflet_polyline_layer(
                yline, name="Yline", poly_id="y_line"
            )
            for map_layers in [current_map, current_map2, current_map3]:
                map_layers = replace_or_add_map_layer(map_layers, "Yline", yline_layer)
        # If callback is triggered by polyline drawing, only update polyline
        if update_poly_only:
            return (
                f"Surface A: {surfattr_map} - {surfname_map} - {ensemble_map} - {calc_map}",
                current_map,
                f"Surface B: {surfattr_map2} - {surfname_map2} - {ensemble_map2} - {calc_map2}",
                no_update,
                "Surface A-B",
                no_update,
            )

        if wellname is not None:
            well = well_set_model.get_well(wellname)
            well_layer = make_well_layer(well, name=well.name)

            # If callback is triggered by well change, only update well layer
            if "well" in ctx["prop_id"] or (
                "source" in ctx["prop_id"] and source == "well"
            ):
                for map_layers in [current_map, current_map2, current_map3]:
                    map_layers = replace_or_add_map_layer(
                        map_layers, "Well", well_layer
                    )
                return (
                    f"Surface A: {surfattr_map} - {surfname_map} - "
                    f"{ensemble_map} - {calc_map}",
                    current_map,
                    f"Surface B: {surfattr_map2} - {surfname_map2} - "
                    f"{ensemble_map2} - {calc_map2}",
                    current_map2,
                    "Surface A-B",
                    no_update,
                )

        # Calculate maps
        if calc_map in ["Mean", "StdDev", "Max", "Min", "P90", "P10"]:
            surface = surface_set_models[ensemble_map].calculate_statistical_surface(
                name=surfname_map,
                attribute=surfattr_map,
                calculation=calc_map,
                realizations=realizations,
            )
        else:
            surface = surface_set_models[ensemble_map].get_realization_surface(
                name=surfname_map, attribute=surfattr_map, realization=int(calc_map)
            )
        if calc_map2 in ["Mean", "StdDev", "Max", "Min", "P90", "P10"]:
            surface2 = surface_set_models[ensemble_map2].calculate_statistical_surface(
                name=surfname_map2,
                attribute=surfattr_map2,
                calculation=calc_map2,
                realizations=realizations,
            )
        else:
            surface2 = surface_set_models[ensemble_map2].get_realization_surface(
                name=surfname_map2, attribute=surfattr_map2, realization=int(calc_map2)
            )

        # Generate Leaflet layers
        update_controls = check_if_update_needed(
            ctx=ctx,
            current_maps=[current_map, current_map2],
            compute_diff=compute_diff,
            color_range_settings=color_range_settings,
        )

        surface_layers = create_or_return_base_layer(
            update_controls,
            surface,
            current_map,
            shade_map,
            color_range_settings,
            map_id="map1",
        )
        surface_layers2 = create_or_return_base_layer(
            update_controls,
            surface2,
            current_map2,
            shade_map2,
            color_range_settings,
            map_id="map2",
        )

        try:
            surface3 = surface.copy()
            surface3.values = surface3.values - surface2.values

            diff_layers = (
                [
                    SurfaceLeafletModel(
                        surface3,
                        name="surface3",
                        apply_shading=shade_map3.get("value", False),
                    ).layer
                ]
                if update_controls["diff_map"]["update"]
                else []
            )
        except ValueError:
            diff_layers = []

        if wellname is not None:
            surface_layers.append(well_layer)
            surface_layers2.append(well_layer)
        if polyline is not None:
            surface_layers.append(poly_layer)
        if xline is not None and source == "xline":
            surface_layers.append(xline_layer)
        if yline is not None and source == "yline":
            surface_layers.append(yline_layer)
        if well_set_model is not None:
            if options is not None or options2 is not None:
                if "intersect_well" in options or "intersect_well" in options2:
                    ### This is potentially a heavy task as it loads all wells into memory
                    wells: List[xtgeo.Well] = list(well_set_model.wells.values())
                if "intersect_well" in options and update_controls["map1"]["update"]:
                    surface_layers.append(
                        create_leaflet_well_marker_layer(wells, surface)
                    )
                if "intersect_well" in options2 and update_controls["map2"]["update"]:
                    surface_layers2.append(
                        create_leaflet_well_marker_layer(wells, surface2)
                    )

        return (
            f"Surface A: {surfattr_map} - {surfname_map} - {ensemble_map} - {calc_map}",
            surface_layers if update_controls["map1"]["update"] else no_update,
            f"Surface B: {surfattr_map2} - {surfname_map2} - {ensemble_map2} - {calc_map2}",
            surface_layers2 if update_controls["map2"]["update"] else no_update,
            "Surface A-B",
            diff_layers if update_controls["diff_map"]["update"] else no_update,
        )

    @app.callback(
        Output({"id": get_uuid("map"), "element": "stored_polyline"}, "data"),
        Input(get_uuid("leaflet-map1"), "polyline_points"),
    )
    def _store_polyline_points(
        positions_yx: List[List[float]],
    ) -> Optional[List[List[float]]]:
        """Stores drawn in polyline in a dcc.Store. Reversing elements to reflect
        normal behaviour"""
        if positions_yx is not None:
            try:
                return [[pos[1], pos[0]] for pos in positions_yx]
            except TypeError:
                warnings.warn("Polyline for map is not valid format")
                return None
        raise PreventUpdate

    @app.callback(
        Output(
            {"id": get_uuid("intersection-data"), "element": "source"},
            "value",
        ),
        Output(
            {"id": get_uuid("intersection-data"), "element": "well"},
            "value",
        ),
        Input(get_uuid("leaflet-map1"), "clicked_shape"),
        Input(get_uuid("leaflet-map1"), "polyline_points"),
    )
    def _update_from_map_click(
        clicked_shape: Optional[Dict],
        _polyline: List[List[float]],
    ) -> Tuple[str, Union[_NoUpdate, str]]:
        """Update intersection source and optionally selected well when
        user clicks a shape in map"""
        ctx = callback_context.triggered[0]
        if "polyline_points" in ctx["prop_id"]:
            return "polyline", no_update
        if clicked_shape is None:
            raise PreventUpdate
        if clicked_shape.get("id") == "random_line":
            return "polyline", no_update
        if clicked_shape.get("id") in well_set_model.well_names:
            return "well", clicked_shape.get("id")
        raise PreventUpdate

    @app.callback(
        Output(get_uuid("map-color-ranges"), "data"),
        Output(
            {"id": get_uuid("map-settings"), "colors": "map2_clip_min"},
            "disabled",
        ),
        Output(
            {"id": get_uuid("map-settings"), "colors": "map2_clip_max"},
            "disabled",
        ),
        Input(
            {"id": get_uuid("map-settings"), "colors": "map1_clip_min"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "colors": "map1_clip_max"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "colors": "map2_clip_min"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "colors": "map2_clip_max"},
            "value",
        ),
        Input(
            {"id": get_uuid("map-settings"), "colors": "sync_range"},
            "value",
        ),
    )
    def _color_range_options(
        clip_min_map1: Optional[float],
        clip_max_map1: Optional[float],
        clip_min_map2: Optional[float],
        clip_max_map2: Optional[float],
        sync_range: list,
    ) -> Tuple[Dict[str, Dict], bool, bool]:
        ctx = callback_context.triggered[0]

        return (
            {
                "map1": {
                    "color_range": [clip_min_map1, clip_max_map1],
                    "update": "map1" in ctx["prop_id"],
                },
                "map2": {
                    "color_range": [clip_min_map2, clip_max_map2]
                    if not sync_range
                    else [clip_min_map1, clip_max_map1],
                    "update": "map2" in ctx["prop_id"]
                    or (sync_range and "map1" in ctx["prop_id"])
                    or (
                        "sync_range" in ctx["prop_id"]
                        and [clip_min_map1, clip_max_map1]
                        != [clip_min_map2, clip_max_map2]
                    ),
                },
            },
            bool(sync_range),
            bool(sync_range),
        )
示例#14
0
def test_inin026_graphs_in_tabs_do_not_share_state(dash_duo):
    app = Dash(__name__, suppress_callback_exceptions=True)

    app.layout = html.Div([
        dcc.Tabs(
            id="tabs",
            children=[
                dcc.Tab(label="Tab 1", value="tab1", id="tab1"),
                dcc.Tab(label="Tab 2", value="tab2", id="tab2"),
            ],
            value="tab1",
        ),
        # Tab content
        html.Div(id="tab_content"),
    ])
    tab1_layout = [
        html.Div([
            dcc.Graph(
                id="graph1",
                figure={
                    "data": [{
                        "x": [1, 2, 3],
                        "y": [5, 10, 6],
                        "type": "bar"
                    }]
                },
            )
        ]),
        html.Pre(id="graph1_info"),
    ]

    tab2_layout = [
        html.Div([
            dcc.Graph(
                id="graph2",
                figure={
                    "data": [{
                        "x": [4, 3, 2],
                        "y": [5, 10, 6],
                        "type": "bar"
                    }]
                },
            )
        ]),
        html.Pre(id="graph2_info"),
    ]

    @app.callback(Output("graph1_info", "children"),
                  Input("graph1", "clickData"))
    def display_hover_data(hover_data):
        return json.dumps(hover_data)

    @app.callback(Output("graph2_info", "children"),
                  Input("graph2", "clickData"))
    def display_hover_data_2(hover_data):
        return json.dumps(hover_data)

    @app.callback(Output("tab_content", "children"), Input("tabs", "value"))
    def render_content(tab):
        return tab2_layout if tab == "tab2" else tab1_layout

    dash_duo.start_server(app)

    dash_duo.find_element("#graph1:not(.dash-graph--pending)").click()

    until(lambda: '"label": 2' in dash_duo.find_element("#graph1_info").text,
          timeout=3)

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

    dash_duo.find_element("#graph2:not(.dash-graph--pending)").click()

    until(lambda: '"label": 3' in dash_duo.find_element("#graph2_info").text,
          timeout=3)
    def set_callbacks(self, app):
        @app.callback(
            Output(self.uuid("output_graph"), component_property="children"),
            Input(self.uuid("filter1_id"), component_property="value"),
            Input(self.uuid("filter2_id"), component_property="value"),
            Input(self.uuid("choice_id"), component_property="value"),
        )
        def _update_graph(input_filter_obs, input_filter_param, choiceplot):
            """Renders KS matrix (how much a parameter is changed from prior to posterior"""
            active_info = read_csv(
                get_path(self.input_dir / "active_obs_info.csv"), index_col=0
            )
            joint_ks = read_csv(
                get_path(self.input_dir / "ks.csv"), index_col=0
            ).replace(np.nan, 0.0)
            input_filter_obs = _set_inputfilter(input_filter_obs)
            input_filter_param = _set_inputfilter(input_filter_param)

            listtoplot = _get_listtoplot(joint_ks, choiceplot)
            joint_ks_sorted = joint_ks.filter(items=listtoplot).sort_index(axis=1)
            xx_data = list(
                joint_ks_sorted.filter(like=input_filter_obs, axis=1).columns
            )
            yy_data = list(
                joint_ks_sorted.filter(like=input_filter_param, axis=0).index
            )
            zz_data = _get_zzdata(joint_ks_sorted, yy_data, xx_data, active_info)

            if not xx_data or not yy_data:
                raise PreventUpdate

            yall_obs_data = list(
                joint_ks_sorted.filter(like=input_filter_param, axis=0).index
            )
            zall_obs_data = joint_ks.loc[yall_obs_data, ["All_obs"]].to_numpy()

            return wcc.Graph(
                id=self.uuid("heatmap_id"),
                figure={
                    "data": [
                        go.Heatmap(
                            x=xx_data,
                            y=yy_data,
                            z=zz_data,
                            type="heatmap",
                            colorscale="YlGnBu",
                            zmin=0,
                            zmax=1,
                            hoverinfo="text",
                            text=_hovertext_list(
                                xx_data, yy_data, zz_data, active_info
                            ),
                        ),
                        go.Heatmap(
                            x=["All_obs"],
                            y=yall_obs_data,
                            z=zall_obs_data,
                            type="heatmap",
                            colorscale="YlGnBu",
                            zmin=0,
                            zmax=1,
                            hoverinfo="text",
                            text=_hovertext_list(
                                ["All_obs"], yall_obs_data, zall_obs_data, active_info
                            ),
                            xaxis="x2",
                        ),
                    ],
                    "layout": self.theme.create_themed_layout(
                        {
                            "title": "KS Matrix (degree of parameter change prior to posterior)",
                            "xaxis": {
                                "title": "Observations",
                                "ticks": "",
                                "domain": [0.0, 0.9],
                                "showticklabels": True,
                                "tickangle": 30,
                                "automargin": True,
                            },
                            "yaxis": {
                                "title": "Parameters",
                                "ticks": "",
                                "showticklabels": True,
                                "tickangle": -30,
                                "automargin": True,
                            },
                            "xaxis2": {"ticks": "", "domain": [0.95, 1.0]},
                            "plot_bgcolor": "grey",
                        }
                    ),
                },
                style={"height": "45vh"},
                clickData={"points": [{"x": xx_data[0], "y": yy_data[0]}]},
            )

        @app.callback(
            Output(self.uuid("click_data"), component_property="children"),
            Input(self.uuid("heatmap_id"), component_property="clickData"),
            Input(self.uuid("choice_hist_id"), component_property="value"),
            prevent_initial_call=True,
        )
        def _display_click_data(celldata, hist_display):
            """render a histogram of parameters distribution prior/posterior or
            an average delta map prior-posterior."""
            obs = celldata["points"][0]["x"]
            param = celldata["points"][0]["y"]
            active_info = read_csv(
                get_path(self.input_dir / "active_obs_info.csv"), index_col=0
            )
            if "FIELD" in param:
                fieldparam = param.replace("FIELD_", "")
                mygrid_ok_short = read_csv(
                    get_path(
                        Path(str(self.input_dir).replace("scalar_", "field_"))
                        / f"delta_field{fieldparam}.csv"
                    )
                )
                maxinput = mygrid_ok_short.filter(like="Mean_").max(axis=1)
                deltadata = "Mean_D_" + obs
                return wcc.Graph(
                    id="2Dmap_avgdelta",
                    figure=px.scatter(
                        mygrid_ok_short,
                        x="X_UTME",
                        y="Y_UTMN",
                        color=deltadata,
                        range_color=[0, maxinput.max()],
                        color_continuous_scale="Rainbow",
                        opacity=0.9,
                        title=f"Mean_delta_posterior-prior {obs}, {param}",
                        hover_data=[
                            "X_UTME",
                            "Y_UTMN",
                            "Z_TVDSS",
                            "IX",
                            "JY",
                            deltadata,
                        ],
                    ),
                )
            post_df = read_csv(get_path(self.input_dir / f"{obs}.csv"))
            prior_df = read_csv(get_path(self.input_dir / "prior.csv"))
            if "TRANS" in hist_display:
                paraml = [ele for ele in prior_df.keys() if f"_{param}" in ele]
                if paraml != []:
                    param = paraml[0]
            fig = go.Figure()
            fig.add_trace(go.Histogram(x=prior_df[param], name="prior", nbinsx=10))
            fig.add_trace(go.Histogram(x=post_df[param], name="update", nbinsx=10))
            fig.update_layout(
                self.theme.create_themed_layout(
                    {
                        "title": (
                            "Parameter distribution for observation "
                            f"{obs} ({active_info.at['ratio', obs]})"
                        ),
                        "bargap": 0.2,
                        "bargroupgap": 0.1,
                        "xaxis": {"title": param},
                    }
                )
            )

            return wcc.Graph(id="lineplots", style={"height": "45vh"}, figure=fig)

        @app.callback(
            Output(self.uuid("generate_table"), component_property="children"),
            Input(self.uuid("choice_id"), component_property="value"),
        )
        def _generatetable(choiceplot, max_rows=10):
            """Generate output table of data in KS matrix plot"""
            misfit_info = read_csv(
                get_path(self.input_dir / "misfit_obs_info.csv"), index_col=0
            )
            list_ok = list(misfit_info.filter(like="All_obs", axis=1).columns)
            listtoplot = [ele for ele in misfit_info.columns if ele not in list_ok]
            if choiceplot == "ALL":
                listtoplot = list_ok
            active_info = read_csv(
                get_path(self.input_dir / "active_obs_info.csv"),
                index_col=0,
            )

            joint_ks = read_csv(
                get_path(self.input_dir / "ks.csv"),
                index_col=0,
            ).replace(np.nan, 0.0)
            ks_filtered = _get_ks_filtered(
                listtoplot, active_info, misfit_info, joint_ks, self.ks_filter
            )

            ks_filtered = ks_filtered.sort_values(by="Ks_value", ascending=False)

            return dash_table.DataTable(
                columns=[{"name": i, "id": i} for i in ks_filtered.columns],
                editable=True,
                style_data_conditional=[
                    {
                        "if": {
                            "filter_query": "{Active Obs}=0",
                            "column_id": "Active Obs",
                        },
                        "backgroundColor": "grey",
                        "color": "white",
                    },
                ],
                data=ks_filtered.to_dict("records"),
                sort_action="native",
                filter_action="native",
                page_action="native",
                page_current=0,
                page_size=max_rows,
            )
                colormap_dropdown,
                html.Img(id="colormap-img", ),
            ],
        ),
        wcc.Frame(
            style={
                "flex": 10,
                "height": "90vh"
            },
            children=[map_obj],
        ),
    ])

    @callback(
        Output("colormap-img", "src"),
        Input("colormap-select", "value"),
    )
    def update_img(value):
        return value

    @callback(
        Output("deckgl-map", "layers"),
        Input("colormap-select", "value"),
        State("deckgl-map", "layers"),
    )
    def _update_layers(colormap, deckgl_layers):
        if not colormap:
            return None

        def apply_colormap(layers):
            # Update the colormap layer then return the updated layers.
def test_cblp001_radio_buttons_callbacks_generating_children(dash_duo):
    TIMEOUT = 2
    with open(os.path.join(os.path.dirname(__file__),
                           "state_path.json")) as fp:
        EXPECTED_PATHS = json.load(fp)

    app = Dash(__name__)
    app.layout = html.Div([
        dcc.RadioItems(
            options=[
                {
                    "label": "Chapter 1",
                    "value": "chapter1"
                },
                {
                    "label": "Chapter 2",
                    "value": "chapter2"
                },
                {
                    "label": "Chapter 3",
                    "value": "chapter3"
                },
                {
                    "label": "Chapter 4",
                    "value": "chapter4"
                },
                {
                    "label": "Chapter 5",
                    "value": "chapter5"
                },
            ],
            value="chapter1",
            id="toc",
        ),
        html.Div(id="body"),
    ])
    for script in dcc._js_dist:
        app.scripts.append_script(script)

    chapters = {
        "chapter1":
        html.Div([
            html.H1("Chapter 1", id="chapter1-header"),
            dcc.Dropdown(
                options=[{
                    "label": i,
                    "value": i
                } for i in ["NYC", "MTL", "SF"]],
                value="NYC",
                id="chapter1-controls",
            ),
            html.Label(id="chapter1-label"),
            dcc.Graph(id="chapter1-graph"),
        ]),
        # Chapter 2 has the some of the same components in the same order
        # as Chapter 1. This means that they won't get remounted
        # unless they set their own keys are differently.
        # Switching back and forth between 1 and 2 implicitly
        # tests how components update when they aren't remounted.
        "chapter2":
        html.Div([
            html.H1("Chapter 2", id="chapter2-header"),
            dcc.RadioItems(
                options=[{
                    "label": i,
                    "value": i
                } for i in ["USA", "Canada"]],
                value="USA",
                id="chapter2-controls",
            ),
            html.Label(id="chapter2-label"),
            dcc.Graph(id="chapter2-graph"),
        ]),
        # Chapter 3 has a different layout and so the components
        # should get rewritten
        "chapter3": [
            html.Div(
                html.Div([
                    html.H3("Chapter 3", id="chapter3-header"),
                    html.Label(id="chapter3-label"),
                    dcc.Graph(id="chapter3-graph"),
                    dcc.RadioItems(
                        options=[{
                            "label": i,
                            "value": i
                        } for i in ["Summer", "Winter"]],
                        value="Winter",
                        id="chapter3-controls",
                    ),
                ]))
        ],
        # Chapter 4 doesn't have an object to recursively traverse
        "chapter4":
        "Just a string",
    }

    call_counts = {
        "body": Value("i", 0),
        "chapter1-graph": Value("i", 0),
        "chapter1-label": Value("i", 0),
        "chapter2-graph": Value("i", 0),
        "chapter2-label": Value("i", 0),
        "chapter3-graph": Value("i", 0),
        "chapter3-label": Value("i", 0),
    }

    @app.callback(Output("body", "children"), [Input("toc", "value")])
    def display_chapter(toc_value):
        call_counts["body"].value += 1
        return chapters[toc_value]

    app.config.suppress_callback_exceptions = True

    def generate_graph_callback(counterId):
        def callback(value):
            call_counts[counterId].value += 1
            return {
                "data": [{
                    "x": ["Call Counter for: {}".format(counterId)],
                    "y": [call_counts[counterId].value],
                    "type": "bar",
                }],
                "layout": {
                    "title": value,
                    "width": 500,
                    "height": 400,
                    "margin": {
                        "autoexpand": False
                    },
                },
            }

        return callback

    def generate_label_callback(id_):
        def update_label(value):
            call_counts[id_].value += 1
            return value

        return update_label

    for chapter in ["chapter1", "chapter2", "chapter3"]:
        app.callback(
            Output("{}-graph".format(chapter), "figure"),
            [Input("{}-controls".format(chapter), "value")],
        )(generate_graph_callback("{}-graph".format(chapter)))

        app.callback(
            Output("{}-label".format(chapter), "children"),
            [Input("{}-controls".format(chapter), "value")],
        )(generate_label_callback("{}-label".format(chapter)))

    dash_duo.start_server(app)

    def check_chapter(chapter):
        dash_duo.wait_for_element(
            "#{}-graph:not(.dash-graph--pending)".format(chapter))

        for key in dash_duo.redux_state_paths["strs"]:
            assert dash_duo.find_elements(
                "#{}".format(key)), "each element should exist in the dom"

        value = (chapters[chapter][0]["{}-controls".format(chapter)].value
                 if chapter == "chapter3" else
                 chapters[chapter]["{}-controls".format(chapter)].value)

        # check the actual values
        dash_duo.wait_for_text_to_equal("#{}-label".format(chapter), value)

        wait.until(
            lambda: (dash_duo.driver.execute_script(
                'return document.querySelector("' +
                "#{}-graph:not(.dash-graph--pending) .js-plotly-plot".format(
                    chapter) + '").layout.title.text') == value),
            TIMEOUT,
        )

        assert not dash_duo.redux_state_is_loading, "loadingMap is empty"

    def check_call_counts(chapters, count):
        for chapter in chapters:
            assert call_counts[chapter + "-graph"].value == count
            assert call_counts[chapter + "-label"].value == count

    wait.until(lambda: call_counts["body"].value == 1, TIMEOUT)
    wait.until(lambda: call_counts["chapter1-graph"].value == 1, TIMEOUT)
    wait.until(lambda: call_counts["chapter1-label"].value == 1, TIMEOUT)
    check_call_counts(("chapter2", "chapter3"), 0)

    assert dash_duo.redux_state_paths == EXPECTED_PATHS["chapter1"]
    check_chapter("chapter1")
    dash_duo.percy_snapshot(name="chapter-1")

    dash_duo.find_elements('input[type="radio"]')[1].click()  # switch chapters

    wait.until(lambda: call_counts["body"].value == 2, TIMEOUT)
    wait.until(lambda: call_counts["chapter2-graph"].value == 1, TIMEOUT)
    wait.until(lambda: call_counts["chapter2-label"].value == 1, TIMEOUT)
    check_call_counts(("chapter1", ), 1)

    assert dash_duo.redux_state_paths == EXPECTED_PATHS["chapter2"]
    check_chapter("chapter2")
    dash_duo.percy_snapshot(name="chapter-2")

    # switch to 3
    dash_duo.find_elements('input[type="radio"]')[2].click()

    wait.until(lambda: call_counts["body"].value == 3, TIMEOUT)
    wait.until(lambda: call_counts["chapter3-graph"].value == 1, TIMEOUT)
    wait.until(lambda: call_counts["chapter3-label"].value == 1, TIMEOUT)
    check_call_counts(("chapter2", "chapter1"), 1)

    assert dash_duo.redux_state_paths == EXPECTED_PATHS["chapter3"]
    check_chapter("chapter3")
    dash_duo.percy_snapshot(name="chapter-3")

    dash_duo.find_elements('input[type="radio"]')[3].click()  # switch to 4
    dash_duo.wait_for_text_to_equal("#body", "Just a string")
    dash_duo.percy_snapshot(name="chapter-4")

    paths = dash_duo.redux_state_paths
    assert paths["objs"] == {}
    for key in paths["strs"]:
        assert dash_duo.find_elements(
            "#{}".format(key)), "each element should exist in the dom"

    assert paths["strs"] == {
        "toc": ["props", "children", 0],
        "body": ["props", "children", 1],
    }

    dash_duo.find_elements('input[type="radio"]')[0].click()

    wait.until(
        lambda: dash_duo.redux_state_paths == EXPECTED_PATHS["chapter1"],
        TIMEOUT)
    check_chapter("chapter1")
    dash_duo.percy_snapshot(name="chapter-1-again")
示例#18
0
import dash_bootstrap_components as dbc
from dash import Input, Output, State, html

offcanvas = html.Div([
    dbc.Button("Open Offcanvas", id="open-offcanvas", n_clicks=0),
    dbc.Offcanvas(
        html.P("This is the content of the Offcanvas. "
               "Close it by clicking on the close button, or "
               "the backdrop."),
        id="offcanvas",
        title="Title",
        is_open=False,
    ),
])


@app.callback(
    Output("offcanvas", "is_open"),
    Input("open-offcanvas", "n_clicks"),
    [State("offcanvas", "is_open")],
)
def toggle_offcanvas(n1, is_open):
    if n1:
        return not is_open
    return is_open
示例#19
0
def update_realizations(app: Dash, get_uuid: Callable) -> None:
    @app.callback(
        Output(
            {
                "id": get_uuid("dialog"),
                "dialog_id": "realization-filter",
                "element": "apply",
            },
            "disabled",
        ),
        Output(
            {
                "id": get_uuid("dialog"),
                "dialog_id": "realization-filter",
                "element": "apply",
            },
            "style",
        ),
        Input(
            get_uuid("realization-store"),
            "data",
        ),
        Input(
            {"id": get_uuid("intersection-data"), "element": "realizations"},
            "value",
        ),
    )
    def _activate_realization_apply_btn(
        stored_reals: List, selected_reals: List
    ) -> Tuple[bool, Dict[str, str]]:
        if stored_reals is None or selected_reals is None:
            raise PreventUpdate
        if set(stored_reals) == set(selected_reals):
            return True, {"visibility": "hidden"}
        return False, {"visibility": "visible"}

    @app.callback(
        Output(
            get_uuid("realization-store"),
            "data",
        ),
        Input(
            {
                "id": get_uuid("dialog"),
                "dialog_id": "realization-filter",
                "element": "apply",
            },
            "n_clicks",
        ),
        State(
            {"id": get_uuid("intersection-data"), "element": "realizations"},
            "value",
        ),
    )
    def _store_realizations(btn_click: Optional[int], selected_reals: List) -> List:
        if btn_click:
            return selected_reals
        raise PreventUpdate

    @app.callback(
        Output(
            {"id": get_uuid("intersection-data"), "element": "realizations"},
            "value",
        ),
        Input(
            {
                "id": get_uuid("dialog"),
                "dialog_id": "realization-filter",
                "element": "clear",
            },
            "n_clicks",
        ),
        Input(
            {
                "id": get_uuid("dialog"),
                "dialog_id": "realization-filter",
                "element": "all",
            },
            "n_clicks",
        ),
        State(
            {"id": get_uuid("intersection-data"), "element": "realizations"},
            "options",
        ),
    )
    def _update_realization_list(
        clear_click: Optional[int], all_click: Optional[int], real_opts: List
    ) -> List:
        if clear_click is None and all_click is None:
            raise PreventUpdate
        ctx = callback_context.triggered
        if "clear" in ctx[0]["prop_id"]:
            return []
        if "all" in ctx[0]["prop_id"]:
            return [opt["value"] for opt in real_opts]
        raise PreventUpdate
示例#20
0
    def callbacks(self, app):
        @app.callback(
            Output("control-panel", "data"),  # Store
            Output("graph-passed", "figure"),
            Output("graph-duration", "figure"),
            Output("card-url", "children"),
            Output("ri-ttypes", "options"),
            Output("ri-cadences", "options"),
            Output("dd-tbeds", "options"),
            Output("ri-duts", "value"),
            Output("ri-ttypes", "value"),
            Output("ri-cadences", "value"),
            Output("dd-tbeds", "value"),
            Output("al-job", "children"),
            State("control-panel", "data"),  # Store
            Input("ri-duts", "value"),
            Input("ri-ttypes", "value"),
            Input("ri-cadences", "value"),
            Input("dd-tbeds", "value"),
            Input("dpr-period", "start_date"),
            Input("dpr-period", "end_date"),
            Input("url", "href")
            # prevent_initial_call=True
        )
        def _update_ctrl_panel(cp_data: dict, dut: str, ttype: str,
                               cadence: str, tbed: str, start: str, end: str,
                               href: str) -> tuple:
            """
            """

            ctrl_panel = self.ControlPanel(cp_data, self.default)

            start = self._get_date(start)
            end = self._get_date(end)

            # Parse the url:
            parsed_url = url_decode(href)
            if parsed_url:
                url_params = parsed_url["params"]
            else:
                url_params = None

            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
            if trigger_id == "ri-duts":
                ttype_opts = self._generate_options(self._get_ttypes(dut))
                ttype_val = ttype_opts[0]["value"]
                cad_opts = self._generate_options(
                    self._get_cadences(dut, ttype_val))
                cad_val = cad_opts[0]["value"]
                tbed_opts = self._generate_options(
                    self._get_test_beds(dut, ttype_val, cad_val))
                tbed_val = tbed_opts[0]["value"]
                ctrl_panel.set({
                    "ri-duts-value": dut,
                    "ri-ttypes-options": ttype_opts,
                    "ri-ttypes-value": ttype_val,
                    "ri-cadences-options": cad_opts,
                    "ri-cadences-value": cad_val,
                    "dd-tbeds-options": tbed_opts,
                    "dd-tbeds-value": tbed_val
                })
            elif trigger_id == "ri-ttypes":
                cad_opts = self._generate_options(
                    self._get_cadences(ctrl_panel.get("ri-duts-value"), ttype))
                cad_val = cad_opts[0]["value"]
                tbed_opts = self._generate_options(
                    self._get_test_beds(ctrl_panel.get("ri-duts-value"), ttype,
                                        cad_val))
                tbed_val = tbed_opts[0]["value"]
                ctrl_panel.set({
                    "ri-ttypes-value": ttype,
                    "ri-cadences-options": cad_opts,
                    "ri-cadences-value": cad_val,
                    "dd-tbeds-options": tbed_opts,
                    "dd-tbeds-value": tbed_val
                })
            elif trigger_id == "ri-cadences":
                tbed_opts = self._generate_options(
                    self._get_test_beds(ctrl_panel.get("ri-duts-value"),
                                        ctrl_panel.get("ri-ttypes-value"),
                                        cadence))
                tbed_val = tbed_opts[0]["value"]
                ctrl_panel.set({
                    "ri-cadences-value": cadence,
                    "dd-tbeds-options": tbed_opts,
                    "dd-tbeds-value": tbed_val
                })
            elif trigger_id == "dd-tbeds":
                ctrl_panel.set({"dd-tbeds-value": tbed})
            elif trigger_id == "dpr-period":
                pass
            elif trigger_id == "url":
                # TODO: Add verification
                if url_params:
                    new_job = url_params.get("job", list())[0]
                    new_start = url_params.get("start", list())[0]
                    new_end = url_params.get("end", list())[0]
                    if new_job and new_start and new_end:
                        start = self._get_date(new_start)
                        end = self._get_date(new_end)
                        job_params = self._set_job_params(new_job)
                        ctrl_panel = self.ControlPanel(None, job_params)
                else:
                    ctrl_panel = self.ControlPanel(cp_data, self.default)
                    job = self._get_job(ctrl_panel.get("ri-duts-value"),
                                        ctrl_panel.get("ri-ttypes-value"),
                                        ctrl_panel.get("ri-cadences-value"),
                                        ctrl_panel.get("dd-tbeds-value"))

            job = self._get_job(ctrl_panel.get("ri-duts-value"),
                                ctrl_panel.get("ri-ttypes-value"),
                                ctrl_panel.get("ri-cadences-value"),
                                ctrl_panel.get("dd-tbeds-value"))

            ctrl_panel.set({"al-job-children": job})
            fig_passed, fig_duration = graph_statistics(
                self.data, job, self.layout, start, end)

            if parsed_url:
                new_url = url_encode({
                    "scheme": parsed_url["scheme"],
                    "netloc": parsed_url["netloc"],
                    "path": parsed_url["path"],
                    "params": {
                        "job": job,
                        "start": start,
                        "end": end
                    }
                })
            else:
                new_url = str()

            ret_val = [
                ctrl_panel.panel,
                fig_passed,
                fig_duration,
                [  # URL
                    dcc.Clipboard(target_id="card-url",
                                  title="Copy URL",
                                  style={"display": "inline-block"}), new_url
                ]
            ]
            ret_val.extend(ctrl_panel.values())
            return ret_val

        @app.callback(
            Output("download-data", "data"),
            State("control-panel", "data"),  # Store
            State("dpr-period", "start_date"),
            State("dpr-period", "end_date"),
            Input("btn-download-data", "n_clicks"),
            prevent_initial_call=True)
        def _download_data(cp_data: dict, start: str, end: str, n_clicks: int):
            """
            """
            if not (n_clicks):
                raise PreventUpdate

            ctrl_panel = self.ControlPanel(cp_data, self.default)

            job = self._get_job(ctrl_panel.get("ri-duts-value"),
                                ctrl_panel.get("ri-ttypes-value"),
                                ctrl_panel.get("ri-cadences-value"),
                                ctrl_panel.get("dd-tbeds-value"))

            start = datetime(int(start[0:4]), int(start[5:7]),
                             int(start[8:10]))
            end = datetime(int(end[0:4]), int(end[5:7]), int(end[8:10]))
            data = select_data(self.data, job, start, end)
            data = data.drop(columns=[
                "job",
            ])

            return dcc.send_data_frame(data.T.to_csv, f"{job}-stats.csv")

        @app.callback(Output("row-metadata", "children"),
                      Output("offcanvas-metadata", "is_open"),
                      Input("graph-passed", "clickData"),
                      Input("graph-duration", "clickData"),
                      prevent_initial_call=True)
        def _show_metadata_from_graphs(passed_data: dict,
                                       duration_data: dict) -> tuple:
            """
            """

            if not (passed_data or duration_data):
                raise PreventUpdate

            metadata = no_update
            open_canvas = False
            title = "Job Statistics"
            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
            if trigger_id == "graph-passed":
                graph_data = passed_data["points"][0].get("hovertext", "")
            elif trigger_id == "graph-duration":
                graph_data = duration_data["points"][0].get("text", "")
            if graph_data:
                metadata = [
                    dbc.Card(
                        class_name="gy-2 p-0",
                        children=[
                            dbc.CardHeader(children=[
                                dcc.Clipboard(
                                    target_id="metadata",
                                    title="Copy",
                                    style={"display": "inline-block"}), title
                            ]),
                            dbc.CardBody(
                                id="metadata",
                                class_name="p-0",
                                children=[
                                    dbc.ListGroup(children=[
                                        dbc.ListGroupItem([
                                            dbc.Badge(x.split(":")[0]),
                                            x.split(": ")[1]
                                        ]) for x in graph_data.split("<br>")
                                    ],
                                                  flush=True),
                                ])
                        ])
                ]
                open_canvas = True

            return metadata, open_canvas
示例#21
0
    dbc.Row([
        dbc.Col([html.Label('CandleStick Chart')], width=dict(size=4, offset=2)),
        dbc.Col([html.Label('OHLC Chart')], width=dict(size=4, offset=2))
    ]),

    dbc.Row([
        dbc.Col([dcc.Graph(id='candle', figure={}, style={'height': '80vh'})], width=6),
        dbc.Col([dcc.Graph(id='ohlc', figure={}, style={'height': '80vh'})], width=6)
    ]),
], fluid=True)


@callback(
    Output(component_id='candle', component_property='figure'),
    Output(component_id='ohlc', component_property='figure'),
    Input(component_id='oil-volume', component_property='value')
)
def build_graphs(chosen_volume):  # represents that which is assigned to the component property of the Input
    dff = df[df.Volume > chosen_volume]
    print(dff.head())

    fig_candle = go.Figure(
        go.Candlestick(x=dff['Date'],
                       open=dff['Open'],
                       high=dff['High'],
                       low=dff['Low'],
                       close=dff['Close'],
                       text=dff['Volume'])
    )
    fig_candle.update_layout(margin=dict(t=30, b=30))  # xaxis_rangeslider_visible=False,
示例#22
0
文件: page1.py 项目: plotly/dash
import dash
from dash import html, Input, Output, callback

dash.register_page(__name__, id="page1")

layout = html.Div([
    html.Div("text for page1", id="text_page1"),
    html.Button("goto page2", id="btn1", n_clicks=0),
])


@callback(Output("url", "pathname"), Input("btn1", "n_clicks"))
def update(n):
    if n > 0:
        return "/page2"
    return dash.no_update
示例#23
0
def test_grva009_originals_maintained_for_responsive_override(
        mutate_fig, dash_dcc):
    # In #905 we made changes to prevent shapes from being lost.
    # This test makes sure that the overrides applied by the `responsive`
    # prop are "undone" when the `responsive` prop changes.

    app = Dash(__name__)

    graph = dcc.Graph(
        id="graph",
        figure={
            "data": [{
                "y": [1, 2]
            }],
            "layout": {
                "width": 300,
                "height": 250
            }
        },
        style={
            "height": "400px",
            "width": "500px"
        },
    )
    responsive_size = [500, 400]
    fixed_size = [300, 250]

    app.layout = html.Div([
        graph,
        html.Br(),
        html.Button(id="edit_figure", children="Edit figure"),
        html.Button(id="edit_responsive", children="Edit responsive"),
        html.Div(id="output", children=""),
    ])

    if mutate_fig:
        # Modify the layout in place (which still has changes made by responsive)
        change_fig = """
            figure.layout.title = {text: String(n_fig || 0)};
            const new_figure = {...figure};
        """
    else:
        # Or create a new one each time
        change_fig = """
            const new_figure = {
                data: [{y: [1, 2]}],
                layout: {width: 300, height: 250, title: {text: String(n_fig || 0)}}
            };
        """

    callback = ("""
        function clone_figure(n_fig, n_resp, figure) {
        """ + change_fig + """
            let responsive = [true, false, 'auto'][(n_resp || 0) % 3];
            return [new_figure, responsive, (n_fig || 0) + ' ' + responsive];
        }
        """)

    app.clientside_callback(
        callback,
        Output("graph", "figure"),
        Output("graph", "responsive"),
        Output("output", "children"),
        Input("edit_figure", "n_clicks"),
        Input("edit_responsive", "n_clicks"),
        State("graph", "figure"),
    )

    dash_dcc.start_server(app)
    edit_figure = dash_dcc.wait_for_element("#edit_figure")
    edit_responsive = dash_dcc.wait_for_element("#edit_responsive")

    def graph_dims():
        return dash_dcc.driver.execute_script("""
            const layout = document.querySelector('.js-plotly-plot')._fullLayout;
            return [layout.width, layout.height];
            """)

    dash_dcc.wait_for_text_to_equal("#output", "0 true")
    dash_dcc.wait_for_text_to_equal(".gtitle", "0")
    assert graph_dims() == responsive_size

    edit_figure.click()
    dash_dcc.wait_for_text_to_equal("#output", "1 true")
    dash_dcc.wait_for_text_to_equal(".gtitle", "1")
    assert graph_dims() == responsive_size

    edit_responsive.click()
    dash_dcc.wait_for_text_to_equal("#output", "1 false")
    dash_dcc.wait_for_text_to_equal(".gtitle", "1")
    assert graph_dims() == fixed_size

    edit_figure.click()
    dash_dcc.wait_for_text_to_equal("#output", "2 false")
    dash_dcc.wait_for_text_to_equal(".gtitle", "2")
    assert graph_dims() == fixed_size

    edit_responsive.click()
    dash_dcc.wait_for_text_to_equal("#output", "2 auto")
    dash_dcc.wait_for_text_to_equal(".gtitle", "2")
    assert graph_dims() == fixed_size

    edit_figure.click()
    dash_dcc.wait_for_text_to_equal("#output", "3 auto")
    dash_dcc.wait_for_text_to_equal(".gtitle", "3")
    assert graph_dims() == fixed_size

    edit_responsive.click()
    dash_dcc.wait_for_text_to_equal("#output", "3 true")
    dash_dcc.wait_for_text_to_equal(".gtitle", "3")
    assert graph_dims() == responsive_size

    assert dash_dcc.get_logs() == []