最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is there a way to save a dash app to an offline html file, while preserving all interactions (hover, checkbox filte

programmeradmin2浏览0评论

I've tried javascript code, but I'm not familiar with JS and the filtering is not working properly. fig.write_html doesn't work because the checkboxes will not show up.

For context, I have 2 main traces on my graph, go.Scatter and px.timeline. The checkbox will filter which medications show up on the y-axis, and remove/add the scatter points and horizontal bars accordingly.

server = Flask(__name__)
app = Dash(__name__, server = server)

ipharm_meds = list(ipharm['meds'].unique())
eimr_meds = list(eimr['meds'].unique())
unique_meds = list(set(ipharm_meds + eimr_meds))

'''
wrapping text so that it can appear nicely on graph on hover
need to play with width value according to the length of text
'''

wrapper = textwrap.TextWrapper(width = 100)
notes['summary'] = notes['summary'].apply(lambda x: wrapper.fill(x))
notes['summary'] = notes['summary'].apply(lambda x: x.replace('\n', '<br>'))

app.layout = html.Div([
    html.Div([
        dcc.Checklist(
            id = 'med-checklist',
            options = [{'label': med, 'value': med} for med in unique_meds],
            value = unique_meds,
            inline = True
        ),
    ], style = {'width': '100%', 'padding': '10px'}),
    dcc.Graph(id = 'timeline-graph')
])

@app.callback(
    Output('timeline-graph', 'figure'),
    Input('med-checklist', 'value')
)

def update_graph(selected_meds):

    # filtered based on checkbox values
    ipharm_filtered = ipharm[ipharm['meds'].isin(selected_meds)]
    ipharm_filtered['Start'] = pd.to_datetime(ipharm_filtered['Start'])
    ipharm_filtered['End'] = pd.to_datetime(ipharm_filtered['End'])
    ipharm_filtered['Start_str'] = ipharm_filtered['Start'].dt.strftime("%d-%m-%Y")
    ipharm_filtered['End_str'] = ipharm_filtered['End'].dt.strftime("%d-%m-%Y")

    # broken barh for ipharm
    fig = px.timeline(
        ipharm_filtered,
        x_start = 'Start',
        x_end = 'End',
        y = 'meds',
        hover_data = {'Start': False,
        'End': False,
        'Start_str': True,
        'End_str': True,
        'FREQUENCY': True,
        'DOSAGE INSTRUCTION': True,
        'dose': True}
    )

    # Add scatter for eimr
    for med in selected_meds:
        med_data = eimr[eimr['meds'] == med]
        if not med_data.empty:
            fig.add_trace(go.Scatter(
                x=med_data['ServingDateTime'],
                y=[med] * len(med_data),
                mode='markers',
                marker=dict(size=5, color = 'red'),
                name=f'eIMR_{med}',
                text=med_data['Dose'],
                hoverinfo='text',
                showlegend=False
                )
            )

    # Add shaded regions for ward duration
    for _, row in ward.iterrows():
        fig.add_vrect(
            x0=row['Adm.Date'],
            x1=row['Disch.Date'],
            fillcolor='grey',
            opacity=0.5,
            line_width=0
            )

    # Add vertical lines for consults
    for _, row in notes.iterrows():
        fig.add_shape(
            type = 'line',
            x0 = row['Date'],
            x1 = row['Date'],
            y0 = 0,
            y1 = 1,
            yref = 'paper',
            line = dict(color = 'black', width = 1)
        )

        # Add scatter points vertically on every vline
        # for hover functionality
        for med in selected_meds:
            fig.add_trace(go.Scatter(
                x = [row['Date']],
                y = [med],
                opacity = 0, # make points invisible
                mode = 'markers',
                marker = dict(size = 5, color = 'black'),
                text = row['summary'],
                hoverinfo = 'text',
                showlegend=False
            ))

    # Update layout
    fig.update_layout(
        xaxis=dict(showgrid=True),
        yaxis=dict(showgrid=True)
    )

    return fig

def save_html():
    # Create the initial figure with all medications

    initial_fig = update_graph(unique_meds)

    # Create JavaScript function for checkbox interactivity
    checkbox_js = """
    <script>
    function updateVisibility() {
        var checkboxes = document.getElementsByClassName('med-checkbox');
        var gd = document.getElementById('timeline-graph');
        var traces = gd.data;
        var selectedMeds = Array.from(checkboxes)
            .filter(cb => cb.checked)
            .map(cb => cb.value);
        var visibility = traces.map(trace => {
            if (trace.y && trace.y.length > 0) {
                return selectedMeds.includes(trace.y[0]) ||
                        selectedMeds.includes(trace.name?.replace('eIMR_', ''));
            }
            return false;
        });
        Plotly.update('timeline-graph',
            {visible: visibility},
            {height: selectedMeds.length * 40},
                
        );
    }

    window.addEventListener('load', function() {
        var gd = document.getElementById('timeline-graph');
        window.originalData = JSON.parse(JSON.stringify(gd.data));
    });
    </script>
    """

   Create HTML string with checkboxes
    checkbox_html = """
    <div style="width:100%; padding:10px">
    """

    for med in unique_meds:
        checkbox_html += f"""
    <label style="margin-right:10px">
    <input type="checkbox" class="med-checkbox" value="{med}"
                    checked onclick="updateVisibility()">
            {med}
    </label>
        """

    checkbox_html += "</div>"

   # Convert figure to HTML
    fig_html = initial_fig.to_html(
        full_html=False,
        include_plotlyjs=True,
        div_id='timeline-graph'
    )

   # Combine everything
    full_html = f"""
    <html>
    <head>
    <title>Medication Timeline</title>
        {checkbox_js}
    </head>
    <body>
        {checkbox_html}
        {fig_html}
    </body>
    </html>
    """
    with open('graph_v1.html', 'w', encoding = 'utf-8') as f:
        f.write(full_html)

if __name__ == "__main__":
    save_html()

I've tried javascript code, but I'm not familiar with JS and the filtering is not working properly. fig.write_html doesn't work because the checkboxes will not show up.

For context, I have 2 main traces on my graph, go.Scatter and px.timeline. The checkbox will filter which medications show up on the y-axis, and remove/add the scatter points and horizontal bars accordingly.

server = Flask(__name__)
app = Dash(__name__, server = server)

ipharm_meds = list(ipharm['meds'].unique())
eimr_meds = list(eimr['meds'].unique())
unique_meds = list(set(ipharm_meds + eimr_meds))

'''
wrapping text so that it can appear nicely on graph on hover
need to play with width value according to the length of text
'''

wrapper = textwrap.TextWrapper(width = 100)
notes['summary'] = notes['summary'].apply(lambda x: wrapper.fill(x))
notes['summary'] = notes['summary'].apply(lambda x: x.replace('\n', '<br>'))

app.layout = html.Div([
    html.Div([
        dcc.Checklist(
            id = 'med-checklist',
            options = [{'label': med, 'value': med} for med in unique_meds],
            value = unique_meds,
            inline = True
        ),
    ], style = {'width': '100%', 'padding': '10px'}),
    dcc.Graph(id = 'timeline-graph')
])

@app.callback(
    Output('timeline-graph', 'figure'),
    Input('med-checklist', 'value')
)

def update_graph(selected_meds):

    # filtered based on checkbox values
    ipharm_filtered = ipharm[ipharm['meds'].isin(selected_meds)]
    ipharm_filtered['Start'] = pd.to_datetime(ipharm_filtered['Start'])
    ipharm_filtered['End'] = pd.to_datetime(ipharm_filtered['End'])
    ipharm_filtered['Start_str'] = ipharm_filtered['Start'].dt.strftime("%d-%m-%Y")
    ipharm_filtered['End_str'] = ipharm_filtered['End'].dt.strftime("%d-%m-%Y")

    # broken barh for ipharm
    fig = px.timeline(
        ipharm_filtered,
        x_start = 'Start',
        x_end = 'End',
        y = 'meds',
        hover_data = {'Start': False,
        'End': False,
        'Start_str': True,
        'End_str': True,
        'FREQUENCY': True,
        'DOSAGE INSTRUCTION': True,
        'dose': True}
    )

    # Add scatter for eimr
    for med in selected_meds:
        med_data = eimr[eimr['meds'] == med]
        if not med_data.empty:
            fig.add_trace(go.Scatter(
                x=med_data['ServingDateTime'],
                y=[med] * len(med_data),
                mode='markers',
                marker=dict(size=5, color = 'red'),
                name=f'eIMR_{med}',
                text=med_data['Dose'],
                hoverinfo='text',
                showlegend=False
                )
            )

    # Add shaded regions for ward duration
    for _, row in ward.iterrows():
        fig.add_vrect(
            x0=row['Adm.Date'],
            x1=row['Disch.Date'],
            fillcolor='grey',
            opacity=0.5,
            line_width=0
            )

    # Add vertical lines for consults
    for _, row in notes.iterrows():
        fig.add_shape(
            type = 'line',
            x0 = row['Date'],
            x1 = row['Date'],
            y0 = 0,
            y1 = 1,
            yref = 'paper',
            line = dict(color = 'black', width = 1)
        )

        # Add scatter points vertically on every vline
        # for hover functionality
        for med in selected_meds:
            fig.add_trace(go.Scatter(
                x = [row['Date']],
                y = [med],
                opacity = 0, # make points invisible
                mode = 'markers',
                marker = dict(size = 5, color = 'black'),
                text = row['summary'],
                hoverinfo = 'text',
                showlegend=False
            ))

    # Update layout
    fig.update_layout(
        xaxis=dict(showgrid=True),
        yaxis=dict(showgrid=True)
    )

    return fig

def save_html():
    # Create the initial figure with all medications

    initial_fig = update_graph(unique_meds)

    # Create JavaScript function for checkbox interactivity
    checkbox_js = """
    <script>
    function updateVisibility() {
        var checkboxes = document.getElementsByClassName('med-checkbox');
        var gd = document.getElementById('timeline-graph');
        var traces = gd.data;
        var selectedMeds = Array.from(checkboxes)
            .filter(cb => cb.checked)
            .map(cb => cb.value);
        var visibility = traces.map(trace => {
            if (trace.y && trace.y.length > 0) {
                return selectedMeds.includes(trace.y[0]) ||
                        selectedMeds.includes(trace.name?.replace('eIMR_', ''));
            }
            return false;
        });
        Plotly.update('timeline-graph',
            {visible: visibility},
            {height: selectedMeds.length * 40},
                
        );
    }

    window.addEventListener('load', function() {
        var gd = document.getElementById('timeline-graph');
        window.originalData = JSON.parse(JSON.stringify(gd.data));
    });
    </script>
    """

   Create HTML string with checkboxes
    checkbox_html = """
    <div style="width:100%; padding:10px">
    """

    for med in unique_meds:
        checkbox_html += f"""
    <label style="margin-right:10px">
    <input type="checkbox" class="med-checkbox" value="{med}"
                    checked onclick="updateVisibility()">
            {med}
    </label>
        """

    checkbox_html += "</div>"

   # Convert figure to HTML
    fig_html = initial_fig.to_html(
        full_html=False,
        include_plotlyjs=True,
        div_id='timeline-graph'
    )

   # Combine everything
    full_html = f"""
    <html>
    <head>
    <title>Medication Timeline</title>
        {checkbox_js}
    </head>
    <body>
        {checkbox_html}
        {fig_html}
    </body>
    </html>
    """
    with open('graph_v1.html', 'w', encoding = 'utf-8') as f:
        f.write(full_html)

if __name__ == "__main__":
    save_html()
Share Improve this question edited Mar 6 at 3:36 Noel Teo asked Mar 6 at 3:32 Noel TeoNoel Teo 32 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

The callbacks rely on a flask server backend which means you won't be able to save anything besides the initial state of your app.

Credit goes to this answer by @Emil on the plotly community form.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论