How to create a sentiment analysis dashboard using Python, Streamlit, and ChatGPT

How to create a sentiment analysis dashboard using Python, Streamlit, and ChatGPT

11 min read

When running a business, knowing how your customers feel is important. Do they like your product or service? Do they hate it? You can collect a lot of feedback via email or web form. But analyzing all those emails can become a tedious and error-prone task, that’s, using an AI tool like ChatGPT can change everything. Along with Python and the Streamlit library, we will create a Sentiment Analysis dashboard that will read our emails and prepare all the information for us.

Is your system ready?

If you already have the Nylas Python SDK installed and your Python environment configured, continue with the blog.

Otherwise, I recommend reading the post How to Send Emails with the Nylas Python SDK where the basic setup is clearly explained.

What are we going to talk about?

What our application will look like

Previously, we wrote a blog post called Python, Sentiment Analysis, R and Shiny. Today, we will do the same, replacing R and Shiny with Streamlit and for the Sentiment Analysis operation, we’re using ChatGPT.

That said, this application is going to look fairly similar:

Sentiment Analysis Dashboard using Python's Streamlit and ChatGPT

Creating a ChatGPT Account

First, we need to have a ChatGTP account and then create our API keys:

ChatGPT API Keys

Once the key has been created, store it safely, as you cannot recover it, so we can take advantage of our .env file and store it there.

Installing the required packages

As we want to create a Flask web application, our best option is to use Flask, one of the most popular Micro Frameworks in the Python world; also, we want to use Streamlit, the best option to create Dashboards in Python:

$ pip3 install Flask
$ pip3 install Flask-session
$ pip3 install streamlit

Also, we need to install the OpenAI Python package and other useful packages:

$ pip3 install openai
$ pip3 install pandas
$ pip3 install pillow

Once everything is installed, we’re ready to go.

Creating the project structure

We’re to create a folder called sentiment_analysis to hold our scripts. Inside, we will create a folder called templates to hold our HTML templates and another folder called images to hold our emojis images.

Our Flask script is going to be called FeedbackForm.py and our Sentiment Analysis Dashboard script is going to be called sentiment_analysis.py.

Creating the Flask web form

We’re going to create a file called FeedbackForm.py which will handle getting the feedback and sending the email:

# Import your dependencies
from dotenv import load_dotenv
from flask import Flask,render_template,json,request,flash,redirect,url_for
from nylas import APIClient

# Load your env variables
load_dotenv()

app = Flask(__name__)

# Load the Nylas APIs
def load_nylas():
    nylas = APIClient(
        os.environ.get("CLIENT_ID"),
        os.environ.get("CLIENT_SECRET"),
        os.environ.get("ACCESS_TOKEN")
    )
    return nylas

# Main page when launching the app	
@app.route("/", methods=['GET','POST'])
def index():
	# If we're reading, load FeebackForm.html
	if request.method == 'GET':
		return render_template('FeedbackForm.html')
	# Otherwise, register the information from the form	
	else:
        # Gather the information from the form
		name = request.form['name']
		email = request.form['email']
		rating = request.form['rating']
		comments = request.form['comments']
		
        # If a field is missing, display an error
		if not name:
			flash('Name is required!')
			return redirect(url_for('index'))
		elif not email:
			flash('Email is required!')
			return redirect(url_for('index'))
		elif not comments:
			flash('Comments are required!')
			return redirect(url_for('index'))			
		else:
            # No errors, we can continue
            # Load the Nylas APIs
			nylas = load_nylas()
            # Create an email draft
			draft = nylas.drafts.create()
            # Define the email subject
			draft.subject = "VeggiEggs Feedback - {} - {} - {}".format(name, email, rating)
            # Define the email body
			draft.body = comments
            # Who are we sending the email to?
			draft.to = [{"name": "Blag", "email": "[email protected]"}]
            # Send the email
			draft.send()
            # Display the confirmation foem	
			return render_template('ConfirmationForm.html', 
						name = name, email = email, 
						rating = rating, comments=comments),{"Refresh":"5;url=/"}
		
# Call the aplication and run it
if __name__ == "__main__":
	app.run()

For this to work properly, we need to create a file called FeedbackForm.html, and we’re going to use TailwindCSS to provide a quick look and feel:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com"></script>
    <title>Nylas</title>
