Implementing Health Check Endpoint in Open Event Server

A health check endpoint was required in the Open Event Server be used by Kubernetes to know when the web instance is ready to receive requests.

Following are the checks that were our primary focus for health checks:

  • Connection to the database.
  • Ensure sql-alchemy models are inline with the migrations.
  • Connection to celery workers.
  • Connection to redis instance.

Runscope/healthcheck seemed like the way to go for the same. Healthcheck wraps a Flask app object and adds a way to write simple health-check functions that can be used to monitor your application. It’s useful for asserting that your dependencies are up and running and your application can respond to HTTP requests. The Healthcheck functions are exposed via a user defined flask route so you can use an external monitoring application (monit, nagios, Runscope, etc.) to check the status and uptime of your application.

Health check endpoint was implemented at /health-check as following:

from healthcheck import HealthCheck
health = HealthCheck(current_app, "/health-check")

 

Following is the function for checking the connection to the database:

def health_check_db():
   """
   Check health status of db
   :return:
   """
   try:
       db.session.execute('SELECT 1')
       return True, 'database ok'
   except:
       sentry.captureException()
       return False, 'Error connecting to database'

 

Check functions take no arguments and should return a tuple of (bool, str). The boolean is whether or not the check passed. The message is any string or output that should be rendered for this check. Useful for error messages/debugging.

The above function executes a query on the database to check whether it is connected properly. If the query runs successfully, it returns a tuple True, ‘database ok’. sentry.captureException() makes sure that the sentry instance receives a proper exception event with all the information about the exception. If there is an error connecting to the database, the exception will be thrown. The tuple returned in this case will be return False, ‘Error connecting to database’.

Finally to add this to the endpoint:

health.add_check(health_check_db)

Following is the response for a successful health check:

{
   "status": "success",
   "timestamp": 1500915121.52474,
   "hostname": "shubham",
   "results": [
       {
           "output": "database ok",
           "checker": "health_check_db",
           "expires": 1500915148.524729,
           "passed": true,
           "timestamp": 1500915121.524729
       }
   ]
}

If the database is not connected the following error will be shown:

{
           "output": "Error connecting to database",
           "checker": "health_check_db",
           "expires": 1500965798.307425,
           "passed": false,
           "timestamp": 1500965789.307425
}

Related:

Continue ReadingImplementing Health Check Endpoint in Open Event Server

How SUSI AI Web Chat Custom Theme Settings are Stored in Server

We had a feature in SUSI Web Chat to make custom themes but those themes were not storing on the server. We needed to store those theme data on server. In this post I discuss how we implemented that feature. This is the PR that I sent to solve this issue.

Previously we had two theme options. According to the user’s choice it changes theme colors. Since we needed to store custom themes and use them without any conflicts with existing “light” and “dark” themes we made another theme option called “custom”. After user clicks on the custom theme it automatically changes to “custom” mode.

This is how we did it in “onClick” of the custom theme .

    this.setState({'theme':'custom'})
     let currSettings = UserPreferencesStore.getPreferences();
     let settingsChanged = {};
     if(currSettings.Theme !=='custom'){
       settingsChanged.Theme = 'custom';
       Actions.settingsChanged(settingsChanged);
     }

Then after we collected all the chosen color values to a variable. While we store our color values on a variable we avoid the “#” letter which is at very first of the color value. Because we can’t send that value to the server with “#” character.

this.customTheme.body=state.body.substring(1);

After selecting color values user have to press the save button to push those selected values to server. We execute below method on click of the save button.

 saveThemeSettings = () => {
    let customData='';
    Object.keys(this.customTheme).forEach((key) => {
      customData=customData+this.customTheme[key]+','
    });
    this.setState({'theme':'custom'})
    let currSettings = UserPreferencesStore.getPreferences();
    let settingsChanged = {};
    if(currSettings.Theme !=='custom'){
      settingsChanged.Theme = 'custom';
      Actions.settingsChanged(settingsChanged);
    }
    Actions.customThemeChanged(customData);
    this.handleClose();
  }

