Encoding and Decoding Images as Data in UserDefaults in SUSI iOS

In this blog post, I will be explaining how to encode and decode images and save them in UserDefaults so that the image persists even if it is removed from the Photos app. It happens a number of times that images are removed from the gallery by the users which results in the app loosing the image. So, to avoid this, we save the image by encoding it in a data object and save it inside UserDefaults. In SUSI iOS app we simply select an image from the image picker, encode it and save it in UserDefaults. To set the image, we simply fetch the image data from the UserDefaults and decode it to an image.

There are two ways we can do the encoding and decoding process:

  • Using Data object
  • Using Base64 string

For the scope of this tutorial, we will use the Data object.

Implementation Steps

  1. To use the image picker, we need to add permissions to `Info.plist` file.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Susi is requesting to get your current location</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Susi needs to request your gallery access to select wallpaper</string>
  1. Select image from gallery

First, we present an alert which gives an option to select the image from the gallery.

// Show wallpaper options to set wallpaper or clear wallpaper
func showWallpaperOptions() {
  let imageDialog = UIAlertController(title: ControllerConstants.wallpaperOptionsTitle, message: nil, preferredStyle: UIAlertControllerStyle.alert)
  imageDialog.addAction(UIAlertAction(title: ControllerConstants.wallpaperOptionsPickAction, style: .default, handler: { (_: UIAlertAction!) in
  imageDialog.dismiss(animated: true, completion: nil)
  self.showImagePicker()
  }))
  imageDialog.addAction(UIAlertAction(title: ControllerConstants.wallpaperOptionsNoWallpaperAction, style: .default, handler: { (_: UIAlertAction!) in
    imageDialog.dismiss(animated: true, completion: nil)
    self.removeWallpaperFromUserDefaults()
  }))
  imageDialog.addAction(UIAlertAction(title: ControllerConstants.dialogCancelAction, style: .cancel, handler: { (_: UIAlertAction!) in
    imageDialog.dismiss(animated: true, completion: nil)
  }))
  self.present(imageDialog, animated: true, completion: nil)
}

Here, we create and UIAlertController with three options to select, one which presents the image picker controller, the second one removes the background wallpaper and the third dismisses the alert.

  1. Set the image as background view
// Callback when image is selected from gallery
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
  dismiss(animated: true, completion: nil)
  let chosenImage = info[UIImagePickerControllerOriginalImage] as? UIImage
  if let image = chosenImage {
    setBackgroundImage(image: image)
  }
}

We use the `didFinishPickingMediaWithInfo` delegate method to set the image as background. First we get the image using the the `info` dictionary using the `UIImagePickerControllerOriginalImage` key.

  1. Save the image in UserDefaults (encoding)
// Save image selected by user to user defaults
func saveWallpaperInUserDefaults(image: UIImage!) {
  let imageData = UIImageJPEGRepresentation(image!, 1.0)
  let defaults = UserDefaults.standard
  defaults.set(imageData, forKey: userDefaultsWallpaperKey)
}

We first convert the image to a data object using the `UIImageJPEGRepresentation` method followed by saving the data object in UserDefaults with the key `wallpaper`.

  1. Decode the data object back to UIImage 

Now whenever we need to decode the image, we simply get the data object from the UserDefaults and use it to display the image.

// Check if user defaults have an image data saved else return nil/Any
func getWallpaperFromUserDefaults() -> Any? {
  let defaults = UserDefaults.standard
  return defaults.object(forKey: userDefaultsWallpaperKey)
}

Below is the output when an image is selected and displayed as a background.

Resources:

Continue ReadingEncoding and Decoding Images as Data in UserDefaults in SUSI iOS

Using The Dark and Light Theme in SUSI iOS

SUSI being an AI for interactive chat bots, provides answers to the users in the intelligent way. So, to make the SUSI iOS app more user friendly, the option of switching between themes was introduced. This also enables the user switch between themes based on the environment around. Any user can switch between the light and dark themes easily from the settings.

We start by declaring an enum called `theme` which contains two strings namely, dark and light.

enum theme: String {
    case light
    case dark
}

We can update the color scheme based on the theme selected very easily by checking the currently active theme and based on that check, we update the color scheme. To check the currently active theme, we define a variable in the `AppDelegate` which holds the value.

var activeTheme: String?

Below is the way the color scheme of the LoginViewController is set.

