Handling graph plots using MPAndroid chart in PSLab Android App

In PSLab Android App, we expose the Oscilloscope and Logic Analyzer functionality of PSLab hardware device. After reading data-points to plot, we need to show plot data on graphs for better understanding and visualisation. Sometimes we need to save graphs to show output/findings of the experiment. Hence we will be using MPAndroidChart library to plot and save graphs as it provides a nice and clean methods to do so.

First add MPAndroid Chart as dependency in your app build.gradle to include the library

dependencies {
 compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
}

For chart view in your layout file, there are many available options like Line Chart, Bar Chart, Pie Chart, etc. For this post I am going to use Line Chart.

Add LineChart in your layout file

<com.github.mikephil.charting.charts.LineChart
        android:id="@+id/lineChart"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Take a reference to LineChart of layout file in your Activity/Fragment

LineChart lineChart = (LineChart) findViewById(R.id.chart);// Activity
LineChart lineChart = (LineChart) view.findViewById(R.id.chart);// Fragment

Now we add dataset to LineChart of layout file, I am going to add data for two curves sine and cosine function to plot sine and cosine wave on LineChart. We create two different LineDataSet one for the sine curve entries and other for the cosine curve entries.

List <Entry> sinEntries = new ArrayList<>(); // List to store data-points of sine curve 
List <Entry> cosEntries = new ArrayList<>(); // List to store data-points of cosine curve

// Obtaining data points by using Math.sin and Math.cos functions
for( float i = 0; i < 7f; i += 0.02f ){
sinEntries.add(new Entry(i,(float)Math.sin(i)));
cosEntries.add(new Entry(i,(float)Math.cos(i)));
}

List<ILineDataSet> dataSets = new ArrayList<>(); // for adding multiple plots

LineDataSet sinSet = new LineDataSet(sinEntries,"sin curve");
LineDataSet cosSet = new LineDataSet(cosEntries,"cos curve");

// Adding colors to different plots 
cosSet.setColor(Color.GREEN);
cosSet.setCircleColor(Color.GREEN);
sinSet.setColor(Color.BLUE);
sinSet.setCircleColor(Color.BLUE);

// Adding each plot data to a List
dataSets.add(sinSet);
dataSets.add(cosSet);

// Setting datapoints and invalidating chart to update with data points
lineChart.setData(new LineData(dataSets));
lineChart.invalidate();

After adding datasets to chart and invalidating it, chart is refreshed with the data points which were added in dataset.

After plotting graph output would look like the image below:

You can change the dataset and invalidate chart to update it with latest dataset.

To save graph plot, make sure you have permission to write to external storage, if not add it into your manifest file

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

To save the photo of chart into Gallery:

lineChart.saveToGallery("title");

To save a some specific location:

lineChart.saveToPath("title", "Location on SD Card");

If you want to do some resizing in chart or save two three charts in a single image, you can do so by taking out the Bitmaps and processing them to meet your requirements:

lineChart.getChartBitmap();

Resources

Continue ReadingHandling graph plots using MPAndroid chart in PSLab Android App

CSS Styling Tips Used for loklak Apps

Cascading Style Sheets (CSS) is one of the main factors which is valuable to create beautiful and dynamic websites. So we use CSS for styling our apps in apps.loklak.org.

In this blog post am going to tell you about few rules and tips for using CSS when you style your App:

1.Always try something new – The loklak apps website is very flexible according to the user whomsoever creates an app. The user is always allowed to use any new CSS frameworks to create an app.

2.Strive for Simplicity – As the app grows, we’ll start developing a lot more than we imagine like many CSS rules and elements etc. Some of the rules may also override each other without we noticing it. It’s good practice to always check before adding a new style rule—maybe an existing one could apply.

3.Proper Structured file –

  • Maintain uniform spacing.
  • Always use semantic or “familiar” class/id names.
  • Follow DRY (Don’t Repeat Yourself) Principle.

CSS file of Compare Twitter Profiles App:

#searchBar {
    width:500px;
}

table {
  border-collapse: collapse;
  width: 70%;
}

th, td {
  padding: 8px;
  text-align: center;
  border-bottom: 1px solid#ddd;
}

 

The output screen of the app:


Do’s and Don’ts while using CSS:

  • Pages must continue to work when style sheets are disabled. In this case this means that the apps which are written in apps.loklak.org should run in any and every case. Let’s say for instance, when a user uses a old browsers or bugs or either because of style conflicts.
  • Do not use the !important attribute to override the user’s settings. Using the !important declaration is often considered bad practice because it has side effects that mess with one of CSS’s core mechanisms: specificity. In many cases, using it could indicate poor CSS architecture.
  • If you have multiple style sheets, then make sure to use the same CLASS names for the same concept in all of the style sheets.
    Do not use more than two fonts. Using a lot of fonts simply because you can will result in a messy look.
  • A firm rule for home page design is more is less : the more buttons and options you put on the home page, the less users are capable of quickly finding the information they need.

Resources:

Continue ReadingCSS Styling Tips Used for loklak Apps

How the Compare Twitter Profiles loklak App works

People usually have a tendency to compare their profiles with others, So this is what exactly this app is used for: To compare Twitter profiles. loklak provides so many API’s which serves different functionalities. One among those API’s which I am using to implement this app is loklak’s User Details API. This API actually help in getting all the details of the user we search giving the user name as the query. In this app am going to implement a comparison between two twitter profiles which is shown in the form of tables on the output screen.

Usage of loklak’s User Profile API in the app:

In this app when the user given in the user names in the search fields as seen below:

The queries entered into the search field are taken and used as query in the User Profile API. The query in the code is taken in the following form:

var userQueryCommand = 'http://api.loklak.org/api/user.json?' +
                       'callback=JSON_CALLBACK&screen_name=' +
                       $scope.query;

var userQueryCommand1 = 'http://api.loklak.org/api/user.json?' +
                        'callback=JSON_CALLBACK&screen_name=' +
                        $scope.query1;

The query return a json output from which we fetch details which we need. A simple query and its json output:

http://api.loklak.org/api/user.json?screen_name=fossasia

Sample json output:

{
  "search_metadata": {"client": "162.158.50.42"},
  "user": {
    "$P": "I",
    "utc_offset": -25200,
    "friends_count": 282,
    "profile_image_url_https": "https://pbs.twimg.com/profile_images/1141238022/fossasia-cubelogo_normal.jpg",
    "listed_count": 185,
    "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/882420659/14d1d447527f8524c6aa0c568fb421d8.jpeg",
    "default_profile_image": false,
    "favourites_count": 1877,
    "description": "#FOSSASIA #OpenTechSummit 2017, March 17-19 in Singapore https://t.co/aKhIo2s1Ck #OpenTech community of developers & creators #Code #Hardware #OpenDesign",
    "created_at": "Sun Jun 20 16:13:15 +0000 2010",
    "is_translator": false,
    "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/882420659/14d1d447527f8524c6aa0c568fb421d8.jpeg",
    "protected": false,
    "screen_name": "fossasia",
    "id_str": "157702526",
    "profile_link_color": "DD2E44",
    "is_translation_enabled": false,
    "translator_type": "none",
    "id": 157702526,
    "geo_enabled": true,
    "profile_background_color": "F50000",
    "lang": "en",
    "has_extended_profile": false,
    "profile_sidebar_border_color": "000000",
    "profile_location": null,
    "profile_text_color": "333333",
    "verified": false,
    "profile_image_url": "http://pbs.twimg.com/profile_images/1141238022/fossasia-cubelogo_normal.jpg",
    "time_zone": "Pacific Time (US & Canada)",
    "url": "http://t.co/eLxWZtqTHh",
    "contributors_enabled": false,
    "profile_background_tile": true,
}

 

I am getting data from the json outputs as shown above, I use different fields from the json output like screen_name, favourites_count etc.

Injecting data from loklak API response using Angular:

As the loklak’s user profile API returns a json format file, I am using Angular JS to align the data according to the needs in the app.

I am using JSONP to retrieve the data from the API. JSONP or “JSON with padding” is a JSON extension wherein a prefix is specified as an input argument of the call itself. This how it is written in code:

$http.jsonp(String(userQueryCommand)).success(function (response) {
    $scope.userData = response.user;
 });

Here the response is stored into a $scope is an application object here. Using the $scope.userData variable , we access the data and display it on the screen using Javascript, HTML and CSS.

<div id="contactCard" style="pull-right">
    <div class="panel panel-default">
        <div class="panel-heading clearfix">
            <h3 class="panel-title pull-left">User 1 Profile</h3>
        </div>
        <div class="list-group">
            <div class="list-group-item">
                <img src="{{userData.profile_image_url}}" alt="" style="pull-left">
                <h4 class="list-group-item-heading" >{{userData.name}}</h4>
            </div>

In this app am also adding keyboard action and validations of fields which will not allow users to search for an empty query using this simple line in the input field.

ng-keyup="$event.keyCode == 13 && query1 != '' && query != '' ? Search() : null"

 


Resources:

Continue ReadingHow the Compare Twitter Profiles loklak App works

Packing and Unpacking Data in PSLab Android App

In PSLab we communicate with PSLab Hardware device, to exchange data, i.e we give a command or instruction and it responses accordingly. So this giving and receiving is in terms of packed byte string. Thus, we need some solid knowledge to pack and unpack data. In python communication library, there is struct module available. In JAVA we can use NIO’s ByteBuffer or implement our own functions. In this blog post I discuss both methods.  

In Python we have struct module for packing data in byte strings. As different languages interpret data types differently like Java takes 4 bytes for int and C++ takes 2 bytes for int. To send and receive data properly, we pack data in a byte string and unpack on other side with it’s data type properties. In PSLab, we have to communicate with device for various applications like getting calibration data during power up time as raw data doesn’t make much sense until calibration is applied on it.

You also need to take care of order of sequence of bytes like there are generally two types of order in which a sequence of bytes are stored in memory location:

  • Big – Endian: In which MSB is stored first.

    Source: Wikipedia
  • Little – Endian: In which LSB is stored first.

    Source: Wikipedia

In Python

The standard sizes and format characters of particular data type can be seen in the image below.

Format C Type Python Type Standard
x Pad byte No value
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer 4
l long integer 4
L unsigned long integer 4
q long long integer 8
Q unsigned long long integer 8
f float float 4
d double float 8
s char[] string
p char[] string
P void* integer

Source: Python Docs

For Packing data

import struct
struct.Struct(“B”).pack(254)   # Output ->  b’\xfe’
a = struct.Struct(“I”).pack(2544)   # Output -> b’\xf0\t\x00\x00′

Now a is the byte string that has packed value as 2544, this can be send to some device byte by byte and reconstructed on receiving side by knowing how many bytes does the data type received contains.

For Unpacking data

import struct
struct.unpack(“I”,a)  # Output -> (2544,)

In JAVA

For Packing data

Suppose you have to pack an integer, in java int takes 32 bits (4 bytes)

Using JAVA’s NIO’s ByteBuffer

byte[] bytes = ByteBuffer.allocate(4).putInt(2544).array();

If you want hardcore method to see what exactly is happening, use

byte[] intToByteArray(int value){
 return new byte[]{
     (byte)value >>> 24,
     (byte)value >>> 16,
     (byte)value >>> 8,
     (byte)value
  };
}

“>>>” is used for unsigned shifting, you can use according to your requirements.

After you have your byte array, you can easily create a string out of it and transmit.

For Unpacking data

Using JAVA’s NIO’s ByteBuffer

int fromByteArray(byte[] bytes){
int a = ByteBuffer.wrap(bytes).getInt();
return a;
}

It assumes that byte array is stored as Big Endian, if bytes in byte array is stored as Little Endian, add order() after wrap()

int fromByteArray(byte[] bytes){
int a = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
return a;
}

Note: Make sure the bytes array that you provide has same number of bytes as that of the data type that you are trying to unpack. For example: if you want int, bytes array should have 4 bytes as int type in JAVA has 4 bytes. If you want short, bytes array should have 2 bytes as short type in JAVA has 2 bytes.

To visualise underlying implementation, see

int from byteArray(byte[] bytes){
return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
}

In all above implementation big-endian order was assumed, you can modify function if you are using little-endian or some other sequence.

References

Continue ReadingPacking and Unpacking Data in PSLab Android App

Better Bookmark Display Viewholder in Home Screen of the Open Event Android App

Earlier in the Open Event Android app we had built the homescreen with the bookmarks showing up at the top as a horizontal list of cards but it wasn’t very user-friendly in terms of UI. Imagine that a user bookmarks over 20-30 sessions, in order to access them he/she might have to scroll horizontally a lot in order to access his/her bookmarked session. So this kind of UI was deemed counter-intuitive. A new UI was proposed involving the viewholder used in the schedule page i.e DayScheduleViewHolder, where the list would be vertical instead of horizontal. An added bonus was that this viewholder conveyed the same amount of information on lesser white space than the earlier viewholder i.e SessionViewHolder.

Old Design
New Design

 

 

 

 

 

 

 

 

 

Above are two images, one in the initial design, the second in the new design. In the earlier design the number of bookmarks visible to the user at a time was at most 1 or 2 but now with the UI upgrade a user can easily see up-to 5-6 bookmarks at a time. Additionally there is more relevant content visible to the user at the same time.

Additionally this form of design also adheres to Google’s Material Design guidelines.

Code Comparison of the two Iterations

Initial Design
sessionsListAdapter = new SessionsListAdapter(getContext(), mSessions, bookmarkedSessionList);
 sessionsListAdapter.setBookmarkView(true);
 bookmarksRecyclerView.setAdapter(sessionsListAdapter);
 bookmarksRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(),LinearLayoutManager.HORIZONTAL,false));

Here we are using the SessionListAdapter for the bookmarks. This was previously being used to display the list of sessions inside the track and location pages. It is again being used here to display the horizontal list of bookmarks.To do this we are using the function setBookmarkView(). Here mSessions consists the list of bookmarks that would appear in the homescreen.

Current Design
bookmarksRecyclerView.setVisibility(View.VISIBLE);
 bookMarksListAdapter = new DayScheduleAdapter(mSessions,getContext());
 bookmarksRecyclerView.setAdapter(bookMarksListAdapter);
 bookmarksRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

Now we are using the DayScheduleAdapter which is the same adapter used in the schedule page of the app. Now we use a vertical layout instead of a horizontal layout  but with a new viewholder design. I will be talking about the last line in the code snippet shortly.

ViewHolder Design (Current Design)

<RelativeLayout android:id="@+id/content_frame">
    <LinearLayout android:id="@+id/ll_sessionDetails">
        <TextView android:id="@+id/slot_title"/>
        <RelativeLayout>
            <TextView android:id="@+id/slot_start_time”/>
            <TextView android:id="@+id/slot_underscore"/>
            <TextView android:id="@+id/slot_end_time"/>

           <TextView android:id="@+id/slot_comma"/>
            <TextView android:id="@+id/slot_location”/>
            <Button android:id="@+id/slot_track"/>
            <ImageButton android:id="@+id/slot_bookmark"/>
        </RelativeLayout>

    </LinearLayout>
    <View android:id=”@+id/divider”/>
 </RelativeLayout>

This layout file is descriptive enough to highlight each element’s location.

In this viewholder we can also access to the track page from the track tag and can also remove bookmarks instantly.

The official widget to make a scroll layout in android is ScrollView. Basically, adding a RecyclerView inside ScrollView can be difficult . The problem was that the scrolling became laggy and weird.  Fortunately, with the appearance of Material Design , NestedScrollView was released and this becomes much easier.

bookmarksRecyclerView.setNestedScrollingEnabled(false);

With this small snippet of code we are able to insert a RecyclerView inside the NestedScrollView without any scroll lag.
So now we have successfully updated the UI/UX for the homescreen  to meet the requirements as given by the Material Design guidelines.

Resources

Continue ReadingBetter Bookmark Display Viewholder in Home Screen of the Open Event Android App

Maintain Aspect Ratio Mixin on Open Event Frontend

The welcome page of the Open-Event-Frontend is designed to contain cards that represent an event. A user is directed to the event-details page by clicking on the corresponding card. The page consists of an image that serves as the banner for the event and an overlapping div to provide some contrast against the image. A comment may also be added onto the image and along with the overlapping div it is wrapped in a container div.

Since we have given a specific height to the contrasting div, the background image shrinks according to the screen size but the contrasting div does not whenever we go from a large screen to a smaller screen.

