Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes v2 dev website dashboard not loading #1256

Merged
merged 19 commits into from
Jul 15, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 145 additions & 44 deletions server/dash/dashboards/neighborhood_recent.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output
import pandas as pd
import plotly.express as px
from flask import request
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved

from app import app, batch_get_data
from config import API_HOST
from dash.dependencies import Input, Output
from design import CONFIG_OPTIONS, DISCRETE_COLORS, DISCRETE_COLORS_MAP, LABELS, apply_figure_style
from flask import request
from design import CONFIG_OPTIONS, DISCRETE_COLORS, LABELS, apply_figure_style, DISCRETE_COLORS_MAP


pretty_columns = {
'srnumber': "SR Number",
Expand All @@ -22,8 +24,10 @@
'address': "Address"
}

start_date = datetime.date.today() - datetime.timedelta(days=7)
end_date = datetime.date.today() - datetime.timedelta(days=1)
START_DATE_DELTA = 7
END_DATE_DELTA = 1
start_date = datetime.date.today() - datetime.timedelta(days=START_DATE_DELTA)
end_date = datetime.date.today() - datetime.timedelta(days=END_DATE_DELTA)

# TITLE
title = "NEIGHBORHOOD WEEKLY REPORT"
Expand All @@ -44,11 +48,38 @@
selected_council = 'Arleta'

table_df = df.query(f"councilName == '{selected_council}'")[['srnumber', 'createdDate', 'closedDate', 'typeName', 'agencyName', 'sourceName', 'address']] # noqa
figure_df = df.query(f"councilName == '{selected_council}' and createdDate >= '{start_date}'").groupby(['createdDate', 'typeName'])['srnumber'].count().reset_index() # noqa

req_type_line_base_graph = px.line()
apply_figure_style(req_type_line_base_graph)

req_type_pie_base_graph = px.pie()
apply_figure_style(req_type_pie_base_graph)

# Populate the neighborhood dropdown
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
def populate_options():
"""Gets a list of neighborhood councils to populate the dropdown menu.

This function calls the councils API to get a list of neighborhood council and
return a unique list of council as a dictionary sorted in ascending order of requests.

Returns:
A list of dictionaries mapping label and value to corresponding councilName ordered by the
total number of requests. For example:

[
{'label': 'Arleta', 'value': 'Arleta'},
{'label': 'Arroyo Seco', 'value': 'Arroyo Seco'},
...
]

Typical usage example:

dcc.Dropdown(
...
options=populate_options()
...
)
"""
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
council_df_path = '/councils'
council_df = pd.read_json(API_HOST + council_df_path)
values = []
Expand All @@ -60,39 +91,6 @@ def populate_options():
return values


fig = px.line(
figure_df,
x="createdDate",
y="srnumber",
color="typeName",
color_discrete_sequence=DISCRETE_COLORS,
color_discrete_map=DISCRETE_COLORS_MAP,
labels=LABELS,
)

fig.update_xaxes(
tickformat="%a\n%m/%d",
)

fig.update_traces(
mode='markers+lines'
) # add markers to lines

apply_figure_style(fig)

pie_fig = px.pie(
figure_df,
names="typeName",
values="srnumber",
color="typeName",
color_discrete_sequence=DISCRETE_COLORS,
color_discrete_map=DISCRETE_COLORS_MAP,
labels=LABELS,
hole=.3,
)
apply_figure_style(pie_fig)


# Layout
layout = html.Div([
html.H1(title),
Expand Down Expand Up @@ -120,11 +118,11 @@ def populate_options():
], className="graph-row"),
html.Div([
html.Div(
dcc.Graph(id='graph', figure=fig, config=CONFIG_OPTIONS),
dcc.Graph(id='graph', figure=req_type_line_base_graph, config=CONFIG_OPTIONS),
className="half-graph"
),
html.Div(
dcc.Graph(id='pie_graph', figure=pie_fig, config=CONFIG_OPTIONS),
dcc.Graph(id='pie_graph', figure=req_type_pie_base_graph, config=CONFIG_OPTIONS),
className="half-graph"
)
]),
Expand Down Expand Up @@ -161,7 +159,32 @@ def populate_options():
Input("council_list", "value")
)
def update_table(selected_council):
"""Filters the LA 311 request data table based on selected_council.

This function takes the selected neighborhood council (nc) value from the "council_list" dropdown and
outputs a list of requests associated with that nc as a data table in dictionary form
with id "council_table" in the layout.

Args:
selected_council: A string argument automatically detected by Dash callback function when "council_list" element is selected in the layout.

Returns:
A list of dictionaries mapping column names to values. For example: [{'srnumber':1234567, 'createdDate':'2022-07-11', 'closeDate':'2022-07-14'...}, {...}, ... ]

Typical usage example:

dash_table.DataTable(
id='council_table',
...
)
"""
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
table_df = df.query(f"councilName == '{selected_council}'")[['srnumber', 'createdDate', 'closedDate', 'typeName', 'agencyName', 'sourceName', 'address']] # noqa

