Using Picasso to Show Images in SUSI Android

Important skills of SUSI.AI are to display web search queries, maps of any location and provide a list of relevant information of a topic. This blog post will cover why Glide is replaced by Picasso to show images related to these action types and how it is implemented in SUSI AndroidPicasso is a powerful image downloading and caching open source library developed by Square.

Why Glide is replaced by Picasso to show images in SUSI Android?

Previously we used Glide library to show preview in SUSI Android but we replace it because it was creating an error continuously.

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity at com.bumptech.glide.manager.RequestManagerRetriever  at Com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102) at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:87)at com.bumptech.glide.Glide.with(Glide.java:629)
atnorg.fossasia.susi.ai.adapters.recycleradapters.WebSearchAdapter.onBindViewHolder(WebSearchAdapter.java:74)

Reason for this error is when activity destroyed and again recreated the context used by glide is old one and  that activity already destroyed .

Glide.with(context).load(imageList.get(0))

One solution of this error is to use context.getApplicationContext()  but it is a bad idea. Another solution is to replace glide by picasso and later one is good because picasso is also a very good image downloading and caching library.

To use Picasso in your project you have to add dependency in build.gradle(Module) file.

dependencies {
  
  compile “com.squareup.picasso:picasso:2.4.0”
  
}

How Picasso is used in different actiontype

Map

“actions”: [
     {
       “type”: “map”,
       “latitude”: “1.2896698812440377”,
       “longitude”: “103.85006683126556”,
       “zoom”: “13”
     }
   ]

Link we used to retrieve image url is

http://staticmap.openstreetmap.de/statucmap.php?center=latitude,longitude& zoom=zoom&size=lengthXbreadth

Picasso will load image from this url and show image in the imageview. Here mapImage is the imageview in which map image is shown.

Picasso.with(currContext).load(mapHelper.getMapURL())
                       .into(mapImage, new  com.squareup.picasso.Callback() {    
                           @Override
                           public void onSuccess() {
                               pointer.setVisibility(View.VISIBLE);
                           }
                           @Override
                           public void onError() {
                               Log.d(“Error”, “map image can’t loaded”);
                           }
                       });

WebSearch

When we query like “Search for fog” we get ‘query’ in reply from server

“query”: “fog”

Now we use this query to retrieve image url which we used in Picasso to show images.Picasso load this image into previewImageView imageview. Image url is retrieved using  DuckDuckGo api. We are using url

https://api.duckduckgo.com/?format=json&pretty=1&q=query&ia=meanings

It gives a json response which contains image url

Picasso.with(context).load(iconUrl)
      .into(holder.previewImageView, new  com.squareup.picasso.Callback() {
                  @Override
                   public void onSuccess() {
                         Log.d(“Sucess”,“image loaded successfully”);
                   }
                   @Override
                   public void onError() {
                       holder.previewImageView.setVisibility(View.GONE);
                     }
                });     

Here also com.squareup.picasso.Callback is use to find that image is loaded successfully or not.

RSS

When we query any like “dhoni” we get ‘link’ in reply from server

“title”: “Dhoni”,

“description”: “”,
“link”: “http://de.wikipedia.org/wiki/Dhoni”

We use this link in android-link-preview library to retrieve relevant image url and then Picasso use this url to load image into imageview previewImageView.

Picasso.with(currContext).load(imageList.get(0))
      .fit().centerCrop()
      .into(previewImageView);

Reference

Continue Reading Using Picasso to Show Images in SUSI Android

Handling Change of Password of SUSI.AI Account

In this blog, we will talk about a very special case, where the user changes his password to his current one only, in other words, the user enters the same password in both current password and new password. This case is now being handled by SUSI.AI server.

Considering the example of SUSI.AI Web Chat, we have following dialog when the user tries to change his/her password:

Here the user can add his/her current password and new password. When the new password meets the minimum conditions (minimum 6 characters), then the user can press CHANGE button.

We make ajax call to the server with the following endpoint:

BASE_URL+'/aaa/changepassword.json?'+
            'changepassword=' + email +
            '&password=' + this.state.passwordValue +
            '&newpassword=' + this.state.newPasswordValue +
            '&access_token='+cookies.get('loggedIn');

Here we have 4 parameters:

  • changepassword: This takes the email of the current user
  • password: This is the password of the current user, which is saved in the state named “passwordValue”
  • newpassword: This is the new password which the user enters
  • access_token: These are access tokens which are fetched from cookies. These are defined on login and are deleted on logout.

This is now handled on the server by a file named PasswordChangeService.java. Here we have to check whether the newpassword and password matches or not.

In this file, we have a function named serviceImpl with return type ServiceResponse and takes in an argument: Query post (Query is the return type). The query is not the only argument, Please read from the file from resources mentioned below for all the argument. To handle our case we just need to work with the post.

We extract the password, newpassword and email as follows:

String useremail = post.get("changepassword", null);
String password = post.get("password", null);
String newpassword = post.get("newpassword",null);

So to simply handle the case where password and newpassword matches, we define an if block in java and compare these two parameters as follows:

if(password.equals(newpassword)){
            result.put("message", "Your current password and new password matches");
            result.put("accepted", false);
            return new ServiceResponse(result);
}

Here we put the message as “Your current password and new password matches” and make the accepted flag of result JSON as false. After this, we return the ServiceResponse.

Now in our web chat client, the ajax call is as follows:

$.ajax({
                url: changePasswordEndPoint,
                dataType: 'jsonp',
                crossDomain: true,
                timeout: 3000,
                async: false,
                statusCode: {
                    422: function() {
                      let msg = 'Invalid Credentials. Please check your Email or Password.';
                      let state = this.state;
                      state.msg = msg;
                      this.setState(state);
                    }
                },
                success: function (response) {
                    let msg = response.message+'\n Please login again.';
                    let state = this.state;
                    state.msg = msg;
                    state.success = true;
                    state.msgOpen = true;
                    this.setState(state);
                }.bind(this),
                error: function(jqXHR, textStatus, errorThrown) {
                    let msg = 'Failed. Try Again';
                    if (status === 'timeout') {
                      msg = 'Please check your internet connection';
                    }
                    let state = this.state;
                    state.msg = msg;
                    state.msgOpen = true;
                    this.setState(state);
                }.bind(this)
            });

In our success method of ajax call,  we receive the JSON response in a variable named response and store this in the state in variable msg and set the state of success equal to true. We then use the state and message to handle accordingly.

Our JSON object when both password and new password are same:

So this is how clients can handle accordingly to the message received from the server instead of doing this on their own end.

Resources

Continue Reading Handling Change of Password of SUSI.AI Account

Change Background of Message Section in SUSI.AI Web Chat

In SUSI.AI Web Chat we pay special attentions to the UI to make it easy to use and attract more users. Many chat apps offer users customization such as changing the colors and background. As this is very popular we decided to give the option to customize the background of message section of SUSI.AI web chat. The UI of the message section had a default gray background which could not be modified by the user. The goal was now to allow the user to customize the look of his or her own client starting with the background of the message section.

We added the settings to change the background image to Custom theme menu which occurs only when the user is logged in.The option looks like this:

User can add URL of any image of his/her choice and it will be set as the background of message section.
Now let’s take a look at the implementation of this option. We added messageBackgroundImage to our state of message section and initialised it to ‘ ’ (empty string). The TextField component looks like this:

<TextField
   name="messageImg"
   style={{display:component.component==='body'?'block':'none'}}
   ref={(input) => { this.backImage = input }}
   onChange={
     (e,value)=>
     this.handleChangeMessageBackground(value) }
    value={this.state.messageBackgroundImage}
    floatingLabelText="Message Background Image URL"
 />

OnChange method handles the input URL and this calls the function handleChangeMessageBackground. This function is having following implementation:

handleChangeMessageBackground(backImage){
    this.setState({messageBackgroundImage:backImage});
  }

It sets the messageBackgroundImage equal to the URL entered by the user. Now to change the background of message section we made a custom React style object messageBackgroundStyles:

const messageBackgroundStyles = {
        backgroundImage: `url(${this.state.messageBackgroundImage})`,
        backgroundRepeat: 'no-repeat',
        backgroundSize: '100% 100%'
    }

In the above object,

backgroundImage: sets the message section background to the image on URL entered by user

backgroundImage: Does not allows to the image to be repeated.

backgroundSize: Fills the entire message section with the background image.

Now this style was added to the component of message section :

<div className='message-section'>
     <ul
      className='message-list'
      ref={(c) => { this.messageList = c; }}
      style={messageBackgroundStyles}> // Styles added
       // Some relevant code    
      </ul>
</div>

Now we have following UI, where the user can modify the message section background with image of his or her own choice. User can add any image as background of message section by adding appropriate URL. The image will cover the entire message section without repeating itself.

Background Image source:link

This gives our user custom look according to his/her own choice.

Resources

Testing Link

http://chat.susi.ai

Background Image source of featured image : link

Continue Reading Change Background of Message Section in SUSI.AI Web Chat

Teaching Susi

In this blog post, I’ll explain how you can add a skill to SUSI.

Skills to be added in this tutorial:

  1. Ask SUSI for conversion of decimal into Binary .
  2. Ask SUSI to tell a quote.
  3. Skill development in SUSI is very interesting and easy. A comprehensive guide for skill development can be found here.

We have a Susi Skill development environment based on an Etherpad. Are you unaware what an Etherpad is? It is a blank web page where you can just put in your text and everyone can collaborate.

Here is a screenshot of what etherpad looks like:

  • open http://dream.susi.ai
  • name a dream (just pick a name for your tests in lower case letters)
  • the etherpad is filled with a welcome message, just delete the content completely

Ask SUSI for conversion of decimal into Binary

“*” here represents any decimal number number.Suppose we enter a decimal number and want susi to return it’s binary equivalent. So to make our skill, first of all we should form a general query.

Query: Convert * into binary or * in binary

After defining our query we want susi to reply with relevant answer.

For taking out the conversion from decimal to binary we can use JavaScript.

We define Javascript syntax in etherpad as follows :

!javascript:Binary for $1$ = $!$
(+$1$).toString(2);
eol

Explanation :

!javascript” allows us to print like javascript console. We can add JavaScript code by using this and do mathematical calculations to convert our decimal into binary.

“Binary for $1$ = $!$”  represents output format where $1$ stores the value in “*” in query given by user to susi. $!$ will print the result of javascript code below.

“(+$1$).toString(2);” This is a single line javascript code while converts value in “$1$” to binary, It’s output is reflected in “$!$”

“eol” represents end of line, which signifies our code for skill is finished.

In etherpad our skill would look like :

“#” – used for commenting out lines in skill

#The following code returns binary equivalent of a decimal number given by user

convert * into binary || binary of *
!javascript:Binary for $1$ = $!$
(+$1$).toString(2);
eol

Screenshot:

Ask SUSI to tell a quote

We can use external API’s for providing answer to user’s query. The external API used for telling the quote is Quotes Rest API created by They Said So. It offers a very good quotes platform and also the quotes are not repeated when several requests are made continuously.

We need a query for our skill.

Query: Tell me a quote.

Now let’s say that we use this JSON response for fetching quote data.

Our JSON object looks like:

