A few months ago, I was reading an email that had a snippy tone to it and several people didn’t take too kindly to it. Despite the tone, I don’t believe that the person who composed the email intended it to come off that way. It got me thinking that it would be cool to have an Outlook Add-In that would use the Sentiment Analysis api made available by the Azure Cognitive Services to score an email. I finally got around to putting this together this weekend and this is a quick overview of the setup.
Setting up Sentiment Analysis in Azure
Start by logging into your Azure portal and searching for Cognitive Services
On the cognitive services blade, click the Add button which will show the “AI + Machine Learning” blade where you can search for “Text Analytics”.
After you select Text Analytics, you can provide you service name, subscription, location, etc as shown below.
Your new service will have some basic information for getting started. The keys that your service will use, links to additional information and tutorials, etc.
There isn’t anything else to configure. The keys needed are in the link under Step 1. You can regenerate the keys if you need to but this is where you’ll get the keys that you need when you call the api.
Building the Outlook Add-In
For this demo, I’m using a circular progress bar sample by Anders Ingemann. I start by creating a Outlook Add-In project in Visual Studio 2017. I’ll show how I created an Add-In that is available in the composer and scores your email message.
Similar to SharePoint Add-Ins, the Outlook Add-In creates 2 projects; one that creates a manifest and the other is a web application.
If you run the project without making any changes, you’ll notice that the Add-In is available when reading an email, but I want it to be visible on compose. You configure this via the manifest file found in the first project. Look for the following lines (I apologize for the code images. Posting code was causing issues):
Replace the first line with:
Now, since this is just a demo that I was playing with, I didn’t bother to change the file names and I left the MessageRead.css, MessageRead.html, and MessageRead.js files as is. If this were an actual project, I would probably create new files for Compose. Open MessageRead.html and find the div with id “content-main.” This is where the main body of your Add-In is rendered from. Using the sample from the link above with some minor tweaks, I came up with the following:
The main change is the div with the class “numbers”, where I removed the list of spans (1 for each available percentage) and replaced it with a span where I will pass in the score provided by the sentiment analysis results. I created a new styles.less file and added the following contents, straight from the link above with one minor change. I removed the inset class which is no longer needed since I’m passing in the score.
<pre>/*@import url(http://fonts.googleapis.com/css?family=Lato:100,300,400,700,900,100italic,300italic,400italic,700italic,900italic);*/ .radial-progress { @circle-size: 120px; @circle-background: #d6dadc; @circle-color: #97a71d; @inset-size: 90px; @inset-color: #fbfbfb; @transition-length: 1s; @shadow: 6px 6px 10px rgba(0,0,0,0.2); @percentage-color: #97a71d; @percentage-font-size: 22px; @percentage-text-width: 57px; margin: 50px; width: @circle-size; height: @circle-size; background-color: @circle-background; border-radius: 50%; .circle { .mask, .fill, .shadow { width: @circle-size; height: @circle-size; position: absolute; border-radius: 50%; } .shadow { box-shadow: @shadow inset; } .mask, .fill { -webkit-backface-visibility: hidden; transition: -webkit-transform @transition-length; transition: -ms-transform @transition-length; transition: transform @transition-length; border-radius: 50%; } .mask { clip: rect(0px, @circle-size, @circle-size, @circle-size/2); .fill { clip: rect(0px, @circle-size/2, @circle-size, 0px); background-color: @circle-color; } } } .inset { width: @inset-size; height: @inset-size; position: absolute; margin-left: (@circle-size - @inset-size)/2; margin-top: (@circle-size - @inset-size)/2; background-color: @inset-color; border-radius: 50%; box-shadow: @shadow; .percentage { height: @percentage-font-size; width: @percentage-text-width; overflow: hidden; position: absolute; top: (@inset-size - @percentage-font-size) / 2; left: (@inset-size - @percentage-text-width) / 2; line-height: 1; .numbers { margin-top: -@percentage-font-size; transition: width @transition-length; span { width: @percentage-text-width; display: inline-block; vertical-align: top; text-align: center; font-weight: 800; font-size: @percentage-font-size; font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; color: @percentage-color; } } } } @i: 0; @increment: 180deg / 100; .loop (@i) when (@i <= 100) { &[data-progress="@{i}"] { .circle { .mask.full, .fill { -webkit-transform: rotate(@increment * @i); -ms-transform: rotate(@increment * @i); transform: rotate(@increment * @i); } .fill.fix { -webkit-transform: rotate(@increment * @i * 2); -ms-transform: rotate(@increment * @i * 2); transform: rotate(@increment * @i * 2); } } .inset .percentage .numbers { /*width: @i * @percentage-text-width + @percentage-text-width;*/ } } .loop(@i + 1); } .loop(@i); } </pre> <pre>
The javascript logic consists of just a few simple steps. First, we get the message and pass it to the calculateSentiment function. Then we construct the “document” that the sentiment analysis will score. For more info on this document schema, visit the how-to page. We then do an http post to your endpoint. The Ocp-Apim-Subscription-Key is the key that we get from Azure that we saw at the beginning of this post. Finally, we get the result if the post succeeds, and we pass it to the function that sets the chart value.
</pre> <pre>(function () { 'use strict'; var item; // The Office initialize function must be run each time a new page is loaded. Office.initialize = function (reason) { item = Office.context.mailbox.item; $(document).ready(function () { // get the body of the message and calculate the score item.body.getAsync('text', function (async) { calculateSentiment(async.value) }); }); }; function calculateSentiment(emailMessage) { // build the document var documentsVal = [{ "language": "en", "id": 1, "text": emailMessage }]; var documentsKey = { "documents": documentsVal }; $.ajax({ type: 'POST', url: "https://eastus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment", headers: { "Ocp-Apim-Subscription-Key": "", "Content-Type": "application/json", "Accept": "application/json" }, data: JSON.stringify(documentsKey, null, 2), dataType: "json", success: function (result) { var documents = result.documents; for (var i = 0; i < documents.length; i++) { var msg = documents[i]; var score = msg.score; setChart(score) } }, error: function (xhr, ajaxOptions, thrownError) { console.log("Error statue: " + xhr.status); console.log("Thrown error: " + thrownError); } }); } function setChart(pct) { window.randomize = function () { $('.radial-progress').attr('data-progress', Math.floor(pct * 100)); let displayVal = Math.round(pct * 100) + '%'; $('#app-pct').text(displayVal); } setTimeout(window.randomize, 200); $('.radial-progress').click(window.randomize); } })(); </pre> <pre>
The end result is as follows:
2 thoughts on “Creating an Outlook Add-In with Sentiment Analysis”
Comments are closed.