# The following check is to ensure Dash graphs are populated with dummy data when query returns empty dataframe.
if table_df.shape[0] == 0:
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
table_df = pd.DataFrame(columns=["Request Type"])
for i, request_type in enumerate(DISCRETE_COLORS_MAP):
table_df.loc[i] = [request_type]
return table_df.to_dict('records')


Expand All @@ -174,19 +197,73 @@ def update_table(selected_council):
Input("council_list", "value")
)
def update_text(selected_council):
"""Updates the indicator cards based on data filtered by selected_council.

This function takes the selected neighborhood council (nc) value from the "council_list" dropdown and
outputs the values for the number of new requests, number of closed requests, and net change in requests
(i.e. # closed requests - # new requests) for visualizations on the indicator visuals in the layout. The
corresponding IDs of the indicator visuals are "created_txt", "closed_txt", and "net_txt".

Args:
selected_council: a string argument automatically detected by Dash callback function when "council_list" element is selected in the layout.

Returns:
A tuple containing three integers:
1) Integer for the number of new requests created since the start date.
2) Integer for the number of close requests since the start date.
3) Integer for the difference in close requests and new requests since the start date.

Typical usage example (using net_txt as example, created_txt and closed_txt are similar):

html.Div(
[html.H2(id="net_txt"), html.Label("Net Change")],
className="stats-label"
)
"""
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
create_count = df.query(f"councilName == '{selected_council}' and createdDate >= '{start_date}'")['srnumber'].count() # noqa
close_count = df.query(f"councilName == '{selected_council}' and closedDate >= '{start_date}'")['srnumber'].count() # noqa
return create_count, close_count, create_count - close_count

# This check is to ensure data quality issues don't flow downstream to the dashboard (i.e., closed requests exist without any new requests).
if create_count == 0 and close_count > 0:
return 0, 0, 0
else:
return create_count, close_count, close_count - create_count


@app.callback(
Output("graph", "figure"),
Input("council_list", "value")
)
def update_figure(selected_council):
"""Updates the Request Type Line Chart based on data filtered by selected_council.

This function takes the selected neighborhood council (nc) value from the "council_list" dropdown and
outputs the request type line chart that shows the trend of of different requests types over
the time range of the data available in the selected neighborhood conucil. The line chart will
show up inside dcc.Graph object as long as id "graph" is passed in.

Args:
selected_council: a string argument automatically detected by Dash callback function when "council_list" element is selected in the layout.

Returns:
Plotly line chart of the total number of requests over time (createdDate) separated by request type.

Typical usage example:

html.Div(
dcc.Graph(id='graph',
...
)
"""
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
figure_df = df.query(f"councilName == '{selected_council}' and createdDate >= '{start_date}'").groupby(['createdDate', 'typeName'])['srnumber'].count().reset_index() # noqa
figure_df.typeName = figure_df.typeName.map(lambda x: '<br>'.join(textwrap.wrap(x, width=16))) # noqa

# The following check is to ensure Dash graphs are populated with dummy data when query returns empty dataframe.
if figure_df.shape[0] == 0:
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
figure_df = pd.DataFrame(columns=["createdDate", "srnumber", "typeName"])
for j in range(START_DATE_DELTA):
for request_type in DISCRETE_COLORS_MAP:
figure_df.loc[figure_df.shape[0]] = [start_date + datetime.timedelta(days=j), 0, request_type]
figure_df.typeName = figure_df.typeName.map(lambda x: '<br>'.join(textwrap.wrap(x, width=16))) # noqa
fig = px.line(
figure_df,
x="createdDate",
Expand All @@ -209,12 +286,36 @@ def update_figure(selected_council):


@app.callback(
Output("pie_graph", "pie_fig"),
Output("pie_graph", "figure"),
Input("council_list", "value")
)
def update_council_figure(selected_council):
"""Updates the Request Type Pie Chart based on data filtered by selected_council.

This function takes the selected neighborhood council (nc) value from the "council_list" dropdown and
outputs the the pie chart showing the share of each request types. The pie chart will
show up inside dcc.Graph object as long as id "pie_graph" is passed in.

Args:
selected_council: a string argument automatically detected by Dash callback function when "council_list" element is selected in the layout.

Returns:
Plotly pie chart for the share of different request types.

Typical usage example:

html.Div(
dcc.Graph(id='pie_graph',
...
)
"""
pie_df = df.query(f"councilName == '{selected_council}' and createdDate >= '{start_date}'").groupby(['typeName']).agg('count').reset_index() # noqa

# The following check is to ensure Dash graphs are populated with dummy data when query returns empty dataframe.
if pie_df.shape[0] == 0:
pie_df = pd.DataFrame(columns=["srnumber", "typeName"])
for i, request_type in enumerate(DISCRETE_COLORS_MAP):
pie_df.loc[i] = [1, request_type]
joshuayhwu marked this conversation as resolved.
Show resolved Hide resolved
pie_fig = px.pie(
pie_df,
names="typeName",
Expand Down