Implementing Plotly view with data table in Flask App

Hi everyone,

I have been working for a while on developing a website using Flask and recently discovered the fantastic tool Plotly. However, I am having some trouble implementing Plotly’s functionalities into my website.

Specifically, I want to use Plotly’s Lasso selection option to create a table with the selected data. I already have the code for this functionality, but I am unsure how to incorporate it into the website.

This is the code

In this part i prepare the data that is going to be analysed

import numpy as np
import plotly.graph_objs as go
from scipy.signal import find_peaks, peak_prominences, peak_widths
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as sc
import plotly.graph_objects as go
from ipywidgets import VBox

# Function to read numeric data from a file
def read_numeric_data(file_path):
    with open(file_path, 'r', encoding='latin1') as f:
        data = []
        live_time = None
        real_time = None
        for line in f:
            if 'LIVE_TIME' in line:
                live_time = line.strip()
            elif 'REAL_TIME' in line:
                real_time = line.strip()
            elif all(char.isdigit() or char in ".-+e" for char in line.strip()):
                row = [float(x) for x in line.split()]
                data.extend(row)
    return np.array(data), live_time, real_time

file_path = '/home/Manuel/Calabria/APP/uploads/uploaded_directory/Unknown/Cópia de Fundo_40kV.mcs'

data, _, _ = read_numeric_data(file_path)

And here i create the Plotly and data slection

import pandas as pd


a, b = -0.014213857884039616, 0.039500128118851285

# Constantes
a, b = -0.014213857884039616, 0.039500128118851285
value = 2048  # Ajusta esto según tu necesidad

# Generar los valores de 'X'
x = a + b * np.linspace(0, value, value)

# Supongamos que 'data' contiene tus valores de 'Y'


# Crear el DataFrame 'dt' con las columnas 'X' y 'Y'
dt = pd.DataFrame({'X (Kev)': x, 'Y': data/np.max(data)})
print(dt.columns) 


# Function definition for plot_energy_spectrum
def plot_energy_spectrum(data, a, b,widht):
    maxim = np.max(data)
    workingarray = np.squeeze(data)
    x = a + b * np.linspace(0, value, value)

    # Finding peaks with prominence and other parameters
    peaks, properties = find_peaks(workingarray / maxim, height=0.008, prominence=0.01, width=widht)
    prominences = peak_prominences(workingarray / maxim, peaks)[0]
    widths, width_heights, left_ips, right_ips = peak_widths(workingarray / maxim, peaks, rel_height=0.5)

    peak_values = workingarray[peaks] / maxim
    peak_indices = peaks

    # Create traces for the plot
    trace1 = go.Scatter(x=x, y=workingarray[:value] / maxim, mode='lines+markers', name='Normalized Data',
                        marker=dict(color='black'))
    trace2 = go.Scatter(x=a + b * peak_indices, y=peak_values, mode='markers', name='Detected Peaks',
                        marker=dict(color='red', size=10))

    # Create traces for the widths
    width_traces = []
    for i in range(len(peaks)):
        width_traces.append(go.Scatter(
            x=[x[int(left_ips[i])], x[int(right_ips[i])]],
            y=[width_heights[i], width_heights[i]],
            mode='lines',
            name=f'Width of Peak {i+1}',
            line=dict(color='blue', dash='dash')
        ))

    # Create annotations for the peaks
    annotations = []
    for j in range(len(peak_indices)):
        rescaled_width = a + b * widths[j]
        annotations.append(dict(
            x=a + b * peak_indices[j],
            y=peak_values[j],
            text=f"{a + b * peak_indices[j]:.2f} KeV<br>Width: {rescaled_width:.2f}",
            showarrow=True,
            arrowhead=2,
            ax=0,
            ay=-15
        ))

    layout = go.Layout(
        title='Calibrated Plot',
        xaxis=dict(title='Energy (KeV)'),
        yaxis=dict(title='Normalized data'),
        showlegend=True,
        annotations=annotations,
        width=100,
        height=800
    )

    fig = go.FigureWidget()
    fig.add_trace(trace1)
    fig.add_trace(trace2)
    fig.add_traces(width_traces)
    fig.update_layout(annotations=annotations)
    return fig

