Lightning · Salesforce · Salesforce Einstein

Einstein Sentiment Analysis

Einstein Sentiment is something to predict the reviews or messages whether it is positive, negative or neutral. Using these companies can categorize the customer attitudes and take action appropriately to build their insights. As earlier discussed in marketing cloud (https://teamforcesite.wordpress.com/2018/02/07/marketing-cloud-social-studio-series-macros/) sentiment analysis can be achieved even using Einstein.

Here user enters some message, Einstein finds the sentiment that will be useful to companies finding it as an appreciation when it is positive and take action when it is negative. This scenario will be helpful when handling more number of clients. For example in real-time usage facebook comments, client reply, etc.

To start with Einstein, first set up your environment with the help of the trailhead (https://trailhead.salesforce.com/modules/einstein_intent_basics/units/einstein_intent_basics_env). For one account mail id, only one key is generated that is stored in files and we get access token generated with the time limit being activated. Create two apex class to generate a JWT access token, you can refer the trailhead (https://trailhead.salesforce.com/projects/predictive_vision_apex/steps/predictive_vision_apex_get_code).

//JWT.apex
public class JWT {

public String alg {get;set;}
public String iss {get;set;}
public String sub {get;set;}
public String aud {get;set;}
public String exp {get;set;}
public String iat {get;set;}
public Map claims {get;set;}
public Integer validFor {get;set;}
public String cert {get;set;}
public String pkcs8 {get;set;}
public String privateKey {get;set;}

public static final String HS256 = 'HS256';
public static final String RS256 = 'RS256';
public static final String NONE = 'none';
public JWT(String alg) {
this.alg = alg;
this.validFor = 300;
}

public String issue() {
String jwt = '';
JSONGenerator header = JSON.createGenerator(false);
header.writeStartObject();
header.writeStringField('alg', this.alg);
header.writeEndObject();
String encodedHeader = base64URLencode(Blob.valueOf(header.getAsString()));

JSONGenerator body = JSON.createGenerator(false);
body.writeStartObject();
body.writeStringField('iss', this.iss);
body.writeStringField('sub', this.sub);
body.writeStringField('aud', this.aud);
Long rightNow = (dateTime.now().getTime()/1000)+1;
body.writeNumberField('iat', rightNow);
body.writeNumberField('exp', (rightNow + validFor));
if (claims != null) {
for (String claim : claims.keySet()) {
body.writeStringField(claim, claims.get(claim));
}
}
body.writeEndObject();

jwt = encodedHeader + '.' + base64URLencode(Blob.valueOf(body.getAsString()));

if ( this.alg == HS256 ) {
Blob key = EncodingUtil.base64Decode(privateKey);
Blob signature = Crypto.generateMac('hmacSHA256',Blob.valueof(jwt),key);
jwt += '.' + base64URLencode(signature);
} else if ( this.alg == RS256 ) {
Blob signature = null;

if (cert != null ) {
signature = Crypto.signWithCertificate('rsa-sha256', Blob.valueOf(jwt), cert);
} else {
Blob privateKey = EncodingUtil.base64Decode(pkcs8);
signature = Crypto.sign('rsa-sha256', Blob.valueOf(jwt), privateKey);
}
jwt += '.' + base64URLencode(signature);
} else if ( this.alg == NONE ) {
jwt += '.';

return jwt;
}
public String base64URLencode(Blob input){
String output = encodingUtil.base64Encode(input);
output = output.replace('+', '-');
output = output.replace('/', '_');
while ( output.endsWith('=')){
output = output.subString(0,output.length()-1);
}
return output;
}
}

To generate new access token each and every time JWTBearerFlow class is used

//JWTBearerFlow.apex
public class JWTBearerFlow {

public static String getAccessToken(String tokenEndpoint, JWT jwt) {

String access_token = null;
String body = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + jwt.issue();
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint(tokenEndpoint);
req.setHeader('Content-type', 'application/x-www-form-urlencoded');
req.setBody(body);
Http http = new Http();
HTTPResponse res = http.send(req);

if ( res.getStatusCode() == 200 ) {
System.JSONParser parser = System.JSON.createParser(res.getBody());
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'access_token')) {
parser.nextToken();
access_token = parser.getText();
break;
}
}
}
return access_token;
}
} 

