diff --git a/apps/dash-clinical-analytics/app.py b/apps/dash-clinical-analytics/app.py index cec2c8b31..8b288951b 100644 --- a/apps/dash-clinical-analytics/app.py +++ b/apps/dash-clinical-analytics/app.py @@ -1,382 +1,382 @@ -# import dash -# # import dash_core_components as dcc -# # import dash_html_components as html -# # from dash.dependencies import Input, Output, ClientsideFunction -# from dash import dcc, html, Input, Output, ClientsideFunction -# import numpy as np -# import pandas as pd -# import datetime -# from datetime import datetime as dt -# import pathlib - -# app = dash.Dash( -# __name__, -# meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}], -# ) -# app.title = "Clinical Analytics Dashboard" - -# server = app.server -# app.config.suppress_callback_exceptions = True - -# # Path -# BASE_PATH = pathlib.Path(__file__).parent.resolve() -# DATA_PATH = BASE_PATH.joinpath("data").resolve() - -# # Read data -# df = pd.read_csv(DATA_PATH.joinpath("clinical_analytics.csv.gz")) - -# clinic_list = df["Clinic Name"].unique() -# df["Admit Source"] = df["Admit Source"].fillna("Not Identified") -# admit_list = df["Admit Source"].unique().tolist() - -# # Date -# # Format checkin Time -# df["Check-In Time"] = df["Check-In Time"].apply( -# lambda x: dt.strptime(x, "%Y-%m-%d %I:%M:%S %p") -# ) # String -> Datetime - -# # Insert weekday and hour of checkin time -# df["Days of Wk"] = df["Check-In Hour"] = df["Check-In Time"] -# df["Days of Wk"] = df["Days of Wk"].apply( -# lambda x: dt.strftime(x, "%A") -# ) # Datetime -> weekday string - -# df["Check-In Hour"] = df["Check-In Hour"].apply( -# lambda x: dt.strftime(x, "%I %p") -# ) # Datetime -> int(hour) + AM/PM - -# day_list = [ -# "Monday", -# "Tuesday", -# "Wednesday", -# "Thursday", -# "Friday", -# "Saturday", -# "Sunday", -# ] - -# check_in_duration = df["Check-In Time"].describe() - -# # Register all departments for callbacks -# all_departments = df["Department"].unique().tolist() -# wait_time_inputs = [ -# Input((i + "_wait_time_graph"), "selectedData") for i in all_departments -# ] -# score_inputs = [Input((i + "_score_graph"), "selectedData") for i in all_departments] - - -# def description_card(): -# """ - -# :return: A Div containing dashboard title & descriptions. -# """ -# return html.Div( -# id="description-card", -# children=[ -# html.H5("Clinical Analytics"), -# html.H3("Welcome to the Clinical Analytics Dashboard"), -# html.Div( -# id="intro", -# children="Explore clinic patient volume by time of day, waiting time, and care score. Click on the heatmap to visualize patient experience at different time points.", -# ), -# ], -# ) - - -# def generate_control_card(): -# """ - -# :return: A Div containing controls for graphs. -# """ -# return html.Div( -# id="control-card", -# children=[ -# html.P("Select Clinic"), -# dcc.Dropdown( -# id="clinic-select", -# options=[{"label": i, "value": i} for i in clinic_list], -# value=clinic_list[0], -# ), -# html.Br(), -# html.P("Select Check-In Time"), -# dcc.DatePickerRange( -# id="date-picker-select", -# start_date=dt(2014, 1, 1), -# end_date=dt(2014, 1, 15), -# min_date_allowed=dt(2014, 1, 1), -# max_date_allowed=dt(2014, 12, 31), -# initial_visible_month=dt(2014, 1, 1), -# ), -# html.Br(), -# html.Br(), -# html.P("Select Admit Source"), -# dcc.Dropdown( -# id="admit-select", -# options=[{"label": i, "value": i} for i in admit_list], -# value=admit_list[:], -# multi=True, -# ), -# html.Br(), -# # html.Div( -# # id="reset-btn-outer", -# # children=html.Button(id="reset-btn", children="Reset", n_clicks=0), -# # ), -# ], -# ) - - -# def generate_patient_volume_heatmap(start, end, clinic, hm_click, admit_type, reset): -# """ -# :param: start: start date from selection. -# :param: end: end date from selection. -# :param: clinic: clinic from selection. -# :param: hm_click: clickData from heatmap. -# :param: admit_type: admission type from selection. -# :param: reset (boolean): reset heatmap graph if True. - -# :return: Patient volume annotated heatmap. -# """ - -# filtered_df = df[ -# (df["Clinic Name"] == clinic) & (df["Admit Source"].isin(admit_type)) -# ] -# filtered_df = filtered_df.sort_values("Check-In Time").set_index("Check-In Time")[ -# start:end -# ] - -# x_axis = [datetime.time(i).strftime("%I %p") for i in range(24)] # 24hr time list -# y_axis = day_list - -# hour_of_day = "" -# weekday = "" -# shapes = [] - -# if hm_click is not None: -# hour_of_day = hm_click["points"][0]["x"] -# weekday = hm_click["points"][0]["y"] - -# # Add shapes -# x0 = x_axis.index(hour_of_day) / 24 -# x1 = x0 + 1 / 24 -# y0 = y_axis.index(weekday) / 7 -# y1 = y0 + 1 / 7 - -# shapes = [ -# dict( -# type="rect", -# xref="paper", -# yref="paper", -# x0=x0, -# x1=x1, -# y0=y0, -# y1=y1, -# line=dict(color="#ff6347"), -# ) -# ] - -# # Get z value : sum(number of records) based on x, y, - -# z = np.zeros((7, 24)) -# annotations = [] - -# for ind_y, day in enumerate(y_axis): -# filtered_day = filtered_df[filtered_df["Days of Wk"] == day] -# for ind_x, x_val in enumerate(x_axis): -# sum_of_record = filtered_day[filtered_day["Check-In Hour"] == x_val][ -# "Number of Records" -# ].sum() -# z[ind_y][ind_x] = sum_of_record - -# annotation_dict = dict( -# showarrow=False, -# text="" + str(sum_of_record) + "", -# xref="x", -# yref="y", -# x=x_val, -# y=day, -# font=dict(family="sans-serif"), -# ) -# # Highlight annotation text by self-click -# if x_val == hour_of_day and day == weekday: -# if not reset: -# annotation_dict.update(size=15, font=dict(color="#ff6347")) - -# annotations.append(annotation_dict) - -# # Heatmap -# hovertemplate = " %{y} %{x}

