Restructured dashboard layout in terms of row and column components from dash_bootstrap_component. Added three bottoms and their callbacks to manage create flag, reset, and commit operations.

This commit is contained in:
2024-08-21 15:26:11 +02:00
parent 4a25ec95d6
commit f883309c1e

View File

@ -1,8 +1,12 @@
import sys import sys, os
import os
import h5py
import numpy as np
import pandas as pd import pandas as pd
import numpy as np
import base64
import dash
import io
# Set up project root directory # Set up project root directory
root_dir = os.path.abspath(os.curdir) root_dir = os.path.abspath(os.curdir)
sys.path.append(root_dir) sys.path.append(root_dir)
@ -11,165 +15,180 @@ sys.path.append(os.path.join(root_dir,'dima'))
import dima.src.hdf5_data_extraction as h5de import dima.src.hdf5_data_extraction as h5de
import dima.src.metadata_review_lib as ma import dima.src.metadata_review_lib as ma
import dima.src.g5505_utils as utils import dima.src.g5505_utils as utils
import base64
import dash
import io
import data_flagging_utils as data_flagging_utils import data_flagging_utils as data_flagging_utils
#import dash_core_components as dcc
#import dash_html_components as html
#from dash.dependencies import Input, Output, State
from dash import Dash, html, dcc, callback, Output, Input, State from dash import Dash, html, dcc, callback, Output, Input, State
import plotly.graph_objs as go import plotly.graph_objs as go
from plotly.subplots import make_subplots from plotly.subplots import make_subplots
import pandas as pd import dash_bootstrap_components as dbc
import numpy as np
import tempfile # Initialize Dash app with Bootstrap theme
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
dbc.Row([
app = dash.Dash(__name__) dbc.Col([
app.layout = html.Div([ dcc.Upload(
html.Div([ id='upload-image',
dcc.Upload( children=html.Div(['Drag and Drop or ',html.A('Select Files')]),
id='upload-image', style={
children=html.Div([ 'fontSize': "16px",
'Drag and Drop or ', 'width': '100%',
html.A('Select Files') 'height': '60px',
]), style={"fontSize": "16px", 'lineHeight': '60px',
'width': '100%', 'borderWidth': '1px',
'height': '60px', 'borderStyle': 'dashed',
'lineHeight': '60px', 'borderRadius': '5px',
'borderWidth': '1px', 'textAlign': 'center',
'borderStyle': 'dashed', 'margin': '10px'
'borderRadius': '5px', }),
'textAlign': 'center', dcc.Dropdown(
'margin': '10px' id='flag-options',
},) options=data_flagging_utils.dropdown_menu_options,
]), )],
html.Div([ width=12
dcc.Dropdown( )],justify="center", align="center"),
id='flag-options',
options=[{'label': data_flagging_utils.flags_dict[key]['flag_description'], 'value': key} for key in data_flagging_utils.flags_dict.keys()]#,
#value='000'
),
html.Button('Flag Selected', id='flag-button')
]),
html.Div([
dcc.Graph(id='timeseries-plot',
config={
'modeBarButtonsToAdd': ['select2d', 'lasso2d'], # Add box select and lasso select tools
'displaylogo': False # Optionally remove the Plotly logo from the mode bar
}),
]), dbc.Row([
dcc.Store(id='memory-output'),#, data=fig.to_dict()), # Store the initial figure dbc.Col([dbc.Button('Create Flag', id='flag-button', color="primary", className="mt-2")],width=2),
html.Div(id='textarea-example-output', style={'whiteSpace': 'pre-line'}) dbc.Col([dbc.Button('Reset Flag', id='reset-flag-button', color="secondary", className="mt-2")],width=2),
#html.Div(id='output-container') dbc.Col([dbc.Button('Commit Flag', id='commit-flag-button', color="secondary", className="mt-2")],width=2)
]) ], justify="center", align="center"),
@app.callback(Output('memory-output','data'), dbc.Row([
Output('timeseries-plot', 'figure'), dbc.Col([ dcc.Graph(id='timeseries-plot')], width=8),
[Input('upload-image','filename')], dbc.Col([html.Div(id='flag-record', style={'whiteSpace': 'pre-line'})], width=4), #config={'modeBarButtons': True,
[Input('upload-image','contents')] #'modeBarButtonsToAdd':['select2d','lasso2d'],
#'modeBarButtonsToRemove': ['zoom', 'pan']}),], width=12)
],justify="center", align="center"),
dbc.Row([ # row 3
dbc.Col([
dcc.Store(id='memory-output'),
html.Div(id='textarea-example-output', style={'whiteSpace': 'pre-line'})
], width=12)
],justify="center", align="center"),
],
) )
def load_data(filename,contents):
data = {'data_loaded_flag':False} @app.callback(
Output('memory-output','data'),
Output('timeseries-plot', 'figure'),
[Input('upload-image','filename')],
[Input('upload-image','contents')]
)
def load_data(filename, contents):
data = {'data_loaded_flag': False}
if filename and contents and filename.endswith('.h5'): if filename and contents and filename.endswith('.h5'):
try: try:
# Decode and read the uploaded file
content_type, content_string = contents.split(',') content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string) decoded = base64.b64decode(content_string)
file_path = io.BytesIO(decoded) file_path = io.BytesIO(decoded)
# Create a temporary file to save the content
#with tempfile.NamedTemporaryFile(delete=False, suffix='.h5') as temp_file:
# temp_file.write(decoded)
# temp_file_path = temp_file.name
fig = data_flagging_utils.create_loaded_file_figure(file_path) fig = data_flagging_utils.create_loaded_file_figure(file_path)
# Return file path and other relevant info
data['data_loaded_flag'] = True data['data_loaded_flag'] = True
fig.update_layout(dragmode='select',
activeselection=dict(fillcolor='yellow'))
return data, fig return data, fig
except Exception as e: except Exception as e:
print(f"Error processing file: {e}") print(f"Error processing file: {e}")
return data, dash.no_update return data, dash.no_update
return data, dash.no_update return data, dash.no_update
@app.callback( @app.callback(
Output('timeseries-plot', 'figure',allow_duplicate=True), Output('timeseries-plot', 'figure', allow_duplicate=True),
Output('textarea-example-output','children'), Output('textarea-example-output','children'),
Input('timeseries-plot', 'relayoutData'), Input('flag-button', 'n_clicks'),
State('timeseries-plot', 'figure'), State('timeseries-plot', 'figure'),
State('memory-output', 'data'), State('memory-output', 'data'),
prevent_initial_call=True prevent_initial_call=True
) )
def update_graph(relayoutData, fig, data): def create_flag(n_clicks, fig, data):
if not data or not data.get('data_loaded_flag', False):
if data is None:
return dash.no_update, dash.no_update return dash.no_update, dash.no_update
if data.get('data_loaded_flag',False) is False: fig['layout'].update({'dragmode' : 'select',
return dash.no_update, dash.no_update 'activeselection' : dict(fillcolor='yellow'),
# If fig is None or in its initial state, return no_update 'doubleClick' : 'reset'
#if fig is None or not fig['data']: # Check if the figure is empty or in an initial state })
# return dash.no_update, dash.no_update value = '{} amigos'.format(n_clicks)
return fig, f'You have entered: \n{value}'
shapes = []
# Handle case where relayoutData is provided
if relayoutData and 'xaxis.range[0]' in relayoutData:
start = relayoutData['xaxis.range[0]']
end = relayoutData['xaxis.range[1]']
else:
start = None # Set to default or previously known values
end = None # Set to default or previously known values
if start and end:
shapes.append({
'type': 'rect',
'xref': 'x',
'yref': 'paper',
'x0': start,
'y0': 0,
'x1': end,
'y1': 1,
'fillcolor': 'rgba(128, 0, 128, 0.3)',
'line': {'width': 0}
})
# Update the figure with the new shape
fig['layout'].update(shapes=shapes)
value = 'hola amigos'
#return fig, 'You have entered: \n{}'.format(value)
return dash.no_update, 'You have entered: \n{}'.format(value)
#@app.callback( #@app.callback(
#Output('output-container', 'children'), # Output('timeseries-plot', 'figure', allow_duplicate=True),
# [Input('flag-button', 'n_clicks')], # Output('timeseries-plot', 'selectedData', allow_duplicate=True),
# [State('flag-options', 'value'), # #Output('textarea-example-output','children'),
# State('timeseries-plot', 'relayoutData')] # Input('reset-flag-button', 'n_clicks'),
# State('timeseries-plot', 'figure'),
# #State('memory-output', 'data'),
# prevent_initial_call=True
#) #)
#def flag_data(n_clicks, flag, relayoutData): #def clear_flag(n_clicks, fig):
# if n_clicks and relayoutData and 'xaxis.range[0]' in relayoutData: #if not data or not data.get('data_loaded_flag', False):
# start = relayoutData['xaxis.range[0]'] # return dash.no_update, dash.no_update
# end = relayoutData['xaxis.range[1]']
# print(f'Flagged with: {flag} from {start} to {end}')
# elif n_clicks:
# print('Please select an interval to flag.')
#if __name__ == '__main__': # fig['layout'].update({'dragmode': 'zoom', 'activeselection': None})
app.run_server(debug=True) #fig.update_layout()
#update_layout(dragmode='select', activeselection=dict(fillcolor='yellow'))
#shapes = []
#if relayoutData and 'xaxis.range[0]' in relayoutData:
# start = relayoutData['xaxis.range[0]']
# end = relayoutData['xaxis.range[1]']
#else:
# start, end = None, None
#if start and end:
# shapes.append({
# 'type': 'rect',
# 'xref': 'x',
# 'yref': 'paper',
# 'x0': start,
# 'y0': 0,
# 'x1': end,
# 'y1': 1,
# 'fillcolor': 'rgba(128, 0, 128, 0.3)',
# 'line': {'width': 0}
# })
# fig['layout'].update(shapes=shapes)
#value = '{} amigos'.format(n_clicks)
# return fig, None #, f'You have entered: \n{value}'
@app.callback(
[Output('timeseries-plot', 'selectedData'),
Output('timeseries-plot', 'figure', allow_duplicate=True)],
[Input('reset-flag-button', 'n_clicks'),
State('timeseries-plot', 'figure'),
State('memory-output', 'data')],
prevent_initial_call = True)
def clear_flag(n_clicks, fig, data):
if n_clicks > 0 and data.get('data_loaded_flag', False):
# Clear selection
selected_data = None
fig['layout'].update({'dragmode': 'zoom', 'activeselection': None,
'selections':{'line': None}})
return selected_data, fig
else:
return dash.no_update, dash.no_update
@callback(Output('flag-record', 'children'),
Input('commit-flag-button','n_clicks'),
State('timeseries-plot','selectedData'),
prevent_initial_call=True)
def commit_flag(n_clicks,selected_Data):
value = selected_Data
return f'You have entered: \n{value}'
if __name__ == '__main__':
app.run_server(debug=True)