</head>
<body>
	<div class="bg-green-300 border-green-600 border-b p-4 m-4 rounded">
	{% block content %}
	{% with messages = get_flashed_messages() %}
		{% if messages %}
			{% for message in messages %}
				{{ message }}
			{% endfor %}
		{% endif %}
	{% endwith %}
	<h1 class="font-black">We hope you enjoyed VeggiEggs. Please take some time to leave a review</h1>
	<br>
	<form method="post">
        <label for="name" class="font-bold">* Name</label>
        <input type="text" name="name"
               placeholder="Your name"
               value="{{ request.form['name'] }}"></input>
        <br><br>
        <label for="email" class="font-bold">* Email</label>
        <input type="email" name="email"
               placeholder="Your email"></input>
        <br><br>
		<label for="rating" class="font-bold">Choose a rating:</label>
		<select name="rating">
			<option value="1">Poor</option>
			<option value="2">Fair</option>
			<option value="3">Ok</option>
			<option value="4">Good</option>
			<option value="5" selected>Great</option>
		</select><br><br>
        <p class="font-bold">* Comments</p>
        <textarea name="comments"
               placeholder="Your comments"
               rows=5
               cols=50></textarea>        
        <br><br>
        <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full">Submit</button>
    </form>
    <p class="font-bold">* Required</p>
	{% endblock %}
	</div>
</body>
</html>

And we will also need a confirmation page so that people know that their responses were recorded correctly.

We will create a page called ConfirmationForm.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com"></script>
    <title>Nylas</title>
</head>
<body>
	<div class="bg-green-300 border-green-600 border-b p-4 m-4 rounded">
	{% block content %}
	<h2 class="font-bold">Thanks {{name}} - {{email}}</h2>
	<h2 class="font-bold">Your rating was: {{rating}}</h2>
	<h2 class="font-bold">Your comments were:<br><br> {{comments}}</h2>
	<br><br>
	<p class="font-semibold">You will be automatically redirected. 
	If it doesn't work, follow the <a href="{{url_for('index')}}">link</a></p>
	{% endblock %}
	</div>
</body>
</html>

We can execute this script from the terminal by typing:

$ python3 FeedbackForm.py
Running the Flask Server

NOTE: If you are using Flask on a Mac that has Monterey or higher, you may have trouble accessing localhost. You can solve this by disabling the Airplay Receiver in your Sharing folder. (Airplay Receiver uses port 5000.) Follow these instructions: How to disable Airplay Receiver.

We need to open localhost:5000 on our favourite browser:

Feedback form for Sentiment Analysis

Once we hit submit, we will get instant feedback:

Feedback confirmation

We use redirection so that it will go back to the main page after 5 seconds. And here, we’re displaying the information entered by the user.

We will find this if we check our mailbox:

Feedback response email

We have all the information we need in this email. So we’re ready for the next stage.

Creating the Sentiment Analysis Dashboard application

Now it’s time to create the sentiment_analysis.py Python script. This will take care of getting the emails, producing the sentiment analysis and creating the Streamlit dashboard and use ChatGPT for the Sentiment Analysis part:

# Import your dependencies
import os
from nylas import APIClient
import openai
import json
from dotenv import load_dotenv
import streamlit as st
import pandas as pd
import altair as alt
from PIL import Image

# Load your env variables
load_dotenv()

# Initialize your Nylas API client
nylas = APIClient(
    os.environ.get("CLIENT_ID"),
    os.environ.get("CLIENT_SECRET"),
    os.environ.get("ACCESS_TOKEN"),
)

# Define your OpenAI keys
openai.api_key = os.environ.get("OPEN_AI")


# Helper for ChatGPT
@st.cache_data
def get_completion(prompt):
    response = openai.Completion.create(
        model="text-davinci-003", prompt=prompt, max_tokens=100, temperature=0
    )
    return json.loads(response["choices"][0]["text"])


# Gather all messages from the label “VeggiEggs”
messages = nylas.messages.where(in_="VeggiEggs")

# Create variables to hold the information needed for the dashboard
emails = ""
sentiments = []
scores = []
ratings = []
dates = []

