Skip to content

Instantly share code, notes, and snippets.

@xhluca
Created August 17, 2022 19:09
Show Gist options
  • Save xhluca/32b6e126485b6f4c4ddaf7cc477a3930 to your computer and use it in GitHub Desktop.
Save xhluca/32b6e126485b6f4c4ddaf7cc477a3930 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "R-S6u7gJ8em3"
},
"source": [
"# Setup app"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "598h-F3U9Fn2",
"outputId": "683ac043-0b8a-40b8-9129-d253ebe971d2"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\u001b[K |████████████████████████████████| 9.9 MB 6.7 MB/s \n",
"\u001b[K |████████████████████████████████| 357 kB 49.8 MB/s \n",
"\u001b[K |████████████████████████████████| 1.6 MB 45.5 MB/s \n",
"\u001b[?25h Building wheel for retrying (setup.py) ... \u001b[?25l\u001b[?25hdone\n"
]
}
],
"source": [
"!pip install -q jupyter-dash"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "5-jwKXiG9HlB"
},
"outputs": [],
"source": [
"from jupyter_dash import JupyterDash"
]
},
{
"cell_type": "code",
"source": [
"import dash\n",
"from dash import dcc\n",
"from dash import html\n",
"import pandas as pd"
],
"metadata": {
"id": "DwyOpGpOBitn"
},
"execution_count": 3,
"outputs": []
},
{
"cell_type": "code",
"source": [
"df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')\n",
"available_indicators = df['Indicator Name'].unique()"
],
"metadata": {
"id": "DwykzOM_Bnty"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "code",
"source": [
"external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n",
"\n",
"app = JupyterDash(__name__, external_stylesheets=external_stylesheets)\n",
"\n",
"# Create server variable with Flask server object for use with gunicorn\n",
"server = app.server\n",
"\n",
"app.layout = html.Div([\n",
" html.Div([\n",
"\n",
" html.Div([\n",
" dcc.Dropdown(\n",
" id='crossfilter-xaxis-column',\n",
" options=[{'label': i, 'value': i} for i in available_indicators],\n",
" value='Fertility rate, total (births per woman)'\n",
" ),\n",
" dcc.RadioItems(\n",
" id='crossfilter-xaxis-type',\n",
" options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n",
" value='Linear',\n",
" labelStyle={'display': 'inline-block'}\n",
" )\n",
" ],\n",
" style={'width': '49%', 'display': 'inline-block'}),\n",
"\n",
" html.Div([\n",
" dcc.Dropdown(\n",
" id='crossfilter-yaxis-column',\n",
" options=[{'label': i, 'value': i} for i in available_indicators],\n",
" value='Life expectancy at birth, total (years)'\n",
" ),\n",
" dcc.RadioItems(\n",
" id='crossfilter-yaxis-type',\n",
" options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n",
" value='Linear',\n",
" labelStyle={'display': 'inline-block'}\n",
" )\n",
" ], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})\n",
" ], style={\n",
" 'borderBottom': 'thin lightgrey solid',\n",
" 'backgroundColor': 'rgb(250, 250, 250)',\n",
" 'padding': '10px 5px'\n",
" }),\n",
"\n",
" html.Div([\n",
" dcc.Graph(\n",
" id='crossfilter-indicator-scatter',\n",
" hoverData={'points': [{'customdata': 'Japan'}]}\n",
" )\n",
" ], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),\n",
" html.Div([\n",
" dcc.Graph(id='x-time-series'),\n",
" dcc.Graph(id='y-time-series'),\n",
" ], style={'display': 'inline-block', 'width': '49%'}),\n",
"\n",
" html.Div(dcc.Slider(\n",
" id='crossfilter-year--slider',\n",
" min=df['Year'].min(),\n",
" max=df['Year'].max(),\n",
" value=df['Year'].max(),\n",
" marks={str(year): str(year) for year in df['Year'].unique()},\n",
" step=None\n",
" ), style={'width': '49%', 'padding': '0px 20px 20px 20px'})\n",
"])\n",
"\n",
"\n",
"@app.callback(\n",
" dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),\n",
" [dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n",
" dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n",
" dash.dependencies.Input('crossfilter-xaxis-type', 'value'),\n",
" dash.dependencies.Input('crossfilter-yaxis-type', 'value'),\n",
" dash.dependencies.Input('crossfilter-year--slider', 'value')])\n",
"def update_graph(xaxis_column_name, yaxis_column_name,\n",
" xaxis_type, yaxis_type,\n",
" year_value):\n",
" dff = df[df['Year'] == year_value]\n",
"\n",
" return {\n",
" 'data': [dict(\n",
" x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],\n",
" y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],\n",
" text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n",
" customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n",
" mode='markers',\n",
" marker={\n",
" 'size': 25,\n",
" 'opacity': 0.7,\n",
" 'color': 'orange',\n",
" 'line': {'width': 2, 'color': 'purple'}\n",
" }\n",
" )],\n",
" 'layout': dict(\n",
" xaxis={\n",
" 'title': xaxis_column_name,\n",
" 'type': 'linear' if xaxis_type == 'Linear' else 'log'\n",
" },\n",
" yaxis={\n",
" 'title': yaxis_column_name,\n",
" 'type': 'linear' if yaxis_type == 'Linear' else 'log'\n",
" },\n",
" margin={'l': 40, 'b': 30, 't': 10, 'r': 0},\n",
" height=450,\n",
" hovermode='closest'\n",
" )\n",
" }\n",
"\n",
"\n",
"def create_time_series(dff, axis_type, title):\n",
" return {\n",
" 'data': [dict(\n",
" x=dff['Year'],\n",
" y=dff['Value'],\n",
" mode='lines+markers'\n",
" )],\n",
" 'layout': {\n",
" 'height': 225,\n",
" 'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},\n",
" 'annotations': [{\n",
" 'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',\n",
" 'xref': 'paper', 'yref': 'paper', 'showarrow': False,\n",
" 'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',\n",
" 'text': title\n",
" }],\n",
" 'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},\n",
" 'xaxis': {'showgrid': False}\n",
" }\n",
" }\n",
"\n",
"\n",
"@app.callback(\n",
" dash.dependencies.Output('x-time-series', 'figure'),\n",
" [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n",
" dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n",
" dash.dependencies.Input('crossfilter-xaxis-type', 'value')])\n",
"def update_y_timeseries(hoverData, xaxis_column_name, axis_type):\n",
" country_name = hoverData['points'][0]['customdata']\n",
" dff = df[df['Country Name'] == country_name]\n",
" dff = dff[dff['Indicator Name'] == xaxis_column_name]\n",
" title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)\n",
" return create_time_series(dff, axis_type, title)\n",
"\n",
"\n",
"@app.callback(\n",
" dash.dependencies.Output('y-time-series', 'figure'),\n",
" [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n",
" dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n",
" dash.dependencies.Input('crossfilter-yaxis-type', 'value')])\n",
"def update_x_timeseries(hoverData, yaxis_column_name, axis_type):\n",
" dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]\n",
" dff = dff[dff['Indicator Name'] == yaxis_column_name]\n",
" return create_time_series(dff, axis_type, yaxis_column_name)"
],
"metadata": {
"id": "jRsFklzuBp6K"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "code",
"source": [
"app.run_server(port=8051)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 52
},
"id": "AheccBwxBzuJ",
"outputId": "7e9dbe84-b1e9-4fdc-c147-20e93b69f71d"
},
"execution_count": 6,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Dash app running on:\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Javascript object>"
],
"application/javascript": [
"(async (port, path, text, element) => {\n",
" if (!google.colab.kernel.accessAllowed) {\n",
" return;\n",
" }\n",
" element.appendChild(document.createTextNode(''));\n",
" const url = await google.colab.kernel.proxyPort(port);\n",
" const anchor = document.createElement('a');\n",
" anchor.href = new URL(path, url).toString();\n",
" anchor.target = '_blank';\n",
" anchor.setAttribute('data-href', url + path);\n",
" anchor.textContent = text;\n",
" element.appendChild(anchor);\n",
" })(8051, \"/\", \"http://127.0.0.1:8051/\", window.element)"
]
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": [
"## Use cloudflare tunnel"
],
"metadata": {
"id": "YK0Us9YxKHMc"
}
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "8CWgQoII9fJA",
"outputId": "739376ef-9221-4277-dea9-5b75ac478f6a"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Selecting previously unselected package cloudflared.\n",
"(Reading database ... 155676 files and directories currently installed.)\n",
"Preparing to unpack cloudflared-linux-amd64.deb ...\n",
"Unpacking cloudflared (2022.8.2) ...\n",
"Setting up cloudflared (2022.8.2) ...\n",
"Processing triggers for man-db (2.8.3-2ubuntu0.1) ...\n"
]
}
],
"source": [
"!wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && dpkg -i cloudflared-linux-amd64.deb"
]
},
{
"cell_type": "code",
"source": [
"!cloudflared tunnel --url localhost:8051 --protocol http2"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "QntdP0TSB6Id",
"outputId": "59aeac4a-c080-4eb9-8fee-ee51aba6da3f"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\u001b[90m2022-08-17T18:53:30Z\u001b[0m \u001b[32mINF\u001b[0m Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps\n",
"\u001b[90m2022-08-17T18:53:30Z\u001b[0m \u001b[32mINF\u001b[0m Requesting new quick Tunnel on trycloudflare.com...\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m +--------------------------------------------------------------------------------------------+\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m | https://diverse-zshops-designing-beads.trycloudflare.com |\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m +--------------------------------------------------------------------------------------------+\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Cannot determine default configuration path. No file [config.yml config.yaml] in [~/.cloudflared ~/.cloudflare-warp ~/cloudflare-warp /etc/cloudflared /usr/local/etc/cloudflared]\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Version 2022.8.2\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m GOOS: linux, GOVersion: go1.18.5, GoArch: amd64\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Settings: map[p:http2 protocol:http2 url:localhost:8051]\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Generated Connector ID: 74539447-91fb-4aee-947a-5398418c6cf6\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m cloudflared will not automatically update if installed by a package manager.\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Initial protocol http2\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Starting metrics server on 127.0.0.1:34683/metrics\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[1m\u001b[31mERR\u001b[0m\u001b[0m Register tunnel error from server side \u001b[31merror=\u001b[0m\u001b[31m\"Unauthorized: Failed to get tunnel\"\u001b[0m \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:33Z\u001b[0m \u001b[32mINF\u001b[0m Retrying connection in up to 2s seconds \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:34Z\u001b[0m \u001b[1m\u001b[31mERR\u001b[0m\u001b[0m Register tunnel error from server side \u001b[31merror=\u001b[0m\u001b[31m\"Unauthorized: Failed to get tunnel\"\u001b[0m \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:34Z\u001b[0m \u001b[32mINF\u001b[0m Retrying connection in up to 4s seconds \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:34Z\u001b[0m \u001b[1m\u001b[31mERR\u001b[0m\u001b[0m Register tunnel error from server side \u001b[31merror=\u001b[0m\u001b[31m\"Unauthorized: Failed to get tunnel\"\u001b[0m \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:34Z\u001b[0m \u001b[32mINF\u001b[0m Retrying connection in up to 8s seconds \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:37Z\u001b[0m \u001b[1m\u001b[31mERR\u001b[0m\u001b[0m Register tunnel error from server side \u001b[31merror=\u001b[0m\u001b[31m\"Unauthorized: Failed to get tunnel\"\u001b[0m \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:37Z\u001b[0m \u001b[32mINF\u001b[0m Retrying connection in up to 16s seconds \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113\n",
"\u001b[90m2022-08-17T18:53:52Z\u001b[0m \u001b[32mINF\u001b[0m Connection fff28f06-8b7d-4cd4-97d6-5786b32c93cd registered \u001b[36mconnIndex=\u001b[0m0 \u001b[36mip=\u001b[0m198.41.200.113 \u001b[36mlocation=\u001b[0mATL\n",
"\u001b[90m2022-08-17T18:53:53Z\u001b[0m \u001b[32mINF\u001b[0m Connection f1b6ecf8-4241-4aa7-8095-758c9bed1067 registered \u001b[36mconnIndex=\u001b[0m1 \u001b[36mip=\u001b[0m198.41.192.7 \u001b[36mlocation=\u001b[0mATL\n",
"\u001b[90m2022-08-17T18:53:53Z\u001b[0m \u001b[32mINF\u001b[0m Connection 91672451-3620-4f26-b0d3-f6506d0571e5 registered \u001b[36mconnIndex=\u001b[0m2 \u001b[36mip=\u001b[0m198.41.200.53 \u001b[36mlocation=\u001b[0mATL\n",
"\u001b[90m2022-08-17T18:53:54Z\u001b[0m \u001b[32mINF\u001b[0m Connection 289ed333-a198-434f-ae0d-c88a71186e50 registered \u001b[36mconnIndex=\u001b[0m3 \u001b[36mip=\u001b[0m198.41.192.37 \u001b[36mlocation=\u001b[0mATL\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"You can also set the tunnel to go to a subdomain that you own through Cloudflare. To do this, follow this guide: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/remote/"
],
"metadata": {
"id": "9O6FXZemKMgH"
}
},
{
"cell_type": "code",
"source": [
""
],
"metadata": {
"id": "uiTT7eaZKS-H"
},
"execution_count": null,
"outputs": []
}
],
"metadata": {
"colab": {
"name": "Dash Cloudflared experiments.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment