cs-340/Dashboard.ipynb
2024-04-14 19:31:38 -07:00

460 lines
14 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "659038cc",
"metadata": {},
"outputs": [],
"source": [
"# Setup the Jupyter version of Dash\n",
"from jupyter_dash import JupyterDash"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1653e615",
"metadata": {},
"outputs": [],
"source": [
"# Configure the necessary Python module imports\n",
"import dash_leaflet as dl\n",
"from dash import dcc\n",
"from dash import html\n",
"import plotly.express as px\n",
"from dash import dash_table\n",
"from dash.dependencies import Input, Output, State\n",
"import base64"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b86503df",
"metadata": {},
"outputs": [],
"source": [
"# Configure OS routines\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "f21674e5",
"metadata": {},
"outputs": [],
"source": [
"# Configure the plotting routines\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "47fcde56",
"metadata": {},
"outputs": [],
"source": [
"# import the CRUD module\n",
"from AnimalShelter import AnimalShelter"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "0f0ff9f4",
"metadata": {},
"outputs": [],
"source": [
"# Instantiate the CRUD module\n",
"username = 'aacuser'\n",
"password = '1B@nana4U$'\n",
"hostname = 'nv-desktop-services.apporto.com'\n",
"port = 30909\n",
"database = 'AAC'\n",
"collection = 'animals'\n",
"db = AnimalShelter(username, password, hostname, port, database, collection)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "03f59f24",
"metadata": {},
"outputs": [],
"source": [
"# populate df with a dataframe of the items from the CRUD module\n",
"df = pd.DataFrame.from_records(db.read({}))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "49395346",
"metadata": {},
"outputs": [],
"source": [
"# Drop _id; but I actually already do this in the CRUD so I will ignore it\n",
"\n",
"#df.drop(columns=['_id'],inplace=True)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "4159b76b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10000\n",
"Index(['rec_num', 'age_upon_outcome', 'animal_id', 'animal_type', 'breed',\n",
" 'color', 'date_of_birth', 'datetime', 'monthyear', 'name',\n",
" 'outcome_subtype', 'outcome_type', 'sex_upon_outcome', 'location_lat',\n",
" 'location_long', 'age_upon_outcome_in_weeks'],\n",
" dtype='object')\n"
]
}
],
"source": [
"# Debugging to confirm records pulled\n",
"print(len(df.to_dict(orient='records')))\n",
"print(df.columns)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "3ed24c36",
"metadata": {},
"outputs": [],
"source": [
"# build the layout\n",
"app = JupyterDash(__name__)\n",
"image_filename = 'logo.png' \n",
"header1 = \"CS-340 Dashboard by Cody Cook\"\n",
"encoded_image = base64.b64encode(open(image_filename, 'rb').read())\n",
"\n",
"# define filter options\n",
"filter_options = [\n",
" {'label': 'Water Rescue', 'value': 'water'},\n",
" {'label': 'Mountain or Wilderness Rescue', 'value': 'mountain'},\n",
" {'label': 'Disaster or Individual Tracking', 'value': 'disaster'},\n",
" {'label': 'Reset', 'value': 'reset'}\n",
"]\n",
"\n",
"app.layout = html.Div([\n",
" html.Div([\n",
" html.Img(\n",
" # show image and set size to 100x100\n",
" src='data:image/png;base64,{}'.format(encoded_image.decode()),\n",
" style={'height': '100px', 'width': '100px', 'display': 'block', 'margin-left': 'auto', 'margin-right': 'auto'}\n",
" ),\n",
" # show header1\n",
" html.H1(header1, style={'textAlign': 'center'}),\n",
" ],\n",
" style={'text-align': 'center'}\n",
" ),\n",
" html.Hr(),\n",
" html.Div(\n",
" dcc.Dropdown(\n",
" id='filter-type',\n",
" options=[\n",
" # show the filter options\n",
" {'label': 'Water Rescue', 'value': 'water'},\n",
" {'label': 'Mountain or Wilderness Rescue', 'value': 'mountain'},\n",
" {'label': 'Disaster or Individual Tracking', 'value': 'disaster'},\n",
" {'label': 'Reset', 'value': 'reset'}\n",
" ],\n",
" value='reset', \n",
" clearable=False \n",
" )),\n",
" html.Hr(),\n",
" dash_table.DataTable(\n",
" id='datatable-id',\n",
" columns=[{\n",
" \"name\": i, \n",
" \"id\": i, \n",
" \"deletable\": False, \n",
" \"selectable\": True\n",
" } for i in df.columns],\n",
" data=df.to_dict('records'),\n",
" editable=False,\n",
" filter_action=\"native\",\n",
" sort_action=\"native\",\n",
" sort_mode=\"multi\",\n",
" column_selectable=\"single\",\n",
" row_selectable=\"single\",\n",
" page_action=\"native\",\n",
" page_current=0,\n",
" page_size=10,\n",
" selected_columns=[df.columns[0]]\n",
" ),\n",
" html.Br(),\n",
" html.Hr(),\n",
" html.Div(className='row',\n",
" style={'display' : 'flex'},\n",
" children=[\n",
" html.Div(\n",
" # show the graph\n",
" id='graph-id',\n",
" className='col s12 m6',\n",
"\n",
" ),\n",
" html.Div(\n",
" # show the map\n",
" id='map-id',\n",
" className='col s12 m6',\n",
" )\n",
" ])\n",
"])\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "8b29d287",
"metadata": {},
"outputs": [],
"source": [
"@app.callback(\n",
" Output('datatable-id', 'data'),\n",
" [Input('filter-type', 'value')]\n",
")\n",
"\n",
"def update_dashboard(filter_type):\n",
" # define what happens when the filter_type changes\n",
" if filter_type == 'water':\n",
" query = {\n",
" \"breed\": {\"$in\": [\n",
" \"Labrador Retriever Mix\", \n",
" \"Chesapeake Bay Retriever\", \n",
" \"Newfoundland\"]},\n",
" \"sex_upon_outcome\": \"Intact Female\",\n",
" \"age_upon_outcome_in_weeks\": {\n",
" \"$gte\": 26, \n",
" \"$lte\": 156}\n",
" }\n",
" elif filter_type == 'mountain':\n",
" query = {\n",
" \"breed\": {\"$in\": [\n",
" \"German Shepherd\", \n",
" \"Alaskan Malamute\", \n",
" \"Old English Sheepdog\", \n",
" \"Siberian Husky\", \n",
" \"Rottweiler\"]},\n",
" \"sex_upon_outcome\": \"Intact Male\",\n",
" \"age_upon_outcome_in_weeks\": {\n",
" \"$gte\": 26, \n",
" \"$lte\": 156}\n",
" }\n",
" elif filter_type == 'disaster':\n",
" query = {\n",
" \"breed\": {\"$in\": [\n",
" \"Doberman Pinscher\", \n",
" \"German Shepherd\", \n",
" \"Golden Retriever\", \n",
" \"Bloodhound\", \n",
" \"Rottweiler\"]},\n",
" \"sex_upon_outcome\": \"Intact Male\",\n",
" \"age_upon_outcome_in_weeks\": {\n",
" \"$gte\": 20, \n",
" \"$lte\": 300}\n",
" }\n",
" else:\n",
" # leave it unfiltered\n",
" query = {}\n",
"\n",
" # Fetch data using CRUD module and query\n",
" filtered_df = pd.DataFrame.from_records(db.read(query))\n",
" \n",
" # Ensure the '_id' column is not passed to the DataTable to prevent rendering issues\n",
" # I readded this and manually drop the _id without errors in case it already doesn't exist\n",
" filtered_df.drop(columns=['_id'], inplace=True, errors='ignore')\n",
"\n",
" # Convert the df to a compatible dictionary format\n",
" return filtered_df.to_dict('records')\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "f91a318d",
"metadata": {},
"outputs": [],
"source": [
"# Display the breed of animal based on quantity represented in the data table\n",
"@app.callback(\n",
" Output('graph-id', \"children\"),\n",
" [Input('datatable-id', \"derived_virtual_data\")]\n",
")\n",
"\n",
"def update_graphs(viewData):\n",
" # return nothing if the data is empty\n",
" if not viewData:\n",
" return None\n",
" \n",
" # if there are too many breeds, limit with \"other\"\n",
" dff = pd.DataFrame.from_dict(viewData)\n",
" breed_counts = dff['breed'].value_counts()\n",
" top_breeds = breed_counts.head(10)\n",
" other_count = breed_counts.iloc[10:].sum()\n",
" pie_title = \"Top 10 Breeds\"\n",
"\n",
" # Check if other_count is greater than 0\n",
" if other_count > 0:\n",
" # Create a Series for Other to concatenate with top_breeds\n",
" other_series = pd.Series({'Other': other_count})\n",
" # Use concat to merge the top breeds with the 'Other' series\n",
" chart_data = pd.concat([top_breeds, other_series], axis=0)\n",
" pie_title = \"Top 10 Breeds + Other\"\n",
" else:\n",
" # If 'other_count' is 0 or less, only use top_breeds\n",
" chart_data = top_breeds\n",
"\n",
"\n",
" chart_data = chart_data.reset_index()\n",
" chart_data.columns = ['Breed', 'Count']\n",
" \n",
" # Generate a pie chart\n",
" fig = px.pie(chart_data, values='Count', names='Breed', title=pie_title)\n",
" \n",
" return dcc.Graph(figure=fig)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "4c3537de",
"metadata": {},
"outputs": [],
"source": [
"# Highlight a selected column\n",
"@app.callback(\n",
" Output('datatable-id', 'style_data_conditional'),\n",
" [Input('datatable-id', 'selected_columns')]\n",
")\n",
"\n",
"def update_styles(selected_columns):\n",
" return [{\n",
" 'if': { 'column_id': i },\n",
" 'background_color': '#D2F3FF'\n",
" } for i in selected_columns]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d1c440a0",
"metadata": {},
"outputs": [],
"source": [
"# Update the geo-location chart with the selected item\n",
"@app.callback(\n",
" Output('map-id', \"children\"),\n",
" [Input('datatable-id', \"derived_virtual_data\"),\n",
" Input('datatable-id', \"derived_virtual_selected_rows\")]\n",
")\n",
"\n",
"def update_map(viewData, index):\n",
" # If viewData is empty or index is not set \n",
" if viewData is None or not index:\n",
" # Center on Austin, TX\n",
" return [\n",
" dl.Map(\n",
" style={\n",
" 'width': '1000px', \n",
" 'height': '500px'\n",
" },\n",
" center=[30.66, -97.40], \n",
" zoom=10, \n",
" children=[dl.TileLayer(id=\"base-layer-id\")])\n",
" ]\n",
" \n",
" # Convert viewData to DataFrame\n",
" dff = pd.DataFrame.from_dict(viewData)\n",
" row = index[0]\n",
"\n",
" # Latitude and longitude for the selected item\n",
" lat = dff.iloc[row, dff.columns.get_loc('location_lat')]\n",
" lon = dff.iloc[row, dff.columns.get_loc('location_long')]\n",
"\n",
" # Update map centering dynamically based on the selected item's location\n",
" return [\n",
" dl.Map(\n",
" style={\n",
" 'width': '1000px', \n",
" 'height': '500px'\n",
" }, \n",
" # reposition the map\n",
" center=[lat, lon], \n",
" zoom=10, \n",
" children=[\n",
" dl.TileLayer(id=\"base-layer-id\"),\n",
" dl.Marker(\n",
" # drop a point on the map\n",
" position=[lat, lon], \n",
" children=[\n",
" dl.Tooltip(\n",
" # show a tooltip on mouseover\n",
" dff.iloc[row, dff.columns.get_loc('breed')]\n",
" ),\n",
" dl.Popup([\n",
" # show a popup of the animal's name\n",
" html.H1(\"Animal Name\"),\n",
" html.P(dff.iloc[row, dff.columns.get_loc('name')]) \n",
" ]) \n",
" ])\n",
" ])\n",
" ]\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "eefb074c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dash app running on http://127.0.0.1:30088/\n"
]
}
],
"source": [
"app.run_server(debug=True)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}