Hi there, @Emil @jokin first of all thanks a lot for those amazing dash-extensions websocket releases
We’re an open-source projet trying hard to have our platform processing smoothly our API Post data. We are then using the Websocket component and it’s working pretty fine as long as we’re on http://localhost:8050/
Here is a slight snapshot of how our app interact with the websocket
main.py →
# Main Dash imports, used to instantiate the web-app and create callbacks (ie. to generate interactivity)
import os
import dash
from dash.dependencies import Input, Output, State, MATCH
from dash.exceptions import PreventUpdate
# Flask caching import
from flask_caching import Cache
# Various modules provided by Dash and Dash Leaflet to build the page layout
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_leaflet as dl
from dash_extensions.websockets import SocketPool, run_server
from dash_extensions import WebSocket
# Pandas to read the login correspondences file
import pandas as pd
# Import used to make the API call in the login callback
import requests
# ----------------------------------------------------------------------------------------------------------------------
# APP INSTANTIATION & OVERALL LAYOUT
# We start by instantiating the app (NB: did not try to look for other stylesheets yet)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.UNITED])
socket_pool = SocketPool(app)
# We define a few attributes of the app object
app.title = 'Pyronear - Monitoring platform'
app.config.suppress_callback_exceptions = True
server = app.server # Gunicorn will be looking for the server attribute of this module
# We create a rough layout, filled with the content of the homepage/alert page
app.layout = html.Div(
[
dcc.Location(id="url", refresh=False),
html.Div(id="page-content", style={"height": "100%"}),
# Storage components which contains data relative to alerts
dcc.Store(id="store_live_alerts_data", storage_type="session"),
dcc.Store(id="last_displayed_event_id", storage_type="session"),
dcc.Store(id="images_url_live_alerts", storage_type="session", data={}),
# Storage component which contains data relative to site devices
dcc.Store(id="site_devices_data_storage", storage_type="session", data=get_site_devices_data(client=api_client))
]
)
# End point ping by the API for each alert being recorded. Then broadcasting message to ALL sessions
@app.server.route("/alert/<message>")
def broadcast_message(message):
socket_pool.broadcast(message)
return f"Message {message} broadcast."
# First API relay once an alert is sent to the platform, passing msg.data into msg hidden div
app.clientside_callback("function(msg){if(msg == null) {return;} else {return msg.data;}}",
Output("msg", "children"), [Input("ws", "message")])
@app.callback(
Output('store_live_alerts_data', 'data'),
Output('images_url_live_alerts', 'data'),
Input('msg', 'children')
)
def update_live_alerts_data(alert):
# Fetching live alerts where is_acknowledged is False
response = api_client.get_ongoing_alerts().json()
all_alerts = pd.DataFrame(response)
if all_alerts.empty:
raise PreventUpdate
live_alerts = all_alerts.loc[~all_alerts["is_acknowledged"]]
# Fetching live_alerts frames urls and instantiating a dict of live_alerts urls having event_id keys
dict_images_url_live_alerts = {}
for _, row in live_alerts.iterrows():
img_url = ""
try:
img_url = api_client.get_media_url(row["media_id"]).json()["url"]
except Exception:
pass
if row['event_id'] not in dict_images_url_live_alerts.keys():
dict_images_url_live_alerts[row['event_id']] = []
dict_images_url_live_alerts[row['event_id']].append(img_url)
else:
dict_images_url_live_alerts[row['event_id']].append(img_url)
return live_alerts.to_json(orient='records'), dict_images_url_live_alerts
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Pyronear web-app',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--host', type=str, default='127.0.0.1', help='Host of the server')
parser.add_argument('--port', type=int, default=8050, help='Port to run the server on')
args = parser.parse_args()
run_server(app, port=args.port)
If my understanding is OK the WebSocket(id="ws")
(located in another homepage.py) is updated either by the app.clientside_callback
or socket_pool.broadcast(message)
Anyway everything is working fine, each time we GET the route e.g : requests.get("http://localhost:8050/alert/16")
the callback chain is triggered.
Things get more tricky when the app is deployed on heroku, we actually deploy modifying the Websocket component to make it possible to use https as follow WebSocket(url=“wss://pyro-platform.herokuapp.com/”, id=“ws”). The connection seems to work but our callbacks are not triggered anymore when requests.get("http://pyro-platform.herokuapp.com/alert/12")
I know it’s long but if you could help it would be awesome, full code here btw, thankksssss
NB : no errors neither among heroku logs nor browser console