Dash_table displaying blank rows

I’m creating a dash app that displays a data table that gets updated when a date time picker value gets updated, the rows are retrieved successfully apparently, but they are displayed in blank as shown below:


# Inicializa la aplicación web
app = Dash('BI-UYEDA')

# Define las hojas de estilos externas a utilizar   
external_css = [
    "https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css",  # Normalize the CSS
    "https://fonts.googleapis.com/css?family=Open+Sans|Roboto",  # Fonts
    "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css",
    "https://cdn.rawgit.com/TahiriNadia/styles/faf8c1c3/stylesheet.css",
    "https://cdn.rawgit.com/TahiriNadia/styles/b1026938/custum-styles_phyloapp.css"
]

# Añade las hojas de estilos a la aplicación
for css in external_css:
    app.css.append_css({"external_url": css})

server = app.server

# Define la estructura de la aplicación
app.layout = Div([
    Div([
        H2("Inteligencia de negocios UYEDA"),
        Img(src="some_url")],
        className="banner"),
    Div([
        Div([
        ], style={
            'margin': '5px',
            'text-align': 'center',
            'vertical-align': 'middle',
            'width': '48%',
            'display': 'inline-block'
        }),
        Div([
            DatePickerRange(
                id='fechas',
                start_date=str((datetime.today() - timedelta(1)).strftime('%Y-%m-%d')),
                end_date=str((datetime.today() - timedelta(1)).strftime('%Y-%m-%d')),
                display_format='DD/MM/YYYY',
                max_date_allowed=str((datetime.today() - timedelta(1)).strftime('%Y-%m-%d')),
                min_date_allowed='2018/01/01'
            ),
        ], style={
            'margin': '5px',
            'text-align': 'center',
            'vertical-align': 'middle',
            'width': '48%',
            'display': 'inline-block'
        })

    ],
        className="container"
    ),
    Div([
        DataTable(id='table',
                  columns=[{'name': i, 'id': i} for i in df.columns],
                  data=[{}], #df.to_dict('rows'),
                  sorting=True,
                  style_table={'overflowX': 'scroll'},
                  style_cell={'minWidth': '0px',
                              'maxWidth': '180px',
                              'whiteSpace': 'normal'},
                  css=[{
                      'selector': '.dash-cell div.dash-cell-value',
                      'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
                  }],
                  virtualization=True,
                  pagination_mode=True,
                  n_fixed_rows=1,
                  merge_duplicate_headers=True
                  )
    ],
        className="container")
])


