Uploading files to Dash App

Hello!
I work in the AEC Industry but am learning programming in my free time. I have an idea for a web-app project that I am passionate about, but I need some guidance on how to execute it efficiently.

In the AEC Industry we use 3D Models t exchange data. The main 3D Model exchange format is .ifc. The software’s used to extract data from the model cost a lot and are not that user friendly.

I have created a python script using the ifcopenshell module that extracts the data, turns it into pandas, cleans it a little bit and then exports it in form of an excel. Every user can then easily filter and use the data in excel for his use case.

So, the idea is to deploy this functionality on a web site. The user uploads his model and then downloads the excel file. That was the main idea. But then I thought it would be cool to use this data to create some basic data visualizations in the web browser so the user can get a broad visual insight into his data.

I have already created the python script that extracts the data and did the data visualizations with the plotly package. Now I “only” need to patch it all together and deploy online.

So, the question is can I do all this only with Dash? Or do I have to combine it with Django or Flask. Is it possible to create a Django App that accepts uploaded files, parses them with another python package and that visualzies the data?

And yeah if someone knows a tutorials that builds something similar please refer me to it.

Thank a lot!

It sounds like everything that you’re interested in would be possible with Dash. Dash does have an upload component that makes uploading files to your web app pretty easy. See: dcc.Upload | Dash for Python Documentation | Plotly

Sounds interesting, let me know if you have any other questions!

Thanks for your input Jwolf!

I started tinkering with the code, but I have to learn the fundamentals first to be able to ask a meaningful question :smiley:

One general question poped up, is there a limit to the file size that can be uploaded with the upload component?

@Borna_Molnar

No there isn’t any file size limit but larger files can slow down your app as the files need to be parsed at server side.
Alternatively you can use dash-uploader which can easily handle large files and also provides a progress bar for file upload.

2 Likes

Cool tip! Thanks a bunch

I am stuck at the first step. I cant seem to be able to open ifc files

Normally I need to feed a path to the method that opens ifc files, but I am not sure how to get the path from the upload component. As far as I understand the pd.read_csv and pd_read_excel functions also require a path but in the dash upload example they work with something else?

I have tried to input the same variables used to open csv and excel but it did not work.

I tried to input contents and contents_string but then I get an error: “Unable to open file for reading”

import base64
import datetime
import io

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import ifcopenshell as ifc

from Functions import *

import pandas as pd


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    html.Div(id='output-data-upload'),
])


def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            message = "YAAAAAAAY"
            # Assume that the user uploaded a CSV file
            # dfa = pd.read_csv(
            #     io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            message = "YAAAAAAAY"
            # Assume that the user uploaded an excel file
            #dfa = pd.read_excel(io.BytesIO(decoded))
        elif '.Ifc'.lower() in filename.lower():
            df = ifc.open(contents)
            message = "YAAAAAAAY"

    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return html.Div([
        html.H5(message)
    ])


@app.callback(Output('output-data-upload', 'children'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children



if __name__ == '__main__':
    app.run_server(debug=True)



Any ides?

@atharvakatre
@Jwolf

I manage to read the file but as soon as I try to do something with the file using the ifc module the server crashes with the error message:

Callback failed: the server did not respond.

(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser’s console.)

Where exactly do I have to click to see the full stack trace? I open the browser console and there is nothing there…

import base64
import datetime
import io

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import ifcopenshell as ifc



from Functions import *

import pandas as pd


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets, prevent_initial_callbacks=True)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    html.Div(id='output-data-upload'),
])


def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            message = "YAAAAAAAY"
            # Assume that the user uploaded a CSV file
            dfa = pd.read_csv(
                 io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            message = "YAAAAAAAY" 
            # Assume that the user uploaded an excel file
            dfa = pd.read_excel(io.BytesIO(decoded))
        elif '.Ifc'.lower() in filename.lower():
            ifc_file = ifc.file.from_string(contents)
            #test = ifc_file.schema
            #instances = ifc_file.by_type("IfcBuildingElement")
            project = ifc_file.by_type("IfcProject")[0].Name

    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return html.Div([
        html.H5(str(type(ifc_file)))
    ])


@app.callback(Output('output-data-upload', 'children'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'),prevent_initial_call=True)
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children



if __name__ == '__main__':
    app.run_server(debug=True)

What am I missing?

@Jwolf could you maybe help me out?