var activeTheme: String?func setupTheme() {
  let image = UIImage(named: ControllerConstants.susi)?.withRenderingMode(.alwaysTemplate)
  susiLogo.image = image
  susiLogo.tintColor = .white
  UIApplication.shared.statusBarStyle = .lightContent
  let activeTheme = AppDelegate().activeTheme
  if activeTheme == theme.light.rawValue {
    view.backgroundColor = UIColor.lightThemeBackground()
  } else if activeTheme == theme.dark.rawValue {
    view.backgroundColor = UIColor.darkThemeBackground()
  }
}

Here, we first get the image and set the rendering mode to `alwaysTemplate` so that we can change the tint color of the image. Next, we assign the image to the `IBOutlet` and change the tint color to `white`. We also change the status bar style to `lightContent`. Next, we check the active theme and change the view’s background color accordingly. For this method to execute, we call it inside, `viewDidLoad` so that the theme loads up as the view loads.

Next, lets add this option of switching between themes inside the `SettingsViewController`. We add a cell with `titleLabel` as `Change Theme` and use the collectionView’s delegate method of `didSelect` to show an alert. This alert contains three options, Dark theme, Light Theme and Cancel. Let’s code that method which presents the alert.

func themeToggleAlert() {
  let imageDialog = UIAlertController(title: ControllerConstants.toggleTheme, message: nil, preferredStyle: UIAlertControllerStyle.alert)
  imageDialog.addAction(UIAlertAction(title: theme.dark.rawValue.capitalized, style: .default, handler: { (_: UIAlertAction!) in
    imageDialog.dismiss(animated: true, completion: nil)
    AppDelegate().activeTheme = theme.dark.rawValue
    self.settingChanged(sender: self.imagePicker)
    self.setupTheme()
  }))
  imageDialog.addAction(UIAlertAction(title: theme.light.rawValue.capitalized, style: .default, handler: { (_: UIAlertAction!) in
    imageDialog.dismiss(animated: true, completion: nil)
    AppDelegate().activeTheme = theme.light.rawValue
    self.settingChanged(sender: self.imagePicker)
    self.setupTheme()
  }))
  imageDialog.addAction(UIAlertAction(title: ControllerConstants.dialogCancelAction, style: .cancel, handler: { (_: UIAlertAction!) in
    imageDialog.dismiss(animated: true, completion: nil)
  }))
  self.present(imageDialog, animated: true, completion: nil)
}

Here, we assign the alert view’s title and add 3 actions and their respective completion handlers. If we see inside these completion handlers, we can notice that we first dismiss the alert followed by updating the activeTheme variable in AppDelegate and call the `settingChanged` function which updates the user’s settings on the server. Finally, we update the color scheme.

Now, if we build and run the app and change the theme from the settings, we will notice that on returning to the chat view, the color scheme is not updated. The reason here is that we are setting up the theme on viewDidLoad which loads only once and is not executed until the controller is presented again. Here, we make use of the `viewDidAppear` method which executes every time the view appears.

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  setupTheme()
}

To persist the selected theme, we used the UserDefaults to save the theme which got assigned everytime to the `activeTheme` variable when the app loads up.

UserDefaults.standard.set(AppDelegate().activeTheme, forKey: ControllerConstants.UserDefaultsKeys.theme)

On app launch, we assigned this User Default the value of the light theme as a default.

Below is the final output:

References:

Continue ReadingUsing The Dark and Light Theme in SUSI iOS

Implementing Experiment Functionality in PSLab Android

Using the PSLab Hardware Device, users can perform experiments in various domains like Electronics, Electrical, Physics, School Level experiments, etc. These experiments can be performed using functionalities exposed by hardware device like Programmable Voltage Sources, Programmable Current Source, etc. In this post we will try implementing the functionality to perform an experiment using the PSLab Hardware Device and the PSLab Android App.

Let us take the Ohm’s law experiment as an example and see how it’s implement using the  PSLab Android App.

Ohm’s law states that the current through a conductor between two points is directly proportional to the voltage across the two points, effectively using a constant of proportionality called Resistance (R) where,

R = V / I

Schematic

Layout to perform Ohm’s law experiment

The Ohm’s law experiment requires a variable current, so a seekbar is provided to change the current coming from PCS channel, values of which are continuously reflected in the TextView next to it.

Implementation

The Read button has a listener attached to it. Once it is clicked, the currentValue is updated with the value parsed from the seekbar progress and the selectedChannel variable is assigned from the spinner. These variables are used by the background thread to change the current supplied by current source (PCS pin) of the device and to read the updated voltage from the selected channel of the device.

