- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
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.
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.
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:
First, we need to have a ChatGTP account and then create our 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.
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.
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.
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
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:
Once we hit submit, we will get instant feedback:
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:
We have all the information we need in this email. So we’re ready for the next stage.
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("")
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:
We had just created a Sentiment Analysis Dashboard using Python, Streamlit and ChatGPT.
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:
Blag aka Alvaro Tejada Galindo is a Senior Developer Advocate at Nylas. He loves learning about programming and sharing knowledge with the community. When he’s not coding, he’s spending time with his wife, daughter and son. He loves Punk Music and reading all sorts of books.