Пример #1
0
def test_cbmt012_initialization_with_overlapping_outputs(generate, dash_duo):
    app = Dash(__name__, suppress_callback_exceptions=generate)
    block = html.Div(
        [
            html.Div(id="input-1", children="input-1"),
            html.Div(id="input-2", children="input-2"),
            html.Div(id="input-3", children="input-3"),
            html.Div(id="input-4", children="input-4"),
            html.Div(id="input-5", children="input-5"),
            html.Div(id="output-1"),
            html.Div(id="output-2"),
            html.Div(id="output-3"),
            html.Div(id="output-4"),
        ]
    )

    call_counts = {
        "container": Value("i", 0),
        "output-1": Value("i", 0),
        "output-2": Value("i", 0),
        "output-3": Value("i", 0),
        "output-4": Value("i", 0),
    }

    if generate:
        app.layout = html.Div([html.Div(id="input"), html.Div(id="container")])

        @app.callback(Output("container", "children"), Input("input", "children"))
        def set_content(_):
            call_counts["container"].value += 1
            return block

    else:
        app.layout = block

    def generate_callback(outputid):
        def callback(*args):
            call_counts[outputid].value += 1
            return "{}, {}".format(*args)

        return callback

    for i in range(1, 5):
        outputid = "output-{}".format(i)
        app.callback(
            Output(outputid, "children"),
            Input("input-{}".format(i), "children"),
            Input("input-{}".format(i + 1), "children"),
        )(generate_callback(outputid))

    dash_duo.start_server(app)

    for i in range(1, 5):
        outputid = "output-{}".format(i)
        dash_duo.wait_for_text_to_equal(
            "#{}".format(outputid), "input-{}, input-{}".format(i, i + 1)
        )
        assert call_counts[outputid].value == 1

    assert call_counts["container"].value == (1 if generate else 0)
Пример #2
0
    def test_generate_overlapping_outputs(self):
        app = Dash()
        app.config["suppress_callback_exceptions"] = True
        block = html.Div([
            html.Div(id="input-1", children="input-1"),
            html.Div(id="input-2", children="input-2"),
            html.Div(id="input-3", children="input-3"),
            html.Div(id="input-4", children="input-4"),
            html.Div(id="input-5", children="input-5"),
            html.Div(id="output-1"),
            html.Div(id="output-2"),
            html.Div(id="output-3"),
            html.Div(id="output-4"),
        ])
        app.layout = html.Div([html.Div(id="input"), html.Div(id="container")])

        call_counts = {
            "container": Value("i", 0),
            "output-1": Value("i", 0),
            "output-2": Value("i", 0),
            "output-3": Value("i", 0),
            "output-4": Value("i", 0),
        }

        @app.callback(Output("container", "children"),
                      [Input("input", "children")])
        def display_output(*args):
            call_counts["container"].value += 1
            return block

        def generate_callback(outputid):
            def callback(*args):
                call_counts[outputid].value += 1
                return "{}, {}".format(*args)

            return callback

        for i in range(1, 5):
            outputid = "output-{}".format(i)
            app.callback(
                Output(outputid, "children"),
                [
                    Input("input-{}".format(i), "children"),
                    Input("input-{}".format(i + 1), "children"),
                ],
            )(generate_callback(outputid))

        self.startServer(app)

        wait_for(lambda: call_counts["container"].value == 1)
        self.wait_for_element_by_css_selector("#output-1")
        time.sleep(5)

        for i in range(1, 5):
            outputid = "output-{}".format(i)
            self.assertEqual(call_counts[outputid].value, 1)
            self.wait_for_text_to_equal("#{}".format(outputid),
                                        "input-{}, input-{}".format(i, i + 1))
        self.assertEqual(call_counts["container"].value, 1)
Пример #3
0
def test_module_component_prefix():
    app = Dash(__name__)
    div = html.H2('Hello World', id='hello')

    app.callback(div.output.children, div.input.n_clicks)

    def _callback(clicks):
        return None

    assert div.id == 'tests-spa-spa_prefix_test-hello'
Пример #4
0
def add_routing_callback(dash: Dash) -> None:
    """Add routing callback"""
    dash.callback(
        Output("row-main", "children"),
        [Input("url", "pathname")]
    )(create_page)

    dash.callback(
        Output("url", "pathname"),
        [Input("btn-next", "n_clicks"),
         Input("btn-prev", "n_clicks")],
        [State("url", "pathname")]
    )(update_url)
Пример #5
0
 def add_callback(self,
                  app: dash.Dash,
                  func: Callable,
                  output: Output,
                  inputs: Sequence[Input],
                  state: Sequence[State] = tuple(),
                  prevent_initial_call: Optional[bool] = None) -> None:
     if output.component_id in self._callback_output_ids:
         return
     self._callback_output_ids.append(output.component_id)
     app.callback(output,
                  inputs,
                  state,
                  prevent_initial_call=prevent_initial_call)(func)
Пример #6
0
    def test_initialization_with_overlapping_outputs(self):
        app = Dash()
        app.layout = html.Div(
            [
                html.Div(id="input-1", children="input-1"),
                html.Div(id="input-2", children="input-2"),
                html.Div(id="input-3", children="input-3"),
                html.Div(id="input-4", children="input-4"),
                html.Div(id="input-5", children="input-5"),
                html.Div(id="output-1"),
                html.Div(id="output-2"),
                html.Div(id="output-3"),
                html.Div(id="output-4"),
            ]
        )
        call_counts = {
            "output-1": Value("i", 0),
            "output-2": Value("i", 0),
            "output-3": Value("i", 0),
            "output-4": Value("i", 0),
        }

        def generate_callback(outputid):
            def callback(*args):
                call_counts[outputid].value += 1
                return "{}, {}".format(*args)

            return callback

        for i in range(1, 5):
            outputid = "output-{}".format(i)
            app.callback(
                Output(outputid, "children"),
                [
                    Input("input-{}".format(i), "children"),
                    Input("input-{}".format(i + 1), "children"),
                ],
            )(generate_callback(outputid))

        self.startServer(app)

        self.wait_for_element_by_css_selector("#output-1")
        time.sleep(5)

        for i in range(1, 5):
            outputid = "output-{}".format(i)
            self.assertEqual(call_counts[outputid].value, 1)
            self.wait_for_text_to_equal(
                "#{}".format(outputid), "input-{}, input-{}".format(i, i + 1)
            )
Пример #7
0
    def apply(cls, app: Dash) -> None:

        for cb in cls.REGISTRY:
            try:
                if isinstance(cb, deferred_callback):
                    LOG.info(f"Applying deferred callback {cb.f.__name__}")
                    app.callback(*cb.args, **cb.kwargs)(cb.f)
                else:
                    LOG.info(
                        f"Applying deferred clientside callback {cb.name}")
                    app.clientside_callback(*cb.args, **cb.kwargs)
            except Exception as e:
                LOG.error(f"Callback {type(cb)=}, {cb=}, "
                          f"{getattr(cb, 'name', None)=}"
                          f"{getattr(cb, 'f', None)=}"
                          f"failed")
                LOG.error(e)
                continue