btnReadVoltage.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       selectedChannel = channelSelectSpinner.getSelectedItem().toString();
       currentValue = Double.parseDouble(tvCurrentValue.getText().toString());
       if (scienceLab.isConnected()) {
           CalcDataPoint calcDataPoint = new CalcDataPoint();
           calcDataPoint.execute();
       } else {
           Toast.makeText(getContext(), "Device not connected", Toast.LENGTH_SHORT).show();
       }
   }
});

CalcDataPoint is an AsyncTask which does all the underlying work like setting the current at the PCS channel, reading the voltage from the CH1 channel and triggering the update of the data points on the graph.

private class CalcDataPoint extends AsyncTask<Void, Void, Void> {

   @Override
   protected Void doInBackground(Void... params) {
       scienceLab.setPCS((float) currentValue);
       switch (selectedChannel) {
           case "CH1":
               voltageValue = scienceLab.getVoltage("CH1", 5);
               break;
           case "CH2":
               voltageValue = scienceLab.getVoltage("CH2", 5);
               break;
           case "CH3":
               voltageValue = scienceLab.getVoltage("CH3", 5);
               break;
           default:
               voltageValue = scienceLab.getVoltage("CH1", 5);
       }
       x.add((float) currentValue);
       y.add((float) voltageValue);
       return null;
   }

   @Override
   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       updateGraph();
   }
}

updateGraph() method is used to update the graph on UI thread. It creates a new dataset from the points which were added by the background thread and refreshes the graph with it using the invalidate() method.

private void updateGraph() {
   tvVoltageValue.setText(df.format(voltageValue));
   List<ILineDataSet> dataSets = new ArrayList<>();
   List<Entry> temp = new ArrayList<>();
   for (int i = 0; i < x.size(); i++) {
       temp.add(new Entry(x.get(i), y.get(i)));
   }
   LineDataSet dataSet = new LineDataSet(temp, "I-V Characteristic");
   dataSet.setColor(Color.RED);
   dataSet.setDrawValues(false);
   dataSets.add(dataSet);
   outputChart.setData(new LineData(dataSets));
   outputChart.invalidate();
}

Roadmap

We are planning to add an option to support multiple trials of the same experiment and save each trails for further reference. App flow to perform experiment is based on Science Journal app by Google.

Resources

  • Article on Ohm’s law and Power on electronics-tutorial
  • To know more about Voltage, Current, Resistance and Ohm’s law, head on to detailed tutorial on sparkfun
  • Implementation of perform experiment functionality in PSLab Desktop App
Continue ReadingImplementing Experiment Functionality in PSLab Android

Performing the Experiments Using the PSLab Android App

General laboratory experiments can be performed using core functionalities offered by the PSLab hardware device like Programmable Voltage Sources, Programmable Current Source, Analog to Digital Converter, Frequency Counter, Function Generators, etc. In this post we will  have a brief look on a general laboratory experiment and how we can perform it using the  PSLab Android App and the PSLab hardware device.

We are going to take Zener I-V Characteristics Curve experiment as an example to understand how we can perform a general experiment using the PSLab device. First, we will  look at the general laboratory experiment and it’s format. Then we will see how that experiment can be performed using the PSLab Android App and the PSLab Hardware Device.

Experiment Format of General Experiment in Laboratory

AIM: In this experiment, our aim is to observe the relation between the voltage and the corresponding current that was generated. We will then plot it to get the dependence.

Apparatus:

  • A Zener Diode
  • A DC Voltage Supplier
  • Bread Board
  • 100 ohm resistor
  • 2 multimeter for measuring current and voltages
  • Connecting wires

Theory: A Zener Diode is constructed for operation in the reverse breakdown region.The relation between I-V is almost linear in this case, Vz = Vz0 + Iz * Rz , where Rz is the dynamic resistance of the zener at the operating point and Vz0 is the voltage at which the straight-line approximation of the I-V characteristic intersects the horizontal axis. After reaching a certain voltage, called the breakdown voltage, the current increases drastically even for a small change in voltage. However, there is no appreciable change in voltage accompanying this current change. So, when we plot the graph, we get a curve which is very near to the x-axis and nearly parallel to it until a particular potential value, called the Zener potential, is reached. After the Zener potential Vz value, there will be a sudden change and the graph becomes exponential.

Source: learning about electronics

Procedure: Construct the circuit as shown in figure below

