A button to upload csv file and plot it

Hi,

I’m trying to create a panel which i’ll be running on a local server (with panel serve “name.ipynb” --port —>open in browser)

I want to have a FileInput button and a figure under it which will update according to file uploaded.
the process is as follows: click on upload button->choose csv file->csv processing->plot results.
I need the csv data to be available for further operations.
i’ve managed to do this with Dash but as far as i can tell, the dash api lacks the option to get current state of a legend in graph (specifically, i need to know which plots are hidden and which plots are shown)

I tried different implementations methods and callbacks with panel but none is working.
can someone provide a working example of such button please?

below is a working code i’ve written with dash. this is the desired behaviour i want to implement with panel.

import plotly.offline as pyo
import plotly.graph_objs as go
import pandas as pd
import base64
import io
from dash.dependencies import Input, Output, State
import dash
import dash_core_components as dcc
import dash_html_components as html
import threading
from test_ui import Ui_MainWindow


def run_dash():
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    app.layout = html.Div(children=[
        html.H1(children='Hello Dash'),
        html.Div(children='''
            Dash: A web application framework for Python.
        '''),
        dcc.Upload(
            id='upload',
            children=html.Div([
                'Drag and Drop or ',
                html.A('Select a File')
            ]),
            style={
                'width': '100%',
                'height': '60px',
                'lineHeight': '60px',
                'borderWidth': '1px',
                'borderStyle': 'dashed',
                'borderRadius': '5px',
                'textAlign': 'center',
                'margin': '10px'
            }
        )
        ,
        dcc.Graph(
            id='example-graph',
        )

    ])
    return app


def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])
    return df


def setupFilteredDataframe(df):
    assert ("Voltage" in df.columns) and ("Time" in df.columns), "no columns voltage and time"
    df = (df.loc[df['Voltage'] != 'Invalid/Calib']).copy(deep=True)
    df['Voltage'] = df['Voltage'].apply(lambda x: float(x))
    df.reset_index()

    traces = []

    if "FubId" in df.columns:
        """filter it to contain measurments from a single vdm.
        then set the transformed and filtered data for next steps"""
        df = df[['FubId', 'Time', 'Voltage']]
        fubIds = df['FubId'].copy(deep=True)
        fubIds = fubIds.drop_duplicates()
        lFubIds = fubIds.tolist()

        for fub in lFubIds:
            curr_df = df.loc[df['FubId'] == fub].copy(deep=True)
            traces.append(go.Scatter(
                x=curr_df['Time'],
                y=curr_df['Voltage'],
                mode='lines',
                name=fub
            ))
    else:
        curr_df = df
        traces.append(go.Scatter(
            x=curr_df['Time'],
            y=curr_df['Voltage'],
            mode='lines',
            name='Waveform'))
    return traces

def printAppLayout():
    print(app.layout)



app = run_dash()
# callback figure creation
@app.callback(Output('example-graph', 'figure'),
              [Input('upload', 'contents')],
              [State('upload', 'filename')])
def update_output(contents, filename):
    if contents is not None:
        dff = parse_contents(contents, filename)
        print(dff.head())
        data = setupFilteredDataframe(dff)
        return {
            'data': data,
            'layout': dict(
                title='Time series with range slider and selectors',
                xaxis=dict(rangeslider=dict(visible=True)))
        }
    else:
        return {}


if __name__ == '__main__':

    app.run_server(debug=True,port=3003)

Hi @avivazran

Welcome to the community. I’ve started creating an example for you. You can find it here

I think you can get the inspiration you need now. I will finish it and nicefy it later. I’ve filed a pull request on it here https://github.com/holoviz/panel/pull/1193

1 Like

I have cleaned up the example and moved the notebook on request of Philipp.

I’ve added a show button to the app so to start running it in the server.

I’ve shared a tweet. Feel free to share if you like https://twitter.com/MarcSkovMadsen/status/1243405883175911426?s=20