{
 success: {
  total: 1
 },
contents: {
quotes: [
{
quote: "Let our advance worrying become advance thinking and planning.",
length: "62",
author: "Winston Churchill",
tags: [
"anxiety",
"inspire",
"planning",
"time-management"
],
category: "inspire",
date: "2017-05-31",
permalink: "https://theysaidso.com/quote/o3aMOSUwOPqUnJv9YyfYHweF/winston-churchill-let-our-advance-worrying-become-advance-thinking-and-planning",
title: "Inspiring Quote of the day",
background: "https://theysaidso.com/img/bgs/man_on_the_mountain.jpg",
id: "o3aMOSUwOPqUnJv9YyfYHweF"
}
],
copyright: "2017-19 theysaidso.com"
}
}

Now we are interested in getting value corresponding to  quotes key, which takes the path:  “path”:”$.contents.quotes“.

We make output query as follows:

!console:$quote$
     {
        "url" : "http://quotes.rest/qod.json",
        "path":"$.contents.quotes"
      }
eol

Explanation :

“!console” prints the output and this is returned by SUSI.AI.

$quote$” is the key whose value will be printed from parsed JSON.It contains our quote and this is a random quote. This is the string which will be responded by SUSI.AI

“url” generated JSON response from external API.

“path” is parsing path of JSON object which we follow for getting correct response.

Our skill will look like:

tell me a quote
!console:$quote$
     {
        "url" : "http://quotes.rest/qod.json",
        "path":"$.contents.quotes"
      }
eol

Screenshot :

Resources:

Continue Reading Teaching Susi

Reset SUSI.AI User Password & Parameter extraction from Link

In this blog I will discuss how does Accounts handle the incoming request to reset the password. If a user forgets his/her password, They can use forgot password button on http://accounts.susi.ai and It’s implementation is quite straightforward. As soon as a user enter his/her e-mail id and hits RESET button, an ajax call is made which first checks if the user email id is registered with SUSI.AI or not. If not, a failure message is thrown to user. But if the user is found to be registered in the database, An email is sent to him/her which contains a 30 characters long token. On the server token is hashed against the user’s email id and a validity of 7 days is set to it.
Let us have a look at the Reset Password link one receives.

http://accounts.susi.ai/?token={30 characters long token}

On clicking this link, what it does is that user is redirected to http://accounts.susi.ai with token as a request parameter. At the client side, A search is made which evaluates whether the URL has a token parameter or not. This was the overview. Since, http://accounts.susi.ai is based on ReactJS framework, it is not easy alike the native php functionality, but much more logical and systematic. Let us now take a closer look at how this parameter is searched for, token extracted and validated.

As you can see http://accounts.susi.ai and http://accounts.susi.ai/?token={token}, Both redirect the user to the same URL. So the first task that needs to be accomplished is check if a parameter is passed in the URL or not.
First import addUrlProps and UrlQueryParamTypes from react-url-query package and PropTypes from prop-types package. These will be required in further steps.
Have a look at the code and then we will understand it’s working.

const urlPropsQueryConfig = {
  token: { type: UrlQueryParamTypes.string },
};

class Login extends Component {
	static propTypes = {
    // URL props are automatically decoded and passed in based on the config
    token: PropTypes.string,
    // change handlers are automatically generated when given a config.
    // By default they update that single query parameter and maintain existing
    // values in the other parameters.
    onChangeToken: PropTypes.func,
  }

