Hi all,
I’m developing an app for monitoring which has 3 graphs and 2 gif that contain monitoring images. This app also uses a dcc.Dropdown to change the graphs depending on the station but the gifs are the same for all the stations.
My problem is that the source of the gifs needs to be updated every 20 minutes, the source of the gif is provided via a python script that uses celery beat to be invoked every 20 minutes.
This script gets images using web scraping and then converts them into a gif, and stores it the project. The thing is I don’t know where the gif should be stored to make this work.
Now I’ll provide the code of my dash app, which uses CSS Grid to display the graphs and gifs inside a 3x3 matrix.
stations = ["station1", "station2", "station3"]
#Create DjangoDash applicaiton
app = DjangoDash(name='Subplots', serve_locally=True)
app.layout = html.Div([
#Add dropdown for option selection
dcc.Dropdown(
id = 'station',
options = [{'label': i, 'value': i} for i in stations],
value = "station1",#Initial value for the dropdown
style={'width': '25%', 'margin':'0px auto'}),
html.Button("Download CSV", id="btn_csv"),
dcc.Download(id="download-dataframe-csv"),
dcc.Interval(id='interval-component',
interval= 3 * 60000, # every 3 minutes,
n_intervals=0
),
html.Div([
html.Div([
dcc.Graph(id = 'seeing_plot',
animate = False,
style={"backgroundColor": "#FFF0F5"}),
], style={'grid-column-start': '1', 'grid-row-start': '1'}),
html.Div([
dcc.Graph(id = 'station_plot',
animate = False,
style={"backgroundColor": "#FFF0F5"}),
], style={'grid-column-start': '1', 'grid-row-start': '2', 'grid-row-end': 'span 2'}),
html.Div([
dcc.Graph(id = 'scattergl_plot',
animate = False,
style={"backgroundColor": "#FFF0F5"}),
], style={'grid-column-start': '2', 'grid-row-start': '1', 'grid-row-end': 'span 2'}),
html.Div([
gif.GifPlayer(
id= 'gif_1',
gif= app.get_asset_url('gif_1.gif'),
still= app.get_asset_url('gif_1.png')
)
], style={'grid-column-start': '2', 'grid-row-start': '3', 'margin-left': 'auto', 'margin-right': 'auto'}),
html.Div([
gif.GifPlayer(
id='gif_2',
gif= app.get_asset_url('gif_2.gif'),
still= app.get_asset_url('gif_2.png')
)
], style={'grid-column-start': '3', 'grid-row-start': '1', 'grid-row-end': '3'}),
], style={'display': 'grid', 'grid-template-columns': '800px 340px 1fr', 'grid-template-rows': '180px 160px 1fr'})
])
# Callback for updating graph, there are 3 of this but since is not the problem I just posted one.
@app.callback(
Output('station_plot', 'figure'), #id of html component
[Input('station', 'value')]) #id of html component
def display_value(station):
"""
This function returns figure object according to value input
Input: Value specified
Output: Figure object
"""
df = Dummyrender(station)
df.generate_stations_plot()
return df.fig
# Callback for downloading csv
@app.callback(
Output("download-dataframe-csv", "data"),
Input("btn_csv", "n_clicks"),
Input('station', 'value'),
prevent_initial_call=True)
def func(*args,**kwargs):
#
#This function is responsible to download the csv but ONLY when the button
#is clicked, without this function the code downloads the csv when changing
#the dropdown or when the button is clicked because of how dash app callback
#inputs works.
#
# In Django_plotly_dash is necessary to use kwargs otherwise it wont work
# For more info check:
# https://stackoverflow.com/questions/76686162/handling-different-actions-based-on-click-event-and-search-in-django-plotly-dash
ctx = kwargs['callback_context']
# The context is received as a list with a dictionary inside thats why
# you have to call [0] and the .get('prop_id')
if ctx.triggered[0].get('prop_id') == 'btn_csv.n_clicks':
# The input values are recived as args
# args[0]: btn_csv.n_clicks
# args[1]: station.value
data = Dummyrender(args[1])
return dcc.send_data_frame(data.df.to_csv, f"{args[1]}-{now_string}.csv")
# Callback for updating gifs
@app.callback(Output("gif_1", "gif"),
[Input('interval-component', 'n_intervals')])
def update_metrics(n):
print("--------------------------")
print("ABOUT TO UPDATE GIF")
print("--------------------------")
return app.get_asset_url('gif_1.gif')
As you can see I’m trying to update the gif using app.get_asset_url('gif_1.gif')
but the problem is that’s for static files and I modified my script with the celery beat to override the gif_1.gif file and it works in the sense that it overrides it but because is a static file I think it doesn’t matter.
So in a nutshell:
-
I’m trying to find a way to have a gif in my project which I can override with my python script and celery beat every 20 mins and use the callback to update the gif image.
-
I need to override the gif to save storage, the only thing that I’m saving are the images that I use to create the gifs in folders, but the script only allows to have 3 folders max for saving storage too. If you want to check the script check: Python Script.
Also I’m starting to think that maybe I’ll need to store the gifs and images in a separate project and provide the source using https, something like this:
gif.GifPlayer(
id='gif_2',
gif= 'https://otherproject/gif_1.gif',
still= 'https://otherproject/gif_1.png'
)
Let me know if you need more information.