Now, start increasing the voltage until a reading in the multimeter for current can be obtained. Note that reading. Now, start increasing the input voltage and take the corresponding current readings. Using the set of readings observed,  construct a V vs I graph. This graph gives us the I-V characteristics. The slope of the curve at any point gives the dynamic resistance at that voltage.

Result: The Characteristic curve has been verified after plotting V-I data points on the graph.

Experiment format in PSLab Android App

We have a ViewPager that renders two fragments:

  1. Experiment Doc– It consists of information like the Aim of experiment, Schematic, Output screenshot that we will get after the experiment has been performed.
  2. Experiment Setup– It consists of the setup to configure the PSLab device. This fragment is analogous to the experiment apparatus of the laboratory.  

Below is a gif showing the experiment doc of the Zener I-V experiment which is to be performed using the PSLab device. It consists of a schematic and a screenshot of the output that we get after performing the experiment.

Source: PSLab Android

Make the circuit connections on a breadboard as shown in the schematic. After the circuit is complete we need to configure experiment.

Source: PSLab Android

To configure the experiment, we give the initial voltage, the final voltage and the step size. After clicking on START EXPERIMENT, the voltage is varied on the PV1 channel from the initial voltage to final voltage by increasing the voltage in step size. At each variation of voltage, the current is calculated by dividing the voltage difference between resistor by its resistance value i.e

I = ( VPV1 - VCH1 ) / R

As soon as the initial voltage reaches the final voltage, the experiment stops and data points are plotted on the graph. From the graph we can see the change in the current through a zener diode when the voltage varies across it’s terminals.

The output that was obtained after the experiment is I-V characteristic curve for Zener Diode as shown in the image below.

It can be clearly seen that after the breakdown voltage (~0.7V) the  current increases drastically with respect to the  increase in the voltage. After this point, the voltage can be considered  nearly constant unlike the current which varies exponentially.

In the PSLab Android App, there are read-back errors while reading bytes serially from the PSLab Hardware Device. As a result, the data points are not read accurately and an inaccurate plot is generated on the graph as shown in the image below.

Source: PSLab Android

Resources

Continue ReadingPerforming the Experiments Using the PSLab Android App

Implementing an Interface for Reading Configuration from a YAML File for Yaydoc

Yaydoc reads configuration specified in a YAML file to set various options during the build process. This allows users to customize various properties of the build process. The current implementation for this was very basic. Basically it uses a pyYAML, a yaml parser for python to read the file and convert it to a python dictionary. From the dictionary we extracted values for various properties and converting them to strings using various heuristics such as converting True to ”true”, False to ”false”, a list to comma separated string and None to an empty string. Finally, we exported variables with those values.

Recently the entire code for this was rewritten using object-oriented paradigm. The motivation for this came from the fact that the implementation lacked certain features and also required some refactoring for long term readability. In the following paragraph, I have discussed the new implementation.

Firstly a Configuration class was created which basically wraps around a dictionary and provide certain utility methods. The primary difference is that the Configuration class allows dotted key access. This means that you can use the following syntax to access nested keys.

theme = conf[‘build.theme.name’]

The class provides another method connect which is used to connect environment variables with configuration values. This method also takes a dotted key but provides an extension on top of that to handle the case when a certain option can take multiple values. For example,

option: my_option

Or,

option:
  - my_option1
  - my_option2

To indicate that a certain config is of this type, you can specify a “@” character at the end of the key. Anything after the “@” character is assumed to be an attribute of each element within the list. Let’s see an example of this whole process.

build:
  subproject:
    - url: <url1>
  source: “doc”
    - url: <url2>

Now to extract all urls from the above file, we’d need to do the following

config.connect(‘SUBPROJECT_URLS’, ‘build.subproject@url’)

To extract sources, we’ll also use the default parameter as the source option is optional.

config.connect(‘SUBPROJECT_SOURCES’, build.subproject@source’, default=’docs’)

Finally, The Configuration object also provides a getenv method which reads all connection and serializes values to string according to the previously described heuristics. It then returns a dictionary of all environment variables which must be set.

Resources

Continue ReadingImplementing an Interface for Reading Configuration from a YAML File for Yaydoc

Implementing Proper CSS for Static Pages in SUSI.AI Web Chat

Our SUSI.AI Web Chat has many static pages like Overview, Devices, Team and Support. We have separate CSS files for each component. Recently, we faced a problem regarding design pattern where CSS files of one component were affecting another component. This blog is all about solving this issue and we take an example of distortion in our team’s page.