Пример #8
0
def register_callbacks(dash: Dash, PageClass: Type[Page],
                       method: Callable[..., Any]):
    outputs, inputs, states = method.callback_parameters  # type: ignore
    n_outputs = len(outputs)
    method_name = method.__name__
    registration_hooks: Iterable[RegistrationHookType] = getattr(
        method, "registration_hooks", None) or []
    for hook in registration_hooks:
        hook(PageClass, method_name, outputs, inputs, states)
    if isinstance(inspect.getattr_static(PageClass, method_name),
                  staticmethod):
        # Callback is staticmethod.
        callback = make_static_callback(PageClass, method, method_name,
                                        n_outputs)
    elif isinstance(inspect.getattr_static(PageClass, method_name),
                    classmethod):
        # Callback is classmethod.
        callback = make_class_callback(PageClass, method, method_name,
                                       n_outputs)
    elif not method.is_mutating:  # type: ignore
        # Callback is non-mutating instance method.
        callback = make_non_mutating_callback(PageClass, method, method_name,
                                              n_outputs)
        # Add session store to states.
        states.append(State(PageClass._store_name, "data"))
    else:
        # Callback is mutating instance method.
        callback = make_mutating_callback(PageClass, method, method_name,
                                          n_outputs)
        # Collect callback store and add to outputs.
        callback_store_name = PageClass._store_name + f"-callback-{method_name}"
        PageClass._callback_stores.append(callback_store_name)
        outputs.append(Output(callback_store_name, "data"))
        # Add session store to states.
        states.append(State(PageClass._store_name, "data"))

    # Collect callback's error div and add to the output.
    error_div_name = f"page-{PageClass.route}-error-div-{method_name}"
    PageClass._callback_error_divs.append(error_div_name)
    outputs.append(Output(error_div_name, "children"))

    # Register callback.
    dash.callback(outputs, inputs, states)(callback)
Пример #9
0
class Dashboard:
    """This is abstract dashboard method
    Every nested class should implement following abstract methods:
        - _set_layout: to set dash.app.layout property
    # TODO: expand the class with default layout
    # TODO: expand the class with default styling
    """

    def __init__(self, mode='default', **kwargs):
        external_stylesheets = [BOOTSTRAP]
        if mode == 'jupyter':
            self.app = JupyterDash(external_stylesheets=external_stylesheets)
        else:
            self.app = Dash(external_stylesheets=external_stylesheets)

        app = self.app  # For referencing with the decorator (see line below)
        app.title = 'CEHS Uganda'

        @app.server.route('/static/<asset_type>/<path>')
        def static_file(asset_type, path):
            return send_from_directory(here / 'static' / asset_type, path)

    ################
    #    LAYOUT    #
    ################

    def _set_layout(self):
        """Method is left deliberately empty. Every child class should implement this class"""
        raise NotImplementedError(
            'Every child class should implement __set_layout method!')

    ###################
    #    EXECUTION    #
    ###################

    def run(self, dev=False, **kwargs):
        self.set_layout_and_callbacks()
        self.app.run_server(debug=dev, use_reloader=dev, **kwargs)

    def set_layout_and_callbacks(self):
        self._set_layout()
        self._define_callbacks()

    def switch_data_set(self, data):
        for x in self.data_cards:
            if isinstance(x, DataCard) or getattr(x, 'data') is not None:
                try:
                    x.data = data
                    # x.figure = x._get_figure(x.data)
                except AttributeError as e:
                    print(e)

    ###################
    #    CALLBACKS    #
    ###################

    def _define_callbacks(self):
        # TODO: self.data_cards is property of datastory... Move this to datastory or data_cards to dashboard?
        # Datacard level
        for x in self.data_cards:
            if x._requires_dropdown():
                for callback in x.callbacks:
                    self.register_callback(
                        callback.get('input'),
                        callback.get('output'),
                        callback.get('func'))
        for x in self.ind_elements:  # FIXME
            if x._requires_dropdown():
                for callback in x.callbacks:
                    self.register_callback(
                        callback.get('input'),
                        callback.get('output'),
                        callback.get('func'))

    def register_callback(self,
                          input_element_params,
                          output_elements_params,
                          function):
        out_set, in_set = self.__define_callback_set(
            output_elements_params, input_element_params)

        callback_function = self.__process_callback_function(function)

        self.app.callback(inputs=in_set, output=out_set)(callback_function)

    def __process_callback_function(self, function):

        def callback_wrapper(*input_values):
            value = function(*input_values)
            return value

        return callback_wrapper

    def __define_callback_set(self,
                              output_elements_id_prop: [(str, str)],
                              input_element_id_prop: [(str, str)]
                              ):

        callback_set_outputs = [
            Output(component_id=component_id,
                   component_property=component_prop)
            for component_id, component_prop
            in output_elements_id_prop
        ]

        callback_set_input = [
            Input(component_id=component_id,
                  component_property=component_prop)
            for component_id, component_prop
            in input_element_id_prop]

        return (callback_set_outputs, callback_set_input)
