API’s in SUSI.AI BotBuilder

In this blog, I’ll explain the different API’s involved in the SUSI.AI Bot Builder and its working. Now if you are wondering how the SUSI.AI bot builder works or how drafts are saved, then this is the perfect blog. I’ll be explaining the different API’s grouped by different API endpoints.

API Implementation

fetchChatBots

export function fetchChatBots() {
 const url = `${API_URL}/${CMS_API_PREFIX}/getSkillList.json`;
 return ajax.get(url, {
   private: 1,
 });
}

This API is used to fetch all your saved chatbots, which are displayed on the BotBuilder Page. The API endpoint is getSkillList.json. Same endpoint is used when a user creates a skill, the difference is query parameter private is passed which then returns your chatbots. Now if you are wondering why we have same endpoint for skills and chatbots, the simple plain reason for this is chatbots are your private skills.

fetchBotDetails

export function fetchBotDetails(payload) {
 const { model, group, language, skill } = payload;
 const url = `${API_URL}/${CMS_API_PREFIX}/getSkill.json`;
 return ajax.get(url, {
   model,
   group,
   language,
   skill,
   private: 1,
 });
}

This API is used to fetch details of bot/skill respectively from the API endpoint getSkill.json. Group name, language, skill name, private and model are passed as query parameters.

fetchBotImages

export function fetchBotImages(payload) {
 const { name: skill, language, group } = payload;
 const url = `${API_URL}/${CMS_API_PREFIX}/getSkill.json`;
 return ajax.get(url, {
   group,
   language,
   skill,
   private: 1,
 });
}

This API is used to fetch skill and bot images from the API endpoint getSkill.json. Group name, language, skill name and private are passed as query parameters.

uploadBotImage

export function uploadBotImage(payload) {
 const url = `${API_URL}/${CMS_API_PREFIX}/uploadImage.json`;
 return ajax.post(url, payload, {
   headers: { 'Content-Type': 'multipart/form-data' },
   isTokenRequired: false,
 });
}

This API is used to upload the Bot image to the API endpoint uploadImage.json.The Content-Type entity header is used to indicate the media type of the resource. multipart/form-data means no characters will be encoded. This is used when a form requires a binary data like the contents of a file or image to be uploaded.

deleteChatBot

export function deleteChatBot(payload) {
 const { group, language, skill } = payload;
 const url = `${API_URL}/${CMS_API_PREFIX}/deleteSkill.json`;
 return ajax.get(url, {
   private: 1,
   group,
   language,
   skill,
 });
}

This API is used to delete Skill and Bot from the API endpoint deleteSkill.json.

storeDraft

export function storeDraft(payload) {
 const { object } = payload;
 const url = `${API_URL}/${CMS_API_PREFIX}/storeDraft.json`;
 return ajax.get(url, { object });
}

This API is used to store draft Bot to the API endpoint storeDraft.json. The object passed as parameter has the properties given by the user such as skill name,group etc., while saving the draft.

readDraft

export function readDraft(payload) {
 const url = `${API_URL}/${CMS_API_PREFIX}/readDraft.json`;
 return ajax.get(url, { ...payload });
}

This API is used to fetch draft from the API endpoint readDraft.json. This API is called on the BotBuilder Page where all the saved drafts are shown.

deleteDraft

export function deleteDraft(payload) {
 const { id } = payload;
 const url = `${API_URL}/${CMS_API_PREFIX}/deleteDraft.json`;
 return ajax.get(url, { id });
}

This API is used to delete the saved Draft from the API endpoint deleteDraft.json. It only needs one query parameter i.e. the draft ID.

In conclusion, the above API’s are the backbone of the SUSI.AI Bot Builder. API endpoints in server ensure the user has the same experience across the clients. Do checkout implementation of different API endpoints in server here.

Resources

Continue ReadingAPI’s in SUSI.AI BotBuilder

Adding different metrics sections to the start page

