Jobs: Ember.JS Frontend Contract Developer for Open Event Project (Freelance)

STATUS: OPEN

Contract Basis | Location: Remote

As a frontend contractor you will collaborate with our team to bring the Open Event project hosted on eventyay.com to the next level. We define the scope through a list of dedicated issues that are postedon our repository and which you need to solve as part of this project. Most issues in this list only require changes on the Ember.JS frontend, for some issues minor changes in the Python/Flask backend might be required where you need to align with the responsible developer.

We use Ember.js as a frontend technology and Flask as a backend. The team follows best practices, uses scrum emails for the daily standup and Gitter chat for chat communication. 

After successful completion of the project options are to take on a follow up freelance project or to be hired. Future opportunities are to work from our bases in Singapore or Germany.

[Apply Here]

Responsibilities

  • Fix bugs and develop features for Open Event frontend as outlined in issue
  • Write unit tests for all portions of your code and add missing tests where applicable in the features handled
  • Work according to FOSSASIA Best Practices
  • Provide scrums and communicate on chat
  • Participate in developer meeting where necessary

Requirements

  • Strong expertise with Ember.js
  • Experience developing HTML, CSS, and Javascript
  • Experience with Continuous Integration tests
  • Understanding of best practices for web development and software design
  • Good spoken/written English

About the team

  • We are a team working with a community of FOSS developers working remotely in different timezones
  • We have an informal and collaborative environment
  • You can talk to us on our Gitter chat

Code

Please check out the project on GitHub before applying. Resolve some relevant issues to showcase your ability.

 

Continue ReadingJobs: Ember.JS Frontend Contract Developer for Open Event Project (Freelance)

How does SUSI AI web bot plugin work

  

In this blog, we’ll learn how SUSI AI web plugin works. Normally, for any bot like Kik bot, we don’t have to worry about the chat boxes or the way chatbot is rendered on screen because all of that is handled by the service on which we are building our bot. So, for these bots, we simply take the text input from user, send a GET request to SUSI server, receive the response and display it.

This is not the case with SUSI AI Web Bot plugin. In this case, there is not Bot platform.

Hence, there are a lot of other things that we need to take care of here. Let’s explore the main ones among them.

Adding bot to website:

The final product that we’re going to provide to the client is going to be just a JavaScript code. Hence the HTML code of bot widget needs to be added to the <body> of the website. This is done usingappend() method of jQuery. The html() method sets or returns the content (innerHTML) of selected elements.

Syntax:

$(selector).append(content)

So we store the HTML code of bot widget in a variable and then returns the content of that variable to the body tag using:

$("body").append(mybot);

Here “mybot” is the variable containing HTML code of bot widget.
The text boxes of user texts and bot texts are added to a HTML element in the same way.

Event handling:

JavaScript’s interaction with HTML is handled through events that occur when the user or the browser manipulates a page. Loading of the page, clicking, pressing a key, closing a window, resizing the window etc are all examples of events.

In the SUSI AI web bot, there are two major events.

  1. Clicking a button

This is required for allowing clicks on send button. Clicking of a button can be done in many ways. There are two major ways that are used in SUSI AI web bot.

Some elements already exist on the webpage. For example – the HTML code of web bot. It is added to the body tag as soon as webpage loads. For these elements we use click(). The click() binding is called “direct” binding which can be attached to the elements that already exists.

Syntax:

$(selector).click(function);

Selector – Class, ID or name of HTML element.
Function – Specifies the function to run when click event occurs.

Some elements are dynamically generated. This means that they are added at a later point of time. For example – the text messages are added when user types in. This happens after loading of page. For this, we have to create “delegated” binding by using on() method.

Syntax:

$(selector).on(event,childSelector,data,function);

Selector – Class, ID or name of HTML element
Event – (Required) Specifies one or more event(s) or namespaces to attach to the selected elements.
childSelector – (Optional) Specifies that the event handler should only be attached to the specified child elements
Data – (Optional) Specifies additional data to pass along to the function
Function – Specifies the function to run when click event occurs.

  1. Pressing the enter key

For identifying which key is pressed, we use key codes. The code for enter key is 13.
The on() method is used for handling this event. It’ll be clear from the following code snippet:

