Make a cumulative API to return skills based on standard metrics in SUSI.AI

In this blog post, we are going to discuss on how to make a cumulative API which returns skills based on different standard metrics. It was implemented to combine various API calls, that were made from various clients, thereby reducing the number of API calls made. It lead to an optimization in the page load time of various clients and also helped to reduce the 503 errors that were received, due to very frequent API hits. The API endpoint for it is https://api.susi.ai/cms/getSkillMetricsData.json.

It accepts 5 optional parameters –

  • model – It represents the model for which the skill are fetched. The default value is set to General.
  • group – It represents the group(category) for which the skill are fetched. The default value is set to Knowledge.
  • language – It represents the language for which the skill are fetched. The default value is set to en.
  • duration – It represents the duration based on which skills are fetched by standard metrics like usage.
  • count – It represents the number of skills to be returned on a particular metric.

The minimalUserRole is set to ANONYMOUS for this API, as the data is required for home-page and doesn’t need the user to be logged-in.

Going through the API development

  • The parameters are first extracted via the call object that is passed to the main function. The  parameters are then stored in variables. If any parameter is absent, then it is set to the default value.
  • There is a check if the count param exists. If exists, then it is checked if it is of valid data-type. If no, an exception is thrown. And if count param doesn’t exist, it is set to default of 10.
  • This code snippet discusses the above two points –
@Override
public ServiceResponse serviceImpl(Query call, HttpServletResponse response, Authorization rights, 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", "All");
        String language_name = call.get("language", "en");
        int duration = call.get("duration", 0);
        JSONArray jsonArray = new JSONArray();
        JSONObject json = new JSONObject(true);
        JSONObject skillObject = new JSONObject();
        String countString = call.get("count", null);
        Integer count = null;

        if(countString != null) {
            if(Integer.parseInt(countString) < 0) {
                throw new APIException(422, "Invalid count value. It should be positive.");
            } else {
                try {
                    count = Integer.parseInt(countString);
                } catch(NumberFormatException ex) {
                    throw new APIException(422, "Invalid count value.");
                }
            }
        } else {
            count = 10;
        }
.
.
.

 

  • Then the skills are fetched based on the group name and then stored in a JSONArray named jsonArray. This array basically contains the metadata objects of each skill.
  • Now, we need to sort and filter these skills based on standard metrics like – Rating, Feedback Count, Usage Count, Latest skills.
  • The implementation for getting the skills based on the creation time is as follows. Rest, all the metrics were also implemented in the same fashion.

JSONObject skillMetrics = new JSONObject();
List<JSONObject> jsonValues = new ArrayList<JSONObject>();

// temporary list to extract objects from skillObject
for (int i = 0; i < jsonArray.length(); i++) {
    jsonValues.add(jsonArray.getJSONObject(i));
}

// Get skills based on creation date - Returns latest skills
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;
    }
});

JSONArray creationDateData = getSlicedArray(jsonValues, count);
skillMetrics.put("latest", creationDateData);

.
.
.

 

  • The above code snippet deals with sorting the skills based on the creation time, that helps us to fetch the latest skills. The latest skills are stored on the skillMetrics object with the key name latest.
  • Similarly, the skills based on metrics like rating, feedback count and usage are stored with key names rating, feedback & usage respectively.

From the above snippet, we can also see a call to the function getSlicedArray. It takes a list of skills and count as input paramters. It returns the first ‘count’ skills from the list depending on the count value. The implementation of it is as follows –

private JSONArray getSlicedArray(List<JSONObject> jsonValues, Integer count)
{
    JSONArray slicedArray = new JSONArray();
    for (int i = 0; i < jsonValues.size(); i++) {
        if(count == 0) {
            break;
        } else {
            count --;
        }
        slicedArray.put(jsonValues.get(i));
    }
    return slicedArray;
}

 

  • The response object is then sent with 6 (six) key values mainly, apart from the session object. They are
    • accepted –  true – It tells that the skills have been fetched.
    • message – “Success: Fetched skill data based on metrics”
    • model –  It is the model that is sent on request params or the default value.
    • group –  It is the group that is sent on request params or the default value.
    • language – It is the language that is sent on request params or the default value.
    • metrics –  It is the main object of relevance for this API, which contains 4 child keys with values as an array of Skill Metadata objects.

{
  "accepted": true,
  "model": "general",
  "group": "All",
  "language": "en",
  "metrics": {
    "feedback": [
    {skill_1}, {skill_2}, ...
    ],
    "usage": [
    {skill_1}, {skill_2}, ...
    ],
    "rating": [
    {skill_1}, {skill_2}, ...
    ],
    "latest": [
    {skill_1}, {skill_2}, ...
    ]
  },
  "message": "Success: Fetched skill data based on metrics",
  "session": {
   ....
  }
}

 

I hope the development of creating the aforesaid API and the purpose of it is clear and proved to be helpful for your understanding.

References

Continue ReadingMake a cumulative API to return skills based on standard metrics in SUSI.AI