In the initial version of the SUSI.AI Skill CMS we simply displayed all the skills present in the system in the form of cards. Once the skill analytics was incorporated into the CMS we got a bunch of skill statistics and thus we enhanced the start page by incorporating horizontally scrollable skill cards as per skill metrics like top rated skills, most used skills, skills which have received the most feedback etc. I worked on adding the skills with most feedback section and the section for the top games. This post will majorly deal with how the metrics sections are implemented on the start page and how any new metrics can be incorporated into the system and thus displayed on the CMS.

About the API

/cms/getSkillMetricsData.json?language=${language}

Sample API call:

https://api.susi.ai/cms/getSkillMetricsData.json?language=en

 

This will return a JSON which contains the skill data for all the metrics.

{
 "accepted": true,
 "model": "general",
 "group": "All",
 "language": "en",
 "metrics": {
        "newest": [...],
     "rating": [...],
      ...
 }
 "message": "Success: Fetched skill data based on metrics",
   "session": {"identity": {
           "type": "host",
          "name": "162.158.23.7_68cefd16",
          "anonymous": true
   }}
}

 

All of the data for several metics comes from the metrics object of the response which in turn contains arrays of skill data for each metric.

CMS Implementation

Once the BrowseSkill component is mounted we make an API call to the server to fetch all the data and save it to the component state, this data is then fed to the ScrollCardList component as props and the scroll component is rendered with appropriate data for different metrics.

loadMetricsSkills = () => {
   let url;
   url =
           urls.API_URL +
           '/cms/getSkillMetricsData.json?language=' +
           this.state.languageValue;
   let self = this;
   $.ajax({
           url: url,
           dataType: 'jsonp',
           jsonp: 'callback',
           crossDomain: true,
           success: function(data) {
                   self.setState({
                           skillsLoaded: true,
                           staffPicksSkills: data.metrics.staffPicks,
                           topRatedSkills: data.metrics.rating,
                           topUsedSkills: data.metrics.usage,
                           latestUpdatedSkills: data.metrics.latest,
                           newestSkills: data.metrics.newest,
                           topFeedbackSkills: data.metrics.feedback,
                           topGames: data.metrics['Games, Trivia and Accessories'],
                   });
           },
           error: function(e) {
                   console.log('Error while fetching skills based on top metrics', e);
                   return self.loadMetricsSkills();
           },
   });
};

 

We are using a single component for skill metrics and skill listing which show up on applying any filter or visiting any category. Thus we think of a condition when the skill metrics are to be displayed and conditionally render the metrics section depending on the condition.

So the metrics section shows up only when we have not visited any category or language page, there’s no search query in the search bar, there’s no rating refine filter applied and no time filter applied.

let metricsHidden =
         this.props.routeType ||
         this.state.searchQuery.length > 0 ||
         this.state.ratingRefine ||
         this.state.timeFilter;

 

Depending on the section you want to display, pass appropriate data as props to the SkillCardScrollList component, say we want to display the section with most feedback

{this.state.topFeedbackSkills.length &&
!metricsHidden ? (
   <div style={metricsContainerStyle}>
           <div
                   style={styles.metricsHeader}
                   className="metrics-header"
           >
                   <h4>
                           {'"SUSI, what are the skills with most feedback?"'}
                   </h4>
           </div>
           {/* Scroll Id must be unique for all instances of SkillCardList*/}
           {!this.props.routeType && (
                   <SkillCardScrollList
                           scrollId="topFeedback"
                           skills={this.state.topFeedbackSkills}
                           modelValue={this.state.modelValue}
                           languageValue={this.state.languageValue}
                           skillUrl={this.state.skillUrl}
                   />
           )}
   </div>
) : null}

 

So if there are skills preset in the topFeedbackSkills array which was saved in the state from the server initially and the condition to hide metrics is false we render the component and pass appropriate props for scrollId, skills data, language and model values and skill url.

In a similar way any metrics section can be implemented in the CMS, if the data is not present in the API, modify the endpoint to enclose the data you need, fetch data data from the server and just render it.

So I hope after reading through this you have a more clearer understanding about how the metrics sections are implemented on the CMS.