Keyup – Keyboard key is released
Keypress – Keyboard key is pressed

$('#txMessage').on('keyup keypress', function(e) {
        var keyCode = e.keyCode || e.which;
        var text = $("#txMessage").val();
        if (keyCode === 13) {
                if(text == "" ||  $.trim(text) == '') {
                        e.preventDefault();
                        return false;
                } else {
                        $("#chat-input").blur();
                        setUserResponse(text);
                        send(text);
                        e.preventDefault();
                        return false;
                }
        }
});

This is the code used to handle “enter” key event in SUSI AI web bot. Here “txMessage” is the id of text box where user entered text.

Making GET HTTP request to SUSI Server

We use ajax() method of jQuery to perform an asynchronous HTTP request to SUSI Server. Looking at the code will make things more clear:

$.ajax({
        type: "GET",
        url: baseUrl+text,
        contentType: "application/json",
        dataType: "json",
        success: function(data) {
                main(data);
        },
        error: function(e) {
                console.log(e);
        }
});

This is a GET type Ajax request. The endpoint of SUSI API is in url . We can see from dataType that the response received will be a JSON file. If the file is successfully received then main function is called else the error is logged into console.

References :

Continue ReadingHow does SUSI AI web bot plugin work

Build Button Resolution in Meilix

Meilix Generator is a webapp which has a build button and after clicking the button it triggers the travis of Meilix repository. You can even generate ISO from your phone. But while opening the web app on a phone, we came to see that the build button is not properly visible. Footer of the page hide the build button. This blog shows the way to identify the build button issue and to make it responsive for all screen sizes.

Error Rectification

There are two elements that need correction:

  • Build button
  • Footer

Build Button

<input class="btn btn-info mx-auto btn-block" id="file-upload" value="Build" required="" type="submit">

 

Here build button act as a input submit button.

class="form-group"> class="btn btn-info mx-auto btn-block" id="file-upload" value="Build" required="" type="submit" style="height: 50px; width: 360px; border-radius: 500px;"/>

 

We first added the group to include the button in that form. Then we add the build button property to give the button certain look.

But now also the footer overlap the button. So now we need to work on footer part.

Footer

<footer class="footer">

 

We remove footer class and made a new div tag with the id = “deployment”. Then we set the css for the deployment id to customise its appearance.

     #deployment {
      position: absolute;
      text-align: center;
      width: 100%;
      padding: 10px;
      bottom: 0px;
      border-top: 1px solid #bfbfbf;
      background-color: #f9f9f9;
    }

 

This set the footer and build button appropriate to become responsive for all sizes.

Reference:

HTML form tag

Input type Submit tag

Continue ReadingBuild Button Resolution in Meilix

Implementing Direct URL in loklak Media Wall

Direct URL is a web address which redirects the user to the preset customized media wall so that the media wall can directly be used to be displayed on the screen. Loklak media wall provides direct URL which has information related to customizations set by the user included in the web address. These customizations, as the query parameters are detected when the page is initialized and actions are dispatched to make changes in the state properties, and hence, the UI properties and the expected behaviour of media wall.

In this blog, I would be explaining how I implemented direct URL in loklak media wall and how customizations are detected to build on initialization of component, a customized media wall.

Flow Chart

Working

Media Wall Direct URL effect

This effect detects when the WALL_GENERATE_DIRECT_URL action is dispatched and creates a direct URL string from all the customization state properties and dispatches a side action WallShortenDirectUrlAction() and stores direct URL string as a state property. For this, we need to get individual wall customization state properties and create an object for it and supply it as a parameter to the generateDirectUrl() function. Direct URL string is returned from the function and now, the action is dispatched to store this string as a state property.

@Effect()
generateDirectUrl$: Observable<Action>
= this.actions$
.ofType(mediaWallDirectUrlAction.ActionTypes.WALL_GENERATE_DIRECT_URL)
.withLatestFrom(this.store$)
.map(([action, state]) => {
return {
query: state.mediaWallQuery.query,
.
.
.
wallBackground: state.mediaWallCustom.wallBackground
};
})
.map(queryObject => {
const configSet = {
queryString: queryObject.query.displayString,
.
.
.
wallBackgroundColor: queryObject.wallBackground.backgroundColor
}
const shortenedUrl = generateDirectUrl(configSet);
return new mediaWallDirectUrlAction.WallShortenDirectUrlAction(shortenedUrl);
});

Generate Direct URL function

This function generates Direct URL string from all the current customization options value. Now,  keys of the object are separated out and for each element of the object, it checks if there is some current value for the elements and it then first parses the value of the element into URI format and then, adds it to the direct URL string. In such a way, we are creating a direct URL string with these customizations provided as the query parameters.

export function generateDirectUrl(customization: any): string {
const shortenedUrl = ;const activeFilterArray: string[] = new Array<string>();
let qs = ;
Object.keys(customization).forEach(config => {
if (customization[config] !== undefined && customization[config] !== null) {
if (config !== ‘blockedUser’ && config !== ‘hiddenFeedId’) {
qs += `${config}=${encodeURIComponent(customization[config])}&`;
}
else {
if (customization[config].length > 0) {
qs += `${config}= ${encodeURIComponent(customization[config].join(‘,’))}&`;
}
}
}
});
qs += `ref=share`;
return qs;
}

Creating a customized media wall

Whenever the user searches for the URL link on the web, a customized media wall must be created on initialization. The media wall component detects and subscribes to the URL query parameters using the queryParams API of the ActivatedRoute. Now, the values are parsed to a required format of payload and the respective actions are dispatched according to the value of the parameters. Now, when all the actions are dispatched, state properties changes accordingly. This creates a unidirectional flow of the state properties from the URL parameters to the template. Now, the state properties that are supplied to the template are detected and a customized media wall is created.

private queryFromURL(): void {
this.__subscriptions__.push(
this.route.queryParams
.subscribe((params: Params) => {
const config = {
queryString: params[‘queryString’] || ,
imageFilter: params[‘imageFilter’] || ,
profanityCheck: params[‘profanityCheck’] || ,
removeDuplicate: params[‘removeDuplicate’] || ,
wallHeaderBackgroundColor: params[‘wallHeaderBackgroundColor’] || ,
wallCardBackgroundColor: params[‘wallCardBackgroundColor’] || ,
wallBackgroundColor: params[‘wallBackgroundColor’] ||
}
this.setConfig(config);
})
);
}public setConfig(configSet: any) {
if (configSet[‘displayHeader’]) {
const isTrueSet = (configSet[‘displayHeader’] === ‘true’);
this.store.dispatch(new mediaWallDesignAction.WallDisplayHeaderAction(isTrueSet));
}
.
.
if (configSet[‘queryString’] || configSet[‘imageFilter’] || configSet[‘location’]) {
if (configSet[‘location’] === ‘null’) {
configSet[‘location’] = null;
}
const isTrueSet = (configSet[‘imageFilter’] === ‘true’);
const query = {
displayString: configSet[‘queryString’],
queryString: ,
routerString: configSet[‘queryString’],
filter: {
video: false,
image: isTrueSet
},
location: configSet[‘location’],
timeBound: {
since: null,
until: null
},
from: false
}
this.store.dispatch(new mediaWallAction.WallQueryChangeAction(query));
}
}

Now, the state properties are rendered accordingly and a customized media wall is created. This saves a lot of effort by the user to change the customization options whenever uses the loklak media wall.

Reference

Continue ReadingImplementing Direct URL in loklak Media Wall

Generating Ticket PDFs in Open Event API Server

In the ordering system of Open Event API Server, there is a requirement to send email notifications to the attendees. These attendees receive the URL of the pdf of the generated ticket. On creating the order, first the pdfs are generated and stored in the preferred storage location and then these are sent to the users through the email.

Generating PDF is a simple process, using xhtml2pdf we can generate PDFs from the html. The generated pdf is then passed to storage helpers to store it in the desired location and pdf-url is updated in the attendees record.

Sample PDF

PDF Template

The templates are written in HTML which is then converted using the module xhtml2pdf.
To store the templates a new directory was created at  app/templates where all HTML files are stored. Now, The template directory needs to be updated at flask initializing app so that template engine can pick the templates from there. So in app/__init__.py we updated flask initialization with