APP.layout = html.Div([
    navbar,
    dcc.Location(id='url', refresh=False),
    html.Div(className="container main-container", children=[])


    authorized_emails=[]
    auth=GoogleOAuth(
        APP,
        authorized_emails,
    )


    @APP.callback(
        Output('username', 'children'),
        [Input('placeholder', 'value')]
    )
    def on_load(value):
        return "{},".format(session['email'])




    @APP.callback(dash.dependencies.Output('page-content', 'children'),
                  [dash.dependencies.Input('url', 'pathname')])
    def display_page(pathname):
        if pathname == '/':
            return wp.generate_page_1_layout()
        elif pathname == '/logout':

            resp=google.post(
Пример #11
0
    dcc.Input(id='input-c', type="text"),
    html.Div(
        [
            'Timestamps:', html.Br(),
            'A: ', html.Span(id='input-a-timestamp'), html.Br(),
            'B: ', html.Span(id='input-b-timestamp'), html.Br(),
            'C: ', html.Span(id='input-c-timestamp'), html.Br(),
        ],
        style={'display': 'block'}  # Switch this on or off for debugging.
    ),
    html.Span(['and the latest value is: ']),
    html.Span(id='latest-value')
])

app.callback(
    Output('input-a-timestamp', 'children'), [Input('input-a', 'value')]
)(update_timestamp)
app.callback(
    Output('input-b-timestamp', 'children'), [Input('input-b', 'value')]
)(update_timestamp)
app.callback(
    Output('input-c-timestamp', 'children'), [Input('input-c', 'value')]
)(update_timestamp)

app.callback(
    Output('latest-value', 'children'),
    [Input('input-a-timestamp', 'children'),
     Input('input-b-timestamp', 'children'),
     Input('input-c-timestamp', 'children')],
    [State('input-a', 'value'),
     State('input-b', 'value'),
Пример #12
0
def init_dash(flask_app: Flask, config: Config):
    logger = flask_app.logger
    dash = Dash(
        __name__,
        server=flask_app,
        routes_pathname_prefix="/",
        external_stylesheets=[
            #  dbc.themes.BOOTSTRAP,
            #  "https://use.fontawesome.com/releases/v5.8.1/css/all.css"
        ],
        meta_tags=[
            {
                "charset": "utf-8"
            },
            {
                "name": "viewport",
                "content":
                "width=device-width, initial-scale=1, shrink-to-fit=no",
            },
        ],
    )
    dash.config["suppress_callback_exceptions"] = True
    dash.title = config.project.display_name or config.project.name
    dash.layout = html.Div(children=[
        # Represents the URL bar, doesn't render anything.
        dcc.Location(id="url", refresh=False),
        dbc.NavbarSimple(
            id="navbar-content",
            brand="AllenNLP Manager",
            brand_href="/",
            sticky="top",
            color="#162328",
            dark=True,
        ),
        dcc.Store(id="current-path"),
        dbc.Container(id="page-content"),
        dbc.Container(
            id="notifications",
            style={
                "position": "fixed",
                "top": 66,
                "right": 10,
                "width": 350
            },
            children=[
                html.Div(id="page-loading-error"),
                html.Div(id="page-notifications"),
                html.Div(id="page-callback-errors"),
            ],
        ),
    ])

    # Import all dashboard pages so that they get registered.
    import_submodules("mallennlp.dashboard")
    for module in config.server.imports or []:
        logger.info("Importing additional module %s", module)
        import_submodules(module)

    additional_navlinks = []
    for page_name in Page.list_available():
        PageClass = Page.by_name(page_name)
        if PageClass.navlink_name is not None:
            additional_navlinks.append(
                dbc.DropdownMenuItem(
                    dcc.Link(PageClass.navlink_name, href=page_name)))

    Page.logger = logger

    # Define callback to render navbar.
    @dash.callback(Output("navbar-content", "children"),
                   [Input("url", "pathname")])
    def render_navbar(pathname):
        source_link = dbc.NavItem(
            dbc.NavLink(
                [html.I(className="fab fa-github"), " Source"],
                href="https://github.com/epwalsh/allennlp-manager",
            ))
        if current_user.is_authenticated:
            menu_items = [
                dbc.DropdownMenuItem(
                    ["Signed in as ",
                     html.Strong(current_user.username)],
                    disabled=True),
                html.Hr(),
                dbc.DropdownMenuItem(dcc.Link("Home", href="/")),
                dbc.DropdownMenuItem(dcc.Link("System info",
                                              href="/sys-info")),
            ]
            if additional_navlinks:
                menu_items.append(html.Hr())
                menu_items.extend(additional_navlinks)
            menu_items.extend([
                html.Hr(),
                dbc.DropdownMenuItem(dcc.Link("Settings", href="/settings")),
                dbc.DropdownMenuItem(
                    dcc.Link("Logout", href="/logout", refresh=True)),
            ])
            return [
                source_link,
                dbc.DropdownMenu(nav=True,
                                 in_navbar=True,
                                 label="Menu",
                                 children=menu_items),
            ]
        return [
            source_link,
            dbc.NavItem(
                dbc.NavLink("Sign in", href="/login", external_link=True)),
        ]

    # Define callback to render pages. Takes the URL path and get the corresponding
    # page.
    @dash.callback(
        [
            Output("page-content", "children"),
            Output("page-callback-errors", "children"),
            Output("page-notifications", "children"),
            Output("page-loading-error", "children"),
            Output("current-path", "data"),
        ],
        [Input("url", "pathname"),
         Input("url", "search")],
        [State("current-path", "data")],
    )
    def render_page(pathname: str, param_string: str, current_path_data):
        if pathname is None:
            raise PreventUpdate
        logger.debug("Attempting to render page %s", pathname)
        if pathname != "/" and pathname.endswith("/") or pathname.endswith(
                "#"):
            pathname = pathname[:-1]
        if current_path_data:
            # If nothing in the path / param_string has changed, don't actually do anything.
            # NOTE: this is kind of a hack, since sometimes we have buttons w/ href='#',
            # and we don't want to actually re-render the pages content when clicked.
            if (current_path_data["pathname"] == pathname
                    and current_path_data["param_string"] == param_string):
                raise PreventUpdate
        updated_data = {"pathname": pathname, "param_string": param_string}
        try:
            PageClass = Page.by_name(pathname)
            if PageClass.permissions:
                if not current_user.is_authenticated:
                    PageClass = Page.by_name("/login")
                    params = PageClass.Params(next_pathname=pathname,
                                              next_params=param_string)
                    return PageClass.from_params(params).render() + (
                        None, updated_data)
                if PageClass.permissions > current_user.permissions:
                    raise NotPermittedError(
                        "You do not have adequate permissions to view this page"
                    )

            params = from_url(PageClass.Params, param_string)
            return PageClass.from_params(params).render() + (None,
                                                             updated_data)
        except RegistrationError:
            return (
                [],
                [],
                [],
                dbc.Toast(f"Page {pathname} not found",
                          header="404",
                          icon="danger"),
                updated_data,
            )
        except InvalidPageParametersError as e:
            return (
                [],
                [],
                [],
                dbc.Toast(str(e), header="Bad page parameters", icon="danger"),
                updated_data,
            )
        except Exception as e:
            logger.exception(e)
            return (
                [],
                [],
                [],
                dbc.Toast(str(e), header=e.__class__.__name__, icon="danger"),
                updated_data,
            )

    # Now we loop through all registered pages and register their callbacks
    # with the dashboard application.
    for page_name in Page.list_available():
        PageClass = Page.by_name(page_name)
        PageClass.route = page_name
        if getattr(PageClass, "logger", None) is None:
            PageClass.logger = logger
        PageClass._store_name = f"page-{page_name}-store"
        PageClass._callback_stores = []
        PageClass._callback_error_divs = []
        for _, method in filter(lambda x: callable(x[1]),
                                inspect.getmembers(PageClass)):
            if not getattr(method, "is_callback", False):
                continue
            register_callbacks(dash, PageClass, method)

        if PageClass._callback_stores:
            dash.callback(
                Output(PageClass._store_name, "data"),
                [
                    Input(s, "modified_timestamp")
                    for s in PageClass._callback_stores
                ],
                [State(s, "data") for s in PageClass._callback_stores],
            )(store_callback)

    return dash
Пример #13
0
    def test_radio_buttons_callbacks_generating_children(self):
        self.maxDiff = 100 * 1000
        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',

            # Chapter 5 contains elements that are bound with events
            'chapter5': [
                html.Div([
                    html.Button(id='chapter5-button'),
                    html.Div(id='chapter5-output')
                ])
            ]
        }

        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),
            'chapter5-output': 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.supress_callback_exceptions = True

        def generate_graph_callback(counterId):
            def callback(value):
                call_counts[counterId].value += 1
                return {
                    'data': [{
                        'x': ['Call Counter'],
                        'y': [call_counts[counterId].value],
                        'type': 'bar'
                    }],
                    'layout': {
                        'title': value
                    }
                }

            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)))

        chapter5_output_children = 'Button clicked'

        @app.callback(Output('chapter5-output', 'children'),
                      events=[Event('chapter5-button', 'click')])
        def display_output():
            call_counts['chapter5-output'].value += 1
            return chapter5_output_children

        self.startServer(app)

        time.sleep(0.5)
        wait_for(lambda: call_counts['body'].value == 1)
        wait_for(lambda: call_counts['chapter1-graph'].value == 1)
        wait_for(lambda: call_counts['chapter1-label'].value == 1)
        self.assertEqual(call_counts['chapter2-graph'].value, 0)
        self.assertEqual(call_counts['chapter2-label'].value, 0)
        self.assertEqual(call_counts['chapter3-graph'].value, 0)
        self.assertEqual(call_counts['chapter3-label'].value, 0)

        def generic_chapter_assertions(chapter):
            # each element should exist in the dom
            paths = self.driver.execute_script(
                'return window.store.getState().paths')
            for key in paths:
                self.driver.find_element_by_id(key)

            if chapter == 'chapter3':
                value = chapters[chapter][0]['{}-controls'.format(
                    chapter)].value
            else:
                value = chapters[chapter]['{}-controls'.format(chapter)].value
            # check the actual values
            wait_for(lambda: (self.driver.find_element_by_id('{}-label'.format(
                chapter)).text == value))
            wait_for(lambda: (self.driver.execute_script(
                'return document.'
                'getElementById("{}-graph").'.format(chapter) + 'layout.title')
                              == value))

            self.assertEqual(
                self.driver.execute_script(
                    'return window.store.getState().requestQueue'), [])

        def chapter1_assertions():
            paths = self.driver.execute_script(
                'return window.store.getState().paths')
            self.assertEqual(
                paths, {
                    'toc': ['props', 'children', 0],
                    'body': ['props', 'children', 1],
                    'chapter1-header': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 0
                    ],
                    'chapter1-controls': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 1
                    ],
                    'chapter1-label': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 2
                    ],
                    'chapter1-graph': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 3
                    ]
                })
            generic_chapter_assertions('chapter1')

        chapter1_assertions()

        # switch chapters
        (self.driver.find_elements_by_css_selector('input[type="radio"]')[1]
         ).click()

        # sleep just to make sure that no calls happen after our check
        time.sleep(2)
        wait_for(lambda: call_counts['body'].value == 2)
        wait_for(lambda: call_counts['chapter2-graph'].value == 1)
        wait_for(lambda: call_counts['chapter2-label'].value == 1)
        self.assertEqual(call_counts['chapter1-graph'].value, 1)
        self.assertEqual(call_counts['chapter1-label'].value, 1)

        def chapter2_assertions():
            paths = self.driver.execute_script(
                'return window.store.getState().paths')
            self.assertEqual(
                paths, {
                    'toc': ['props', 'children', 0],
                    'body': ['props', 'children', 1],
                    'chapter2-header': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 0
                    ],
                    'chapter2-controls': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 1
                    ],
                    'chapter2-label': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 2
                    ],
                    'chapter2-graph': [
                        'props', 'children', 1, 'props', 'children', 'props',
                        'children', 3
                    ]
                })
            generic_chapter_assertions('chapter2')

        chapter2_assertions()

        # switch to 3
        (self.driver.find_elements_by_css_selector('input[type="radio"]')[2]
         ).click()
        # sleep just to make sure that no calls happen after our check
        time.sleep(2)
        wait_for(lambda: call_counts['body'].value == 3)
        wait_for(lambda: call_counts['chapter3-graph'].value == 1)
        wait_for(lambda: call_counts['chapter3-label'].value == 1)
        self.assertEqual(call_counts['chapter2-graph'].value, 1)
        self.assertEqual(call_counts['chapter2-label'].value, 1)
        self.assertEqual(call_counts['chapter1-graph'].value, 1)
        self.assertEqual(call_counts['chapter1-label'].value, 1)

        def chapter3_assertions():
            paths = self.driver.execute_script(
                'return window.store.getState().paths')
            self.assertEqual(
                paths, {
                    'toc': ['props', 'children', 0],
                    'body': ['props', 'children', 1],
                    'chapter3-header': [
                        'props', 'children', 1, 'props', 'children', 0,
                        'props', 'children', 'props', 'children', 0
                    ],
                    'chapter3-label': [
                        'props', 'children', 1, 'props', 'children', 0,
                        'props', 'children', 'props', 'children', 1
                    ],
                    'chapter3-graph': [
                        'props', 'children', 1, 'props', 'children', 0,
                        'props', 'children', 'props', 'children', 2
                    ],
                    'chapter3-controls': [
                        'props', 'children', 1, 'props', 'children', 0,
                        'props', 'children', 'props', 'children', 3
                    ]
                })
            generic_chapter_assertions('chapter3')

        chapter3_assertions()

        # switch to 4
        (self.driver.find_elements_by_css_selector('input[type="radio"]')[3]
         ).click()
        wait_for(lambda: (self.driver.find_element_by_id('body').text ==
                          'Just a string'))
        # each element should exist in the dom
        paths = self.driver.execute_script(
            'return window.store.getState().paths')
        for key in paths:
            self.driver.find_element_by_id(key)
        self.assertEqual(paths, {
            'toc': ['props', 'children', 0],
            'body': ['props', 'children', 1]
        })

        # switch back to 1
        (self.driver.find_elements_by_css_selector('input[type="radio"]')[0]
         ).click()
        time.sleep(0.5)
        chapter1_assertions()

        # switch to 5
        (self.driver.find_elements_by_css_selector('input[type="radio"]')[4]
         ).click()
        time.sleep(1)
        # click on the button and check the output div before and after
        chapter5_div = lambda: self.driver.find_element_by_id('chapter5-output'
                                                              )
        chapter5_button = lambda: self.driver.find_element_by_id(
            'chapter5-button')
        self.assertEqual(chapter5_div().text, '')
        chapter5_button().click()
        wait_for(lambda: chapter5_div().text == chapter5_output_children)
        time.sleep(0.5)
        self.assertEqual(call_counts['chapter5-output'].value, 1)
