From 35954bd2645930bcbfd5125ed0ad17bed2a3bf6e Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 13:17:08 +0700 Subject: [PATCH 1/6] Merge changes --- .../__pycache__/data_model.cpython-39.pyc | Bin 9651 -> 9645 bytes src/python/app.py | 28 +++++++++++------- src/python/assets/css/covid-dash.css | 2 -- src/python/components/left_panel.py | 4 +-- src/python/components/right_panel.py | 15 ++++++---- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/python/__pycache__/data_model.cpython-39.pyc b/src/python/__pycache__/data_model.cpython-39.pyc index 37c53dfdfa96c62057926fbacfec74d8fb3df46e..b0eec9bdb5081b0cb3cb7ab7fe3ad70f9993604a 100644 GIT binary patch delta 230 zcmYk1I|{-;5Qb-x&E~O2u?iLnqP2JkEA4E=LL?9opO7%+EFQwnD(P%2G*|EnHlD_f zSU52J^ELm+l#|;;#bFqLnQqj%tC!PfSXvCZ%~;?!?O%!l*S2;@kaHHYcapiwUV++Y zKld26ej*~EAc6uBSSHra$}VhIeaE_C$MBU!#WO%ipEF3OXf6a&2y;V2vx(FJ)hML8 tu_;y~SPZnwLDkM~1lpsox%RIvA2P>gBWsyQ-ac&(t7NsOxZHT-_6M-oH}e1h delta 236 zcmZ4Mz1f>Pk(ZZ?0SGb%T>Z#!dJo%w2ixl+l3)kua>I*1^vEsFlFZ!Hl+>J(#3+_xAhkJ}!%G$b+nqZF diff --git a/src/python/app.py b/src/python/app.py index ad7872b..85ca5e4 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -37,15 +37,25 @@ map_panel = mid_panel(data_reader) alt.themes.enable("ggplot2") app.title = "Covid-19 Data Portal" -dashboard_heading = ( - "Covid-19 Data Portal" -) +dashboard_heading = "Covid-19 Data Portal" app.layout = dbc.Container( [ - dbc.Row(dbc.Col(html.Div([html.H1(dashboard_heading)]), className="heading")), dbc.Row( - [ - + dbc.Col( + html.Div( + [ + html.H1(dashboard_heading), + html.H6( + "Data Last Updated: " + + data_reader.last_updated.strftime("%m/%d/%Y") + ), + ] + ), + className="heading", + ) + ), + dbc.Row( + [ dbc.Col([global_panel.render()], width=3, className="left_panel"), dbc.Col([map_panel.render()], width=6, className="panel"), dbc.Col( @@ -55,13 +65,9 @@ ), ], ), - dbc.Row(dbc.Col(html.Div([html.H4("Data Last Updated: " + data_reader.last_updated.strftime("%m/%d/%Y"))]), className="footer")), ], fluid=True, - style={"border-width": "0", - "width": "100%", - "height": "100%"} - + style={"border-width": "0", "width": "100%", "height": "100%"}, ) # endregion diff --git a/src/python/assets/css/covid-dash.css b/src/python/assets/css/covid-dash.css index 2ad944b..13ae5dd 100644 --- a/src/python/assets/css/covid-dash.css +++ b/src/python/assets/css/covid-dash.css @@ -20,8 +20,6 @@ .footer{ text-align: center; - background-color: white; - color:black } .h2{ diff --git a/src/python/components/left_panel.py b/src/python/components/left_panel.py index a428eac..6f168dd 100644 --- a/src/python/components/left_panel.py +++ b/src/python/components/left_panel.py @@ -34,7 +34,7 @@ def __init__(self, datamodel): id="chart_cases_ranking", style={ "border-width": "0", - "width": "300px", + "width": "400px", "height": "800px", }, ) @@ -112,7 +112,7 @@ def __create_ranking_bar_chart(self, data, type): title=alt.TitleParams( text="Top 30 Countries", subtitle="By " + type + " Cases" ), - width=130 + width=190, ) .mark_bar() .encode( diff --git a/src/python/components/right_panel.py b/src/python/components/right_panel.py index d968c8e..4d6c05a 100644 --- a/src/python/components/right_panel.py +++ b/src/python/components/right_panel.py @@ -24,8 +24,10 @@ def __init__(self, datamodel): self.content = dbc.Col( [ - dbc.Row(dbc.Col(self.__create_country_dropdown())), - html.Br(), + dbc.Row( + dbc.Col(self.__create_country_dropdown()), + style={"padding-bottom": "10px"}, + ), dbc.Row( dbc.Col( [ @@ -41,10 +43,13 @@ def __init__(self, datamodel): ] ) ] - ) + ), + style={"padding-bottom": "10px"}, + ), + dbc.Row( + dbc.Col(self.__create_button_groups()), + style={"padding-bottom": "10px"}, ), - html.Br(), - dbc.Row(dbc.Col(self.__create_button_groups())), dbc.Row( dbc.Col( [ From b4b9afff34d1b6f2ec2f02d4b8381cd9fb9d2abf Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 14:11:12 +0700 Subject: [PATCH 2/6] Update layout --- src/python/app.py | 23 +++++++++++++---------- src/python/assets/css/covid-dash.css | 24 +++++++++++++++++------- src/python/components/left_panel.py | 4 ++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/python/app.py b/src/python/app.py index 85ca5e4..a0df446 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -38,21 +38,24 @@ alt.themes.enable("ggplot2") app.title = "Covid-19 Data Portal" dashboard_heading = "Covid-19 Data Portal" +last_updated = "Last Updated: " + data_reader.last_updated.strftime("%m/%d/%Y") app.layout = dbc.Container( [ dbc.Row( - dbc.Col( + [ html.Div( - [ - html.H1(dashboard_heading), - html.H6( - "Data Last Updated: " - + data_reader.last_updated.strftime("%m/%d/%Y") - ), - ] + dashboard_heading, + style={ + "font-size": "30pt", + "text-align": "center", + "left": "0%", + "right": "0%", + }, ), - className="heading", - ) + html.Div(className="space"), + html.Div(last_updated, style={"font-size": "14pt", "right": "0px"}), + ], + className="heading", ), dbc.Row( [ diff --git a/src/python/assets/css/covid-dash.css b/src/python/assets/css/covid-dash.css index 13ae5dd..b5fdd42 100644 --- a/src/python/assets/css/covid-dash.css +++ b/src/python/assets/css/covid-dash.css @@ -13,9 +13,10 @@ } .heading{ - text-align: center; background-color: black; - color:white + color:white; + place-items: center; + place-content: center; } .footer{ @@ -39,8 +40,17 @@ text-align: center; } -.body { - overflow: hidden; /* Hide scrollbars */ - width: 100%, - height: 100% -} \ No newline at end of file +body { + width: 100%; + height: 100%; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.space{ + width:50px +} + +body::-webkit-scrollbar { + display: none; +} diff --git a/src/python/components/left_panel.py b/src/python/components/left_panel.py index 6f168dd..18fb21a 100644 --- a/src/python/components/left_panel.py +++ b/src/python/components/left_panel.py @@ -34,7 +34,7 @@ def __init__(self, datamodel): id="chart_cases_ranking", style={ "border-width": "0", - "width": "400px", + "width": "450px", "height": "800px", }, ) @@ -112,7 +112,7 @@ def __create_ranking_bar_chart(self, data, type): title=alt.TitleParams( text="Top 30 Countries", subtitle="By " + type + " Cases" ), - width=190, + width=290, ) .mark_bar() .encode( From deb59b4d57df4eeae5108f41141cc898fe21da15 Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 14:23:45 +0700 Subject: [PATCH 3/6] add docstrings --- src/python/app.py | 3 +++ src/python/components/left_panel.py | 21 +++++++++++++++++++++ src/python/components/mid_panel.py | 13 +++++++------ src/python/components/right_panel.py | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/python/app.py b/src/python/app.py index a0df446..2d7e080 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -87,6 +87,7 @@ Input("rp_btn_new", "n_clicks"), ) def update_right_panel(country, total_click, new_click): + """update the right panel when changes triggered by its controls""" ctx = dash.callback_context if not ctx.triggered: ntype = "Total" @@ -110,6 +111,7 @@ def update_right_panel(country, total_click, new_click): Input("btn_recovered", "n_clicks"), ) def update_left_panel(active, confirmed, death, recovered): + """update all components on the left panel upon callback""" ctx = dash.callback_context if not ctx.triggered: ctype = "confirmed" @@ -135,6 +137,7 @@ def update_left_panel(active, confirmed, death, recovered): Input("wm_recovered", "n_clicks"), ) def update_mid_panel(confirmed, death, recovered): + """update all controls on mid panel upon callback""" ctx = dash.callback_context if not ctx.triggered: ctype = "confirmed" diff --git a/src/python/components/left_panel.py b/src/python/components/left_panel.py index 18fb21a..7fc07e1 100644 --- a/src/python/components/left_panel.py +++ b/src/python/components/left_panel.py @@ -20,6 +20,8 @@ class left_panel(panel): + """handle all activities of the left panel""" + def __init__(self, datamodel): super().__init__("Global", datamodel) @@ -76,6 +78,11 @@ def refresh(self, chart_type="confirmed"): return chart def __create_button_groups(self): + """create buttons Confirmed/Death/Recovered/Active + + Returns: + dbc.ButtonGroup + """ button_groups = dbc.ButtonGroup( [ dbc.Button("Confirmed", active=True, id="btn_confirmed"), @@ -89,6 +96,11 @@ def __create_button_groups(self): return button_groups def __create_total_statistics(self): + """retrieve global statistics + + Returns: + html: all statistics + """ data = self.data_reader.cumulative_filter() confirmed_cases = panel.format_number(data.Confirmed) active_cases = panel.format_number(data.Active) @@ -106,6 +118,15 @@ def __create_total_statistics(self): return content def __create_ranking_bar_chart(self, data, type): + """create bar chart to rank countries by case type + + Args: + data (dataframe): dataset + type (string): "confirmed", "death", "recovered + + Returns: + altair barchart + """ chart = ( alt.Chart( data, diff --git a/src/python/components/mid_panel.py b/src/python/components/mid_panel.py index aa5e592..f6e538c 100644 --- a/src/python/components/mid_panel.py +++ b/src/python/components/mid_panel.py @@ -21,6 +21,8 @@ class mid_panel(panel): + """handle all activities related to world map panel""" + def __init__(self, datamodel): super().__init__("World Map", datamodel) @@ -92,11 +94,11 @@ def refresh(self, chart_type="confirmed", ntype="Total"): return world_map, trend_chart def __create_button_groups(self): - """Create button + """Create button Returns: buttons for three cases - """ + """ button_groups = dbc.ButtonGroup( [ dbc.Button("Confirmed", active=True, id="wm_confirmed"), @@ -125,7 +127,7 @@ def __create_world_map_chart(self, data, type): base = ( alt.Chart(source, title="") .mark_geoshape(fill="lightgray", stroke="white") - .properties(width=860, height=450 ) + .properties(width=860, height=450) .project("equirectangular") # .padding({"left": 0, "right": 0, "bottom": 1, "top":1}) ) @@ -151,9 +153,8 @@ def __create_world_map_chart(self, data, type): ) chart = base + points - chart = ( - chart.configure_legend(orient="bottom") - .configure_view(strokeWidth=0, ) + chart = chart.configure_legend(orient="bottom").configure_view( + strokeWidth=0, ) return chart.to_html() diff --git a/src/python/components/right_panel.py b/src/python/components/right_panel.py index 4d6c05a..13b8631 100644 --- a/src/python/components/right_panel.py +++ b/src/python/components/right_panel.py @@ -19,6 +19,8 @@ class right_panel(panel): + """handle all activities related to country panel""" + def __init__(self, datamodel): super().__init__("Country", datamodel) @@ -102,6 +104,11 @@ def refresh(self, country, ntype="Total"): return confirmed, recovered, deaths, c_chart, d_chart def __create_country_dropdown(self): + """create a dropdown list to select country + + Returns: + dcc.Dropdown + """ return dcc.Dropdown( id="dd_country", options=self.data_reader.get_country_options(), @@ -109,6 +116,11 @@ def __create_country_dropdown(self): ) def __create_button_groups(self): + """create Total | New buttons + + Returns: + dbc.ButtonGroup: group of buttons + """ button_groups = dbc.ButtonGroup( [ dbc.Button("Total", active=True, id="rp_btn_total"), @@ -120,6 +132,16 @@ def __create_button_groups(self): return button_groups def __create_timeserie_chart(self, country, case_type=1, ntype="Total"): + """create trend chart showing cases of a country + + Args: + country (string): country name + case_type (int, optional): 1: confirmed, 2: death, 3: recovered. Defaults to 1. + ntype (str, optional): "Total" or "New". Defaults to "Total". + + Returns: + chart: altair chart object + """ data = self.data_reader.get_timeserie_data_by_country(country, case_type) if case_type == 1: chart_title = "Cases over time" From 0ac092f35b0edb10d896b7cf205ef5f8b853b642 Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 14:26:39 +0700 Subject: [PATCH 4/6] disable debug mode --- src/python/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/app.py b/src/python/app.py index 2d7e080..fa34262 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -156,5 +156,5 @@ def update_mid_panel(confirmed, death, recovered): # endregion -if __name__ == "__main__": - app.run_server(debug=True) # activate hot reloading +# if __name__ == "__main__": +# app.run_server(debug=True) # activate hot reloading From f322e99851212bf0f2c9da6ca8cde6c648b08689 Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 16:10:31 +0700 Subject: [PATCH 5/6] Update docs --- README.md | 12 +++--- docs/reflection-milestone4.md | 70 ++++++++++++++++++++++++----------- src/python/app.py | 11 ++++-- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index fef86ff..9450371 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # COVID-19 Data Portal ## About -The goal of this repository is providing COVID-19 statistics by using Dash and Python. -The COVID-19 Data Portal live dashboard can be accessed here +The goal of this repository is providing COVID-19 statistics by using [Dash](https://github.com/plotly/dash) and [Python](https://github.com/python). + +The COVID-19 Data Portal live dashboard can be accessed [here](https://covid-data-portal.herokuapp.com). ## Description @@ -13,11 +14,10 @@ On the left side of the dashboard, the user can view the world cases trend for r On the right side of the dashboard, the country level statistics can be seen in both number format and trend-line format. By default, the user will see Canada's (based on locale) statistics when opening the dashboard. Users can also opt for other region's statistics by selecting from the drop-down menu to view both the case summary and the trend line. -## Dashboard Demo +## Dashboard ![](docs/images/dash-demo.gif) - ### Build the dashboard locally **Step 1: Clone this repository** @@ -26,7 +26,7 @@ On the right side of the dashboard, the country level statistics can be seen in Create and activate a conda environment using the env.yaml at the root of this project by running the following command at the root directory of the project. (Alternatively, you can manually install the dependencies listed in the env.yaml file) -```bash +``` {.bash} conda env create --file env.yaml conda activate covid_dash ``` @@ -35,7 +35,7 @@ Go to the root folder of the repo and execute `python src/python/app.py` ## License -The COVID-19 Data Portal materials here are licensed under the Creative Commons Attribution 2.5 Canada License (CC BY 2.5 CA). If re-using/re-mixing please provide attribution and link to this webpage. +- The COVID-19 Data Portal materials here are licensed under the Creative Commons Attribution 2.5 Canada License (CC BY 2.5 CA). If re-using/re-mixing please provide attribution and link to this web page. ## References diff --git a/docs/reflection-milestone4.md b/docs/reflection-milestone4.md index 1c0aa87..ce8c51a 100644 --- a/docs/reflection-milestone4.md +++ b/docs/reflection-milestone4.md @@ -1,29 +1,55 @@ +--- +editor_options: + markdown: + wrap: sentence +--- + # Reflection -## Progress of the dashboard: -We have managed to achieve all the functionalities as we designed in our initial proposal. In this milestone, we have attempted to cover most of the high and middle priority feedback coming from the TA, Joel, and our peers. The main improvements have been listed as follow: -- As instructed by the TA from the feedback, we filled the middle panel with another new cases per day trendline which can be controled by the buttons in the middle panels to switch the cases type. This change filled up most of the spacing in the bottom of the dashboard, which rendered the layout more balanced. -- We also rescaled the middle panel map size and adjusted the bubble sizes, so that users can gain more information from the visualization. Especially for countries with smaller amount of cases, their bubbles can be more representative. -- We removed the scrolling bar from the left panel and resized the graph to provide the users with better a user experience. In addition, the total cases count has also been added to on the left panel to give the users more information. -- We adjusted the layout slightly for the right panel to make it less compact. -- The updated time has been added at the very bottom of the page following Joel's suggestion. + +## Progress of the dashboard + +We have managed to achieve most of the functionalities designed in our initial proposal. +In this milestone, we have attempted to cover most of the high and medium priority feedback coming from the TA, Joel, and our peers. + +The main improvements are listed as follow: + +- As recommended by our TA's [feedback](https://github.com/UBC-MDS/DSCI_532_Group_12/issues/44), a line graph showing new cases trend is added to the middle panel. + This change fills up most of the space under the world map, which makes the layout more balanced + +- We adjusted the scale of the bubbles showing number of cases on the world map to highlight better the difference of Covid-19's impact among countries + +- We removed the scroll-bar from the bar chart ranking Top 30 countries and added global statistics to the left panel + +- We added some spacing among rows of component on the right panel per Joel's feedback + +- A time-stamp showing the last updated date of the data is added Up to this release, our panel should have the following functionalities: -The left panel contains a statistic summary and a bar chart which shows the user the ranking of case count by country with the button for user to choose if they want to see (Confirmed Cases, Active Cases, Recovered Cases or Deaths). -In the middle panel, the user could surface the distribution of the cases geographically in a map, with the options to choose confirmed cases, death cases, or recovered cases. The user can also check new cases per day for each categories from this categories. +- The left panel (Global) contains global statistics and a ranking bar chart which shows Top 30 countries most affected by the pandemic + +- The middle panel (World Map) illustrates the case distribution geographically across the world and a line graph showing new cases trend + +- The right panel shows a country's summary statistics and trends per user selection + +## Development Discussion + +There are some improvement opportunities acknowledged but delayed to future development due to the time constraint of this milestone, those can be found below. + +- **New features with substantial development effort** + + - Allow user to select a time frame for viewing data: it requires changes both to our data access and UI layers + + - Allow user to switch between total case and case per population: this requires using another data set to get world population, changes to our data access and UI layers + + - Allow interaction between panels: selecting a country in the world map/selecting a country from the world ranking will display its statistics on the right panel + +- **Persistent layout issues without a technical proper solution** + + - Horizontal scroll-bar is still displayed when displaying the dashboard on 1920x1080 screen: we tried applying `overflow:hidden` but it will always disable using scroll-bar for all components -In the right panel, the user can check the detailed trend by country and category. Meanwhile, the right panel also shows the user how many cases in the selected category each country currently have. -## Development Discussion -Due to the time constraint of the implementation for this milestone, we have prioritized the tasks to implement by their complexity, development efforts, benefits and demand rationality. During the implementation, it's been acknowledged by our team that customizing the CSS for some details has been tricky in python with dash library. That's why we haven't completed some of the UI enhacement tasks which couldn't bring about significant benefits from the UX perspective. We also found some potential improvements are complex due to the nature of our source data, as we have to refactor the code substantially to create a toggle button or a slide bar. +- **Small UI improvements suggested by our peer but not implemented** -***Below we have categorized what we have left by why we didn't choose to do it:*** + - Change color palette for different types of case (confirmed, death, recovered): we find our current color scheme (creme, red, white, black, gray) reflect well the situation of the pandemic -- Relatively low benefits added with high development efforts - - Allow filtering by time frame for charts to show trends. (This task will need significant amount of effort to refactor the code for the data model, which is also out of the scope of our initial proposal) -- Technically complicated - - Add interaction between the left panel and middle panel. (As the left panel and middel panel were implemented seperately and they were different classes from higher level, it's technically hard to make them interactive without refactor the code extensively at this stage.) - - Add a toggle button to switch between cases number and cases per population density. (We had problem looking for a reliable population data that can be merged to our COVID data easily. This will also create a substantial amount of code change that can hardly be done within the given time.) - - Previously we thought we should have make the dashboard responsive, however this change can be relatively hard to achieve given the framework we are using and the time constraint. -- Not bring about many benefits with low priority - - Our peers suggested changing the color palettes for all panels, but it's really hard to tell as people all have different views on design of color. - - Our team also considered to cache the `.csv` file instead of load it everytime from John Hopkins Repository, but we haven't got time to refactor this code yet. This might be very minor in terms of affecting the usage of the dashboard. \ No newline at end of file + - Highlight selected buttons: the buttons are currently wrapped by boxes around them, changing css style of those buttons will make the code more complicated to maintain as there is no easy way to do it but addressing that within callback methods (which are already quite complex) diff --git a/src/python/app.py b/src/python/app.py index fa34262..d993ebf 100644 --- a/src/python/app.py +++ b/src/python/app.py @@ -70,7 +70,12 @@ ), ], fluid=True, - style={"border-width": "0", "width": "100%", "height": "100%"}, + style={ + "border-width": "0", + "width": "100%", + "height": "100%", + "overflow-x": "hidden", + }, ) # endregion @@ -156,5 +161,5 @@ def update_mid_panel(confirmed, death, recovered): # endregion -# if __name__ == "__main__": -# app.run_server(debug=True) # activate hot reloading +if __name__ == "__main__": + app.run_server(debug=True) # activate hot reloading From 193599c482c6bda075bf49826daea314664dee1e Mon Sep 17 00:00:00 2001 From: Mai Le Date: Sat, 6 Feb 2021 16:11:17 +0700 Subject: [PATCH 6/6] Update docs --- docs/reflection-milestone4.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/docs/reflection-milestone4.md b/docs/reflection-milestone4.md index ce8c51a..842e293 100644 --- a/docs/reflection-milestone4.md +++ b/docs/reflection-milestone4.md @@ -1,20 +1,12 @@ ---- -editor_options: - markdown: - wrap: sentence ---- - # Reflection ## Progress of the dashboard -We have managed to achieve most of the functionalities designed in our initial proposal. -In this milestone, we have attempted to cover most of the high and medium priority feedback coming from the TA, Joel, and our peers. +We have managed to achieve most of the functionalities designed in our initial proposal. In this milestone, we have attempted to cover most of the high and medium priority feedback coming from the TA, Joel, and our peers. The main improvements are listed as follow: -- As recommended by our TA's [feedback](https://github.com/UBC-MDS/DSCI_532_Group_12/issues/44), a line graph showing new cases trend is added to the middle panel. - This change fills up most of the space under the world map, which makes the layout more balanced +- As recommended by our TA's [feedback](https://github.com/UBC-MDS/DSCI_532_Group_12/issues/44), a line graph showing new cases trend is added to the middle panel. This change fills up most of the space under the world map, which makes the layout more balanced - We adjusted the scale of the bubbles showing number of cases on the world map to highlight better the difference of Covid-19's impact among countries