diff --git a/apps/dash-wind-streaming/.gitignore b/apps/dash-wind-streaming/.gitignore
index f465a23fa..7cabaa742 100644
--- a/apps/dash-wind-streaming/.gitignore
+++ b/apps/dash-wind-streaming/.gitignore
@@ -1,5 +1,145 @@
-venv
-*.pyc
-.DS_Store
+# .gitignore specifies the files that shouldn't be included
+# in version control and therefore shouldn't be included when
+# deploying an application to Dash Enterprise
+# This is a very exhaustive list!
+# This list was based off of https://github.com/github/gitignore
+# Ignore data that is generated during the runtime of an application
+# This folder is used by the "Large Data" sample applications
+runtime_data/
+
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+# Jupyter Notebook
+.ipynb_checkpoints
+*/.ipynb_checkpoints/*
+# IPython
+profile_default/
+ipython_config.py
+# Environments
.env
-settings.json
\ No newline at end of file
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+# Spyder project settings
+.spyderproject
+.spyproject
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+# macOS General
+.DS_Store
+.AppleDouble
+.LSOverride
+# Icon must end with two \r
+Icon
+# Thumbnails
+._*
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+# Dump file
+*.stackdump
+# Folder config file
+[Dd]esktop.ini
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+# Windows shortcuts
+*.lnk
+# History files
+.Rhistory
+.Rapp.history
+# Session Data files
+.RData
+# User-specific files
+.Ruserdata
+# Example code in package build process
+*-Ex.R
+# Output files from R CMD check
+/*.Rcheck/
+# RStudio files
+.Rproj.user/
+# produced vignettes
+vignettes/*.html
+vignettes/*.pdf
+# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
+.httr-oauth
+# knitr and R markdown default cache directories
+*_cache/
+/cache/
+# Temporary files created by R markdown
+*.utf8.md
+*.knit.md
+# R Environment Variables
+.Renviron
+# Linux
+*~
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+# KDE directory preferences
+.directory
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+# VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+# SublineText
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+# Workspace files are user-specific
+*.sublime-workspace
+# Project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using Sublime Text
+# *.sublime-project
+# SFTP configuration file
+sftp-config.json
+# Package control specific files
+Package Control.last-run
+Package Control.ca-list
+Package Control.ca-bundle
+Package Control.system-ca-bundle
+Package Control.cache/
+Package Control.ca-certs/
+Package Control.merged-ca-bundle
+Package Control.user-ca-bundle
+oscrypto-ca-bundle.crt
+bh_unicode_properties.cache
+# Sublime-github package stores a github token in this file
+# https://packagecontrol.io/packages/sublime-github
+GitHub.sublime-settings
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/Notebook/wind-speed-generation.ipynb b/apps/dash-wind-streaming/Notebook/wind-speed-generation.ipynb
deleted file mode 100644
index 1976288ed..000000000
--- a/apps/dash-wind-streaming/Notebook/wind-speed-generation.ipynb
+++ /dev/null
@@ -1,664 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "import plotly.plotly as py\n",
- "from plotly.graph_objs import *\n",
- "from scipy.optimize import curve_fit\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "from scipy.stats import norm\n",
- "import plotly.figure_factory as ff\n",
- "import math\n",
- "from scipy.stats import skewnorm\n",
- "import datetime as dt"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "windVal = []\n",
- "windError = []\n",
- "windOrientation = []\n",
- "prevVal = 20\n",
- "prevOrientation = np.random.uniform(0, 360)\n",
- "for i in range(0, 86400):\n",
- " windVal.append(abs(np.random.normal(prevVal, 2, 1)[0]))\n",
- " windError.append(abs(np.random.normal(round(prevVal/10), 1)))\n",
- " if(i % 100 == 0):\n",
- " windOrientation.append(np.random.uniform(prevOrientation-50,\n",
- " prevOrientation+50))\n",
- " else:\n",
- " windOrientation.append(np.random.uniform(prevOrientation-5,\n",
- " prevOrientation+5))\n",
- " if(round(windVal[-1]) > 45):\n",
- " prevVal = int(math.floor(windVal[-1]))\n",
- " elif(round(windVal[-1]) < 10):\n",
- " prevVal = int(math.ceil(windVal[-1]))\n",
- " else:\n",
- " prevVal = int(round(windVal[-1]))\n",
- " prevOrientation = windOrientation[-1]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "df = pd.DataFrame.from_dict({\n",
- " 'Speed': windVal,\n",
- " 'SpeedError': windError,\n",
- " 'Direction': windOrientation\n",
- " })"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "now = dt.datetime.now()\n",
- "sec = now.second\n",
- "minute = now.minute\n",
- "hour = now.hour\n",
- "\n",
- "totalTime = (hour * 3600) + (minute * 60) + (sec)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "import sqlite3\n",
- "from datetime import *\n",
- "connex = sqlite3.connect(\"wind-data.db\") # Opens file if exists, else creates file\n",
- "cur = connex.cursor() "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "df.to_sql(name='Wind', con=connex)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " index | \n",
- " Direction | \n",
- " Speed | \n",
- " SpeedError | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " 0 | \n",
- " 51818 | \n",
- " 388.467113 | \n",
- " 23.455603 | \n",
- " 2.098050 | \n",
- "
\n",
- " \n",
- " 1 | \n",
- " 51819 | \n",
- " 385.246230 | \n",
- " 22.822748 | \n",
- " 1.073134 | \n",
- "
\n",
- " \n",
- " 2 | \n",
- " 51820 | \n",
- " 388.454137 | \n",
- " 21.537013 | \n",
- " 1.982093 | \n",
- "
\n",
- " \n",
- " 3 | \n",
- " 51821 | \n",
- " 386.653642 | \n",
- " 25.422632 | \n",
- " 2.680533 | \n",
- "
\n",
- " \n",
- " 4 | \n",
- " 51822 | \n",
- " 387.924312 | \n",
- " 26.444059 | \n",
- " 3.757128 | \n",
- "
\n",
- " \n",
- " 5 | \n",
- " 51823 | \n",
- " 392.384678 | \n",
- " 24.849731 | \n",
- " 1.705354 | \n",
- "
\n",
- " \n",
- " 6 | \n",
- " 51824 | \n",
- " 388.825201 | \n",
- " 27.255339 | \n",
- " 1.626143 | \n",
- "
\n",
- " \n",
- " 7 | \n",
- " 51825 | \n",
- " 384.114820 | \n",
- " 25.504271 | \n",
- " 1.971804 | \n",
- "
\n",
- " \n",
- " 8 | \n",
- " 51826 | \n",
- " 384.646137 | \n",
- " 23.141819 | \n",
- " 2.417021 | \n",
- "
\n",
- " \n",
- " 9 | \n",
- " 51827 | \n",
- " 387.909547 | \n",
- " 23.208711 | \n",
- " 1.300878 | \n",
- "
\n",
- " \n",
- " 10 | \n",
- " 51828 | \n",
- " 388.013444 | \n",
- " 19.468876 | \n",
- " 1.846851 | \n",
- "
\n",
- " \n",
- " 11 | \n",
- " 51829 | \n",
- " 391.431363 | \n",
- " 16.096508 | \n",
- " 1.756832 | \n",
- "
\n",
- " \n",
- " 12 | \n",
- " 51830 | \n",
- " 395.276298 | \n",
- " 13.434031 | \n",
- " 0.085637 | \n",
- "
\n",
- " \n",
- " 13 | \n",
- " 51831 | \n",
- " 391.453616 | \n",
- " 14.472317 | \n",
- " 0.229909 | \n",
- "
\n",
- " \n",
- " 14 | \n",
- " 51832 | \n",
- " 394.933161 | \n",
- " 9.355090 | \n",
- " 0.369801 | \n",
- "
\n",
- " \n",
- " 15 | \n",
- " 51833 | \n",
- " 395.196892 | \n",
- " 6.285318 | \n",
- " 1.750981 | \n",
- "
\n",
- " \n",
- " 16 | \n",
- " 51834 | \n",
- " 396.410848 | \n",
- " 4.811851 | \n",
- " 1.339742 | \n",
- "
\n",
- " \n",
- " 17 | \n",
- " 51835 | \n",
- " 399.557762 | \n",
- " 2.857893 | \n",
- " 0.961948 | \n",
- "
\n",
- " \n",
- " 18 | \n",
- " 51836 | \n",
- " 394.945787 | \n",
- " 3.804883 | \n",
- " 1.296398 | \n",
- "
\n",
- " \n",
- " 19 | \n",
- " 51837 | \n",
- " 396.697627 | \n",
- " 3.637859 | \n",
- " 0.559455 | \n",
- "
\n",
- " \n",
- " 20 | \n",
- " 51838 | \n",
- " 397.116428 | \n",
- " 2.725407 | \n",
- " 0.465070 | \n",
- "
\n",
- " \n",
- " 21 | \n",
- " 51839 | \n",
- " 395.553973 | \n",
- " 3.254478 | \n",
- " 1.126300 | \n",
- "
\n",
- " \n",
- " 22 | \n",
- " 51840 | \n",
- " 395.357468 | \n",
- " 6.278269 | \n",
- " 0.242901 | \n",
- "
\n",
- " \n",
- " 23 | \n",
- " 51841 | \n",
- " 399.309159 | \n",
- " 6.217941 | \n",
- " 1.110519 | \n",
- "
\n",
- " \n",
- " 24 | \n",
- " 51842 | \n",
- " 396.167154 | \n",
- " 11.574918 | \n",
- " 0.472902 | \n",
- "
\n",
- " \n",
- " 25 | \n",
- " 51843 | \n",
- " 392.946369 | \n",
- " 12.546761 | \n",
- " 2.696686 | \n",
- "
\n",
- " \n",
- " 26 | \n",
- " 51844 | \n",
- " 388.651890 | \n",
- " 11.567795 | \n",
- " 0.000096 | \n",
- "
\n",
- " \n",
- " 27 | \n",
- " 51845 | \n",
- " 389.262298 | \n",
- " 12.573260 | \n",
- " 1.361332 | \n",
- "
\n",
- " \n",
- " 28 | \n",
- " 51846 | \n",
- " 389.440563 | \n",
- " 15.219582 | \n",
- " 0.180667 | \n",
- "
\n",
- " \n",
- " 29 | \n",
- " 51847 | \n",
- " 390.460858 | \n",
- " 13.764902 | \n",
- " 2.724583 | \n",
- "
\n",
- " \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- "
\n",
- " \n",
- " 169 | \n",
- " 51987 | \n",
- " 423.504998 | \n",
- " 11.725522 | \n",
- " 0.794980 | \n",
- "
\n",
- " \n",
- " 170 | \n",
- " 51988 | \n",
- " 423.845203 | \n",
- " 13.905336 | \n",
- " 0.421338 | \n",
- "
\n",
- " \n",
- " 171 | \n",
- " 51989 | \n",
- " 423.178823 | \n",
- " 12.324926 | \n",
- " 1.188691 | \n",
- "
\n",
- " \n",
- " 172 | \n",
- " 51990 | \n",
- " 425.206527 | \n",
- " 11.251139 | \n",
- " 0.113524 | \n",
- "
\n",
- " \n",
- " 173 | \n",
- " 51991 | \n",
- " 423.737817 | \n",
- " 12.640440 | \n",
- " 2.880048 | \n",
- "
\n",
- " \n",
- " 174 | \n",
- " 51992 | \n",
- " 418.898456 | \n",
- " 16.550466 | \n",
- " 1.918532 | \n",
- "
\n",
- " \n",
- " 175 | \n",
- " 51993 | \n",
- " 417.238872 | \n",
- " 17.793784 | \n",
- " 0.551114 | \n",
- "
\n",
- " \n",
- " 176 | \n",
- " 51994 | \n",
- " 415.241132 | \n",
- " 18.097223 | \n",
- " 0.654216 | \n",
- "
\n",
- " \n",
- " 177 | \n",
- " 51995 | \n",
- " 417.110020 | \n",
- " 16.429510 | \n",
- " 2.152254 | \n",
- "
\n",
- " \n",
- " 178 | \n",
- " 51996 | \n",
- " 421.549616 | \n",
- " 12.342433 | \n",
- " 0.132018 | \n",
- "
\n",
- " \n",
- " 179 | \n",
- " 51997 | \n",
- " 422.692404 | \n",
- " 8.855690 | \n",
- " 1.373699 | \n",
- "
\n",
- " \n",
- " 180 | \n",
- " 51998 | \n",
- " 419.323263 | \n",
- " 4.136734 | \n",
- " 0.133812 | \n",
- "
\n",
- " \n",
- " 181 | \n",
- " 51999 | \n",
- " 420.651488 | \n",
- " 7.781654 | \n",
- " 1.317008 | \n",
- "
\n",
- " \n",
- " 182 | \n",
- " 52000 | \n",
- " 383.283248 | \n",
- " 8.408271 | \n",
- " 1.286599 | \n",
- "
\n",
- " \n",
- " 183 | \n",
- " 52001 | \n",
- " 378.455239 | \n",
- " 9.031757 | \n",
- " 0.560556 | \n",
- "
\n",
- " \n",
- " 184 | \n",
- " 52002 | \n",
- " 376.464200 | \n",
- " 11.943432 | \n",
- " 0.222717 | \n",
- "
\n",
- " \n",
- " 185 | \n",
- " 52003 | \n",
- " 376.390306 | \n",
- " 9.969383 | \n",
- " 0.134329 | \n",
- "
\n",
- " \n",
- " 186 | \n",
- " 52004 | \n",
- " 380.201827 | \n",
- " 14.736779 | \n",
- " 1.031438 | \n",
- "
\n",
- " \n",
- " 187 | \n",
- " 52005 | \n",
- " 382.746163 | \n",
- " 14.692422 | \n",
- " 1.896312 | \n",
- "
\n",
- " \n",
- " 188 | \n",
- " 52006 | \n",
- " 386.201541 | \n",
- " 14.610690 | \n",
- " 2.840782 | \n",
- "
\n",
- " \n",
- " 189 | \n",
- " 52007 | \n",
- " 385.191312 | \n",
- " 16.216857 | \n",
- " 1.175926 | \n",
- "
\n",
- " \n",
- " 190 | \n",
- " 52008 | \n",
- " 385.689913 | \n",
- " 16.272750 | \n",
- " 0.249718 | \n",
- "
\n",
- " \n",
- " 191 | \n",
- " 52009 | \n",
- " 383.080845 | \n",
- " 16.021107 | \n",
- " 0.289782 | \n",
- "
\n",
- " \n",
- " 192 | \n",
- " 52010 | \n",
- " 385.675010 | \n",
- " 13.630253 | \n",
- " 0.994068 | \n",
- "
\n",
- " \n",
- " 193 | \n",
- " 52011 | \n",
- " 382.759256 | \n",
- " 18.145036 | \n",
- " 2.438731 | \n",
- "
\n",
- " \n",
- " 194 | \n",
- " 52012 | \n",
- " 383.773835 | \n",
- " 17.261319 | \n",
- " 2.498423 | \n",
- "
\n",
- " \n",
- " 195 | \n",
- " 52013 | \n",
- " 385.414001 | \n",
- " 22.596576 | \n",
- " 2.913657 | \n",
- "
\n",
- " \n",
- " 196 | \n",
- " 52014 | \n",
- " 383.930966 | \n",
- " 25.388647 | \n",
- " 1.465414 | \n",
- "
\n",
- " \n",
- " 197 | \n",
- " 52015 | \n",
- " 386.898876 | \n",
- " 23.412001 | \n",
- " 2.190445 | \n",
- "
\n",
- " \n",
- " 198 | \n",
- " 52016 | \n",
- " 384.857677 | \n",
- " 23.883922 | \n",
- " 1.601534 | \n",
- "
\n",
- " \n",
- "
\n",
- "
199 rows × 4 columns
\n",
- "
"
- ],
- "text/plain": [
- " index Direction Speed SpeedError\n",
- "0 51818 388.467113 23.455603 2.098050\n",
- "1 51819 385.246230 22.822748 1.073134\n",
- "2 51820 388.454137 21.537013 1.982093\n",
- "3 51821 386.653642 25.422632 2.680533\n",
- "4 51822 387.924312 26.444059 3.757128\n",
- "5 51823 392.384678 24.849731 1.705354\n",
- "6 51824 388.825201 27.255339 1.626143\n",
- "7 51825 384.114820 25.504271 1.971804\n",
- "8 51826 384.646137 23.141819 2.417021\n",
- "9 51827 387.909547 23.208711 1.300878\n",
- "10 51828 388.013444 19.468876 1.846851\n",
- "11 51829 391.431363 16.096508 1.756832\n",
- "12 51830 395.276298 13.434031 0.085637\n",
- "13 51831 391.453616 14.472317 0.229909\n",
- "14 51832 394.933161 9.355090 0.369801\n",
- "15 51833 395.196892 6.285318 1.750981\n",
- "16 51834 396.410848 4.811851 1.339742\n",
- "17 51835 399.557762 2.857893 0.961948\n",
- "18 51836 394.945787 3.804883 1.296398\n",
- "19 51837 396.697627 3.637859 0.559455\n",
- "20 51838 397.116428 2.725407 0.465070\n",
- "21 51839 395.553973 3.254478 1.126300\n",
- "22 51840 395.357468 6.278269 0.242901\n",
- "23 51841 399.309159 6.217941 1.110519\n",
- "24 51842 396.167154 11.574918 0.472902\n",
- "25 51843 392.946369 12.546761 2.696686\n",
- "26 51844 388.651890 11.567795 0.000096\n",
- "27 51845 389.262298 12.573260 1.361332\n",
- "28 51846 389.440563 15.219582 0.180667\n",
- "29 51847 390.460858 13.764902 2.724583\n",
- ".. ... ... ... ...\n",
- "169 51987 423.504998 11.725522 0.794980\n",
- "170 51988 423.845203 13.905336 0.421338\n",
- "171 51989 423.178823 12.324926 1.188691\n",
- "172 51990 425.206527 11.251139 0.113524\n",
- "173 51991 423.737817 12.640440 2.880048\n",
- "174 51992 418.898456 16.550466 1.918532\n",
- "175 51993 417.238872 17.793784 0.551114\n",
- "176 51994 415.241132 18.097223 0.654216\n",
- "177 51995 417.110020 16.429510 2.152254\n",
- "178 51996 421.549616 12.342433 0.132018\n",
- "179 51997 422.692404 8.855690 1.373699\n",
- "180 51998 419.323263 4.136734 0.133812\n",
- "181 51999 420.651488 7.781654 1.317008\n",
- "182 52000 383.283248 8.408271 1.286599\n",
- "183 52001 378.455239 9.031757 0.560556\n",
- "184 52002 376.464200 11.943432 0.222717\n",
- "185 52003 376.390306 9.969383 0.134329\n",
- "186 52004 380.201827 14.736779 1.031438\n",
- "187 52005 382.746163 14.692422 1.896312\n",
- "188 52006 386.201541 14.610690 2.840782\n",
- "189 52007 385.191312 16.216857 1.175926\n",
- "190 52008 385.689913 16.272750 0.249718\n",
- "191 52009 383.080845 16.021107 0.289782\n",
- "192 52010 385.675010 13.630253 0.994068\n",
- "193 52011 382.759256 18.145036 2.438731\n",
- "194 52012 383.773835 17.261319 2.498423\n",
- "195 52013 385.414001 22.596576 2.913657\n",
- "196 52014 383.930966 25.388647 1.465414\n",
- "197 52015 386.898876 23.412001 2.190445\n",
- "198 52016 384.857677 23.883922 1.601534\n",
- "\n",
- "[199 rows x 4 columns]"
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "con = sqlite3.connect(\"wind-data.db\")\n",
- "df = pd.read_sql_query(\"SELECT * from Wind where rowid > \"+ str(totalTime-200) + \" AND rowid < \" + str(totalTime) + \";\" , con)\n",
- "df"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 2",
- "language": "python",
- "name": "python2"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 2
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/apps/dash-wind-streaming/README.md b/apps/dash-wind-streaming/README.md
index 58a450925..7dfb6d6ac 100644
--- a/apps/dash-wind-streaming/README.md
+++ b/apps/dash-wind-streaming/README.md
@@ -37,7 +37,7 @@ Open a browser at http://127.0.0.1:8050
## Screenshots
-
+
## Resources
diff --git a/apps/dash-wind-streaming/__init__.py b/apps/dash-wind-streaming/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/apps/dash-wind-streaming/app.py b/apps/dash-wind-streaming/app.py
index 37e9d481b..e7ff083e1 100644
--- a/apps/dash-wind-streaming/app.py
+++ b/apps/dash-wind-streaming/app.py
@@ -1,472 +1,66 @@
-import os
-import pathlib
-import numpy as np
-import datetime as dt
-import dash
-import dash_core_components as dcc
-import dash_html_components as html
+from dash import Dash, html, dcc, Input, Output, State, callback, callback_context
-from dash.exceptions import PreventUpdate
-from dash.dependencies import Input, Output, State
-from scipy.stats import rayleigh
-from db.api import get_wind_data, get_wind_data_by_id
-
-
-GRAPH_INTERVAL = os.environ.get("GRAPH_INTERVAL", 5000)
-
-app = dash.Dash(
- __name__,
- meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
-)
-app.title = "Wind Speed Dashboard"
+from constants import GRAPH_INTERVAL
+from utils.components import Header, wind_speed_card, histogram_card, wind_direction_card
+import utils.figures as figs
+app = Dash(__name__, title="Wind Speed Dashboard")
server = app.server
-app_color = {"graph_bg": "#082255", "graph_line": "#007ACE"}
-
app.layout = html.Div(
[
- # header
- html.Div(
- [
- html.Div(
- [
- html.H4("WIND SPEED STREAMING", className="app__header__title"),
- html.P(
- "This app continually queries a SQL database and displays live charts of wind speed and wind direction.",
- className="app__header__title--grey",
- ),
- ],
- className="app__header__desc",
- ),
- html.Div(
- [
- html.A(
- html.Button("SOURCE CODE", className="link-button"),
- href="https://github.com/plotly/dash-sample-apps/tree/main/apps/dash-wind-streaming",
- ),
- html.A(
- html.Button("ENTERPRISE DEMO", className="link-button"),
- href="https://plotly.com/get-demo/",
- ),
- html.A(
- html.Img(
- src=app.get_asset_url("dash-new-logo.png"),
- className="app__menu__img",
- ),
- href="https://plotly.com/dash/",
- ),
- ],
- className="app__header__logo",
- ),
- ],
- className="app__header",
+ Header(
+ app,
+ "WIND SPEED STREAMING",
+ "This app continually queries a SQL database and displays live charts of wind speed and wind direction.",
),
html.Div(
[
- # wind speed
- html.Div(
- [
- html.Div(
- [html.H6("WIND SPEED (MPH)", className="graph__title")]
- ),
- dcc.Graph(
- id="wind-speed",
- figure=dict(
- layout=dict(
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- )
- ),
- ),
- dcc.Interval(
- id="wind-speed-update",
- interval=int(GRAPH_INTERVAL),
- n_intervals=0,
- ),
- ],
- className="two-thirds column wind__speed__container",
- ),
+ wind_speed_card("wind-speed"),
html.Div(
[
- # histogram
- html.Div(
- [
- html.Div(
- [
- html.H6(
- "WIND SPEED HISTOGRAM",
- className="graph__title",
- )
- ]
- ),
- html.Div(
- [
- dcc.Slider(
- id="bin-slider",
- min=1,
- max=60,
- step=1,
- value=20,
- updatemode="drag",
- marks={
- 20: {"label": "20"},
- 40: {"label": "40"},
- 60: {"label": "60"},
- },
- )
- ],
- className="slider",
- ),
- html.Div(
- [
- dcc.Checklist(
- id="bin-auto",
- options=[
- {"label": "Auto", "value": "Auto"}
- ],
- value=["Auto"],
- inputClassName="auto__checkbox",
- labelClassName="auto__label",
- ),
- html.P(
- "# of Bins: Auto",
- id="bin-size",
- className="auto__p",
- ),
- ],
- className="auto__container",
- ),
- dcc.Graph(
- id="wind-histogram",
- figure=dict(
- layout=dict(
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- )
- ),
- ),
- ],
- className="graph__container first",
- ),
- # wind direction
- html.Div(
- [
- html.Div(
- [
- html.H6(
- "WIND DIRECTION", className="graph__title"
- )
- ]
- ),
- dcc.Graph(
- id="wind-direction",
- figure=dict(
- layout=dict(
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- )
- ),
- ),
- ],
- className="graph__container second",
- ),
+ histogram_card("wind-histogram"),
+ wind_direction_card("wind-direction")
],
className="one-third column histogram__direction",
),
],
className="app__content",
),
+ dcc.Interval(id="wind-speed-update", interval=GRAPH_INTERVAL)
],
className="app__container",
)
-def get_current_time():
- """ Helper function to get the current time in seconds. """
-
- now = dt.datetime.now()
- total_time = (now.hour * 3600) + (now.minute * 60) + (now.second)
- return total_time
-
-
-@app.callback(
- Output("wind-speed", "figure"), [Input("wind-speed-update", "n_intervals")]
-)
-def gen_wind_speed(interval):
- """
- Generate the wind speed graph.
-
- :params interval: update the graph based on an interval
- """
-
- total_time = get_current_time()
- df = get_wind_data(total_time - 200, total_time)
-
- trace = dict(
- type="scatter",
- y=df["Speed"],
- line={"color": "#42C4F7"},
- hoverinfo="skip",
- error_y={
- "type": "data",
- "array": df["SpeedError"],
- "thickness": 1.5,
- "width": 2,
- "color": "#B4E8FC",
- },
- mode="lines",
- )
-
- layout = dict(
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- font={"color": "#fff"},
- height=700,
- xaxis={
- "range": [0, 200],
- "showline": True,
- "zeroline": False,
- "fixedrange": True,
- "tickvals": [0, 50, 100, 150, 200],
- "ticktext": ["200", "150", "100", "50", "0"],
- "title": "Time Elapsed (sec)",
- },
- yaxis={
- "range": [
- min(0, min(df["Speed"])),
- max(45, max(df["Speed"]) + max(df["SpeedError"])),
- ],
- "showgrid": True,
- "showline": True,
- "fixedrange": True,
- "zeroline": False,
- "gridcolor": app_color["graph_line"],
- "nticks": max(6, round(df["Speed"].iloc[-1] / 10)),
- },
- )
-
- return dict(data=[trace], layout=layout)
-
-
-@app.callback(
- Output("wind-direction", "figure"), [Input("wind-speed-update", "n_intervals")]
-)
-def gen_wind_direction(interval):
- """
- Generate the wind direction graph.
-
- :params interval: update the graph based on an interval
- """
-
- total_time = get_current_time()
- df = get_wind_data_by_id(total_time)
- val = df["Speed"].iloc[-1]
- direction = [0, (df["Direction"][0] - 20), (df["Direction"][0] + 20), 0]
-
- traces_scatterpolar = [
- {"r": [0, val, val, 0], "fillcolor": "#084E8A"},
- {"r": [0, val * 0.65, val * 0.65, 0], "fillcolor": "#B4E1FA"},
- {"r": [0, val * 0.3, val * 0.3, 0], "fillcolor": "#EBF5FA"},
- ]
-
- data = [
- dict(
- type="scatterpolar",
- r=traces["r"],
- theta=direction,
- mode="lines",
- fill="toself",
- fillcolor=traces["fillcolor"],
- line={"color": "rgba(32, 32, 32, .6)", "width": 1},
- )
- for traces in traces_scatterpolar
- ]
-
- layout = dict(
- height=350,
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- font={"color": "#fff"},
- autosize=False,
- polar={
- "bgcolor": app_color["graph_line"],
- "radialaxis": {"range": [0, 45], "angle": 45, "dtick": 10},
- "angularaxis": {"showline": False, "tickcolor": "white"},
- },
- showlegend=False,
- )
-
- return dict(data=data, layout=layout)
-
-
-@app.callback(
+@callback(
Output("wind-histogram", "figure"),
- [Input("wind-speed-update", "n_intervals")],
- [
- State("wind-speed", "figure"),
- State("bin-slider", "value"),
- State("bin-auto", "value"),
- ],
+ Output("wind-speed", "figure"),
+ Output("wind-direction", "figure"),
+ Input("wind-speed-update", "n_intervals"),
+ State("bin-slider", "value"),
+ State("bin-auto", "value"),
)
-def gen_wind_histogram(interval, wind_speed_figure, slider_value, auto_state):
- """
- Genererate wind histogram graph.
-
- :params interval: upadte the graph based on an interval
- :params wind_speed_figure: current wind speed graph
- :params slider_value: current slider value
- :params auto_state: current auto state
- """
-
- wind_val = []
+def return_gen_wind_histogram(interval, slider_value, auto_state):
+ fig_wind_speed = figs.gen_wind_speed()
+ fig_wind_direction =figs.gen_wind_direction()
+ fig_wind_histogram = figs.gen_wind_histogram(fig_wind_speed, slider_value, auto_state)
+ return fig_wind_histogram, fig_wind_speed, fig_wind_direction
- try:
- # Check to see whether wind-speed has been plotted yet
- if wind_speed_figure is not None:
- wind_val = wind_speed_figure["data"][0]["y"]
- if "Auto" in auto_state:
- bin_val = np.histogram(
- wind_val,
- bins=range(int(round(min(wind_val))), int(round(max(wind_val)))),
- )
- else:
- bin_val = np.histogram(wind_val, bins=slider_value)
- except Exception as error:
- raise PreventUpdate
- avg_val = float(sum(wind_val)) / len(wind_val)
- median_val = np.median(wind_val)
-
- pdf_fitted = rayleigh.pdf(
- bin_val[1], loc=(avg_val) * 0.55, scale=(bin_val[1][-1] - bin_val[1][0]) / 3
- )
-
- y_val = (pdf_fitted * max(bin_val[0]) * 20,)
- y_val_max = max(y_val[0])
- bin_val_max = max(bin_val[0])
-
- trace = dict(
- type="bar",
- x=bin_val[1],
- y=bin_val[0],
- marker={"color": app_color["graph_line"]},
- showlegend=False,
- hoverinfo="x+y",
- )
-
- traces_scatter = [
- {"line_dash": "dash", "line_color": "#2E5266", "name": "Average"},
- {"line_dash": "dot", "line_color": "#BD9391", "name": "Median"},
- ]
-
- scatter_data = [
- dict(
- type="scatter",
- x=[bin_val[int(len(bin_val) / 2)]],
- y=[0],
- mode="lines",
- line={"dash": traces["line_dash"], "color": traces["line_color"]},
- marker={"opacity": 0},
- visible=True,
- name=traces["name"],
- )
- for traces in traces_scatter
- ]
-
- trace3 = dict(
- type="scatter",
- mode="lines",
- line={"color": "#42C4F7"},
- y=y_val[0],
- x=bin_val[1][: len(bin_val[1])],
- name="Rayleigh Fit",
- )
- layout = dict(
- height=350,
- plot_bgcolor=app_color["graph_bg"],
- paper_bgcolor=app_color["graph_bg"],
- font={"color": "#fff"},
- xaxis={
- "title": "Wind Speed (mph)",
- "showgrid": False,
- "showline": False,
- "fixedrange": True,
- },
- yaxis={
- "showgrid": False,
- "showline": False,
- "zeroline": False,
- "title": "Number of Samples",
- "fixedrange": True,
- },
- autosize=True,
- bargap=0.01,
- bargroupgap=0,
- hovermode="closest",
- legend={
- "orientation": "h",
- "yanchor": "bottom",
- "xanchor": "center",
- "y": 1,
- "x": 0.5,
- },
- shapes=[
- {
- "xref": "x",
- "yref": "y",
- "y1": int(max(bin_val_max, y_val_max)) + 0.5,
- "y0": 0,
- "x0": avg_val,
- "x1": avg_val,
- "type": "line",
- "line": {"dash": "dash", "color": "#2E5266", "width": 5},
- },
- {
- "xref": "x",
- "yref": "y",
- "y1": int(max(bin_val_max, y_val_max)) + 0.5,
- "y0": 0,
- "x0": median_val,
- "x1": median_val,
- "type": "line",
- "line": {"dash": "dot", "color": "#BD9391", "width": 5},
- },
- ],
- )
- return dict(data=[trace, scatter_data[0], scatter_data[1], trace3], layout=layout)
-
-
-@app.callback(
+@callback(
Output("bin-auto", "value"),
- [Input("bin-slider", "value")],
- [State("wind-speed", "figure")],
-)
-def deselect_auto(slider_value, wind_speed_figure):
- """ Toggle the auto checkbox. """
-
- # prevent update if graph has no data
- if "data" not in wind_speed_figure:
- raise PreventUpdate
- if not len(wind_speed_figure["data"]):
- raise PreventUpdate
-
- if wind_speed_figure is not None and len(wind_speed_figure["data"][0]["y"]) > 5:
- return [""]
- return ["Auto"]
-
-
-@app.callback(
Output("bin-size", "children"),
- [Input("bin-auto", "value")],
- [State("bin-slider", "value")],
+ Input("bin-slider", "value"),
+ Input("bin-auto", "value"),
+ prevent_initial_call=True,
)
-def show_num_bins(autoValue, slider_value):
- """ Display the number of bins. """
-
- if "Auto" in autoValue:
- return "# of Bins: Auto"
- return "# of Bins: " + str(int(slider_value))
+def deselect_auto(slider_value, bin_auto):
+ triggered_id = callback_context.triggered[0]["prop_id"].split(".")[0]
+ if triggered_id == "bin-slider":
+ return [], "# of Bins: " + str(int(slider_value))
+ else:
+ return ["Auto"], "# of Bins: Auto"
if __name__ == "__main__":
diff --git a/apps/dash-wind-streaming/assets/app.css b/apps/dash-wind-streaming/assets/css/app.css
similarity index 100%
rename from apps/dash-wind-streaming/assets/app.css
rename to apps/dash-wind-streaming/assets/css/app.css
diff --git a/apps/dash-wind-streaming/assets/css/header.css b/apps/dash-wind-streaming/assets/css/header.css
new file mode 100644
index 000000000..ec39e0172
--- /dev/null
+++ b/apps/dash-wind-streaming/assets/css/header.css
@@ -0,0 +1,65 @@
+/* Header */
+.header {
+ height: 10vh;
+ display: flex;
+ background-color: transparent;
+ padding-left: 2%;
+ padding-right: 2%;
+ font-family: playfair display, sans-serif;
+}
+
+.header .header-title {
+ color: #FFFFFF !important;
+ font-size: 5vh;
+}
+
+.header-logos {
+ margin-left: auto;
+ align-self: center;
+}
+
+.header-logos img {
+ margin-left: 3vh !important;
+ max-height: 5vh;
+}
+
+.subheader-title {
+ font-size: 1.5vh;
+ color: #A2ACB8 !important;
+}
+
+
+/* Demo button css */
+.demo-button {
+ font-size: 1.5vh;
+ font-family: Open Sans, sans-serif;
+ text-decoration: none;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ border-radius: 8px;
+ font-weight: 700;
+ -webkit-padding-start: 1rem;
+ padding-inline-start: 1rem;
+ -webkit-padding-end: 1rem;
+ padding-inline-end: 1rem;
+ color: #ffffff;
+ letter-spacing: 1.5px;
+ border: solid 1.5px transparent;
+ box-shadow: 2px 1000px 1px #0c0c0c inset;
+ background-image: linear-gradient(135deg, #7A76FF, #7A76FF, #7FE4FF);
+ -webkit-background-size: 200% 100%;
+ background-size: 200% 100%;
+ -webkit-background-position: 99%;
+ background-position: 99%;
+ background-origin: border-box;
+ transition: all .4s ease-in-out;
+ padding-top: 1vh;
+ padding-bottom: 1vh;
+}
+
+.demo-button:hover {
+ color: #7A76FF;
+ background-position: 0%;
+}
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/assets/style.css b/apps/dash-wind-streaming/assets/css/style.css
similarity index 100%
rename from apps/dash-wind-streaming/assets/style.css
rename to apps/dash-wind-streaming/assets/css/style.css
diff --git a/apps/dash-wind-streaming/assets/dash-new-logo.png b/apps/dash-wind-streaming/assets/dash-new-logo.png
deleted file mode 100644
index 040cde174..000000000
Binary files a/apps/dash-wind-streaming/assets/dash-new-logo.png and /dev/null differ
diff --git a/apps/dash-wind-streaming/assets/demo-button.css b/apps/dash-wind-streaming/assets/demo-button.css
deleted file mode 100644
index f030e1b9e..000000000
--- a/apps/dash-wind-streaming/assets/demo-button.css
+++ /dev/null
@@ -1,6 +0,0 @@
-.link-button {
- margin-top: 10px;
- margin-right: 10px;
- vertical-align: top;
- color: white;
-}
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/demo.gif b/apps/dash-wind-streaming/assets/github/demo.gif
similarity index 100%
rename from apps/dash-wind-streaming/demo.gif
rename to apps/dash-wind-streaming/assets/github/demo.gif
diff --git a/apps/dash-wind-streaming/assets/images/plotly-logo.png b/apps/dash-wind-streaming/assets/images/plotly-logo.png
new file mode 100644
index 000000000..984dd57ab
Binary files /dev/null and b/apps/dash-wind-streaming/assets/images/plotly-logo.png differ
diff --git a/apps/dash-wind-streaming/constants.py b/apps/dash-wind-streaming/constants.py
new file mode 100644
index 000000000..608ffde50
--- /dev/null
+++ b/apps/dash-wind-streaming/constants.py
@@ -0,0 +1,7 @@
+import os
+
+app_color = {"graph_bg": "#082255", "graph_line": "#007ACE"}
+
+DB_FILE = "data/wind-data.db"
+
+GRAPH_INTERVAL = int(os.environ.get("GRAPH_INTERVAL", 5000))
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/db/wind-data.db b/apps/dash-wind-streaming/data/wind-data.db
similarity index 100%
rename from apps/dash-wind-streaming/db/wind-data.db
rename to apps/dash-wind-streaming/data/wind-data.db
diff --git a/apps/dash-wind-streaming/db/__init__.py b/apps/dash-wind-streaming/db/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/apps/dash-wind-streaming/requirements.txt b/apps/dash-wind-streaming/requirements.txt
index c34ae2487..da7dcd54e 100644
--- a/apps/dash-wind-streaming/requirements.txt
+++ b/apps/dash-wind-streaming/requirements.txt
@@ -1,5 +1,4 @@
-dash==1.0.0
-numpy==1.16.4
-pandas==0.24.2
-scipy==1.3.0
-gunicorn==19.9.0
\ No newline at end of file
+dash==2.4.1
+pandas==1.4.2
+gunicorn==20.1.0
+scipy==1.8.1
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/runtime.txt b/apps/dash-wind-streaming/runtime.txt
new file mode 100644
index 000000000..cfa660c42
--- /dev/null
+++ b/apps/dash-wind-streaming/runtime.txt
@@ -0,0 +1 @@
+python-3.8.0
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/utils/components.py b/apps/dash-wind-streaming/utils/components.py
new file mode 100644
index 000000000..cc20688a8
--- /dev/null
+++ b/apps/dash-wind-streaming/utils/components.py
@@ -0,0 +1,99 @@
+from dash import html, dcc
+
+from constants import app_color
+
+def Header(app, header, subheader=None):
+ left_headers = html.Div(
+ [
+ html.Div(header, className="header-title"),
+ html.Div(subheader, className="subheader-title"),
+ ]
+ )
+
+ logo = html.Img(src=app.get_asset_url("images/plotly-logo.png"))
+ link = html.A(logo, href="https://plotly.com/dash/", target="_blank")
+ demo_link = html.A(
+ "LEARN MORE",
+ href="https://plotly.com/dash/",
+ target="_blank",
+ className="demo-button",
+ )
+ right_logos = html.Div([demo_link, link], className="header-logos")
+
+ return html.Div([left_headers, right_logos], className="header")
+
+
+def wind_speed_card(graph_id):
+ return html.Div([
+ html.Div(html.H6("WIND SPEED (MPH)", className="graph__title")),
+ dcc.Graph(
+ id=graph_id,
+ figure=dict(
+ layout=dict(
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ )
+ ),
+ ),
+ ], className="two-thirds column wind__speed__container",
+ )
+
+def histogram_card(graph_id):
+ return html.Div([
+ html.Div(html.H6("WIND SPEED HISTOGRAM", className="graph__title")),
+ html.Div(
+ dcc.Slider(
+ id="bin-slider",
+ min=1,
+ max=60,
+ step=1,
+ value=20,
+ marks={
+ 20: {"label": "20"},
+ 40: {"label": "40"},
+ 60: {"label": "60"},
+ },
+ ),
+ className="slider",
+ ),
+ html.Div([
+ dcc.Checklist(
+ id="bin-auto",
+ options=["Auto"],
+ value=["Auto"],
+ inputClassName="auto__checkbox",
+ labelClassName="auto__label",
+ ),
+ html.P(
+ "# of Bins: Auto",
+ id="bin-size",
+ className="auto__p",
+ )],
+ className="auto__container",
+ ),
+ dcc.Graph(
+ id=graph_id,
+ figure=dict(
+ layout=dict(
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ )
+ ),
+ ),
+ ], className="graph__container first",
+ )
+
+def wind_direction_card(graph_id):
+ return html.Div([
+ html.Div(html.H6("WIND DIRECTION", className="graph__title")),
+ dcc.Graph(
+ id=graph_id,
+ figure=dict(
+ layout=dict(
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ )
+ ),
+ ),
+ ], className="graph__container second",
+ )
\ No newline at end of file
diff --git a/apps/dash-wind-streaming/db/api.py b/apps/dash-wind-streaming/utils/database_connection.py
similarity index 87%
rename from apps/dash-wind-streaming/db/api.py
rename to apps/dash-wind-streaming/utils/database_connection.py
index 5a21df66a..fea16418f 100644
--- a/apps/dash-wind-streaming/db/api.py
+++ b/apps/dash-wind-streaming/utils/database_connection.py
@@ -1,10 +1,7 @@
-import pathlib
import sqlite3
import pandas as pd
-
-DB_FILE = pathlib.Path(__file__).resolve().parent.joinpath("wind-data.db").resolve()
-
+from constants import DB_FILE
def get_wind_data(start, end):
"""
diff --git a/apps/dash-wind-streaming/utils/figures.py b/apps/dash-wind-streaming/utils/figures.py
new file mode 100644
index 000000000..b209e47e5
--- /dev/null
+++ b/apps/dash-wind-streaming/utils/figures.py
@@ -0,0 +1,243 @@
+
+import numpy as np
+from scipy.stats import rayleigh
+from dash.exceptions import PreventUpdate
+
+from constants import app_color
+from utils.helper_functions import get_current_time
+from utils.database_connection import get_wind_data, get_wind_data_by_id
+
+
+def gen_wind_speed():
+ """
+ Generate the wind speed graph.
+
+ :params interval: update the graph based on an interval
+ """
+
+ total_time = get_current_time()
+ df = get_wind_data(total_time - 200, total_time)
+
+ trace = dict(
+ type="scatter",
+ y=df["Speed"],
+ line={"color": "#42C4F7"},
+ hoverinfo="skip",
+ error_y={
+ "type": "data",
+ "array": df["SpeedError"],
+ "thickness": 1.5,
+ "width": 2,
+ "color": "#B4E8FC",
+ },
+ mode="lines",
+ )
+
+ layout = dict(
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ font={"color": "#fff"},
+ height=700,
+ xaxis={
+ "range": [0, 200],
+ "showline": True,
+ "zeroline": False,
+ "fixedrange": True,
+ "tickvals": [0, 50, 100, 150, 200],
+ "ticktext": ["200", "150", "100", "50", "0"],
+ "title": "Time Elapsed (sec)",
+ },
+ yaxis={
+ "range": [
+ min(0, min(df["Speed"])),
+ max(45, max(df["Speed"]) + max(df["SpeedError"])),
+ ],
+ "showgrid": True,
+ "showline": True,
+ "fixedrange": True,
+ "zeroline": False,
+ "gridcolor": app_color["graph_line"],
+ "nticks": max(6, round(df["Speed"].iloc[-1] / 10)),
+ },
+ )
+
+ return dict(data=[trace], layout=layout)
+
+
+
+
+def gen_wind_direction():
+ """
+ Generate the wind direction graph.
+
+ :params interval: update the graph based on an interval
+ """
+
+ total_time = get_current_time()
+ df = get_wind_data_by_id(total_time)
+ val = df["Speed"].iloc[-1]
+ direction = [0, (df["Direction"][0] - 20), (df["Direction"][0] + 20), 0]
+
+ traces_scatterpolar = [
+ {"r": [0, val, val, 0], "fillcolor": "#084E8A"},
+ {"r": [0, val * 0.65, val * 0.65, 0], "fillcolor": "#B4E1FA"},
+ {"r": [0, val * 0.3, val * 0.3, 0], "fillcolor": "#EBF5FA"},
+ ]
+
+ data = [
+ dict(
+ type="scatterpolar",
+ r=traces["r"],
+ theta=direction,
+ mode="lines",
+ fill="toself",
+ fillcolor=traces["fillcolor"],
+ line={"color": "rgba(32, 32, 32, .6)", "width": 1},
+ )
+ for traces in traces_scatterpolar
+ ]
+
+ layout = dict(
+ height=350,
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ font={"color": "#fff"},
+ autosize=False,
+ polar={
+ "bgcolor": app_color["graph_line"],
+ "radialaxis": {"range": [0, 45], "angle": 45, "dtick": 10},
+ "angularaxis": {"showline": False, "tickcolor": "white"},
+ },
+ showlegend=False,
+ )
+
+ return dict(data=data, layout=layout)
+
+
+
+def gen_wind_histogram(wind_speed_figure, slider_value, auto_state):
+ """
+ Genererate wind histogram graph.
+
+ :params interval: upadte the graph based on an interval
+ :params wind_speed_figure: current wind speed graph
+ :params slider_value: current slider value
+ :params auto_state: current auto state
+ """
+
+ wind_val = []
+
+ try:
+ # Check to see whether wind-speed has been plotted yet
+ if wind_speed_figure is not None:
+ wind_val = wind_speed_figure["data"][0]["y"]
+ if "Auto" in auto_state:
+ bin_val = np.histogram(
+ wind_val,
+ bins=range(int(round(min(wind_val))), int(round(max(wind_val)))),
+ )
+ else:
+ bin_val = np.histogram(wind_val, bins=slider_value)
+ except Exception as error:
+ raise PreventUpdate
+
+ avg_val = float(sum(wind_val)) / len(wind_val)
+ median_val = np.median(wind_val)
+
+ pdf_fitted = rayleigh.pdf(
+ bin_val[1], loc=(avg_val) * 0.55, scale=(bin_val[1][-1] - bin_val[1][0]) / 3
+ )
+
+ y_val = (pdf_fitted * max(bin_val[0]) * 20,)
+ y_val_max = max(y_val[0])
+ bin_val_max = max(bin_val[0])
+
+ trace = dict(
+ type="bar",
+ x=bin_val[1],
+ y=bin_val[0],
+ marker={"color": app_color["graph_line"]},
+ showlegend=False,
+ hoverinfo="x+y",
+ )
+
+ traces_scatter = [
+ {"line_dash": "dash", "line_color": "#2E5266", "name": "Average"},
+ {"line_dash": "dot", "line_color": "#BD9391", "name": "Median"},
+ ]
+
+ scatter_data = [
+ dict(
+ type="scatter",
+ x=[bin_val[int(len(bin_val) / 2)]],
+ y=[0],
+ mode="lines",
+ line={"dash": traces["line_dash"], "color": traces["line_color"]},
+ marker={"opacity": 0},
+ visible=True,
+ name=traces["name"],
+ )
+ for traces in traces_scatter
+ ]
+
+ trace3 = dict(
+ type="scatter",
+ mode="lines",
+ line={"color": "#42C4F7"},
+ y=y_val[0],
+ x=bin_val[1][: len(bin_val[1])],
+ name="Rayleigh Fit",
+ )
+ layout = dict(
+ height=350,
+ plot_bgcolor=app_color["graph_bg"],
+ paper_bgcolor=app_color["graph_bg"],
+ font={"color": "#fff"},
+ xaxis={
+ "title": "Wind Speed (mph)",
+ "showgrid": False,
+ "showline": False,
+ "fixedrange": True,
+ },
+ yaxis={
+ "showgrid": False,
+ "showline": False,
+ "zeroline": False,
+ "title": "Number of Samples",
+ "fixedrange": True,
+ },
+ autosize=True,
+ bargap=0.01,
+ bargroupgap=0,
+ hovermode="closest",
+ legend={
+ "orientation": "h",
+ "yanchor": "bottom",
+ "xanchor": "center",
+ "y": 1,
+ "x": 0.5,
+ },
+ shapes=[
+ {
+ "xref": "x",
+ "yref": "y",
+ "y1": int(max(bin_val_max, y_val_max)) + 0.5,
+ "y0": 0,
+ "x0": avg_val,
+ "x1": avg_val,
+ "type": "line",
+ "line": {"dash": "dash", "color": "#2E5266", "width": 5},
+ },
+ {
+ "xref": "x",
+ "yref": "y",
+ "y1": int(max(bin_val_max, y_val_max)) + 0.5,
+ "y0": 0,
+ "x0": median_val,
+ "x1": median_val,
+ "type": "line",
+ "line": {"dash": "dot", "color": "#BD9391", "width": 5},
+ },
+ ],
+ )
+ return dict(data=[trace, scatter_data[0], scatter_data[1], trace3], layout=layout)
diff --git a/apps/dash-wind-streaming/utils/helper_functions.py b/apps/dash-wind-streaming/utils/helper_functions.py
new file mode 100644
index 000000000..b7efd5039
--- /dev/null
+++ b/apps/dash-wind-streaming/utils/helper_functions.py
@@ -0,0 +1,8 @@
+import datetime as dt
+
+def get_current_time():
+ """Helper function to get the current time in seconds."""
+
+ now = dt.datetime.now()
+ total_time = (now.hour * 3600) + (now.minute * 60) + (now.second)
+ return total_time
\ No newline at end of file