#http://localhost:5000/d/DisplayScreen@screen=ResultOverview&asset=Alperia-VSM
#http://localhost:5000/d/DisplayScreen@screen=test&asset=Alperia-VSM


def getScreenVariables(user):
    def getScreenVariablesForUser():
        return dictionaryOfAllScreenVariables[user]

    return getScreenVariablesForUser


interactionsDict = parse("001")
callbackFunctions = compile_callbacks("001", getScreenVariables("002"))

for interaction in interactionsDict:
    inputLst = []
    for input in interaction['input']:
        inputId = input['object'] + '-' + input['type'] + '-' + interaction[
            'screen']
        if input['type'] == 'CDatePicker':
            inputLst.append(Input('date-picker-range-' + inputId,
                                  'start_date'))
            inputLst.append(Input('date-picker-range-' + inputId, 'end_date'))
        else:
            inputLst.append(Input(inputId, 'value'))
    outputId = interaction['output']['object'] + '-' + interaction['output'][
        'type'] + '-' + interaction['screen']
    dash_app.callback(Output(
        outputId, interaction['output']['param']), inputLst)(
            callbackFunctions[interaction['screen']][interaction['callback']])
def add_slide2_callbacks(dash: Dash) -> None:
    """Add routing callback"""
    dash.callback(Output("slide2-plot", "children"),
                  [Input("url", "pathname")])(update_plot)
