Updating the UI of the generator form in Open Event Webapp

The design of the generator form in Open Event Webapp has been kept very simple and there have been minor modifications to it over time. In keeping up with the changes made to the front page of the other apps, there was a need to modify the UI of the generator and add some new elements to it. This is the related issue for it. The whole work can be seen here. There were three main parts to it: Add a top bar similar to the Open Event Frontend Add a pop-up menu bar similar to the one shown in Google/Susper Add a version deployment link at the bottom of the page like the one shown in staging.loklak.org.   Implementing the top-bar and the pop-up menu bar The first task was to introduce a top-bar and a pop-up menu bar in Generator. The top-bar would contain the text Open Event Webapp Generator and an icon button on the right side of it which would show a pop-up menu. The pop-up menu would contain a number of icons which would link to different pages like FOSSASIA blogs and it’s official website, different projects like loklak, SUSI and Eventyay and also to the Webapp Project Readme and issues page. Creating a top navbar is easy but the pop-up menu is a comparatively tougher. The first step was to gather the gather the small images of the different services. Since this feature had already been implemented in Susper project, we just copied all the icon images from there and copy it into a folder named icons in the open event webapp. Then we create a custom menu div which would hold all the different icons and present it an aesthetic manner. Write the HTML code for the menu and then CSS to decorate and position it! Also, we have to create a click event handler on the pop-up menu button for toggling the menu on and off. Here is an excerpt of the code. The whole file can be seen here <div class="custom-navbar"> <a href='.' class="custom-navtitle"> <strong>Open Event Webapp Generator</strong> <!-- Navbar Title --> </a> <div class="custom-menubutton"> <i class="glyphicon glyphicon-th"></i> <!-- Pop-up Menu button --> </div> <div class="custom-menu"> <!-- Custom pop-up menu containing different links --> <div class="custom-menu-item"> <a class="custom-icon" href="http://github.com/fossasia/open-event-webapp" target="_blank"><img src="./icons/code.png"> <p class="custom-title">Code</p></a> </div> <!-- Code for other links to different projects--> </div> </div> Here is a screenshot of how the top-bar and the pop-up menu looks! Adding version deployment info to the bottom The next task was to add a footer to the page which would contain the version deployment info. The user can click on that link and we can then be taken to the latest version of the code which is currently deployed. To show the version info, we make use of the Github API. We need to get the hash of the latest commit made on the development branch. We send an API request to the Github requesting for the latest hash and then dynamically add the info and the link received to the footer. The user can then click on that…

Continue ReadingUpdating the UI of the generator form in Open Event Webapp

Implementing Search Functionality In Calendar Mode On Schedule Page In Open Event Webapp

The schedule page in the event websites generated by the Open Event Webapp displays all the sessions of an event in a chronological manner. There are two modes on the page: List and the Calendar Mode. The former displays all the sessions in the form of a list while the latter displays them in a graphical grid or a rectangular format.  Below are the screenshots of the modes: List Mode Calendar Mode The list mode of the page already supported the search feature. We needed to implement it in the calendar mode. The corresponding issue for this feature is here. The whole work can be seen here. First, we see the basic structure of the page in the calendar mode. <div class="{{slug}} calendar"> <!-- slug represents the currently selected date --> <!-- This div contains all the sessions scheduled on the selected date --> <div class="rooms"> <!-- This div contains all the rooms of an event --> <!-- Each particular room has a set of sessions associated with it on that particular date --> <div class="room"> <!-- This div contains the list of session happening in a particular room --> <div class="session"> <!-- This div contains all the information about a session --> <div class="session-name"> {{title}} </div> <!-- Title of the session --> <h4 class="text"> {{{description}}} </h4> <!-- Description of the session --> <!-- This div contains the info of the speakers presenting the session --> <div class="session-speakers-list"> <div class="speaker-name"><strong>{{{title}}}</div> <!-- Name of the speaker --> <div class="session-speakers-more"> {{position}} {{organisation}} </div> <!-- Position and organization of speaker--> </div> </div> </div> </div> </div> </div> The user will type the query in the search bar near the top of the page. The search bar has the class fossasia-filter. We set up a keyup event listener on that element so that whenever the user will press and release a key, we will invoke the event handler function which will display only those elements which match the current query entered in the search bar. This way, we are able to change the results of the search dynamically on user input. Whenever a single key is pressed and lifted off, the event is fired which invokes the handler and the session elements are filtered accordingly. Now the important part is how we actually display and hide the session elements. We actually compare few session attributes to the text entered in the search box. The text attributes that we look for are the title of the session, the name, position , and organization of the speaker(s) presenting the session. We check whether the text entered by the user in the search bar appears contiguously in any of the above-mentioned attributes or not. If it appears, then the session element is shown. Otherwise, its display is set to hidden. The checking is case insensitive. We also count the number of the visible sessions on the page and if it is equal to zero, display a message saying that no results were found. For example:- Suppose the user enters the string ‘wel’ in…

