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.

  1. Go to the sign up page.
  2. Click Sign Up Using Salesforce.
  3. On the Salesforce login page, type your username and password, and click Log In.
  4. Click Allow so the page can access basic information, such as your email address, and perform requests.
  5. Download Key to save the key locally. The key file is einstein_platform.pem
  6. Upload the einstein_platform.pem key to Salesforce files.
  7. 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);
    
    
},
 })