Mobile view (before):-

We want our contrasting div also to resize in accordance to the image. To do it, we first define a sass mixin to maintain a common aspect ratio for image and overlapping div. Let us see it’s code.

 @mixin aspect-ratio($width, $height) {
    position: relative;
    &:before {
      display: block;
      content: "";
      width: 100%;
      padding-top: ($height / $width) * 100%;
    }
    > .content {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
    > img {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
  }

So what does this mixin actually doing is we are passing the height and width of the image and we are defining a pseudo element for our image and give it a margin top of (height/width)*100 since this value is related to image width (height: 0; padding-bottom: 100%; would also work, but then we have to adjust the padding-bottom value every time we change the width). Now we just position the content element and image as absolute with all four orientations set to 0. This just covers the parent element completely, no matter which size it has.

Now we can simply use our mixin by adding the following line of code in out container div

@include aspect-ratio(2, 1);

Here we want to maintain a 2:1 aspect ratio and the user is also expected to upload the image in the same aspect ratio. Therefore, we pass width as 2 and height as 1 to our mixin.

Now when we resize our screen both the image and the overlapping div resize maintaining 2:1 aspect ratio.

Mobile view (after):-

Resources

  • mademyday blog describes a method of using pseudo elements to maintain an element’s aspect ratio.
  • css-tricks snippet.
Continue ReadingMaintain Aspect Ratio Mixin on Open Event Frontend

Reducing UI lags with AsyncTask in PSLab Android

In the Oscilloscope Activity, communication with the PSLab device goes in parallel with updations of the graph which result in inoperable UI if both these functions are performed in the main thread. This would severely degrade the user experience. In order to avoid this, we simply used AsyncTask. AsyncTasks are used to perform communications with the device in the background thread and update UI when the task in background thread completes. AsyncTask thus solves the problem of making UI super laggy while performing certain time-consuming functions. The UI remains responsive throughout.

More about AsyncTask

AsyncTask is an abstract Android class which helps the Android applications to handle the Main UI thread in a more efficient way. AsyncTask class allows to perform long lasting background operations and update the results in UI thread without affecting the main thread.

Implementing AsyncTask in Android applications

  • Create a new class inside Activity class and extend AsyncTask:

private class Task extends AsyncTask<Void, Void, Void> {
  	protected Long doInBackground(aVoid) {
     	}
 	protected void onProgressUpdate(aVoid) {
     	}
 	protected void onPostExecute(aVoid) {
     	}
}
  • Execute the task:

new Task().execute();

How they are used in Oscilloscope Activity?

The following diagram explains how AsyncTasks are used in Oscilloscope Activity. 

AsyncTask in PSLab Android App

A public class extending AsyncTask is defined, this task is executed from another thread.

public class Task extends AsyncTask<String, Void, Void> {
   ArrayList<Entry> entries;
   String analogInput;

 

doInBackgroundMethod performs the part related to communication with the PSLab device.

Here we are capturing the data from the hardware using captureTraces and fetchTraces method.

 @Override
   protected Void doInBackground(String... params) {
       try {
           analogInput = params[0];
           //no. of samples and timegap still need to be determined
           scienceLab.captureTraces(1, 800, 10, analogInput, false, null);
           Log.v("Sleep Time", "" + (800 * 10 * 1e-3));
           Thread.sleep((long) (800 * 10 * 1e-3));
           HashMap<String, double[]> data = scienceLab.fetchTrace(1); //fetching data
           double[] xData = data.get("x");
           double[] yData = data.get("y");
           entries = new ArrayList<Entry>();
           for (int i = 0; i < xData.length; i++) {
               entries.add(new Entry((float) xData[i], (float) yData[i]));
           }
       }
       catch (NullPointerException e){
           cancel(true);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return null;
   }

 

After the thread, completely executed onPostExecute is called to update the UI/graph. This method runs on the main thread.

 @Override
   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       LineDataSet dataset = new LineDataSet(entries, analogInput);
       LineData lineData = new LineData(dataset);
       dataset.setDrawCircles(false);
       mChart.setData(lineData);
       mChart.invalidate();    //refresh the chart
       synchronized (lock){
           lock.notify();
       }
   }
}

 

This simply solves the problem of lags and the Oscilloscope works like a charm.

Resources

Continue ReadingReducing UI lags with AsyncTask in PSLab Android

Building a showcase site to display sample events and auto deploying them on each PR merge in Open Event Webapp

Open Event Webapp generates static websites of the event fed to it in the form of JSON data. Earlier, we used to automatically deploy the FOSSASIA Summit event website on the Github pages of the repository on every merge of a pull request.  The presence of it helped in showcasing and testing purposes. But a single event was not enough. Events are diverse, taking place in a large number of rooms, h a variety of sessions and extending over several days. So, having multiple events on the showcase site and continuously showcasing and testing them was a really great idea.

Here is the feature request for it. I will be explaining the major portions of it. The whole code can be found here

First of all, we need to build the main index page which we will showcase our sample events. It will contain the small images of the event and on clicking them, the selected event will be opened. For displaying features and capability of the generator,  we have two separate sections of events: one having the single session page and the other having the expandable sessions page. There will be links to the other components of the Open Event Ecosystem like Android App generator,  Web App generatorOrganizer App, and Eventyay.

The contents of that page are kept in the overviewSite folder. It contains the event background images of the events and the main index file. The basic structure of the file is shown below. The whole file can be viewed here

<div class="container-fluid bg-3 text-center">
 <div class="row margin">
   <div class="col-md-6 col-sm-12 col-xs-12">
     <div class="row">
       <h2 class="margin"> Apps with expandable sessions page </h2>
       <div class="col-md-6 col-sm-6 col-xs-12 margin">
         <p><strong>Open Tech Summit 2017</strong></p>
         <a href='./OpenTechSummit/index.html' target='_blank'>
           <img src="./otssmall.jpg" class=""  alt="Image">
         </a>
       </div>
      </div>
    </div>
   <div class="col-md-6 col-sm-12 col-xs-12">
     <div class="row">
       <h2 class="margin"> Apps with single sessions page </h2>
       <div class="col-md-6 col-sm-6 col-xs-12 margin">
         <p><strong>Mozilla All Hands 2017</strong></p>
         <a href='./MozillaAllHands2017/index.html' target='_blank'>
           <img src="./mozilla_banner.jpg" class=""  alt="Image">
         </a>
       </div>
     </div>
   </div>
 </div>
</div>

But, this is just the front end of the page. We haven’t generated the sample events yet and neither have we made any changes to make them auto deploy on each PR merge. The test script of the app which contains unit, acceptance and selenium tests runs on each PR made against the repo when Travis CI build is triggered. So, it makes sense to write code for generating the event there itself. When the test script has finished executing, all of the events would have been generated and present inside a single folder named a@a.com (We needed something short and easy to remember and the name doesn’t really matter). We then copy the contents of the overviewSite folder into the above folder. It already contains the folder of different sample events.

Here is the related code. The full test script file can be found here

describe('generate', function() {
 describe('.create different event sites and copy assets of overview site', function() {
   // Sample events are generated inside the a@a.com folder
   it('should generate the Mozilla All Hands 2017', function(done) {
     var data = {};
     // API endpoint configuration of the All Hands 2017 event and the session style set to single pages
     data.body = {
       "email": "a@a.com",
       "name": "Open Event",
       "apiendpoint":    "https://raw.githubusercontent.com/fossasia/open-event/master/sample/MozillaAllHands17",
       "sessionMode": "single",
       "datasource": "eventapi",
       "assetmode" : "download"
     };
     generator.createDistDir(data, 'Socket', function(appFolder) {
       assert.equal(appFolder, "a@a.com/MozillaAllHands2017");
       done();
     });
   });
   // For copying the static files required for the showcase site
   it('should copy all the static files', function(done) {
     var staticPath = __dirname + '/../src/backend/overviewSite/';
     function copyStatic(fileName) {
       // Copy the static files in the overviewSite folder to the a@a.com folder
     }
   });
 });
});

Everything is almost done now. We then just make some changes in the deploy script to publish the whole folder containing the different event samples instead of a particular sample and everything works fine.

We navigate to the a@a.com folder inside the dist directory and initialize a git repository there. We then set the username and email and fetch the contents of the gh-pages branch of the official repo (using the Github token. It is defined in the Travis Settings as a private environment variable) and reset it. We then add all the files present in the current directory to the staging area, commit them and push it to the upstream repository. Here is the relevant code. The whole github deploy script file can be viewed here

eval cd dist/a@a.com/
git init
git config --global user.name "Travis CI"
git config --global user.email "noreply+travis@fossasia.org"
git remote add upstream "https://$GH_TOKEN@github.com/"${TRAVIS_REPO_SLUG}".git"
git fetch upstream
git reset upstream/gh-pages
touch .
git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

Here is the screenshot showing the successful deployment of the sample events. You can view the whole log here. A screenshot of it is below. You can view the higher quality image by clicking on it.

29e53dc0-8841-4329-8a37-8d2e9f08756c.png

After all the work, this is how the showcase page looks like. Samples are re-generated and the site is auto-deployed on every Pull Request merged in the repository.

66804d19-6862-494f-80b6-e1749493e780.png

On clicking on any of the listed events, we jump to the index page of that particular event. Like, If we click on the Facebook Developer Conference, a new tab is opened and we are taken to the front page of that event!

6a47f623-82c6-49e2-af1c-4d152aaaf9b7.png

References

Continue ReadingBuilding a showcase site to display sample events and auto deploying them on each PR merge in Open Event Webapp

Implementing Single Session Pages in Open Event Webapp

Recently, we implemented a feature request in the Open Event Webapp which allows the organizers of an event to choose the style of the sessions displayed on the event web site. Before this feature of individual session pages was implemented, we had a single default style of displaying collapsible session elements on the page. A small preview of the session containing its title, type, name, position, and picture of the speaker(s) presenting it would be shown on the different pages. When the user will click on this session element, it would collapse showing detailed information about it.

Before Clicking

2c12db41-d999-423a-bdee-6b639cbdbb0b.png

After Clicking on the first session element

fe1a3362-ebad-42a0-ba9d-0909c2de21a9.png

While this default behavior of collapse of session element on click (shown above) works well in most of the cases, it might not be apt in situations where the session contains a large amount of detail and a higher number of speakers presenting it. Single session pages would work better in that case.

So, we provided an option to select it on the generator form itself. We provided an input field asking which session style does the organizer want? Is it the single session style or the expandable session? The organizer can then select one of them and the site will be generated according to that!!

2989ee1f-509c-400d-94cd-023a06a3601d.png

The whole work was huge and you can view all of it here. I will only be describing the major parts of it here.

The first challenge was to make a template (handlebars) file for the individual sessions. This template would take a single session object as an input. The object would contain all the details about the session and after compilation during generation, a unique individual page for that session would be created.

Here is the basic structure of the template. You can view the whole file here

<div class="container session-container">
 <!-- Contains all the information about the session -->
 <div class="row single-session" id="{{session_id}}">
   <!-- Displaying the date, start and end time of the session -->
   <h3> {{startDay}} </h3>
   <div class = "eventtime"><span class="time-track">{{start}} - {{end}}</span></div>
   <div class="session-content">
     <div class="sizeevent event" id="event-title">
       <!-- Display the title, type and the track of the session -->
     </div>
     <div>
       <!-- Short abstract of the session -->
       {{{description}}}
       <div class="session-speakers-list">
         <!-- Contains detailed information about the speakers of the session. Display their picture, show their position, short biography and social links links like Github, Twitter and LinkedIn -->
       </div>
     </div>
   </div>
 </div>
</div>

But the work is not completed yet. We have to check the style of the session selected by the organizer and if he/she has selected the single session option, then we have to pass all the sessions to this template file during the generation process and create the pages. If the mode selected is expandable, then we carry out the normal generation procedure. Else, we extract every session from the JSON data and feed into to the above template. Since the number of sessions can be quite large, we don’t generate them alongside the other pages like tracks, schedule, rooms, speakers and so on. Instead, we create a new folder named sessions and put in all of the new individual pages there in one place. It helps to keep the directory clean and modularized. Also, since we are placing it inside of a session folder, we will have to update the links to the main pages in the navbar section. Like, instead of track.html, it will be ../tracks.html now. The file is given a common format name of session_sessionId where sessionId is the id of that particular session.

Here is the related code. It is taken from generator.js file in the project

function templateGenerate() {
 if(mode == 'single') {
   function checkLinks() {
     // Made necessary modifications in the links to the main pages
   }
   //jsonData contains all the information about the event
   var trackArr = jsonData.tracks;
   for(var i = 0; i < trackArr.length; i++) {
     var sessionArr = trackArr[i].sessions;
     for(var j = 0; j < sessionArr.length; j++) {
       var sessionObj = JSON.parse(JSON.stringify(sessionArr[j]));
       // Do some modifications in the sessionObj to include the track background and font color and additional links
     } 
   }
   var data = {session: sessionObj};
   checkLinks();
   // Pass the session object to the template, compile and minify the HTML file and place into the sessions folder
   fs.writeFileSync(distHelper.distPath + '/' + appFolder + '/sessions/session_' + sessionId + '.html', minifyHtml(sessiontpl(data)));
 }
}

Ok, most of the things are done now. Just one simple step is missing. When the user clicks on the small session element on the tracks, rooms or the schedule page, then we collapse the session element or open up a brand new session page for showing detailed information about the session depending upon what option the organizer has selected. So, we will have to make little appropriate change for handling this as well. Below code is taken from the tracks page template file

<div class = "room-filter">
 {{#if ../../../mode}}
   <div class="sizeevent event">
 {{else}}
   <div class="sizeevent event" data-toggle="collapse" data-target="#desc-{{session_id}}, #desc2-{{session_id}}">
 {{/if}}
</div>

$('.room-filter').click(function () {
 var sessionMode = "{{mode}}";
 var id =  $(this).attr('id');
 if(sessionMode == 'single') {
   var curUrl = window.location.href;
   var newUrl = curUrl.substring(0, curUrl.lastIndexOf('/') + 1) + 'sessions/session_' + id + ".html";
   window.location.href = newUrl;
 }
});

We are simply checking the mode and if it is set, it means that the session style is a single page. So, we don’t include the bootstrap collapse classes in that case. We handle that click event in the javascript part and appropriate redirect the user to the unique page for that session.

So, after all this hard work, this is how it looks like.

Before clicking

0fdb3641-2579-4537-8f36-910c636cf172.png

After clicking on the first session element, a new page is opened

e5d2ed0d-c54a-44fd-b30c-af440936c8ed.png

Resources

Continue ReadingImplementing Single Session Pages in Open Event Webapp

Using Hierarchical Blocks in KiCAD to Collaborate in PSLab Hardware Development

The PSLab hardware project designed in KiCAD, an ECAD tool; doesn’t support collaborative features like Git providing for software projects. As explained in a previous blog post on techniques to help up with project collaboration, this blog post will demonstrate how two developers can work together on the same hardware project.

The difficulties arise as the whole project is in one big schematic file. Editing made by one developer will affect to the editing done by the other developers causing merge conflicts. KiCAD doesn’t compile nicely if the changes were fixed manually most of the cases.

The solution practiced in the pslab-hardware project is using hierarchical blocks. This blog post will use a KiCAD project with an oscillator implementation and a voltage regulator implementation just like the ones in pslab-hardware schematics. To avoid complications in understanding changes in a huge circuit, only these two modules will be implemented separately in the blog.

Initially the project will look like the following figure;

Sheet1 Sheet2

These two hierarchical blocks will be created as different .sch files in the project directory as follows;

Assume two different developers are working on these two different blocks. That is the key concept in collaborating hardware projects in KiCAD. As long as the outer connections (pins) don’t get changed, edits made to one block will have no effect on the other blocks.

Developer 1 decided that the existing power circuit is not efficient for the PSLab device. So he decided to change the circuit in Sheet 1. The circuit before and after modification is shown in the table below.

Sheet 1 (Before) Sheet 1 (After)

If we take a look at the git status now, it will be as follows;

From this it is noticeable that neither the main schematic file nor Developer2.sch hasn’t been touched by the edits made to Developer1.sch file. This avoids merge conflicts happening when all the developers are working on the same schematic file.

Resources :

Continue ReadingUsing Hierarchical Blocks in KiCAD to Collaborate in PSLab Hardware Development