Not able to implement dynamic dropdown properly

For the below code update and clear button are not working if i select any course other than Initial state

I have been trying to figure out a solution for almost a week cananyone help me with this.

import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_auth
import pandas as pd

VALID_USERNAME_PASSWORD_PAIRS = {    
    'admin': 'password'}

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

auth = dash_auth.BasicAuth(    
    app,    
    VALID_USERNAME_PASSWORD_PAIRS)

tasks = pd.read_csv('tasks.csv')
courses = tasks['Course'].unique()
selected_tasks = tasks[tasks['Course'] == courses[0]]
course = courses[0]

app.layout = html.Div([
    html.H1('Task Management System', className='text-center mt-5'),
    html.Div([
        dcc.Dropdown(
            id='course-dropdown',
            options=[{'label': course, 'value': course} for course in courses],
            value=courses[0]
        )
    ], className='mb-3'),
    html.Div(id='table-container'),
    dbc.Button('Update Progress', id='update-button', className="btn btn-success float-start", n_clicks=None),
    dbc.Button('Clear Progress', id='clear-button', className="btn btn-danger float-end",n_clicks=None)
], className='container')

@app.callback(
    dash.dependencies.Output('table-container', 'children'),
    [dash.dependencies.Input('course-dropdown', 'value')]
)
def display_tasks(tmp):
    global course
    global selected_tasks
    course = tmp
    selected_tasks = tasks[tasks['Course'] == course]
    return dbc.Table([
        html.Thead([html.Tr([html.Th('Task'), html.Th('Completion')])]),
        html.Tbody([
            html.Tr([html.Td(task),
                     html.Td(dcc.Slider(id=f"{course}-{task}", value=value, min=0, max=3, step=1, marks={0:'yet to start', 1:'completed learning', 2:'completed practice', 3:'completed tests'}))])
            for task, value in zip(selected_tasks['Task'], selected_tasks['Completion'])
        ])
    ], className='mt-3 mb-3')

@app.callback(
[dash.dependencies.Output(f"{course}-{task}", 'value') for course, task in zip(selected_tasks['Course'], selected_tasks['Task'])],
[dash.dependencies.Input('course-dropdown', 'value'),
dash.dependencies.Input('update-button', 'n_clicks'),
dash.dependencies.Input('clear-button', 'n_clicks')],
[dash.dependencies.State(f"{course}-{task}", 'value') for course, task in zip(selected_tasks['Course'], selected_tasks['Task'])]
)
def update_tasks(value, update_clicks, clear_clicks, *values):
    global selected_tasks
    global tasks
    global course
    if value:
        if update_clicks is not None:
            selected_tasks['Completion'] = values
            tasks.update(selected_tasks)
            tasks.to_csv('tasks.csv', index=False)
        elif clear_clicks is not None:
            tasks = pd.read_csv('tasks.csv')
            selected_tasks = tasks[tasks['Course'] == course]
        return [selected_tasks.at[i, 'Completion'] for i in range(len(selected_tasks))]

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

This is task.csv

Course,Task,Completion
C01,C01T01,2
C01,C01T02,3
C02,C02T01,0
C02,C02T02,3
C02,C02T03,3

HI @Mdadilfarooq welocme to the forums.

You might take a look into pattern matching callbacks for your callback:

Also take a look at this concerning the use of global variables:

Hi @Mdadilfarooq, I took a look at your code, what functionality do you want to achieve with the buttons?

EDIT:

Here a version of your code using pattern matching callbacks. Up to now, the buttons return the drop down value (update-button) and values of the slider components (clear-button) into a html.Div().

import dash
from dash import html, dcc, MATCH, ALL, Input, Output, State, ctx
import dash_bootstrap_components as dbc
import pandas as pd

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

# base data
tasks = pd.read_csv('tasks.csv')
courses = tasks['Course'].unique()
initial_course = courses[0]
initial_tasks = tasks[tasks['Course'] == initial_course]

app.layout = html.Div(
    [
        html.H1('Task Management System', className='text-center mt-5'),
        html.Div(
            [
                dcc.Dropdown(
                    id={'type': 'course-dropdown', 'index': 0},
                    options=[{'label': course, 'value': course} for course in courses],
                    value=courses[0]
                )
            ], className='mb-3'),
        html.Div(id={'type': 'table-container', 'index': 0}),
        dbc.Button(
            'Update Progress',
            id={'type': 'update-button', 'index': 0},
            className="btn btn-success float-start",
            n_clicks=None
        ),
        dbc.Button(
            'Clear Progress',
            id={'type': 'clear-button', 'index': 0},
            className="btn btn-danger float-end",
            n_clicks=None
        ),
        html.Div(id={'type': 'debug-out', 'index': 0})
    ],
    className='container'
)