Resources

Continue ReadingAdding different metrics sections to the start page

Adding Marketer and Sales Admin Events Relationship with User on Open Event Server

In this blog, we will talk about how to add API for adding and displaying events in with a user acts as a Marketer and/or Sales Admin on Open Event Server. The focus is on Model Updation and Schema updation of User.

Model Updation

For the Marketer and Sales Admin events, we’ll update User model as follows

Now, let’s try to understand these relationships.

In this feature, we are providing user to act as a marketer and sales admin for a event.

  1. Both the relationships will return the events in which the user is acting as a Marketer and/or Sales Admin.
  2. There are two custom system roles in model CustomSysRole which are Marketer and Sales Admin. A user can act as these custom system roles with respect to an event.
  3. In this relationship, we will return those events from UserSystemRole model in which a user is acting as Marketer Custom System Role and Sales Admin Custom System Role.
  4. We make use of Event and join UserSystemRole and CustomSysRole where we use that user where UserSystemRole.user_id == User.id , CustomSysRole.id == UserSystemRole.role_id, CustomSysRole.name == “Sales Admin” and then we return events in which Event.id == UserSystemRole.event_id
  5. Similarly, for Marketer events we make use of Event and join UserSystemRole and CustomSysRole where we use that user where UserSystemRole.user_id == User.id , CustomSysRole.id == UserSystemRole.role_id, CustomSysRole.name == “Marketer” and then we return events in which Event.id == UserSystemRole.event_id

Schema Updation

For the Marketer and Sales Admin events, we’ll update UserSchema as follows

Now, let’s try to understand this Schema.

In this feature, we are providing user to act as a marketer and sales admin for a event.

  1. For displaying marketer_events relation self_view is displayed by API v1.user_marketer_events and collection of these events is displayed by API v1.event_list
  2. These APIs will return the Events as schema=”EventSchema”. Here, many=True tells us that this is One to many relationship with Events model.

So, we saw how an user can act as a marketer and/or sales admin for many events.

Resources

Continue ReadingAdding Marketer and Sales Admin Events Relationship with User on Open Event Server

Adding Custom System Roles in Open Event Server

In this blog, we will talk about how to add different custom system roles concerning a user on Open Event Server. The focus is on its model and Schema updation.

Model Updation

For the User Table, we’ll update our User Model as follows:

Now, let’s try to understand these hybrid properties.

In this feature, we are providing Admin the rights to see whether a user is acting as a Marketer and / or  Sales Admin of any of the event or not. Here, _is__system_role method is used to check whether an user plays a system role like Marketer, Sales Admin or not. This is done by querying the record from UserSystemRole model. If the record is present then the returned value is True otherwise false.

Schema Updation

For the User Model, we’ll update our Schema as follows:

Now, let’s try to understand this Schema.

Since all the properties will return either True or false so these all properties are set to Boolean in Schema.Here dump_only means, we will return this property in the Schema.

So, we saw how User Model and Schema is updated to show custom system roles concerning a user on Open Event Server.

Resources

Continue ReadingAdding Custom System Roles in Open Event Server

Loading Default System Image of Event Topic on Open Event Server

In this blog, we will talk about how to add feature of loading system image of event topic from server to display it on Open Event Server. The focus is on adding a helper function to create system image and loading that local image onto server.

Helper function

In this feature, we are providing feature of addition of loading default system image if user doesn’t provides that.

  1. First we get a suitable filename for a image file using get_file_name() function.
  2. After getting filename, we check if the url provided by user is a valid url or not.
  3. If the url is invalid then we use the default system image as the image of that particular event topic.
  4. After getting the local image then we read that image, if the given image file or the default image is not readable or gives IOError then we send a message to the user that Image url is invalid.
  5. After successful reading of image we upload the image to event_topic directory in static directory of the project.
  6. After uploading of this image we get a local URL which shows where is the image is stored. This path is stored into database and finally we can display this image.

Resources

Continue ReadingLoading Default System Image of Event Topic on Open Event Server