Пример #16
0
			elif state['type'] == 'CChecklist':
				stateLst.append(State(stateId, 'values'))
			elif state['type'] == 'CRadioItems':
				stateLst.append(State(stateId, 'value'))
			elif state['type'] == 'CButton':
				stateLst.append(State(stateId, 'n_clicks'))
			elif state['type'] == 'CUpload':
				stateLst.append(State(stateId, 'contents'))
				stateLst.append(State(stateId, 'filename'))
			elif state['type'] == 'CTabs':
				stateLst.append(State(stateId, 'value'))
			elif state['type'] == 'CDataTable':
				stateLst.append(State(stateId, 'selected_row_indices'))
			elif state['type'] == 'CChart':
				stateLst.append(State(stateId, 'figure'))
			elif state['type'] == 'CMap':
				stateLst.append(State(stateId, 'figure'))
			elif state['type'] == 'CTopologyMap':
				stateLst.append(State(stateId, 'clickData'))
			elif state['type'] == 'CInterval':
				state.append(State(stateId, 'n_intervals'))
			'''
    outputId = generateId(interaction['output']['object'],
                          interaction['output']['type'], interaction['screen'])
    dash_app.callback(
        Output(outputId, interaction['output']['param']),
        inputLst,
        stateLst,
    )(callbackFunctions[interaction['screen']][interaction['callback']]
      if interaction['callback'] in callbackFunctions[interaction['screen']]
      else globalCallbacks[interaction['callback']])
Пример #17
0
@app.callback(Output('stats-container', 'children'),
               [Input('replayUpload', 'contents'),
                Input('replayUpload', 'filename'),
                Input('replayUpload', 'last_modified')],
               [State('aliases', 'value')])

def update_output_div(list_of_contents, list_of_names, list_of_dates, aliases):
    # if(n_clicks > 0):
    if(list_of_names):
        replays = [load_replay(c, n, d) for c, n, d in zip(list_of_contents, list_of_names, list_of_dates)]
        (stats, rep_list) = get_statistics(replays, [aliases])
        return get_stats_layout(stats, rep_list)

app.callback(Output('full-game-list-content', 'style'),
            [Input('tabs', 'value')])(
create_full_game_list_tab_callback())
for race in list(ALL_RACES.keys()):
    app.callback(Output(f'{race}-content', 'style'),
              [Input('tabs', 'value')])(
    create_mainrace_tab_callback(race))
    app.callback(Output(f'{race}-content-total', 'style'),
              [Input(f'{race}-tabs', 'value')])(
    create_total_tab_callback(race))
    app.callback(Output(f'{race}-content-race', 'style'),
              [Input(f'{race}-tabs', 'value')])(
    create_race_tab_callback(race))
    app.callback(Output(f'{race}-content-map', 'style'),
              [Input(f'{race}-tabs', 'value')])(
    create_map_tab_callback(race))
    for enemy_race in list(ALL_RACES.keys()):
Пример #18
0
class DualDashGraph:
    """
    The DualDashGraph class is the inerface for comparing and highlighting the difference between two graphs.
    Two Graph class objects should be supplied - such as MST and ALMST graphs.
    """
    def __init__(self, graph_one, graph_two, app_display='default'):
        """
        Initialises the dual graph interface and generates the interface layout.

        :param graph_one: (Graph) The first graph for the comparison interface.
        :param graph_two: (Graph) The second graph for the comparison interface.
        :param app_display: (str) 'default' by default and 'jupyter notebook' for running Dash inside Jupyter Notebook.
        """

        # Dash app styling with Bootstrap
        if app_display == 'jupyter notebook':
            self.app = JupyterDash(__name__,
                                   external_stylesheets=[dbc.themes.BOOTSTRAP])
        else:
            self.app = Dash(__name__,
                            external_stylesheets=[dbc.themes.BOOTSTRAP])

        # Setting input graphs as objest variables
        cyto.load_extra_layouts()
        self.graph_one = graph_one
        self.graph_two = graph_two

        # Getting a list of tuples with differnet edge connections
        difference = graph_one.get_difference(graph_two)

        # Updating the elements needed for the Dash Cytoscape Graph object
        self.one_components = None
        self.two_components = None
        self._update_elements_dual(self.graph_one, difference, 1)
        self._update_elements_dual(self.graph_two, difference, 2)

        self.cyto_one = None
        self.cyto_two = None

        # Callback functions to allow simultaneous node selection when clicked
        self.app.callback(Output('cytoscape_two',
                                 'elements'), [Input('cytoscape', 'tapNode')],
                          [State('cytoscape_two', 'elements')])(
                              DualDashGraph._select_other_graph_node)
        self.app.callback(Output('cytoscape', 'elements'),
                          [Input('cytoscape_two', 'tapNode')],
                          [State('cytoscape', 'elements')])(
                              DualDashGraph._select_other_graph_node)

    @staticmethod
    def _select_other_graph_node(data, elements):
        """
        Callback function to select the other graph node when a graph node
        is selected by setting selected to True.

        :param data: (Dict) Dictionary of "tapped" or selected node.
        :param elements: (Dict) Dictionary of elements.
        :return: (Dict) Returns updates dictionary of elements.
        """
        if data:
            for element in elements:
                element['selected'] = (
                    data['data']['id'] == element.get('data').get('id'))

        return elements

    def _generate_comparison_layout(self, graph_one, graph_two):
        """
        Returns and generates a dual comparison layout.

        :param graph_one: (Graph) The first graph object for the dual interface.
        :param graph_two: (Graph) Comparison graph object for the dual interface.
        :return: (html.Div) Returns a Div containing the interface.
        """
        # Set Graph names
        graph_one_name = type(graph_one).__name__
        graph_two_name = type(graph_two).__name__

        # Set the cyto graphs
        self._set_cyto_graph()

        # Get different edges between two graphs
        difference = graph_one.get_difference(graph_two)

        # Layout components
        padding = {'padding': '10px 10px 10px 10px'}
        cards = dbc.CardDeck([
            dbc.Card([
                dbc.CardHeader(graph_one_name),
                dbc.CardBody(self.cyto_one),
            ], ),
            dbc.Card(
                [dbc.CardHeader(graph_two_name),
                 dbc.CardBody(self.cyto_two)], )
        ],
                             style=padding)
        summary = dbc.Card([
            html.H5("Summary", className="card-title"),
            html.P("{} nodes in each graph and {} different edge(s) per graph."
                   .format(graph_one.get_graph().number_of_nodes(),
                           int(len(difference) / 2)),
                   className="card-text")
        ],
                           className="w-50",
                           style={
                               'margin': '0 auto',
                               'padding': '10px 10px 10px 10px'
                           })
        layout = html.Div([
            dbc.Row(dbc.Col(cards, width=12, align='center')),
            summary,
        ],
                          style={'padding-bottom': '10px'})

        return layout

    @staticmethod
    def _get_default_stylesheet(weights):
        """
        Returns the default stylesheet for initialisation.

        :param weights: (List) A list of weights of the edges.
        :return: (List) A List of definitions used for Dash styling.
        """
        stylesheet = \
            [
                {
                    'selector': 'node',
                    'style': {
                        'label': 'data(label)',
                        'text-valign': 'center',
                        'background-color': '#4cc9f0',
                        'font-family': 'sans-serif',
                        'font-size': '12',
                        'font-weight': 'bold',
                        'border-width': 1.5,
                        'border-color': '#161615',
                    }
                },
                {
                    "selector": 'edge',
                    "style": {
                        'label': 'data(weight)',
                        "line-color": "#4cc9f0",
                        'font-size': '8',
                    }
                },
                {
                    "selector": '[weight => 0]',
                    "style": {
                        "width": "mapData(weight, 0, {}, 1, 8)".format(max(weights)),
                    }
                },
                {
                    "selector": '[weight < 0]',
                    "style": {
                        "width": "mapData(weight, 0, {}, 1, 8)".format(min(weights)),
                    }
                },
                {
                    "selector": '.central',
                    "style": {
                        "background-color": "#80b918"
                    }
                },
                {
                    'selector': ':selected',
                    "style": {
                        "border-width": 2,
                        'background-color': '#f72585',
                        "border-color": "black",
                        "border-opacity": 1,
                        "opacity": 1,
                        "label": "data(label)",
                        "color": "black",
                        "font-size": 12,
                        'z-index': 9999
                    }
                },
                {
                    "selector": '.different',
                    "style": {
                        "line-color": "#f72585",
                        }
                }
            ]
        return stylesheet

    def _set_cyto_graph(self):
        """
        Updates and sets the two cytoscape graphs using the corresponding components.
        """
        layout = {'name': 'cose-bilkent'}
        style = {
            'width': '100%',
            'height': '600px',
            'padding': '5px 3px 5px 3px'
        }
        self.cyto_one = cyto.Cytoscape(
            id="cytoscape",
            layout=layout,
            style=style,
            elements=self.one_components[1],
            stylesheet=DualDashGraph._get_default_stylesheet(
                self.one_components[0]))
        self.cyto_two = cyto.Cytoscape(
            id="cytoscape_two",
            layout=layout,
            style=style,
            elements=self.two_components[1],
            stylesheet=DualDashGraph._get_default_stylesheet(
                self.two_components[0]))

    def _update_elements_dual(self, graph, difference, graph_number):
        """
        Updates the elements needed for the Dash Cytoscape Graph object.

        :param graph: (Graph) Graph object such as MST or ALMST.
        :param difference: (List) List of edges where the two graphs differ.
        :param graph_number: (Int) Graph number to update the correct graph.
        """
        weights = []
        elements = []

        for node in graph.get_pos():
            # If a node is "central", add the central label as a class
            if graph.get_graph().degree(node) >= 5:
                elements.append({
                    'data': {
                        'id': node,
                        'label': node
                    },
                    'selectable': 'true',
                    'classes': 'central'
                })
            else:
                elements.append({
                    'data': {
                        'id': node,
                        'label': node
                    },
                    'selectable': 'true',
                })

        for node1, node2, weight in graph.get_graph().edges(data=True):
            element = {
                'data': {
                    'source': node1,
                    'target': node2,
                    'weight': round(weight['weight'], 4)
                }
            }

            # If the edge is a "different" edge, label with class "different" to highlight this edge
            if (node1, node2) in difference:
                element = {
                    'data': {
                        'source': node1,
                        'target': node2,
                        'weight': round(weight['weight'], 4)
                    },
                    'classes': 'different'
                }

            weights.append(round(weight['weight'], 4))
            elements.append(element)

        # Update correct graph components
        if graph_number == 1:
            self.one_components = (weights, elements)
        if graph_number == 2:
            self.two_components = (weights, elements)

    def get_server(self):
        """
        Returns the comparison interface server

        :return: (Dash) Returns the Dash app object, which can be run using run_server.
            Returns a Jupyter Dash object if DashGraph has been initialised for Jupyter Notebook.
        """
        # Create an app from a comparison layout
        self.app.layout = self._generate_comparison_layout(
            self.graph_one, self.graph_two)
        # Return the app
        return self.app
Пример #19
0
class DashGraph:
    """
    This DashGraph class creates a server for Dash cytoscape visualisations.
    """
    def __init__(self, input_graph, app_display='default'):
        """
        Initialises the DashGraph object from the Graph class object.
        Dash creates a mini Flask server to visualise the graphs.

        :param app_display: (str) 'default' by default and 'jupyter notebook' for running Dash inside Jupyter Notebook.
        :param input_graph: (Graph) Graph class from graph.py.
        """
        self.graph = None
        # Dash app styling with Bootstrap
        if app_display == 'jupyter notebook':
            self.app = JupyterDash(__name__,
                                   external_stylesheets=[dbc.themes.BOOTSTRAP])
        else:
            self.app = Dash(__name__,
                            external_stylesheets=[dbc.themes.BOOTSTRAP])

        # Graph class object
        self.graph = input_graph
        # The dictionary of the nodes coordinates
        self.pos = self.graph.get_pos()

        # Colours of nodes
        self.colour_groups = {}
        # If colours have been assigned in Graph class, add styling
        if self.graph.get_node_colours():
            colour_map = self.graph.get_node_colours()
            self._assign_colours_to_groups(list(colour_map.keys()))

        self.weights = []
        self.elements = []
        self._update_elements()

        # Load the different graph layouts
        cyto.load_extra_layouts()
        self.layout_options = ['cose-bilkent', 'cola', 'spread']
        self.statistics = [
            'graph_summary', 'average_degree_connectivity',
            'average_neighbor_degree', 'betweenness_centrality'
        ]
        # Load default stylesheet
        self.stylesheet = None
        self.stylesheet = self._get_default_stylesheet()

        # Append stylesheet for colour and size
        # If sizes have been set in the Graph class
        self._style_colours()
        if self.graph.get_node_sizes():
            self._assign_sizes()

        self.cyto_graph = None

        # Callback functions to hook frontend elements to functions
        self.app.callback(Output('cytoscape', 'layout'),
                          [Input('dropdown-layout', 'value')])(
                              DashGraph._update_cytoscape_layout)
        self.app.callback(Output('json-output', 'children'),
                          [Input('dropdown-stat', 'value')])(
                              self._update_stat_json)
        self.app.callback(Output('cytoscape', 'elements'),
                          [Input('rounding_decimals', 'value')])(
                              self._round_decimals)

    def _set_cyto_graph(self):
        """
        Sets the cytoscape graph elements.
        """
        self.cyto_graph = cyto.Cytoscape(
            id="cytoscape",
            layout={'name': self.layout_options[0]},
            style={
                'width': '100%',
                'height': '600px',
                'padding': '5px 3px 5px 3px'
            },
            elements=self.elements,
            stylesheet=self.stylesheet)

    def _get_node_group(self, node_name):
        """
        Returns the industry or sector name for a given node name.

        :param node_name: (str) Name of a given node in the graph.
        :return: (str) Name of industry that the node is in or "default" for nodes which haven't been assigned a group.
        """
        node_colour_map = self.graph.get_node_colours()
        for key, val in node_colour_map.items():
            if node_name in val:
                return key
        return "default"

    def _get_node_size(self, index):
        """
        Returns the node size for given node index if the node sizes have been set.

        :param index: (int) The index of the node.
        :return: (float) Returns size of node set, 0 if it has not been set.
        """
        if self.graph.get_node_sizes():
            return self.graph.get_node_sizes()[index]
        return 0

    def _update_elements(self, dps=4):
        """
        Updates the elements needed for the Dash Cytoscape Graph object.

        :param dps: (int) Decimal places to round the edge values.
        """

        i = 0
        self.weights = []
        self.elements = []

        for node in self.pos:
            self.elements.append({
                'data': {
                    'id': node,
                    'label': node,
                    'colour_group': self._get_node_group(node),
                    'size': self._get_node_size(i)
                },
                'selectable': 'true',
            })
            i += 1

        for node1, node2, weight in self.graph.get_graph().edges(data=True):
            self.weights.append(round(weight['weight'], dps))
            self.elements.append({
                'data': {
                    'source': node1,
                    'target': node2,
                    'weight': round(weight['weight'], dps)
                }
            })

    def _generate_layout(self):
        """
        Generates the layout for cytoscape.

        :return: (dbc.Container) Returns Dash Bootstrap Component Container containing the layout of UI.
        """
        graph_type = type(self.graph).__name__

        self._set_cyto_graph()

        layout_input = [
            html.H1("{} from {} matrix".format(graph_type,
                                               self.graph.get_matrix_type())),
            html.Hr(),
            dbc.Row(
                [
                    dbc.Col(self._get_default_controls(), md=4),
                    dbc.Col(self.cyto_graph, md=8),
                ],
                align="center",
            )
        ]
        if self.colour_groups:
            layout_input.append(self._get_toast())

        layout = dbc.Container(
            layout_input,
            fluid=True,
        )
        return layout

    def _assign_colours_to_groups(self, groups):
        """
        Assigns the colours to industry or sector groups by creating a dictionary of group name to colour.

        :param groups: (List) List of industry groups as strings.
        """
        # List of colours selected to match with industry groups
        colours = [
            "#d0b7d5", "#a0b3dc", "#90e190", "#9bd8de", "#eaa2a2", "#f6c384",
            "#dad4a2", '#ff52a8', '#ffd1e8', '#bd66ff', '#6666ff', '#66ffff',
            '#00e600', '#fff957', '#ffc966', '#ff8833', '#ff6666', '#C0C0C0',
            '#008080'
        ]

        # Random colours are generated if industry groups added exceeds 19
        while len(groups) > len(colours):
            random_number = random.randint(0, 16777215)
            hex_number = str(hex(random_number))
            hex_number = '#' + hex_number[2:]
            colours.append(hex_number)

        # Create and add to the colour map
        colour_map = {}
        for i, item in enumerate(groups):
            colour_map[item] = colours[i].capitalize()
        self.colour_groups = colour_map

    def _style_colours(self):
        """
        Appends the colour styling to stylesheet for the different groups.
        """
        if self.colour_groups:
            keys = list(self.colour_groups.keys())
            for item in keys:
                new_colour = {
                    "selector": "node[colour_group=\"{}\"]".format(item),
                    "style": {
                        'background-color':
                        '{}'.format(self.colour_groups[item]),
                    }
                }
                self.stylesheet.append(new_colour)

    def _assign_sizes(self):
        """
        Assigns the node sizing by appending to the stylesheet.
        """
        sizes = self.graph.get_node_sizes()
        max_size = max(sizes)
        min_size = min(sizes)
        new_sizes = {
            'selector': 'node',
            'style': {
                "width":
                "mapData(size, {min}, {max}, 25, 250)".format(min=min_size,
                                                              max=max_size),
                "height":
                "mapData(size, {min}, {max}, 25, 250)".format(min=min_size,
                                                              max=max_size),
            }
        }
        self.stylesheet.append(new_sizes)

    def get_server(self):
        """
        Returns a small Flask server.

        :return: (Dash) Returns the Dash app object, which can be run using run_server.
            Returns a Jupyter Dash object if DashGraph has been initialised for Jupyter Notebook.
        """
        self.app.layout = self._generate_layout()
        return self.app

    @staticmethod
    def _update_cytoscape_layout(layout):
        """
        Callback function for updating the cytoscape layout.
        The useful layouts for MST have been included as options (cola, cose-bilkent, spread).

        :return: (Dict) Dictionary of the key 'name' to the desired layout (e.g. cola, spread).
        """
        return {'name': layout}

    def _update_stat_json(self, stat_name):
        """
        Callback function for updating the statistic shown.

        :param stat_name: (str) Name of the statistic to display (e.g. graph_summary).
        :return: (json) Json of the graph information depending on chosen statistic.
        """
        switcher = {
            "graph_summary":
            self.get_graph_summary(),
            "average_degree_connectivity":
            nx.average_degree_connectivity(self.graph.get_graph()),
            "average_neighbor_degree":
            nx.average_neighbor_degree(self.graph.get_graph()),
            "betweenness_centrality":
            nx.betweenness_centrality(self.graph.get_graph()),
        }
        if type(self.graph).__name__ == "PMFG":
            switcher["disparity_measure"] = self.graph.get_disparity_measure()
        return json.dumps(switcher.get(stat_name), indent=2)

    def get_graph_summary(self):
        """
        Returns the Graph Summary statistics.
        The following statistics are included - the number of nodes and edges, smallest and largest edge,
        average node connectivity, normalised tree length and the average shortest path.

        :return: (Dict) Dictionary of graph summary statistics.
        """
        summary = {
            "nodes":
            len(self.pos),
            "edges":
            self.graph.get_graph().number_of_edges(),
            "smallest_edge":
            min(self.weights),
            "largest_edge":
            max(self.weights),
            "average_node_connectivity":
            nx.average_node_connectivity(self.graph.get_graph()),
            "normalised_tree_length":
            (sum(self.weights) / (len(self.weights))),
            "average_shortest_path":
            nx.average_shortest_path_length(self.graph.get_graph())
        }
        return summary

    def _round_decimals(self, dps):
        """
        Callback function for updating decimal places.
        Updates the elements to modify the rounding of edge values.

        :param dps: (int) Number of decimals places to round to.
        :return: (List) Returns the list of elements used to define graph.
        """

        if dps:
            self._update_elements(dps)

        return self.elements

    def _get_default_stylesheet(self):
        """
        Returns the default stylesheet for initialisation.

        :return: (List) A List of definitions used for Dash styling.
        """
        stylesheet = \
            [
                {
                    'selector': 'node',
                    'style': {
                        'label': 'data(label)',
                        'text-valign': 'center',
                        'background-color': '#65afff',
                        'color': '',
                        'font-family': 'sans-serif',
                        'font-size': '12',
                        'font-weight': 'bold',
                        'border-width': 1.5,
                        'border-color': '#161615',
                    }
                },
                {
                    "selector": 'edge',
                    "style": {
                        'label': 'data(weight)',
                        "line-color": "#a3d5ff",
                        'font-size': '8',
                    }
                },
                {
                    "selector": '[weight => 0]',
                    "style": {
                        "width": "mapData(weight, 0, {}, 1, 8)".format(max(self.weights)),
                    }
                },
                {
                    "selector": '[weight < 0]',
                    "style": {
                        "width": "mapData(weight, 0, {}, 1, 8)".format(min(self.weights)),
                    }
                }
            ]
        return stylesheet

    def _get_toast(self):
        """
        Toast is the floating colour legend to display when industry groups have been added.
        This method returns the toast component with the styled colour legend.

        :return: (html.Div) Returns Div containing colour legend.
        """
        list_elements = []
        for industry, colour in self.colour_groups.items():
            span_styling = \
                {
                    "border": "1px solid #ccc",
                    "background-color": colour,
                    "float": "left",
                    "width": "12px",
                    "height": "12px",
                    "margin-right": "5px"
                }
            children = [industry.title(), html.Span(style=span_styling)]
            list_elements.append(html.Li(children))

        toast = html.Div([
            dbc.Toast(
                html.Ul(list_elements,
                        style={
                            "list-style": "None",
                            "padding-left": 0
                        }),
                id="positioned-toast",
                header="Industry Groups",
                dismissable=True,
                # stuck on bottom right corner
                style={
                    "position": "fixed",
                    "bottom": 36,
                    "right": 10,
                    "width": 350
                },
            ),
        ])
        return toast

    def _get_default_controls(self):
        """
        Returns the default controls for initialisation.

        :return: (dbc.Card) Dash Bootstrap Component Card which defines the side panel.
        """
        controls = dbc.Card(
            [
                html.Div([
                    dbc.FormGroup([
                        dbc.Label("Graph Layout"),
                        dcc.Dropdown(
                            id="dropdown-layout",
                            options=[{
                                "label": col,
                                "value": col
                            } for col in self.layout_options],
                            value=self.layout_options[0],
                            clearable=False,
                        ),
                    ]),
                    dbc.FormGroup([
                        dbc.Label("Statistic Type"),
                        dcc.Dropdown(
                            id="dropdown-stat",
                            options=[{
                                "label": col,
                                "value": col
                            } for col in self.statistics],
                            value="graph_summary",
                            clearable=False,
                        ),
                    ]),
                    html.Pre(id='json-output',
                             style={
                                 'overflow-y': 'scroll',
                                 'height': '100px',
                                 'border': 'thin lightgrey solid'
                             }),
                    dbc.FormGroup([
                        dbc.Label("Decimal Places"),
                        dbc.Input(id="rounding_decimals",
                                  type="number",
                                  value=4,
                                  min=1),
                    ]),
                ]),
                dbc.CardBody(html.Div(id="card-content",
                                      className="card-text")),
            ],
            body=True,
        )
        return controls
Пример #20
0
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 dash_duo.redux_state_rqs == [], "pendingCallbacks 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")
def add_slide1_callbacks(dash: Dash) -> None:
    """Add routing callback"""
    dash.callback(Output("slide1-tab-content", "children"),
                  [Input("slide1-tabs", "active_tab")])(update_tab)
Пример #22
0
    def set_callbacks(self, app: Dash) -> None:
        @app.callback(
            [
                Output(self.uuid("map"), "layers"),
                Output(self.uuid("map2"), "layers"),
                Output(self.uuid("map3"), "layers"),
                Output(self.uuid("map3-label"), "children"),
            ],
            [
                Input(self.selector.storage_id, "data"),
                Input(self.uuid("ensemble"), "value"),
                Input(self.uuid("realization"), "value"),
                Input(self.selector2.storage_id, "data"),
                Input(self.uuid("ensemble2"), "value"),
                Input(self.uuid("realization2"), "value"),
                Input(self.uuid("calculation"), "value"),
                Input(self.uuid("attribute-settings"), "data"),
                Input(self.uuid("truncate-diff-min"), "value"),
                Input(self.uuid("truncate-diff-max"), "value"),
                Input(self.uuid("map"), "switch"),
                Input(self.uuid("map2"), "switch"),
                Input(self.uuid("map3"), "switch"),
            ],
        )
        # pylint: disable=too-many-arguments, too-many-locals
        def _set_base_layer(
            stored_selector_data: str,
            ensemble: str,
            real: str,
            stored_selector2_data: str,
            ensemble2: str,
            real2: str,
            calculation: str,
            stored_attribute_settings: str,
            diff_min: Union[int, float, None],
            diff_max: Union[int, float, None],
            hillshade: dict,
            hillshade2: dict,
            hillshade3: dict,
        ) -> Tuple[List[dict], List[dict], List[dict], str]:
            ctx = callback_context.triggered
            if not ctx or not stored_selector_data or not stored_selector2_data:
                raise PreventUpdate

            # TODO(Sigurd)
            # These two are presumably of type dict, but the type depends on the actual python
            # objects that get serialized inside SurfaceSelector.
            # Should deserialization and validation be delegated to SurfaceSelector?
            # Note that according to the doc, it seems that dcc.Store actualy does the
            # serialization/deserialization for us!
            # Should be refactored
            data: dict = json.loads(stored_selector_data)
            data2: dict = json.loads(stored_selector2_data)
            if not isinstance(data, dict) or not isinstance(data2, dict):
                raise TypeError("Selector data payload must be of type dict")
            attribute_settings: dict = json.loads(stored_attribute_settings)
            if not isinstance(attribute_settings, dict):
                raise TypeError("Expected stored attribute_settings to be of type dict")

            if real in ["Mean", "StdDev", "Min", "Max"]:
                surface = self._surface_ensemble_set_model[
                    ensemble
                ].calculate_statistical_surface(**data, calculation=real)

            else:
                surface = self._surface_ensemble_set_model[
                    ensemble
                ].get_realization_surface(**data, realization=int(real))

            if real2 in ["Mean", "StdDev", "Min", "Max"]:
                surface2 = self._surface_ensemble_set_model[
                    ensemble2
                ].calculate_statistical_surface(**data2, calculation=real2)

            else:
                surface2 = self._surface_ensemble_set_model[
                    ensemble2
                ].get_realization_surface(**data2, realization=int(real2))

            surface_layers: List[dict] = [
                SurfaceLeafletModel(
                    surface,
                    name="surface",
                    colors=attribute_settings.get(data["attribute"], {}).get("color"),
                    apply_shading=hillshade.get("value", False),
                    clip_min=attribute_settings.get(data["attribute"], {}).get(
                        "min", None
                    ),
                    clip_max=attribute_settings.get(data["attribute"], {}).get(
                        "max", None
                    ),
                    unit=attribute_settings.get(data["attribute"], {}).get("unit", " "),
                ).layer
            ]
            surface_layers2: List[dict] = [
                SurfaceLeafletModel(
                    surface2,
                    name="surface2",
                    colors=attribute_settings.get(data2["attribute"], {}).get("color"),
                    apply_shading=hillshade2.get("value", False),
                    clip_min=attribute_settings.get(data2["attribute"], {}).get(
                        "min", None
                    ),
                    clip_max=attribute_settings.get(data2["attribute"], {}).get(
                        "max", None
                    ),
                    unit=attribute_settings.get(data2["attribute"], {}).get(
                        "unit", " "
                    ),
                ).layer
            ]

            try:
                surface3 = calculate_surface_difference(surface, surface2, calculation)
                if diff_min is not None:
                    surface3.values[surface3.values <= diff_min] = diff_min
                if diff_max is not None:
                    surface3.values[surface3.values >= diff_max] = diff_max
                diff_layers: List[dict] = []
                diff_layers.append(
                    SurfaceLeafletModel(
                        surface3,
                        name="surface3",
                        colors=attribute_settings.get(data["attribute"], {}).get(
                            "color"
                        ),
                        apply_shading=hillshade3.get("value", False),
                    ).layer
                )
                error_label = ""
            except ValueError:
                diff_layers = []
                error_label = (
                    "Cannot calculate because the surfaces have different geometries"
                )

            if self.well_layer:
                surface_layers.append(self.well_layer)
                surface_layers2.append(self.well_layer)
                diff_layers.append(self.well_layer)
            return (surface_layers, surface_layers2, diff_layers, error_label)

        def _update_from_btn(
            _n_prev: int, _n_next: int, current_value: str, options: List[dict]
        ) -> str:
            """Updates dropdown value if previous/next btn is clicked"""
            option_values: List[str] = [opt["value"] for opt in options]
            ctx = callback_context.triggered
            if not ctx or current_value is None:
                raise PreventUpdate
            if not ctx[0]["value"]:
                return current_value
            callback = ctx[0]["prop_id"]
            if "-prev" in callback:
                return prev_value(current_value, option_values)
            if "-next" in callback:
                return next_value(current_value, option_values)
            return current_value

        for btn_name in ["ensemble", "realization", "ensemble2", "realization2"]:
            app.callback(
                Output(self.uuid(f"{btn_name}"), "value"),
                [
                    Input(self.uuid(f"{btn_name}-prev"), "n_clicks"),
                    Input(self.uuid(f"{btn_name}-next"), "n_clicks"),
                ],
                [
                    State(self.uuid(f"{btn_name}"), "value"),
                    State(self.uuid(f"{btn_name}"), "options"),
                ],
            )(_update_from_btn)
Пример #23
0
def activate_all(dash: Dash) -> NoReturn:
    """Активирует все контролерры для указанного Dash-приложения."""
    for controller in CONTROLLERS:
        kwargs = controller.dict()
        func = kwargs.pop("func")
        dash.callback(**kwargs)(func)