Implementing Order Statistics API on Tickets Route in Open Event Frontend

The order statistics API endpoints are used to display the statistics related to tickets, orders, and sales. It contains the details about the total number of orders, the total number of tickets sold and the amount of the sales. It also gives the detailed information about the pending, expired, placed and completed orders, tickets, and sales. This article will illustrate how the order statistics can be displayed using the Order Statistics API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for statistics is GET /v1/events/{event_identifier}/order-statistics First, we need to create a model for the order statistics, which will have the fields corresponding to the API, so we proceed with the ember CLI command: ember g model order-statistics-tickets Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the model looks like this: import attr from 'ember-data/attr'; import ModelBase from 'open-event-frontend/models/base'; export default ModelBase.extend({ orders : attr(), tickets : attr(), sales : attr() }); As we need to display the statistics related to orders, tickets, and sales so we have their respective variables inside the model which will fetch and store the details from the API. Now, after creating a model, we need to make an API call to get the details. This can be done using the following: return this.modelFor('events.view').query('orderStatistics', {}); Since the tickets route is nested inside the event.view route so, first we are getting the model for event.view route and then we’re querying order statistics from the model. The complete code can be seen here. Now, we need to call the model inside the template file to display the details. To fetch the total orders we can write like this {{model.orders.total}}   In a similar way, the total sales can be displayed like this. {{model.sales.total}}   And total tickets can be displayed like this {{model.tickets.total}}   If we want to fetch other details like the pending sales or completed orders then the only thing we need to replace is the total attribute. In place of total, we can add any other attribute depending on the requirement. The complete code of the template can be seen here. The UI for the order statistics on the tickets route looks like this. Fig. 1: The user interface for displaying the statistics The complete source code can be seen here. Resources: Open Event API Docs Official Ember Data documentation An article on how to create GET requests in ember in the blog by Adam Sommer

Continue ReadingImplementing Order Statistics API on Tickets Route in Open Event Frontend

Implementing Pages API in Open Event Frontend

The pages endpoints are used to create static pages which such as about page or any other page that doesn’t need to be updated frequently and only a specific content is to be shown. This article will illustrate how the pages can be added or removed from the /admin/content/pages route using the pages API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for pages is GET /v1/pages First, we need to create a model for the pages, which will have the fields corresponding to the API, so we proceed with the ember CLI command: ember g model page Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the page model looks like this: import attr from 'ember-data/attr'; import ModelBase from 'open-event-frontend/models/base'; export default ModelBase.extend({ name : attr('string'), title : attr('string'), url : attr('string'), description : attr('string'), language : attr('string'), index : attr('number', { defaultValue: 0 }), place : attr('string') }); As the page will have name, title, url which will tell the URL of the page, the language, the description, index and the place of the page where it has to be which can be either a footer or an event. The complete code for the model can be seen here. Now, after creating a model, we need to make an API call to get and post the pages created. This can be done using the following: return this.get('store').findAll('page'); The above line will check the store and find all the pages which have been cached in and if there is no record found then it will make an API call and cache the records in the store so that when called it can return it immediately. Since in the case of pages we have multiple options like creating a new page, updating a new page, deleting an existing page etc. For creating and updating the page we have a form which has the fields required by the API to create the page.  The UI of the form looks like this. Fig. 1: The user interface of the form used to create the page. Fig. 2: The user interface of the form used to update and delete the already existing page The code for the above form can be seen here. Now, if we click the items which are present in the sidebar on the left, it enables us to edit and update the page by displaying the information stored in the form and then the details be later updated on the server by clicking the Update button. If we want to delete the form we can do so using the delete button which first shows a pop up to confirm whether we actually want to delete it or not. The code for displaying the delete confirmation pop up looks like this. <button class="ui red button" {{action (confirm (t 'Are you sure you would like to delete…

Continue ReadingImplementing Pages API in Open Event Frontend

Using Android Palette with Glide in Open Event Organizer Android App

Open Event Organizer is an Android Application for the Event Organizers and Entry Managers. The core feature of the App is to scan a QR code from the ticket to validate an attendee's check in. Other features of the App are to display an overview of sales, ticket management and basic editing in the Event Details. Open Event API Server acts as a backend for this App. The App uses Navigation Drawer for navigation in the App. The side drawer contains menus, event name, event start date and event image in the header. Event name and date is shown just below the event image in a palette. For a better visibility Android Palette is used which extracts prominent colors from images. The App uses Glide to handle image loading hence GlidePalette library is used for palette generation which integrates Android Palette with Glide. I will be talking about the implementation of GlidePalette in the App in this blog. The App uses Data Binding so the image URLs are directly passed to the XML views in the layouts and the image loading logic is implemented in the BindingAdapter class. The image loading code looks like: GlideApp .with(imageView.getContext()) .load(Uri.parse(url)) ... .into(imageView);   So as to implement palette generation for event detail label, it has to be implemented with the event image loading. GlideApp takes request listener which implements methods on success and failure where palette can be generated using the bitmap loaded. With GlidePalette most of this part is covered in the library itself. It provides GlidePalette class which is a sub class of GlideApp request listener which is passed to the GlideApp using the method listener. In the App, BindingAdapter has a method named bindImageWithPalette which takes a view container, image url, a placeholder drawable and the ids of imageview and palette. The relevant code is: @BindingAdapter(value = {"paletteImageUrl", "placeholder", "imageId", "paletteId"}, requireAll = false) public static void bindImageWithPalette(View container, String url, Drawable drawable, int imageId, int paletteId) { ImageView imageView = (ImageView) container.findViewById(imageId); ViewGroup palette = (ViewGroup) container.findViewById(paletteId); if (TextUtils.isEmpty(url)) { if (drawable != null) imageView.setImageDrawable(drawable); palette.setBackgroundColor(container.getResources().getColor(R.color.grey_600)); for (int i = 0; i < palette.getChildCount(); i++) { View child = palette.getChildAt(i); if (child instanceof TextView) ((TextView) child).setTextColor(Color.WHITE); } return; } GlidePalette<Drawable> glidePalette = GlidePalette.with(url) .use(GlidePalette.Profile.MUTED) .intoBackground(palette) .crossfade(true); for (int i = 0; i < palette.getChildCount(); i++) { View child = palette.getChildAt(i); if (child instanceof TextView) glidePalette .intoTextColor((TextView) child, GlidePalette.Swatch.TITLE_TEXT_COLOR); } setGlideImage(imageView, url, drawable, null, glidePalette); }   The code is pretty obvious. The method checks passed URL for nullability. If null, it sets the placeholder drawable to the image view and default colors to the text views and the palette. The GlidePalette object is generated using the initializer method with which takes the image URL. The request is passed to the method setGlideImage which loads the image and passes the GlidePalette to the GlideApp as a listener. Accordingly, the palette is generated and the colors are set to the label and text views accordingly. The container view in the XML…

Continue ReadingUsing Android Palette with Glide in Open Event Organizer Android App

Making App Name Configurable for Open Event Organizer App

Open Event Organizer is a client side android application of Open Event API server created for event organizers and entry managers. The application provides a way to configure the app name via environment variable app_name. This allows the user to change the app name just by setting the environment variable app_name to the new name. I will be talking about its implementation in the application in this blog. Generally, in an android application, the app name is stored as a static string resource and set in the manifest file by referencing to it. In the Organizer application, the app name variable is defined in the app's gradle file. It is assigned to the value of environment variable app_name and the default value is assigned if the variable is null. The relevant code in the manifest file is: def app_name = System.getenv('app_name') ?: "eventyay organizer" app/build.gradle The default value of app_name is kept, eventyay organizer. This is the app name when the user doesn't set environment variable app_name. To reference the variable from the gradle file into the manifest, manifestPlaceholders are defined in the gradle's defaultConfig. It is a map of key value pairs. The relevant code is: defaultConfig { ... ... manifestPlaceholders = [appName: app_name] } app/build.gradle This makes appName available for use in the app manifest. In the manifest file, app name is assigned to the appName set in the gradle. <application ... ... android:label="${appName}" app/src/main/AndroidManifest.xml By this, the application name is made configurable from the environment variable. Links: 1. ManifestPlaceholders documentation 2. Stackoverflow answer about getting environment variable in gradle

Continue ReadingMaking App Name Configurable for Open Event Organizer App

Implementing Notifications in Open Event Server

In FOSSASIA’s Open Event Server project, along with emails, almost all actions have necessary user notifications as well. So, when a new session is created or a session is accepted by the event organisers, along with the email, a user notification is also sent. Though showing the user notification is mainly implemented in the frontend site but the content to be shown and on which action to show is strictly decided by the server project. A notification essentially helps an user to get the necessary information while staying in the platform itself and not needing to go to check his/her email for every action he performs. So unlike email which acts as a backup for the informations, notification is more of an instant thing. The API The Notifications API is mostly like all other JSON API endpoints in the open event project. However in Notifications API we do not allow any to send a POST request. The admin of the server is able to send a GET a request to view all the notifications that are there in the system while a user can only view his/her notification. As of PATCH we allow only the user to edit his/her notification to mark it as read or not read. Following is the schema for the API: class NotificationSchema(Schema): """ API Schema for Notification Model """ class Meta: """ Meta class for Notification API schema """ type_ = 'notification' self_view = 'v1.notification_detail' self_view_kwargs = {'id': '<id>'} self_view_many = 'v1.microlocation_list_post' inflect = dasherize id = fields.Str(dump_only=True) title = fields.Str(allow_none=True, dump_only=True) message = fields.Str(allow_none=True, dump_only=True) received_at = fields.DateTime(dump_only=True) accept = fields.Str(allow_none=True, dump_only=True) is_read = fields.Boolean() user = Relationship(attribute='user', self_view='v1.notification_user', self_view_kwargs={'id': '<id>'}, related_view='v1.user_detail', related_view_kwargs={'notification_id': '<id>'}, schema='UserSchema', type_='user' ) The main things that are shown in the notification from the frontend are the title and message. The title is the text that is shown without expanding the entire notification that gives an overview about the message in case you don’t want to read the entire message. The message however provides the entire detail that is associated with the action performed. The user relationship stores which user the particular notification is related with. It is a one-to-one relationship where one notification can only belong to one user. However one user can have multiple notifications. Another important attribute is the is_read attribute. This is the only attribute that is allowed to be changed. By default, when we make an entry in the database, is_read is set to FALSE. Once an user has read the notification, a request is sent from the frontend to change is_read to TRUE. The different actions for which we send notification are stored in the models/notification.py file as global variables. USER_CHANGE_EMAIL = "User email"' NEW_SESSION = 'New Session Proposal' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' TICKET_PURCHASED = 'Ticket(s) Purchased' TICKET_PURCHASED_ATTENDEE = 'Ticket(s) purchased to Attendee ' EVENT_EXPORTED = 'Event Exported' EVENT_EXPORT_FAIL = 'Event Export Failed' EVENT_IMPORTED = 'Event Imported' HTML Templates The notification title and message that is stored in the…

Continue ReadingImplementing Notifications in Open Event Server

Implement Email in Open Event Server

In FOSSASIA’s Open Event Server project, we send out emails when various different actions are performed using the API. For example, when a new user is created, he/she receives an email welcoming him to the server as well as an email verification email. Users get role invites from event organisers in the form of emails, when someone buys a ticket he/she gets a PDF link to the ticket as email. So as you can understand all the important informations that are necessary to be notified to the user are sent as an email to the user and sometimes to the organizer as well. In FOSSASIA, we use sendgrid’s API or an SMTP server depending on the admin settings for sending emails. You can read more about how we use sendgrid’s API to send emails in FOSSASIA here. Now let’s dive into the modules that we have for sending the emails. The three main parts in the entire email sending are: Model - Storing the Various Actions Templates - Storing the HTML templates for the emails Email Functions - Individual functions for various different actions Let’s go through each of these modules one by one. Model USER_REGISTER = 'User Registration' USER_CONFIRM = 'User Confirmation' USER_CHANGE_EMAIL = "User email" INVITE_PAPERS = 'Invitation For Papers' NEXT_EVENT = 'Next Event' NEW_SESSION = 'New Session Proposal' PASSWORD_RESET = 'Reset Password' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' SESSION_ACCEPT_REJECT = 'Session Accept or Reject' SESSION_SCHEDULE = 'Session Schedule Change' EVENT_PUBLISH = 'Event Published' AFTER_EVENT = 'After Event' USER_REGISTER_WITH_PASSWORD = 'User Registration during Payment' TICKET_PURCHASED = 'Ticket(s) Purchased' In the Model file, named as mail.py, we firstly declare the various different actions for which we send the emails out. These actions are globally used as the keys in the other modules of the email sending service. Here, we define global variables with the name of the action as strings in them. These are all constant variables, which means that there value remains throughout and never changes. For example, USER_REGISTER has the value ‘User Registration’, which essentially means that anything related to the USER_REGISTER key is executed when the User Registration action occurs. Or in other words, whenever an user registers into the system by signing up or creating a new user through the API, he/she receives the corresponding emails. Apart from this, we have the model class which defines a table in the database. We use this model class to store the actions performed while sending emails in the database. So we store the action, the time at which the email was sent, the recipient and the sender. That way we have a record about all the emails that were sent out via our server. class Mail(db.Model): __tablename__ = 'mails' id = db.Column(db.Integer, primary_key=True) recipient = db.Column(db.String) time = db.Column(db.DateTime(timezone=True)) action = db.Column(db.String) subject = db.Column(db.String) message = db.Column(db.String) def __init__(self, recipient=None, time=None, action=None, subject=None, message=None): self.recipient = recipient self.time = time if self.time is None: self.time = datetime.now(pytz.utc) self.action = action self.subject = subject…

Continue ReadingImplement Email in Open Event Server

Enabling Google App Signing for Android Project

Signing key management of Android Apps is a hectic procedure and can grow out of hand rather quickly for large organizations with several independent projects. We, at FOSSASIA also had to face similar difficulties in management of individual keys by project maintainers and wanted to gather all these Android Projects under singular key management platform: Phimp.me Pocket Science Lab loklak wok Open Event Android and sample apps eventyay Organizer App Ask SUSI.AI To handle the complexities and security aspect of the process, this year Google announced App Signing optional program where Google takes your existing key’s encrypted file and stores it on their servers and asks you to create a new upload key which will be used to sign further updates of the app. It takes the certificates of your new upload key and maps it to the managed private key. Now, whenever there is a new upload of the app, it’s signing certificate is matched with the upload key certificate and after verification, the app is signed by the original private key on the server itself and delivered to the user. The advantage comes where you lose your key, its password or it is compromised. Before App Signing program, if your key got lost, you had to launch your app under a new package name, losing your existing user base. With Google managing your key, if you lose your upload key, then the account owner can request Google to reassign a new upload key as the private key is secure on their servers. There is no difference in the delivered app from the previous one as it is still finally signed by the original private key as it was before, except that Google also optimizes the app by splitting it into multiple APKs according to hardware, demographic and other factors, resulting in a much smaller app! This blog will take you through the steps in how to enable the program for existing and new apps. A bit of a warning though, for security reasons, opting in the program is permanent and once you do it, it is not possible to back out, so think it through before committing. For existing apps: First you need to go to the particular app’s detail section and then into Release Management > App Releases. There you would see the Get Started button for App Signing. The account owner must first agree to its terms and conditions and once it's done, a page like this will be presented with information about app signing infrastructure at top. So, as per the instructions, download the PEPK jar file to encrypt your private key. For this process, you need to have your existing private key and its alias and password. It is fine if you don’t know the key password but store password is needed to generate the encrypted file. Then execute this command in the terminal as written in Step 2 of your Play console: java -jar pepk.jar --keystore={{keystore_path}} --alias={{alias}} --output={{encrypted_file_output_path}} --encryptionkey=eb10fe8f7c7c9df715022017b00c6471f8ba8170b13049a11e6c09ffe3056a104a3bbe4ac5a955f4ba4fe93fc8cef27558a3eb9d2a529a2092761fb833b656cd48b9de6a You will have to…

Continue ReadingEnabling Google App Signing for Android Project

Implementing Roles API on Open Event Frontend to Create Roles Using an External Modal

This blog article will illustrate how the roles are created via the external model  on the admin permissions page in Open Event Frontend, using the roles API. Our discussion primarily will involve the admin/permissions/index route to illustrate the process.The primary end point of Open Event API with which we are concerned with for fetching the permissions  for a user is POST /v1/roles First we need to create a model for the user-permissions, which will have the fields corresponding to the api, so we proceed with the ember CLI command: ember g model role Next we define the model according to the requirements. The model needs to extend the base model class, and has only two fields one for the title and one for the actual name of the role. import attr from 'ember-data/attr'; import ModelBase from 'open-event-frontend/models/base'; export default ModelBase.extend({  name           : attr('string'),  titleName      : attr('string')  }); Next we need to modify the existing modal to incorporate the API and creation of roles in it. It is very important to note here that using createRecord as the model will result in a major flaw. If createRecord is used and the user tries to create multiple roles, other than the first POST request all the subsequent requests will be PATCH requests and will keep on modifying the same role. To avoid this, a new record needs to be created every time the user clicks on Add Role.  We slightly modify the modal component call to pass in the name and titleName to it. {{modals/add-system-role-modal  isOpen=isAddSystemRoleModalOpen isLoading=isLoading name=name titleName=titleName addSystemRole=(action 'addSystemRole')}} Upon entering the details of the roles and successful validation of the form, if the user clicks the Add Role button of the modal, the action addSystemRole will be triggered. We will write the entire logic for the same in the respective controller of the route. addSystemRole() {      this.set('isLoading', true);      this.get('store').createRecord('role', {        name      : this.get('name'),        titleName : this.get('titleName')      }).save()        .then(() => {          this.set('isLoading', false);          this.notify.success(this.l10n.t('User permissions have been saved successfully.'));          this.set('isAddSystemRoleModalOpen', false);          this.setProperties({            name          : null,            roleTitleName : null          });        })        .catch(()=> {          this.set('isLoading', false);          this.notify.error(this.l10n.t('An unexpected error has occurred. User permissions not saved.'));        });    }, At first the isLoading property is made true.This adds the semantic UI class loading to the the form,  and so the form goes in the loading state, Next, a record is created of the type role  and it’s properties are made equal to the corresponding values entered by the user. Then save() is called, which subsequently makes a POST request to the server. If the request is successful the modal is closed by setting the isAddSystemRoleModalOpen property to false. Also, the fields of the modal are cleared for a  better user experience in case multiple roles need to be added one after the other. In cases when  there is an error during the processing of the request the catch() block executes. And the modal is not closed. Neither are the fields cleared. Resources Ember JS-models - Official ember documentation Ember JS-Controller - Official ember documentation…

Continue ReadingImplementing Roles API on Open Event Frontend to Create Roles Using an External Modal

Giving Offline Support to the Open Event Organizer Android App

Open Event Organizer is an Android Application for Event Organizers and Entry Managers which uses Open Event API Server as a backend. The core feature of the App is to scan a QR code to validate an attendee's check in. The App maintains a local database and syncs it with the server. The basic workflow of the attendee check in is - the App scans a QR code on an attendee's ticket. The code scanned is processed to validate the attendee from the attendees database which is maintained locally. On finding, the App makes a check in status toggling request to the server. The server toggles the status of the attendee and sends back a response containing the updated attendee's data which is updated in the local database. Everything described above goes well till the App gets a good network connection always which cannot be assumed as a network can go down sometimes at the event site. So to support the functionality even in the absence of the network, Orga App uses Job Schedulers which handle requests in absence of network and the requests are made when the network is available again. I will be talking about its implementation in the App through this blog. The App uses the library Android-Job developed by evernote which handles jobs in the background. The library provides a class JobManager which does most of the part. The singleton of this class is initialized in the Application class. Job is the class which is where actually a background task is implemented. There can be more than one jobs in the App, hence the library requires to implement JobCreator interface which has create method which takes a string tag and the relevant Job is returned. JobCreator is passed to the JobManager in Application while initialization. The relevant code is: JobManager.create(this).addJobCreator(new OrgaJobCreator()); Initialization of JobManager in Application class public class OrgaJobCreator implements JobCreator { @Override public Job create(String tag) { switch (tag) { case AttendeeCheckInJob.TAG: return new AttendeeCheckInJob(); default: return null; } } } Implementation of JobCreator public class AttendeeCheckInJob extends Job { ... ... @NonNull @Override protected Result onRunJob(Params params) { ... ... Iterable<Attendee> attendees = attendeeRepository.getPendingCheckIns().blockingIterable(); for (Attendee attendee : attendees) { try { Attendee toggled = attendeeRepository.toggleAttendeeCheckStatus(attendee).blockingFirst(); ... } catch (Exception exception) { ... return Result.RESCHEDULE; } } return Result.SUCCESS; } public static void scheduleJob() { new JobRequest.Builder(AttendeeCheckInJob.TAG) .setExecutionWindow(1, 5000L) .setBackoffCriteria(10000L, JobRequest.BackoffPolicy.EXPONENTIAL) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) .setRequirementsEnforced(true) .setPersisted(true) .setUpdateCurrent(true) .build() .schedule(); } } Job class for attendee check in job To create a Job, these two methods are overridden. onRunJob is where the actual background job is going to run. This is the place where you implement your job logic which should be run in the background. In this method, the attendees with pending sync are fetched from the local database and the network requests are made. On failure, the same job is scheduled again. The process goes on until the job is done. scheduleJob method is where the related setting options are set. This method is…

Continue ReadingGiving Offline Support to the Open Event Organizer Android App

Implementing Users API to Display the Users at Admin Route in Open Event Frontend

This article will illustrate how the users are displayed and updated on the /admin/users route, their roles, user links etc. using the users API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for fetching the users is GET /v1/users First, we need to create a model for the user, which will have the fields corresponding to the API, so we proceed with the ember CLI command: ember g model user Next, we need to define the model according to the requirements. The model needs to extend the base model class. As a user can have multiple notifications, orders and  sessions etc. so we have to use ember data relationships “hasMany”. Hence, the model will have the following format. import ModelBase from 'open-event-frontend/models/base'; import { hasMany } from 'ember-data/relationships'; export default ModelBase.extend({ email : attr('string'), password : attr('string'), isVerified : attr('boolean', { readOnly: true }), isSuperAdmin : attr('boolean', { readOnly: true }), isAdmin : attr('boolean', { readOnly: true }), firstName : attr('string'), lastName : attr('string') }); The complete code for the model can be seen here Now, we need to load the data from the API using the above model, so we will send a GET request to the API to fetch the users. This can be easily achieved using this. return this.get('store').query('user', {'page[size]': 10 }); The above line is querying for the users from the store which is place where cache of all of the records that have been loaded by our application is there. If a route asks for a record, the store can return it immediately if it is there in the cache and we want to display only 10 users in a page so defined how many number of users has to be loaded at a time. Now we need to filter the users based on whether they are active or they have deleted their accounts. For this purpose, we need to pass filter to the query which will tell what type of users to be loaded at once. The next thing we need to do is to display the above data fetched from the API into an ember table. Ember table helps in allowing us to render very large data sets by only rendering the rows that are being displayed. For this, we defined a controller class which will help in letting the table know what all columns will be required to display and the attribute values they correspond in the API. We can also define the template for each column. The code for the controller class looks like this. import Ember from 'ember'; const { Controller } = Ember; export default Controller.extend({ columns: [ { propertyName : 'first-name', title : 'Name', disableSorting : true, disableFiltering : true }, { propertyName : 'email', title : 'Email', disableSorting : true, disableFiltering : true }, { propertyName : 'last-accessed-at', title : 'Last Accessed', template : 'components/ui-table/cell/admin/users/cell-last-accessed-at', disableSorting : true, disableFiltering : true } ] }); In the above…

Continue ReadingImplementing Users API to Display the Users at Admin Route in Open Event Frontend