Salesforce Einstein Community Sentiment
Introduction
In this blog, I am going to explain how to analyze the text sentiment by using Salesforce Einstein Language pre-built sentiment model that allows you to classify the predict into positive, negative, neutral classes.Use the community sentiment model to classify text without building your own custom model. This model was created from data that comes from multiple sources. The data is short snippets of text, about one or two sentences, and similar to what you would find in a public community or Chatter group, a review/feedback forum, or enterprise social media. The final output looks below. You can enter the text for the prediction from the input box and you can see Polar area chart with predictions details.
Prerequisites
Salesforce Einstein Platform Services APIs user OAuth token for authenticating. To generate a token, you create a JWT payload, sign the payload with your private key, and then call the API to get the token. So you need to get your own einstein_platform private key. please follow this steps to get your einstein_platform key.
- Go to the sign up page.
- Click Sign Up Using Salesforce.
- On the Salesforce login page, type your username and password, and click Log In.
- Click Allow so the page can access basic information, such as your email address, and perform requests.
- Download Key to save the key locally. The key file is
einstein_platform.pem
- Upload the einstein_platform.pem key to Salesforce files.
- Copy JWTBearerFlow and JWT Apex Class from this git repository https://github.com/salesforceidentity/jwt
Refer this link for more information on the above steps.
Create custom metadata type or custom setting whatever works for you here to store the service endpoint, token endpoint and account details. Below is the custom metadata type which we are going to use it in this blog post.
Static Resource and Remote Site Setting.
Here I am using d3.js, so upload the d3.js &d3pie.js files to the static resource as shown below.
Add remote site setting as shown below.
Code
Apex class is here below
public class CaseSentimentAnalysis { @AuraEnabled public static List<Probabilities> getCaseAnalysis(String sentimentModel , String caseId){ ContentVersion base64Content = [SELECT Title, VersionData FROM ContentVersion WHERE Title = 'einstein_platform' LIMIT 1 ]; Einstein_Settings__mdt einsteinSettings = [Select DeveloperName, Label , Account_Email__c , Service_EndPoint__c , Token_EndPoint__c from Einstein_Settings__mdt where DeveloperName ='CommunitySentiment' Limit 1] ; DateTime tokenExpireTime = DateTime.now().addMinutes(30); String tokenExpireTimeinUnixFormate = ''+tokenExpireTime.getTime()/1000; String keyContents = base64Content.VersionData.tostring(); keyContents = keyContents.replace('-----BEGIN RSA PRIVATE KEY-----', ''); keyContents = keyContents.replace('-----END RSA PRIVATE KEY-----', ''); keyContents = keyContents.replace('\n', ''); JWT jwt = new JWT('RS256'); jwt.pkcs8 = keyContents; jwt.iss = 'developer.force.com'; jwt.sub = einsteinSettings.Account_Email__c ; jwt.aud = einsteinSettings.Token_EndPoint__c; jwt.exp = tokenExpireTimeinUnixFormate; String access_token = JWTBearerFlow.getAccessToken(einsteinSettings.Token_EndPoint__c, jwt); Case caseRec = [Select id , Description ,Subject from Case where id=:caseId] ; String textToAnylize = caseRec.Subject+'.'+ caseRec.Description ; Http http = new Http(); HttpRequest req = new HttpRequest(); req.setMethod('POST'); req.setEndpoint(einsteinSettings.Service_EndPoint__c); req.setHeader('Authorization', 'Bearer ' + access_token); req.setHeader('Content-type', 'application/json'); String body = '{\"modelId\":\"'+ sentimentModel + '\",\"document\":\"' + textToAnylize + '\"}'; req.setBody(body); HTTPResponse res = http.send(req); System.debug('res'+res.getBody()); JSONParser parser = JSON.createParser(res.getBody()) ; String label =''; Decimal probability = 0 ; List<Probabilities> probabilities = new List<Probabilities>() ; while (parser.nextToken() != JSONToken.END_OBJECT) { if (parser.getCurrentToken() == JSONToken.FIELD_NAME) { String text = parser.getText(); if (parser.nextToken() != JSONToken.VALUE_NULL) { if (text == 'probabilities') { probabilities = new List<Probabilities>(); while (parser.nextToken() != JSONToken.END_ARRAY) { probabilities.add(new Probabilities(parser)); } } } } } return probabilities ; } public class Probabilities { @AuraEnabled public String label { get; set; } @AuraEnabled public Double probability { get; set; } public Probabilities(JSONParser parser) { while (parser.nextToken() != JSONToken.END_OBJECT) { if (parser.getCurrentToken() == JSONToken.FIELD_NAME) { String text = parser.getText(); if (parser.nextToken() != JSONToken.VALUE_NULL) { if (text == 'label') { label = parser.getText(); } else if (text == 'probability') { probability = parser.getDoubleValue(); } } } } } } }
Lightning Component.
<aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="CaseSentimentAnalysis"> <aura:attribute name="modelName" type="String" default="CommunitySentiment"/> <aura:attribute name="caseId" type="String" default="5001N00000Vu19M"/> <ltng:require scripts="{!join(',', $Resource.d3pie + '/d3pie/d3.min.js', $Resource.d3pie + '/d3pie/d3pie.min.js')}" afterScriptsLoaded="{!c.doInit}"/> <div id="pieChart"></div> </aura:component>
Controller.js
({ doInit : function(component, event, helper) { helper.load(component); } })
helper.js
({ load: function(component) { var action = component.get("c.getCaseAnalysis"); action.setParams({ "sentimentModel": component.get("v.modelName") , "caseId": component.get("v.caseId") }); var arr = []; action.setCallback(this, function(response) { var state = response.getState(); console.log('state'+state); if (state === "SUCCESS"){ var predectionResult = response.getReturnValue(); console.log(predectionResult); predectionResult.forEach((key, value) => { arr.push({ "label": predectionResult[value].label, "value": predectionResult[value].probability }); }); var pie = new d3pie("pieChart", { "header": { "title": { "text": "Case Sentiment Analysis", "color": "#2a4b95", "fontSize": 24, "font": "verdana" }, "location": "pie-center", "titleSubtitlePadding": 11 }, "size": { "canvasWidth": 500, "pieInnerRadius": "78%", "pieOuterRadius": "94%" }, "data": { "sortOrder": "value-desc", "content": arr },"labels": { "outer": { "pieDistance": 40 }, "inner": { "hideWhenLessThanPercentage": 3 }, "mainLabel": { "fontSize": 11 }, "percentage": { "color": "#ffffff", "decimalPlaces": 0 }, "value": { "color": "#adadad", "fontSize": 11 }, "lines": { "enabled": true }, "truncation": { "enabled": true } }, "effects": { "pullOutSegmentOnClick": { "effect": "linear", "speed": 400, "size": 8 } }, "misc": { "gradient": { "enabled": true, "percentage": 100 } } }); } else if(state == "ERROR"){ var errors = response.getError(); console.log(errors); } }); $A.enqueueAction(action); }, })