How do I enable my app users to lodge comments against the charts displayed in my streamlit app?
My app has a lot of line-charts displaying time-series of various business metrics.
My app is used by business executives - who sometimes want to lodge comments about certain data points in the charts.
What would be the right way to do it? Are there any extensions that allows this out of the box?
Any help greatly appreciated! Thanks!
What is the purpose? Will the comments be permanently stored? Should they appear when the app is run again? Should they keep appearing even if the data changes?
Thanks for your response. Yes, if my business stakeholder makes an annotation, I should be able to see it… (more like a social place where people leave comments, interesting insights etc…).
I am sure i am asking for too many things… But any pointers towards Streamlit feature / extension that can help me realize this with some coding from my side … would be great!
Here’s a bit of a complicated example using streamlit-plotly-events and saving the data to a local file with the built-in shelve method from Python. To make this work in a real scenario, or one where you can’t count on local files to persist (e.g. on Community Cloud), you would want to use an external database instead of shelveConnecting to data - Streamlit Docs
import plotly.express as px
import pandas as pd
import streamlit as st
import shelve
from streamlit_plotly_events import plotly_events
DB_FILE = "db.shelve"
if "comments" not in st.session_state:
with shelve.open(DB_FILE) as db:
if "comments" in db:
st.session_state.comments = db["comments"]
else:
st.session_state.comments = {}
if "selected_point" not in st.session_state:
st.session_state.selected_point = None
def point_to_key(point: dict[str, int]) -> str:
return f"{point['x']}_{point['y']}"
def key_to_xy(key: str) -> tuple[int, int]:
return tuple([int(a) for a in key.split("_")])
points = pd.DataFrame({"x": [1, 2, 3, 4, 5, 6], "y": [1, 4, 9, 16, 25, 36]})
points["has_comment"] = False
for key, comment in st.session_state.comments.items():
x, y = key_to_xy(key)
points.loc[(points["x"] == x) & (points["y"] == y), "has_comment"] = True
fig = px.scatter(
points,
x="x",
y="y",
color="has_comment",
color_discrete_map={True: "red", False: "blue"},
)
selected_points = plotly_events(fig)
if selected_points:
st.session_state.selected_point = point_to_key(selected_points[0])
if not st.session_state.selected_point:
st.stop()
def submit_comments(key: str):
if key not in st.session_state.comments:
st.session_state.comments[key] = []
st.session_state.comments[key].append(st.session_state["new_comment"])
with shelve.open(DB_FILE) as db:
db["comments"] = st.session_state.comments
key = st.session_state.selected_point
if key in st.session_state.comments:
x, y = key_to_xy(key)
st.write(f"Comments: on point ({x}, {y}):")
for comment in st.session_state.comments[key]:
st.write(f"* {comment}")
with st.form("comments"):
x, y = key_to_xy(key)
st.text_input(
f"What would you like to say about this point ({x}, {y})", key="new_comment"
)
st.form_submit_button("Submit", on_click=submit_comments, args=(key,))