Implementing the PDF download of Schedule in Open Event Web app

Open Event Web app now provides an option to its users to download the PDF of event schedule. Earlier it supported the download of list-view only, now it provides the support to download calendar-view as well. The problem incurred while downloading the calendar-view was that the view gets cropped due to limitations with the library used for PDF generation, thus only some parts of the calendar remained in the PDF. The problem is resolved by creating an image for every date in the schedule and adding the generated image to the PDF.

Selecting and adding the data for PDF generation

The data to be added to PDF depending on the filters and date-selectors applied is chosen from the DOM. Selection of data is done by looping through all the dates and adding only the ones which do not have ‘hide’ class added to them. The selected dates are first expanded such that their complete view is available while generating the image. The complete data is stored in a variable depending on if the complete schedule is requested for download or some filter is applied, which is later used for generating the image.

let fullScheduler = true;
let mapValue = '';

pdf = new jsPDF('l', 'pt', 'a1');
$('.calendar').each(function() {
 let hidePresent = $(this).attr('class').split(' ').indexOf('hide') <= 0;

 if (hidePresent) {
   $timeline = $(this);

  // Expanding the schedule for current date
  ...
  ...
 }
 fullScheduler = hidePresent && fullScheduler;
});

if(fullScheduler) {
 $timeline = $('.calendar').parent();
 mapValue = $timeline.children();
}

Adding the notification while generating the PDF

A loader with the notification is added to provide better user experience, as the PDF generation takes place at the time of request itself it may take some time depending on the size of the schedule. The notifications are added using ‘sweetalert’ library already added for Add to calendar notifications.

swal("Generating the PDF",{
 icon: "./images/loader.gif",
 buttons: false
});

Downloading the PDF

The selected dates are stored in an array named ‘schedArr’ whose data sequentially is passed for canvas generation. A new page is added to the PDF of size equal to canvas and the generated canvas is added to that page. With every new page added to the calendar a count is increased to keep a track if all the selected dates are added to PDF.

async.eachSeries(schedArr, function (child, callback) {
 html2canvas(child, {
   onrendered: function (canvas) {
     pdf.addPage(canvas.width, canvas.height);
     child.style.width = initialWidth[count] + 'px';
     pdf.addImage(canvas, 'png', 0, 0, canvas.width, canvas.height);
     currDate++;
     if(currDate === schedArr.length){
       pdf.deletePage(1);
       swal.close();
       pdf.save(scheduleDate + '.pdf');
     }
     count++;
     callback();
   }
 });
});

When the last page is added, the notification is closed and user is prompted to download pop-box.

Resources

Continue ReadingImplementing the PDF download of Schedule in Open Event Web app

Integrating Google Calendar API in Open Event Web app

Open Event Web app allows organizers to add a feature which enables the users to add any session to their google calendar. The organizer is required to generate an API key and client ID on Google developers console and add the generated credentials to web app generator. The credentials are added to the generated application and every session is added with an Add to calendar button, which on click makes the request to add the corresponding session to the calendar.

Creating Client ID and API key

Enable the Google calendar API from Google developers console. Go to `Create Credentials` tab and generate an API key and client ID for your app. While creating the client ID, an input field is present which requires Authorised javascript origins, mention the domains where the generated application would be deployed.

Adding Client ID and API key to the generator

The Client ID and API key obtained from the developer console is added to the web app generator. The event generated uses these credentials to make a request to the server for adding any session to the calendar.

The added credentials are used to initialise the client in the procedure `initClient()` –

function initClient() {
 let id = document.getElementById('gcalendar-id').value;
 let key = document.getElementById('gcalendar-key').value;
 let CLIENT_ID = id;
 let API_KEY = key;
 let DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
 let SCOPES = "https://www.googleapis.com/auth/calendar";

 gapi.client.init({
   apiKey: API_KEY,
   clientId: CLIENT_ID,
   discoveryDocs: DISCOVERY_DOCS,
   scope: SCOPES
 })
}

Adding session to the Google calendar

Every Google calendar enabled event is provided a button with every session, so that corresponding session can be added to the calendar. A procedure named `handleAuthClick` is called with the details of session being passed as parameter when the user clicks on the button. This function handles the authentication required for adding session to the calendar.

function handleAuthClick(title, location, calendarStart, calendarEnd, timezone, description) {
 let isSignedIn = gapi.auth2.getAuthInstance().isSignedIn.get();
 if (!isSignedIn) {
   gapi.auth2.getAuthInstance().signIn().then(function() {
     main.listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description);
   });
 } else {
   main.listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description);
 }
}

A function named `listUpcomingEvents` makes the request to insert the event object with details of the session to the calendar.

function listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description) {
 let event = {
  ... // Event details  


  ... // Code for notifications
  ...
  ...
   'colorId': '5'
 };

 let request = gapi.client.calendar.events.insert({
   'calendarId': 'primary',
   'resource': event
 });
 request.execute(function(event) {
  // Success notification
 });
}

When the session with the corresponding data is added to calendar, an alert box notifying successful addition of session is shown up on the screen.

Resources

Continue ReadingIntegrating Google Calendar API in Open Event Web app

Add feature to view slides and videos of sessions in Open Event Webapp

The Open Event Web App has two components :

  • An event website generator
  • The actual generated website output.

The web generator application can generate event websites by getting data from event JSON files and binary media files, that are stored in a compressed zip file or through an API endpoint. The JSON data format of version 1 as well as version 2, provides user an option to add the slide and video URLs of the sessions. The data from JSONs is extracted and stored in the objects for a particular session, and in the template, the data for videos and slides are rendered in their corresponding iframes.

Extracting data from event JSONs

The data is extracted from the JSONs and is stored in an object. The object containing the data is sent to the procedure which compiles the handlebars templates with that data.

JSON data format v1

video: session.video,
slides: session.slides,
audio: session.audio

 

JSON data format v2

video: session['video-url'],
slides: session['slides-url'],
Audio: session['audio-url']

 

The JSON data format for v1 and v2 are different and thus the data is extracted from the file depending on API version chosen for web app generation. The files where data extraction takes place are fold_v1.js and fold_v2.js for API v1 and v2 respectively.

Adding event emitter

Onclick event emitter on schedule division calls the procedure “loadVideoAndSlides” with the parameters corresponding to the session clicked.

<div class="schedule-track" id="{{session_id}}" onclick = "loadVideoAndSlides('{{session_id}}', '{{video}}', '{{slides}}')">
   .....
   .....
</div>

The parameters Session ID, Video URL and Slide URL are passed to the procedure which is responsible for displaying the slides and video iframes for the sessions. This resolves the problem of heavy data binding to the page, as the frames for videos and slides are loaded on page only when the session is clicked.

Procedure called on click event

The performance of web app is significantly improved by using the call and listen mechanism as only the requested videos are loaded into the document object model.

function loadVideoAndSlides(div, videoURL, slideURL){
 if(videoURL !== null && videoURL !== '') {
     $('#desc-' + div).children('div').prepend(' + div + '" class = "video-iframe col-xs-12 col-sm-12 col-md-12" src="https://www.youtube.com/embed/' + videoURL + '" frameborder="0" allowfullscreen>');
 }
 if(slideURL !== null && slideURL !== '') {
     $('#desc-' + div).children('div').prepend(' + div + '" class = "iframe col-xs-12 col-sm-12 col-md-12" frameborder="0" src="https://view.officeapps.live.com/op/embed.aspx?src=' + slideURL  +'">');
 }
}

 

The video and slide URLs passed to the procedure are used for loading the iframes from youtube and office apps or google docs respectively as shown above, and the resulting slide view is as shown below

Resources

Continue ReadingAdd feature to view slides and videos of sessions in Open Event Webapp

Adding multiple themes in Open Event Web app

Open Event Web app generator provides an option to event organizers to choose the theme for their event website. Previously it supported the default light theme for the websites, but now it supports a customized dark-theme as well inspired from the twitter dark-mode. Adding this new feature to the User Interface of the generated website, Open Event Web app provides a stylish view for the website of an event.

How we enabled multiple theme support?

The client is provided an option to choose the theme, either ‘light’ or ‘dark’ from the generator. Depending on the option selected, the data is sent to the server, the server extracts the data and while compiling the stylesheet for the pages takes into the account which theme is chosen by the client.

Adding option to choose theme

A drop-down menu is added on the generator page which lets the client choose either of the light or dark themes. The default theme is light.

<label for="theme">Choose your theme</label>
<select class="form-control" id="theme" name="theme">
 <option value="light">Light</option>
 <option value="dark">Dark</option>
</select>

Defining the color schema

The color schema is set different for both the themes, this is achieved by making two different stylesheets for the light and dark themes. The style or color of an element is assigned a variable in main css file(application.scss). Those variables are overwritten while compiling these specific files depending upon the theme chosen.

_light.scss
Defines the values of variables for light theme.

$navbar-color: #ffffff;
$link-color: $gray-dark !default;
$hover-color: $black !default;
$span-background: #efefef !default;

 

_dark.scss
Defines the values of variables for dark theme.

$navbar-color: #2b324a;
$link-color: $light-blue;
$hover-color: $light-blue;
$span-background: #333d5a;

Setting up theme field in json data

The json data to be used for compiling the templates, sets or unsets the theme field according to the theme selected by the client.

if (req.body.theme === 'light') {
 jsonData.theme = 0;
} else {
 jsonData.theme = 1;
}

 

Rendering style in template files

Now we have the value for theme selected by the client, the data used for rendering the CSS file is chosen accordingly. If the theme is light, the file containing color-scheme for light proposition is selected else the dark one. The output file contains the values for color variables according to the theme.

sass.render({
 file: __dirname + '/_scss/_themes/_' + theme + '-theme/_' + theme + '.scss',
 outFile: distHelper.distPath + '/' + appFolder + '/css/schedule.css'
}, function(err, result) {
 if (!err) {
      ...
      ... // Minifying CSS file and writing 
   callback(null);
});

The output file is schedule.css which contains the style for different pages according to the theme chosen while web app generation.

Resources

Continue ReadingAdding multiple themes in Open Event Web app

Adding event to overview site of Open Event Webapp

Open Event Web app has two components: The generator used for generating event website and the generic web application for any event. The overview site showcases the example events generated using web app generator. The event added to the overview site keeps on updating with every new feature added. The samples on overview site updates whenever Travis build is triggered. This blog will illustrate how to add an event to overview site.

Adding event name and links to overview site

The event to be shown on overview site is added to the ‘index’ page of overview site with the link to event and a link to image as shown below.

<div class="col-md-4 col-xs-12 col-sm-6 event-holder">
   <div class="thumbnail">
       <a href='./SparkSummit2017/index.html' target='_blank'>
           <img src='./SparkSummit17.jpg' onerror="imgError(this, true);">
           <div class="caption">
               <h4 class="name">Spark Summit 2017</h4>
           </div>
       </a>
   </div>
</div>

Adding event image

The event image is chosen and added to the same directory containing the index page for overview site. Following is the image selected for Spark Summit 2017 sample.

Copying the event image to event directory

The samples are kept updated by generating the samples with the modified changes using travis-ci. Therefore, the static files for samples are copied to event folder which is pushed to github later for build. Following is the helper function used for copying the files. The counter keeps track for number of files copied.

it('should copy all the static files', function (done) {
 let staticPath = __dirname + '/../src/backend/overviewSite/';
 let totalFiles = 17;
 let counter = 0;

 function copyStatic(fileName) {
   fs.readFile(staticPath + fileName, function (err, data) {
     if (err) {
       done(err);
       return;
     }
     fs.writeFile(dist.distPath + '/a@a.com/' + fileName, data, function (err) {
       if (err) {
         done(err);
         return;
       }
       counter++;
       if (counter === totalFiles) {
         done();
       }
     });
   });
 }

Adding tests for the event and regenerating on every build

The sample is rebuilt on every modified change in the project, so every feature added is reflected in the samples present on the overview site. The generator procedure ‘createDistDir’ is called with the required data being passed to it. Following is an example for generating Spark Summit 2017 sample and testing the sample generation in event directory.

it('should generate the Spark Summit 2017 event', function (done) {
 let data = {};

 data.body = {
   "email": 'a@a.com',
   "theme": 'light',
   "sessionMode": 'expand',
   "apiVersion": 'api_v2',
   "datasource": 'eventapi',
   "apiendpoint": 'https://raw.githubusercontent.com/fossasia/open-event/master/sample/SparkSummit17',
   "assetmode": 'download'
 };


 generator.createDistDir(data, 'Socket', function (appFolder) {
   assert.equal(appFolder, "a@a.com/SparkSummit2017");
   done();
 });

});

Resources

Continue ReadingAdding event to overview site of Open Event Webapp

Adding Logs for Request Status in Open Event Web app

Open Event Web app handles multiple requests from the client using task queue, every request from client is put in the job queue and handled one at a time. The only log shown to client was either ‘waiting’ or ‘processing’, so we need to show additional logs like request waiting number as well. The logs are shown in real time using sockets.

How to add logs?

The logs of any request are shown to the client in real time using socket emit and listener events. Whenever any data is to be displayed inside the logs, the server emits an event with the data. The socket listens to the event and appends the data received to the logs section of the view.

Creating helper for emitting data

The helper function named ‘logger’ is created which emits the event ‘buildLog’ whenever it is called with the data being passed as arguments. Every time a message is passed to this procedure, it adds it to array of objects containing the data.

'use strict';

// eslint-disable-next-line no-var
var exports = module.exports = {};
const buildLog = [];
let obj = {};
let emit, largeMsg, message;

exports.addLog = function(type, smallMessage, socket, largeMessage) {
 if (typeof largeMessage === 'undefined') {
   largeMsg = smallMessage;
 }

 buildLog.push({'type': type, 'smallMessage': smallMessage, 'largeMessage': largeMsg});
 message = largeMsg.toString();
 obj = {'type': type, 'smallMessage': smallMessage, 'largeMessage': message};
 emit = false;

 if (socket.constructor.name === 'Socket') {
   emit = true;
 }
 if (emit) {
   socket.emit('buildLog', obj);
 }
};

Updating logs in real time

The helper created above emits the event ‘buildLog’, the socket on listening this event appends the data inside logs division with the data received from the helper while emitting the event.

socket.on('buildLog', function(data) {
   const spanElem = $('<span></span>'); // will contain the info about type of statement
   const spanMess = $('<span></span>'); // will contain the actual message
   const aElem = $('<button></button>'); // Button to view the detailed error log
   const divElem = $('

); // Contain the detailed error log
   const paragraph = $('<p></p>'); // Contain the whole statement

  //Code for styling the logs division
  ....
  ....


     divElem.text(data.largeMessage);
     paragraph.append(aElem);
     paragraph.append(divElem);
     updateStatusAnimate(data.smallMessage, 200, 'red');
     $('#btnGenerate').prop('disabled', false);
     $('input[ type = "radio" ]').attr('disabled', false);
     $('#email').prop('disabled', false);
   }
   $('#buildLog').append(paragraph);
   $('#buildLog').scrollTop($('#buildLog')[0].scrollHeight);
 });
});

Add request waiting number

Whenever a new request is received from the client the server emits the event ‘waiting’ if any other job is currently being processed. The helper above is used to add request waiting number to the logs.

const jobs = await queue.getJobs('waiting', {start: 0, end: 25});
const activeJob = await queue.getJobs('active', {start: 0, end: 25});
const jobIds = jobs.map((currJob) => currJob.id);

if (jobIds.indexOf(currJobId) !== -1) {
 socket.emit('waiting');
 logger.addLog('Info', 'Request waiting number: ' + (currJobId - activeJob[0].id), socket);
}

Add status in logs

On listening the event named ‘waiting’ the status is updated to ‘waiting’ in the view and is shown to the client.

socket.on('waiting', function() {
 updateStatusAnimate('Request status: Waiting');
});

Update request waiting number

Whenever a job is started being processed from the queue, the waiting number of all the requests in the ready queue is updated. The socket connection for corresponding request is obtained from the main socket object(socketObj) which updates whenever a new request comes from the client.

const jobs = new Promise(function(resolve) {
 resolve(queue.getJobs('waiting', {start: 0, end: 25}));
});

generator.createDistDir(job.data, socketObj[processId], done);
jobs.then(function(waitingJobs) {
 waitingJobs.forEach(function(waitingJob) {
   logger.addLog('Info', 'Request waiting number: ' + (waitingJob.id - job.id), socketObj[waitingJob.id]);
 });
});

 

Resources

Continue ReadingAdding Logs for Request Status in Open Event Web app

Adding Code of Conduct in Open Event Web app

Open Event Server sends JSON data as a response of its REST (Representational State Transfer) API. The main eventyay platform allows organizers to add code of conduct to their event, as a result the JSON data sent by the server contains code of conduct key value pair, this value is extracted from the data and is used to create a separate page for Code of Conduct in any event.

The steps for data extraction and compilation are as follows:

Extracting code of conduct

Since open event server has two types of JSON data formats v1 and v2, both of them contains code of conduct. The key for code of conduct in v1 is code_of_conduct and for v2 is code-of-conduct. The data extraction for v1 data format occurs in fold_v1.js and the main event details are stored in an object urls as shown below:

fold_v1.js

const urls= {
 ....
 ....
 ....

 email: event.email,
 orgname: event.organizer_name,
 location_name: event.location_name,
 featuresection: featuresection,
 sponsorsection: sponsorsection,
 codeOfConduct: event.code_of_conduct
};

 

fold_v2.js

const urls= {
 ....
 ....
 ....

 email: event.email,
 orgname: event['organizer-name'],
 location_name: event['location-name'],
 featuresection: featuresection,
 sponsorsection: sponsorsection,
 codeOfConduct: event['code-of-conduct']
};

Adding template for CoC

Now we have extracted the data and have stored the value for code of conduct in an object, we need to render this in a template. For this, we created a template named CoC.hbs and the data for code of conduct is accessed via {{{eventurls.codeOfConduct}}} as shown below.

{{>navbar}}
<div class="main-coc-container container">
 <div class="row">
   <div class="middle col-sm-12">
     <h2 class="filter-heading track-heading text-center">
       <span>Code of Conduct</span>
     </h2>
   </div>
 </div>

 <div class="row">
   <div class="col-sm-12 col-md-12">
     <div class="coc">
       {{{eventurls.codeOfConduct}}}
     </div>
   </div>
 </div>
</div>
{{>footer}}

Compiling and minifying

Now we have stored the event details in an object we copy this object as a key to jsonData, this data is passed as an argument for compiling the code of conduct template namely CoC.hbs to a HTML file and is lately minified. For minification purpose gulp module is used.

if(jsonData.eventurls.codeOfConduct) {
 setPageFlag('CoC');
 fs.writeFileSync(distHelper.distPath + '/' + appFolder + '/CoC.html', minifyHtml(codeOfConductTpl(jsonData)));
}

Adding link to CoC page

Till now, we have successfully compiled a HTML page for code of conduct of an event. This page is linked under a heading in the footer section of every page by placing reference to it in footer.hbs.

{{#if eventurls.codeOfConduct}}
 <li><a target="_self" href="CoC.html">Code of Conduct</a></li>
{{/if}}

Customizing the CoC container

The code of conduct page is customized by placing the container in the center and aligning the text. Styling like background-color, padding and margin are set on the container to provide a better appearance to the page.

.coc {
 margin: auto;
 text-align: justify;
 width: 60%;

 a {
   &:hover {
     color: $dark-black;
   }
 }
}

.main-coc-container {
 background-color: $main-background;
 margin-bottom: 4%;
 margin-top: 2%;
 padding-bottom: 50px;
 padding-top: 2%;
}

Resources

Continue ReadingAdding Code of Conduct in Open Event Web app

Implementing job queue in Open Event Web app

Open Event Web app enables multiple request handling by the implementation of queue system in its generator. Every request received from the client is saved and stored in the queue backed by redis server. The jobs are then processed one at a time using the FCFS (First come First Serve) job scheduling algorithm. Processing the requests one by one prevents the crashing of app and also prevents the loss of requests from the client.

Initialising job queue

The job queue is initialised with a name and the connection object of redis server as the arguments.

const redisClient =  require('redis').createClient(process.env.REDIS_URL);
const Queue = require('bee-queue');
const queue = new Queue('generator-queue', {redis: redisClient});

Handling jobs in queue

The client emits an event namely ‘live’ when request for event generation is received, the corresponding event is listened and a new job for the request is created and enqueued in the job queue. Every request received by the client is saved to ensure that there is no loss of request. The queue is then searched for the requests or the jobs which are in ‘waiting’ state, if the current request status for the job Id is waiting the socket emits an event namely ‘waiting’.

socket.on('live', function(formData) {
 const req = {body: formData};
 const job = queue.createJob(req);

 job.on('succeeded', function() {
   console.log('completed job ' + job.id);
 });

 job.save(async function(err, currentJob) {
   if (err) {
     console.log('job failed to save');
   }
   emitter = socket;
   console.log('saved job ' + currentJob.id);
   const jobs = await queue.getJobs('waiting', {start: 0, end: 25});
   const jobIds = await jobs.map((currJob) => currJob.id);

   if(jobIds.indexOf(currentJob.id) !== -1) {
     socket.emit('waiting');
   }
 });

});

Updating the status of request

If the socket emits the event ‘waiting’ it signifies that some other job is currently in process and the status of the current request is ‘waiting’.

socket.on('waiting', function () {
 updateStatusAnimate('Request status: Waiting');
});

Processing the jobs

When the queue is in ready state and no job is currently in process, it starts processing the saved job. The job is not completed until it receives a callback. The generator starts generating the event when the processing of request starts.

queue.on('ready', function() {
 queue.process(function(job, done) {
   console.log('processing job ' + job.id);
   generator.createDistDir(job.data, emitter, done);
 });
 console.log('processing jobs...');
});

 

The generator calls the callback function for the current job when the event generation completes or it is halted in between due to some error. As soon as the current job completes, next job in the queue starts being processed.

generator.createDistDir() = function(req, socket, callback){
  
  .....
  .....
  .....

  mailer.uploadAndsendMail(req.body.email, eventName, socket, (obj) => {
    if(obj.mail)
      logger.addLog('Success', 'Mail sent succesfully', socket);
    else
      logger.addLog('Error', 'Error sending mail', socket);

    if(emit) {
      socket.emit('live.ready', {
        appDir: appFolder,
        url: obj.url
      });
      callback(null);
    }
    else {
      callback(appFolder);
    }

    done(null, 'write');
   });
}

Resources

Continue ReadingImplementing job queue in Open Event Web app

Parallelizing travis build in Open Event Web app

 

Open Event Web app uses Travis CI as a platform to perform unit testing. Travis CI is a hosted, distributed continuous integration service used to build and test projects hosted at GitHub. Travis CI automatically detects when a commit has been made and pushed to a GitHub repository that is using Travis CI, and each time this happens, it will try to build the project and run tests. Travis build took around 24 minutes to complete when any commit is made to the project, which is a very large time, to reduce the build time we parallelized the build which uses maximum amount of resources available at that time and run the builds parallely which resulted into better use of resources as well as required lesser amount of time.

Open Event web app uses saucelabs integration to perform selenium tests and travis to perform continuous integration.

Why parallelize the build?

When there are unit tests that are independent of each other and can be executed using a common set of dependencies, those procedures can be performed parallely on different virtual machines bringing out maximum throughput.

Running say a large number of tests on a single machine can increase the build time to a large extent, this build time can be reduced significantly by running the tests parallely on different machines. Open Event Webapp has a build time of around 24 minutes which is reduced to half on parallelising the build.

Parallelizing your builds across virtual machines

To speed up a test suite, you can break it up into several parts using Travis CI’s build matrix feature.

Say you want to split up your unit tests and your integration tests into two different build jobs. They’ll run in parallel and fully utilize the available build capacity and the resources.

The architecture of open event webapp supports test suite for all the pages in the generated application. To parallelize the build, the test suite is divided in different files  with the directory structure as shown below:

   
   ├── test
      ├── serverTest.js
      ├── roomsAndSpeakers.js
      ├── tracks.js
      ├── generatorAndSchedule.js
      ├── sessionAndEvent,js

 

The env key in travis.yml is modified as shown below:

env:
  - TESTFOLDER=test/serverTest.js
  - TESTFOLDER=test/roomsAndSpeakers.js
  - TESTFOLDER=test/tracks.js
  - TESTFOLDER=test/generatorAndSchedule.js
  - TESTFOLDER=test/sessionAndEvent.js

 

The script running tests fetches environment variable and runs the test file accordingly as shown below:

# installing required items for build
install:
 - npm install -g istanbul mocha@3
 - npm install
 - npm install --save-dev

# testing script
script:
 - istanbul cover _mocha -- $TESTFOLDER

# notify codecov and deploy to cloud
after_success:
 if ([ "$TESTFOLDER" == "test/serverTest.js" ]); then
   bash <(curl -s https://codecov.io/bash);
   bash gh_deploy.sh && kubernetes/travis/deploy.sh;
 fi

Results:

The build time which was earlier 23 minutes is reduced to 12 minutes after parallelizing the build.

Resources

 

Continue ReadingParallelizing travis build in Open Event Web app

Enable web app generation for multiple API formats

 

 

 

 

 

Open event server has two types of API (Application Programming Interface) formats, with one being generated by the legacy server and other one by the server side of decoupled development structure. The open event web app supported only the new version of API format, thus an error in read contents of JSON was thrown for the old version API format. To enable the support for both kind of API formats such that web app can be generated for each of them and there is no need to convert JSON files of version v1 to v2 we added an option field to the generator, where the client can choose the API version.

Excerpts and description for difference between data formats of API v1 and v2

The following excerpt is a subprogram getCopyrightData in both versions v1 and v2. The key for getting licence details in v1 is ‘licence_details’ and in v2 is ‘licence-details’. Similarly the key for getting copyright details in v1 is ‘copyright’ and in v2 is ‘event-copyright’.

So the data is extracted from the JSON files depending on the API version, the client has selected.

API V1

function getCopyrightData(event) {
 if(event.licence_details) {
   return convertLicenseToCopyright(event.licence_details, event.copyright);
 } else {
   return event.copyright;
 }
}

 

API V2

function getCopyrightData(event) {
 if(event['licence-details']) {
   return convertLicenseToCopyright(event['licence-details'], event['event-copyright']);
 } else {
   event['event-copyright'].logo = event['event-copyright']['logo-url'];
   return event['event-copyright'];
 }
}

 

Another example showing the difference between the API formats of v1 and v2 is excerpted below.

The following excerpt shows a constant ‘url’ containing the event URLs and the details. The version v1 uses event_url as a key for the main page url whereas v2 uses event-url for getting the same. A similar kind of structural difference is present for rest of the fields where the special character underscore has been replaced by hyphen and a bit of change in the name format for keys such as start_time, end_time.

API v1

const urls= {
 main_page_url: event.event_url,
 logo_url: event.logo,
 background_url: event.background_image,
 background_path: event.background_image,
 description: event.description,
 location: event.location_name,
 orgname: event.organizer_name,
 location_name: event.location_name,
};

 

API v2

const urls= {
 main_page_url: event['event-url'],
 logo_url: event['logo-url'],
 background_url: event['original-image-url'],
 background_path: event['original-image-url'],
 location: event['location-name'],
 orgname: event['organizer-name'],
 location_name: event['location-name'],
};

How we enabled support for both API formats?

To add the support for both API formats we added a options field on generator’s index page where the user chooses the type of API format for web app generation.

<label>Choose your API version</label>
<ul style="list-style-type:none">
 <li id="version1"><input name="apiVersion" type="radio" value="api_v1">   API_v1</li>
 <li id="version2"><input name="apiVersion" type="radio" value="api_v2"> API_v2</li>
</ul>

The generator depending on the version of API format, chooses the correct file where the data extraction from the input JSON files takes place. The file names are fold_v1.js and fold_v2.js for extraction of JSON v1 data and JSON v2 data respectively.

var type = req.body.apiVersion || 'api_v2';

if(type === 'api_v1') {
 fold = require(__dirname + '/fold_v1.js');
}
else {
 fold = require(__dirname + '/fold_v2.js');
}

 

The excerpts of code showing the difference between API formats of v1 and v2 are the contents of fold_v1.js and fold_v2.js files respectively.

Resources

Continue ReadingEnable web app generation for multiple API formats