%{z} Patient Records" - -# data = [ -# dict( -# x=x_axis, -# y=y_axis, -# z=z, -# type="heatmap", -# name="", -# hovertemplate=hovertemplate, -# showscale=False, -# colorscale=[[0, "#caf3ff"], [1, "#2c82ff"]], -# ) -# ] - -# layout = dict( -# margin=dict(l=70, b=50, t=50, r=50), -# modebar={"orientation": "v"}, -# font=dict(family="Open Sans"), -# annotations=annotations, -# shapes=shapes, -# xaxis=dict( -# side="top", -# ticks="", -# ticklen=2, -# tickfont=dict(family="sans-serif"), -# tickcolor="#ffffff", -# ), -# yaxis=dict( -# side="left", ticks="", tickfont=dict(family="sans-serif"), ticksuffix=" " -# ), -# hovermode="closest", -# showlegend=False, -# ) -# return {"data": data, "layout": layout} - - -# def generate_table_row(id, style, col1, col2, col3): -# """ Generate table rows. - -# :param id: The ID of table row. -# :param style: Css style of this row. -# :param col1 (dict): Defining id and children for the first column. -# :param col2 (dict): Defining id and children for the second column. -# :param col3 (dict): Defining id and children for the third column. -# """ - -# return html.Div( -# id=id, -# className="row table-row", -# style=style, -# children=[ -# html.Div( -# id=col1["id"], -# style={"display": "table", "height": "100%"}, -# className="two columns row-department", -# children=col1["children"], -# ), -# html.Div( -# id=col2["id"], -# style={"textAlign": "center", "height": "100%"}, -# className="five columns", -# children=col2["children"], -# ), -# html.Div( -# id=col3["id"], -# style={"textAlign": "center", "height": "100%"}, -# className="five columns", -# children=col3["children"], -# ), -# ], -# ) - - -# def generate_table_row_helper(department): -# """Helper function. - -# :param: department (string): Name of department. -# :return: Table row. -# """ -# return generate_table_row( -# department, -# {}, -# {"id": department + "_department", "children": html.B(department)}, -# { -# "id": department + "wait_time", -# "children": dcc.Graph( -# id=department + "_wait_time_graph", -# style={"height": "100%", "width": "100%"}, -# className="wait_time_graph", -# config={ -# "staticPlot": False, -# "editable": False, -# "displayModeBar": False, -# }, -# figure={ -# "layout": dict( -# margin=dict(l=0, r=0, b=0, t=0, pad=0), -# xaxis=dict( -# showgrid=False, -# showline=False, -# showticklabels=False, -# zeroline=False, -# ), -# yaxis=dict( -# showgrid=False, -# showline=False, -# showticklabels=False, -# zeroline=False, -# ), -# paper_bgcolor="rgba(0,0,0,0)", -# plot_bgcolor="rgba(0,0,0,0)", -# ) -# }, -# ), -# }, -# { -# "id": department + "_patient_score", -# "children": dcc.Graph( -# id=department + "_score_graph", -# style={"height": "100%", "width": "100%"}, -# className="patient_score_graph", -# config={ -# "staticPlot": False, -# "editable": False, -# "displayModeBar": False, -# }, -# figure={ -# "layout": dict( -# margin=dict(l=0, r=0, b=0, t=0, pad=0), -# xaxis=dict( -# showgrid=False, -# showline=False, -# showticklabels=False, -# zeroline=False, -# ), -# yaxis=dict( -# showgrid=False, -# showline=False, -# showticklabels=False, -# zeroline=False, -# ), -# paper_bgcolor="rgba(0,0,0,0)", -# plot_bgcolor="rgba(0,0,0,0)", -# ) -# }, -# ), -# }, -# ) - - -# def initialize_table(): -# """ -# :return: empty table children. This is intialized for registering all figure ID at page load. -# """ - -# # header_row -# header = [ -# generate_table_row( -# "header", -# {"height": "50px"}, -# {"id": "header_department", "children": html.B("Department")}, -# {"id": "header_wait_time_min", "children": html.B("Wait Time Minutes")}, -# {"id": "header_care_score", "children": html.B("Care Score")}, -# ) -# ] - -# # department_row -# rows = [generate_table_row_helper(department) for department in all_departments] -# header.extend(rows) -# empty_table = header - -# return empty_table +import dash +# import dash_core_components as dcc +# import dash_html_components as html +# from dash.dependencies import Input, Output, ClientsideFunction +from dash import dcc, html, Input, Output, ClientsideFunction +import numpy as np +import pandas as pd +import datetime +from datetime import datetime as dt +import pathlib + +app = dash.Dash( + __name__, + meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}], +) +app.title = "Clinical Analytics Dashboard" + +server = app.server +app.config.suppress_callback_exceptions = True + +# Path +BASE_PATH = pathlib.Path(__file__).parent.resolve() +DATA_PATH = BASE_PATH.joinpath("data").resolve() + +# Read data +df = pd.read_csv(DATA_PATH.joinpath("clinical_analytics.csv.gz")) + +clinic_list = df["Clinic Name"].unique() +df["Admit Source"] = df["Admit Source"].fillna("Not Identified") +admit_list = df["Admit Source"].unique().tolist() + +# Date +# Format checkin Time +df["Check-In Time"] = df["Check-In Time"].apply( + lambda x: dt.strptime(x, "%Y-%m-%d %I:%M:%S %p") +) # String -> Datetime + +# Insert weekday and hour of checkin time +df["Days of Wk"] = df["Check-In Hour"] = df["Check-In Time"] +df["Days of Wk"] = df["Days of Wk"].apply( + lambda x: dt.strftime(x, "%A") +) # Datetime -> weekday string + +df["Check-In Hour"] = df["Check-In Hour"].apply( + lambda x: dt.strftime(x, "%I %p") +) # Datetime -> int(hour) + AM/PM + +day_list = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +] + +check_in_duration = df["Check-In Time"].describe() + +# Register all departments for callbacks +all_departments = df["Department"].unique().tolist() +wait_time_inputs = [ + Input((i + "_wait_time_graph"), "selectedData") for i in all_departments +] +score_inputs = [Input((i + "_score_graph"), "selectedData") for i in all_departments] + + +def description_card(): + """ + + :return: A Div containing dashboard title & descriptions. + """ + return html.Div( + id="description-card", + children=[ + html.H5("Clinical Analytics"), + html.H3("Welcome to the Clinical Analytics Dashboard"), + html.Div( + id="intro", + children="Explore clinic patient volume by time of day, waiting time, and care score. Click on the heatmap to visualize patient experience at different time points.", + ), + ], + ) + + +def generate_control_card(): + """ + + :return: A Div containing controls for graphs. + """ + return html.Div( + id="control-card", + children=[ + html.P("Select Clinic"), + dcc.Dropdown( + id="clinic-select", + options=[{"label": i, "value": i} for i in clinic_list], + value=clinic_list[0], + ), + html.Br(), + html.P("Select Check-In Time"), + dcc.DatePickerRange( + id="date-picker-select", + start_date=dt(2014, 1, 1), + end_date=dt(2014, 1, 15), + min_date_allowed=dt(2014, 1, 1), + max_date_allowed=dt(2014, 12, 31), + initial_visible_month=dt(2014, 1, 1), + ), + html.Br(), + html.Br(), + html.P("Select Admit Source"), + dcc.Dropdown( + id="admit-select", + options=[{"label": i, "value": i} for i in admit_list], + value=admit_list[:], + multi=True, + ), + html.Br(), + # html.Div( + # id="reset-btn-outer", + # children=html.Button(id="reset-btn", children="Reset", n_clicks=0), + # ), + ], + ) + + +def generate_patient_volume_heatmap(start, end, clinic, hm_click, admit_type, reset): + """ + :param: start: start date from selection. + :param: end: end date from selection. + :param: clinic: clinic from selection. + :param: hm_click: clickData from heatmap. + :param: admit_type: admission type from selection. + :param: reset (boolean): reset heatmap graph if True. + + :return: Patient volume annotated heatmap. + """ + + filtered_df = df[ + (df["Clinic Name"] == clinic) & (df["Admit Source"].isin(admit_type)) + ] + filtered_df = filtered_df.sort_values("Check-In Time").set_index("Check-In Time")[ + start:end + ] + + x_axis = [datetime.time(i).strftime("%I %p") for i in range(24)] # 24hr time list + y_axis = day_list + + hour_of_day = "" + weekday = "" + shapes = [] + + if hm_click is not None: + hour_of_day = hm_click["points"][0]["x"] + weekday = hm_click["points"][0]["y"] + + # Add shapes + x0 = x_axis.index(hour_of_day) / 24 + x1 = x0 + 1 / 24 + y0 = y_axis.index(weekday) / 7 + y1 = y0 + 1 / 7 + + shapes = [ + dict( + type="rect", + xref="paper", + yref="paper", + x0=x0, + x1=x1, + y0=y0, + y1=y1, + line=dict(color="#ff6347"), + ) + ] + + # Get z value : sum(number of records) based on x, y, + + z = np.zeros((7, 24)) + annotations = [] + + for ind_y, day in enumerate(y_axis): + filtered_day = filtered_df[filtered_df["Days of Wk"] == day] + for ind_x, x_val in enumerate(x_axis): + sum_of_record = filtered_day[filtered_day["Check-In Hour"] == x_val][ + "Number of Records" + ].sum() + z[ind_y][ind_x] = sum_of_record + + annotation_dict = dict( + showarrow=False, + text="" + str(sum_of_record) + "", + xref="x", + yref="y", + x=x_val, + y=day, + font=dict(family="sans-serif"), + ) + # Highlight annotation text by self-click + if x_val == hour_of_day and day == weekday: + if not reset: + annotation_dict.update(size=15, font=dict(color="#ff6347")) + + annotations.append(annotation_dict) + + # Heatmap + hovertemplate = " %{y} %{x}

