Exemple #1
0
def add_callbacks(app, trace):
    add_include_transformed_callback(app, 'univariate', trace)

    @app.callback(dep.Output('univariate-autocorr', 'figure'),
                  [dep.Input('univariate-selector', 'value')])
    def update_autocorr(varname):
        return autocorr_figure(trace, varname)

    @app.callback(dep.Output('univariate-effective-n', 'children'),
                  [dep.Input('univariate-selector', 'value')])
    def update_effective_n(varname):
        return effective_n_p(trace, varname)

    @app.callback(dep.Output('univariate-gelman-rubin', 'children'),
                  [dep.Input('univariate-selector', 'value')])
    def update_gelman_rubin(varname):
        return gelman_rubin_p(trace, varname)

    @app.callback(dep.Output('univariate-hist', 'figure'),
                  [dep.Input('univariate-selector', 'value')])
    def update_hist(varname):
        return hist_figure(trace, varname)

    @app.callback(dep.Output('univariate-lines', 'figure'),
                  [dep.Input('univariate-selector', 'value')])
    def update_lines(varname):
        return lines_figure(trace, varname)
def build_degroot_callbacks(app):
    build_step_callback(app, id_degroot_slider_steps_value,
                        id_degroot_slider_steps, 'Steps')
    build_init_callback(app, id_degroot_modal, id_degroot_modal_init_slider,
                        model_degroot['name'])

    @app.callback(dp.Output('session-tracer-degroot', 'data'),
                  dp.Input(id_degroot_dropdown, 'value'))
    def tracer_callback(value):
        return [model_degroot['id'], value]

    @app.callback(dp.Output('session-actions-degroot', 'data'),
                  dp.Input(id_degroot_button_random, 'n_clicks'),
                  dp.Input(id_degroot_button_stochastic, 'n_clicks'),
                  dp.Input(id_degroot_button_step, 'n_clicks'),
                  dp.Input(id_degroot_modal_generate, 'n_clicks'),
                  dp.State(id_degroot_modal_init_slider, 'value'),
                  dp.State(id_degroot_slider_steps, 'value'))
    def callback(n1, n2, n3, n4, init, steps):
        ctx = dash.callback_context
        if not ctx.triggered: return []
        source = ctx.triggered[0]['prop_id'].split('.')[0]
        args = {'steps': steps, 'init': init}
        ac = {
            id_degroot_button_random: action_degroot_random,
            id_degroot_button_stochastic: action_degroot_stochastic,
            id_degroot_button_step: action_degroot_step,
            id_degroot_modal_generate: action_degroot_init
        }
        if source in ac:
            return [(model_degroot['id'], ac[source], args)]
        print(
            f'DeGroot callback: Could not find property with source: {source}')
        raise PreventUpdate()
Exemple #3
0
def tha_build_callbacks(app):
    build_step_callback(app, id_tha_slider_steps_value, id_tha_slider_steps, 'Steps')
    build_init_callback(app, id_tha_modal, id_tha_modal_init_slider, 'Threshold Automata')

    @app.callback(
        dp.Output(model_tha['session-tracer'], 'data'),
        dp.Input(id_tha_dropdown, 'value'))
    def tracer_callback(value):
        return [model_tha['id'], value]

    @app.callback(
        dp.Output(model_tha['session-actions'], 'data'),
        dp.Input(id_tha_button_random, 'n_clicks'),
        dp.Input(id_tha_button_step, 'n_clicks'),
        dp.Input(id_tha_modal_generate, 'n_clicks'),
        dp.State(id_tha_modal_init_slider, 'value'),
        dp.State(id_tha_slider_steps, 'value'))
    def callback(n1, n2, n3, init, steps):
        ctx = dash.callback_context
        if not ctx.triggered: return []
        source = ctx.triggered[0]['prop_id'].split('.')[0]
        args = {'steps': steps, 'init': init}
        ac = {
            id_tha_button_random: action_tha_random,
            id_tha_button_step: action_tha_step,
            id_tha_modal_generate: action_tha_init,
        }
        if source in ac:
            return [(model_tha['id'], ac[source], args)]
        print(f'THA callback: Could not find property with source: {source}')
        raise PreventUpdate()
Exemple #4
0
def assign_callbacks(app: dash.Dash):
    @app.callback(
        [
            dep.Output("known_index_bar", "figure"),
            dep.Output("unknown_index_bar", "figure"),
            dep.Output("known_unknown_pie", "figure"),
            dep.Output("known_fraction", "value"),
        ],
        [dep.Input("run_select", "value")],
    )
    def update_layout(run_alias):
        """
        When input(run dropdown) is changed, known index bar, unknown index bar,
        piechart and textarea are updated

        Parameter:
            run_alias: user-selected run name from dropdown
        Returns:
            functions update_known_index_bar, update_unknown_index_bar,
            update_pie_chart's data value, and update_pie_chart's fraction value
        """

        run = index[index[index_col.Run] == run_alias]
        run = run[run[index_col.ReadNumber] == 1]
        run = run[~run[index_col.SampleID].isna()]
        run = run.drop_duplicates([index_col.SampleID, index_col.LaneNumber])
        # TODO: Replace with join from Pinery (on Run, Lane, Index1, Index2)
        #  10X index (SI-GA-H11) will have to be converted to nucleotide
        run[COL_LIBRARY] = run[index_col.SampleID].str.extract(
            r"SWID_\d+_(\w+_\d+_.*_\d+_[A-Z]{2})_"
        )
        run[COL_INDEX] = run[index_col.Index1].str.cat(
            run[index_col.Index2].fillna(""), sep=" "
        )

        pruned = gsiqcetl.bcl2fastq.utility.prune_unknown_index_from_run(
            run_alias, index, unknown
        )
        pruned[COL_INDEX] = pruned[un_col.Index1].str.cat(
            pruned[un_col.Index2].fillna(""), sep=" "
        )
        pruned = pruned.sort_values(un_col.Count, ascending=False)
        pruned = pruned.head(30)

        total_clusters = gsiqcetl.bcl2fastq.utility.total_clusters_for_run(
            run_alias, index
        )

        pie_data, textarea_fraction = create_pie_chart(
            run, pruned, total_clusters
        )

        return (
            create_known_index_bar(run),
            create_unknown_index_bar(pruned),
            pie_data,
            textarea_fraction,
        )
