Adding New Arrivals in the Metrics of SUSI.AI

The SUSI Skill CMS homepage contains a lot of metics on the homepage. For example, the highest rated skills, latest skills, most used skills etc. Another important metric is the newly arrived skills in a given period of time, say in last week. This keeps the users updated with the system and allows them to know what work is going on the assistant. This also inspires the skill creators to create more skills.

Update skill listing API

To get the list of recently added skill, first of all, we need a mechanism to sort them in descending order of their creation time. Update the skill listing API ie, ListSkillService.java to sort skills by their creation time. The creation time is stored in “YYYY-MM-DD T M S” format, for ex “2018-08-12T03:11:32Z”. So it can be sorted easily using string comparison function.

Collections.sort(jsonValues, new Comparator<JSONObject>() {
    private static final String KEY_NAME = "creationTime";
    @Override
    public int compare(JSONObject a, JSONObject b) {
        String valA = new String();
        String valB = new String();
        int result = 0;
         try {
            valA = a.get(KEY_NAME).toString();
            valB = b.get(KEY_NAME).toString();
            result = valB.compareToIgnoreCase(valA);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return result;
    }
});

After sorting the skills in descending order of their creation date. We need to filter them based on the time period. For example, if the skills created in last days are required then we need a generalized filter for that. This can be achieved by creating a variable for the starting date of the required time period. Say, if the skill created in last 7 days are required, then the number of milliseconds equivalent to 7 days is subtracted from the current timestamp. All the skills created after this timestamp are added to the result while others are skipped.

if (dateFilter) {
	long durationInMillisec = TimeUnit.DAYS.toMillis(duration);
	long timestamp = System.currentTimeMillis() - durationInMillisec;
	String startDate = new Timestamp(timestamp).toString().substring(0, 10); //substring is used for getting timestamp upto date only
	String skillCreationDate = jsonValues.get(i).get("creationTime").toString().substring(0,10);
	if (skillCreationDate.compareToIgnoreCase(startDate) < 0)
	{
	 continue;
	}
}

This filtering works in the API only when the filter type is set to date and duration in days is passed in the endpoint.

Implement new arrivals on CMS

Create the MenuItems in the sidebar that shows the filter name and add onClick handler on them. The skill listing API with the duration filter is passed to the handler. 3 MenuItems are added:

  • Last 7 Days
  • Last 30 Days
  • Last 90 Days

<MenuItem  value="&applyFilter=true&filter_name=descending&filter_type=date&duration=7"  key="Last 7 Days"
  primaryText="Last 7 Days"
  onClick={event =>
    this.handleArrivalTimeChange(
      event, '&applyFilter=true&filter_name=descending&filter_type=date&duration=7',
    )
  }
/>

Create a handler that listens to the onClick event of the above MenuItems. This handler accepts the API endpoint and calls the loadCards function with it.

handleArrivalTimeChange = (event, value) => {
 this.setState({ filter: value }, function() {
   // console.log(this.state);
   this.loadCards();
 });
};

Resources

Continue ReadingAdding New Arrivals in the Metrics of SUSI.AI

Feature to Report a Skill as Inappropriate

There are hundreds of skills on SUSI Skill CMS. News skills are created daily. Often some skills are made only for testing purpose. Also, some skills are published even though they are not completely developed. Further users may also create some skills that are not suitable for all age groups. To avoid this a skill reporting feature has been added on the CMS.

Server side implementation

Create a JSONTray object in DAO.java that stores the reported skill data. These reports are stored in reportedSkill.json.

Then create an API to report a skill as inappropriate. It runs at /cms/reportSkill.json endpoint and accepts the following parameters :

  • Model
  • Group
  • Language
  • Skill name
  • Feedback

A user should be logged in to report a skill as inappropriate, so the minimum user role is set to user.

public ServiceResponse serviceImpl(Query call, HttpServletResponse response, Authorization authorization, final JsonObjectWithDefault permissions) throws APIException {
	String model_name = call.get("model", "general");
	File model = new File(DAO.model_watch_dir, model_name);
	String group_name = call.get("group", "Knowledge");
	File group = new File(model, group_name);
	String language_name = call.get("language", "en");
	File language = new File(group, language_name);
	String skill_name = call.get("skill", null);
	File skill = SusiSkill.getSkillFileInLanguage(language, skill_name, false);
	String skill_feedback = call.get("feedback", null);
}

Next search for the reported skill in reportedSkill.json through DAO object. If it is found then add a new report object to it else create a new skill object containing the report and store it in the reportedSkill.json.

JSONObject reportObject = new JSONObject();
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
if (authorization.getIdentity().isEmail()) reportObject.put("email", idvalue);
if (authorization.getIdentity().isUuid()) reportObject.put("uuid", idvalue);
reportObject.put("feedback", skill_feedback);
reportObject.put("timestamp", timestamp.toString());
reports.put(reportObject);
skillName.put("reports", reports);

Also, increment the counter of the total number of reports on the skill. This helps in getting better an overview of the skill and in future may also help in taking automatic actions on the reported skills.

Finally, add the API to SusiServer.java

Resources

 

Continue ReadingFeature to Report a Skill as Inappropriate

Server Side Implement of Usage Analytics of a Skill in SUSI.AI

Which skills are being used most frequently in SUSI.AI? And which are rarely used? Such statistics are needed to give proper responses from the most used skills.

So when the user sends a query to SUSI, it searches for the best-suited skill to respond to that query. The usage count of that skill is incremented accordingly with the date.

Storage of Skill Usage Data

  1. Create a skillUsage.json file to store the stats and make a JSONTray object for that in DAO.java file.
  2. Modify the SusiArguement.java file to check which skill is being currently used for the response and write the skill usage stats to skillUsage.json.The function updateUsageData takes the skillPath in parameter and splits it from slash character. Thus the model name, group name, language name and skill name can be obtained. Then it searches for the date in the JSON file. If it exists then its count value is increased by 1, else a new object is inserted in the JSON file that contains the date and the corresponding usage count.

public void updateUsageData(String skillPath) {
    Boolean dateExists = false;
    for (int i = 0; i<usageData.length(); i++) {
        JSONObject dayUsage = usageData.getJSONObject(i);
        if (dayUsage.get("date").equals(today)){
            dayUsage.put("count", dayUsage.getInt("count")+1+"");
            usageData.put(i,dayUsage);
            dateExists = true;
            break;
        }
    }
}   

API to access the Skill Usage Data

  1. Create GetSkillUsageService.java file to return the usage stats stored in skillUsage.json. It runs at the endponit – /cms/getSkillUage.json and mining role required to access this API is anonymous. It simply reads the data stored in skillUsage.json file and returns it.
JsonTray skillRating = DAO.skillUsage;
JSONObject  languageName = groupName.getJSONObject(language_name);
if (languageName.has(skill_name)) {
    JSONArray skillUsage = languageName.getJSONArray(skill_name);
    result.put("skill_name", skill_name);
    result.put("skill_usage", skillUsage);
    result.put("accepted", true);
    result.put("message", "Skill usage fetched");
    return new ServiceResponse(result);
}
  1. Add the API file to skillUsage.json
services = new Class[]{
	...
	//Skill usage data
	GetSkillUsageService.class
	...
}

So now we know which skills are being used at what rate and make the required changes like server scaling, adding more accuracy to the feature etc. We can expand these stats to country wise or device wise usage distribution.

Resources

 

Continue ReadingServer Side Implement of Usage Analytics of a Skill in SUSI.AI

Add More Languages to a Skill in SUSI.AI

The SUSI SKill CMS provides skills in multiple languages. Often there are similar skills in different languages. For example, there is a News skill in English and Samachar skill in Hindi. Then why not link them together and mark one as the translation of the other. This will help the user to reach and explore the desired skill in an efficient way. Moreover, it may be easier to type ‘News’ than ‘समाचार’ and find the required skill through translations. So here it has been explained how to link two SUSI skills as translations.

Server side implementation

Create a skillSupportedLanguages.json file to store the related skills together as translations and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the language name and the skill name in that language, wrapped in an array.

public static JsonTray skillSupportedLanguages;

Path skillSupportedLanguages_per = skill_status_dir.resolve("skillSupportedLanguages.json");
Path skillSupportedLanguages_vol = skill_status_dir.resolve("skillSupportedLanguages_session.json");
skillSupportedLanguages = new JsonTray(skillSupportedLanguages_per.toFile(), skillSupportedLanguages_vol.toFile(), 1000000);
OS.protectPath(skillSupportedLanguages_per);
OS.protectPath(skillSupportedLanguages_vol);

Now create an API that accepts the skill details and translation details and stores them in the JSON file. Create UpdateSupportedLanguages.java class for the API.

Endpoint: /cms/updateSupportedLanguages.json

Minimum user role: Anonymous

Params:

  • Model
  • Group
  • Language (language of the skill for which translation is to be added)
  • Skill (name of the skill for which translation is to be added)
  • New language (translation language of the skill)
  • New skill name (name of the skill in translated language)

When a new translation is added check if it already exists in the translation group stored in the skillSupportedLanguages.json. Use the DAO object and loop over the array, check is the language name and the language name already exists. If yes then simply return.

if (!alreadyExixts) {
    groupName.put(createSupportedLanguagesArray(language_name, skill_name, new_language_name, new_skill_name));
}

Otherwise, create a new object containing the new language name and the skill name in that language and add it to the translation group.

public JSONArray createSupportedLanguagesArray(String language_name, String skill_name, String new_language_name, String new_skill_name) {
    JSONArray supportedLanguages =  new JSONArray();

    JSONObject languageObject = new JSONObject();
    languageObject.put("language", language_name);
    languageObject.put("name", skill_name);
    supportedLanguages.put(languageObject);

    JSONObject newLanguageObject = new JSONObject();
    newLanguageObject.put("language", new_language_name);
    newLanguageObject.put("name", new_skill_name);
    supportedLanguages.put(newLanguageObject);

    return supportedLanguages;
}

Add this API to SusiServer.java

// Add translation to the skill
UpdateSupportedLanguages.class

Resources

 

Continue ReadingAdd More Languages to a Skill in SUSI.AI

Device wise Usage Statistics of a Skill in SUSI.AI

The device wise usage distribution in SUSI.AI helps in understanding what kind of skills are used more on which type of devices, so that the skill creator can harness the core features of that device to enhance the skills or make the user experience smoother. For example, music playing skill may be used mostly on Smart Speakers whereas Android devices may have higher usage of alarm setting skill.

Sending the device type (ex, web client)

  1. Send the device type parameter as “Web Client” along with the query while fetching reply from SUSI server in chat.json API. The parameter is device_type.

// Add the type of device in the query
{
url += '&device_type=Web Client';
}

Storage of Device Wise Skill Usage Data on SUSI Server

  1. Create a deviceWiseSkillUsage.json file to store the device wise skill usage stats and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the device type and the usage count on that type of device (like Android, iOS, Web Client, Smart Speaker and others).
  2. Modify the src/ai/susi/server/api/susi/SusiService.java file to fetch device_type from the query parameters and pass them SusiCognition constructor.

public ServiceResponse serviceImpl(Query post, HttpServletResponse response, Authorization user, final JsonObjectWithDefault permissions) throws APIException {
	...
	String deviceType = post.get("device_type", "Others");
	...
	SusiCognition cognition = new SusiCognition(q, timezoneOffset, latitude, longitude, countryCode, countryName, language, deviceType, count, user.getIdentity(), minds.toArray(new SusiMind[minds.size()]));
	...
}
  1. Modify the src/ai/susi/mind/SusiCognition.java file to accept the deviceType in the constructor parameters. Check which skill is being currently used for the response and update the skill’s usage stats for the current device in deviceWiseSkillUsage.json. Call the function updateDeviceWiseUsageData() to update the skill usage data.

List<String> skills = dispute.get(0).getSkills();
for (String skill : skills) {
    updateDeviceWiseUsageData(skill, deviceType);
}

The updateDeviceWiseUsageData() function accepts the skill path and type of device. It parses the skill path to get the skill metadata like its model name, group name, language etc. The function then checks if the device already exists in the JSON file or not. If it exists then it increments the usage count by 1 else it creates an entry for the device in the JSON file and initializes it with the usage count 1.

for (int i = 0; i < deviceWiseUsageData.length(); i++) {
  deviceUsage = deviceWiseUsageData.getJSONObject(i);
  if (deviceUsage.get("device_type").equals(deviceType)) {
    deviceUsage.put("count", deviceUsage.getInt("count") + 1);
    deviceWiseUsageData.put(i,deviceUsage);
  }
}

API to access the Device Wise Skill Usage Data

  1. Create GetDeviceWiseSkillUsageService.java file to return the usage stats stored in deviceWiseSkillUsage.json

public ServiceResponse serviceImpl(Query call, HttpServletResponse response, Authorization rights, final JsonObjectWithDefault permissions) {        
  ...  // Fetch the query parameters
  JSONArray deviceWiseSkillUsage = languageName.getJSONArray(skill_name);
  result.put("skill_name", skill_name);
  result.put("skill_usage", deviceWiseSkillUsage);
  result.put("accepted", true);
  result.put("message", "Device wise skill usage fetched"); 
  return new ServiceResponse(result);    
}
  1. Add the API file to src/ai/susi/server/api/susi/SusiServer.java

services = new Class[]{
	...

	//Skill usage data
	GetDeviceWiseSkillUsageService.class
	
	...
}

 

Endpoint : /cms/getDeviceWiseSkillUsage.json

Parameters

  • model
  • group
  • language
  • Skill

Sample query: /cms/getDeviceWiseSkillUsage.json?model=general&group=Knowledge&language=en&skill=aboutsusi

Sample response:

{  
   "skill_usage":[  
    {
      "device_type": "Web Client",
      "count": 1
    },
    {
        "device_type": "Android",
        "count": 4
    },
    {
        "device_type": "iOS",
        "count": 2
    },
    {
        "device_type": "Smart Speaker",
        "count": 1
    },
    {
        "device_type": "Others",
        "count": 2
    }
   ],
   "session":{  
      "identity":{  
         "type":"host",
         "name":"162.158.154.147_81c88a10",
         "anonymous":true
      }
   },
   "skill_name":"ceo",
   "accepted":true,
   "message":"Device wise skill usage fetched"
}

Resources

Continue ReadingDevice wise Usage Statistics of a Skill in SUSI.AI

Countrywise Usage Analytics of a Skill in SUSI.AI

The statistics regarding which country the skills are being used is quite important. They help in updating the skill to support the native language of those countries. SUSI.AI must be able to understand as well as reply in its user’s language. So mainly the server side and some client side (web client) implementation of country wise skill usage statistics is explained in this blog.

Fetching the user’s location on the web client

  1. Add a function in chat.susi.ai/src/actions/API.actions.js to fetch the users location. The function makes a call to freegeoip.net API which returns the client’s location based on its IP address. So country name and code are required for country wise usage analytics.

export function getLocation(){
  $.ajax({
    url: 'https://cors-anywhere.herokuapp.com/http://freegeoip.net/json/',
    success: function (response) {
      _Location = {
        lat: response.latitude,
        lng: response.longitude,
        countryCode: response.country_code,
        countryName: response.country_name
      };
    },
  });
}
  1. Send the location parameters along with the query while fetching reply from SUSI server in chat.json API. The parameters are country_name and country_code.

if(_Location){
	url += '&latitude='+_Location.lat+'&longitude='+_Location.lng+'&country_code='+_Location.countryCode+'&country_name='+_Location.countryName;
}

Storage of Country Wise Skill Usage Data on SUSI Server

  1. Create a countryWiseSkillUsage.json file to store the country wise skill usage stats and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the country name, country code and the usage count in that country.
  1. Modify the src/ai/susi/server/api/susi/SusiService.java file to fetch country_name and country_code from the query parameters and pass them SusiCognition constructor.

String countryName = post.get("country_name", "");
String countryCode = post.get("country_code", "");
...

SusiCognition cognition = new SusiCognition(q, timezoneOffset, latitude, longitude, countryCode, countryName, language, count, user.getIdentity(), minds.toArray(new SusiMind[minds.size()]));
  1. Modify the src/ai/susi/mind/SusiCognition.java file to accept the countryCode and countryName in the constructor parameters. Check which skill is being currently used for the response and update the skill usage stats for that country in countryWiseSkillUsage.json. Call the function updateCountryWiseUsageData() to update the skill usage data.

if (!countryCode.equals("") && !countryName.equals("")) {
    List<String> skills = dispute.get(0).getSkills();
    for (String skill : skills) {
        try {
            updateCountryWiseUsageData(skill, countryCode, countryName);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The updateCountryWiseUsageData() function accepts the skill path , country name and country code. It parses the skill path to get the skill metadata like its model name, group name, language etc. The function then checks if the country already exists in the JSON file or not. If it exists then it increments the usage count by 1 else it creates an entry for the skill in the JSON file and initializes it with the current country name and usage count 1.

for (int i = 0; i < countryWiseUsageData.length(); i++) {
  countryUsage = countryWiseUsageData.getJSONObject(i);
  if (countryUsage.get("country_code").equals(countryCode)) {
    countryUsage.put("count", countryUsage.getInt("count")+1);
    countryWiseUsageData.put(i,countryUsage);
  }
}

API to access the Country Wise Skill Usage Data

  1. Create GetCountryWiseSkillUsageService.java file to return the usage stats stored in countryWiseSkillUsage.json

public ServiceResponse serviceImpl(Query call, HttpServletResponse response, Authorization rights, final JsonObjectWithDefault permissions) {
        
  ...  // Fetch the query parameters
  JSONArray countryWiseSkillUsage = languageName.getJSONArray(skill_name);
  return new ServiceResponse(result);
}
  1. Add the API file to src/ai/susi/server/api/susi/SusiServer.java

services = new Class[]{
	...
	//Skill usage data
	GetCountryWiseSkillUsageService.class
	...
}

 

Endpoint : /cms/getCountryWiseSkillUsage.json

Parameters

  • model
  • group
  • language
  • Skill

Sample query: /cms/getCountryWiseSkillUsage.json?model=general&group=Knowledge&language=en&skill=aboutsusi

Sample response:

{  
   "skill_usage":[  
      {  
         "country_code":"MYS",
         "country_name":"Malaysia",
         "count":1
      },
      {  
         "country_code":"MYS",
         "country_name":"Malaysia",
         "count":1
      }
   ],
   "session":{  
      "identity":{  
         "type":"host",
         "name":"162.158.154.147_81c88a10",
         "anonymous":true
      }
   },
   "skill_name":"ceo",
   "accepted":true,
   "message":"Country wise skill usage fetched"
}

Resources

Continue ReadingCountrywise Usage Analytics of a Skill in SUSI.AI

Multiple Languages Filter in SUSI.AI Skills CMS and Server

There are numerous users of SUSI.AI globally. Most of the users use skills in English languages while some prefer their native languages. Also,there are some users who want SUSI skills of multiple languages. So the process of fetching skills from multiple languages has been explained in this blog.

Server side implementation

The language parameter in ListSkillService.java is modified to accept a string that contains the required languages separated by a comma. Then this parameter is split by comma symbol which returns an array of the required languages.

String language_list = call.get("language", "en");
String[] language_names = language_list.split(",");

Then simple loop over this array language by language and keep adding the the skills’ metadata, in that language into the response object.

for (String language_name : language_names) {
	// fetch the skills in this language.
}

CMS side implementation

Convert the state variable languageValue, in BrowseSkill.js, from strings to an array so that multiple languages can be kept in it.

languageValue: ['en']

Change the language dropdown menu to allow selection of multiple values and attach an onChange listener to it. Its value is the same as that of state variable languageValue and its content is filled by calling a function languageMenuItems().

<SelectField
    multiple={true}
    hintText="Languages"
    value={languageValue}
    onChange={this.handleLanguageChange}
  >
    {this.languageMenuItems(languageValue)}
</SelectField>

The languageMenuItems() function gets the list of checked languages as a parameter. The whole list of languages are stored in a global variable called languages. So this function loops over the list of all the languages and check / uncheck them based on the values passed in the argument. It build a menu item for each language and put the ISO6391 native name of that language into the menu item.

languageMenuItems(values) {
  return languages.map(name => (
    <MenuItem
      insetChildren={true}
      checked={values && values.indexOf(name) > -1}
      value={name}
      primaryText={
        ISO6391.getNativeName(name)
          ? ISO6391.getNativeName(name)
          : 'Universal'
      }
    />
  ));
}

While the language change handler gets the values of the selected languages in the form of an array, from the drop down menu. It simply assigns this value to the state variable languageValue and calls the loadCards() function to load the skills based on the new filter.

this.setState({ languageValue: values }, function() {
    this.loadCards();
  });

 References

Continue ReadingMultiple Languages Filter in SUSI.AI Skills CMS and Server

Showing top metrics from skill groups

SUSI.AI shows top metrics on the home page. They include highest rated skills, most used skills, latest skills and skills with most feedbacks etc. Now the idea is to include top skills from a particular category also. For example “SUSI, what are your top games”? So how to fetch the required metrics in a generalized way.

Updating the skill metrics data API

Add an API parameter in SkillMetricsDataService.java to specify the names of groups to fetch the required metrics. It accepts a semicolon (;) separated list of group names. If no group is passed then by default it shows the top games.

String metrics_list = call.get("metrics", "Games, Trivia and Accessories");
String[] metrics_names = metrics_list.split(";");

Split the metrics parameter by semicolon and store in an array. This array contains all the groups of which top skills are to be displayed on the CMS home page. Loop over the array, group by group and filter out the skills that don’t belong to the current metrics group. Sort the filtered skills in decreasing order of the overall rating. But this sorting does not simply arrange the skills in decreasing order of their overall rating. Actually, it divides the skills into 2 halves. The first half contains the skills that have been rated by at least 10 users in decreasing order of the overall ratings. And the second half contains the rest of the skills in decreasing order of their overall rating.

for (String metric_name : metrics_names) {
    try {
        metric_name = metric_name.trim();
        List<JSONObject> groupJsonValues = new ArrayList<JSONObject>();
        for (int i = 0; i < jsonArray.length(); i++) {
            if (jsonArray.getJSONObject(i).get("group").toString().equals(metric_name)) {
                groupJsonValues.add(jsonArray.getJSONObject(i));
            }
        }
        // Get skills based on ratings of a particular group
        SusiSkill.sortByAvgStar(groupJsonValues, false);

        JSONArray topGroup = new JSONArray();
        topGroup = getSlicedArray(groupJsonValues, count);
        skillMetrics.put(metric_name, topGroup);
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

So if top games and news skills are to be shown on the CMS then the API endpoint looks like :

https://api.susi.ai/cms/getSkillMetricsData.json?metrics=Games, Trivia and Accessories; News

And the response is like :

{
	accepted: true,
	model: "general",
	group: "All",
	language: "en",
	metrics: {
		+newest: [...],
		+latest: [...],
		+rating: [...],
		+usage: [...],
		+feedback: [...],
		+staffPicks: [...],
		+Games, Trivia and Accessories: [...],
		+News: [...]
	},
	message: "Success: Fetched skill data based on metrics"
}

Resources

  • NA
Continue ReadingShowing top metrics from skill groups

5 Star Skill Rating System in the SUSI.AI CMS

For making a system more reliable and robust, continuous evaluation is quite important. So is in the case of SUSI AI. User feedback is important to improve SUSI skills and create new ones. Previously we had only thumbs up / thumbs down as a feedback method, from the SUSI chat client. But now a 5 star rating system has been added to the SUSI Skill CMS so that users can rate a skill there. Before the implementation of API  let’s look how data is stored in SUSI AI Susi_server uses DAO in which skill rating is stored as JSONTray. The rating system has been implemented on SkillListing.js file on the CMS.

The CMS side

  1. Install the Recharts Data Visualization library.

$ npm install --save recharts
  1. Install the Rating library for react.

$ npm install --save react-ratings-declarative
  1. Import the Barchart, Cell, LabelList, Bar, XAxis, YAxis and Tooltip components from Recharts.

import {BarChart, Cell, LabelList, Bar, XAxis, YAxis, Tooltip} from 'recharts';
  1. Import the Ratings components from React Ratings Declarative.

import Ratings from 'react-ratings-declarative';
  1. Add average rating, total ratings, rating counts on each star and rating given by the users as state variables.

this.state = {
    ...
    avg_rating: '',
    total_star: '',
    skill_ratings: [],
    rating : 0
    ...
}
  1. Load the skill ratings as soon as the page loads. getSkillRating.json API is used to get skill ratings. Fetch the stars related data from the API response and save them in the state variable using saveSkillRatings() function.

$.ajax({
    url: skillRatingUrl,
    success: function (data) {
      self.saveSkillRatings(data.skill_rating.stars)
    },
);
  1.  Store the skills rating data to be visualized on the charts. The rating data is kept in an array and store in a state variable. Put zero if the rating count is not available. This state variable is used by the Recharts library as an input for data visualization.

saveSkillRatings = (skill_ratings) => {
       const ratings_data = [
             {name: '5 ⭐', value: skill_ratings.five_star || 0},
             {name: '4 ⭐', value: skill_ratings.four_star || 0},
             {name: '3 ⭐', value: skill_ratings.three_star || 0},
             {name: '2 ⭐', value: skill_ratings.two_star || 0},
             {name: '1 ⭐', value: skill_ratings.one_star || 0}];
       this.setState({
           skill_ratings: ratings_data,
           avg_rating: skill_ratings.avg_star,
           total_star: skill_ratings.total_star
       })
   }
  1. Create a function to that informs the server about the rating given by the current user. The changeRating() function calls the fiveStarRateSkill.json API with the parameters like who has rated the skill and what rating has been given.

changeRating = (newRating) => {
    $.ajax({
        url: changeRatingUrl,
        success: function(data) {
            console.log('Ratings accepted');
        },
    this.setState({
        rating: newRating
    });
};
  1.  Check if the user is logged in and display the rating bar. The rating bar should appear only if the user is logged in. The document’s cookies hold the information about the logged in user.

{
  cookies.get('loggedIn') ?
  <Ratings
     rating={this.state.rating}
     changeRating={this.changeRating}
    >
     <Ratings.Widget />
     <Ratings.Widget />
     <Ratings.Widget />
     <Ratings.Widget />
     <Ratings.Widget />
  </Ratings>
  :
  null
}
  1.  Display the BarChart of existing ratings, total ratings and average rating. It shows the count on each star.

If the average rating is available in the state then show it, otherwise put zero in the average rating.

Add a Bar Chart and define the data to be visualized i.e. the skill rating data. The Y-axis of the chart maps to the particular rating and the X-axis shows the count of that rating. The value of bar and label is set from the count of each star rating.

<BarChart layout='vertical' width={400} height={250} data={this.state.skill_ratings}>
    <XAxis type="number" padding={{right: 20}} />
    <YAxis dataKey="name" type="category"/>
    <Bar name="Skill Rating" dataKey="value" fill="#8884d8">
        <LabelList dataKey="value" position="right" />
        {
            this.state.skill_ratings
                .map((entry, index) =>
                    <Cell key={index} fill={
                        ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#FF2323'][index % 5]
                    }/>)
        }
    </Bar>
</BarChart>

If the total number of ratings is available in the state then show it, otherwise put zero in the total number of ratings.

Conclusion

So this 5 star rating system will help in improving the SUSI skills. Also, it will help in making better decisions when we have multiple similar skills and we have to choose one to respond to the user query.

References

Continue Reading5 Star Skill Rating System in the SUSI.AI CMS

Skill Ratings Over Time

The SUSI SKill CMS provides an option to rate and review a skill. These feedbacks help the skill creators to improve the skills. Also, the ratings and reviews can be updated by the reviewer. But the CMS only provides the current rating of a skill. What if a user or a developer wants to see how that skill has performed over time? Are there any improvements in the skill or not?

For that, we need the skill ratings over time !

Server side implementation

Create a ratingsOverTime.json file to store the monthly average rating of the skills and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the timestamp for every month, the average ratings on a skill in that month and the total number of ratings in that month.

public static JsonTray ratingsOverTime;

Path ratingsOverTime_per = susi_skill_rating_dir.resolve("ratingsOverTime.json");
Path ratingsOverTime_vol = susi_skill_rating_dir.resolve("ratingsOverTime_session.json");
ratingsOverTime = new JsonTray(ratingsOverTime_per.toFile(), ratingsOverTime_vol.toFile(), 1000000);
OS.protectPath(ratingsOverTime_per);
OS.protectPath(ratingsOverTime_vol);

Now whenever a user rates a skill, the data in ratingsOverTime.json needs to be updated. For this fetch the overall rating data of the current month. Multiply the average rating with the total number of ratings (count) of that month.

sum = average_rating X number_of_ratings

Then add the rating given by the current user to this sum and divide by count + 1 to again get the new average rating. Also increment the total number of ratings by 1.

new_sum = sum + rating_by_user

new_avg = new_sum/(count+1)

number_of_ratings =  number_of_ratings + 1

float totalRating = skillRating * ratingCount;
float newAvgRating = (totalRating + skill_stars)/(ratingCount + 1);
ratingObject.put("rating", newAvgRating);
ratingObject.put("count", ratingCount + 1);

Now we have got the ratings over time stored in ratingsOverTime.json file. An API to access this data is also required. So create an API GetRatingOverTime.java returns the ratings over time of a particular skill. The API has the following attributes :

Endpoint : /cms/getRatingsOverTime.json

Minimum user role : anonymous

Parameters : model, group, language and skill

JSONArray skillRatings = languageName.getJSONArray(skill_name);
result.put("skill_name", skill_name);
result.put("ratings_over_time", skillRatings);
return new ServiceResponse(result);

It fetches the data corresponding to the skill from ratingsOverTime.json and returns it to the CMS.

Add this API to SusiServer.java

//Skill ratings over time
GetRatingsOverTime.class

References

Continue ReadingSkill Ratings Over Time