AnnMarieW / dash-app-gallery

Dash Examples Index
https://dash-example-index.herokuapp.com/
MIT License
59 stars 24 forks source link

Sorted datatable #122

Closed Asaf95 closed 1 year ago

AnnMarieW commented 1 year ago

Hi @Asaf95

I found another way to get a runtime error. Try adding more than 9 different resources. (it runs out of unique colors for the index) How would you like to handle that?

I think a user would be surprised to see the table automatically reload when they delete all the lines. It may be better to add a default row instead. If you would like a more obvious way to reload the table than refreshing the browser, perhaps add a "refresh" button?

For button, let's use dbc.Button rather than html.Button .

If you make the browser window narrow, then the table doesn't stay in the container. One way to fix this is by adding:

style_table={"overflowX": "auto"},

With Bootstrap, it's a best practice to use a dbc.Container as the outter container rather than an html.Div. More info here. So please take out all the new left and right margins and start with this:

app.layout =  dbc.Container(
    [
       ....
    ],
    fluid=True
)

The conditional formatting doesn't work in the table. The idea is to make the Finish column with a grey background to show it's disabled. Try:

style_data_conditional=[
    {
        "if": {
            "column_id": "Finish",
        },
        "backgroundColor": "#eee",
    },
],
style_header_conditional=[
    {
        "if": {
            "column_id": "Finish",
        },
        "backgroundColor": "#eee",
    },
]

Still TODO: Comments and Style Just a heads up, for the Example Index we want to use minimal comments and minimal style, but we can get to that last.

Coding-with-Adam commented 1 year ago

Great suggestions @AnnMarieW. I like the suggestion of adding a default row when all rows are deleted. There are a lot of good things to learn in the app. So I think adding a default row would be a simpler better way to move forward than a refresh button in this case.

Asaf95 commented 1 year ago

Hi @AnnMarieW and @Coding-with-Adam, Thanks for the feedback! I will start with the good parts, I changed the layout and Button to dbc.Container and dbc.Button like you as you suggested. Also I created a new constants dict for all style formats of the datatable. I change the update_table_and_figure function to contain only the final virables that will be returned to the user, all the action that needed to be done and now in fucntion, I think it will be easier to uncerstand and explain this way, hope you like it :)

Now the bad part, when I change the function to return only the new row when the user deletes all the rows. It's causing the inistal table to start as empty table that become one line table (with the default row for new line). From what I found, the issue accoures becouse when the Dash App First loads it runs three times and get different values for the table becouse they are running in parallel. I added few logging in the callback function: image

And in the update_datatable function: image

and accourding to the logs: image

I'm trying to find a solution to that, the best that I found for now is this explantion (the part When a Dash App First Loads) of what happening... I will find the solution and update you, but if you have an advaice to what can I do feel free to tell me :)

Thanks again!

AnnMarieW commented 1 year ago

Hi @Asaf95 You can fix the bug by initializing the data in the table:

data=__get_default_table().to_dict("records"),

And set prevent_intial_callbacks:

app = Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB], suppress_callback_exceptions=True, prevent_initial_callbacks=True)

Then you may not need to check for the None condition in the callback :-)

I like how you changed the colors=px.colors.qualitative.Alphabet, however, now you will get a runtime error if you have more than 26 different Resources. :cry:

A couple option - maybe a try except block to catch the "PlotlyError" and display a nice message to the screen. Or you can limit the choices of Resources by using dropdowns in the DataTable https://dash.plotly.com/datatable/dropdowns

The error message may be the most flexable, then people can change the name of the Resources in the table. - But either one would be good examples.

Asaf95 commented 1 year ago

Thanks again @AnnMarieW! I'm still working of finding why the dropdown dosent work for me... I will go with the soulusion to provide only "A-Z" letters (goes well with 26 different colors that I have). I'm still working of finding why the dropdown dosent work for me... maybe it's the hour that evrything is not working for me :( so I will let it go... but this is how I define the dropdown for Resource.

            dropdown={
                'Resource': {
                    'options': [
                        {'label': i, 'value': i}
                        for i in list(map(chr, range(65, 91)))
                    ]
                },
            },

Do you have any more things you want me to change / add to the app? I will try to work on this tomorrow :)