def register_app_callbacks(app: dash.Dash) -> None:
    @app.callback(
        ddp.Output("editor-euler-solution", "value"),
        [ddp.Input("select-solution-number", "value")],
    )
    def populate_solution_code(name: str) -> str:
        if not name:
            return ""
        module = sys.modules[__name__]
        func: tp.Callable = getattr(module, name)
        code: str = inspect.getsource(func)
        seen: tp.List[str] = []
        root: ast.Module = ast.parse(source=code)
        for node in ast.walk(node=root):
            if not isinstance(node, ast.Call):
                continue
            if not hasattr(node.func, 'value') or not hasattr(
                    node.func, 'attr'):
                continue
            if node.func.attr in seen:
                continue
            else:
                seen.append(node.func.attr)
            helper_module = getattr(module, node.func.value.id)
            helper_func = getattr(helper_module, node.func.attr)
            code += "\n"
            code += inspect.getsource(helper_func)
        return code

    @app.callback(
        ddp.Output("store-solution-profile", "data"),
        [ddp.Input("button-solution-profile", "n_clicks")],
        [ddp.State("select-solution-number", "value")],
    )
    def profile_solution(n_clicks: int, name: str) -> int:
        if not name:
            return {}
        module = sys.modules[__name__]
        func = getattr(module, name)

        print(inspect.getsource(func))
        with pi.Profiler() as profiler:
            func()
        profile_str = profiler.output(renderer=pi.renderers.JSONRenderer(
            show_all=False))
        profile_dict = json.loads(s=profile_str)
        return profile_dict

    @app.callback(
        ddp.Output("graph-solution-profile", "figure"),
        [ddp.Input("store-solution-profile", "data")],
    )
    def visualize_profile(data: dict) -> int:
        if not data:
            return {}

        root_frame = data.pop("root_frame")
        return {}
Exemple #6
0
def build_init_callback(app, id_modal, slider_id, text):
    @app.callback(dp.Output(f'{slider_id}-value', 'children'),
                  dp.Input(f'{slider_id}', 'value'))
    def slider_update(value):
        return f'{text} {value}'

    @app.callback(dp.Output(f'modal-{id_modal}', 'is_open'),
                  dp.Input(f'modal-{id_modal}-open', 'n_clicks'),
                  dp.Input(f'modal-{id_modal}-close', 'n_clicks'),
                  dp.State(f'modal-{id_modal}', 'is_open'))
    def toggle_modal_init(n1, n2, is_open):
        if n1 or n2:
            return not is_open
        return is_open
Exemple #7
0
def build_callbacks(app):
    @app.callback(
        [dd.Output(component_id='hyspec-fwhm_vs_Ei', component_property='figure'),
         dd.Output(component_id='hyspec-flux_vs_Ei', component_property='figure'),
        ],
        [dd.Input('hyspec_operation_mode', 'value'),
        ],
    )
    def update_figures(mode):
        fwhm_data = getFWHM_vs_Ei(mode)
        flux_data = getFlux_vs_Ei(mode)
        return [
            {
                'data': fwhm_data,
                'layout': dict(
                    title = 'Resolution vs incident energy',
                    showlegend=True,
                    xaxis=dict(
                        title='Ei (meV)',
                        type='log',
                        showspikes=True,
                    ),
                    yaxis=dict(
                        title='FWHM (meV)',
                        type='log',
                        showspikes=True,
                    ),
                ),
            },
            {
                'data': flux_data,
                'layout': dict(
                    title = 'Flux vs incident energy',
                    showlegend=True,
                    xaxis=dict(
                        title='Ei (meV)',
                        type='log',
                        showspikes=True,
                    ),
                    yaxis=dict(
                        # title='Flux (counts/s/cm^2/MW)',
                        title='Flux (arb. unit)',
                        type='log',
                        showspikes=True,
                    ),
                ),
            },
        ]
    return
Exemple #8
0
def init_callbacks(app):
    @app.callback(dd.Output('wordcloud', 'src'), dd.Input('wordcloud', 'id'),
                  dd.Input('wc_query', 'value'))
    def make_image(b, query):
        print(f"query : {query}")
        img = BytesIO()
        plot_wordcloud(data=df, query=query).save(img, format='PNG')
        return 'data:image/png;base64,{}'.format(
            base64.b64encode(img.getvalue()).decode())

    @app.callback(dd.Output('distibution', 'figure'),
                  dd.Input('distribution_query', 'value'))
    def distribution_selector(d_query='Pie Chart'):
        print(f"d_query : {d_query}")
        if (d_query == "Histogram"):
            return histogram
        return pie_chart
 def apply(self, callbacks):
     for callback in callbacks:
         if len(callback[dd.Output]) == 0:
             output_id = str(uuid.uuid4())
             hidden_div = html.Div(id=output_id, style={"display": "none"})
             callback[dd.Output] = [dd.Output(output_id, "children")]
             self.hidden_divs.append(hidden_div)
     return callbacks
Exemple #10
0
def add_callbacks(app, trace):
    add_include_transformed_callback(app, 'bivariate-x', trace)
    add_include_transformed_callback(app, 'bivariate-y', trace)

    @app.callback(dep.Output('bivariate-scatter', 'figure'), [
        dep.Input('bivariate-x-selector', 'value'),
        dep.Input('bivariate-y-selector', 'value')
    ])
    def update_bivariate_scatter(x_varname, y_varname):
        return scatter_figure(trace, x_varname, y_varname)
def sir_build_callbacks(app):
    build_step_callback(app, id_sir_slider_steps_value, id_sir_slider_steps,
                        'Steps')
    build_prob_callback(app, id_sir_slider_prob_value, id_sir_slider_prob)
    build_infection_callback(app, id_sir_slider_itime_value,
                             id_sir_slider_itime)
    build_init_callback(app, id_sir_modal, id_sir_modal_init_slider,
                        model_sir['name'])

    @app.callback(dp.Output(model_sir['session-tracer'], 'data'),
                  dp.Input(id_sir_dropdown, 'value'))
    def tracer_callback(value):
        return [model_sir['id'], value]

    @app.callback(
        dp.Output(model_sir['session-actions'], 'data'),
        dp.Input(id_sir_button_random, 'n_clicks'),
        dp.Input(id_sir_button_step, 'n_clicks'),
        dp.Input(id_sir_button_one, 'n_clicks'),
        dp.Input(id_sir_modal_generate, 'n_clicks'),
        dp.State(id_sir_modal_init_slider, 'value'),
        dp.State(id_sir_slider_steps, 'value'),
        dp.State(id_sir_slider_prob, 'value'),
        dp.State(id_sir_slider_itime, 'value'),
    )
    def callback(n1, n2, n3, n4, init, steps, prob, itime):
        ctx = dash.callback_context
        if not ctx.triggered: return []
        source = ctx.triggered[0]['prop_id'].split('.')[0]
        args = {'steps': steps, 'prob': prob, 'itime': itime, 'init': init}

        ac = {
            id_sir_button_random: action_sir_random,
            id_sir_button_step: action_sir_step,
            id_sir_button_one: action_sir_one,
            id_sir_modal_generate: action_sir_init
        }
        if source in ac:
            return [(model_sir['id'], ac[source], args)]
        print(f'SIR callback: Could not find property with source: {source}')
        raise PreventUpdate()
