diff --git a/examples/dash/README.rst b/examples/dash/README.rst new file mode 100644 index 0000000000..fbebbe313e --- /dev/null +++ b/examples/dash/README.rst @@ -0,0 +1,13 @@ +Dash embedded in FAB +----------------------- + +Example of Dash embedded under flask-appbuilder + +thanks to @jimmybow (https://github.com/jimmybow/Flask_template_auth_with_Dash) for the inspiration + + +Run it:: + + $ export FLASK_APP=app/__init__.py + $ flask fab create-admin + $ flask run diff --git a/examples/dash/app/Dashboard/Dash_App1.py b/examples/dash/app/Dashboard/Dash_App1.py new file mode 100644 index 0000000000..e68d9ac642 --- /dev/null +++ b/examples/dash/app/Dashboard/Dash_App1.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +""" +Created on Sun Jul 8 10:39:33 2018 + +@author: jimmybow +""" +from dash import Dash +from dash.dependencies import Input, State, Output +from .Dash_fun import apply_layout_with_auth, load_object, save_object +import dash_core_components as dcc +import dash_html_components as html + +url_base = "/dash/app1/" + +layout = html.Div( + [html.Div("This is dash app1"), html.Br(), dcc.Input(id="input_text"), html.Br(), html.Br(), html.Div(id="target")] +) + + +def Add_Dash(server, appbuilder): + app = Dash(server=server, url_base_pathname=url_base) + apply_layout_with_auth(app, layout, appbuilder) + + @app.callback(Output("target", "children"), [Input("input_text", "value")]) + def callback_fun(value): + return "your input is {}".format(value) + + return app.server diff --git a/examples/dash/app/Dashboard/Dash_App2.py b/examples/dash/app/Dashboard/Dash_App2.py new file mode 100644 index 0000000000..2edb01ea2b --- /dev/null +++ b/examples/dash/app/Dashboard/Dash_App2.py @@ -0,0 +1,41 @@ +from datetime import datetime as dt + +from dash import Dash + +import dash_core_components as dcc +import dash_html_components as html +import pandas_datareader as pdr +from dash.dependencies import Input +from dash.dependencies import Output +from .Dash_fun import apply_layout_with_auth, load_object, save_object + +url_base = "/dash/app2/" + +layout = html.Div( + [ + html.H1("Stock Tickers"), + dcc.Dropdown( + id="my-dropdown", + options=[ + {"label": "Coke", "value": "COKE"}, + {"label": "Tesla", "value": "TSLA"}, + {"label": "Apple", "value": "AAPL"}, + ], + value="COKE", + ), + dcc.Graph(id="my-graph"), + ], + style={"width": "500"}, +) + + +def Add_Dash(server, appbuilder): + app = Dash(server=server, url_base_pathname=url_base) + apply_layout_with_auth(app, layout, appbuilder) + + @app.callback(Output("my-graph", "figure"), [Input("my-dropdown", "value")]) + def update_graph(selected_dropdown_value): + df = pdr.get_data_yahoo(selected_dropdown_value, start=dt(2017, 1, 1), end=dt.now()) + return {"data": [{"x": df.index, "y": df.Close}], "layout": {"margin": {"l": 40, "r": 0, "t": 20, "b": 30}}} + + return app.server diff --git a/examples/dash/app/Dashboard/Dash_fun.py b/examples/dash/app/Dashboard/Dash_fun.py new file mode 100644 index 0000000000..b188fb0e66 --- /dev/null +++ b/examples/dash/app/Dashboard/Dash_fun.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jan 25 22:34:51 2019 + +@author: jimmybow +""" + +from datetime import datetime, timedelta +from flask_login import current_user +from flask import redirect, has_app_context +import dash_html_components as html +import dash_core_components as dcc +import pandas as pd +import uuid +import os +import pickle + + +def save_object(obj, session_id, name): + os.makedirs("Dir_Store", exist_ok=True) + file = "Dir_Store/{}_{}".format(session_id, name) + pickle.dump(obj, open(file, "wb")) + + +def load_object(session_id, name): + file = "Dir_Store/{}_{}".format(session_id, name) + obj = pickle.load(open(file, "rb")) + os.remove(file) + return obj + + +def clean_Dir_Store(): + if os.path.isdir("Dir_Store"): + file_list = pd.Series("Dir_Store/" + i for i in os.listdir("Dir_Store")) + mt = file_list.apply(lambda x: datetime.fromtimestamp(os.path.getmtime(x))).astype(str) + for i in file_list[mt < str(datetime.now() - timedelta(hours=3))]: + os.remove(i) + + +def apply_layout_with_auth(app, layout, appbuilder): + def serve_layout(): + if current_user and current_user.is_authenticated: + session_id = str(uuid.uuid4()) + clean_Dir_Store() + return html.Div([html.Div(session_id, id="session_id", style={"display": "none"}), layout]) + loginurl = None + if has_app_context(): + return dcc.Location(pathname=appbuilder.get_url_for_login, id="") + return None + + app.config.suppress_callback_exceptions = True + app.layout = serve_layout diff --git a/examples/dash/app/Dashboard/__init__.py b/examples/dash/app/Dashboard/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/dash/app/__init__.py b/examples/dash/app/__init__.py new file mode 100644 index 0000000000..cb9441b33f --- /dev/null +++ b/examples/dash/app/__init__.py @@ -0,0 +1,21 @@ +import logging + +from flask import Flask +from flask_appbuilder import AppBuilder, SQLA +from .Dashboard import Dash_App1, Dash_App2 + +""" + Logging configuration +""" +logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s") +logging.getLogger().setLevel(logging.DEBUG) + + +app = Flask(__name__) +app.config.from_object("config") +db = SQLA(app) +appbuilder = AppBuilder(app, db.session) +app = Dash_App1.Add_Dash(app, appbuilder) +app = Dash_App2.Add_Dash(app, appbuilder) + +from . import views # noqa diff --git a/examples/dash/app/templates/dash.html b/examples/dash/app/templates/dash.html new file mode 100644 index 0000000000..b54a3521f4 --- /dev/null +++ b/examples/dash/app/templates/dash.html @@ -0,0 +1,22 @@ +{% extends "appbuilder/baselayout.html" %} +{% block head_css %} +{{ super() }} + +{% endblock %} +{% block content %} +