The current folder structure looks like this :

We can see that there are separate CSS files for all components. When the build of our react web app is complete, all the CSS files are loaded at once. So if CSS files contain classes with similar names, then this can disturb the original intended design of a particular component.

Our Team Page after merging of recent pull requests looked like this :

The Card component holding the images had extended vertically. The card component has following code:

<Card className='team-card' key={i}>
  <CardMedia className="container" >
    <img src={serv.avatar} alt={serv.name} 
      className="image" />
      <div className="overlay" >
        <div className="text">
         <FourButtons member={serv} />
        </div>
      </div>
  </CardMedia>
  <CardTitle title={serv.name} subtitle={serv.designation} />
</Card>

The CardMedia component is having className = “container”. This was defined in Team.css file. The CSS for this component is as follows :

.container {
  position: relative;
}
.container:hover .overlay {
  bottom: 0;
  height: 100%;
  opacity:0.7;
}

After inspecting through Chrome’s developer’s tool, it was found that these CSS properties were overwritten by another component having the same className as container. To resolve this issue there are multiple approaches:

  • Find the component with the same className and change the className of that component.
  • Change the className of current component.
  • Change the name of both components to resolve conflicts in future.

All the approaches will do the job for us. Here the easiest task was to change the className of the current component. This will save us time and we would not be adding extra lines of code. This is an efficient solution. So we decided to change the className to “container_div”. Then the CSS files will look like this:

.container_div {
  position: relative;
}
.container_div:hover .overlay {
  bottom: 0;
  height: 100%;
  opacity:0.7;
}

We also have to update the className in our CardMedia to “container_div”. After doing these changes. The cards were back to intended design:

To avoid such conflicts in future, it is recommended to name your CSS classes uniquely and after you’re done with making any component, recheck through developer’s tool that your component’s className does not have any conflicts with other components.

Resources:

CSS best practises: https://code.tutsplus.com/tutorials/30-css-best-practices-for-beginners–net-6741

Code for Team’s Page: https://github.com/fossasia/chat.susi.ai/tree/master/src/components/Team

Team Page: http://chat.susi.ai/team

Continue ReadingImplementing Proper CSS for Static Pages in SUSI.AI Web Chat

Advanced functionality in SUSI FBbot

SUSI AI is integrated to Facebook (blog). During the initial phase, SUSI FBbot had basic UI and functionalities like just “plain text” replies. Facebook provides many more features like replies enclosed in templates (blog link), sharing the replies by SUSI A.I. with friends, get started button or a persistent menu to show quick reply options to the user etc. All these features to enhance the user experience with SUSI AI chatbot.

This blog post walks you through on adding these functionalities to the SUSI FBbot:

Adding Get Started button

A Get Started button is added to the SUSI FBbot to give the user a brief introduction about SUSI AI and what the user can try next.

Clicking on the get started button , will send the message as “Get Started” to the SUSI FBbot:

The reply message, provides the user with options to visit SUSI A.I. repository or to just start chatting.

To have this button in our bot, we use this code snippet:

// Add a get started button to the messenger
request({
    url: 'https://graph.facebook.com/v2.6/me/messenger_profile',
    qs: {access_token:token},
    method: 'POST',
    json: { 
      "get_started":{
        "payload":"GET_STARTED_PAYLOAD"
      }
    }
}, function(error, response, body) {
    // handle errors and response here
})

When a user clicks this button, a postback is sent to the webhook of SUSI FBbot with payload as “GET_STARTED_PAYLOAD”. When we receive such postback, we reply with a message as shown above using generic template.

Adding a persistent menu to the bot

If not at the start, while chatting with SUSI AI for sometime, it is quite possible that the user becomes curious to visit the repository of SUSI A.I. . So we need a quick access to the “Visit repository” button all the time. Persistent menu, helps us with this feature:

This way it is accessible at each point of time. Some other buttons can also be added to the menu like “Latest News” as shown in the image above.

To have a persistent menu for the SUSI FBbot, the following code snippet is used:

request({
        url: 'https://graph.facebook.com/v2.6/me/messenger_profile',
        qs: {access_token:token},
        method: 'POST',
        json: {
                "persistent_menu":[{
                    "locale":"default", 
                        "composer_input_disabled":false,
                        "call_to_actions":[{
                            "type":"web_url",
                            "title":"Visit Repository",
                            "url":"https://github.com/fossasia/susi_server",
                            "webview_height_ratio":"full"
                        }]
                 }]
            }
    }, function(error, response, body) {
        // handle errors and response
    })