template_dir = os.path.dirname(__file__) + "/templates"

app = Flask(__name__, static_folder=static_dir, template_folder=template_dir)

This allows the template engine to pick the templates files from this template directory.

Generating PDFs

Generating PDF is done by rendering the html template first. This html content is then parsed into the pdf

file = open(dest, "wb")

pisa.CreatePDF(cStringIO.StringIO(pdf_data.encode('utf-8')), file)

file.close()

The generated pdf is stored in the temporary location and then passed to storage helper to upload it.

uploaded_file = UploadedFile(dest, filename)

upload_path = UPLOAD_PATHS['pdf']['ticket_attendee'].format(identifier=get_file_name())

new_file = upload(uploaded_file, upload_path)

This generated pdf path is returned here

Rendering HTML and storing PDF

for holder in order.ticket_holders:

  if holder.id != current_user.id:

      pdf = create_save_pdf(render_template('/pdf/ticket_attendee.html', order=order, holder=holder))

  else:

      pdf = create_save_pdf(render_template('/pdf/ticket_purchaser.html', order=order))

  holder.pdf_url = pdf

  save_to_db(holder)

The html is rendered using flask template engine and passed to create_save_pdf and link is updated on the attendee record.

Sending PDF on email

These pdfs are sent as a link to the email after creating the order. Thus a ticket is sent to each attendee and a summarized order details with attendees to the purchased.

send_email(

  to=holder.email,

  action=TICKET_PURCHASED_ATTENDEE,

  subject=MAILS[TICKET_PURCHASED_ATTENDEE]['subject'].format(

      event_name=order.event.name,

      invoice_id=order.invoice_number

  ),

  html= MAILS[TICKET_PURCHASED_ATTENDEE]['message'].format(

      pdf_url=holder.pdf_url,

      event_name=order.event.name

  )

)

References

  1. Readme – xhtml2pdf
    https://github.com/xhtml2pdf/xhtml2pdf/blob/master/README.rst
  2. Using xhtml2pdf and create pdfs
    https://micropyramid.com/blog/generating-pdf-files-in-python-using-xhtml2pdf/

 

Continue ReadingGenerating Ticket PDFs in Open Event API Server

Creating an Infobox for Mobile View Using Angular in Susper

In Susper, the Information and Analytics boxes disappeared for widths smaller than 1100px, since they were too big to fit in the existing page layout.
In Laptop view:

In Mobile view:

Hence we decided to design a new Info-cum-Analytics box for mobile devices, where the Analytics are displayed only if the ‘Show Analytics’ button is clicked and they are hidden on clicking the ‘Hide Analytics’ button.

The following is the html code for the Infobox:

<div class=“combo-box”>
<appinfobox></app-infobox>
<button class=“btn” id=“toggle-button” (click)=“BoxToggle()” type=“button” datatoggle=“collapse” datatarget=“#statbox” ariaexpanded=“false” ariacontrols=“collapseExample”>
{{boxMessage}} Analytics
</button>
<appstatsbox class=“collapse” id=“statbox”></app-statsbox>
</div>

We can make the following observations:

  • The combo box has both the Info and the Stats box, in addition to a button, which toggles the display status of the Analytics box.
  • To display the appropriate message on the button, we have a special function called BoxToggle() which sets the value of boxMessage()
  • The data-toggle attribute of the button has been set to collapse and the data-target is statbox
  • The statsbox has the class collapse, so that it collapses on clicking on the Toggle button.

The typescript code is as follows:

BoxToggle() {
if (this.boxMessage === ‘Show’) {
this.boxMessage = ‘Hide’;
} else {
this.boxMessage = ‘Show’;
}
}

This code thus effectively toggles between the messages Show and Hide, if the button is clicked when the message says Show, it changes to Hide and vice-versa

The CSS code:

@media screen and (min-width:768px) {
div.combo-box {
display: none;
}
}

The css code ensures that this combo-box is displayed only for widths less than 768px (only mobile and tablet screens).

To view the entire code you can check results.component.html and results.component.css in the Susper repository.

References

  1. W3 Schools for Collapsible buttons and their implementation: https://www.w3schools.com/bootstrap/bootstrap_collapse.asp
  2. Official Bootstrap documentation for collapsible buttons: https://v4-alpha.getbootstrap.com/components/collapse/