@app.callback(
    Output({'type': 'table-container', 'index': MATCH}, 'children'),
    Input({'type': 'course-dropdown', 'index': MATCH}, 'value')
)
def display_tasks(course):
    course_tasks = tasks[tasks['Course'] == course]
    return dbc.Table([
        html.Thead([html.Tr([html.Th('Task'), html.Th('Completion')])]),
        html.Tbody([
            html.Tr(
                [
                    html.Td(task),
                    html.Td(
                        dcc.Slider(
                            id={'type': 'slide', 'index': idx},
                            value=value,
                            min=0,
                            max=3,
                            step=1,
                            marks={
                                0: 'yet to start',
                                1: 'completed learning',
                                2: 'completed practice',
                                3: 'completed tests'
                            }
                        )
                    )
                ]
            )
            for idx, task, value in zip(
                range(len(course_tasks['Task'])),
                course_tasks['Task'],
                course_tasks['Completion']
            )
        ])
    ], className='mt-3 mb-3')


@app.callback(
    Output({'type': 'debug-out', 'index': ALL}, 'children'),
    # Output({'type': 'slide', 'index': ALL}, 'value'),
    Input({'type': 'update-button', 'index': ALL}, 'n_clicks'),
    Input({'type': 'clear-button', 'index': ALL}, 'n_clicks'),
    State({'type': 'course-dropdown', 'index': ALL}, 'value'),
    State({'type': 'slide', 'index': ALL}, 'value'),
    prevent_initial_call=True
)
def update_tasks(update_click, clear_click, drop_value, slide_values):
    trigger = ctx.triggered_id['type']

    if trigger == 'update-button':
        return drop_value

    if trigger == 'clear-button':
        return [slide_values]


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

Thank you vary much. I was able to solve my doubt

import dash
from dash import html, dcc, MATCH, ALL, Input, Output, State, ctx
import dash_bootstrap_components as dbc
import pandas as pd

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

tasks = pd.read_csv('tasks.csv')
courses = tasks['Course'].unique()
initial_course = courses[0]
initial_tasks = tasks[tasks['Course'] == initial_course]

app.layout = html.Div(
    [
        html.H1('Learning Management System', className='text-center mt-5'),
        html.Div(
            [
                dcc.Dropdown(
                    id={'type': 'course-dropdown', 'index': 0},
                    options=[{'label': course, 'value': course} for course in courses],
                    value=courses[0]
                )
            ], className='mb-3'),
        html.Div(id={'type': 'table-container', 'index': 0}),
        dbc.Button(
            'Update Progress',
            id={'type': 'update-button', 'index': 0},
            className="btn btn-success float-end",
            n_clicks=None
        ),
        dbc.Modal(
            [
                dbc.ModalHeader(dbc.ModalTitle("BLEND360 | LMS")),
                dbc.ModalBody("Your progress has been recorded"),
            ],
            id='popup',
            size="xl",
            is_open=False,
        )
    ],
    className='container'
)

@app.callback(
    Output({'type': 'table-container', 'index': MATCH}, 'children'),
    Input({'type': 'course-dropdown', 'index': MATCH}, 'value')
)
def display_tasks(course):
    course_tasks = tasks[tasks['Course'] == course]
    return dbc.Table([
        html.Thead([html.Tr([html.Th('Task'), html.Th('Completion')])]),
        html.Tbody([
            html.Tr(
                [
                    html.Td(task),
                    html.Td(
                        dcc.Slider(
                            id={'type': 'slide', 'index': idx},
                            value=value,
                            min=0,
                            max=3,
                            step=1
                        )
                    )
                ]
            )
            for idx, task, value in zip(
                range(len(course_tasks['Task'])),
                course_tasks['Task'],
                course_tasks['Completion']
            )
        ])
    ], className='mt-3 mb-3')

@app.callback(
    Output('popup', 'is_open'),
    Input({'type': 'update-button', 'index': ALL}, 'n_clicks'),
    State({'type': 'course-dropdown', 'index': ALL}, 'value'),
    State({'type': 'slide', 'index': ALL}, 'value'),
    prevent_initial_call=True
)
def update_tasks(update_click, drop_value, slide_values):
    if update_click is not None:
        print(drop_value, slide_values)
        df = tasks[tasks['Course'] == drop_value[0]]
        df['Completion'] = slide_values
        print(drop_value, slide_values)
        tasks.update(df)
        tasks.to_csv('tasks.csv', index=False)
        return True

if __name__ == '__main__':
    app.run(debug=True)
1 Like