  static defaultProps = {
    token: "null",
  }

Above in the first step, we have defined by what parameter should the Reset Password triggered. It means, if and only if the incoming parameter in the URL is token, we move to next step, otherwise normal http://accounts.susi.ai page should be rendered. Also we have defined the data type of the token parameter to be UrlQueryParamTypes.string.
PropTypes are attributes in ReactJS similar to tags in HTML. So again, we have defined the data type. onChangeToken is a custom attribute which is fired whenever token parameter is modified. To declare default values, we have used defaultProps function. If token is not passed in the URL, by default it makes it null. This is still not the last step. Till now we have not yet checked if token is there or not. This is done in the componentDidMount function. componentDidMount is a function which gets called before the client renders the page. In this function, we will check that if the value of token is not equal to null, then a value must have been passed to it. If the value of token is not equal to null, it extracts the token from the URL and redirects user to reset password application. Below is the code snippet for better understanding.

componentDidMount() {
		const {
      token
    } = this.props;
		console.log(token)
		if(token !== "null") {
			this.props.history.push('/resetpass/?token='+token);
		}
		if(cookies.get('loggedIn')) {
			this.props.history.push('/userhome', { showLogin: false });
		}
	}

With this the first step of the process has been achieved. Again in the second step, we extract the token from the URL in the similar above fashion followed by an ajax request to the server which will validate the token and sends response to client accordingly {token might be invalid or expired}. Success is encoded in a JSON object and sent to client. Error is thrown as an error only and client shows an error of “invalid token”. If it is a success, it sends email id of the user against which the token was hashed and the client displays it on the page. Now user has to enter new password and confirm it. Client also tests whether both the fields have same values or not. If they are same, a simple ajax call is made to server with new password and the email id and token. If no error is caused in between (like connection timeout, server might be down etc), client is sent a JSON response again with “accepted” flag = true and message as “Your password has been changed!”.

Additional Resources:

(npmjs – official documentation of create-react-apps)

(Fortnox developer’s blog post)

(Dave Ceddia’s blog post for new ReactJS developers)

Continue Reading Reset SUSI.AI User Password & Parameter extraction from Link

Download SUSI.AI Setting Files from Data Folder

In this blog, I will discuss how the DownloadDataSettings servlet hosted on SUSI server functions. This post also covers a step by step demonstration on how to use this feature if you have hosted your own custom SUSI server and have admin rights to it. Given below is the endpoint where the request to download a particular file has to be made.

/data/settings

For systematic functionality and workflow, Users with admin login, are given a special access. This allows them to download the settings files and go through them easily when needed. There are various files which have email ids of registered users (accounting.json), user roles associated to them (authorization.json), groups they are a part of (groups.json) etc. To list all the files in the folder, use the given below end point:

/aaa/listSettings.json

How does the above servlet works? Prior to that, let us see how to to get admin rights on your custom SUSI.AI server.
For admin login, it is required that you have access to files and folders on server. Signup with an account and browse to

/data/settings/authorization.json

Find the email id with which you signed up for admin login and change userRole to “admin”. For example,

{
	"email:[email protected]": {
		"permissions": {},
		"userRole": "user"
	}
}

If you have signed up with an email id “[email protected]” and want to give admin access to it, modify the userRole to “admin”. See below.

{
	"email:[email protected]": {
		"permissions": {},
		"userRole": "admin"
	}
}

Till now, server did not have any email id with admin login or user role equal to admin. Hence, this exercise is required only for the first admin. Later admins can use changeUserRole application and give/change/modify user roles for any of the users registered. By now you must have admin login session. Let’s see now how the download and file listing servlets work.
First, the server creates a path by locally referencing settings folder with the help of DAO.data_dir.getPath(). This will give a string path to the data directory containing all the data-settings files. Now the server just has to make a JSONArray and has to pass a String array to JSONArray’s constructor, which will eventually be containing the name of all the data/settings files. If the process is not successfull ,then, “accepted” = false will be sent as an error to the user. The base user role to access the servlet is ADMIN as only admins are allowed to download data/setting files,
The file name which you have to download has to be sent in a HTTP request as a get parameter. For example, if an admin has to download accounting.json to get the list of all the registered users, the request is to be made in the following way:

BASE_URL+/data/settings?file=file_name

*BASE_URL is the URL where the server is hosted. For standard server, use BASE_URL = http://api.susi.ai.

In the initial steps, Server generates a path to data/settings folder and finds the file, name of which it receives in the request. If no filename is specified in the API call, by default, the server sends accounting.json file.

File settings = new File(DAO.data_dir.getPath()+"/settings");
String filePath = settings.getPath(); 
String fileName = post.get("file","accounting"); 
filePath += "/"+fileName+".json";

Next, the server will extract the file and using ServletOutputStream class, it will generate properties for it and set appropriate context for it. This context will, in turn, fetch the mime type for the file generated. If the mime type is returned as null, by default, mime type for the file will be set to application/octet-stream. For more information on mime type, please look at the following link. A complete list of mime types is compiled and documented here.

response.setContentType(mimetype);
response.setContentLength((int)file.length());

In the above code snippet, mime type and length of the file being downloaded is set. Next, we set the headers for the download response and use filename for that.

response.setHeader("Content-Disposition", "attachment; filename=" + fileName +".json");

All the manual work is done by now. The only thing left is to open a buffer stream, size of which has been defined as a class variable.
Here we use a byte array of size 4096 elements and write the file to client’s default download storage.

private static final int BUFSIZE = 4096;
byte[] byteBuffer = new byte[BUFSIZE];
             DataInputStream in = new DataInputStream(new FileInputStream(file));
            while ((in != null) && ((length = in.read(byteBuffer)) != -1))
            {
                outStream.write(byteBuffer,0,length);
            }

            in.close();
            outStream.close();

All the above-mentioned steps are enclosed in a try-catch block, which catches an exception if any ,and logs it in the log file. This message is also sent to the client for appropriate user information along with the success or failure indication through a boolean flag. Do not forget to close the input and output buffers as it may lead to memory leaks and someone with proper knowledge of network and buffer stream would be able to steal any essential or secured data.

Additional Resources

Continue Reading Download SUSI.AI Setting Files from Data Folder

Password Recovery Link Generation in SUSI with JSON

In this blog, I will discuss how the SUSI server processes requests using JSON when a request for password recovery is made.. The blog post will also cover some parts of the client side implementation as well for better insight.
All the clients function in the same way. When you click on forget password button, client asks you that whether you want to recover password for your own custom server or the standard one. This choice of user defines the base URL where to make the forget password request. If you select custom server radio button, then you will be asked to enter the URL to your server, Otherwise standard base URL will be used. The API endpoint used will be

/aaa/recoverpassword.json

The client will make a POST request with “forgotemail” parameter containing the email id of the user making the request. Following the email id provided, server generates a client identity if and only if the email id is registered. If email id is not found in the database, server throws an exception with error code 422.

String usermail = call.get("forgotemail", null);
ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, usermail);
ClientIdentity identity = new ClientIdentity(ClientIdentity.Type.email, credential.getName());
if (!DAO.hasAuthentication(credential)) {
	throw new APIException(422, "email does not exist");
}

If the email id is found to be registered against a valid user in the database, call to a method is made which returns a random string of length passed in as a parameter. This returned random string acts as a token.
Below is the implementation of the createRandomString(int length) method.

public static String createRandomString(Integer length){
    	char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
    	StringBuilder sb = new StringBuilder();
    	Random random = new Random();
    	for (int i = 0; i < length; i++) {
    	    char c = chars[random.nextInt(chars.length)];
    	    sb.append(c);
    	}
    	return sb.toString();
    }

This method is defined in AbsractAPIHandler class. The function createRandomString(int length) initialises an array with alphabet (both upper and lower cases) and integers 1 to 10. An object of StringBuilder is declared and initialised in the fol loop.
Next, the token generated is hashed against the user’s email id. Since we have used a token of length 30, there will be 30! (30 factorial) combinations and hence chances of two tokens to be same is ver very low (approximately Zero). Validity for token is set for 7 days (i.e. one week). After that the token will expire and to reset the password a new token will be needed.

String token = createRandomString(30);
		ClientCredential tokenkey = new ClientCredential(ClientCredential.Type.resetpass_token, token);
		Authentication resetauth = new Authentication(tokenkey, DAO.passwordreset);
		resetauth.setIdentity(identity);
		resetauth.setExpireTime(7 * 24 * 60 * 60);
		resetauth.put("one_time", true);

Everything is set by now. Only thing left is send a mail to the user. For that we call a method sendEmail() of EmailHandler class. This requires 3 parameters. User email id, subject for the email, and the body of the email. The body contains a verification link. To get this verification link, getVerificationMailContent(String token) is called and token generated in the previous step is sent to it as a parameter.

String verificationLink = DAO.getConfig("host.url", "http://127.0.0.1:9000") + "/apps/resetpass/index.html?token=" + token;

The above command gets the base URL for the server and appends the link to reset password app along with the token it received in the method call. Rest of the body is saved as a template in /conf//templates/reset-mail.txt file. Finally, if no exception was catched during the process, the message “Recovery email sent to your email ID. Please check” and accepted = true is encoded into JSON data and sent to the client. If some exceptions was encountered, The exception message and accepted = false is sent to client.
Now the client processes the JSON object and notifies the user appropriately.

Additional Resources

Continue Reading Password Recovery Link Generation in SUSI with JSON

How to Implement Memory like Servlets in SUSI.AI

In this blog, I’ll be discussing about how a server gets previous messages from the Log files in SUSI server. SUSI AI clients, Android, iOS and web chat, follow a very simple rule. Whenever a user logs in to the app, the app makes a http GET call to the server in the background and in response, server returns the chat history.

Link to the API endpoint -> http://api.susi.ai/susi/memory.json

But parsing a lot of data might depend on the connection speed. If the connection is poor or lacking speed, the history would cost user’s time. To prevent this, server by default returns last 10 pair of messages. It is up to the client that how many messages they want to render. So for example, if the client requests last 5 messages, then the client has to make a GET request and pass the cognitions parameter. Hence the modified end point will be :

http://api.susi.ai/susi/memory.json?cognitions=2

But how does the server process it? Let us see.
Browse to susi_server/src/ai/susi/server/api/susi/UserService.java file. This is the main working servlet. If you are new and wondering how servlets for susi are implemented, Please go through this first how-to-add-a-new-servletapi-to-susi-server
This is how serviceImpl() method looks like :

@Override
    public ServiceResponse serviceImpl(Query post, HttpServletResponse response, Authorization user, final JsonObjectWithDefault permissions) throws APIException {

        int cognitionsCount = Math.min(10, post.get("cognitions", 10));
        String client = user.getIdentity().getClient();
        List<SusiCognition> cognitions = DAO.susi.getMemories().getCognitions(client);
        JSONArray coga = new JSONArray();
        for (SusiCognition cognition: cognitions) {
            coga.put(cognition.getJSON());
            if (--cognitionsCount <= 0) break;
        }
        JSONObject json = new JSONObject(true);
        json.put("cognitions", coga);
        return new ServiceResponse(json);
    }

In the first step, we find the minimum of default value (i.e. 10) and the value of cognitions received as GET parameter. Messages equivalent to minimum variable are encoded in JSONArray and sent to the client.

Whenever the server receives a valid signup request, It makes a directory with the name “email_emailid”. In this directory, a log.txt file is maintained which stores all the queries along with the other details associated with it. For example if user has signed up with the email id [email protected], Then the path of this directory will be /data/susi/[email protected]. If a user queries “http://api.susi.ai/susi/chat.json?timezoneOffset=-330&q=flip+a+coin”,  then

{
	"query": "flip a coin",
	"count": 1,
	"client_id": "",
	"query_date": "2017-06-30T12:22:05.918Z",
	"answers": [{
		"data": [{
			"0": "flip a coin",
			"token_original": "coin",
			"token_canonical": "coin",
			"token_categorized": "coin",
			"timezoneOffset": "-330",
			"answer": "tails",

			"skill": "/susi_skill_data/models/general/entertainment/en/flip_coin.txt",
			"_etherpad_dream": "cricket"
		},
		"metadata": {
			"count": 1
		},
		"actions": [{
			"type": "answer",
			"expression": "tails"
		}],
		"skills": ["/susi_skill_data/models/general/entertainment/en/flip_coin.txt"]
	}],
	"answer_date": "2017-06-30T12:22:05.928Z",
	"answer_time": 10,
	"language": "en"
}

The server has user’s identity. It will use this identity and store (will be appended) in the respective log file.

The next steps in retrieving the message are pretty easy and includes getting the identity of the current user session. Use this identity to populate the JSONArray named coga. This is finally encoded in a JSONObject along with other basic details and returned to the clients where they render the data received, and show the messages in an appropriate way.

Resources

Continue Reading How to Implement Memory like Servlets in SUSI.AI

Implementation of Responsive SUSI Web Chat Search Bar

When we were building the first phase of the SUSI Web Chat Application we didn’t consider about  the responsiveness as a main goal. The main thing we needed was a working application. This changed at a later stage. In this post I’m going to emphasize how we implemented the responsive design and problems we met while we were developing the design.

When we were moving to Material-UI from static HTML CSS we were able to make most of the parts responsive. As an example App-bar of the application. We added App-bar like as follows: We made a separate component for App-bar and it includes the “searchfield” element. Material-UI app bar handles the responsiveness for some extent. We have to handle responsiveness of other sub-parts of the app bar manually.

In “TopBar.react.js” I returned marterial-ui <Toolbar> element like this.

<Toolbar>
                <ToolbarGroup >
                </ToolbarGroup>
                <ToolbarGroup lastChild={true}> //inside of this we have to include other components of the top bar inside this element

                </ToolbarGroup>
             </Toolbar>

We have to add the search button inside the element.
In this we added search component.

This field has the ability to expand and collapse like this.

It looks good. But it appears on mobile screen in a different way. This is how it appears on mobile devices.

So we wanted to hide the SUSI logo on small sized screens. For that we wrote medial queries like this.

@media only screen and (max-width: 860px){
 .app-bar-search{
   background-image: none;
 }
 .search{
   width: 100px !important;
 }
}

Even in smaller screens it appears like this.

To avoid that we minimized the width of the search bar in different screen sizes using media queries .

@media only screen and (max-width: 480px){
 .search{
   width: 100px !important;
 }
}
@media only screen and (max-width: 360px){
 .search{
   width: 65px !important;
 }
}

But in even smaller screens it still appears in the same way. We can’t show the search bar on small screens because the screen size is not enough to show the search bar.
So we wrote another media query to hide all the elements of search component in small screens except close button. Because when we collapse the screen on search mode it hides all the search components and messagecomposer. To take it back to the chat mode we have to enable the close button on smaller screens.

@media only screen and (max-width: 300px){
 .displayNone{
   display: none !important;
 }
 .displayCloseNone{
     position: relative;
     top:6px  !important;
 }
}

We have to define these two classes in “SearchField.react.js” file.

<IconButton className='displayNone'
                   <SearchIcon />
               </IconButton>
               <TextField  name='search'
                   className='search displayNone'
                   placeholder="Search..."/>
               <IconButton className='displayNone'>
                   <UpIcon />
               </IconButton>
               <IconButton className='displayNone'>
                   <DownIcon />
               </IconButton>
               <IconButton className='displayCloseNone'>
                   <ExitIcon />
               </IconButton>

Since we have “Codacy” integrated into our Github Repository we have to change Codacy rules because we used “!important” in media queries to override inline style which comes from Material-UI.
To change codacy rules we can simply login to the codacy and select the “code pattern ” button from the column left side.

It shows the list of rules that Codacy checks. And you can see the “!important” rule under CSSlint category like this.

Just uncheck it. Then codacy will not check your source code for “!important” attributes.

Resources
Configuring Codacy: Use Your Own Conventions: https://blog.codacy.com/configuring-codacy-use-your-own-conventions-9272bee5dcdb
Media queries: https://www.w3schools.com/css/css_rwd_mediaqueries.asp

Continue Reading Implementation of Responsive SUSI Web Chat Search Bar