widht=(0.05-a)/b

# Create plot
fig = plot_energy_spectrum(data, a, b,widht)




# Create a table figure widget
table = go.FigureWidget([go.Table(
    header=dict(values=['X (Kev)', 'Y'],
                fill_color='#C2D4FF',
                align='left'),
    cells=dict(values=[dt[col] for col in ['X (Kev)', 'Y']],
               fill_color='#F5F8FF',
               align='left'))
])

# Variable para guardar los datos seleccionados
selected_data = None

# Function to update the table based on scatter plot selection and save selected data
def selection_fn(trace, points, selector):
    global selected_data
    inds = points.point_inds
    if inds:
        selected_data = dt.iloc[inds].copy()
        table.data[0].cells.values = [selected_data[col].tolist() for col in ['X (Kev)', 'Y']]
    else:
        selected_data = None
        table.data[0].cells.values = [dt[col].tolist() for col in ['X (Kev)', 'Y']]

# Link the selection function to the scatter plot
fig.data[0].on_selection(selection_fn)

# Display the figure and table in a VBox
display(VBox([fig, table]))

print(selected_data)

Does anyone know where i need to look, in orther to learn how to implement that code properly in my website?

In my app, I created the corresponding app route and thought that using jsonify from Flask could help, but it did not work.

The app route essentially gets some parameters that are used as thresholds for the find_peak function, which I know works and generates a plot on the website. However, after trying to add the table, the plot no longer shows up.


@app.route('/noisydata', methods=['GET', 'POST'])
def noisydata():
    global a, b, spectradata, energy_spectrum, dt  # Assuming these are defined elsewhere

    # Constantes
    a, b = -0.014213857884039616, 0.039500128118851285
    value = 2048  # Ajusta esto según tu necesidad

    # Generar los valores de 'X'
    x = a + b * np.linspace(0, value, value)

    # Supongamos que 'data' contiene tus valores de 'Y'


    # Crear el DataFrame 'dt' con las columnas 'X' y 'Y'
    dt = pd.DataFrame({'X (Kev)': x, 'Y': spectradata/np.max(spectradata)})



    req_data = request.get_json()
    height = float(req_data['height'])
    prominence = float(req_data['prominence'])
    width = (float(req_data['width']) - a) / b

    value = len(spectradata)

    print('altura', height)
    print('prominence', prominence)
    print('ancho', width)
    print(value)

    fig, energy_spectrum = plot_noisy_spectrum(spectradata, a, b, height, prominence, width, value)

    plot_html = fig.to_html(full_html=False)

    # Create a table figure widget
    table = go.FigureWidget([go.Table(
        header=dict(values=['X (Kev)', 'Y'],
                    fill_color='#C2D4FF',
                    align='left'),
        cells=dict(values=[dt[col] for col in ['X (Kev)', 'Y']],
                   fill_color='#F5F8FF',
                   align='left'))
    ])

    # Variable to store selected data
    selected_data = None

    # Function to update the table based on scatter plot selection and save selected data
    def selection_fn(trace, points, selector):
        global selected_data
        inds = points.point_inds
        if inds:
            selected_data = dt.iloc[inds].copy()
            table.data[0].cells.values = [selected_data[col].tolist() for col in ['X (Kev)', 'Y']]
        else:
            selected_data = None
            table.data[0].cells.values = [dt[col].tolist() for col in ['X (Kev)', 'Y']]

    # Link the selection function to the scatter plot
    fig.data[0].on_selection(selection_fn)

    table_html = pio.to_html(table, full_html=False)



    print(selected_data)

    return jsonify(plot_html=plot_html, table_html=table_html)