We can add more buttons to the menu. JSON object having the required properties of that button can be appended to the key “call_to_actions” to do so.

Adding a messenger code to join SUSI FBbot

To enable Facebook users to chat with SUSI AI by scanning a code through messenger. This feature is added to the bot by making the following POST request:

request({
        url: 'https://graph.facebook.com/v2.6/me/messenger_codes',
        qs: {access_token:token},
        method: 'POST',
        json: {
                type: "standard",
                image_size: 1000
        }
    }, function(error, response, body) {
        // handle errors and response.
});

Adding message sharing feature

To increase the reach of SUSI A.I. to more users on Facebook, message sharing proves to be a big boon. The reply by SUSI A.I. to users can be shared with their friends. Along with the message we can also send a promotional message(related to SUSI A.I.), to the people with which the message was shared.

This sharing can end up having more users trying SUSI A.I., leading to increase the user base of SUSI AI and its popularity.

We can allow sharing of just the message(i.e. the reply) or a promotional message with it. In case of just the reply:

Clicking the share button, will share just the reply with another person. To add capabilities of sharing the reply along with one more message(prompting to try SUSI A.I.), some changes to the code are done:

We need to set the buttons property in generic template like:

buttons : [
            {
                "type":"element_share",
                    "share_contents": { 
                      "attachment": {
                        "type": "template",
                        "payload": {
                          "template_type": "generic",
                          "elements": [
                            {
                              "title": "I had an amazing chat with SUSI.",
                              "buttons": [
                                {
                                  "type": "web_url",
                                  "url": "https://m.me/asksusisu", 
                                  "title": "Chat with SUSI AI"
                                }
                              ]
                            }
                          ]
                        }
                      }
                   }
            } 
       ];

This way when a user shares the message with other, an extra message is sent with the original message, tempting the user to try a chat with SUSI A.I.:

Resources:

  1. By Seth Rosenberg from Facebook developers blogLink Ads to Messenger, Enhanced Mobile Websites, Payments and More.
  2. By Slobodan Stojanović from smashing magazineDevelop a chat bot with node js.
Continue ReadingAdvanced functionality in SUSI FBbot

Adding JSON API support to ember-models-table in Open Event Front-end

Open Event Front-end project uses ember-models-table for handling all the table components in the application. Although ember-models-table is great for handling server requests for operations like pagination, sorting & filtering, but it does not support JSON API used in the Front-end project.

In this blog we will see how we integrated JSON API standards to ember-models-table. Lets see how we added support for JSON API to table and made requests to the Open Event Orga-server.

Adding JSON API support for filtering & sorting

The JSON API specs follow a strict structure for supporting meta data & filtering options, the server expects an array of objects for specifying the name of the field, operation and the value for filtering. The name attribute specifies the column for which we need to apply the filter. eg we use `name` for the events name in the. `op` attribute specifies the operation to be used for filtration, `val` attribute is used to provide a value for comparison. You can check the list of all the supported operations here.

For implementation of filter we will check if the column filter is being used i.e if the filter string is empty or not, if the string is not empty we add a filter object of the column using the specified specs, else we remove the filter object of the column.

if (filter) {
  query.filter.pushObject({
    name : filterTitle,
    op   : 'ilike',
    val  : `%${filter}%`
  });
} else {
  query.filter.removeObject({
    name : filterTitle,
    op   : 'ilike',
    val  : `%${filter}%`
  });
}

For sort functionally we need to pass a query parameter called `sort` which is a string value in the URL. Sorting can be done in ascending or descending order for which the server expects different values. We pass `sort=name` & `sort=-name` for sorting in ascending order & descending order respectively.

const sortSign = {
  none : '',
  asc  : '-',
  desc : ''
};
let sortedBy = get(column, 'sortedBy');
if (typeOf(sortedBy) === 'undefined') {
  sortedBy = get(column, 'propertyName');
}

Adding support for pagination

The pagination in JSON API is implemented using query parameters `page[size]` & `page[number]` which specify the size of the page & the current page number respectively eg

page[size]=10&page[number]=1

This will load the first ten events from the server in the application.

Once the data is loaded in the application we calculate the number of pages to be rendered. The response from the server has attached meta-data which contains the total number of the events in the following structure:

meta: {
  count: 100
}