Exemple #12
0
    def _assign_callbacks(self) -> None:
        """Assign callbacks to connect templated HTML with pipeline behavior"""

        self.callback(ddep.Output('pipeline-cyto', 'layout'),
                      ddep.Input('dropdown-layout',
                                 'value'))(callbacks.cast_layout_to_dict)

        pipeline_callbacks = callbacks.PipelineStatus(
            self._num_displayed_data_points, self._pipeline.connectors[0])
        self.callback(ddep.Output('graph-queue-size', 'extendData'),
                      ddep.Input('interval', 'n_intervals'))(
                          pipeline_callbacks.get_queue_sizes)

        system_callbacks = callbacks.SystemUsage(
            self._num_displayed_data_points)
        self.callback(ddep.Output('graph-cpu-usage', 'extendData'),
                      ddep.Input('interval', 'n_intervals'))(
                          system_callbacks.get_cpu_usage)

        self.callback(ddep.Output('graph-mem-usage', 'extendData'),
                      ddep.Input('interval', 'n_intervals'))(
                          system_callbacks.get_memory_usage)
Exemple #13
0
def add_callbacks(app, trace):
    bivariate_layout = bivariate.layout(trace)
    univariate_layout = univariate.layout(trace)

    @app.callback(dep.Output('page-content', 'children'),
                  [dep.Input('url', 'pathname')])
    def update_page_content(pathname):
        if pathname in ['/', '/univariate']:
            return univariate_layout
        elif pathname == '/bivariate':
            return bivariate_layout

    bivariate.add_callbacks(app, trace)
    univariate.add_callbacks(app, trace)
Exemple #14
0
def build_callbacks(app):
    @app.callback(
        [
            dd.Output(component_id='arcs-flux_vs_fwhm',
                      component_property='figure'),
        ],
        [
            dd.Input('Ei_select', 'value'),
        ],
    )
    def update_figure(Ei):
        Ei = float(Ei)

        data = [
            getFlux_vs_FWHMdata(fc1data, Ei, 'ARCS-700-1.5'),
            getFlux_vs_FWHMdata(fc2data, Ei, 'ARCS-100-1.5'),
            getFlux_vs_FWHMdata(fc1_highres_data, Ei, 'ARCS-700-0.5'),
        ]
        return {
            'data':
            data,
            'layout':
            dict(
                title='Flux vs resolution',
                showlegend=True,
                xaxis=dict(
                    title='FWHM (meV)',
                    showspikes=True,
                ),
                yaxis=dict(
                    title='Flux (counts/s/cm^2/MW)',
                    showspikes=True,
                ),
            ),
        },

    return
Exemple #15
0
def build_prob_callback(app, id_value, id_slider):
    @app.callback(dp.Output(id_value, 'children'),
                  dp.Input(id_slider, 'value'))
    def slider_update(value):
        return f'Probability {value}'
Exemple #16
0
                ]),

        dcc.Markdown('Apparently, you can inline a dcc.Component by listing it '
                     'as a child in the list of children of a html container'),
    ],

}
#-- 3 interaction

# subscribe to all possible triggers, even though they donot exist all
# at the same time (qresponse has either a values or a value trigger,
# but not both).  This requires to disable the callback exceptions in app.config

# register answer with current question before setting new question idx
@app.callback(
    dd.Output('page-content', 'children'),
    [dd.Input('select-qtype', 'value')])
def setPageContent(value):
    'show the new question'
    # Notes:
    qstn = QTYPE.get(value, value)
    return qstn

@app.callback(
    dd.Output('response-output', 'children'),
    [dd.Input('response', 'value'),
     dd.Input('response', 'values')])
def setResponseOutput(*kids):
    k1 = '{}'.format(kids[0])
    k2 = '{}'.format(repr(kids[1]))
    return html.Div([
Exemple #17
0
def add_include_transformed_callback(app, title, trace):
    @app.callback(
        dep.Output('{}-selector'.format(title), 'options'),
        [dep.Input('{}-selector-include-transformed'.format(title), 'values')])
    def update_selector_transformed(values):
        return get_varname_options(trace, include_transformed=values)
Exemple #18
0
                            },
                        ],
                        values=['opt-random-q', 'opt-random-a'],
                        style={'display': 'block'},
                        labelStyle={'display': 'block'},
                    )
                ]),
            ]),
        html.Div(className='eight columns',
                 id=DISPLAY,
                 children=['loading ...']),
    ])


# -- Page controls
@app.callback(dd.Output(CONTROLS, 'children'), [
    dd.Input(ID('mode'), 'value'),
    dd.Input(ID('max'), 'value'),
    dd.Input(ID('options'), 'values'),
    dd.Input(ID('limit-maxq'), 'values'),
    dd.Input(ID('limit-time'), 'values'),
    dd.Input(ID('max-time'), 'value'),
])
def controls(mode, maxq, options, limit_maxq, limit_time, max_time):
    'store page state in cache and controls for revisits'
    controls = json.dumps([
        (ID('mode'), 'value', mode),
        (ID('max'), 'value', maxq),
        (ID('options'), 'values', options),
        (ID('limit-maxq'), 'values', limit_maxq),
        (ID('limit-time'), 'values', limit_time),
                "x": curve[0],
                "y": curve[1],
                "marker_color": discrete_color_sequence[0]
            }
        ],
        layout={
            "xaxis": dict(title="False Positive Rate"),
            "yaxis": dict(title="True Positive Rate"),
            "plot_bgcolor": bg_color,
            "paper_bgcolor": bg_color,
        }
    )


@app.callback(
    dependencies.Output("lr-feature-importance", "figure"),
    [dependencies.Input("lr-importance-slider", "value")]
)
def update_lr_importance(n_features):
    def get_transformer_feature_names(column_transformer):
        """Most important features from a pipeline."""
        output_features = []
        for name, pipe, features in column_transformer.transformers_:
            if name != 'remainder':
                for i in pipe:
                    trans_features = []
                    if hasattr(i, 'categories_'):
                        trans_features.extend(i.get_feature_names(features))
                    else:
                        trans_features = features
                output_features.extend(trans_features)
def main():
    usage = 'usage: %prog [options] <sad_hdf5_path>'
    parser = OptionParser(usage)
    parser.add_option(
        '-c',
        dest='chrom_hdf5',
        default=False,
        action='store_true',
        help='HDF5 files split by chromosome [Default: %default]')
    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error('Must provide SAD HDF5')
    else:
        sad_hdf5_file = args[0]

    #############################################
    # precursors

    print('Preparing data.', flush=True)

    chr_sad_h5_open = {}

    if not options.chrom_hdf5:
        # open HDF5
        sad_h5_open = h5py.File(sad_hdf5_file, 'r')

        # with one file, hash to a fake chromosome
        chr_sad_h5_open = {1: sad_h5_open}

        # hash SNP ids to indexes
        snps = np.array(sad_h5_open['snp'])
        snp_indexes = {}
        for i, snp_id in enumerate(snps):
            snp_id = snp_id.decode('UTF-8')
            snp_indexes[snp_id] = (1, i)
        del snps

    else:
        snp_indexes = {}

        for ci in range(1, 6):
            # open HDF5
            sad_h5_open = h5py.File('%s/chr%d/sad.h5' % (sad_hdf5_file, ci),
                                    'r')

            # with one file, hash to a fake chromosome
            chr_sad_h5_open[ci] = sad_h5_open

            # hash SNP ids to indexes
            snps = np.array(sad_h5_open['snp'])
            for i, snp_id in enumerate(snps):
                snp_id = snp_id.decode('UTF-8')
                snp_indexes[snp_id] = (ci, i)
            del snps

        # open chr1 HDF5 for non-chr specific data
        sad_h5_open = chr_sad_h5_open[1]

    # easy access to target information
    target_ids = np.array(
        [tl.decode('UTF-8') for tl in sad_h5_open['target_ids']])
    target_labels = np.array(
        [tl.decode('UTF-8') for tl in sad_h5_open['target_labels']])

    # read SAD percentile indexes into memory
    sad_pct = np.array(sad_h5_open['SAD_pct'])

    # read percentiles
    percentiles = np.around(sad_h5_open['percentiles'], 3)
    percentiles = np.append(percentiles, percentiles[-1])

    # initialize BigQuery client
    # client = bigquery.Client('seqnn-170614')
    pop_emerald = {
        'EUR': '%s/popgen/1000G/phase3/eur/1000G.EUR.QC' % os.environ['HG19']
    }
    pop_emerald = {}
    for pop in ['EUR']:
        pop_vcf_stem = '%s/popgen/1000G/phase3/%s/1000G.%s.QC' % (
            os.environ['HG19'], pop.lower(), pop)
        pop_emerald[pop] = EmeraldVCF(pop_vcf_stem)

    print('Done.', flush=True)

    #############################################
    # layout

    app = dash.Dash()
    app.css.append_css(
        {"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

    app.layout = html.Div([
        html.Div(
            [
                html.H1('Basenji SNP activity difference'),
                dcc.Markdown('Instructions...'),
                html.Div([
                    html.Label('Datasets'),
                    dcc.Dropdown(id='dataset',
                                 options=[{
                                     'label': 'CAGE',
                                     'value': 'CAGE'
                                 }, {
                                     'label': 'DNase',
                                     'value': 'DNASE'
                                 }, {
                                     'label': 'H3K4me3',
                                     'value': 'CHIP:H3K4me3'
                                 }, {
                                     'label': 'All',
                                     'value': 'All'
                                 }],
                                 value='CAGE')
                ],
                         style={
                             'width': '250',
                             'display': 'inline-block'
                         }),
                html.Div([
                    html.Label('Population'),
                    dcc.Dropdown(id='population',
                                 options=[{
                                     'label': '-',
                                     'value': '-'
                                 }, {
                                     'label': '1kG African',
                                     'value': 'AFR'
                                 }, {
                                     'label': '1kG American',
                                     'value': 'AMR'
                                 }, {
                                     'label': '1kG East Asian',
                                     'value': 'EAS'
                                 }, {
                                     'label': '1kG European',
                                     'value': 'EUR'
                                 }, {
                                     'label': '1kG South Asian',
                                     'value': 'SAS'
                                 }],
                                 value='-')
                ],
                         style={
                             'width': '250',
                             'display': 'inline-block'
                         }),
                html.Div([
                    html.Label('SNP ID'),
                    dcc.Input(id='snp_id', value='rs2157719', type='text'),
                    html.Button(id='snp_submit', n_clicks=0, children='Submit')
                ],
                         style={
                             'display': 'inline-block',
                             'float': 'right'
                         })
            ],
            style={
                'borderBottom': 'thin lightgrey solid',
                'backgroundColor': 'rgb(250, 250, 250)',
                'padding': '10px 5px'
            }),
        dcc.Graph(id='assoc_plot'),
        html.Div([
            dt.DataTable(id='table',
                         rows=[],
                         columns=[
                             'SNP', 'Association', 'Score', 'ScoreQ', 'R2',
                             'Experiment', 'Description'
                         ],
                         column_widths=[150, 125, 125, 125, 125, 200],
                         editable=False,
                         filterable=True,
                         sortable=True,
                         resizable=True,
                         sortColumn='Association',
                         row_selectable=True,
                         selected_row_indices=[],
                         max_rows_in_viewport=20)
        ])
    ])

    #############################################
    # callback helpers

    @memoized
    def query_ld_bq(snp_id, population):
        """Query Google Genomics 1000 Genomes LD table for the given SNP."""
        bq_path = 'genomics-public-data.linkage_disequilibrium_1000G_phase_3'

        # construct query
        query = 'SELECT tname, corr'
        query += ' FROM `%s.super_pop_%s`' % (bq_path, population)
        query += ' WHERE qname = "%s"' % snp_id

        # run query
        print('Running a BigQuery!', file=sys.stderr)
        query_results = client.query(query)

        return query_results

    @memoized
    def query_ld(population, snp_id):
        if population not in pop_emerald:
            print('Population unavailable.', file=sys.stderr)
            return pd.DataFrame()
        else:
            chrm, snp_i = snp_indexes.get(snp_id, (None, None))
            pos = sad_h5_open['pos'][snp_i]
            if chrm is None:
                return pd.DataFrame()
            else:
                return pop_emerald[population].query_ld(snp_id,
                                                        chrm,
                                                        pos,
                                                        ld_threshold=0.333)

    @memoized
    def read_pos(chrom, snp_i):
        return chr_sad_h5_open[chrom]['pos'][snp_i]

    @memoized
    def read_sad(chrom, snp_i, verbose=True):
        """Read SAD scores from HDF5 for the given SNP index."""
        if verbose:
            print('Reading SAD!', file=sys.stderr)

        # read SAD
        snp_sad = chr_sad_h5_open[chrom]['SAD'][snp_i, :].astype('float64')

        # compute percentile indexes
        snp_sadq = []
        for ti in range(len(snp_sad)):
            snp_sadq.append(int(np.searchsorted(sad_pct[ti], snp_sad[ti])))

        return snp_sad, snp_sadq

    def snp_rows(snp_id, dataset, ld_r2=1., verbose=True):
        """Construct table rows for the given SNP id and its LD set
           in the given dataset."""
        rows = []

        # search for SNP
        chrom, snp_i = snp_indexes.get(snp_id, (None, None))
        if chrom is not None:
            # SAD
            snp_sad, snp_sadq = read_sad(chrom, snp_i)

            # round floats
            snp_sad = np.around(snp_sad, 4)
            snp_assoc = np.around(snp_sad * ld_r2, 4)
            ld_r2_round = np.around(ld_r2, 4)

            # extract target scores and info
            for ti, tid in enumerate(target_ids):
                if dataset == 'All' or target_labels[ti].startswith(dataset):
                    rows.append({
                        'SNP': snp_id,
                        'Association': snp_assoc[ti],
                        'Score': snp_sad[ti],
                        'ScoreQ': percentiles[snp_sadq[ti]],
                        'R2': ld_r2_round,
                        'Experiment': tid,
                        'Description': target_labels[ti]
                    })
        elif verbose:
            print('Cannot find %s in snp_indexes.' % snp_id)

        return rows

    def make_data_mask(dataset):
        """Make a mask across targets for the given dataset."""
        dataset_mask = []
        for ti, tid in enumerate(target_ids):
            if dataset == 'All':
                dataset_mask.append(True)
            else:
                dataset_mask.append(target_labels[ti].startswith(dataset))
        return np.array(dataset_mask, dtype='bool')

    def snp_scores(snp_id, dataset, ld_r2=1.):
        """Compute an array of scores for this SNP
           in the specified dataset."""

        dataset_mask = make_data_mask(dataset)

        scores = np.zeros(dataset_mask.sum(), dtype='float64')

        # search for SNP
        if snp_id in snp_indexes:
            chrom, snp_i = snp_indexes[snp_id]

            # read SAD
            snp_sad, _ = read_sad(chrom, snp_i)

            # filter datasets
            snp_sad = snp_sad[dataset_mask]

            # add
            scores += snp_sad * ld_r2

        return scores

    #############################################
    # callbacks

    @app.callback(dd.Output('table', 'rows'),
                  [dd.Input('snp_submit', 'n_clicks')], [
                      dd.State('snp_id', 'value'),
                      dd.State('dataset', 'value'),
                      dd.State('population', 'value')
                  ])
    def update_table(n_clicks, snp_id, dataset, population, verbose=True):
        """Update the table with a new parameter set."""
        if verbose:
            print('Tabling')

        # add snp_id rows
        rows = snp_rows(snp_id, dataset)

        if population != '-':
            # query_results = query_ld(snp_id, population)

            # for ld_snp, ld_corr in query_results:
            #     rows += snp_rows(ld_snp, dataset, ld_corr)

            df_ld = query_ld(population, snp_id)
            for i, v in df_ld.iterrows():
                rows += snp_rows(v.snp, dataset, v.r)

        return rows

    @app.callback(dd.Output('assoc_plot', 'figure'),
                  [dd.Input('snp_submit', 'n_clicks')], [
                      dd.State('snp_id', 'value'),
                      dd.State('dataset', 'value'),
                      dd.State('population', 'value')
                  ])
    def update_plot(n_clicks, snp_id, dataset, population, verbose=True):
        if verbose:
            print('Plotting')

        target_mask = make_data_mask(dataset)

        # add snp_id rows
        query_scores = snp_scores(snp_id, dataset)

        if population != '-':
            # query_results = query_ld(snp_id, population)
            # for ld_snp, ld_corr in query_results:
            #     query_scores += snp_scores(ld_snp, dataset, ld_corr)

            df_ld = query_ld(population, snp_id)
            for i, v in df_ld.iterrows():
                query_scores += snp_scores(v.snp, dataset, v.r)

        # sort
        sorted_indexes = np.argsort(query_scores)

        # range
        ymax = np.abs(query_scores).max()
        ymax *= 1.2

        return {
            'data': [
                go.Scatter(x=np.arange(len(query_scores)),
                           y=query_scores[sorted_indexes],
                           text=target_ids[target_mask][sorted_indexes],
                           mode='markers')
            ],
            'layout': {
                'height': 400,
                'margin': {
                    'l': 20,
                    'b': 30,
                    'r': 10,
                    't': 10
                },
                'yaxis': {
                    'range': [-ymax, ymax]
                },
                'xaxis': {
                    'range': [-1, 1 + len(query_scores)]
                }
            }
        }

    #############################################
    # run

    app.scripts.config.serve_locally = True
    app.run_server(debug=False, port=8787)
Exemple #21
0
# %% CALLBACKS

### These essentially control the interactions in the apps:
# Originally this was broken into lots of sub-callbacks but now we only have 2
# This probably uses marginally more processing time, but really simplifies stuff
#   1. Store selected stack as a dict() in a dcc.Store()
#   2. MEGA-CALLBACK - which does all the processing
#   3. Loci-image callback(s) UNRELATED to functioning of the game


### Setup Stack (Store & Table)
# Source df of stack from stack2df function using the name from the dropdown
# Convert to dict() twice in output; 1. for dcc.Store() and 2. for dash_table
# on dash_table need to convert with 'records' so need to do this op twice
@app.callback(
    [dd.Output('stack_store', 'data'),
     dd.Output('stack_table', 'data')], [dd.Input('dropdown_stack', 'value')])
def stack2store(stack):
    #df = stack2df(stack=stack)
    df = pd.DataFrame(stacks[stack], columns=['posn', 'card', 'loci', 'image'])
    return df.to_dict(), df.to_dict('records')


### MEGA-CALLBACK OF DOOM
# An issue with Dash is a dd.Output(X) can only be triggered in 1 callback
# This is a pain for eg when we want to re-load a problem via a "reload" button
# or after the user submits their solution; we try & fix with the MEGA CALLBACK


## Helper functions
def _problem_setup(df, pType):
def main():
    usage = 'usage: %prog [options] <sad_hdf5_path>'
    parser = OptionParser(usage)
    parser.add_option(
        '-c',
        dest='chrom_hdf5',
        default=False,
        action='store_true',
        help='HDF5 files split by chromosome [Default: %default]')
    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error('Must provide SAD HDF5')
    else:
        sad_h5_path = args[0]

    #############################################
    # precursors

    print('Preparing data...', end='', flush=True)
    sad5 = ChrSAD5(sad_h5_path, index_chr=True)
    print('DONE.', flush=True)

    #############################################
    # layout

    column_widths = [('SNP', 150), ('Association', 125), ('Score', 125),
                     ('ScoreQ', 125), ('R', 125), ('Experiment', 125),
                     ('Description', 200)]
    scc = [{
        'if': {
            'column_id': cw[0]
        },
        'width': cw[1]
    } for cw in column_widths]

    app = dash.Dash(__name__)
    app.css.append_css(
        {"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

    app.layout = html.Div([
        html.Div(
            [
                html.H1('Basenji SNP activity difference'),
                dcc.Markdown('Instructions...'),
                html.Div([
                    html.Label('Datasets'),
                    dcc.Dropdown(id='dataset',
                                 options=[{
                                     'label': 'CAGE',
                                     'value': 'CAGE'
                                 }, {
                                     'label': 'DNase',
                                     'value': 'DNASE'
                                 }, {
                                     'label': 'H3K4me3',
                                     'value': 'CHIP:H3K4me3'
                                 }, {
                                     'label': 'All',
                                     'value': 'All'
                                 }],
                                 value='CAGE')
                ],
                         style={
                             'width': '250',
                             'display': 'inline-block'
                         }),
                html.Div([
                    html.Label('Population'),
                    dcc.Dropdown(id='population',
                                 options=[{
                                     'label': '-',
                                     'value': '-'
                                 }, {
                                     'label': '1kG African',
                                     'value': 'AFR'
                                 }, {
                                     'label': '1kG American',
                                     'value': 'AMR'
                                 }, {
                                     'label': '1kG East Asian',
                                     'value': 'EAS'
                                 }, {
                                     'label': '1kG European',
                                     'value': 'EUR'
                                 }, {
                                     'label': '1kG South Asian',
                                     'value': 'SAS'
                                 }],
                                 value='-')
                ],
                         style={
                             'width': '250',
                             'display': 'inline-block'
                         }),
                html.Div([
                    html.Label('SNP ID'),
                    dcc.Input(id='snp_id', value='rs6656401', type='text'),
                    html.Button(id='snp_submit', n_clicks=0, children='Submit')
                ],
                         style={
                             'display': 'inline-block',
                             'float': 'right'
                         })
            ],
            style={
                'borderBottom': 'thin lightgrey solid',
                'backgroundColor': 'rgb(250, 250, 250)',
                'padding': '10px 5px'
            }),
        dcc.Graph(id='assoc_plot'),
        html.Div([
            dt.DataTable(id='table',
                         data=[],
                         columns=[{
                             'id': cw[0],
                             'name': cw[0]
                         } for cw in column_widths],
                         style_cell_conditional=scc,
                         editable=False,
                         filtering=True,
                         sorting=True,
                         n_fixed_rows=20)
        ])
    ])

    # html.Div([
    #     dt.DataTable(
    #         id='table',
    #         data=[],
    #         columns=[cw[0] for cw in column_widths],
    #         style_cell_conditional=scc,
    #         editable=False,
    #         filtering=True,
    #         sorting=True,
    #         n_fixed_rows=20
    #     )

    #############################################
    # callback helpers

    @memoized
    def query_ld(population, snp_id):
        try:
            sad5.set_population(population)

        except ValueError:
            print('Population unavailable.', file=sys.stderr)
            return pd.DataFrame()

        chrm, snp_i = sad5.snp_chr_index(snp_id)
        pos = sad5.snp_pos(snp_i, chrm)

        if chrm is None:
            return pd.DataFrame()
        else:
            return sad5.emerald_vcf.query_ld(snp_id,
                                             chrm,
                                             pos,
                                             ld_threshold=0.8)

    @memoized
    def read_sad(chrm, snp_i, verbose=True):
        """Read SAD scores from HDF5 for the given SNP index."""
        if verbose:
            print('Reading SAD!', file=sys.stderr)

        # read SAD
        snp_sad = sad5.chr_sad5[chrm][snp_i].astype('float64')

        # read percentiles
        snp_pct = sad5.chr_sad5[chrm].sad_pct(snp_sad)

        return snp_sad, snp_pct

    def snp_rows(snp_id, dataset, ld_r2=1., verbose=True):
        """Construct table rows for the given SNP id and its LD set
           in the given dataset."""
        rows = []

        # search for SNP
        # chrom, snp_i = snp_indexes.get(snp_id, (None,None))
        chrm, snp_i = sad5.snp_chr_index(snp_id)

        if chrm is not None:
            # SAD
            snp_sad, snp_pct = read_sad(chrm, snp_i)

            # round floats
            snp_sad = np.around(snp_sad, 4)
            snp_assoc = np.around(snp_sad * ld_r2, 4)
            ld_r2_round = np.around(ld_r2, 4)

            # extract target scores and info
            for ti, tid in enumerate(sad5.target_ids):
                if dataset == 'All' or sad5.target_labels[ti].startswith(
                        dataset):
                    rows.append({
                        'SNP': snp_id,
                        'Association': snp_assoc[ti],
                        'Score': snp_sad[ti],
                        'ScoreQ': snp_pct[ti],
                        'R': ld_r2_round,
                        'Experiment': tid,
                        'Description': sad5.target_labels[ti]
                    })
        elif verbose:
            print('Cannot find %s in snp_indexes.' % snp_id)

        return rows

    def make_data_mask(dataset):
        """Make a mask across targets for the given dataset."""
        dataset_mask = []
        for ti, tid in enumerate(sad5.target_ids):
            if dataset == 'All':
                dataset_mask.append(True)
            else:
                dataset_mask.append(sad5.target_labels[ti].startswith(dataset))
        return np.array(dataset_mask, dtype='bool')

    def snp_scores(snp_id, dataset, ld_r2=1.):
        """Compute an array of scores for this SNP
           in the specified dataset."""

        dataset_mask = make_data_mask(dataset)

        scores = np.zeros(dataset_mask.sum(), dtype='float64')

        # search for SNP
        chrm, snp_i = sad5.snp_chr_index(snp_id)
        if snp_i is not None:
            # read SAD
            snp_sad, _ = read_sad(chrm, snp_i)

            # filter datasets
            snp_sad = snp_sad[dataset_mask]

            # add
            scores += snp_sad * ld_r2

        return scores

    #############################################
    # callbacks

    @app.callback(dd.Output('table', 'data'),
                  [dd.Input('snp_submit', 'n_clicks')], [
                      dd.State('snp_id', 'value'),
                      dd.State('dataset', 'value'),
                      dd.State('population', 'value')
                  ])
    def update_table(n_clicks, snp_id, dataset, population, verbose=True):
        """Update the table with a new parameter set."""
        if verbose:
            print('Tabling')

        # add snp_id rows
        rows = snp_rows(snp_id, dataset)

        if population != '-':
            df_ld = query_ld(population, snp_id)
            for i, v in df_ld.iterrows():
                rows += snp_rows(v.snp, dataset, v.r)

        return rows

    @app.callback(dd.Output('assoc_plot', 'figure'),
                  [dd.Input('snp_submit', 'n_clicks')], [
                      dd.State('snp_id', 'value'),
                      dd.State('dataset', 'value'),
                      dd.State('population', 'value')
                  ])
    def update_plot(n_clicks, snp_id, dataset, population, verbose=True):
        if verbose:
            print('Plotting')

        target_mask = make_data_mask(dataset)

        # add snp_id rows
        query_scores = snp_scores(snp_id, dataset)

        if population != '-':
            df_ld = query_ld(population, snp_id)
            for i, v in df_ld.iterrows():
                query_scores += snp_scores(v.snp, dataset, v.r)

        # sort
        sorted_indexes = np.argsort(query_scores)

        # range
        ymax = np.abs(query_scores).max()
        ymax *= 1.2

        return {
            'data': [
                go.Scatter(x=np.arange(len(query_scores)),
                           y=query_scores[sorted_indexes],
                           text=sad5.target_ids[target_mask][sorted_indexes],
                           mode='markers')
            ],
            'layout': {
                'height': 400,
                'margin': {
                    'l': 20,
                    'b': 30,
                    'r': 10,
                    't': 10
                },
                'yaxis': {
                    'range': [-ymax, ymax]
                },
                'xaxis': {
                    'range': [-1, 1 + len(query_scores)]
                }
            }
        }

    #############################################
    # run

    app.scripts.config.serve_locally = True
    app.run_server(debug=False, port=8787)
Exemple #23
0
def build_infection_callback(app, id_value, id_slider):
    @app.callback(dp.Output(id_value, 'children'),
                  dp.Input(id_slider, 'value'))
    def slider_update(value):
        return f'Infection Time {value}'
Exemple #24
0

def plot_wordcloud(data):
    d = data
    wc = WordCloud(
        background_color='white',
        font_path="/usr/share/fonts/truetype/msttcorefonts/times.ttf",
        max_words=2000,
        mask=mask,
        width=1024,
        height=1024,
        max_font_size=90,
        min_font_size=5,
        mode='RGBA')
    wc.fit_words(d)
    wc.recolor(color_func=image_colors)

    return wc.to_image()


@app.callback(dd.Output('image_wc', 'src'), [dd.Input('image_wc', 'id')])
def make_image(b):
    img = BytesIO()
    plot_wordcloud(data=freqs).save(img, format='PNG')
    return 'data:image/png;base64,{}'.format(
        base64.b64encode(img.getvalue()).decode())


if __name__ == '__main__':
    app.run_server(debug=True)
Exemple #25
0
from dash import dependencies as dd
from dash.exceptions import PreventUpdate

from cars.analysis import etl as etl

from .. import DashOptions, opts_from_vals
from ..layout import INPID_MAX_DIST, INPID_STATE, INPID_ZIPCODE
from . import deferred_callback


@deferred_callback(
    dd.Output(INPID_STATE, "options"),
    [dd.Input(INPID_ZIPCODE, "value"),
     dd.Input(INPID_MAX_DIST, "value")],
)
def populate_state_options(zipcode: str, max_miles: int) -> list[DashOptions]:
    """
    Generates the state refinement menu.
    """
    if zipcode not in etl.LATLONG_BY_ZIP:
        raise PreventUpdate
    return opts_from_vals(
        state.upper() for state in etl.get_states_in_range(zipcode, max_miles))


__all__ = ["populate_state_options"]
ERR_BAD_ZIP = "bad_zip"
ERR_NO_ZIP = "err_no_zip"
ERR_NO_DEALERS = "err_no_dealers"
Exemple #26
0
    dcc.Dropdown(id='database-name',
                 options=ml.dropdown_get_all_database(dbclient),
                 value='Database Name'),
    html.Div('Table Name'),
    dcc.Dropdown(id='table-name',
                 options=[{
                     'value': 'Table',
                     'label': 'Table'
                 }],
                 value='Table Name'),
    html.Div(id='my-table')
])


@dash_app1.callback(
    depend.Output(component_id='table-name', component_property='options'),
    [depend.Input(component_id='database-name', component_property='value')])
def update_table_name(database):
    try:
        df = ml.dropdown_get_all_table(dbclient, database)
        return df
    except:
        return html.Div('Invalid table')


@dash_app1.callback(
    depend.Output(component_id='my-table', component_property='children'), [
        depend.Input(component_id='database-name', component_property='value'),
        depend.Input(component_id='table-name', component_property='value'),
    ])
def update_output_div(database, table):
Exemple #27
0
def build_callbacks(app):
    @app.callback(
        [
            dd.Output(component_id='cncs-fwhm_vs_Ei',
                      component_property='figure'),
            dd.Output(component_id='cncs-flux_vs_Ei',
                      component_property='figure'),
        ],
        [
            dd.Input('cncs_chopper_mode', 'value'),
        ],
    )
    def update_figures(chopper):
        if chopper == 'all':
            fwhm_data = sum(
                [getFWHM_vs_Ei(chopper) for chopper in chopper_modes], [])
            flux_data = sum(
                [getFlux_vs_Ei(chopper) for chopper in chopper_modes], [])
        else:
            fwhm_data = getFWHM_vs_Ei(chopper)
            flux_data = getFlux_vs_Ei(chopper)
        return [
            {
                'data':
                fwhm_data,
                'layout':
                dict(
                    title='Resolution vs incident energy',
                    showlegend=True,
                    xaxis=dict(
                        title='Ei (meV)',
                        type='log',
                        showspikes=True,
                    ),
                    yaxis=dict(
                        title='FWHM (meV)',
                        type='log',
                        showspikes=True,
                    ),
                ),
            },
            {
                'data':
                flux_data,
                'layout':
                dict(
                    title='Flux vs incident energy',
                    showlegend=True,
                    xaxis=dict(
                        title='Ei (meV)',
                        type='log',
                        showspikes=True,
                    ),
                    yaxis=dict(
                        title='Flux (counts/s/cm^2/MW)',
                        type='log',
                        showspikes=True,
                    ),
                ),
            },
        ]

    return
Exemple #28
0
app.css.append_css({"external_url": "/css/my.css"})
app.css.append_css(
    {"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
#app.css.append_css({'external_url': 'https://cdn.rawgit.com/plotly/dash-app-stylesheets/2d266c578d2a6e8850ebce48fdb52759b2aef506/stylesheet-oil-and-gas.css'})  # noqa: E501

app.layout = html.Div(children=[
    html.Div([html.Button(id='button', n_clicks=0, children='STARTING...')],
             style={'color': 'white'}),
    html.Div([dcc.Graph(id='graph_one', figure={})],
             style={'display': 'inline-block'}),
    dcc.Interval(id='live-update', interval=1000),
],
                      style={'background-color': "#F0F0F0"})


@app.callback(dep.Output('graph_one',
                         'figure'), [], [dep.State('graph_one', 'figure')],
              [dep.Event('live-update', 'interval')])
def do_plot(figure):
    df = pd.read_csv('/tmp/mon.csv')

    plt_w = uj2w(df.time, df.cpu_uj)
    sys_w = uj2w(df.time, df.sys_uj) * 2

    t = df.time - df.time[0]

    y_data = (
        df.v0 / 1e6,
        df.v1 / 1e6,
        df.i0 / 1e6,
        df.i1 / 1e6,
        df.i0 * df.v0 / 1e12,
def get_all_msg_dataframe(session_id, data):
    @ cache.memoize()
    def calculate_all_msg_dataframe(session_id, data):
        print('starting all msg 2')

        list_of_dfs = [msg_fx.get_msg_df(msg_dict) for msg_dict in data["Messages"]]
        print("Working on msg df "+ str(datetime.datetime.now()))
        all_msg_df = pd.concat(list_of_dfs, axis=0, sort=True)
        all_msg_df['date'] = all_msg_df['sent_date'].dt.date
        print('error in json')
        return(all_msg_df.reset_index().to_json())
    return(pd.read_json(calculate_all_msg_dataframe(session_id, data)))

@app.callback([
                dd.Output('usage_hidden', 'children'),
                dd.Output('all_msg_hidden', 'children')
                ],
              [dd.Input('upload-data', 'contents', ),
               dd.Input('upload-data', 'filename'),
               dd.Input('session-id', 'children')])
def open_file(data, filename, session_id):
    print("started open file " + str(datetime.datetime.now()))
    if data is not None:
        content_type, content_string = data.split(',')
        if '.json' in filename:
            ret_val = base64.b64decode(content_string)
            data = json.load(io.BytesIO(ret_val))
        else:
            return(html.H1(children='File not a json'))
Exemple #30
0
def register_app_callbacks(app:dash.Dash) -> None:

    @app.callback(
        ddp.Output("rubik-button-reset", "n_clicks"),
        [ddp.Input("tabs-projects", "value")],
        [ddp.State("rubik-button-reset", "n_clicks")],
    )
    def click_reset(tab:str, n_clicks:int) -> int:
        if tab != "rubik" or n_clicks > 0:
            raise dex.PreventUpdate
        return n_clicks + 1

    @app.callback(
        [
            ddp.Output("rubik-select-layer", "options"),
            ddp.Output("rubik-select-layer", "value"),
            ddp.Output("rubik-store-state", "data"),
            ddp.Output("rubik-table-history", "data"),
        ],
        [
            ddp.Input("rubik-button-clear", "n_clicks"),
            ddp.Input("rubik-button-reset", "n_clicks"),
            ddp.Input("rubik-button-scramble", "n_clicks"),
            ddp.Input("rubik-button-solve", "n_clicks"),
            ddp.Input("rubik-graph-state", "clickData"),
            ddp.Input("rubik-table-history", "rowClicked"),
            ddp.Input("rubik-select-dim", "value"),
        ],
        [
            ddp.State("rubik-select-layer", "value"),
            ddp.State("rubik-select-scramble", "value"),
            ddp.State("rubik-store-state", "data"),
            ddp.State("rubik-table-history", "data"),
        ],
    )
    def set_cube_state(*args:list) -> tp.Tuple[object, ...]:
        trigger = dash.callback_context.triggered[0]
        if not trigger["value"] or trigger["prop_id"].endswith("clear.n_clicks"):
            # Clear cube.
            return constants.EMPTY_OPTIONS, "", [], [], 

        # Unpack arguments and enforce data types.
        *_, dim, layer, scramble, state, history = args
        dim = int(dim)
        layer = int(layer) if layer else 1
        scramble = int(scramble)
        
        options = [
            {"label":f"Rotate {hu.ordinal(layer)} layer", "value":layer}
            for layer in range(1, 1+dim)
        ]
        layer = min(layer, dim)

        if trigger["prop_id"].endswith("reset.n_clicks") or trigger["prop_id"].endswith("dim.value"):
            # Load new cube.
            cube = RubiksCube(dim=dim, state=None)
            return options, layer, cube.get_state(), []

        if trigger["prop_id"].endswith("history.rowClicked"):
            # Revert cube.
            i = trigger["value"]["i"]
            history = [move for move in history if move["i"]<i]
            cube = RubiksCube(dim=dim, state=None)
            rotations = [move["move"] for move in history]
            cube.rotate(rotations=rotations)
            return options, layer, cube.get_state(), history

        cube = RubiksCube(dim=dim, state=state)
        
        if trigger["prop_id"].endswith("scramble.n_clicks"):
            # Scramble cube.
            rotations = cube.scramble(n=scramble)

        if trigger["prop_id"].endswith("solve.n_clicks"):
            # Solve cube.
            rotations = cube.solve()

        if trigger["prop_id"].endswith("state.clickData"):
            # Rotate face of cube.
            faces, axes = zip(*RubiksCube._face_axes.items())
            point = trigger["value"]["points"][0]
            coord = {axis:point[axis] for axis in ["x", "y", "z"]}
            axis = max(coord.keys(), key=lambda axis:abs(coord[axis]))
            axis += "+" if coord[axis]>0 else "-"
            face = list(faces)[list(axes).index(axis)]
            rotations = f"{layer}{face}"
            rotations = cube.rotate(rotations=rotations)

        n = len(history)
        history.extend({"move":rotation, "i":n+i} for i, rotation in enumerate(rotations))
        return options, layer, cube.get_state(), history
        
    @app.callback(
        ddp.Output("rubik-graph-state", "figure"),
        [ddp.Input("rubik-store-state", "data")],
        [ddp.State("rubik-select-dim", "value")],
    )
    def graph_cube_state(state:list, dim:str) -> dict:
        if not state:
            return empty_cube_figure
        dim = int(dim)
        cube = RubiksCube(dim=dim, state=state)
        figure = cube.get_figure()
        for trace in figure["data"]:
            # Disable hover information.
            trace.update({
                "hoverinfo":"none" if trace["type"]=="mesh3d" else "skip",
                "hovertemplate":"",
                "name":None,
            })
        figure["layout"].update({
            **empty_cube_figure["layout"],
            # Remove center annotation.
            "annotations":empty_cube_figure["layout"]["annotations"][1:],
        })
        return figure

    @app.callback(
        [
            ddp.Output("rubik-button-scramble", "disabled"),
            ddp.Output("rubik-button-solve", "disabled"),
        ],
        [ddp.Input("rubik-store-state", "data")],
        [ddp.State("rubik-select-dim", "value")],
    )
    def disable_buttons(state:tp.List[str], dim:str) -> tp.Tuple[bool, bool]:
        if not state or not dim:
            return True, True
        if int(dim) not in RubiksCube._solvable_dims:
            return False, True
        return False, False