Using this method we derived those data that we added into the variable and made a single string array. Then after we executed the action that we needed to execute to store data on the server.
It is “Actions.customThemeChanged(customData);”.
This action is defined in “Settings.actions.js” file.

export function customThemeChanged(customTheme) {
  ChatAppDispatcher.dispatch({
    type: ActionTypes.CHANGE_CUSTOM_THEME,
    customTheme
  });
  Actions.pushCustomThemeToServer(customTheme);
}

We used this Action name constant “CHANGE_CUSTOM_THEME” in “ChatConstant.js” file

We defined this “pushCustomThemeToServer”  function on “API.actions.js” file. here

export function pushCustomThemeToServer(customTheme){
  
  if(cookies.get('loggedIn')===null||
    cookies.get('loggedIn')===undefined) {
    return;
  }
       url = BASE_URL+'/aaa/changeUserSettings.json?'
          +'key=custom_theme_value&value='+customTheme
          +'&access_token='+cookies.get('loggedIn');
        makeServerCall(url);
}

Here we check whether user is logged in or not. If user is logged in we get the access token from cookies and attach it to the request URL and execute the “makeServerCall” function that we defined previously.

Now our data are saved on server. Use this url to check what settings you have in your user account.
api.susi.ai/aaa/listUserSettings.json?access_token=YOUR_ACCESS_TOKEN
Now we can use stored values. First we need to update state. For that we got theme values from server like this

  var themeValue=[];
   if(UserPreferencesStore.getThemeValues()){
     themeValue=UserPreferencesStore.getThemeValues().split(',');
   }

 

Here we got data from server and put it to the array.

Then after we set it to state. While adding custom theme settings to state we set the “#” character before each colour value.  Here is the code

    header: themeValue.length>4?'#'+themeValue[0]:'#4285f4',
    pane: themeValue.length>4?'#'+themeValue[1]:'#f5f4f6',
    body: themeValue.length>4?'#'+themeValue[2]:'#fff',
    composer: themeValue.length>4?'#'+themeValue[3]:'#f5f4f6',
    textarea:  themeValue.length>4?'#'+themeValue[4]:'#fff',

 

Now we have to use these data with our JSX elements. This is how we did this.

We checked the current theme mode. If it is “custom” we used the values we got from server. Otherwise we used corresponding colors for other “light” and “dark” theme. Here is the full code.

 

var bodyColor;
    var TopBarColor;
    var composerColor;
    var messagePane;
    var textArea;
switch(this.state.currTheme){
  case 'custom':{
    bodyColor = this.state.body;
    TopBarColor = this.state.header;
    composerColor = this.state.composer;
    messagePane = this.state.pane;
    textArea = this.state.textarea;
    break;
  }

You can use these variables wherever you need to show colors. As an example this is how we passed header color to top bar.

 <TopBar  header={TopBarColor} >

This is how we stored and fetched custom theme data from store.

Resources:

  • How to store and receive data from SUSI server using HTTP requests. https://github.com/fossasia/chat.susi.ai/blob/master/docs/Accounting.md
  • How Flux Architecture works: https://facebook.github.io/flux/
Continue ReadingHow SUSI AI Web Chat Custom Theme Settings are Stored in Server

Deploying loklak Server on Kubernetes with External Elasticsearch

Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.

kubernetes.io

Kubernetes is an awesome cloud platform, which ensures that cloud applications run reliably. It runs automated tests, flawless updates, smart roll out and rollbacks, simple scaling and a lot more.

So as a part of GSoC, I worked on taking the loklak server to Kubernetes on Google Cloud Platform. In this blog post, I will be discussing the approach followed to deploy development branch of loklak on Kubernetes.

New Docker Image

Since Kubernetes deployments work on Docker images, we needed one for the loklak project. The existing image would not be up to the mark for Kubernetes as it contained the declaration of volumes and exposing of ports. So I wrote a new Docker image which could be used in Kubernetes.

The image would simply clone loklak server, build the project and trigger the server as CMD

FROM alpine:latest

ENV LANG=en_US.UTF-8
ENV JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8

WORKDIR /loklak_server

RUN apk update && apk add openjdk8 git bash && \
    git clone https://github.com/loklak/loklak_server.git /loklak_server && \
    git checkout development && \
    ./gradlew build -x test -x checkstyleTest -x checkstyleMain -x jacocoTestReport && \
    # Some Configurations and Cleanups

CMD ["bin/start.sh", "-Idn"]

[SOURCE]

This image wouldn’t have any volumes or exposed ports and we are now free to configure them in the configuration files (discussed in a later section).

Building and Pushing Docker Image using Travis

To automatically build and push on a commit to the master branch, Travis build is used. In the after_success section, a call to push Docker image is made.

Travis environment variables hold the username and password for Docker hub and are used for logging in –

docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD

[SOURCE]

We needed checks there to ensure that we are on the right branch for the push and we are not handling a pull request –

# Build and push Kubernetes Docker image
KUBERNETES_BRANCH=loklak/loklak_server:latest-kubernetes-$TRAVIS_BRANCH
KUBERNETES_COMMIT=loklak/loklak_server:kubernetes-$TRAVIS_COMMIT
  
if [ "$TRAVIS_BRANCH" == "development" ]; then
    docker build -t loklak_server_kubernetes kubernetes/images/development
    docker tag loklak_server_kubernetes $KUBERNETES_BRANCH
    docker push $KUBERNETES_BRANCH
    docker tag $KUBERNETES_BRANCH $KUBERNETES_COMMIT
    docker push $KUBERNETES_COMMIT
elif [ "$TRAVIS_BRANCH" == "master" ]; then
    # Build and push master
else
    echo "Skipping Kubernetes image push for branch $TRAVIS_BRANCH"
fi

[SOURCE]

Kubernetes Configurations for loklak

Kubernetes cluster can completely be configured using configurations written in YAML format. The deployment of loklak uses the previously built image. Initially, the image tagged as latest-kubernetes-development is used –

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: server
  namespace: web
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: server
    spec:
      containers:
      - name: server
        image: loklak/loklak_server:latest-kubernetes-development
        ...

[SOURCE]

Readiness and Liveness Probes

Probes act as the top level tester for the health of a deployment in Kubernetes. The probes are performed periodically to ensure that things are working fine and appropriate steps are taken if they fail.

When a new image is updated, the older pod still runs and servers the requests. It is replaced by the new ones only when the probes are successful, otherwise, the update is rolled back.

In loklak, the /api/status.json endpoint gives information about status of deployment and hence is a good target for probes –

livenessProbe:
  httpGet:
    path: /api/status.json
    port: 80
  initialDelaySeconds: 30
  timeoutSeconds: 3
readinessProbe:
  httpGet:
    path: /api/status.json
    port: 80
  initialDelaySeconds: 30
  timeoutSeconds: 3

[SOURCE]

These probes are performed periodically and the server is restarted if they fail (non-success HTTP status code or takes more than 3 seconds).

Ports and Volumes

In the configurations, port 80 is exposed as this is where Jetty serves inside loklak –

ports:
- containerPort: 80
  protocol: TCP

[SOURCE]

If we notice, this is the port that we used for running the probes. Since the development branch deployment holds no dumps, we didn’t need to specify any explicit volumes for persistence.

Load Balancer Service

While creating the configurations, a new public IP is assigned to the deployment using Google Cloud Platform’s load balancer. It starts listening on port 80 –

ports:
- containerPort: 80
  protocol: TCP

[SOURCE]

Since this service creates a new public IP, it is recommended not to replace/recreate this services as this would result in the creation of new public IP. Other components can be updated individually.

Kubernetes Configurations for Elasticsearch

To maintain a persistent index, this deployment would require an external Elasticsearch cluster. loklak is able to connect itself to external Elasticsearch cluster by changing a few configurations.

Docker Image and Environment Variables

The image used for Elasticsearch is taken from pires/docker-elasticsearch-kubernetes. It allows easy configuration of properties from environment variables in configurations. Here is a list of configurable variables, but we needed just a few of them to do our task –

image: quay.io/pires/docker-elasticsearch-kubernetes:2.0.0
env:
- name: KUBERNETES_CA_CERTIFICATE_FILE
  value: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- name: NAMESPACE
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace
- name: "CLUSTER_NAME"
  value: "loklakcluster"
- name: "DISCOVERY_SERVICE"
  value: "elasticsearch"
- name: NODE_MASTER
  value: "true"
- name: NODE_DATA
  value: "true"
- name: HTTP_ENABLE
  value: "true"

[SOURCE]

Persistent Index using Persistent Cloud Disk

To make the index last even after the deployment is stopped, we needed a stable place where we could store all that data. Here, Google Compute Engine’s standard persistent disk was used. The disk can be created using GCP web portal or the gcloud CLI.

Before attaching the disk, we need to declare a volume where we could mount it –

volumeMounts:
- mountPath: /data
  name: storage

[SOURCE]

Now that we have a volume, we can simply mount the persistent disk on it –

volumes:
- name: storage
  gcePersistentDisk:
    pdName: data-index-disk
    fsType: ext4

[SOURCE]

Now, whenever we deploy these configurations, we can reuse the previous index.

Exposing Kubernetes to Cluster

The HTTP and transport clients are enabled on port 9200 and 9300 respectively. They can be exposed to the rest of the cluster using the following service –

apiVersion: v1
kind: Service
...
Spec:
  ...
  ports:
  - name: http
    port: 9200
    protocol: TCP
  - name: transport
    port: 9300
    protocol: TCP

[SOURCE]

Once deployed, other deployments can access the cluster API from ports 9200 and 9300.

Connecting loklak to Kubernetes

To connect loklak to external Elasticsearch cluster, TransportClient Java API is used. In order to enable these settings, we simply need to make some changes in configurations.

Since we enable the service named “elasticsearch” in namespace “elasticsearch”, we can access the cluster at address elasticsearch.elasticsearch:9200 (web) and elasticsearch.elasticsearch:9300 (transport).

To confine these changes only to Kubernetes deployment, we can use sed command while building the image (in Dockerfile) –

sed -i.bak 's/^\(elasticsearch_transport.enabled\).*/\1=true/' conf/config.properties && \
sed -i.bak 's/^\(elasticsearch_transport.addresses\).*/\1=elasticsearch.elasticsearch:9300/' conf/config.properties && \

[SOURCE]

Now when we create the deployments in Kubernetes cluster, loklak auto connects to the external elasticsearch index and creates indices if needed.

Verifying persistence of the Elasticsearch Index

In order to see that the data persists, we can completely delete the deployment or even the cluster if we want. Later, when we recreate the deployment, we can see all the messages already present in the index.

I  [2017-07-29 09:42:51,804][INFO ][node                     ] [Hellion] initializing ...
 
I  [2017-07-29 09:42:52,024][INFO ][plugins                  ] [Hellion] loaded [cloud-kubernetes], sites []
 
I  [2017-07-29 09:42:52,055][INFO ][env                      ] [Hellion] using [1] data paths, mounts [[/data (/dev/sdb)]], net usable_space [84.9gb], net total_space [97.9gb], spins? [possibly], types [ext4]
 
I  [2017-07-29 09:42:53,543][INFO ][node                     ] [Hellion] initialized
 
I  [2017-07-29 09:42:53,543][INFO ][node                     ] [Hellion] starting ...
 
I  [2017-07-29 09:42:53,620][INFO ][transport                ] [Hellion] publish_address {10.8.1.13:9300}, bound_addresses {10.8.1.13:9300}
 
I  [2017-07-29 09:42:53,633][INFO ][discovery                ] [Hellion] loklakcluster/cJtXERHETKutq7nujluJvA
 
I  [2017-07-29 09:42:57,866][INFO ][cluster.service          ] [Hellion] new_master {Hellion}{cJtXERHETKutq7nujluJvA}{10.8.1.13}{10.8.1.13:9300}{master=true}, reason: zen-disco-join(elected_as_master, [0] joins received)
 
I  [2017-07-29 09:42:57,955][INFO ][http                     ] [Hellion] publish_address {10.8.1.13:9200}, bound_addresses {10.8.1.13:9200}
 
I  [2017-07-29 09:42:57,955][INFO ][node                     ] [Hellion] started
 
I  [2017-07-29 09:42:58,082][INFO ][gateway                  ] [Hellion] recovered [8] indices into cluster_state

In the last line from the logs, we can see that indices already present on the disk were recovered. Now if we head to the public IP assigned to the cluster, we can see that the message count is restored.

Conclusion

In this blog post, I discussed how we utilised the Kubernetes setup to shift loklak to Google Cloud Platform. The deployment is active and can be accessed from the link provided under wiki section of loklak/loklak_server repo.

I introduced these changes in pull request loklak/loklak_server#1349 with the help of @niranjan94, @uday96 and @chiragw15.

Resources

Continue ReadingDeploying loklak Server on Kubernetes with External Elasticsearch

Customizing Firefox in Meilix using skel

We had a problem of customizing the Firefox browser configuration using Meilix generator so we used the skel folder in Linux. The /etc/skel directory contains files and directories that are automatically copied over to a new user’s home directory when such user is created by the useradd program.

Firefox generally reads its settings from ~/.mozilla/firefox folder present in Linux home of the user, which is changed when user modifies his settings like changing the homepage or disabling the bookmarks. To add these changes for every new user we can copy these configurations into skel folder.

Which we have done with Meilix which have these configurations in it and can be modified by using Meilix generator.

If you are going to look in your ~/.mozilla/firefox folder you will find a random string followed by .default folder this will contain the default settings of your Firefox browser and this random string is created uniquely for every user. The profile name will be different for all users but should always end with .default so if we use skel the .default will be same for everyone and we may choose any name for that like we have used meilix.default in below script.

For example to change the default homepage URL we can create a script that can be used by Meilix generator to modify the URL in Meilix.

#!/bin/bash

set -eu   	 
# firefox
preferences_file="`echo meilix-default-settings/etc/skel/.mozilla/firefox/meilix.default/user.js`"
if [ -f "$preferences_file" ]
then
	echo "user_pref(\"browser.startup.homepage\", \"${event_url}\");" >> $preferences_file
fi

 

Here the ${event_url} is the environment variable sent by Meilix generator to be added to configuration of Firefox that is added to skel folder in Meilix.

We can similarly edit more configuration by editing the file pref.js file it contains the configuration for Firefox like bookmarks, download location, default URL etc.

Resources

Continue ReadingCustomizing Firefox in Meilix using skel

Email and Password Validation in Open Event Android

The Open Event API Server exposes a well documented JSONAPI compliant REST API that can be used in The Open Even Android and Frontend. The Open Event API Server enables the Android and web clients to add the user authentication (sign up/login) in the project. In the process of signing up or logging in user it is needed to validate email and password entered by the user and show the error to give better user experience. In this post I explain how to validate email and password entered by the user using TextInputLayout.

1. Add TextInputLayout

TextInputLayout wraps an EditText (or descendant) to show a floating label when the hint is hidden due to the user inputting text. Add TextInputLayout for email field in the layout as given below.

<android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v7.widget.AppCompatEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/email"
                android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>

Here the hint attribute is used to display hint in the floating label. Specify the input type so the system displays the appropriate soft input method (such as an on-screen keyboard) for the field. For email EditText we are using textEmailAddress input type. Similarly add TextInputLayout for the password field. The input type for the password is textPassword.

2.  Create and initialize object

Now in the activity create and initialize TextInputLayout and EditText objects for email and password.

@BindView(R.id.text_input_layout_email)
TextInputLayout mTextInputLayoutEmail;
@BindView(R.id.text_input_layout_password)
TextInputLayout mTextInputLayoutPassword;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ButterKnife.bind(this);

    private AppCompatEditText mEditTextEmail = (AppCompatEditText) mTextInputLayoutEmail.getEditText();
    private AppCompatEditText mEditTextPassword = (AppCompatEditText) mTextInputLayoutPassword.getEditText();
}