We calculate the number of pages by dividing the total count by the size of the page. We check if the number of items is greater than the pageSize, and calculate the number of the pages using the formula `items / pagesize + (items % pagesize ? 1 : 0)`. If the items are less than the pageSize we do not have to calculate the pages and we simply hide the pagination in the footer.

if (pageSize > items) {
  this.$('.pagination').css({
    display: 'none'
  });
} else {
  this.$('.pagination').removeAttr('style');
  pages = parseInt((items / pageSize));
  if (items % pageSize) {
    pages = pages + 1;
  }
}

Adding dynamic routing support to ember-models-table

We may want to use the ember-models-table for dynamic routes like `events/list` route, where we load live, drafted & past events based on the current route. The ember-models-table by default do not support the dynamic routes. To add this we override the didReceiveAttrs() method of the component which is executed every time the component updates. We add reset the pageSize, currentPageNumber and the content of the table, as the routes change.

didReceiveAttrs() {
  set(this, 'pageSize', 10);
  set(this, 'currentPageNumber', 1);
  set(this, 'filteredContent', get(this, 'data'));
}

The result of this we now have tables supporting JSON API in the Open Event Front-end application

Thank you for reading the blog, you can check the source code for the example here.

Resources

Continue ReadingAdding JSON API support to ember-models-table in Open Event Front-end

Implementation of SUSI Web Chat Auto Sizing Message Composer

While we are using SUSI Web Chat Application we may have to send lengthy messages. Existing application’s Message composer supports for lengthy messages but it manages a constant value for every user input. While we were developing the application we got a requirement to build a growing message composer.

Final output of this implementation produces a message composer that grows when user completes a new line until user completes 5 lines and after 5 lines it maintains a fixed size and enables scrolling.

So we tried several packages to get this done. And finally we did this  using react-textarea-autosize  it gives all these features and it gives user to customize the elements furthermore.

First we have to install the npm package:

npm install --save react-textarea-autosize

After the installation we have to import the package on top of the “MessageComposer.react.js”

import TextareaAutosize from 'react-textarea-autosize';

Next we need to use this package like this,

         <TextareaAutosize
           className='scroll'
           id='scroll'
           minRows={1}
           maxRows={5}
           placeholder="Type a message..."
           value={this.state.text}
           onChange={this._onChange.bind(this)}
           onKeyDown={this._onKeyDown.bind(this)}
           ref={(textarea) => { this.nameInput = textarea; }}
           style={{ background: this.props.textarea}}
         />

 

This package provides “minRows” and “maxRows”  attributes and we can define minimum height of the text area and maximum height it can grow. If you need to know more about auto growing text areas and to get examples refer this.

Next we wanted to hide the scrollbar which is displaying when the textarea height is exceeding.

How we hide the scrollbars  on chrome browsers.

.scroll::-webkit-scrollbar {
 	 display: none;
}

This is how we hide the scrollbar on firefox browser.

.scroll {
 	overflow: -moz-scrollbars-none;
}

Now we have to style up the textarea because it comes with default styles. We wrapped up the textarea with the div and applied our styles to that. In my case we wrapped up my textarea with  <div className=“textBack”>

This is how we styled the textarea using the wrapper div.

.textBack{
 background: #fff;
 width: 83%;
 border-radius: 40px;
 padding: 5px 20px;
 display: block;
 position: relative;
 top: 12%;
 box-sizing: content-box;
 margin: 0px 0 10px 0;
}

Our textarea is like this.

It expands when user exceeds the width of textarea.

This is how we implemented the SUSI Web Chat’s growing message composer. If you would like to contribute please fork our repository on github  

Resources:

Continue ReadingImplementation of SUSI Web Chat Auto Sizing Message Composer

Auto Deployment of SUSI Server using Kubernetes on Google Cloud Platform

Recently, we auto deployed SUSI Server on Google Cloud Platform using Kubernetes and Docker Images after each commit in the GitHub repo with the help of Travis Continuous Integration. So, basically, whenever a new commit is added to the repo, during the Travis build, we build the docker image of the server and then use it to deploy the server on Google Cloud Platform. We use Kubernetes for deployment since it is very easy to scale up the Project when traffic on the server is increased and Docker because using it we can easily build docker images which then can be used to update the deployment. This schematic will make things more clear what exactly is the procedure.

Prerequisites

  1. You must be signed in to your Google Cloud Console and have enabled billing and must have credits left in your account.
  2. You must have a docker account and a repo in it. If you don’t have one, make it now.
  3. You should have enabled Travis on your repo and have a Travis.yml file in your repo.
  4. You must already have a project in Google Cloud Console. Make a new one if you don’t have.

