package com.ibm.personafusion.service;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import com.ibm.json.java.JSONArray;
import com.ibm.json.java.JSONObject;
import com.ibm.personafusion.Config;
import com.ibm.personafusion.model.Person;
import com.ibm.personafusion.model.Trait;
/** A wrapper for accessing the Watson User Model API.
* Usage:
* WatsonUserModeller WUM = new WatsonUserModeller();
* List<Trait> traits = WUM.getTraitsList("all of the twitter text");
*
* String vizHTML = WUM.getPersonVizHTML(personObject);
* @author Sean Welleck **/
public class WatsonUserModeller
{
private String username;
private String password;
private String base_url;
private String profile_api;
private String visual_api;
private static JSONArray watson;
private static JSONObject watsonInstance;
private static JSONObject watsonCredentials;
private Executor executor;
public WatsonUserModeller()
{
try {
String VCAP_SERVICES = System.getenv("VCAP_SERVICES");
JSONObject vcap;
vcap = (JSONObject) JSONObject.parse(VCAP_SERVICES);
watson = (JSONArray) vcap.get("personality_insights");
watsonInstance = (JSONObject) watson.get(0);
watsonCredentials = (JSONObject) watsonInstance.get("credentials");
} catch (IOException e) {
e.printStackTrace();
}
this.username = (String) watsonCredentials.get("username");
this.password = (String) watsonCredentials.get("password");
this.base_url = (String) watsonCredentials.get("url");
this.profile_api = Config.WATSON_PROF_API;
this.visual_api = Config.WATSON_VIZ_API;
this.executor = Executor.newInstance().auth(username, password);
if (this.executor == null)
{
System.err.println("Authentication failed in WatsonUserModeller.");
}
}
/** Get the list of Traits for this text using the data
* returned by Watson. **/
public List<Trait> getTraitsList(String text)
{
List<Trait> traits = new ArrayList<Trait>();
String json = getProfileJSON(text);
List<Map<String,String>> lm = formatTraits(json);
for (Map<String, String> m : lm)
{
String id = m.get("id");
String val = m.get("value");
if (id != null)
{
double pct = 0.0;
/** NOTE: val == null means that this id is the name of a
* trait 'category'; e.g. values, personality.
* We do not add categories to the returned list of traits.
*/
if (val != null)
{
val = val.substring(0, val.length()-1);
pct = Double.parseDouble(val);
traits.add(new Trait(id, pct));
}
}
}
return traits;
}
/** Produce a visualization of a Person's traits based
* on the text of their tweets. Uses the Watson API. **/
public String getPersonVizHTML(Person p)
{
if (p == null) { return null; }
String profileJSON = this.getProfileJSON(combine(p.qaResponses));
String vizHTML = this.getVizHTML(profileJSON);
return vizHTML;
}
/** Get a personality profile from Watson based on the input text.
* Returns a string of JSON. **/
private String getProfileJSON(String text)
{
return makePOST(base_url, profile_api, buildContent(text).toString());
}
/** Get a visualization from Watson based on the input personality profile,
* e.g. from the output of getProfileJSON(). Returns a string of HTML. **/
private String getVizHTML(String profileJSON)
{
String vizHTML = null;
try
{
byte[] vizBytes = makePOSTByte(base_url, visual_api, profileJSON);
vizHTML = new String(vizBytes, "utf-8");
} catch (Exception e)
{
e.printStackTrace();
}
return vizHTML;
}
private static List<Map<String,String>> formatTraits(String profileJson){
List<Map<String,String>> arr = new ArrayList<Map<String,String>>();
try
{
JSONObject tree = JSONObject.parse(profileJson);
formatTree((JSONObject)tree.get("tree"), 0, arr);
} catch(Exception e) { e.printStackTrace(); }
return arr;
}
private static void formatTree(JSONObject node, int level, List<Map<String,String>> arr) {
if (node == null) return;
JSONArray children = (JSONArray)node.get("children");
if (level > 0 && (children == null || level != 2)) {
Map<String,String> obj = new HashMap<String,String>();
obj.put("id", (String)node.get("id"));
if (children != null) obj.put("title", "true");
if (node.containsKey("percentage")) {
double p = (Double)node.get("percentage");
p = Math.floor(p * 100.0);
obj.put("value", Double.toString(p) + "%");
}
arr.add(obj);
}
if (children != null && !"sbh".equals(node.get("id"))) {
for (int i = 0; i < children.size(); i++) {
formatTree((JSONObject)children.get(i), level + 1, arr);
}
}
}
private String makePOST(String base, String suffix, String content)
{
try
{
URI uri = new URI(base + suffix).normalize();
String profileJSON = executor.execute(Request.Post(uri)
.setHeader("Accept", "application/json")
.bodyString(content, ContentType.APPLICATION_JSON)
).returnContent().asString();
return profileJSON;
} catch (Exception e)
{
e.printStackTrace();
}
return "An error occured on POST to " + base + suffix;
}
private byte[] makePOSTByte(String base, String suffix, String content)
{
byte[] vizHTMLData = null;
try
{
URI uri = new URI(base + suffix).normalize();
vizHTMLData = executor.execute(Request.Post(uri)
.setHeader("Accept", "text/html")
.bodyString(content, ContentType.APPLICATION_JSON)
).returnContent().asBytes();
} catch (Exception e)
{
e.printStackTrace();
System.err.println("An error occured on POST to " + base + suffix);
}
return vizHTMLData;
}
private JSONObject buildContent(String text)
{
JSONObject contentItem = new JSONObject();
contentItem.put("userid", UUID.randomUUID().toString());
contentItem.put("id", UUID.randomUUID().toString());
contentItem.put("sourceid", "freetext");
contentItem.put("contenttype", "text/plain");
contentItem.put("language", "en");
contentItem.put("content", text);
JSONObject content = new JSONObject();
JSONArray contentItems = new JSONArray();
content.put("contentItems", contentItems);
contentItems.add(contentItem);
return content;
}
private static String combine(List<String> lst)
{
String out = "";
for (String s: lst)
{
out += s;
}
return out;
}
}