# A spinner to let users the app is doing something
with st.spinner("Crunching your emails and waiting for ChatGPT..."):
    # Read all messages
    for message in messages:
        # Split the title in order to get the rating value
        line = message.subject.split(" - ")
        ratings.append(line[3])
        dates.append(str(message.received_at.date()))

        # Read each email and get sentiment and score
        prompt = f"""
        You're going to receive an email, analyze it 
        and generate the sentiment analysis. 
        Give me the result as a json using only the sentiment and the score.
        The email will be delimited by triple backticks.
        ```{message.body}```.
        Sentiment:
        """
        # Record the reponses
        response_json = get_completion(prompt)
        sentiments.append(response_json["sentiment"].lower())
        scores.append(response_json["score"])

    # With all the sentiments and scores, give me the median values
    prompt = f"""
    I'm going to give you two arrays, one of sentiments and one of scores. 
    Give me the global sentiment and global score 
    as json using only the sentiment and the score. 
    Both arrays will be delimited by triple backticks.
    ```{sentiments}```.
    ```{scores}```. 
    result:
    """

    # Record the reponses
    response_json = get_completion(prompt)
    global_sentiment = response_json["global_sentiment"]
    global_score = response_json["global_score"]

    # Determine which image to use depending on the sentiment score
    if global_score < -0.5:
        image = Image.open("images/angry-emoji.png")
    if global_score > -0.5 and global_score < 0.5:
        image = Image.open("images/neutral-emoji.png")
    if global_score > 0.5:
        image = Image.open("images/happy-emoji.png")

# Create a Dictionary to handle all results
dt = {"Ratings": ratings, "Sentiments": sentiments, "Scores": scores, "Dates": dates}

# Add two emtpy spaces and then the title
st.write("")
st.write("")
st.title("VeggiEggs Dashboard")
# Create a Pandas DataFrame based on the dictionary
entries = pd.DataFrame(dt)

# Count up ratings, dates and sentiments
ratings = entries["Ratings"].value_counts().reset_index(name="count")
ratings.columns = ["Ratings", "Count"]

dates = entries["Dates"].value_counts().reset_index(name="count")
dates.columns = ["Dates", "Count"]

sentiments = entries["Sentiments"].value_counts().reset_index(name="count")
sentiments.columns = ["Sentiments", "Count"]

# Create columns layout
col1, col2 = st.columns(2)
with col1:
    bar_chart = (
        alt.Chart(ratings)
        .mark_bar()
        .encode(
            alt.X("Ratings", title="Ratings"),
            alt.Y("Count", title="Count"),
            color="Ratings:N",
        )
        .properties(title="Ratings Count")
    )
    # Display Time Series Chart
    st.altair_chart(bar_chart, use_container_width=True)
with col2:
    bar_chart = (
        alt.Chart(dates)
        .mark_line()
        .encode(alt.X("Dates", title="Dates"), alt.Y("Count", title="Entries"))
        .properties(title="Entries per Date")
    )
    # Display the Bar Chart
    st.altair_chart(bar_chart, use_container_width=True)

col1, col2 = st.columns(2)
with col1:
    bar_chart = (
        alt.Chart(sentiments)
        .mark_arc()
        .encode(theta="Count", color="Sentiments")
        .properties(title="Sentiments Cluster")
    )
    # Display the Pie Chart
    st.altair_chart(bar_chart, use_container_width=True)
with col2:
    col1, col2, col3 = st.columns([2, 4, 2])
    with col1:
        st.write("")
    with col2:
        st.write("")
        st.write("")
        st.write("Sentiment Icon")
        # Display the image
        st.image(image, caption=global_sentiment)
    with col3:
        st.write("")

Running our Sentiment Analysis Dashboard application

To call our Sentiment Analysis Dashboard, the only thing we need to do is run our Python script by typing this into the terminal window:

$ streamlit run sentiment_analysis.py

Our Streamlit Dashboard will be opened automatically on our default browser and will let us know that it’s working:

Streamlit dashboard crunching ChatGPT Sentiment Analysis data

We had just created a Sentiment Analysis Dashboard using Python, Streamlit and ChatGPT.

What’s next?

If you want to learn more about Emails, go to our documentation. You can sign up Nylas for free and start building!

Don’t miss the action, join our LiveStream Coding with Nylas:

Related resources

How to create an appointment scheduler in your React app

Learn how to create an appointment scheduler in your React app using Nylas Scheduler. Streamline bookings and UX with step-by-step guidance.

Beyond APIs: Designing elegant, smart Web Components

Dive into the world of smart Web Components for advanced API integration solutions. Design elegant, customizable elements to elevate user experiences.

How to integrate a React calendar component to streamline scheduling

Elevate your app’s scheduling with a React calendar component and seamlessly integrate with Nylas for efficient calendar management.