mirror of
https://github.com/didyouexpectthat/cs-340.git
synced 2024-12-03 01:50:17 -08:00
460 lines
14 KiB
Plaintext
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
|
|
}
|