Implementing Sign up Feature through Email in Badgeyay

Badgeyay project is divided into two parts i.e front-end of Ember JS and back-end with REST-API programmed in Python.

We already have logging In features implemented with the help of Firebase Authentication. A User can login in the Badgeyay with the help of Google, Facebook and Twitter credentials through a single click. Now, the challenging part is to implement the sign up with Email feature in Frontend and Backend to enable the user to signup and Login with the help of Email and Password

In this blog, I will be discussing how I set up Sign up feature in Badgeyay frontend to send the data in backend besides having Oauth logging features in Badgeyay integrated with Firebase in my Pull Request.

The sign up form is already implemented and I have already mentioned in my previous blog. So we need to send the form data to backend to register user so that user can login using the registered credentials. We need an Adapter, Signup action, controller , Signup Data model  and a serializer for doing this task.

Let’s get started and understand the terminologies before implementing the feature.

What is Ember Data ?

It is a data management library for Ember Framework which help to deal with persistent application data.
We will generate Ember data model using Ember CLI in which we will define the data structure we will be requiring to provide to our application for User Signup.

Step 1 : Generate ember data model for signup.

$ ember g model user-signup

 

Step 2: Define the user-signup data model.

import DS from 'ember-data';

const { Model, attr } = DS;

export default Model.extend({
  username : attr('string'),
  email    : attr('string'),
  password : attr('string')
});

 

What are Actions ?

We already have the signup form implemented in frontend. Now we need to provide a action to the form when the user enters the data in form.

If we add the {{action}} helper to any HTML DOM element, when a user clicks the element, the named event will be sent to the template’s corresponding component or controller.

<button class="ui orange submit button" {{ action 'signUp' }}>Sign Up</button>

 

We need to add signUp action in sign-up component and controller.

// Signup Controller 
import Controller from '@ember/controller';

import { inject as service } from '@ember/service';

export default Controller.extend({
  routing : service('-routing'),
  actions : {
    signUp(email, username, password) {
      const _this = this;
      let user_ = this.get('store').createRecord('user-signup', {
        email,
        username,
        password
      });
      user_.save()
        .then(record => {
          _this.transitionToRoute('/');
        })
        .catch(err => {
          console.log(err);
        });
    }
  }
});

// Sign up Component
import Component from '@ember/component';

export default Component.extend({
  init() {
    this._super(...arguments);
  },

  email     : '',
  password  : '',
  isLoading : false,

  actions: {
    signUp(event) {
      event.preventDefault();
      let email = '';
      let password = '';
      let username = '';
      email = this.get('email');
      password = this.get('password');
      username = this.get('username');
      this.get('signUp')(email, username, password);
    }
  },
});

 

What is an Adapter ?

An adapter determines how the data is persisted to a backend data store. We can configure the backend host, URL format and headers for REST API.

Now as we have specific Data Model for User Signup that we will be using for communicating with its backend so we have to create User-Signup Adapter with the help of Ember-CLI.

Step 1: Generate User Signup Adapter by following together.

$ ember generate adapter user-signup

 

Step 2: Extend the Adapter according to User-Signup Model.

import DS from 'ember-data';
import ENV from '../config/environment';

const { APP } = ENV;
const { JSONAPIAdapter } = DS;

export default JSONAPIAdapter.extend({
  host        : APP.backLink,
  pathForType : () => {
    return 'user/register';
  }
});

 

What are Serializers ?

Serializers format the Data sent to and received from the backend store. By default, Ember Data serializes data using the JSON API format.

Now as we have specific Data Model for User Signup that we will be using for communicating with its backend so we have to create User-Signup Serializer with the help Ember-CLI.

Step 1: Generate the User Signup Adapter by following command:

$ ember generate serializer user-signup

 

Step 2: Extend the serializer according to User-Signup Model.

import DS from 'ember-data';

const { JSONAPISerializer } = DS;

export default JSONAPISerializer.extend({

  serialize(snapshot, options) {
    let json = this._super(...arguments);
    return json;
  },

  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    return payload;
  }
});

 

We have successfully set up the User Signup in the frontend and data is communicated to backend in JSON API v1 specification with the help of serializers and Adapters.

This is how I set up Sign up feature in Badgeyay frontend to send the data in backend besides having Oauth logging features in Badgeyay integrated with Firebase in my Pull Request.

Resources:

  1. Ember Docs – Link
  2. Firebase Docs – Link
  3. Badgeyay Repository – Link
Continue ReadingImplementing Sign up Feature through Email in Badgeyay

Creating Forms and their validation using Semantic UI in Badgeyay

Badgeyay project is now divided into two parts i.e front-end of Ember JS and back-end with REST-API programmed in Python.

After a discussion, we have finalized to go with Semantic UI framework which uses simple, common language for parts of interface elements, and familiar patterns found in natural languages for describing elements. Semantic allows to build beautiful websites fast, with concise HTML, intuitive javascript and simplified debugging, helping make front-end development a delightful experience. Semantic is responsively designed allowing a web application to scale on multiple devices. Semantic is production ready and partnered with Ember framework which means we can integrate it with Ember frameworks to organize our UI layer alongside our application logic.

In this blog, I will be discussing how I added Log In and Signup Forms and their validations using Semantic UI for badgeyay frontend in my Pull Request.

Let’s get started and understand it step by step.

Step 1:

Generate ember components of Login and Sign up by using the following command :

$ ember generate component forms/login-form
$ ember generate component forms/signup-form

 

Step 2:

Generate Login and Sign up route by following commands.

$ ember generate route login
$ ember generate route signup 

 

Step 3:

Generate Login and Sign up controller by following commands.

$ ember generate controller login
$ ember generate controller signup

 

Step 4:

Now we have set up the components, routes, and controllers for adding the forms for login and Sign up. Now let’s start writing HTML in handlebars, adding validations and implementing validations for the form components. In this blog, I will be sharing the code of Login form and actions related to logging In of user. You can check the whole code my Pull Request which I have made for adding these Forms.

Step 4.1: Creating a Login Form

<div class="ui hidden divider"></div>
<div class="ui raised segment">
    <div class="ui stackable column doubling centered grid">
        <div class="ui middle aligned center aligned grid">
            <div class="row" >
                <div class="column">
                    <h1 class="ui orange header">
                        Welcome back !
                        <div class="sub header">We're happy  helping you get beautiful name badges.</div>
                    </h1>
                    <div class="ui hidden divider"></div>
                    <form class="ui form">
                        <div class="ui stacked element">
                            <div class="field required">
                                <div class="ui left icon input">
                                    <i class="mail icon"></i>
                                    {{input type="text" value=email name="email" placeholder="E-mail address"}}
                                </div>
                            </div>
                            <div class="field required">
                                <div class="ui left icon input">
                                    <i class="lock icon"></i>
                                    {{input type="password" value=password name="password" placeholder="Password"}}
                                </div>
                            </div>
                            <button class="ui button orange fluid" style="margin-bottom: 10px;" {{ action 'logIn' 'password' }}>Log In</button>
                            <a href="#" class="text muted"> Forgot your password ?</a>
                            <div class="ui divider"></div>
                            <a href="{{href-to 'signup'}}" class="text muted weight-800">Don't have an account yet? Signup</a>
                        </div>
                    </form>
                    <div class="ui horizontal divider">
                        Or
                    </div>
                    <h1 class="ui header">
                        <div class="sub header">Login with</div>
                    </h1>
                </div>
            </div>
            <div class="three column row">
                <div class="column">
                    <div class="ui vertical animated red button fluid" {{ action 'logIn' 'google' }}>
                        <div class="hidden content">Google</div>
                        <div class="visible content">
                            <i class="google plus icon"></i>
                        </div>
                    </div>
                </div>
                <div class="column">
                    <div class="ui vertical animated violet button fluid" tabindex="0" {{ action 'logIn' 'facebook' }}>
                        <div class="hidden content">Facebook</div>
                        <div class="visible content">
                            <i class="facebook f icon"></i>
                        </div>
                    </div>
                </div>
                <div class="column">
                    <div class="ui vertical animated blue button fluid" tabindex="0" {{ action 'logIn' 'twitter' }}>
                        <div class="hidden content">Twitter</div>
                        <div class="visible content">
                            <i class="twitter icon"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

 

Step 4.2: Adding Form Validations

import Component from '@ember/component';

export default Component.extend({
  init() {
    this._super(...arguments);
  },

  actions: {
    logIn(provider) {
      let email = '';
      let password = '';
      if (provider == 'password') {
        email = this.get('email');
        password = this.get('password');
      }
      this.get('login')(provider, email, password);
    },

    logOut() {
      this.get('session').close();
    }
  },

  didRender() {
    this.$('.ui.form')
      .form({
        inline : true,
        delay  : false,
        fields : {
          email: {
            identifier : 'email',
            rules      : [
              {
                type   : 'email',
                prompt : 'Please enter a valid email address'
              }
            ]
          },
          password: {
            identifier : 'password',
            rules      : [
              {
                type   : 'empty',
                prompt : 'Please enter a password'
              }
            ]
          }
        }
      })
    ;
  }
});

 

Step 4.3: Adding Login Actions

import Ember from 'ember';

import Controller from '@ember/controller';

const { inject } = Ember;

export default Controller.extend({
  session: inject.service(),
  beforeModel() {
    return this.get('session').fetch().catch(function() {});
  },
  actions: {
    login(provider, email, password) {
      const that = this;
      if (provider === 'password') {
        this.get('session').open('firebase', {
          provider: 'password',
          email,
          password
        }).then(function(userData) {
          console.log(userData);
          that.transitionToRoute('/');
        }).catch(function(err) {
          console.log(err.message);
        });
      } else {
        const that = this;
        this.get('session').open('firebase', {
          provider
        }).then(function(userData) {
          console.log(userData);
          that.transitionTo('/');
        }).catch(function(err) {
          console.log(err.message);
        });
      }
    },

    logOut() {
      this.get('session').close();
    }
  }
});

 

I have made Login form and in a similar way I implemented the SignUp form and complete code can be seen in my Pull Request.

Now, we are done with writing HTML in handlebars, adding validations and implementing validations for the form components.

Step 5:

Now run the server to see the implemented changes by the following command.

$ ember serve

 

It will show like this :

Navigate to localhost to see the changes.

  • Login Form

  • Sign up  Form

  • Form Validations

Now we are all done with setting up Log In and Signup Forms and their validations using Semantic UI in the badgeyay repository.

This is how I have added Log In and Signup Forms and their validations in my Pull Request.

Resources:

  • Semantic UI Docs – Link
  • Ember Docs – Link
Continue ReadingCreating Forms and their validation using Semantic UI in Badgeyay

Structure of Open Event Frontend

In Open Event Frontend, new contributors always fall into a dilemma of identifying the proper files where they have to make changes if they want to contribute. The project structure is quite complex and which is obvious because it is a large project. So, in this blog, we will walk through the structure of Open Event Frontend.

Following are the different folders of the project explained:

Root:
The root of the project contains folders like app, config, kubernetes, tests, scripts. Our main project is in the app folder where all the files are present. The config folder in the root has files related to the deployment of the app in development, production, etc. It also has the environment setup such as host, api keys, etc. Other files such as package.json, bower.json, etc are basically to store the current versions of the packages and to ease the installation of the project.

App:
The app folder has all the files and is mainly classified into the following folder:
adapters
components
controllers
helpers
Initializers
mixins
models
routes
serializers
services
styles
templates
transforms
utils

The folders with their significance are listed below:

Adapters: This folder contains the files for building URLs for our endpoints. Sometimes it happens to have a somewhat customised URL for an endpoint which we pass through adapter to modify it.
Components: This folder contains different components which we reuse in our app. For example, the image uploader component can be used at multiple places in our app, so we keep such elements in our components. This folder basically contains the js files of all the components(since when we generate a component, a js file and a hbs template is generated).
Controllers: This folder contains the controller associated with each route. Since the main principle of ember js is DDAU i.e data down actions up, all the actions are written in the files of this folder.
Helpers: Many a time it happens that, we want to format date, time, encode URL etc. There are some predefined helpers but sometimes custom helpers are also needed. All of them have been written in helpers folder.
Initializers: This folder has a file for now called ‘blanket.js’ which basically injects the services into our routes, components. So if you want to write any service and want to inject it into routes/components, it should go in here.
Mixins: In EmberJS the Mixin class can create objects whose properties and functions can be shared amongst other classes and instances. This allows for an easy way to share behavior between objects as well as design objects that may need multiple inheritance. All of them used for the application are in the mixins folder.
Models: This folder contains the schema’s for our data. Since we are using ember data, we need to have proper skeleton of the data. All of this goes it this folder. Observing this folder will show you some models like user, event, etc.
Routes: This folder contains the js files of the routes created. Routes handle which template to render and what to return from the model, etc.
Serializers: We use serializers to modify the data that ember sends automatically in a request. Consider we want to get a user with the help of user model, and don’t want to get the password attribute present in it. We can thus omit that by defining it in a serializer.
Services: Services are the ember objects which are available throughout the running time of the application. These are used to perform tasks like getting current user model, making third party API calls etc. All such services go in this folder.
Styles: As the name infers, all the style sheets go in here.
Templates: A template is generated with generation of each route and component. All of them go here. Thus, the markup will be written over here.
Transforms: Ember Data has a feature called transforms that allow you to transform values before they are set on a model or sent back to the server. In our case, we have a transform called moment.
Utils: This folder contains some functions exported as modules which are reusable. There is some JSON data as well.

References: Ember JS official guide: https://guides.emberjs.com/v2.17.0/
Blog posts: https://spin.atomicobject.com/2015/09/17/ember-js-clean/
http://www.programwitherik.com/ember-pods/

Continue ReadingStructure of Open Event Frontend

Implementing the tickets API at the ‘tickets/add-order’ route

In Open Event Frontend, we have the ‘tickets/add-order’ route for a specific event which facilitates us to add the order based on the tickets that we create at the time of creation of event. The tickets are listed at the ‘tickets/add-order’ route where we can select the tickets required for example, ‘free’, ‘paid’, the payment type and proceed to the ticket buyer’s info page.

This is how we achieved implementing the API:
We use table to show the data to the user, the columns of which are Ticket Type, Price, Quantity, Item Total something like:

So, the workflow to achieve this is as follows:

  • Query the tickets for current event.
  • Have a controller to calculate the ‘Grand Total’ of the individual tickets that the user wants to buy.
  • Show the tickets in our table.

Querying the tickets: Since we are using ember data, we query tickets by the following query in our model method of route.

  model() {
    return this.modelFor('events.view').query('tickets', {});
  }

Thus, the above query shows that we get the current event by actually querying the model for route ‘events.view’ which returns the current event and then query the tickets model so that we get the tickets associated with the current event.

Since there is no UI table support for ember data, we are using a custom table for all the tables in Open Event Frontend and pass the data to it. To render the data in tables, we follow the following approach.
In our controller, we have a columns property as:

columns: [
    {
      propertyName : 'name',
      title        : 'Ticket Type'
    },
    {
      propertyName   : 'price',
      title          : 'Price(US$)',
      disableSorting : true
    },
    {
      propertyName : '',
      title        : 'Quantity',
      template     : 'components/ui-table/cell/cell-input-number'
    },
    {
      propertyName : 'itemTotal',
      title        : 'Item Total'
    }
  ]

The propertyName maps the property of the objects returned from the server i.e in our case, the ‘tickets’. Thus, we pass this skeleton of columns and data from the model to our component so as to render the table in view.

  {{events/events-table columns=columns data=model
    useNumericPagination=true
    showGlobalFilter=true
    showPageSize=true
  }

Also, as seen from the image shown earlier in this blog post, we can see that we also need to calculate the ‘Grand Total’ of the total purchase. Thus, we have a computed property in controller to do this:

  total: computed('model.@each.itemTotal', function() {
    let sum = 0.0;
    this.get('model').forEach(ticket => {
      sum += ticket.get('itemTotal');
    });
    return sum;
  }),

We iterate over the each ‘itemTotal’ in the model and keep on adding it so that the total purchase gets added accordingly. Lastly we show the Grand Total to the user as seen in the image shown earlier in the blog.
Thus, the user can select the tickets and proceed towards the checkout.

Resources:
Ember data official guide
Blog on ember data by Andy Crum

Continue ReadingImplementing the tickets API at the ‘tickets/add-order’ route

Creating an Elementary Oscilloscope in PSLab’s Remote Framework

The last couple of blog posts explained how we could put together the versatility of ember components, the visual appeal of jqplot, the flexibility of Python Flask, and the simplicity of Python itself in order to make simple scripts for PSLab that would could be run on a server by a remote client anywhere on the web. We have also seen how callbacks could be assigned to widgets created in these scripts in order to make object oriented applications. In this blog post, we shall see how to assign a capture method to a button, and update a plot with the received data. It will also demonstrate how to use ember-lodash to perform array manipulations.

Specifying the return data type in the callback success routine

For a more instructive write-up on assigning callbacks, please refer to these posts .

Whenever the callback assigned to a button is a function that returns an array of elements, and the target for the resultant data is a plot, the stacking order of the returned array must be specified in order to change its shape to suit the plotting library. The default return data from a capture routine (oscilloscope) is made up of separate arrays for X coordinate and Y coordinate values. Since JQplot requires [X,Y] pairs , we must specify a stacking order of ‘xy’ so that the application knows that it must convert them to pairs (using lodash/zip)  before passing the result to the plot widget. Similarly, different stacking orders for capture2, and capture4 must also be defined.

Creating an action that performs necessary array manipulations and plots the received data

It can be seen from the excerpt below, that if the onSuccess target for a callback is specified to be a plot in the actionDefinition object, then the stacking order is checked, and the returned data is modified accordingly

Relevant excerpt from controllers/user-home.js/runButtonAction

if (actionDefinition.success.type === 'update-plot') {
  if (actionDefinition.success.stacking === 'xy') {
    $.jqplot(actionDefinition.success.target, [zip(...resultValue)]).replot();
  } else if (actionDefinition.success.stacking === 'xyy') {
    $.jqplot(actionDefinition.success.target, [zip(...[resultValue[0], resultValue[1]]), zip(...[resultValue[0], resultValue[2]])]).replot();
  } else if (actionDefinition.success.stacking === 'xyyyy') {
    $.jqplot(actionDefinition.success.target, [zip(...[resultValue[0], resultValue[1]]), zip(...[resultValue[0], resultValue[2]]), zip(...[resultValue[0], resultValue[3]]), zip(...[resultValue[0], resultValue[4]])]).replot();
  } else {
    $.jqplot(actionDefinition.success.target, resultValue).replot();
  }
}

 

With the above framework in place, we can add a plot with the line plt = plot(x, np.sin(x)) , and associate a button with a capture routine that will update its contents with a single line of code: button(‘capture1’,”capture1(‘CH1’,100,10)”,”update-plot”,target=plt)

Final Result

The following script created on the pslab-remote platform makes three buttons and plots, and sets the buttons to invoke capture1, capture2, and capture4 respectively when clicked.

import numpy as np
x=np.linspace(0,2*np.pi,30)
plt = plot(x, np.sin(x))
button('capture 1',"capture1('CH1',100,10)","update-plot",target=plt)

plt2 = plot(x, np.sin(x))
button('capture 2',"capture2(50,10)","update-plot",target=plt2,stacking='xyy')

plt3 = plot(x, np.sin(x))
button('capture 4',"capture4(50,10)","update-plot",target=plt3,stacking='xyyyy')

 

 

 

 

 

 

 

 

 

 

 

 

Resources

 

Continue ReadingCreating an Elementary Oscilloscope in PSLab’s Remote Framework

Implement Sessions API for the ‘admin/sessions’ Route in Open Event Frontend

In Open Event Frontend, under the ‘admin/sessions’ route, the admin can track the record of the sessions. The info which is shown along with the sessions is the speakers for the session, its title, submitted date, start time, end time. So to integrate the sessions API, we followed the following approach:

Firstly, we add a sessions model and establish its relationship with the speakers model since we need speaker names also:

export default ModelBase.extend({
  title         : attr('string'),
  subtitle      : attr('string'),
  startsAt      : attr('moment'),
  endsAt        : attr('moment'),
  j
  sessionType   : belongsTo('session-type'),
  microlocation : belongsTo('microlocation'),
  track         : belongsTo('track'),
  speakers      : hasMany('speaker'),
  event         : belongsTo('event'), // temporary
});

After creating the model for sessions, we do the query to get the sessions.
Since, the sessions here are classified into the sections like ‘pending’, ‘accepted’, ‘rejected’, ‘deleted’, etc, we need to filter the response returned by the query:

if (params.sessions_state === 'pending') {
      filterOptions = [
        {
          name : 'state',
          op   : 'eq',
          val  : 'pending'
        }
      ];
    } else if (params.sessions_state === 'accepted') {
      filterOptions = [
        {
          name : 'state',
          op   : 'eq',
          val  : 'accepted'
        }
      ];
    }

Above code shows how we filter the response on the server side itself. Hence we pass the filterOptions array to the query as follows:

return this.get('store').query('session', {
      include      : 'event,speakers',
      filter       : filterOptions,
      'page[size]' : 10
});

Once we get data from the query, we just pass it to our controller to implement the columns.
Once we retrieve data from the query, we just pass it to our controller to implement the columns. Since, we are using a different way to render the ember data in the column, the approach goes as follows:
In our controller we do:

export default Controller.extend({
  columns: [
    {
      propertyName   : 'event.name',
      title          : 'Event Name',
      disableSorting : true
    },
    {
      propertyName : 'title',
      title        : 'Title'
    },
    {
      propertyName   : 'speakers',
      template       : 'components/ui-table/cell/cell-speakers',
      title          : 'Speakers',
      disableSorting : true
    }
  ]
});

I have shown here only limited columns, there are others too. Here, we are mapping the propertyName to the attribute returned by the server. Also the ‘title’ indicates the column name. We can also create a custom template(as a component) if we want customization while rendering the data in the rows and columns. For example, if we want to iterate the multiple speaker names for a session, we can do:

<div class="ui list">
  {{#each record.speakers as |speaker|}}
    <div class="item">
      {{speaker.name}}
    </div>
  {{/each}}
</div>

As a result, we can make custom templates for a particular property. Another example of formatting the response is:

<span>
  {{moment-format record.startsAt 'MMMM DD, YYYY - HH:mm A'}}
  {{moment-format record.endsAt 'MMMM DD, YYYY - HH:mm A'}}
</span>

Thus, we get the following view after integration of API:

Resources:

Ember data Official guide

Blog on medium: By “Embedly” publication

Continue ReadingImplement Sessions API for the ‘admin/sessions’ Route in Open Event Frontend

Enhancing the Functionality of User Submitted Scripts in the PSLab-remote framework

The remote-lab framework of the pocket science lab enables users to access their devices remotely via the internet. Its design involves an API server built with Python-Flask and a webapp that uses EmberJS. This post is the latest in a series of blog posts which have explored and elaborated various aspect of the remote-lab such as designing the API server and testing with Postman, remote execution of function strings, automatic deployment on various domains etc. It also supports creating and submitting python scripts which will be run on the remote server, and the console output relayed to the webapp.

In this post, we shall take a look at how we can extend the functionality by providing support for object oriented code in user submitted scripts.

Let’s take an example of a Python script where the user wishes to create a button which when clicked will read a voltage via the API server, and display the value to the remote user. Clearly, an interpreter that only provides the console output is not enough for this task. We need the interpreter to generate an app structure that also includes callbacks for widgets such as buttons, and JSON objects are an obvious choice for relaying such a structure to the webapp.

In a nutshell, we had earlier created an API method that could execute a python script and return a string output, and now we will modify this method to return a JSON encoded structure which will be parsed by the webapp in order to display an output.

Let’s elaborate this with an example : Example.py

print ('testing')
print ('testing some changes..... ')
print_('highlighted print statement')

 

JSON returned by the API [localhost:8000/runScriptById] , for the above script:

{"Date": "Tue, 01 Aug 2017 21:39:12 GMT", "Filename": "example.py", "Id": 4,
 "result": [
  {"name": "print", "type": "span", "value": "('testing',)"},
  {"name": "print", "type": "span", "value": "('testing some changes..... ',)"},
  {"class": "row well", "name": "print", "type": "span", "value": "highlighted print statement"}
  ],
"status": true}
Screenshot of the EmberJS webapp showing the output rendered with the above JSON

Adding Support for Widgets

In the previous section, we laid the groundwork for a flexible platform. Instead of returning a string, the webapp accepts a JSON object and parses it. We shall now add support for a clickable button which can be associated with a valid PSLab function.

An elementary JS twiddle has been made by Niranjan Rajendran which will help newbies to understand how to render dynamic templates via JSON objects retrieved from APIs. The twiddle uses two API endpoints; one to retrieve the compiled JSON output, and another to act as a voltmeter method which returns a voltage value.

To understand how this works in pslab-remote, consider a one line script called button.py:

button('get voltage',"get_voltage('CH1')")

The objective is to create a button with the text ‘get voltage’ on it , and which when clicked will run the command ‘get_voltage(‘CH1’)’ on the API server, and display the result.

When this script is run on the API server, it returns a JSON object with the following structure:

{"Date": "Tue, 01 Aug 2017 21:39:12 GMT", "Filename": "example.py", "Id": 4,
 "result": [  {"type":"button","name":"button-id0","label":"get_voltage","fetched_value":"","action":{"type":"POST","endpoint":"get_voltage('CH1')","success":{"datapoint":'result',"type":"display_number", "target":"button-id0-label"}}},
  {"name": "button-id0label", "type": "label", "value": ""},
  ],
"status": true}

The above JSON object is parsed by the webapp’s user-home template, and a corresponding button and label are generated. The following section of code from user-home.hbs renders the JSON object

{{#each codeResults as |element|}}
  {{#if (eq element.type 'label')}}
    <label  id="{{element.name}}" class="{{element.class}}">{{element.value}}</label>
  {{/if}}
  {{#if (eq element.type 'button')}}
    <button id="{{element.name}}" {{action 'runButtonAction' element.action}}>{{element.label}}</button>
  {{/if}}
{{/each}}    

An action was also associated with the the created button, and this is the “get_voltage(‘CH1’)” string which we had specified in our one line script.

For the concluding section, we shall see how this action is invoked when the button is clicked, and how the returned value is used to update the contents of the label that was generated as part of this button.

Action defined in controllers/user-home.js :

runButtonAction(actionDefinition) {
  if(actionDefinition.type === 'POST') {
    Ember.$.post('/evalFunctionString',{'function':actionDefinition.endpoint},this,"json")
      .then(response => {
        const resultValue = Ember.get(response, actionDefinition.success.datapoint);
        if (actionDefinition.success.type === 'display_number') {
           Ember.$('#' + actionDefinition.success.target).text(resultValue.toFixed(3));
        }
      });
  }
}

The action string is passed to the evalFunctionString endpoint of the API, and the contents are mapped to the display label.

Screencast of the above process
Resources:
Continue ReadingEnhancing the Functionality of User Submitted Scripts in the PSLab-remote framework

PSLab Remote Lab: Automatically deploying the EmberJS WebApp and Flask API Server to different domains

The remote-lab software of the pocket science lab enables users to access their devices remotely via the internet. Its design involves an API server designed with Python Flask, and a web-app designed with EmberJS that allows users to access the API and carry out various tasks such as writing and executing Python scripts. For testing purposes, the repository needed to be setup to deploy both the backend as well as the webapp automatically when a build passes, and this blog post deals with how this can be achieved.

Deploying the API server

The Heroku PaaS was chosen due to its ease of use with a wide range of server software, and support for postgresql databases. It can be configured to automatically deploy branches from github repositories, and conditions such as passing of a linked CI can also be included. The following screenshot shows the Heroku configuration page of an app called pslab-test1. Most of the configuration actions can be carried out offline via the Heroku-Cli

 

In the above page, the pslab-test1 has been set to deploy automatically from the master branch of github.com/jithinbp/pslab-remote . The wait for CI to pass before deploy has been disabled since a CI has not been setup on the repository.

Files required for Heroku to deploy automatically

Once the Heroku PaaS has copied the latest commit made to the linked repository, it searches the base directory for a configuration file called runtime.txt which contains details about the language of the app and the version of the compiler/interpretor to use, and a Procfile which contains the command to launch the app once it is ready. Since the PSLab’s API server is written in Python, we also have a requirements.txt which is a list of dependencies to be installed before launching the application.

Procfile

web: gunicorn app:app –log-file –

runtime.txt

python-3.6.1

requirements.txt

gunicorn==19.6.0
flask >= 0.10.1
psycopg2==2.6.2
flask-sqlalchemy
SQLAlchemy>=0.8.0
numpy>=1.13
flask-cors>=3.0.0

But wait, our app cannot run yet, because it requires a postgresql database, and we did not do anything to set up one. The following steps will set up a postgres database using the heroku-cli usable from your command prompt.

  • Point Heroku-cli to our app
    $ heroku git:remote -a pslab-test1
  • Create a postgres database under the hobby-dev plan available for free users.
    $ heroku addons:create heroku-postgresql:hobby-dev

    Creating heroku-postgresql:hobby-dev on ⬢ pslab-test1… free
    Database has been created and is available
    ! This database is empty. If upgrading, you can transfer
    ! data from another database with pg:copy
    Created postgresql-slippery-81404 as HEROKU_POSTGRESQL_CHARCOAL_URL
    Use heroku addons:docs heroku-postgresql to view documentation

  • The previous step created a database along with an environment variable HEROKU_POSTGRESQL_CHARCOAL_URL . As a shorthand, we can also refer to it simply as CHARCOAL .
  • In order to make it our primary database, it must be promoted

    $ heroku pg:promote HEROKU_POSTGRESQL_CHARCOAL_URL
    The database will now be available via the environment variable DATABASE_URL

  • Further documentation on creating and modifying postgres databases on Heroku can be found in the articles section .

At this point, if the app is in good shape, Heroku will automatically deploy its contents to pslab-test1.herokuapp.com. We can test it using a developer tool such as Postman, or make our own webapp to use it.

Deploying the EmberJS WebApp

Since we are using the free plan on Heroku which only allows one dyno, our EmberJS webapp which shares the repository cannot be deployed on the same heroku server. Therefore, we must look for other domains where the frontend can be deployed.

Surge.sh allows easy deployment of Ember apps, and we shall set up our CI’s configuration file .travis.yml to do this for us when a pull request is made, and the build passes

This excerpt from .travis.yml only shows parts relevant to deployment on Surge.sh

after_success:
– pushd frontend
– bash surge_deploy.sh
– popd

Once the build has passed, the after_success hook executes a script called surge_deploy.sh which is located in the directory of the webapp.

Contents of surge_deploy.sh

#!/usr/bin/env bash
if [ “$TRAVIS_PULL_REQUEST” == “false” ]; then
echo “Not a PR. Skipping surge deployment”
exit 0
fi

ember build –environment=’production’

export REPO_SLUG_ARRAY=(${TRAVIS_REPO_SLUG//\// })
export REPO_OWNER=${REPO_SLUG_ARRAY[0]}
export REPO_NAME=${REPO_SLUG_ARRAY[1]}

npm i -g surge

# Details of a dummy account. So can be added to vcs.
export SURGE_LOGIN=j********r@gmail.com
export SURGE_TOKEN=4********************************f
export DEPLOY_DOMAIN=https://${REPO_NAME}.surge.sh
surge –project ./dist –domain $DEPLOY_DOMAIN;

The variables SURGE_LOGIN and SURGE_TOKEN must be specified, otherwise Surge will open a login prompt, and since there is no way to feed details into a prompt in a Travis build, it will timeout and fail. The surge token can be obtained with a simple `surge login` followed by `surge token` on your system’s terminal.

Final Application

A user’s homepage on the webapp deployed at pslab-remote.surge.sh . The EmberJS app has been configured to send all AJAX requests to the API server located at pslab-remote.herokuapp.com .

Resources
Continue ReadingPSLab Remote Lab: Automatically deploying the EmberJS WebApp and Flask API Server to different domains

Implementing Notifications API in Open Event Frontend

In Open Event Frontend, at the index page of the application, we have a notification dropdown in which a user gets the notifications regarding the events, sessions, etc. Thus, a user gets notified for the particular event or session he wants to receive notifications about. While dealing with an issue, we had to integrate the API with the frontend. We achieved it as follows:

First, we create a model of notifications so that we have basic structure ready. It goes as follows:

export default ModelBase.extend({
  title      : attr('string'),
  message    : attr('string'),
  isRead     : attr('boolean', { defaultValue: false }),
  receivedAt : attr('moment'),

  user: belongsTo('user')
});

Thus, we have fields like title, message, isRead, receivedAt which we will get from the server response as JSON which we will need to show on the page. To show the notifications to the user, first we need to query the notifications for a specific user using ember data. Since we are querying the notifications for a specific user when he is logged in, we are also having relationship between user and notification as shown in the above notification model. In user model we do:

notifications: hasMany('notification')

Now, we query the notifications in our application route i.e routes/application.js

model() {
    if (this.get('session.isAuthenticated')) {
      return new RSVP.Promise((resolve, reject) => {
        this.store.findRecord('user', this.get('authManager.currentUser.id'), { reload: true })
          .then(user => {
            user.get('notifications').then(resolve).catch(reject);
          })
          .catch(reject);
      });
    }
  }

The reason why we used a RSVP promise here was because the authManager couldn’t load the user befor the notifications were queried and returned. Thus, we query the notifications by using currentUser from authManager. Thus, in our template, we iterate over our notifications as follows:

    {{#each notifications as  notification }}
      <div class="item">
        <div class="header">
          {{notification.title}}
        </div>
        <div class="content weight-600">
          {{notification.description}}
        </div>
        <div class="left floated content">
          {{moment-from-now notification.createdAt}}
        </div>
      </div>
    {{/each}}

The notifications are thus shown to the user when he clicks the icon in the nav-bar. As a result, we get the following notifications in the dropdown:

Resources:

Ember data official guide

Blog on Ember data by Embedly.

Continue ReadingImplementing Notifications API in Open Event Frontend

Implementing Email Notifications in Open Event Frontend

In Open Event Frontend, we have a ‘settings/email-preferences’ route where we give the user an access to change the email-notifications that he wants to subscribe for a specific event. For now, we are having three notifications for an event which the user can toggle to on and off. To achieve this, we did the following things:

First, we create a model for email-notifications so as to have a skeleton of the data that we are going to receive from the JSON API.

export default ModelBase.extend({

  /**
   * Attributes
   */

  nextEvent           : attr('boolean'),
  newPaper            : attr('boolean'),
  sessionAcceptReject : attr('boolean'),
  sessionSchedule     : attr('boolean'),
  afterTicketPurchase : attr('boolean'),

  /**
   * Relationships
   */
  user  : belongsTo('user'),
  event : belongsTo('event')
});

Thus, above code shows the attributes which we are going to receive via our JSON API and we will render the data accordingly on the page. We have established the relationship of the email-notifications with user and event so that in future wherever needed, we can query the records from either side. The client side has checkboxes to show the data to the user. Following is the format of the checkboxes:

<div class="row">
        <div class="column eight wide">
          {{t 'New Paper is Submitted to your Event'}}
        </div>
        <div class="ui column eight wide right aligned">
          {{ui-checkbox class='toggle' checked=preference.newPaper onChange=(pipe-action (action (mut preference.newPaper)) (action 'savePreference' preference))}}
        </div>
      </div>
      <div class="row">
        <div class="column eight wide">
          {{t 'Change in Schedule of Sessions in your Event'}}
        </div>
        <div class="ui column eight wide right aligned">
          {{ui-checkbox class='toggle' checked=preference.sessionSchedule onChange=(pipe-action (action (mut preference.sessionSchedule)) (action 'savePreference' preference))}}
        </div>
      </div>

The states of the checkboxes are determined by the data that we receive from the API. For example, if for a record, the API returns:

nextEvent           : true,
newPaper            : false,
sessionAcceptReject : true,
sessionSchedule     : false,
afterTicketPurchase : false,

Then, the respective states will be shown by the checkbox and the user can toggle the states to change the email-preferences as they want.

Thus to get the data sent by the server to the client, we return it as a model and query it as:

model() {
    return this.get('authManager.currentUser').query('emailNotifications', { include: 'event' });
}

As we can see, as mentioned earlier, we kept the relationships so that we can query the email-notifications specific to the particular user or specific to particular event. Here, we are showing a user’s email-notifications and hence we queried it with the user relationship.
The authManager loads the currentUser and queries the email-notifications for a particular use. We also want the event details to show the email-preferences, hence we include the event model to be fetched in the query also.

We also let the user change the preferences of the email-notifications so that he can customise the notifications and keep the ones he wants to receive. We implement the updating of email-preferences API as follows:

Whenever a user toggles the checkbox, we are having an action as called ‘savePreference’, which handles the updation of the preferences.

 

{{ui-checkbox class='toggle' checked=preference.newPaper onChange=(pipe-action (action (mut preference.newPaper)) (action 'savePreference' preference))}}

savePreference(emailPreference) {
      emailPreference.save()
        .then(() => {
          this.get('notify').success(this.l10n.t('Email notifications updated successfully'));
        })
        .catch(() => {
          emailPreference.rollbackAttributes();
          this.get('notify').error(this.l10n.t('An unexpected error occurred.'));
        });
    }

We are passing the parameter(the whole preference object to the action), and then just performing a ‘save’ method on it which will send a PATCH request to the server to update the data.

Thus, in this way, the user can change the email-notification preferences in the Open Event Frontend.

Resources:
Ember data Official guide
Blog on Models and Ember data by Daniel Lavigne: Learning Ember.js Part 4: Models

Source code: https://github.com/fossasia/open-event-frontend/pull/537/files

Continue ReadingImplementing Email Notifications in Open Event Frontend