# Crea las llamadas a la aplicación para actualizar las filas
@app.callback(
    Output('table', 'data'),
    [Input('fechas', 'start_date'),
     Input('fechas', 'end_date')]
)
# Crea la función que actualiza las filas de la tabla
def act_filas(start_date, end_date):
    """
    Esta función actualiza las filas de un datatable
    :param start_date: Cadena de caracteres que indica la fecha inicial del reporte
    :param end_date: Cadena de caracteres que indica la fehca final del reporte
    :return: Retorna las filas que actualizaran las filas del datatable
    """
    # crea las variables necesarias para conectarse al servicio web de future time
    url = 'some_url'
    usr = 'some_usr'
    pwd = 'some_pwd'
    pwdh = pwd.encode()
    ts = str(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))  # ; del datetime
    msg = (usr + pwd + ts).encode()

    # crea la firma hash HMAC MD5 utilizando la contraseña como llave y la guarda en la variable digest
    digest = new(pwdh, msg, md5).hexdigest()

    # Crea los argumentos necesarios para acceder al webservice y los asigna a la variable argumentos
    arg = {'LoginName': usr,
           'Signature': digest,
           'TimeStamp': ts}

    # Establece la conexión al web service, utiliza el recurso /System/LoginUser y autentica al usuario,
    # guarda en la variable r el resultado de la conexión.
    r = post(url, arg)

    # Obtiene el UserID
    UserId = str(r.json()['Result'][11:13])

    # Crea las variable necesarias para consumir el servicio de Working Day
    url = 'some_url'
    Keywords = ''
    StartDate = start_date + ' 00:00:00'
    EndDate = end_date + ' 23:59:59'
    Filter = 'todos'
    OrderBy = ''

    # Construye el nuevo mensaje a ser firmado
    msg = (Keywords + StartDate + EndDate + Filter + OrderBy + UserId).encode()

    # Crea la firma hash HMAC MD5
    digest = new(pwdh, msg, md5).hexdigest()  # ; del new, pwdh, msg, md5

    # Crea los argumentos necesarios para acceder al webservice y los asigna a la variable argumentos
    arg = {'Keywords': '',
           'StartDate': StartDate,
           'EndDate': EndDate,
           'Filter': Filter,
           'OrderBy': '',
           'UserID': UserId,
           'Signature': digest}  # ; del Keywords, StartDate, EndDate, Filter, OrderBy, UserId, digest

    # Invoca mediante get el recurso /WorkingDay/Get
    r = get(url, arg)  # ; del url, arg

    # Convierte la cadena json del webservice a dataframe de pandas
    df = read_json(r.json()['Result'])  # ; del r; read_json

    # Reemplaza los valores nulos por caracter vacio
    df.fillna('', None, None, True)

    # Descarta los registros correspondientes a practicantes, del cómputo de faltas
    df.drop(df[df['TimeGroup'] == 'Practicantes'].index, inplace=True)

    # Descarta los registros correspondientes a horarios libres, del cómputo de faltas
    df.drop(df[df['TimeGroup'] == 'Libre'].index, inplace=True)

    # Descarta los registros correspondientes a descansos, del cómputo de faltas
    df.drop(df[df['ProjectedTimeToWork'].str.contains('00:00:00')].index, inplace=True)

    # Descarta los registros correspondientes a trabajadores borrados, del cómputo de faltas
    df.drop(df[df['Name'].str.contains('Borrado')].index, inplace=True)

    # Descarta los registros correspondientes a visitantes, del cómputo de faltas
    df.drop(df[df['Name'].str.contains('Visitante')].index, inplace=True)

    # Descarta los registros correspondientes a personal directivo, del cómputo de faltas
    df.drop(df[df['Name'].str.contains('Director')].index, inplace=True)

    # Descarta los registros correspondientes a trabajadores borrados, del cómputo de faltas
    df.drop(df[df['Name'].str.contains('New node')].index, inplace=True)

    # Descarta los registros correspondientes a personal de soporte y pruebas, del cómputo de faltas
    df.drop(df[df['Name'].str.contains('Soporte y Pruebas')].index, inplace=True)

    # Descarta los registros correspondientes a Incapacidad IMSS Riesgo de Trabajo, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('[057]')].index, inplace=True)

    # Descarta los registros correspondientes a permiso sin goce de sueldo 069, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('Permiso Sin Goce de Sueldo')].index, inplace=True)

    # Descarta los registros correspondientes a Día de castigo 069, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('Día de castigo')].index, inplace=True)

    # Descarta los registros correspondientes a vacaciones, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('[801]')].index, inplace=True)

    # Descarta los registros correspondientes a falta con goce de sueldo 800, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('falta con goce de sueldo')].index, inplace=True)

    # Descarta los registros correspondientes a dias feriados, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('Dia No Laborable General de Ley día feriado')].index, inplace=True)

    # Descarta los registros correspondientes a Incapacidad IMSS Enfermedad General 060, del cómputo de faltas
    df.drop(df[df['Exceptions'].str.contains('Incapacidad IMSS Enfermedad General')].index, inplace=True)

    # Descarta los registros correspondientes a omisión de registro de entrada
    df.drop(df[df['InHour'].str[11:16] > '00:00'].index, inplace=True)

    # Filtra las columnas necesarias para el reporte de faltas injustificadas
    df = df.filter(items=['ExportId', 'Name', 'OU5', 'Date', 'TimeGroup', 'Exceptions'])

    # Renombra las columnas del reporte
    df.columns = ['Código de empleado', 'Nombre del empleado',
                  'Departamento', 'Fecha', 'Horario', 'Excepción']

    # Envía los resultados del DataFrame a las filas del data table
    data = [{'name': i, 'id': i} for i in df.to_dict('rows')]

    # Retorna los registros para actualizar la tabla
    return data


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

This are my components for the dash app:


certifi==2018.11.29
chardet==3.0.4
Click==7.0
Cython==0.29.4
dash==0.36.0
dash-core-components==0.43.0
dash-html-components==0.13.5
dash-renderer==0.17.0
dash-table==3.3.0
decorator==4.3.2
Flask==1.0.2
Flask-Compress==1.4.0
gunicorn==19.9.0
idna==2.8
ipython-genutils==0.2.0
itsdangerous==1.1.0
Jinja2==2.10
jsonschema==2.6.0
jupyter-core==4.4.0
lxml==4.3.0
MarkupSafe==1.1.0
nbformat==4.4.0
numpy==1.16.1
pandas==0.24.1
pandas-datareader==0.7.0
plotly==3.6.0
python-dateutil==2.8.0
pytz==2018.9
requests==2.21.0
retrying==1.3.3
six==1.12.0
traitlets==4.3.2
urllib3==1.24.1
Werkzeug==0.14.1
wrapt==1.11.1

What could be causing this behavior?