Here we are using ButterKnife for binding views with fields. The getEditText() method returns the EditText view used for text input inside the TextInputLayout.

3.  Create validate() method

Create validate() method which takes two arguments. The first is email and the second password. It will return true if the email and password are valid else false.

private boolean validate(String email, String password) {

        // Reset errors.
        mTextInputLayoutEmail.setError(null);
        mTextInputLayoutPassword.setError(null);

        if (Utils.isEmpty(email)) {
            mTextInputLayoutEmail.setError("Email is required");
            return false;
        } else if (!Utils.isEmailValid(email)) {
            mTextInputLayoutEmail.setError("Enter a valid email");
            return false;
        }

        if (Utils.isEmpty(password)) {
            mTextInputLayoutPassword.setError("Password is required");
            return false;
        } else if (!Utils.isPasswordValid(password)) {
            mTextInputLayoutPassword.setError("Password must contain at least 6 characters");
            return false;
        }

        return true;
}

Here it first resets the error for the TextInputLayout by setting it to null. Then it checks email string if it is empty then it will show “Email is required” error using setError() method.

4.  Create isEmailValid() and isPasswordValid() method

Now create isEmailValid() and isPasswordvalid() method which is used by validate() method. The isEmailValid() method should take email string as an argument and return boolean indicating whether the email is valid or not. The isEmailValid() method uses Pattern and Matcher class to determine if the pattern of input is email or not. The isPasswordValid() method should take password string as an argument and return true if the password is satisfying minimum condition. Here in our case length of the password should be minimum 6.

public static boolean isEmailValid(String email){
        Pattern pattern = Patterns.EMAIL_ADDRESS;
        Matcher matcher = pattern.matcher(email);
        return matcher.matches();
}

//Check password with minimum requirement here(it should be minimum 6 characters)
public static boolean isPasswordValid(String password){
        return password.length() >= 6;
}

5.  Use validate() method

Now we are ready to use validate() method when signing up or logging in the user. The getText() method of EditText will return text input.

String email = mEditTextEmail.getText().toString();
String password = mEditTextPassword.getText().toString();

if (validate(email, password)) {
    //Sign up or login User
}

Conclusion

Using TextInputLayout with floating hint label and error handling gives awesome UI and UX.

Continue ReadingEmail and Password Validation in Open Event Android

Using Lombok to Reduce Boilerplate Code in Open Event Android App

The Open Even App Android project contains data/model classes like Event, Track, Session, Speaker etc which are used to fetch data from the server and store data in the database. Each model contains private fields, getters, setters and toString() method which are used to change data of the object, access data of the object, logging and debugging. Adding all these methods manually makes the code boilerplate.

In this blog post I explain how to use Lombok to reduce boilerplate code in the model class.

Add dependency

To set up Lombok for your application you have to add the dependency in your app module’s build.gradle file.