Now the sentiment is analyzed through the probability given by Einstein. In the below class we refer to JWT apex class to pass the key and generate a new access token.(https://metamind.readme.io/docs/what-you-need-to-call-api). The below apex class returns a string with labels and its probability.

//EinsteinVision_Sentiment.apex
@auraenabled
 public static String findSentiment(String text)
{
 ContentVersion con = [SELECT Title,VersionData
FROM ContentVersion
WHERE Title = 'einstein_platform'
OR Title = 'predictive_services'
ORDER BY Title LIMIT 1];

String key = con.VersionData.tostring();
key = key.replace( '-----BEGIN RSA PRIVATE KEY-----', '' );
key = key.replace( '-----END RSA PRIVATE KEY-----', '' );
key = key.replace( '\n', '' );
JWT jwt = new JWT( 'RS256' );
jwt.pkcs8 = key;
jwt.iss = 'developer.force.com';
jwt.sub = 'xxx@xxx.com'; // Update with your own email ID
jwt.aud = 'https://api.metamind.io/v1/oauth2/token';
jwt.exp = String.valueOf(3600);
String access_token = JWTBearerFlow.getAccessToken( 'https://api.metamind.io/v1/oauth2/token', jwt );
String keyaccess = access_token;

Http http = new Http();
HttpRequest req = new HttpRequest();
req.setMethod( 'POST' );
req.setEndpoint( 'https://api.einstein.ai/v2/language/sentiment');
req.setHeader( 'Authorization', 'Bearer ' + keyaccess );
req.setHeader( 'Content-type', 'application/json' );
String body = '{\"modelId\":\"CommunitySentiment\",\"document\":\"' + text + '\"}';
req.setBody( body );
HTTPResponse res = http.send( req );
string fullString=res.getBody();
string removeString=fullString.removeEnd('],"object":"predictresponse"}');
string stringBody=removeString.removeStart('{"probabilities":[');
return stringBody;
} 

Using the probability, I display them as charts using chart.js which can be referred to the given link (http://www.chartjs.org/docs/latest/getting-started/). The chart.js is saved as a static resource in Salesforce and used in the component.

lightng.png

The response from apex controller is handled in js and we split the data into two lists that are passed as values to the chart data.

({
 extractfile: function(component, event, helper) {
 var val = component.find("select").get("v.value");
 alert('value'+val);
 var action1 = component.get("c.findSentiment");
 action1.setParams({ text: val });

 action1.setCallback(this, function(response) {

 var ret=response.getReturnValue();
 component.set("v.probability",ret);
 alert('probability '+component.get("v.probability"));

 var line=component.get("v.probability");
 var list=line.split(',');
 var temp=0;
 var labels=[];
 var values=[];
 for(var i=0;i		<list.length;i++){
 if(i%2==0){
 list[i]=list[i].match(':"(.*)"')[1];
 temp=temp+1;
 labels.push(list[i]);
 }
 else
 {
 temp=temp+1;
 list[i]=list[i].match(':(.*)}')[1];
 values.push(list[i]);
 }
 }
 component.set("v.labels",labels);
 component.set("v.values",values);

 var label=component.get("v.labels");
 var value=component.get("v.values");
 var data = {
 labels: [label[0],label[1],label[2]],
 datasets: [
 {
 fillColor: '#b9f6ca',
 strokeColor: '#b9f6ca',
 data: [value[0],value[1],value[2]]
 }
 ]
 }
 var options = { responsive: true };
 var element = component.find('chart').getElement();

 var ctx = element.getContext('2d');

 var myBarChart = new Chart(ctx).Bar(data, options);
 });
 $A.enqueueAction(action1);
 },

})

The result of above data is shown below,

output for sentiment.png

To know more about Einstein keep visiting our blog. For any doubts, you can feel free to reach out us.

 

2 thoughts on “Einstein Sentiment Analysis

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s