AnnMarieW commented 1 year ago

We are getting really close!

For the dropdown to work you need to add `'presentation': 'dropdown' to the Resource column in DATA_TABLE_SCHEMA

Then there's a bit of css that fixes the table when the dropdown doesn't drop down. This needs to be added to you assets folder. It's already in the Example Index, so it will work in production.


/* fixes  when the table dropdown doesn't drop down */
.dbc .Select-menu-outer {
  display: block !important;
}

.dash-table-container .dropdown {
  position: static;
}

There is a weird thing going on when you add overflowX:auto to the style_table prop. Haven't figured that out yet. - try commenting out for now. We might need to do something different with the width. Right now the dropdown doesn't show on the last couple rows of the table.

Asaf95 commented 1 year ago

Hi, I think I did what you meant... but it's not working for me :( image I have running on production repo colne with the css file... I will try again tomorrow. Thank you very much for the guidance I really appreciate it ❤️ (and all the great documentation you have in the site)

AnnMarieW commented 1 year ago

Yeah, I have a couple tricks up my sleeve still. I think we can call this substantially complete. What you have looks great. :100:

Tomorrow, I'll do a version with a couple workaround for the dropdown. issue. I'll also do some style updates -- only so it's consistent with other apps in the Example Index. (Not because there is anything wrong with the style in your version)

I think this is awesome - nice work! :tada:

AnnMarieW commented 1 year ago

One more update is needed. I just noticed that the figure factory get_gantt() function is deprecated and it's recommended to use px.timeline. Would you like to try updating the figure?

https://plotly.com/python/gantt/

AnnMarieW commented 1 year ago

Check out this Gist: https://gist.github.com/AnnMarieW/5fdd10cf2f0ff4dbd7b8b1300ac232ba

Try putting the content of the pages-timeline-gantt.py in a file called timeline-gantt.py in the pages folder: https://github.com/AnnMarieW/dash-app-gallery/tree/main/pages. Then run app.py from the root directory. You can find your app near the bottom (it doesn't have an image yet) You will see that it's now using the css from the assets folder.

Next, you can update the app in the examples folder with timeline-gantt.py from the Gist.

Here are some of the updates:

I think it's a bug that the style_table={'overflowX': 'auto'}, isn't compatible with the dropdowns. I'm sure there is a solution with CSS, but I haven't figure it out (yet). A workaround is to use other ways to control the table width. This version uses shorter column headings -so now the entire table fits in the smallest browser window.

When you delete a Resource using the dropdown, it caused a runtime error. I added clearable: False to the dropdown to fix this.

I moved the add rows button to the top of the table. I think it looks better on the bottom where you had it, but it's a little annoying to chase it around when you add/delete rows and the table changes size. :-)

I removed the background color from the table to make the disabled column more visible, and from the background of the app so it would be easier to use with different themes.

The ids are now updated for production. All ids in the Example Index need to be unique, so we use a prefix of "<filename>-x-"

The other changes are just to make the coding and comment style similar to other apps in the dash docs and the Example Index. For example, I kept the comments that explained the "why" and removed the comments that just described what the code does. I think the code is really clear - I especially like where you made separate functions for updating the table and the chart in the callback.

Let me know if you would like to make any other changes - otherwise, I think it's ready for a final review from Adam. :confetti_ball:

Still TODO - update the figure to px.Timeline. Adam - can we update the csv file so it's not 2016?

Asaf95 commented 1 year ago

Hi @AnnMarieW,

I'm changing the gantt to px.timeline(), I didn't find a simple way to add the 26 colors of px.colors.qualitative.Alphabet so I used this 'trick' to generate the list.

Resource_Keys = list(map(chr, range(65, 91)))
Resource_Color_values = px.colors.qualitative.Alphabet
res = {Resource_Keys[i]: Resource_Color_values[i] for i in range(len(Resource_Color_values))}

ALso the all the rest of the px.timeline was changed accourding to the the formats of the old gantt.

Please review 🙏

AnnMarieW commented 1 year ago

Nice! However, it looks like it doesn't sort properly with px.timeline :cry:

We are looking into how to make the sort work. Worst case, we can move back to the Figure Factory function.

Asaf95 commented 1 year ago

Hi, How do you wanted is soeted? by Task name? If yes maybe this can work? gantt_fig.update_layout(yaxis_categoryorder='category ascending')

image

AnnMarieW commented 1 year ago

Yes, that works - but the cool thing about the Figure Factory version is that when you sorted the DataTable, it would also sort the figure automatically. Seems like that may not be easy to do with px.timeline?

AnnMarieW commented 1 year ago

Adam found the answer :trophy:

Try this:

gantt_fig.update_yaxes(categoryorder="array", categoryarray=updated_table_as_df["Task"])
Asaf95 commented 1 year ago

greattt!! thank you both, I added it to the yaxis dict (link 129-130) and its working :)

AnnMarieW commented 1 year ago

Super! :confetti_ball:

@Coding-with-Adam This app is ready for final review

Coding-with-Adam commented 1 year ago

Thanks for letting me know, @AnnMarieW And thank you for this @Asaf95 .

I should have time to review this app in several hours. I'll keep you both posted.

Asaf95 commented 1 year ago

Hi @Coding-with-Adam thanks for the feedback and the changes! it's looking better now :)

About the dropdown issue, also @AnnMarieW told me about it (and showed me how to fix it) and when we updated the css code that needed it's working image From what I understood, the css file in production is running with the additional css code that we need. Can you try to run the app with the 1.css file?

AnnMarieW commented 1 year ago

@Asaf95 , Adam is off for a bit, so I'll pass along the rest of his comments and help get this app ready to merge.


1) The dropdown works in production, but it should also work if you copy the code from the Example Index and run it locally. There are two possible solutions - one is to add comments on how to add the CSS to an assets folder. The other (simpler way) is to add to the css prop in the table. For example:

css=[
    {"selector": ".Select-value", "rule": "padding-right: 22px"}, # makes space for the dropdown caret
    {"selector": ".dropdown", "rule": "position: static"} # makes dropdown visible
]

2) Let's use the naming convention of dff for the filtered or modified df. So add_finish_column(dff) rather than add_finish_column(df)

3) Can remove this line since it's unused:

style_table=DATA_TABLE_STYLE.get("style_table"),

4) The Example Index is designed for beginners, and this app has a more advanced coding style. Here are a couple suggestions to simplify:

dropdown={
    "Resource": {
        "clearable": False,
        "options": [
            {"label": i, "value": i} for i in ["A", "B", "C", "D"]
        ],
    },
}
Asaf95 commented 1 year ago

NP @AnnMarieW, Thanks again for the feedback, I changed all the things that you mentioned. The only thing that I think to change is to use Constants for css and dropdown in the datatable (maybe to change DATA_TABLE_STYLE name to something more generic and add css and dropdown as keys in this DATA_TABLE_STYLE). I also changed some names and removed the line that "stored" the df before returning it, from my POV user who are new to Dash but not new to Python and Pandas will find this better to understand and more efficient.

What are your thoughts on the change?

AnnMarieW commented 1 year ago

Thanks for the update. Did you miss the this comment? What do you think about removing the function annotations:

Many beginners are not familiar with function annotation, so it would be better to remove these. This would also make it consistent with other apps in the Dash docs and Example Index.

I like the DATA_TABLE_STYLE name - and it's a good idea to add the css style there as well. If you want to change a name, maybe DATA_TABLE_COLUMNS instead of DATA_TABLE_SCHEMA ? but I don't think that's too important.

The other comments, I'll make in-line

Asaf95 commented 1 year ago

Hi @AnnMarieW, All changed are done :)

AnnMarieW commented 1 year ago

Looks Awesome! Thanks for all your hard work :1st_place_medal: I'll merge it and add the files necessary for production. Should be live soon after Adam is back.

:woman_dancing:

Asaf95 commented 1 year ago

Thanks!! It was a pleasure doing this with you! (and also with Adam). I'll try to do this more :)

Coding-with-Adam commented 1 year ago

🥳 Congrats @Asaf95. Your app is officially online: https://dash-example-index.herokuapp.com/timeline-gantt

Thank you for your support, @AnnMarieW .

Asaf95 commented 1 year ago

thank you both!!!