Continue ReadingImplementing Search Functionality In Calendar Mode On Schedule Page In Open Event Webapp

Adding Service Workers In Generated Event Websites In Open Event Webapp

Open Event Webapp Generator takes in the event data in form of a JSON zip or an API endpoint as input and outputs an event website. Since the generated event websites are static, we can use caching of static assets to improve the page-load time significantly. All this has been made possible by the introduction of service workers in the browsers. Service workers are event-driven scripts (written in JavaScript) that have access to domain-wide events, including network fetches.With the help of service workers, we can cache all static resources, which could drastically reduce network requests and improve performance considerably, too. The service workers are like a proxy which sits between the browser and the network and intercept it. We will listen for fetch events on these workers and whenever a request for a resource is made, we intercept and process it first. Since our generated event sites are static, it makes no sense to fetch the assets again and again. When a user first loads a page, the script gets activated and try to cache all the static assets it can. On further reload of the page, whenever the browser request for an already stored asset, instead of fetching it from the network, it can directly give them back to the browser from the local store. Also, in the meantime, it is also caching all the new static assets so they won't be loaded or fetched again from the network later. Thus the performance of the app gets increased more and more with every reload. It becomes fully functional offline after a few page loads. The issue for this feature is here and the whole work can be seen here. To know more details about the basic functioning and the lifecycle of the service workers, check out this excellent Google Developers article. This blog mainly focuses on how we added service workers in the event websites. We create a new fallback HTML page offline.html to return in response to an offline user who requests a page which has not been cached yet. Similarly, for images, we have a fallback image named avatar.png which is returned when the requested image is not present in the local store and the network is down. Since these assets are integral to the functioning of the service worker, we cache them in the installation step itself. The whole service worker file can be seen here var urlsToCache = [ './css/bootstrap.min.css', './offline.html', './images/avatar.png' ]; self.addEventListener('install', function(event) { event.waitUntil( caches.open(CACHE_NAME).then(function(cache) { return cache.addAll(urlsToCache); }) ); }); All the other assets are cached lazily. Only when they are requested, we fetch them from the network and store it in the local store. This way, we avoid caching a large number of assets at the install step. Caching of several files in the install step is not recommended since if any of the listed files fails to download and cache, then the service worker won’t be installed! self.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request).then(function(response) { // Cache hit - return response if (response) { return response; } // Fetch resource from internet and put…

Continue ReadingAdding Service Workers In Generated Event Websites In Open Event Webapp

Implementing ICS/ICAL to sync calendars with the event schedule in Open Event Webapp

