Skip to content

Instantly share code, notes, and snippets.

@alysivji
Created December 23, 2017 13:02
Show Gist options
  • Save alysivji/42cee0fc83b824d7f6a462ddced3bf24 to your computer and use it in GitHub Desktop.
Save alysivji/42cee0fc83b824d7f6a462ddced3bf24 to your computer and use it in GitHub Desktop.
troubleshooting-soccer-dashboard-20171223
# standard library
import os
# dash libs
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.figure_factory as ff
import plotly.graph_objs as go
# pydata stack
import pandas as pd
from sqlalchemy import create_engine
# set params
conn = create_engine(os.environ['DB_URI'])
###########################
# Data Manipulation / Model
###########################
def fetch_data(q):
result = pd.read_sql(
sql=q,
con=conn
)
return result
def get_divisions():
'''Returns the list of divisions that are stored in the database'''
division_query = (
f'''
SELECT DISTINCT division
FROM results
'''
)
divisions = fetch_data(division_query)
divisions = list(divisions['division'].sort_values(ascending=True))
return divisions
def get_seasons(division):
'''Returns the seasons of the datbase store'''
seasons_query = (
f'''
SELECT DISTINCT season
FROM results
WHERE division='{division}'
'''
)
seasons = fetch_data(seasons_query)
seasons = list(seasons['season'].sort_values(ascending=False))
return seasons
def get_teams(division, season):
'''Returns all teams playing in the division in the season'''
teams_query = (
f'''
SELECT DISTINCT team
FROM results
WHERE division='{division}'
AND season='{season}'
'''
)
teams = fetch_data(teams_query)
teams = list(teams['team'].sort_values(ascending=True))
return teams
def get_match_results(division, season, team):
'''Returns match results for the selected prompts'''
results_query = (
f'''
SELECT date, team, opponent, goals, goals_opp, result, points
FROM results
WHERE division='{division}'
AND season='{season}'
AND team='{team}'
ORDER BY date ASC
'''
)
match_results = fetch_data(results_query)
return match_results
def calculate_season_summary(results):
record = results.groupby(by=['result'])['team'].count()
summary = pd.DataFrame(
data={
'W': record['W'],
'L': record['L'],
'D': record['D'],
'Points': results['points'].sum()
},
columns=['W', 'D', 'L', 'Points'],
index=results['team'].unique(),
)
return summary
def draw_season_points_graph(results):
dates = results['date']
points = results['points'].cumsum()
figure = go.Figure(
data=[
go.Scatter(x=dates, y=points, mode='lines+markers')
],
layout=go.Layout(
title='Points Accumulation',
showlegend=False
)
)
return figure
#########################
# Dashboard Layout / View
#########################
def generate_table(dataframe, max_rows=10):
'''Given dataframe, return template generated using Dash components
'''
return html.Table(
# Header
[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
[html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))]
)
def onLoad_division_options():
'''Actions to perform upon initial page load'''
division_options = (
[{'label': division, 'value': division}
for division in get_divisions()]
)
return division_options
# Set up Dashboard and create layout
app = dash.Dash()
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
app.layout = html.Div([
# Page Header
html.Div([
html.H1('Soccer Results Viewer')
]),
# Dropdown Grid
html.Div([
html.Div([
# Select Division Dropdown
html.Div([
html.Div('Select Division', className='three columns'),
html.Div(dcc.Dropdown(id='division-selector',
options=onLoad_division_options()),
className='nine columns')
]),
# Select Season Dropdown
html.Div([
html.Div('Select Season', className='three columns'),
html.Div(dcc.Dropdown(id='season-selector'),
className='nine columns')
]),
# Select Team Dropdown
html.Div([
html.Div('Select Team', className='three columns'),
html.Div(dcc.Dropdown(id='team-selector'),
className='nine columns')
]),
], className='six columns'),
# Empty
html.Div(className='six columns'),
], className='twleve columns'),
# Match Results Grid
html.Div([
# Match Results Table
html.Div(
html.Table(id='match-results'),
className='six columns'
),
# Season Summary Table and Graph
html.Div([
# summary table
dcc.Graph(id='season-summary'),
# graph
dcc.Graph(id='season-graph')
# style={},
], className='six columns')
]),
])
#############################################
# Interaction Between Components / Controller
#############################################
# Load Seasons in Dropdown
@app.callback(
Output(component_id='season-selector', component_property='options'),
[
Input(component_id='division-selector', component_property='value')
]
)
def populate_season_selector(division):
seasons = get_seasons(division)
return [
{'label': season, 'value': season}
for season in seasons
]
# Load Teams into dropdown
@app.callback(
Output(component_id='team-selector', component_property='options'),
[
Input(component_id='division-selector', component_property='value'),
Input(component_id='season-selector', component_property='value')
]
)
def populate_team_selector(division, season):
teams = get_teams(division, season)
return [
{'label': team, 'value': team}
for team in teams
]
# Load Match results
@app.callback(
Output(component_id='match-results', component_property='children'),
[
Input(component_id='division-selector', component_property='value'),
Input(component_id='season-selector', component_property='value'),
Input(component_id='team-selector', component_property='value')
]
)
def load_match_results(division, season, team):
results = get_match_results(division, season, team)
return generate_table(results, max_rows=50)
# Update Season Summary Table
@app.callback(
Output(component_id='season-summary', component_property='figure'),
[
Input(component_id='division-selector', component_property='value'),
Input(component_id='season-selector', component_property='value'),
Input(component_id='team-selector', component_property='value')
]
)
def load_season_summary(division, season, team):
results = get_match_results(division, season, team)
table = []
if len(results) > 0:
summary = calculate_season_summary(results)
table = ff.create_table(summary)
return table
# Update Season Point Graph
@app.callback(
Output(component_id='season-graph', component_property='figure'),
[
Input(component_id='division-selector', component_property='value'),
Input(component_id='season-selector', component_property='value'),
Input(component_id='team-selector', component_property='value')
]
)
def load_season_points_graph(division, season, team):
results = get_match_results(division, season, team)
figure = []
if len(results) > 0:
figure = draw_season_points_graph(results)
return figure
# start Flask server
if __name__ == '__main__':
app.run_server(
debug=True,
host='0.0.0.0',
port=8050
)
certifi==2017.7.27.1
chardet==3.0.4
click==6.7
dash==0.17.7
dash-core-components==0.12.0
dash-html-components==0.7.0
dash-renderer==0.7.4
decorator==4.1.2
Flask==0.12.2
Flask-Compress==1.4.0
Flask-SeaSurf==0.2.2
idna==2.6
ipython-genutils==0.2.0
itsdangerous==0.24
Jinja2==2.9.6
jsonschema==2.6.0
jupyter-core==4.3.0
MarkupSafe==1.0
nbformat==4.3.0
numpy==1.13.1
pandas==0.20.3
plotly==2.0.13
psycopg2==2.7.3
python-dateutil==2.6.1
pytz==2017.2
requests==2.18.4
six==1.10.0
SQLAlchemy==1.1.13
traitlets==4.3.2
urllib3==1.22
Werkzeug==0.12.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment