Displaying skill rating for each skill on skill page of SUSI SKILL CMS

SUSI exhibits several skills which are managed by the SUSI Skill CMS, it essentially is a client which allows users to create/update skills conveniently since for each skill it is important to have the functionality of rating system so developers can get to know which skills are performing better than the rest and consequently improve them, thus a skill rating system which allows the users to give positive or negative feedback for each skill is implemented on the server.

Fetching skill_rating from the server

  1. Fetch skill data for which ratings are to be displayed through ajax calls
    API Endpoint –

    /cms/getSkillMetadata.json?
    

  2. Parse the received metadata object to get positive and negative ratings for that skill
  3. if(skillData.skill_rating) {
           	let positive_rating = skillData.skill_rating.positive;
            	let negative_rating = skillData.skill_rating.negative;
    }
    

    Sample API response

    {
      "skill_metadata": {
        "model": "general",
        "group": "Knowledge",
        "language": "en",
        "developer_privacy_policy": null,
        "descriptions": "Want to know about fossasia, just ask susi to tell that, Susi tells about the SUSI.AI creators",
        "image": "images/creator_info.png",
        "author": "madhav rathi",
        "author_url": "https://github.com/madhavrathi",
        "author_email": null,
        "skill_name": "Creator Info",
        "terms_of_use": null,
        "dynamic_content": false,
        "examples": [
          "Who created you?",
          "what is fossasia?"
        ],
        "skill_rating": {
          "negative": "0",
          "positive": "0",
          "stars": {
            "one_star": 0,
            "four_star": 0,
            "five_star": 0,
            "total_star": 0,
            "three_star": 0,
            "avg_star": 0,
            "two_star": 0
          },
          "feedback_count": 0
        },
        "creationTime": "2018-03-17T16:38:29Z",
        "lastAccessTime": "2018-06-15T15:51:50Z",
        "lastModifiedTime": "2018-03-17T16:38:29Z"
      },
      "accepted": true,
      "message": "Success: Fetched Skill's Metadata",
      "session": {"identity": {
        "type": "host",
        "name": "162.158.166.37_d80fb5c9",
        "anonymous": true
      }}
    }
    

  4. Set the react state of the component to store positive and negative rating.
  5. this.setState({
      positive_rating,
      negative_rating
    })
    

  6. Use react-icons to fetch like and dislike icon components from font-awesome.
  7. npm i -S react-icons
    

  8. Import the corresponding icons in the SkillPage component
  9. import { FaThumbsOUp, FaThumbsODown } from 'react-icons/lib/fa/'
    

  10. Display the rating count along with their icons
  11. <div className="rating">
        <div className="positive">
             <FaThumbsOUp />
             {this.state.positive_rating}
         </div>
           <div className="negative">
                 <FaThumbsODown />
                 {this.state.negative_rating}
             </div>
    </div>
    

Example

References

Continue ReadingDisplaying skill rating for each skill on skill page of SUSI SKILL CMS

SUSI AI 5 Star Skill Rating System

For making a system more reliable and robust, continuous evaluation is quite important. So is in 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 server side implementation

A new java class has been created for the API, FiveStarRateSkillService.java.

public class FiveStarRateSkillService extends AbstractAPIHandler implements APIHandler {
    private static final long serialVersionUID =7947060716231250102L;
    @Override
    public BaseUserRole getMinimalBaseUserRole() {
        return BaseUserRole.USER;
    }
    @Override
    public JSONObject getDefaultPermissions(BaseUserRole baseUserRole) {
        return null;
    }
    @Override
    public String getAPIPath() {
        return "/cms/rateSkill.json";
    }
...
}

The getMinimalBaseRole method tells the minimum User role required to access this servlet it can also be ADMIN, USER or ANONYMOUS. In our case it is USER. A user needs to be logged in to rate a skill on a scale of 1-5 stars.  The API runs at “/cms/fiveStarRateSkill.json” endpoint.

Next, create serviceImpl method in the above class to handle the request from the client and respond to it.

1. Fetch the required query parameters and store them in variables. They include skill model, group, language, skill name and starts that the user has given in the rating.

String skill_name = call.get("skill", null);
String skill_stars = call.get("stars", null);

2. Then check if the skill exists. If not them throw an exception. Otherwise, increment the count of the corresponding rating. The rating object has keys as one_star, two_star, three_star, four_star and five_star that has the count of that star rating.       

if (skill_stars.equals("1")) {
    skillName.put("one_star", skillName.getInt("one_star") + 1 + "");
}
else if (skill_stars.equals("2")) {
    skillName.put("two_star", skillName.getInt("two_star") + 1 + "");
}
else if (skill_stars.equals("3")) {
    skillName.put("three_star", skillName.getInt("three_star") + 1 + "");
}
else if (skill_stars.equals("4")) {
    skillName.put("four_star", skillName.getInt("four_star") + 1 + "");
}
else if (skill_stars.equals("5")) {
    skillName.put("five_star", skillName.getInt("five_star") + 1 + "");
}

3. Re-calculate the total rating done on that skill and its average rating and update the object. If the skill has not been already rated then create a new rating object and initialize it with the 0 star counts.

public JSONObject createRatingObject(String skill_stars) {
        JSONObject skillName = new JSONObject();
        JSONObject skillStars = new JSONObject();

        skillStars.put("one_star", 0);
        skillStars.put("two_star", 0);
        skillStars.put("three_star", 0);
        skillStars.put("four_star", 0);
        skillStars.put("five_star", 0);
        skillStars.put("avg_star", 0);
        skillStars.put("total_star", 0);

        skillName.put("stars", skillStars);
}

The complete FiveStarRateSkillService.java is available here : –

https://github.com/fossasia/susi_server/blob/development/src/ai/susi/server/api/cms/FiveStarRateSkillService.java

Rating a skill

Sample endpoint

https://api.susi.ai/cms/fiveStarRateSkill.json?model=general&group=Knowledge&language=en&skill=aboutsusi&stars=3&callback=p&_=1526813916145

This gives 3 star rating to the “aboutsusi” skill.

Parameters

  • Model
  • Group
  • Language
  • Skill
  • Stars

Response

{
  "ratings": {
    "one_star": 0,
    "four_star": 0,
    "five_star": 1,
    "total_star": 1,
    "three_star": 0,
    "avg_star": 5,
    "two_star": 0
  },
  "session": {"identity": {
    "type": "email",
    "name": "1anuppanwar@gmail.com",
    "anonymous": false
  }},
  "accepted": true,
  "message": "Skill ratings updated"
}

Getting the stats of Skill Ratings

Sample endpoint

http://127.0.0.1:4000/cms/getSkillRating.json?model=general&group=Knowledge&language=en&skill=aboutsusi&callback=p&_=1526813916145

This fetches the current ratings of the “aboutsusi” skill.

Parameters

  • Model
  • Group
  • Language
  • Skill

Response

{
    "session": {
        "identity": {
            "type": "host",
            "name": "172.68.144.159_81c88a10",
            "anonymous": true
        }
    },
    "skill_name": "aboutsusi",
    "accepted": true,
    "message": "Skill ratings fetched",
    "skill_rating": {
        "negative": "0",
        "positive": "0",
        "stars": {
            "one_star": 0,
            "four_star": 2,
            "five_star": 1,
            "total_star": 4,
            "three_star": 1,
            "avg_star": 4,
            "two_star": 0
        },
        "feedback_count": 3
    }
}

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 ReadingSUSI AI 5 Star Skill Rating System

Use of Flux Architecture in SUSI.AI

SUSI Web Chat is based on Flux, a React.js Architecture. It provides a simple way for implementing many features in SUSI Web Chat. This blog post explains how Flux Architecture works, and also how it is used in SUSI Web Chat to implement the feature of switching between Light and Dark themes.

What is Flux?

Flux is a micro-architecture for software application development. It is the application architecture that Facebook uses for building client-side web applications. It complements React’s composable view components by utilizing a unidirectional data flow.

Flux Overview

As can be seen from the above diagram, Flux has four main components:

Component Description
Actions Helper methods that pass data to the Dispatcher.
Dispatcher Receives these Actions and broadcast payloads to registered callbacks.
Stores The Stores that are registered to the Dispatcher will update the Views accordingly.
Views  Views are actually React Components that grab the state from the stores and then pass it down to the child components.

How these 4 components work together?

  • Application Initialisation:
    1. Stores let the registered Dispatcher know that they want to be notified whenever an action comes in.
    2. Then the controller views ask the stores for the latest state.
    3. When the stores give the state to the controller views, they pass that state along to their child views to render.
    4. The controller views also ask the stores to keep them notified when state changes so that they can re-render accordingly.
  • The Data Flow:

Once the setup is done, the application is ready to accept user input. So let us trigger an action by having the user make a change.

    1. The view tells the action creator to prepare an action.
    2. The action creator formats the action and sends it off to the Dispatcher.
    3. The Dispatcher dispatches the action off to all of the stores in sequence. Then the store decides whether it cares about this action or not, and changes the state accordingly.
    4. Once it’s done changing state, the store lets its subscribed view controllers know.
    5. Those view controllers will then ask the store to give them the updated state.
    6. After the store gives it the state, the view controller will tell its child views to re-render based on the new state.

So this is how Flux works. Now let us see an example of Flux being used in SUSI Web Chat.

Use of Flux Architecture to Switch Between Themes in SUSI Web Chat

Different Flux components used in SUSI Web Chat:

  1. Actions – Settings.actions.js
  2. Dispatcher – ChatAppDispatcher.js
  3. Store – UserPreferencesStore.js
  4. View – MessageSection.react.js

Let us now see in detail how we are able to switch themes:
In the MessageSection.react.js file, we have the following functions to handle theme change:

handleThemeChanger = () => {
    //code
    this.applyLightTheme();
}

applyLightTheme = () =>{
    //code
    Actions.settingsChanged(settingsChanged);
}

 
Hence, the view tells the action creator to prepare an action.
Now, the settingsChanged() function in Settings.actions.js file is called as follows:

export function settingsChanged(settings) {
    ChatAppDispatcher.dispatch({
    type: ActionTypes.SETTINGS_CHANGED,
    settings
});
    //code
}

 
Hence, the process of invoking the callbacks is initiated through the dispatch() method, which takes a data payload object as its sole argument.

Now, as the UserPreferencesStore is registered with the ChatAppDispatcher, it receives the actions dispatched by the Dispatcher. It checks for the type of action, and with the help of switch cases, executes the code for the corresponding action type accordingly. In this case, the states inside the Store are updated with the new state which the User wants to switch to.

UserPreferencesStore.dispatchToken = ChatAppDispatcher.register(action => {
    switch (action.type) {
        //code
        case ActionTypes.SETTINGS_CHANGED: {
            let settings = action.settings;
            //code
            UserPreferencesStore.emitChange();
            break;
        }
        //code
    }
});

 
This is how Flux Architecture is facilitating the switching of themes in SUSI Web Chat.

Resources

 

Continue ReadingUse of Flux Architecture in SUSI.AI

How search works on Susi Skill CMS

The SUSI Skill CMS is a central dashboard where the developers can add skills to make SUSI more intelligent. One of the major thing that was missing there was a search bar. So I recently added one that can search a skill based on:

  • Skill name
  • Skill description
  • Author name
  • Examples of the skill

How to add the search bar?

  1. Install the Material Search Bar component from the terminal.

npm i --save material-ui-search-bar

 

  1. Import this component in the BrowseSkill.js file.

import SearchBar from 'material-ui-search-bar'

 

  1. Create a state variables for the search query and initialize it to an empty string.

this.state = {
	...
	searchQuery:''
	...
};

 

  1. Add the search bar (UI Part) below the filters on CMS. Add a listener function to it that is called when the value of the search query changes.

<SearchBar
     onChange={this.handleSearch}
     value={this.state.searchQuery}
   />

 

  1. The search handler : Create a handler (handleSearch) to that listens to onChange events on the SearchBar. This function sets the value of searhQuery state variable and loads the card again based on the search filter.

handleSearch
  = (value) => {
    this.setState({searchQuery: value}, function () {
    this.loadCards();
    });
};

 

  1. The loadCards() function : The loadCards() function send a request to the Susi Server which in turn send a json response. Then this function makes cards for every skill and adds them to the CMS. Modify the loadCards() function to filter the cards array based on the search query. The javascript string function match() is used to check if the skill name, description, author’s name or examples match the search query. The filter() function adds the skill card to the filtered data if the match() function returns true (i.e., the skill is relevant to the search query).

How the filter works?

  1. First check if there’s something to search for. If the searchQuery.length is equal to zero then that means that there is nothing to search for.

self.state.searchQuery.length >0

 

  1. Then filter the related results based on
  • Skill name : The filter() function adds the skill card to the filtered data if the match function returns true (i.e., the skill is relevant to the search query). The match() function retuns true if the skill name matches the search query.

if (i.skill_name) {
    	result =  i.skill_name.toLowerCase()
        	.match( self.state.searchQuery.toLowerCase() );
    	if (result) {
        	return result;
    	}
}

 

Similarly, filter the cards on the basis of skill description and skill author’s name.

  • Skill examples: Loop over all the skill examples and check if any example matches the search query.

if (i.examples && i.examples.length>0) {
    	i.examples.map((el,j)=>{
      	result =  el.toLowerCase()
        	.match( self.state.searchQuery.toLowerCase() );
      	if (result) {
          	return result;
      	}
    	})
}

Example

Here the search query is “country”. The word “country” appears the skill description of the filtered cards.

Resources

Continue ReadingHow search works on Susi Skill CMS

Adding “All” in Skill Categories

The SUSI SKill CMS has various filters to explore the skills of interest.  For example skill category and skill language. The skills are stored in the susi_skill_data Github repo in the following structure:

susi_skill_data/models/<model_name>/<group_name>/<language_name>/<skill_name>.txt

NOTE: group and category are same terms and can be used interchangeably

So when a category filter is applied the skills from the corresponding directory are returned.

susi_skill_data/models/<model_name>/<group_name>/<language_name>/<skill_name>.txt

But there’s no directory called “All”,  so how to get skills of all groups? For this, we need to loop through all the directories present in the model.

Server side implementation

Create a helper function that returns a list of all the folders present in a directory. The function accepts the parent directory name and an empty list. First, fetch all the items (files and folders) present in that directory and store them in an array. Then apply a filter over the array to check if the element is a directory and doesn’t start with a dot(.) i.e., it’s not hidden. Add the filtered array to the list.

private void listFoldersForFolder(final File folder, ArrayList<String> fileList) {
    File[] filesInFolder = folder.listFiles();
    if (filesInFolder != null) {
        Arrays.stream(filesInFolder)
                .filter(fileEntry -> fileEntry.isDirectory() && !fileEntry.getName().startsWith("."))
                .forEach(fileEntry -> fileList.add(fileEntry.getName() + ""));
    }
}

Fetch the group name form the request and add a check if the CMS is asking for all skill. Otherwise, return the skills of a particular group only.

String group_name = call.get("group", "Knowledge");
if (group_name.equals("All")) {
  // Return the list of all skills
} else {
  // Return the list of a skills in a particular group only
}

To fetch the list of all skills, call the listFoldersForFolders() function with the model name and an empty list as arguments. The function adds all the directories, present in that model directory, to folderList.

File allGroup = new File(String.valueOf(model));
ArrayList<String> folderList = new ArrayList<String>();
listFoldersForFolder(allGroup, folderList);

Then loop over all the groups present in the list to get all the skills present in that group. This process is the same as the existing process of getting skills of a particular category. Just keep adding the skill list to a global array.

CMS side implementation

The list of categories is first fetched from the API and then added to the dropdown menu. Since the API doesn’t return “All” in it, so we need to push it to the list manually.

groups.push(<MenuItem
                value="All"
                key="All"
                primaryText="All" />);

References

Continue ReadingAdding “All” in Skill Categories

Holding query search in Loklak

Continuously searching user’s input while the user kept typing is not good. It has certain drawbacks which result in continuous action dispatch and loading of the result page which depicts the site to be slow, but actually it is not. To prevent continuous loading and searching while user types a query in Loklak Search, it was important to keep hold on query until user completes typing and then start searching for the results. I am writing this blog post to emphasise a proper solution to constantly keep track of the query and search on an appropriate event.

Adding keypress event

First and foremost task would be to add keypress event to input tag on home and results page.

<input     
     

     (keypress)=“onEnter($event)” 
     [formControl]=“searchInputControl

/>

 

formControl will constantly keep track of the user input and will be stored in searchInputControl, while keypress event will activate onEnter() method firing an $event as an input to onEnter() method.

Adding onEnter() method

Note: Keeping track of special keypress event code 13 which is equivalent for an enter key press.

Add a new method onEnter() with event as an input which will execute its function only when the event’s keypress code is 13 (Enter key press). One point should be noted here that query to be searched should not be empty or containing leading and trailing spaces or should not contain only spaces (treated as an incomplete query to be searched).

onEnter(event: any) {  
   if (event.which === 13) {  

      if (this.searchInputControl.value.trim() !== ”) {

        // Actual work of query search will go here.

  }

}

Add query searching and migration code under onEnter()

Now comes the last part to shift query search and route migration code from setupSearchField() to the portion mentioned above in comments. We need to take one point in consideration that from now on we actually do not need to subscribe to formControl in ngOnInit() since now it depends on user query input.

Complete onEnter() in home component file

After completion, onEnter() method should be similar to:

onEnter(event: any) {
   if (event.which === 13) {
       if (this._queryControl.value.trim() !== ”) {
           this.store.dispatch(new 
               queryAction.RelocationAfterQuerySetAction());
           this.store.dispatch(new suggestAction
               .SuggestAction(this._queryControl.value.trim()));
           this.store.dispatch(new queryAction
               .InputValueChangeAction(this._queryControl.value.trim()
           ));
           this.router.navigate([`/search`],
               { queryParams: { query: this._queryControl.value.trim() }
               , skipLocationChange: true } );
           this.store.dispatch(new titleAction
           .SetTitleAction(this._queryControl.value.trim()
               +   Loklak Search’));
           this.getDataFromStore();
       }
   }
}

 

Since we are not depending on ngOnInit() therefore we also need to append getDataFromStore() to onEnter() method.

Complete onEnter() in feed-header component file

After completion, onEnter() method should be similar to:

onEnter(event: any) {
   if (event.which === 13) {
       if (this.searchInputControl.value.trim() !== '') {
           this.searchEvent.emit(this.searchInputControl
               .value.trim());
           this.setupSuggestBoxClosing();
       }
   }
}

Testing

Try typing different formats of query on home and results page of loklak.

Resources

Continue ReadingHolding query search in Loklak

Displaying all the images from storage at once in Phimpme Android Application

In the Phimpme Android application, the images are displayed in the albums in which they are indexed in the device’s storage. However, there is also an “All photos” section in the application where all the images present in the device’s storage are displayed at once irrespective of the folder they’re indexed in. So in this post, I will be discussing how we achieved the “All Photos”  functionality.

Android framework provides developers with a media content provider class called MediaStore. It contains metadata for all available media on both internal and external storage devices. With the help of particular methods we can obtain metadata for all the images stored in the device’s storage.

Step 1

So First we need to get the Uri from MediaStore content provider pointing to the media(here media refers to the photos stored on the device). This can be done by the following snippet of code.

Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

Step 2

Now retrieving a cursor object containing the data column for the image media is required to be performed. The data column will contain the path to the particular image files on the disk. This can be done by querying the MediaColumns table of the MediaStore class, which can be performed by the use of the content resolver query method. The mentioned functionality can be achieved by the following lines of code.

String[] projection = {MediaStore.MediaColumns.DATA};
Cursor cursor = activity.getContentResolver().query(uri, projection, null, null, null);

Step 3

In the final step, we would retrieve the path of all the images by iterating through the cursor object obtained in the previous step and keep adding those paths to an ArrayList<String>. Creating Media objects passing in the image path and concurrently adding those Media objects to an ArrayList<Media> to be done thereafter. Finally, the dataset(ArrayList<Media> in this case) containing Media objects for all the images is required to be passed to the Media Adapter in order to display all the photos in the UI. The code snippets used for the final steps are provided below.

while (cursor.moveToNext()) {
  absolutePathOfImage = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
  listOfAllImages.add(absolutePathOfImage);
}
ArrayList<Media> list = new ArrayList<>();

for (String path : listOfAllImages) {
  list.add(new Media(new File(path)));
}
return list;

This is how we achieved the functionality to display all the images from the storage on a single screen(at once) in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

The screenshot for displaying the “All photos” section is provided below.

Resources

1.Android Developer Guide – https://developer.android.com/reference/android/provider/MediaStore

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Displaying all the images from gallery in android – https://deepshikhapuri.wordpress.com/2017/03/20/get-all-images-from-gallery-in-android-programmatically/

Continue ReadingDisplaying all the images from storage at once in Phimpme Android Application

Initial Setups for Connecting SUSI Smart Speaker with iPhone/iPad

You may have experienced Apple HomPad, Google Home, Alexa etc or read about smart speakers that offer interactive action over voice commands. The smart speaker uses the hot word for activation. They utilize Wi-Fi, Bluetooth, and other wireless protocols.

SUSI.AI is also coming with Open Source smart speaker that can do various actions like playing music etc over voice commands. To use SUSI Smart Speaker, you have to connect it to the SUSI iOS or Android App. You can manage your connected devices in SUSI iOS, Android and Web clients. Here we will see initial setups for connecting SUSI Smart Speaker with iPhone/iPad (iOS Devices).

You may aware that iOS does not allow connecting to wifi within the app. To connect to a particular Wi-Fi, you have to go to phone settings, from there you can connect to Wi-Fi. SUSI Smart Speaker create a temporary Hotspot for initial setups. Follow the instruction below to connect to SUSI Smart Speaker hotspot –

  1. Tap to Home button, and go to your iPhone Settings > Wi-Fi
  2. Connect to the Wi-Fi hotspot for the device that you are setting up. It will have name “susi.ai”, like in the image below
  3. Come back to the SUSI app to proceed with setup.

These instruction is also available within the app when you are not connected to SUSI Smart Speaker hotspot and click `Setup a Device` or plus icon on Device Activity screen navigation bar.

Devices Activity and getting current Wi-Fi SSID:

Devices section in Settings screen shows the currently connected device. In Devices Activity screen, the user can manage the connected device. Only a logged-in user can access Devices Activity. When the user clicks on Device Accessories in setting, if the user is not logged-in, an alert is prompted with Login option. By clicking Login option, user directed to Login screen where the user can log in and come back to device section to proceed further.

If the user is already logged-in, Device Activity screen is presented. We use following method to scan if iPhone/iPad is connected to SUSI Smart Speaker:

func fetchSSIDInfo() -> String? {
var ssid: String?
if let interfaces = CNCopySupportedInterfaces() as? [String] {
for interface in interfaces {
if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as CFString) as NSDictionary? {
ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
break
}
}
}
return ssid
}

Apple’s SystemConfiguration API is used to get current Wi-Fi SSID. SystemConfiguration Allow applications to access a device’s network configuration settings. Determine the reachability of the device, such as whether Wi-Fi or cell connectivity is active.

import SystemConfiguration.CaptiveNetwork

The method above return the SSID of your device current Wi-Fi. SSID is simply the technical term for a network name. When you set up a wireless home network, you give it a name to distinguish it from other networks in your neighborhood. You’ll see this name when you connect your device to your wireless network.

If current Wi-Fi match with SUSI Smart Speaker hotspot, we display device in TableView, if not we display “No device connected yet”.

if let speakerSSID = fetchSSIDInfo(), speakerSSID == "susi.ai" {
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = speakerSSID
} else {
cell.accessoryType = .none
cell.textLabel?.text = "No device connected yet"
}

SUSI Smart Speaker is coming with very exciting features. Stay tuned.

Resources –

  1. SUSI iOS Link: https://github.com/fossasia/susi_iOS
  2. Apple’s SystemConfiguration Framework Documentation
  3. Bell’s article on What Do SSID and WPA2 mean
Continue ReadingInitial Setups for Connecting SUSI Smart Speaker with iPhone/iPad

Creating the Mockup for Wave Generator using Moqups

PSLab is a versatile device that can be used as a waveform signal generator, as it is capable of producing different types of waves. It provides a cheap and portable alternative as compared to the physical wave generators available in markets.

The current UI of the Wave Generator present in the PSLab app as shown in Figure 1 is very basic and makes use of some basic UI components which are not able to properly expose all the functionalities of the wave generator. This affects the user experience negatively.

Figure 1 shows the current layout of Wave Generator in PSLab android app

In this blog, we will discuss creating a mockup for the PSLab wave generator using modern design to improve the UI/UX of the app. I will try to keep the layout simple enough so that users can easily understand it. 

Understanding the Functionality of Waveform Generator

Figure 2 shows the schematic pin diagram of the PSLab

We can observe from the image that PSLab provide:

  • Two pins named W1 and W2 which are capable of producing two different sine or saw-tooth waves having different characteristics simultaneously.
  • Four Pins named SQ1, SQ2, SQ3, SQ4 which are capable of producing Square Wave and Pulse Width modulation signals.
  • It also allows us to change the properties of these waves such as frequency, phase, duty cycle.

Before starting with the mockup a physical commodity Waveform Signal Generator as shown in Figure 3 was inspected.

Figure 3 shows digital Wave Generator by HP

Most waveform generators in the market have a control panel with buttons and a monitor screen so that the user can view the changes while controlling the characteristics of the wave. I will try to create a layout similar to this in following steps by dividing it into two halves:

  1. Upper half containing monitor screens
  2. Lower half containing controlling panel having buttons

Steps for creating the mockup

Step 1: Choosing the Tool

The tool that we will be using here to create a mockup is moqups[1].

I have chosen this software because:-

  • It is an online tool and doesn’t require it to be set up first
  • It is very efficient and powerful tool for creating mobile or web wireframes.
  • It doesn’t require any prior knowledge of programming language.
  • It has a palette to drag & drop UI elements like Buttons, Textboxes, Checkboxes etc. on the left-hand side, allows modifying the features of each element (here on the right) and other options at the top related to prototyping, previewing etc as shown in figure 4.
Figure 4  shows Moqups prototyping tool webpage

Step 2: Creating the base of the layout

The mockup tool moqups[1] have a built-in template of Android screen which we can drag and drop from the palette and use it as our base.

Figure 5 shows the Android mobile template picked from the palette of available design elements in the ‘moqups’ application.

Step 3: Creating the Lower Half

Now as discussed above I will create controlling panel containing buttons in the lower half of the screen, for this I will drag and drop buttons from the palette on the right-hand side and design them according to the theme of the app that is making their border round shape, changing their color to match the theme color and editing text in them. As the PSLab has two groups of pins so I will create two different panels with buttons for properties related to the pins of that group.

At this point we have a layout looking like this:

Figure 6 shows the lower panel containing buttons being drawn for Wave Generator UI. At right-hand side, it shows the palette having different UI elements.

I will create the monitor screens in the upper half, for our purpose as I have two panels we will create two monitor screen as shown in Figure 6. The left panel will correspond to the left panel and the right monitor will correspond to the right panel.

Step 4: Creating the Upper Half

For creating monitor, I will use simple rectangles with rounded borders and give the background black to resemble the monitor in physical devices.

I will also create some partitions in the monitor to make compartment. I will use these compartments to position the text view that represents the characteristics of the wave so that they are clearly visible.

Figure 7 shows two monitor layout with the black background being drawn using text areas and lines

So, in this layout the user will be able to see all the properties corresponding to one type of waveform on the monitor together so he/she doesn’t have to click the buttons, again and again, to see the different properties as he/she have to do in a physical device.

For instance let’s say, if the user wants to generate sine wave he/she will have to click the Wave 1 button in the panel below and on clicking,all the values of characteristics related to waveform produced by W1 pin will be visible together to the user in one screen which makes it easier for the user to analyze and set the values.

Therefore, the final layout produced is shown in Figure 7

Figure 8 shows final Wave Generator Mockup.

As we can see this layout is quite interactive and looks very appealing to the user and this will help to improve UI/UX of the app.

Resources

  1. Moqups prototyping tool website: https://moqups.com
  2. Youtube Video – How to use moqups
Continue ReadingCreating the Mockup for Wave Generator using Moqups