dependencies {
	provided   "org.projectlombok:lombok:1.16.18"
}

Install Lombok plugin

In addition to setting up your gradle project correctly, you need to add the Lombok IntelliJ plugin to add Lombok support to Android Studio

  1. Go to File > Settings > Plugins
  2. Click on Browse repositories
  3. Search for Lombok Plugin
  4. Click on Install plugin
  5. Restart Android Studio

Write model class

Lombok has annotations to generate Getters, Setters, Constructors, toString(), Equal() and hashCode() methods.

@Getter,  @Setter, @ToString, @EqualsAndHashCode

@Data is a shortcut annotation that bundles the features of @Getter, @Setter, @ToString and @EqualsAndHashCode

Here I am only defining track model because of its simplicity and less complexity.

@Data
public class Track {

    private int id;
    private String name;
    private String description;
    private String color;
    private String fontColor;
    private RealmList<Session> sessions;
}

Create and use object

After defining models you can create an instance of the object and you will notice that you can access all the getters and setters.

Track track = new Track();
track.setName("Android");

String name = track.getName(); // here value of name will be "Android" 

You can also specify which fields to include and exclude in the toString(), equals() and hashCode() methods using @ToString, @EqualsAndHashCode annotation.

@ToString(of={"id", "name"})

@ToString(exclude="color")

