Closed tuopouk closed 1 year ago
Thank you @tuopouk @AnnMarieW would you be able to do a code review for this next week?
Yes, I can make some time next week.
@AnnMarieW Here is also the serverside version. You can compare the map updates. To me it seems that the clientside version's update is faster. I have a some demos here: Clientside callback demo (more data): http://keyfiguresfinland.herokuapp.com/ Serveside callback demo: https://serversidefigures.herokuapp.com/
This is a great topic - nice to see an app with data from a country other than US or Canada. I like the use of the clientside callback, it's a significant performance bump -- and the figure is fantastic.
For the review, let's start by focusing on this callback:
@app.callback(Output('key-figures-finland-whole-country-header-x','children'),Input('key-figures-finland-key-figure-selection-x', 'value'))
def update_whole_country_header(key_figure):
kf_string = key_figure.split(',')
kf_string.pop(-2)
kf_string = ', '.join(kf_string)
kf_string = {True:key_figure, False:kf_string}[len(kf_string.split(','))==1]
return html.P([kf_string,html.Br(),"in Finland:",html.Br(), ("{:,}".format(whole_country_df.loc[key_figure])).replace('.0','').replace(',',' ')+key_figure.split(',')[-2].replace('2020','').replace('2021','').replace(key_figure.split(',')[0],'')],className="fw-bold")
It's difficult to understand what's going on in the callback function. After a bit of study, it appears that you are parsing the value
of the Dropdown and displaying it as a sentence that includes the value from the whole_country_df
This needs to be simplified to make the code more readable and maintainable. One suggestion is to do a little more data wrangling at the start to make the whole_country_df
shaped like this:
(This example is just 2 rows to show the format)
data = {
"stat_key": [
"Employment rate, %, 2020",
"Annual contribution margin, EUR per capita, 2020",
],
"stat_name": ["Employment rate", "Annual contribution margin"],
"units": ["%", "EUR per capita"],
"year": ["2020", "2020"],
"stat": [69.5, 728.8],
}
whole_country_df = pd.DataFrame(data).set_index("stat_key")
Then the callback could look like:
@app.callback(
Output("key-figures-finland-whole-country-header-x", "children"),
Input("key-figures-finland-key-figure-selection-x", "value"),
)
def update_whole_country_header(key_figure):
dff = whole_country_df.loc[key_figure]
return html.Div(
[
html.Div([dff.stat_name, ", ", dff.year]),
html.Div("in Finland"),
html.Span([dff.stat, dff.units]),
]
)
Now it's much easier to see what this callback does. Plus, if you would like to style the returned data in a different way, like put it in a card, or make the number stand out more, then it will be really easy to update.
@AnnMarieW I updated the code making it probably easier to read. The challenges were to extract names, units and years from the stat name, to display discrete values (e.g. population) as integer values, and to have a space as the thousand separator. I hope it makes more sense now. I also formatted the dropdown style because the labels overlapped on mobile.
Hi @tuopouk
Yes, the latest changes are much easier to read!
The formatting of the number can be done with the other data wrangling in the global space rather than the callback. Personally, I think the values would look nice with one decimal place and comma separators, and this could be done with an f-string in the component. This is a style question, and Adam can weigh in too - it's a trade-off but we are trying to keep the code simple and concise for the Example Index.
I recently made a checklist for all the things we look for in the code review. Can you please review the list and try to complete as many as possible?
Basic Requirements
requirements.txt
. Adding anything to the requriements.txt
needs prior approval. __name__
in app instantiation and may include a Bootstrap external stylesheet. Anything else needs prior approval.app.title = "...."
server = app.server
assets
folder for use during review. Before deployment, the data will be moved to the Plotly datasets repo.Naming conventions
submit_button
id
's, uses hyphens. ie id="submit-button"
style
prop, uses camel case: ie style={"textAlign": "center"}
df
for main pandas dataframe in the global scope. Uses dff
for the filtered df
in a callback function.Concise code
dcc.Dropdown(df.columns)
rather than
dcc.Dropdown(options = [{'label':c, 'value':c} for c in df.columns]
Input()
s Output()
s or State()
s in a listmulti=False
in the dcc.Dropdown
. Check the reference section of the docs to see the defaults for the components..format()
In apps using dash-bootstrap-components
:
className
prop whenever possible instead of the style
prop
For example: className="bg-primary"
rather than style={"backgroundColor": "blue"}
html.BR()
for spacing and instead uses margin in the className
prop dbc.Button
instead of html.Button
. dbc.Container
as the outer container of the app rather than html.Div
dbc.themes.SPACELAB
sine that is the stylesheet used in production. Before release: (For Maintainers only)
<filename>-x-
assets
folderassets
folder during reviewpages
folder and run app.py
from the root directory to ensure the new app works in the Example Index.
I should not have included the clock now.