Backend functionality of any Badge generator is to generate badges as per the requirements of the user. Currently Badgeyay is capable of generating badges by the following way:
Adding or Selecting a Pre-defined Image from the given set
Uploading a new image and then using it as a background
Well, badgeyay has been missing a functionality of generating Custom Colored images.
What is meant by Custom Colored Badges?
Currently, there are a set of 7 different kind of pre-defined images to choose from. But let’s say that a user want to choose from the images but doesn’t like any of the color. Therefore we provide the user with an additional option of applying custom background-color for their badges. This allows Badgeyay to deliver a more versatile amount of badges than ever before.
Adding the functionality to backend
Lets see how this functionality has been implemented in the backend of the project.
Step 1 : Adding a background-color route to backend
Before generating badges, we need to know that what is the color that the user wants on the badge. Therefore we created a route that gathers the color and saves the user-defined.svg into that particular color.
Now we have our schema and route done, So we can move forward with the logic of making badges.
Step 3 : Converting the SVG to PNG and adding custom color
Now we have the user-defined color for the badge background, but we still need a way to apply it to the badges. It is done using the following code below.
def do_svg2png(self, opacity, fill):
“””
Module to convert svg to png
:param `opacity` – Opacity for the output
:param `fill` – Background fill for the output
“””
filename = os.path.join(self.APP_ROOT, ‘svg’, ‘user_defined.svg’)
tree = parse(open(filename, ‘r’))
element = tree.getroot() # changing style using XPath.
path = element.xpath(‘//*[@id=”rect4504″]’)[0]
style_detail = path.get(“style”)
style_detail = style_detail.split(“;”)
style_detail[0] = “opacity:” + str(opacity)
style_detail[1] = “fill:” + str(fill)
style_detail = ‘;’.join(style_detail)
path.set(“style”, style_detail) # changing text using XPath.
path = element.xpath(‘//*[@id=”tspan932″]’)[0] # Saving in the original XML tree
etree.ElementTree(element).write(filename, pretty_print=True)
print(“done”)
png_name = os.path.join(self.APP_ROOT, ‘static’, ‘uploads’, ‘image’, str(uuid.uuid4())) + “.png”
svg2png(url=filename, write_to=png_name)
return png_name
Finally , we have our badges generating with custom colored background.
Badgeyay is an open source project developed by FOSSASIA Community for generating badges for conferences and events. The Project is divided into two parts frontend, which is in ember, and backend, which is in flask. Backend uses firebase admin SDK (Python) and Frontend uses firebase javascript client with emberfire wrapper for ember. Whenever an user signs up on the website, database listener that is attached to to the Model gets triggered and uses flask-mail for sending welcome mail to the user and in case of email and password signup, verification mail as well.
Problem is sending mail using libraries is a synchronous process and takes a lot of processing on the server. We can use messaging queues like RabbitMQ and Redis but that will be burden as server cost will increase. The workaround is to remove the code from the server and create a firebase cloud function for the same task.
Firebase cloud functions lets you run backend code on the cloud and can be triggered with HTTP events or listen for the events on the cloud, like user registration.
Procedure
Firebase uses our Gmail ID for login, so make sure to have a Gmail ID and on the first sight we will be greeted with Firebase console, where we can see our created or imported firebase apps.
Create the app by clicking on the Add Project Icon and write the name of the application (e.g. Test Application) and select the region, in my case it is India. Firebase will automatically generated an application ID for the app. Click on Create Project to complete creation of project
After Completion, click on the project to enter into the project. You will be greeted with an overview saying to integrate firebase with your project. We will click on the Add Firebase to web App and save the config as JSON in a file as clientKey.json for later use.
Now we need to install the firebase tools on our local machine so for that execute
npm i -g firebase-tools
Now login from the CLI so that firebase gets token for the Gmail ID of the user and can access the firebase account of that Gmail ID.
firebase login
After giving permissions to the firebase CLI from your Gmail account in the new tab opened in browser, create a folder named cloud_functions in the project directory and in that execute
firebase init
Select only functions from the list of options by pressing space.
After this select the project from the list where you want to use the cloud function. You can skip the step if you later want to add the cloud function to project by selecting don’t setup a default project and can later be used by command
firebase use --add
Choose the language of choice
If you want, you can enforce eslint on the project and after this the cloud function is set up and the directory structure looks as follows.
We will write our cloud function in index.js. So let’s take a look at index.js
const functions = require('firebase-functions');
// // Create and Deploy Your First Cloud Functions// // https://firebase.google.com/docs/functions/write-firebase-functions//// exports.helloWorld = functions.https.onRequest((request, response) => {// response.send("Hello from Firebase!");// });
As we can see there is a sample function already given, we don’t need that sample function so we will remove it and will write the logic for sending mail. Before that we need to acquire the key for service accounts so that admin functionality can be accessed in the cloud function. So for that go to project settings and then service accounts and click on Generate New Private Key and save it as serviceKey.json
Now the directory structure will look like this after adding the clientKey.json and serviceKey.json
We will use node-mailer for sending mails in cloud functions and as there is a limitation on the gmail account to send only 500 mails in a day, we can use third party services like sendGrid and others for sending mails with firebase. Configure node-mailer for sending mails as
Logic for sending Greeting Mail on user registration
exports.greetingMail = functions.auth.user().onCreate((user) => {
const email = user.email;
const displayName = user.displayName;
return sendGreetingMail(email, displayName);
});
function sendGreetingMail(email, displayName) {
const mailOptions = {
from: `${APP_NAME}<noreply@firebase.com>`,
to: email,
};
mailOptions.subject = `Welcome to Badgeyay`;
mailOptions.text = `Hey ${displayName || ''}! Welcome to Badgeyay. We welcome you onboard and pleased to offer you service.`;
return mailTransport.sendMail(mailOptions).then(() => {
return console.log('Welcome mail sent to: ', email)
}).catch((err) => {
console.error(err.message);
});
}
Function will get triggered on creation of user in firebase and calls the greeting mail function with parameters as the email id of the registered user and the Display name. Then a default template is used to send mail to the recipient and Logged on successful submission.
Currently firebase admin sdk doesn’t support the functionality to send verification mail but the client SDK does. So the approach which is followed in badgeyay is that admin SDK will create a custom token and client sdk will use that custom token to sign in and them send verification mail to the user.
Badgeyay is an open source utility to develop badges for events and tech conferences. Badgeyay project is divided into two components. Frontend part is designed with ember and backend part is designed with Flask and database as PostgreSQL and Firebase as PaaS.
After refactoring the backend API for generation of badges, now it is time to consume the API in frontend by ember, and the way to consume the api in ember front–end is with the use of in built ember-data library. Ember data behaves in a way similar to server side ORM’s (Object Relational Mappers). It is a very versatile library and can be equipped with variety of backend services. It can be used with REST as well as sockets and other transfer protocols for communication.
For better understanding the working of ember data, let’s see how to use the same to consume the File Upload endpoint in the backend.
Procedure
Enabling CORS on server, to allow cross-domain requests to the API.
fromflask_corsimport CORS
CORS(app, resources={r"*": {"origins": "*"}})
Creating Adapter for the model in frontend. In our case it is csv-file. In the adapter we need to specify the host and the path, because our backend api is not running on the same port.
After creating the adapter we need to create the record in the controller of the respective component. The record is like an object of a class, which when pushed to store will make a network request to backend (POST) and fetch the response from the backend. Backend response will provide the id to save in store
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
exportdefault Controller.extend({
routing : service('-routing'),
actions : {
mutateCSV(csvData) {
let csv_ = this.get('store').createRecord('csv-file', {
csvFile : csvData,
extension : 'csv'
});
csv_.save();
},
mutateText(txtData) {
console.log(txtData);
}
}
});
Next is to create serializers for the model. Serializers gets triggered at two moments, first when the data is sent to the server and second when data is received from the server. Each time an independent function gets executed. As the naming conventions of the functions pretty much explains their role, but for the sake of clarification serialize function gets executed when we send request to the server and normalizeResponse gets executed when we are getting response from the server.
After receiving the response a promise is returned by the push method to save the record in the store and we can see the id is saved in the ember-data object.
Badgeyay has two main components, the Python-Flask backend server and the EmberJS frontend.
EmberJS frontend uses ember data to save the data from the backend server api into the store of EmberJS frontend. To make the ember data frontend comply with backend api we need the backend server to send responses that comply with the standards of the JSON-API.
What is JSON-API?
As stated by JSONAPI.ORG
"If you've ever argued with your team about the way your JSON responses should be formatted, JSON API can be your anti-bikeshedding tool."
To put it up simply, JSON-API is a way of representing the JSON data that is being generated by the server backend. In this way we represent the JSON data in a particular way that follows the JSON-API convention. An example of data that follows json-api standards is given below:
We proceeded on to adding json-api into the Python-Flask backend. Before we proceed to adding json-api, we first need to install marshmallow_jsonapi
To install marshmallow_jsonapi
$ ~ pip install marshmallow-jsonapi
After installing marshmallow_jsonapi, we proceed onto making our first schema.
A schema is a layer of abstraction that is provided over a database model that can be used to dump data from or into an object. This object can therefore be used to either store in database or to dump it to the EmberJS frontend. Let us create a schema for File.
from marshmallow_jsonapi.flask import Schema
from marshmallow_jsonapi import fields
class FileSchema(Schema):
class Meta:
type_ = 'File'
self_view = 'fileUploader.get_file'
kwargs = {'id': '<id>'}
id = fields.Str(required=True, dump_only=True)
filename = fields.Str(required=True)
filetype = fields.Str(required=True)
user_id = fields.Relationship(
self_url='/api/upload/get_file',
self_url_kwargs={'file_id': '<id>'},
related_url='/user/register',
related_url_kwargs={'id': '<id>'},
include_resource_linkage=True,
type_='User'
)
So we have successfully created a Schema for getting files. This schema has an id, filename and filetype. It also has a relationship with the User.
Let us now create a route for this Schema. The below snippet of code is used to find a given file using this schema.
After adding JSON-API standards to the backend API we can easily integrate it with the EmberJS frontend. Now we can work on adding more schemas as a method of layers of abstraction so that the backend can serve more functionalities and the data can be consumed by the frontend as well.
Badgeyay project is divided into two parts i.e front-end with Ember JS and back-end with REST-API programmed in Python.
Badgeyay has many features related to enhancement in the generation of badges. It gives the choice of uploading data entries i.e by CSV or manually. There are options available for choosing Badge Background and font specifications. But there is an important feature missing which will make the service more user-friendly in terms of creation of badges for different types of events i.e, Badge Size.
Badge Size feature is implemented in Backend. I need to send the data in the backend in the desired format for creation of Badges with different sizes.
In this Blog, I will be discussing how I implemented Badge Size feature in Badgeyay Frontend in my Pull Request.
Let’s get started and understand it step by step.
Step 1:
Create Badge Size component with Ember CLI.
$ ember g component badge-component/badge-size
Step 2:
Write the HTML required in the badge-size component:
Badgeyay project is divided into two parts i.e front-end with Ember JS and back-end with REST-API programmed in Python.
Badgeyay frontend has many features like Login and Sign up features and Login with OAuth and the most important, the badge generation feature is also up and running but the important thing from the User’s perspective is to get notified of all the actions performed in the application so that user can proceed easily further after performing a specific action in the Application..
In this Blog, I will be discussing how I integrated ember-notify in Badgeyay frontend to notify user about the actions performed in my Pull Request.
Ember-notify displays a little notification message down the bottom of our application.
Let’s get started and understand it step by step.
Step 1:
This module is an ember-cli addon, so installation is easy:
npm install ember-notify --save-dev
Step 2:
Inject the notify service in the controller of the template. Here, I will showing how I added it in showing Log In and Logout messages and you can check the whole code in my Pull request for other controllers also.
// controllers/login.js import Ember from 'ember';
import Controller from '@ember/controller';
const { inject } = Ember;
exportdefault Controller.extend({
session : inject.service(),
notify : inject.service('notify'),
..........
this_.transitionToRoute('/');
this_.get('notify').success('Log In Successful');
}).catch(function(err) {
console.log(err.message);
this_.get('notify').error('Log In Failed ! Please try again');
});
............
this_.transitionToRoute('/');
this_.get('notify').success('Log In Successful');
})
.catch(err => {
console.log(err);
});
}).catch(function(err) {
console.log(err.message);
this_.get('notify').error('Log In Failed ! Please try again');
});
..........
// controllers/logout.jsimport Ember from 'ember';
import Controller from '@ember/controller';
const { inject } = Ember;
exportdefault Controller.extend({
session : inject.service(),
notify : inject.service('notify'),
beforeModel() {
returnthis.get('session').fetch().catch(function() {});
},
actions: {
logOut() {
this.get('session').close();
this.transitionToRoute('/');
this.get('notify').warning('Log Out Successful');
}
}
});
I have implemented ember-notify for Logging In and Out feature & in the similar way I have implemented it for other controllers and complete code can be seen in my Pull Request.
Step 3::
Now run the server to see the implemented changes by following command.
$ ember serve
Navigate to localhost and perform login and logout actions to see the changes.
Successful Log In
Successful Log out
Successful CSV Upload
Now, we are done with the integration of ember-notify in Badgeyay frontend to notify user about the actions performed in the Application.
Badgeyay is a badge generator and its main functionality is generating badges. Since the beginning of GSoC 2018 period, Badgeyay is under refactoring and remodeling process. We have introduced many APIs to make sure that Badgeyay works. Now, the badge generator has an endpoint to generate badges for your events/meetups
How to create badges?
Creating badges using the newly formed API is simpler than before. All you need to do is pass some basic details of the image you want, the data you want, the size and the color of font etc to the API and woosh! Within a blink of your eye the badges are generated.
Backend requires some data fields to generate badges
“csv” is the filename of the csv that the user uploads and get back as a result, “image” is the image name that user gets after a successful upload to the respective APIs, “text-color” is the color of the text that the user wants on the badges.
Once the user sends the data to the API, the required route is triggered and the data is checked,If the data is not present an error response is sent back to the user so as to inform them about the misplacement or improper format of data.
import os
from flask import Blueprint, jsonify, request
from flask import current_app as app
# from api.helpers.verifyToken import loginRequired
from api.utils.response import Response
from api.utils.svg_to_png import SVG2PNG
from api.utils.merge_badges import MergeBadges
router = Blueprint('generateBadges', __name__)
@router.route('/generate_badges', methods=['POST'])
def generateBadges():
try:
data = request.get_json()
except Exception as e:
return jsonify(
Response(401).exceptWithMessage(str(e),'Could not find any JSON'))
if not data.get('csv'):
return jsonify(
Response(401).generateMessage('No CSV filename found'))
if not data.get('image'):
return jsonify(Response(401).generateMessage('No Image filename found'))
csv_name = data.get('csv')
image_name = data.get('image')
text_color = data.get('text-color') or '#ffffff'
svg2png = SVG2PNG()
svg2png.do_text_fill('static/badges/8BadgesOnA3.svg', text_color)
merge_badges = MergeBadges(image_name, csv_name)
merge_badges.merge_pdfs()
output = os.path.join(app.config.get('BASE_DIR'), 'static', 'temporary', image_name)
return jsonify(
Response(200).generateMessage(str(output)))
After the data is received, we send it to MergeBadges which internally calls the GenerateBadges class which creates the badges.
Brief explanation of the Badge Generation Process:
- Gather data from the user- Fill the SVG for badges with the text color
- Load the image from uploads directory
- Generate badges for every individual
- Create PDFs for individual Badges
- Merge those PDFs to provide an all-badges pdf to the user
And this is how we generated badges for the user using the Badgeyay Backend API.
How is this effective?
We are making sure that the user chooses the image and csv that he/she has uploaded only,
In this way we maintain a proper workflow, we also manage these badges into the database and hence using the filenames helps a lot.It does not involve sending huge files and a lot of data like we had in the previous API.
Earlier, we used to send the image and the csv altogether that caused a serious mismanagement of the project. In this case we are accepting the CSVs and the Images on different API routes and then using the specific image and csv to make badges. We can now more easily relate to the files associated with each and every badge and henceforth we can easily manage them in the database.
Further Improvements
We will work on adding security to the route so that not anyone can create badges. We also need to integrate database into badges generated service so that we can maintain the badges that the user has generated.
Badgeyay has seen many changes in the recent past during its refactoring. It started off with backend and we have now transition to remodeling backend as well.
The backend transition is working perfectly. We have established sufficient APIs so far to get it working.
Some of the most important APIs that we created are
Image Upload API
File Upload API
Why do we need APIs?
We need APIs so that the frontend written in Ember JS can coordinate with the backend written in Python Flask with the database being PostgreSQL.
Creating the APIs
Creating these APIs is easy and straightforward. The following APIs are written in Python Flask with a backend database support of PostgreSQL.
Image Upload API
The image upload API considers that the frontend is sending the Image as a base64 encoded string and the backend is supposed to accept this string and convert this string into an image and save it onto the server.
We proceed by creating a file named fileUploader.py and code the following API.
First of all, we need to declare the imports
from flask import Blueprint, request, jsonify
from api.utils.response import Response
from api.helpers.verifyToken import loginRequired
from api.helpers.uploads import saveToImage, saveToCSV
Now, let’s create a route for image upload.
router = Blueprint('fileUploader', __name__)
@router.route('/image', methods=['POST'])
@loginRequired
def uploadImage():
try:
image = request.json['data']
except Exception as e:
return jsonify(
Response(400).exceptWithMessage(
str(e),
'No Image is specified'))
extension = request.json['extension']
try:
imageName = saveToImage(imageFile=image, extension=extension)
except Exception as e:
return jsonify(
Response(400).exceptWithMessage(
str(e),
'Image could not be uploaded'))
return jsonify(
Response(200).generateMessage({
'message': 'Image Uploaded Successfully',
'unique_id': imageName}))
We are using the saveToImage function to actually save the image to the backend server.
The function definition of saveToImage function is given below.
@router.route('/file', methods=['POST'])
@loginRequired
def fileUpload():
if 'file' not in request.files:
return jsonify(
Response(401).generateMessage(
'No file is specified'))file = request.files['file']
try:
csvName = saveToCSV(csvFile=file, extension='.csv')
except Exception as e:
return jsonify(
Response(400).exceptWithMessage(
str(e),
'CSV File could not be uploaded'))return jsonify(
Response(200).generateMessage({
'message': 'CSV Uploaded successfully',
'unique_id': csvName}))
What happens to the uploaded files?
The uploaded files gets saved into their respective directories, i.e. static/uploads/csv for CSV files and static/uploads/images for Image uploads.
The developer can view them from their respective folders. The static folder has been added to .gitignore so that it does not gets uploaded to github repository.
Everything has been taken care of with immense accuracy and proper error handling.
Further Improvements
Further improvements in Badgeyay includes adding separate database models, work on adding a beautiful frontend and to add proper routes for completing the backend.
Badgeyay project is divided into two parts i.e front-end of Ember JS and back-end with REST-API programmed in Python.
We already have logging In features implemented with the help of Firebase Authentication. A User can login in the Badgeyay with the help of Google, Facebook and Twitter credentials through a single click. Now, the challenging part is to implement the sign up with Email feature in Frontend and Backend to enable the user to signup and Login with the help of Email and Password
In this blog, I will be discussing how I set up Sign up feature in Badgeyay frontend to send the data in backend besides having Oauth logging features in Badgeyay integrated with Firebase in my Pull Request.
The sign up form is already implemented and I have already mentioned in my previous blog. So we need to send the form data to backend to register user so that user can login using the registered credentials. We need an Adapter, Signup action, controller , Signup Data model and a serializer for doing this task.
Let’s get started and understand the terminologies before implementing the feature.
What is Ember Data ?
It is a data management library for Ember Framework which help to deal with persistent application data.
We will generate Ember data model using Ember CLI in which we will define the data structure we will be requiring to provide to our application for User Signup.
We already have the signup form implemented in frontend. Now we need to provide a action to the form when the user enters the data in form.
If we add the{{action}} helper to any HTML DOM element, when a user clicks the element, the named event will be sent to the template’s corresponding component or controller.
We need to add signUp action in sign-up component and controller.
// Signup Controller import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
exportdefault Controller.extend({
routing : service('-routing'),
actions : {
signUp(email, username, password) {
const _this =this;
let user_ =this.get('store').createRecord('user-signup', {
email,
username,
password
});
user_.save()
.then(record => {
_this.transitionToRoute('/');
})
.catch(err => {
console.log(err);
});
}
}
});
// Sign up Componentimport Component from '@ember/component';
exportdefault Component.extend({
init() {
this._super(...arguments);
},
email :'',
password :'',
isLoading :false,
actions: {
signUp(event) {
event.preventDefault();
let email ='';
let password ='';
let username ='';
email =this.get('email');
password =this.get('password');
username =this.get('username');
this.get('signUp')(email, username, password);
}
},
});
What is an Adapter ?
An adapter determines how the data is persisted to a backend data store. We can configure the backend host, URL format and headers for REST API.
Now as we have specific Data Model for User Signup that we will be using for communicating with its backend so we have to create User-Signup Adapter with the help of Ember-CLI.
Step 1: Generate User Signup Adapter by following together.
$ ember generate adapter user-signup
Step 2: Extend the Adapter according to User-Signup Model.
Serializers format the Data sent to and received from the backend store. By default, Ember Data serializes data using the JSON API format.
Now as we have specific Data Model for User Signup that we will be using for communicating with its backend so we have to create User-Signup Serializer with the help Ember-CLI.
Step 1: Generate the User Signup Adapter by following command:
$ ember generate serializer user-signup
Step 2: Extend the serializer according to User-Signup Model.
We have successfully set up the User Signup in the frontend and data is communicated to backend in JSON API v1 specification with the help of serializers and Adapters.
This is how I set up Sign up feature in Badgeyay frontend to send the data in backend besides having Oauth logging features in Badgeyay integrated with Firebase in my Pull Request.
Badgeyay project is now divided into two parts i.e front-end of Ember JS and back-end with REST-API programmed in Python.
After a discussion, we have finalized to go with Semantic UI framework which uses simple, common language for parts of interface elements, and familiar patterns found in natural languages for describing elements. Semantic allows to build beautiful websites fast, with concise HTML, intuitive javascript and simplified debugging, helping make front-end development a delightful experience. Semantic is responsively designed allowing a web application to scale on multiple devices. Semantic is production ready and partnered with Ember framework which means we can integrate it with Ember frameworks to organize our UI layer alongside our application logic.
In this blog, I will be discussing how I added Log In and Signup Forms and their validations using Semantic UI for badgeyay frontend in my Pull Request.
Let’s get started and understand it step by step.
Step 1:
Generate ember components of Login and Sign up by using the following command :
$ ember generate component forms/login-form
$ ember generate component forms/signup-form
Step 2:
Generate Login and Sign up route by following commands.
$ ember generate route login
$ ember generate route signup
Step 3:
Generate Login and Sign up controller by following commands.
$ ember generate controller login
$ ember generate controller signup
Step 4:
Now we have set up the components, routes, and controllers for adding the forms for login and Sign up. Now let’s start writing HTML in handlebars, adding validations and implementing validations for the form components. In this blog, I will be sharing the code of Login form and actions related to logging In of user. You can check the whole code my Pull Request which I have made for adding these Forms.
You must be logged in to post a comment.