Pre Deployment Steps

You will be needed to do some work on Google Cloud Platform before actually starting the auto deployment process. Those are:

  1. Creating a new Cluster.
  2. Adding and Formatting Persistence Disk
  3. Adding a Persistent Volume CLaim (PVC)
  4. Labeling a node as primary.

Check out this documentation on how to do that. It may help.

Implementation

Img src: https://cloud.google.com/solutions/continuous-delivery-with-travis-ci

1. The first step is simply to add this line in Travis.yml file and create an empty deploy.sh, file mentioned below.

after_success:
- bash kubernetes/travis/deploy.sh

Now we’ll be moving line by line and adding commands in the empty deploy.sh file that we created in the previous step.

2. Next step is to remove obsolete Google Cloud files and install Google Cloud SDK and kubectl command. Use following lines to do that.

echo ">>> Removing obsolete gcoud files"
sudo rm -f /usr/bin/git-credential-gcloud.sh
sudo rm -f /usr/bin/bq
sudo rm -f /usr/bin/gsutil
sudo rm -f /usr/bin/gcloud

echo ">>> Installing new files"
curl https://sdk.cloud.google.com | bash;
source ~/.bashrc
gcloud components install kubectl

3. In this step you will be needed to download a JSON file which contains your Google Cloud Credentials, then copy that file to your repo and encrypt it using Travis encryption keys. Follow https://youtu.be/7U4jjRw_AJk this video to see how to do that.

4. So, now you have added your encrypted credentials.json files in your repo and now you need to use those credentials to login into your google cloud account. So, use below lines to do that.

echo ">>> Decrypting credentials and authenticating gcloud account"
# Decrypt the credentials we added to the repo using the key we added with the Travis command line tool
openssl aes-256-cbc -K $encrypted_YOUR_key -iv $encrypted_YOUR_iv -in ./kubernetes/travis/Credentials.json.enc -out Credentials.json -d
gcloud auth activate-service-account --key-file Credentials.json
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/Credentials.json
#add gcoud project id
gcloud config set project YOUR_PROJECT_ID
gcloud container clusters get-credentials YOUR_CONTAINER

The above lines of code first decrypt your credentials, then login into your account and set the project you already created earlier.

5. Now, we have logged into Google Cloud, we need to build docker image from a dockerfile. Follow official docker docs to see how to write a dockerfile. Here is an example of dockerfile. You will need to add “$DOCKER_USERNAME” and “$DOCKER_PASSWORD” as environment variables in Travis settings of your repo.

echo ">>> Building Docker image"
cd kubernetes/images

docker build --no-cache -t YOUR_DOCKER_USERNAME/YOUR_DOCKER_REPO:$TRAVIS_COMMIT .
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
docker tag YOUR_DOCKER_USERNAME/YOUR_DOCKER_REPO:$TRAVIS_COMMIT YOUR_DOCKER_USERNAME/YOUR_DOCKER_REPO:latest

6. Now, just push the docker image created in previous step and update the deployment.

echo ">>> Pushing docker image"
docker push YOUR_DOCKER_USERNAME/YOUR_DOCKER_REPO

echo ">>> Updating deployment"
kubectl set image deployment/YOUR_CONTAINER_NAME --namespace=default YOUR_CONTAINER_NAME=YOUR_DOCKER_USERNAME/YOUR_DOCKER_REPO:$TRAVIS_COMMIT

Summary

This blog was about how we have configured travis build and auto deployed SUSI Server on Google Cloud Platform using Kubernetes and Docker. You can do the same with your server too or if you are looking to contribute to SUSI Server, this may help you a little in understanding the code of the repo.

Resources

  1. The documentation for setting up your project on Google CLoud Console before starting auto deployment https://github.com/fossasia/susi_server/blob/afb00cd9c421876f5d640ce87941e502aa52e004/docs/installation/installation_kubernetes_gcloud.md
  2. The documentation for encrypting your google cloud credentials and adding them to your repo https://cloud.google.com/solutions/continuous-delivery-with-travis-ci
  3. Docs for Docker to get you started with Docker https://docs.docker.com/
  4. Travis Documentation on how to secure your credentials https://docs.travis-ci.com/user/encryption-keys/
  5. Travis Documentation on how to add environment variables in your repo settings https://docs.travis-ci.com/user/environment-variables/
Continue ReadingAuto Deployment of SUSI Server using Kubernetes on Google Cloud Platform