Continue ReadingCreating an Infobox for Mobile View Using Angular in Susper

How to Parse HTML Tags and Anchor Clickable Links in SUSI Android App

Html tags are used to define how contents of a webpage should be formatted and displayed. Sometimes the SUSI answer type response contains some html tags but showing these html tags without parsing would distort the normal text flow in SUSI Android.

For the query ‘Ask me something’ SUSI’s reply is

“data”: [
     {
      “question”: “Which soccer team won the Copa Am&eacute;rica 2015 Championship ? “,                                          
     }]

In SUSI Android this message looks like

As you can see that showing html tags without parsing distort the normal text flow. So we need to parse html tags properly. We use Html class for this purpose. Html class is present in android.text package and you can import it in the class where you want to use it.

import android.text.Html

fromHtml method of Html class is used to parse html tags. But for API level less than 24 and equal to or greater than 24 we use different parameters in fromHtml method.

For API level less than 24 we used

Html.fromHtml(model.getContent())

But for API level equal to or greater than 24 we have to use

Html.fromHtml(model.getContent(), Html.FROM_HTML_MODE_COMPACT)

Here the second parameter is legacy flags which decides how text inside a tag will be shown after parsing.

In case of Html.fromHtml(String string) legacy flag is by default FROM_HTML_MODE_LEGACY. It indicates that separate block-level elements with blank lines.

So after parsing html tags using fromHtml

But return type of fromHtml method is Spanned so if you need String then you have to convert it into string using toString() method.

Anchor action type in susi response contains link and text.

       “link”: “https://www.openstreetmap.org/#map=13/1.2896698812440377/103.85006683126556”,
       “text”: “Link to Openstreetmap: Singapore”

Here the text is the text we show in textview and link is used to show web content in the browser when user click on text. So first link and text are attached together like

“<a href=\”” +susiResponse.getAnswers().get(0).getActions().get(i).getAnchorLink() + “\”>”
+ susiResponse.getAnswers().get(0).getActions().get(1).getAnchorText() + “</a>”

Here text between the tag is description of link and after parsing we show this text in textview. It can be parsed using fromHtml method of Html class and textview is made clickable by

chatTextView.setMovementMethod(LinkMovementMethod.getInstance());

Resources

Continue ReadingHow to Parse HTML Tags and Anchor Clickable Links in SUSI Android App

Implementing Advanced Search Feature In Susper

Susper has been provided ‘Advanced Search’ feature which provides the user a great experience to search for desired results. Advanced search has been implemented in such a way it shows top authors, top providers, and distribution regarding protocols. Users can choose any of these options to get best results.

We receive data of each facet name from Yacy using yacy search endpoint. More about yacy search endpoint can be found here:  http://yacy.searchlab.eu/solr/select?query=india&fl=last_modified&start=0&rows=15&facet=true&facet.mincount=1&facet.field=host_s&facet.field=url_protocol_s&facet.field=author_sxt&facet.field=collection_sxt&wt=yjson

For implementing this feature, we created Actions and Reducers using concepts of Redux. The implemented actions can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/actions/search.ts

Actions have been implemented because these actually represent some kind of event. For e.g. like the beginning of an API call here.

We also have created an interface for search action which can be found here under reducers as filename index.ts: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/index.ts

Reducers are a pure type of function that takes the previous state and an action and returns the next state. We have used Redux to implement actions and reducers for the advanced search.

For advanced search, the reducer file can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/search.ts

The main logic has been implemented under advancedsearch.component.ts:

export class AdvancedsearchComponent implements OnInit {
  querylook = {}; // array of urls
  navigation$: Observable<any>;
  selectedelements: Array<any> = []; // selected urls by user
changeurl
(modifier, element) {
// based on query urls are fetched
// if an url is selected by user, it is decoded
  this.querylook[‘query’] = this.querylook[‘query’] + ‘+’ + decodeURIComponent(modifier);
  this.selectedelements.push(element);
// according to selected urls
// results are loaded from yacy
  this.route.navigate([‘/search’], {queryParams: this.querylook});
}

// same method is implemented for removing an url
removeurl(modifier) {
  this.querylook[‘query’] = this.querylook[‘query’].replace(‘+’ + decodeURIComponent(modifier), );

  this.route.navigate([‘/search’], {queryParams: this.querylook});
}

 

The changeurl() function replaces the query with a query and selected URL and searches for the results only from the URL provider. The removeurl() function removes URL from the query and works as a normal search, searching for the results from all providers.

The source code for the implementation of advanced search feature can be found here: https://github.com/fossasia/susper.com/tree/master/src/app/advancedsearch

Resources

Continue ReadingImplementing Advanced Search Feature In Susper

Writing Browser Specific CSS for Susper in Angular

In Susper, we were facing a unique problem for Information box and Analytics box alignment.
At a width of around 1290 px, the boxes fit perfectly in Firefox as shown:

However, they were slipping to the next line in Google Chrome browsers for the same dimension(1290 px)

The solution to this issue was to write browser-specific CSS.
The two most commonly used browser specific tags are

  1. @-moz-document url-prefix() { }: This tag is used to target the Mozilla Firefox browser in particular. Anything written within the curly braces will not apply to any other browser.
  2. @media screen and (-webkit-min-device-pixel-ratio:0) { }: This tag is used to target all browsers that support webkit such as Chrome, Safari etc.

For our problem, we need to use @media screen and (-webkit-min-device-pixel-ratio:0) { }
This was how the code was written for both the components (Information box and Analytics box). Please refer to infobox.component.css and statsbox.component.css for the entire code.

@media screen and (-webkit-min-device-pixel-ratio:0) {
@media screen and (max-width: 1300px) {
.card {
width: 366px;
}
}
}
@media screen and (max-width: 1280px) {
.card {
width: 366px;
}

As a result of this snippet of code, we see the following effects:

  • In Chrome, the boxes change to a smaller width at 1300px itself, thus preventing it from slipping to the next line
  • In Firefox, the boxes change to a smaller width only at 1280px, and not at 1300px, thus achieving the exact design we envisioned.

This is how the display finally looks in Chrome:

References:

  1. Stack overflow on specific CSS tags for Chrome: https://stackoverflow.com/questions/9328832/how-to-apply-specific-css-rules-to-chrome-only
  2. Stack overflow on specific CSS tags for Firefox: https://stackoverflow.com/questions/952861/targeting-only-firefox-with-css
Continue ReadingWriting Browser Specific CSS for Susper in Angular

Making a Sticky Top Navigation bar for Susper using Angular

A lot of websites, require a top navigation bar that sticks to the top, irrespective of the screen dimension size. This blog deals with how the top navigation bar was made sticky in Susper.

  1. Using the correct Bootstrap classes. Notice the code enveloping the navigation bar.

<nav class=“top-nav navbar navbar-static-top navbar-default”>

class=“container-fluid”>

class=“navbar-header” id=“navcontainer”>

</div>
</div>
</nav>

Points to note:

  • Using navbar and navbar-default creates a standard gray navigation bar.
  • Using navbar-static-top makes the navbar stick only to the top of the page and disappear on scrolling down.
  • Using container-fluid creates a container for the contents of the navbar with wide margins
  1.  Now we also need to write some personalized CSS code. Notice the classes navcontainer and  top-nav. This is the CSS code for these classes:

.top-nav{
margin-bottom: 0;
}
#navcontainer {
height: 65px;
width: 100vw;
}#navcontainer ul {
margin: 0;
padding: 0;
list-style-type: none;
}

Points to note:

  • Margin and padding can be set according to how the navbar should look. Click here to know the difference between margins and padding.
  • The height has been customized to 65px in Susper, with a width of 100vw(entire viewpost width).
  1. Lastly, if your navigation bar is inside the body tag, remember that by default, body has a top margin of 57 px. As a result you may see an extra white space on top of your navigation bar. To remove this:
  • Move the navigation bar code out of the body tag. If you can’t then,
  • Place your navigation bar in a container ( resultContainer on the Susper result page) and write this in your CSS file.

.resultContainer{
margin-top: -57px;
}

References:

Continue ReadingMaking a Sticky Top Navigation bar for Susper using Angular