Adding Custom System Roles API on Open Event Server

In this blog, we will talk about how to add API for accessing the Custom System Roles on Open Event Server. The focus is on Schema creation and it’s API creation.

Schema Creation

For the CustomSystemRoleSchema, we’ll make our Schema as follows

Now, let’s try to understand this Schema.

In this feature, we are providing Admin the rights to get and create more system roles.

  1. First of all, we are provide the two fields in this Schema, which are id and name.
  2. The very first attribute id should be of type string as it would have the identity which will auto increment when a new system role is created. Here dump_only means that this value can’t be changed after the record is created.
  3. Next attribute name should be of string type and it will contain the name of new custom system role. This attribute is required in a custom_system_roles table.

API Creation

For the Custom System Roles, we’ll make our API as follows

Now, let’s try to understand this Schema.

In this API, we are providing Admin the rights to set Custom System roles.

  1. CustomSystemRoleList inherits ResourceList which will give us list of all the custom system roles in the whole system.
  2. CustomSystemRoleList has a decorators attribute which gives the permission of POST request to only admins of the system.
  3. CustomSystemRoleDetail inherits ResourceDetail which will give the details of a CustomSystemRole object by id.
  4. CustomSystemRoleDetail has a decorators attribute which gives the permission of PATCH and DELETE requests to only admins of the system.

So, we saw how Custom System Role Schema and API is created to allow users to get it’s values and Admin users to update and delete it’s record.

Resources

Continue ReadingAdding Custom System Roles API on Open Event Server

Adding Panel Permissions API in Open Event Server

In this blog, we will talk about how to add API for accessing the Panel Permissions on Open Event Server. The focus is on Schema creation and it’s API creation.

Schema Creation

For the PanelPermissionSchema, we’ll make our Schema as follows

Now, let’s try to understand this Schema.

In this feature, we are providing Admin the rights to create and assign panel permission to any of the custom system role.

  1. First of all, we are provide the four fields in this Schema, which are id, panel_name, role_id and can_access.
  2. The very first attribute id should be of type string as it would have the identity which will auto increment when a new system role is created. Here dump_only means that this value can’t be changed after the record is created.
  3. Next attribute panel_name should be of string type and it will contain the name of panel. This attribute is required in a panel_permissions table so set as allow_none=False.
  4. Next attribute role_id should be of integer type as it will tell us that to which role current panel is concerning.
  5. Next attribute can_access should be of boolean type as it will tell us whether a role of id=role_id has access to this panel or not.
  6. There is also a relationship named role which will give us the details of the custom system role with id=role_id.

API Creation

For the Panel Permissions, we’ll make our API as follows

Now, let’s try to understand this Schema.

In this API, we are providing Admin the rights to set panel permissions for a custom system role.

  1. PanelPermissionList inherits ResourceList which will give us list of all the custom system roles in the whole system.
  2. PanelPermissionList has a decorators attribute which gives the permission of both GET and POST requests to only admins of the system.
  3. The POST request of PanelPermissionList API requires the relationship of role.
  4. PanelPermissionDetail inherits ResourceDetail which will give the details of a Panel Permission object by id.
  5. PanelPermissionDetail has a decorators attribute which gives the permission of GET, PATCH and DELETE requests to only admins of the system.

So, we saw how Panel Permissions Schema and API is created to allow Admin users to get, update and delete it’s record.

Resources

 

Continue ReadingAdding Panel Permissions API in Open Event Server

Adding device names’ support for check-ins to Open Event Server

The Open Event Server provides backend support to Open Event Organizer Android App which is used to check-in attendees in an event. When checking in attendees, it is important for any event organizer to keep track of the device that was used to check someone in. For this, we provide an option in the Organizer App settings to set the device name. But this device name should have support in the server as well.

The problem is to be able to add device name data corresponding to each check-in time. Currently attendees model has an attribute called `checkin-times`, which is a csv of time strings. For each value in the csv, there has to be a corresponding device name value. This could be achieved by providing a similar csv key-value pair for “device-name-checkin”.