Many people use different calendar apps to mark important events which they want to follow up on later. Other closed source event management systems like sched.org provide users the functionality to export the sessions data of an event into a file which can then be easily imported into other apps like Google Calendar. We wanted to implement the same functionality in the Open Event Webapp. Here is the issue regarding the feature here. ICS/ICAL is the standard format for calendar files (which was discussed above) used by different programs like Google Calendar, Microsoft Outlook etc. They help users to quickly import and export data from their calendars and share it with the outside world. As is the case with any other global format, the contents of the ICS file must follow some specifications for it to be able to be read by the different programs. The official description is quite verbose and detailed and can be read here. As an end result, we want to provide a button to the user which will export the whole data of the event schedule to an ICS file and present it to the user for download after clicking the button. The whole work regarding the feature can be seen here. Instead of implementing the whole specification ourselves which would be much tougher and time-consuming, we looked for some good open source libraries to do a bit of heavy lifting for us. After searching exhaustively for the solution, we came across this library which seemed appropriate for the job. The heart of the library is a function which takes in an object which contains information about the session. It expects information about the start and end time, subject, description and location of the session. Here is an excerpt from the function. The whole file can be seen here var addEvent = function (session) { var calendarEvent = [ 'BEGIN:VEVENT', 'UID:' + session.uid, 'CLASS:PUBLIC', 'DESCRIPTION:' + session.description, 'DTSTART;VALUE=DATETIME:' + session.begin, 'DTEND;VALUE=DATE:' + session.stop, 'LOCATION:' + session.location, 'SUMMARY;LANGUAGE=en-us:' + session.subject, 'TRANSP:TRANSPARENT', 'END:VEVENT' ]; calendarEvents.push(calendarEvent); }; We need to call the above function for every session in the event schedule. In the schedule template file, we have the jsonData object available which contain all the information about the event. It contains a field called timeList which contains the chronological order of the different sessions taking place throughout the events. The structure of that sub-object is something like this. [{'slug': '2017-03-20', 'times': {'caption' : '09:00-09:30', 'sessions': [{'title': 'Welcome', 'description': 'Opening of the event', 'start': '09:00', 'end': '09:30'}]}] So, we define a function for iterating through every session in the above object and adding it to the calendar. We can use most of the attributes directly but have to modify the date and time fields of the session to an appropriate format before adding it. The specification expects time in the ISO 8601 Format. You can read more about the specification here. For eg - If the date is 2017-03-20 and the time is 09:30 then it should be written as 20170320T093000. Here is some part of the function here function exportICS() {…

Continue ReadingImplementing ICS/ICAL to sync calendars with the event schedule in Open Event Webapp

Implementing Logging Functionality in Open Event Webapp

Open Event Webapp allows event organizers to generate an event website by providing JSON data in the form of a zip file or an API endpoint. The generation of event website is a multi step process and takes some time to complete. In order to see the ongoing progress of the process and catch any errors, logging feature is a must. The challenging part was to send the log messages in real time about the tasks being performed in the generator instead of sending it after some time when the task is already finished. To enable real time communication between the web server and the client, we use the Socket IO library. But before using that library for sending log messages from server to client, we have to design the architecture of the logging feature. The log statements can be of several types. It could be about the inception of a task, the successful completion of it or some error which occurred while performing the task. The log message object contains a field named type to show the type of the statements. We define three categories of logs messages:- INFO: Info statements give information about the task currently being performed by the webapp SUCCESS: Success statements give the information of a task being successfully completed ERROR: Error statements give information about a task failing to complete. These statements also contain a detailed error log Along with the type of the statement, the object also contains information about the task. For all types of statements, there is a field called smallMessage containing short information about the task. For the ERROR statements where more information is required to see what went wrong, the message object has an additional field called largeMessage which holds detailed information about the event. We also create a new file called buildlogger.js and define a function for creating log statements about the tasks being performed by generator and export it. The function creates a message object from the arguments received and then return it to the client under the buildLog event via the socket IO. exports.addLog = function(type, smallMessage, socket, largeMessage) { var obj = {'type' : type, 'smallMessage' : smallMessage, 'largeMessage': largeMessage}; var emit = false; if (socket.constructor.name === 'Socket') { emit = true; } if (emit) { socket.emit('buildLog', obj); } }; Most of the steps of the generation process are defined in the generator.js file. So, we include the logging file there and call the addLog function for sending logs messages to the client. All the different steps like cleaning temporary folders, copying assets, fetching JSONs, creating the website directory, resizing images etc have multiple log statements for their inception and successful/erroneous completion. Below is an excerpt from the cleaning step. var logger = require('./buildlogger.js'); async.series([ (done) => { console.log('CLEANING TEMPORARY FOLDERS\n'); logger.addLog('Info', 'Cleaning up the previously existing temporary folders', socket); fs.remove(distHelper.distPath + '/' + appFolder, (err) => { if(err !== null) { // Sending Error Message when the remove process failed logger.addLog('Error', 'Failed to clean up the previously existing temporary folders', socket, err); } // Success message denoting the completion of the…

Continue ReadingImplementing Logging Functionality in Open Event Webapp

Implementing Tracks Filter in Open Event Webapp using the side track name list

Event Websites generated by Open Event Webapp may contain a large number of sessions presented by different speakers. The Sessions are divided into the different group based on their type of track. For example, few sessions may belong to Database Track, few may belong to Machine Learning Track and so on. It is natural that the user may want to filter the visible sessions on the basis of tracks. Before we implemented the tracks filter using the track names list, we had a sub navbar on the tracks page for jumping to the different tracks of the event on a particular day. Below are the screenshots of that feature. On Clicking the Design, Art, Community Track But, it was not an elegant solution. We already had a track names list present on the side of the page which remained unused. A better idea was to use this side track names list to filter the sessions. Other event management sites like http://sched.org follow the same idea. The relevant issue for it is here and the major work can be seen in this Pull Request. Below is the screenshot of the unused side track names list. The end behavior should be something like this, the user clicks on a track and only sessions belonging to the track should be visible and the rest be hidden. There should also be a button for clearing the applied filter and reverting the page back to its default view. Let’s jump to the implementation part. First, we make the side track name list and make the individual tracks clickable. <div class="track-names col-md-3 col-sm-3"> {{#tracknames}} <div class="track-info"> <span style="background-color: {{color}};" class="titlecolor"></span> <span class="track-name" style="cursor: pointer">{{title}} </span> </div> {{/tracknames}} </div> Now we need to write a function for handling the user click event on the track name. Before writing the function, we need to see the basic structure of the tracks page. The divs with the class date-filter contain all the sessions scheduled on a given day. Inside that div, we have another div with class tracks-filter which contains the name of the track and all the sessions of that track are inside the div with class room-filter. Below is a relevant block of code from the tracks.hbs file <div class="date-filter"> // Contains all the sessions present in a single day <div class="track-filter row"> // Contains all the sessions of a single track <div class="row"> // Contains the name of the track <h5 class="text">{{caption}}</h4> </div> <div class="room-filter" id="{{session_id}}"> // Contain the information about the session </div> </div> </div> We iterate over all the date-filter divs and check all the track-filter divs inside it. We extract the name of the track and compare it to the name of the track which the user selected. If both of them are same, then we show that track div and all the sessions inside it. If the names don’t match, then we hide that track div and all the content inside it. We also keep a variable named flag and set it to 0 initially. If the user selected track is present on a given day, we set the flag to 1. Based on it, we decide whether to display that particular day or not. If the flag is set, we display the date-filter div of that…

Continue ReadingImplementing Tracks Filter in Open Event Webapp using the side track name list

Scaling the logo of the generated events properly in Open Event Webapp

In the Open Event Webapp we came across an issue to scale the logo of the different generated events properly. On the outset, it looks a simple problem but it is a bit tricky when we consider the fact that different events have different logo sizes and we have to make sure that the logo is not too wide nor is it too long. Also, the aspect ratio of the image shouldn’t be changed otherwise it would look stretched and pixelated. Here are some screenshots to demonstrate the issue. In the Facebook Developer Conference, the logo was too small In the Open Tech Summit Event, the logo was too long and increased the height of the navigation bar We decide some constraints regarding the width and the height of the logo. We don’t want the width of the logo to exceed greater than 110 pixels in order to not let it become too wide. It would look odd on small and medium screen if barely passable on bigger screens. We also don’t want the logo to become too long so we set a max-height of 45 pixels on the logo. So, we apply a class on the logo element with these properties .logo-image {  max-width: 110px;  max-height: 45px; } But simply using these properties doesn’t work properly in some cases as shown in the above screenshots. An alternative approach is to resize the logo appropriately during the generation process itself. There are many different ways in which we can resize the logo. One of them was to scale the logo to a fixed dimension during the generation process. The disadvantage of that approach was that the event logo comes in different size and shapes. So resizing them to a fixed size will change its aspect ratio and it will appear stretched and pixelated. So, that approach is not feasible. We need to think of something different.  After a lot of thinking, we came up with an algorithm for the problem. We know the height of the logo would not be greater than 45px. We calculate the appropriate width and height of the logo, resize the image, and calculate dynamic padding which we add to the anchor element (inside which the image is located) if the height of the image comes out to be less than 45px. This is all done during the generation of the app. Note that the default padding is 5px and we add the extra pixels on top of it. This way, the logo doesn’t appear out of place or pixelated or extra wide and long. The detailed steps are mentioned below Declare variable padding = 5px Get the width, height and aspect ratio of the image. Set the height to 45px and calculate the width according to the aspect ratio. If the width <= 110px, then directly resize the image and no change in padding is necessary If the width > 110px, then make width constant to 110px and calculate height according to the aspect ratio. It will surely come…

Continue ReadingScaling the logo of the generated events properly in Open Event Webapp

Writing Selenium Tests for Checking Bookmark Feature and Search functionality in Open Event Webapp

We integrated Selenium Testing in the Open Event Webapp and are in full swing in writing tests to check the major features of the webapp. Tests help us to fix the issues/bugs which have been solved earlier but keep on resurging when some new changes are incorporated in the repo. I describe the major features that we are testing in this. Bookmark Feature The first major feature that we want to test is the bookmark feature. It allows the users to mark a session they are interested in and view them all at once with a single click on the starred button. We want to ensure that the feature is working on all the pages. Let us discuss the design of the test. First, we start with tracks page. We select few sessions (2 here) for test and note down their session_ids. Finding an element by its id is simple in Selenium can be done easily. After we find the session element, we then find the mark button inside it (with the help of its class name) and click on it to mark the session. After that, we click on the starred button to display only the marked sessions and proceed to count the number of visible elements on the page. If the number of visible session elements comes out to be 2 (the ones that we marked), it means that the feature is working. If the number deviates, it indicates that something is wrong and the test fails. Here is a part of the code implementing the above logic. The whole code can be seen here // Returns the number of visible session elements on the tracks page TrackPage.getNoOfVisibleSessionElems = function() { return this.findAll(By.className('room-filter')).then(this.getElemsDisplayStatus).then(function(displayArr) { return displayArr.reduce(function(counter, value) { return value == 1 ? counter + 1 : counter; }, 0); }); }; // Bookmark the sessions, scrolls down the page and then count the number of visible session elements TrackPage.checkIsolatedBookmark = function() { // Sample sessions having ids of 3014 and 3015 being checked for the bookmark feature var sessionIdsArr = ['3014', '3015']; var self = this; return self.toggleSessionBookmark(sessionIdsArr).then(self.toggleStarredButton.bind(self)).then(function() { return self.driver.executeScript('window.scrollTo(0, 400)').then(self.getNoOfVisibleSessionElems.bind(self)); }); }; Here is the excerpt of code which matches the actual number of visible session elements to the expected number. You can view the whole test script here //Test for checking the bookmark feature on the tracks page it('Checking the bookmark toggle', function(done) { trackPage.checkIsolatedBookmark().then(function(num) { assert.equal(num, 2); done(); }).catch(function(err) { done(err); }); }); Now, we want to test this feature on the other pages: schedule and rooms page. We can simply follow the same approach as done on the tracks page but it is time expensive. Checking the visibility of all the sessions elements present on the page takes quite some time due to a large number of sessions. We need to think of a different approach.We had already marked two elements on the tracks page. We then go to the schedule page and click on the starred mode. We calculate the current height of the page. We…

Continue ReadingWriting Selenium Tests for Checking Bookmark Feature and Search functionality in Open Event Webapp

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 generator, Organizer 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 = {}; //…

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 After Clicking on the first session element 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!! 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…

Continue ReadingImplementing Single Session Pages in Open Event Webapp