%{z} Patient Records" + + data = [ + dict( + x=x_axis, + y=y_axis, + z=z, + type="heatmap", + name="", + hovertemplate=hovertemplate, + showscale=False, + colorscale=[[0, "#caf3ff"], [1, "#2c82ff"]], + ) + ] + + layout = dict( + margin=dict(l=70, b=50, t=50, r=50), + modebar={"orientation": "v"}, + font=dict(family="Open Sans"), + annotations=annotations, + shapes=shapes, + xaxis=dict( + side="top", + ticks="", + ticklen=2, + tickfont=dict(family="sans-serif"), + tickcolor="#ffffff", + ), + yaxis=dict( + side="left", ticks="", tickfont=dict(family="sans-serif"), ticksuffix=" " + ), + hovermode="closest", + showlegend=False, + ) + return {"data": data, "layout": layout} + + +def generate_table_row(id, style, col1, col2, col3): + """ Generate table rows. + + :param id: The ID of table row. + :param style: Css style of this row. + :param col1 (dict): Defining id and children for the first column. + :param col2 (dict): Defining id and children for the second column. + :param col3 (dict): Defining id and children for the third column. + """ + + return html.Div( + id=id, + className="row table-row", + style=style, + children=[ + html.Div( + id=col1["id"], + style={"display": "table", "height": "100%"}, + className="two columns row-department", + children=col1["children"], + ), + html.Div( + id=col2["id"], + style={"textAlign": "center", "height": "100%"}, + className="five columns", + children=col2["children"], + ), + html.Div( + id=col3["id"], + style={"textAlign": "center", "height": "100%"}, + className="five columns", + children=col3["children"], + ), + ], + ) + + +def generate_table_row_helper(department): + """Helper function. + + :param: department (string): Name of department. + :return: Table row. + """ + return generate_table_row( + department, + {}, + {"id": department + "_department", "children": html.B(department)}, + { + "id": department + "wait_time", + "children": dcc.Graph( + id=department + "_wait_time_graph", + style={"height": "100%", "width": "100%"}, + className="wait_time_graph", + config={ + "staticPlot": False, + "editable": False, + "displayModeBar": False, + }, + figure={ + "layout": dict( + margin=dict(l=0, r=0, b=0, t=0, pad=0), + xaxis=dict( + showgrid=False, + showline=False, + showticklabels=False, + zeroline=False, + ), + yaxis=dict( + showgrid=False, + showline=False, + showticklabels=False, + zeroline=False, + ), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + ) + }, + ), + }, + { + "id": department + "_patient_score", + "children": dcc.Graph( + id=department + "_score_graph", + style={"height": "100%", "width": "100%"}, + className="patient_score_graph", + config={ + "staticPlot": False, + "editable": False, + "displayModeBar": False, + }, + figure={ + "layout": dict( + margin=dict(l=0, r=0, b=0, t=0, pad=0), + xaxis=dict( + showgrid=False, + showline=False, + showticklabels=False, + zeroline=False, + ), + yaxis=dict( + showgrid=False, + showline=False, + showticklabels=False, + zeroline=False, + ), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + ) + }, + ), + }, + ) + + +def initialize_table(): + """ + :return: empty table children. This is intialized for registering all figure ID at page load. + """ + + # header_row + header = [ + generate_table_row( + "header", + {"height": "50px"}, + {"id": "header_department", "children": html.B("Department")}, + {"id": "header_wait_time_min", "children": html.B("Wait Time Minutes")}, + {"id": "header_care_score", "children": html.B("Care Score")}, + ) + ] + + # department_row + rows = [generate_table_row_helper(department) for department in all_departments] + header.extend(rows) + empty_table = header + + return empty_table def generate_patient_table(figure_list, departments, wait_time_xrange, score_xrange): diff --git a/apps/dash-clinical-analytics/requirements.txt b/apps/dash-clinical-analytics/requirements.txt index aec9e8e0e..76424bc1c 100644 --- a/apps/dash-clinical-analytics/requirements.txt +++ b/apps/dash-clinical-analytics/requirements.txt @@ -3,5 +3,4 @@ gunicorn==20.0.4 numpy>=1.16.2 pandas>=1.5.1 datetime>=4.3 -pathlib>=1.0.1 -# Hello testtesttest \ No newline at end of file +pathlib>=1.0.1 \ No newline at end of file