@EqualsAndHashCode(of={"id", "name"})

@EqualsAndHashCode(exclude={"color", "fontColor"})

Constructors

Lombok has three methods to generator constructors

  • @NoArgsConstructor: It generates constructor with no parameters
  • @RequiredArgsConstructor: It generates a constructor with 1 parameter for each field that requires special handling.
  • @AllArgsConstructor: It generates a constructor with 1 parameter for each field in your class.

Conclusion

As you can see, Lombok uses succinct annotations to generate methods such as getters, setters, and constructors. It can easily help you get rid of hundreds of lines of boilerplate code. Lombok also allows you to make your code more expressive, concise and can help you avoid some bugs. To learn more about Lombok project follow the links given below.

Continue ReadingUsing Lombok to Reduce Boilerplate Code in Open Event Android App

Creating SharedPreferences Util in Open Event Android

In the Open Event Android we have the fragment for schedule, speakers which has the option to sort the list. Schedule Fragment have the option to sort by Title, Tracks and  Start Time. Speakers Fragment has the option to sort by Name, Organization and Country. If the user preferred to sort by name then it should always sort the list by name whenever the user uses the app. For this we need to store user preference for sorting list. Another part of the app like Live feed, About fragment also needs to store event id, facebook page id/name etc.

In Android there is a SharedPreferences class to store key value pair in the App specific storage. To store data in SharedPreferences we need to create SharedPreferences Object in different activities and fragment. In this post I explain how to create SharedPreferences Util which can be used to store key value pairs from all over the App.

1. Create SharedPreferencesUtil Class

The first step is to create SharedPreferncesUtil.java file which will contain static SharedPreferences object.

public class SharedPreferencesUtil {
    ...
}

2. Create static objects

Create static SharedPreferences and SharedPreferences.Editor object in the SharedPreferncesUtil.java file.

private static SharedPreferences sharedPreferences;
private static SharedPreferences.Editor editor;

3. Initialize objects

Now after creating objects initialize them in the static block. The code inside static block is executed only once: The first time you make an object of that class or the first time you access a static member of that class.

static {
        sharedPreferences = OpenEventApp.getAppContext().getSharedPreferences(ConstantStrings.FOSS_PREFS, Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
}

 

Here make sure to use the Application context to avoid a memory leak. The getSharedPreferences() method takes two arguments name of the shared preference and mode. Here we are using Context.MODE_PRIVATE File creation mode where the created file can only be accessed by the calling application.

4. Add methods

Now create static methods to store data so that we can use these methods directly from the other activities or classes. Here I am only adding methods for integer you can add more methods for String, long, boolean etc.

public static void putInt(String key, int value) {
        editor.putInt(key, value).apply();
}

public static int getInt(String key, int defaultValue) {
        return sharedPreferences.getInt(key, defaultValue);
}

5. Use SharedPreferencesUtil class

Now we are ready to use this Util class to store key value pair in SharedPreferences.

SharedPreferencesUtil.putInt(ConstantStrings.PREF_SORT, sortType);

Here the putInt() methods take two arguments one the key and second the value. To get the stored value use getInt() method.

SharedPreferencesUtil.getInt(ConstantStrings.PREF_SORT, 0);

To know more how I solved this issue in Open Event Project visit this link.

Continue ReadingCreating SharedPreferences Util in Open Event Android

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 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