The constraints that we need to check for while handling device names are as follows:

  • If there’s `device_name_checkin` in the request, there must be `is_checked_in` and `checkin_times` in the data as well.
  • Number of items in checkin_times csv in data should be equal to the length of the device_name_checkin csv.
  • If there’s checkin_times in data, and device-name-checkin is absent, it must be set to `-` indicating no set device name.
if ‘device_name_checkin’ in data and data[‘device_name_checkin’] is not None:
  if ‘is_checked_in’ not in data or not data[‘is_checked_in’]:
       raise UnprocessableEntity(
           {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Attendee needs to be checked in first”
       )
  elif ‘checkin_times’ not in data or data[‘checkin_times’] is None:
      raise UnprocessableEntity(
          {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Check in Times missing”
      )
  elif len(data[‘checkin_times’].split(“,”)) != len(data[‘device_name_checkin’].split(“,”)):
     raise UnprocessableEntity(
           {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Check in Times missing for the corresponding device name”
         )
 if ‘checkin_times’ in data:
   if ‘device_name_checkin’ not in data or data[‘device_name_checkin’] is None:
       data[‘device_name_checkin’] = ‘-‘

The case is a little different for a PATCH request since we need to check for the number of items differently like this:

if ‘device_name_checkin’ in data and data[‘device_name_checkin’] is not None:
            if obj.device_name_checkin is not None:
               data[‘device_name_checkin’] = ‘{},{}’.format(obj.device_name_checkin, data[‘device_name_checkin’])                                                   
            if len(data[‘checkin_times’].split(“,”)) != len(data[‘device_name_checkin’].split(“,”)):
               raise UnprocessableEntity(
                   {‘pointer’: ‘/data/attributes/device_name_checkin’},
                   “Check in Time missing for the corresponding device name”)

Since we expect only the latest value to be present in a PATCH request, we first add it to the object by formatting using:

'{},{}'.format(obj.device_name_checkin, data['device_name_checkin'])

and then compare the length of the obtained CSVs for check in times and device names, so that corresponding to each check in time, we have either a device name or the default fill in value ‘-’.

That’s all. Read the full code here.

Requests and Responses:

Resources

  1. SQLAlchemy Docs
    https://docs.sqlalchemy.org/en/latest/
  2. Alembic Docs
    http://alembic.zzzcomputing.com/en/latest/
  3. Flask REST JSON API Classical CRUD operation
    https://flask-rest-jsonapi.readthedocs.io/en/latest/quickstart.html#classical-crud-operations
Continue ReadingAdding device names’ support for check-ins to Open Event Server

Fetch Five Star Skill Rating from getSkillList API in SUSI.AI Android

SUSI.AI had a thumbs up/down rating system till now, which has now been replaced by a five star skill rating system. Now, the user is allowed to rate the skill based on a five star rating system. The UI components include a rating bar and below the rating bar is a section that displays the skill rating statistics – total number of ratings, average rating and a graph showing the percentage of users who rated the skill with five stars, four stars and so on.

SUSI.AI Skills are rules that are defined in SUSI Skill Data repo which are basically the processed responses that SUSI returns to the user queries. When a user queries something from the SUSI Android app, a query to SUSI Server is made which in turn fetches data from SUSI Skill Data and returns a JSON response to the app. Similarly, to get skill ratings, a call to the ‘/cms/getSkillList.json’ API is made. In this API, the server checks the SUSI Skill Data repo for the skills and returns a JSON response consisting of all the required information like skill name, author name, description, ratings, etc. to the app. Then, this JSON response is parsed to extract individual fields to display the appropriate information in the skill details screen of the app.

API Information

The endpoint to fetch skills is ‘/cms/getSkillList.json’
The endpoints takes three parameters as input –

  • model – It tells the model to which the skill belongs. The default value is set to general.
  • group – It tells the group(category) to which the skill belongs. The default value is set to All.
  • language – It tells the language to which the skill belongs. The default value is set to en.

Since all skills have to be fetched, this API is called for every group individually. For instance, call “https://api.susi.ai/cms/getSkillList.json?group=Knowledge” to get all skills in group “Knowledge”. Similarly, call for other groups.

Here is a sample response of a skill named ‘Capital’ from the group Knowledge :

"capital": {
      "model": "general",
      "group": "Knowledge",
      "language": "en",
      "developer_privacy_policy": null,
      "descriptions": "A skill to tell user about capital of any country.",
      "image": "images/capital.png",
      "author": "chashmeet singh",
      "author_url": "https://github.com/chashmeetsingh",
      "skill_name": "Capital",
      "terms_of_use": null,
      "dynamic_content": true,
      "examples": ["What is the capital of India?"],
      "skill_rating": {
        "negative": "0",
        "positive": "4",
        "feedback_count" : 0,
        "stars": {
          "one_star": 0,
          "four_star": 1,
          "five_star": 0,
          "total_star": 1,
          "three_star": 0,
          "avg_star": 4,
          "two_star": 0
        }
      },
      "creationTime": "2018-03-17T17:11:59Z",
      "lastAccessTime": "2018-06-06T00:46:22Z",
      "lastModifiedTime": "2018-03-17T17:11:59Z"
    },


It consists of all details about the skill called ‘Capital’:

  1. Model (model)
  2. Group (group)
  3. Language (language)
  4. Developer Privacy Policy (developer_privacy_policy)
  5. Description (descriptions)
  6. Image (image)
  7. Author (author)
  8. Author URL (author_url)
  9. Skill name (skill_name)
  10. Terms of Use (terms_of_use)
  11. Content Type (dynamic_content)
  12. Examples (examples)
  13. Skill Rating (skill_rating)
  14. Creation Time (creationTime)
  15. Last Access Time (lastAccessTime)
  16. Last Modified Time (lastModifiedTime)

From among all this information, the information of interest for this blog is Skill Rating. This blog mainly deals with showing how to parse the JSON response to get the skill rating star values, so as to display the actual data in the skill rating graph.

A request to the getSkillList API is made for each group using the GET method.

@GET("/cms/getSkillList.json")
Call<ListSkillsResponse> fetchListSkills(@Query("group") String groups);

It returns a JSON response consisting of all the aforementioned information. Now, to parse the JSON response, do the following :

  1. Add a response for the response received as a result of API call. ListSkillsResponse contains two objects – group and skills.
    This blog is about getting the skill rating, so let us proceed with parsing the required response. The skills object contains the skill data that we need. Hence, next a SkillData class is created.

    class ListSkillsResponse {
       val group: String = "Knowledge"
       val skillMap: Map<String, SkillData> = HashMap()
    }
  2. Now, add the SkillData class. This class defines the response that we saw for ‘Capital’ skill above. It contains skill name, author, skill rating and so on.

    class SkillData : Serializable {
       var image: String = ""
       @SerializedName("author_url")
       @Expose
       var authorUrl: String = ""
       var examples: List<String> = ArrayList()
       @SerializedName("developer_privacy_policy")
       @Expose
       var developerPrivacyPolicy: String = ""
       var author: String = ""
       @SerializedName("skill_name")
       @Expose
       var skillName: String = ""
       @SerializedName("dynamic_content")
       @Expose
       var dynamicContent: Boolean? = null
       @SerializedName("terms_of_use")
       @Expose
       var termsOfUse: String = ""
       var descriptions: String = ""
       @SerializedName("skill_rating")
       @Expose
       var skillRating: SkillRating? = null
    }
    
  3. Now, add the SkillRating class. As what is required is the skill rating, narrowing down to the skill_rating object. The skill_rating object contains the actual rating for each skill i.e. the stars values. So, this files defines the response for the skill_rating object.

    class SkillRating : Serializable {
       var stars: Stars? = null
    }
    
  4. Further, add a Stars class. Ultimately, the values that are needed are the number of users who rated a skill at five stars, four stars and so on and also the total number of users and the average rating. Thus, this file contains the values inside the ‘stars’ object.

    class Stars : Serializable {
       @SerializedName("one_star")
       @Expose
       var oneStar: String? = null
       @SerializedName("two_star")
       @Expose
       var twoStar: String? = null
       @SerializedName("three_star")
       @Expose
       var threeStar: String? = null
       @SerializedName("four_star")
       @Expose
       var fourStar: String? = null
       @SerializedName("five_star")
       @Expose
       var fiveStar: String? = null
       @SerializedName("total_star")
       @Expose
       var totalStar: String? = null
       @SerializedName("avg_star")
       @Expose
       var averageStar: String? = null
    }
    

Now, the parsing is all done. It is time to use these values to plot the skill rating graph and complete the section displaying the five star skill rating.

To plot these values on the skill rating graph refer to the blog on plotting horizontal bar graph using MPAndroid Chart library. In step 5 of the linked blog, replace the second parameter to the BarEntry constructor by the actual values obtained by parsing.

Here is how we do it.

  • To get the total number of ratings
val  totalNumberofRatings: Int = skillData.skillRating?.stars?.totalStars

 

  • To get the average rating
val averageRating: Float = skillData.skillRating?.stars?.averageStars

 

  • To get number of users who rated the skill at five stars
val fiveStarUsers: Int = skillData.skillRating?.stars?.fiveStar

Similarly, get the number of users for fourStar, threeStar, twoStar and oneStar.

Note : If the totalNumberOfRatings equals to zero, then the skill is unrated. In this case, display a message informing the user that the skill is unrated instead of plotting the graph.

Now, as the graph shows the percentage of users who rated the skill at a particular number of stars, calculate the percentage of users corresponding to each rating, parse the result to Float and place it as the second parameter to the BarEntry constructor  as follows :

 entries.add(BarEntry(4f, (fiveStarUsers!!.toFloat() / totalUsers) * 100f)))

Similarly, replace the values for all five entries. Finally, add the total ratings and average rating section and display the detailed skill rating statistics for each skill, as in the following figure.

Resources

 

 

Continue ReadingFetch Five Star Skill Rating from getSkillList API in SUSI.AI Android

Modifying Allowed Usage for a User

Badgeyay has been progressing in a very good pace. There are a lot of features being developed and modified in this project. One such feature that has been added is the increasing allowed usage of a user by an admin.

What is Allowed Usage?

Allowed usage is an integer associated with a particular user that determines the number of badges that a person can generate using a single email id. This will allow us to keep track of the number of badges being produced by a particular ID and all.

Modifying the Allowed Usage

This feature is basically an Admin feature, that will allow an admin to increase or decrease the allowed usage of a particular user. This will ensure that if incase a particular user has his/her usage finished, then by contacting the admin, he/she can get the usage refilled.

Adding the functionality

The functionality required us to to add two things

  • A schema for modifying allowed user
  • A route in backend to carry out the functionality

So, Let us start by creating the schema

class UserAllowedUsage(Schema):
class Meta:
type_ =
‘user_allowed_usage’
kwargs = {
‘id’: ‘<id>’}

id = fields.Str(required=True, dump_only=True)
allowed_usage = fields.Str(required=
True, dump_only=True)

Once we have our schema created, then we can create a route to modify the allowed usage for a particular user.

This route will be made accessible to the admin of Badgeyay.

@router.route(‘/add_usage’, methods=[‘POST’])
def admin_add_usage():
try:
data = request.get_json()[
‘data’]
print(data)
except Exception:
return ErrorResponse(JsonNotFound().message, 422, {‘Content-Type’: ‘application/json’}).respond()

uid = data[‘uid’]
allowed_usage = data[
‘allowed_usage’]
user = User.getUser(user_id=uid)
user.allowed_usage = user.allowed_usage + allowed_usage
db.session.commit()

return jsonify(UserAllowedUsage().dump(user).data)

The add_usage route is given above. We can use this route to increase the usage of a particular user.

Given below is an image that shows the API working.

Resources

Continue ReadingModifying Allowed Usage for a User