Making Email IDs Case Insensitive

In this Blog-Post, I will show how I made email-id case insensitive so that the open-event-server won’t allow multiple accounts with a single handle.

Previously, email ID was stored in the open-event-server database as raw string, i.e., case sensitive, so if a user entered the email with upper case alphabets, he/she needs to enter the same way during Auth, which could have lead to creation of multiple accounts with one email id and so is a major bug.  

Migration updation:

First, we need to handle the cases on the production database where people have made multiple accounts with a single handle because of this bug. For this, we need to write a complex SQL query, so let’s just make a new migration instead of updating the models.

def upgrade():  
      op.execute("UPDATE users SET deleted_at = current_timestamp, _email =      
   concat(_email, '_') where _email not in (SELECT DISTINCT ON (upper(_email)) _email 
   FROM users);", execution_options=None)

The nested query will return the list of all Emails which are not distinct on upper(_email). In simple words, the following query says: “Get the _email from users table where upper(_email) is distinct”. We want to delete the emails which are not returned in this query. For that, we set “deleted_at = current_timestamp” for those row. 

Make ‘_email’ column case insensitive:

We need to change the column data type to something that treats email as case-insensitive. That’s where “citext” comes into play. But we will still get an error on applying citext to “_email” column.

Any guesses why?

Remember we just updated “deleted_at” column of duplicate _email rows. So there still exists duplicate entries of emails in case we treat them case-insensitive, which defies the “_email” column rule to have unique entries defined in “user” Model. As it’s never a good idea to delete entries on the production database, we add a “_” to distinguish those email ids. 

Now the following lines will change the column type of “_email” to citext.

op.execute("create extension citext;", execution_options=None)

Now, we need  to write the opposite logic in case someone downgrades from the migration.

def downgrade():
    op.execute("alter table users alter column _email type     text;",execution_options=None)
    op.execute("UPDATE users SET deleted_at = null, _email = left(_email, length(_email)-1)        
    where right(_email, 1) = '_';",execution_options=None)
    op.execute("drop extension citext;",execution_options=None)

This will solve our bug, and won’t allow multiple accounts from the same handle.

Resources:

Issue: fossasia/open-event-server#5643
Pull Request : fossasia/open-event-server#5728
Documentation | Postgresql: https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT
Documentation | Citext: https://www.postgresql.org/docs/9.1/citext.html 


Continue Reading

Adding an option to hide map in browse events.

Open Event provides filtering while browsing events. These filters are present in a sidebar which also consists of a map. In this blog, I will describe how I implemented the feature to toggle the visibility of the map present in sidebar for mobile devices.

About the issue

This issue was part of improvements decided for the browse events section. Earlier the map in sidebar was shown irrespective of the user’s device. It is essentially not required to always show the map in mobile device and is a better choice to provide user with an option to view or hide the map.

Sidebar as viewed from an android device before the fix was merged.

The Solution

A button is introduced in the mobile view which controls if the map should be visible or hidden. In the sidebar component (app/components/explore/side-bar.js), a variable “isMapVisible” which decides if the map should be visible (if it is true) or not (if it is false) at a particular instant. A new action “toggleMap” is written which changes the value of “isMapVisible” whenever the button is clicked.

isMapVisible = true;
@action
  toggleMap() {
    this.toggleProperty(‘isMapVisible’);
  }

In the handlebar file (app/templates/components/explore/side-bar.hbs), the map and associated text is changed as per the truthy or falsy value of the “isMapVisible” in the component.

<div class=”map item {{if (not isMapVisible) ‘mobile hidden’}}”>

{{#if device.isMobile}}
  <div class=”ui bottom attached button” role=”button” {{action ‘toggleMap’}}> {{if (not isMapVisible) ‘Show’ ‘Hide’}} Map </div>
{{/if}}

After making the changes, the sidebar looks as follows on the mobile devices

The above images are from an Android device.

Resources:

Issue: https://github.com/fossasia/open-event-frontend/issues/3122
Pull Request: https://github.com/fossasia/open-event-frontend/pull/3444

Ember Docs:https://guides.emberjs.com/v2.14.0/tutorial/simple-component/Toggle Component Tutorial: https://www.learnhowtoprogram.com/ember-js/ember-js/components-hide-show-image

Continue Reading

Addition of new filters for event search

Open Event has an event search provided but it lacked two of the userful filters which will make searching for an event for a user easier than the current ecosystem. In this blog post, I describe how I implemented filtering of events on the basis of CFP status and Ticket Type.

About the issue

Addition of these two filters were subparts of improving the browse events page better. The browse events page currently functions in an optimal way but these improvements make it even better.

How the filters are added

For adding these two filters, it was required that the request sent to server to filter the event must contain ticket type and cfp so that the results from the server can be received. To achieve this, the frontend code needed the following changes:

  1. Passing of request variable into sidebar component present in app/templates/explore.hbs and addition of a clear filter for the same in the same file.e startDate=start_date endDate=end_date location=location ticket_type=ticket_type}
{{explore/side-bar model=model category=category sub_category=sub_category event_type=event_type startDate=start_date endDate=end_date location=location ticket_type=ticket_type}}
{{#if filters.ticket_type}}
  <div class=”ui mini label”>
    {{ticket_type}}
    <a role=”button” {{action ‘clearFilter’ ‘ticket_type’}}>
      <i class=”icon close”></i>
    </a>
  </div>
{{/if}}
  1. Adding UI for both the filters in app/templates/components/explore/side-bar.hbs. Accordion UI is used to achieve this.
  <div class=”item”>
    {{#ui-accordion}}
      <span class=”title”>
        <i class=”dropdown icon”></i>
        {{t ‘Ticket Type’ }}
      </span>
      <div class=”content menu”>
        <a href=”#”
          class=”link item {{if (eq ticket_type ‘free’) ‘active’}}”
          {{action ‘selectTicketType’ ‘free’}}>
          {{t ‘Free’}}
        </a>
        <a href=”#”
          class=”link item {{if (eq ticket_type ‘paid’) ‘active’}}”
          {{action ‘selectTicketType’ ‘paid’}}>
          {{t ‘Paid’}}
        </a>
      </div>
    {{/ui-accordion}}
  </div>
  1. Editing the routes (app/routes/explore.js) to make sure if the request has new filter parameter then it should be sent to server.
if (params.ticket_type) {
      filterOptions.push({
        name : ‘tickets’,
        op   : ‘any’,
        val  : {
          name : ‘type’,
          op   : ‘eq’,
          val  : params.ticket_type
        }
      });
    }
  1. Addition of new request parameter in the controller (app/controllers/explore.js)
queryParams  : [‘category’, ‘sub_category’, ‘event_type’, ‘start_date’, ‘end_date’, ‘location’, ‘ticket_type’],

ticket_type  : null,

if (filterType === ‘ticket_type’) {  this.set(‘ticket_type’, null);
}
  1. Editing the sidebar component to set the value of request parameter when the user interacts with the filter.
hideClearFilters: computed(‘category’, ‘sub_category’, ‘event_type’, ‘startDate’, ‘endDate’, ‘location’, ‘ticket_type’, function() {
    return !(this.category || this.sub_category || this.event_type || this.startDate || this.endDate || this.location || this.ticket_type !== null);
}),

selectTicketType(ticketType) {
  this.set(‘ticket_type’, ticketType === this.ticket_type ? null : ticketType);
},

this.set(‘ticket_type’, null);

Sidebar after the addition of filters.

Resources:

Issue: https://github.com/fossasia/open-event-frontend/issues/3098
Pull Requests:

Ticket Type: https://github.com/fossasia/open-event-frontend/pull/3158
CFS: https://github.com/fossasia/open-event-frontend/pull/3144

Specifying Query Params: https://guides.emberjs.com/release/routing/query-params/

UI Resources for the feature: https://semantic-org.github.io/Semantic-UI-Ember/#/modules/accordion

Continue Reading

Making an About Section for Badge Magic Android App

Whenever an application is created, it needs an “About Section” for people to know about who built the app. But a lot of about sections forget to give credit to the open-source libraries they use among other things. This blog post will describe how to create a good about section for an open-source android app, by making an example of the about section made in Badge Magic Android.

The about section is composed of multiple card views, each card view representing one section. The first card view contains the app icon along with some basic details of what the app does. Mention is made of the developers of the app by providing a link to the contributor’s page of the application. This is a really important part in order to give credit to the contributors of the app who helped build it. 

The proceeding section is another card view that gives additional information about the app. Here a link is provided to the GitHub repo of the app in order to help new contributors join and also help users report any issues they face with the app. Here, links to social media pages of the application such as Twitter, Facebook, etc can also be provided.

Then comes the last section, the one which generally gets missed out. Being an Open source application, the license of the application and the open-source libraries used should also be given credit. In this section, this is what is done. The license has been provided, which in this case is the Apache License 2.0. Then come the open-source dependencies. Since a lot of these are used in most open source apps, all cannot be mentioned in the card view itself. Here we use a special type of dialog, LisenceDialog to show all the dependencies the licenses used by these dependencies. To make use of the licenseDialog, just add the following line to the gradle of the app:

implementation ‘de.psdev.licensesdialog:licensesdialog:2.0.0’

Once this is done, dependencies can be added to the dialog in the form of notices. Each notice contains the name of the dependency, the link to where it is hosted and the license it uses. There is already a collection of all licenses provided included in the licencesDialog dependency and it just has to be called. For example, if the moshi dependency has to be shown in the dialog, it’s notice is added as follows:

val notices = Notice()

notices.addNotice(
Notice(
context?.getString(R.string.moshi),

context?.getString(R.string.moshi_github_link),
context?.getString(R.string.moshi_copy),
ApacheSoftwareLicense20()
)
)

Here are a couple of screenshots of how the About Section of Bade Magic looks like:

References:  

Licenses Dialog:   https://github.com/PSDev/LicensesDialog

Card view based layout: https://developer.android.com/guide/topics/ui/layout/cardview

Link to the pr in badge magic which added this feature: https://github.com/fossasia/badge-magic-android/pull/265

Tags:

Android, kotlin, badge magic, about, documentation, best practices, card view, license, open-source libraries, internship

Continue Reading

Managing Repository Size of the Phimp.me Android App

How to manage your repository size and prevent it from getting too large? Sometimes the size of a repository can get too large. This was the case with the Phimp.me and PSLabs Android repositories, which kept the history of apk files for testing in the Git history.

Did you ever notice that your repository size keeps increasing? Like your application size might be small, but the size of the repository is too large? 

This can happen because of a large commit history. Whatever changes that are made to your repo are stored as history. This process of maintaining the history isn’t bad, but it does increase the size of the repo unnecessarily. This might create a problem for contributors who will have to download the bloated repo, while the actual size of the application might be quite small. Due to an enormous amount of contributions to the Phimp.me and PSLab  the repository, their size increased too much. The Phimp.me repo grew to a staggering 600+mb, while the application is just around 20mb. This creates problems for new contributors because to work on an application that is just 20mb, they have to spend more than half a GB of data. Not only new contributors but sometimes existing contributors might also have to clone the repo again and to do so they would have to waste a lot of time and data. Therefore, a repo must be maintained in such a way that the size is not much bigger than the application on which the work is to be done. 

To get the repo size back under control, we used a tool called bfg repo cleaner. The advantage of using this tool is that the commit history does not get erased, and all contributors get credit for the changes they made.

So the first step to do is to find the files in the git history which are causing the repo size to be increased. This can be done using a handy command given below: 

git rev-list –objects –all | git cat-file –batch-check=’%(objecttype) %(objectname) %(objectsize) %(rest)’ | sed -n ‘s/^blob //p’ | sort –numeric-sort –key=2 | cut -c 1-12,41- | numfmt –field=2 –to=iec-i –suffix=B –padding=7 –round=nearest

This command will give all the files comprising the git history in an order such that the files towards the last will the ones with the largest size. Here, you can see which large files are bloating the repo size. The files that are not being used any longer or are refreshed every time a change is made in the repo can be safely deleted. 

For example, let’s take Phimp.me into consideration. 

The files which were causing the increase in size were the redundant apk files, gradle files and old screenshots of the app that used to be in the readme but were not in use anymore. Using the above command, we got all these files and deleted them using bfg. 

You can download the bfg.jar file from here. Once this is done, to delete files and folders using bfg, the commands needed are:

java -jar bfg.jar –delete-files <file name>java -jar bfg.jar –delete-folders <folder name>

To make your work easier, you can maintain bfg as an alias for java -jar bfg.jar.

Using this command deletes the required files. Once that is done, force push the changes to your repo. 

That’s it. This way you can easily maintain the size of your repo and keep deleting the extra files that cause a rrepo to bloat without erasing the commit history. 

Here is the link to the entire discussion of the issue on the Phimp.me project where you might find several other insights into the process especially while doing it for repos with android apps.

After doing the entire process, the size of Phimp.me was brought down to 27mb from 600mb. 

git clone https://github.com/fossasia/phimpme-android
Cloning into ‘phimpme-android’…
remote: Enumerating objects: 6304, done.
remote: Counting objects: 100% (6304/6304), done.
remote: Compressing objects: 100% (4674/4674), done.
remote: Total 23695 (delta 4285), reused 3539 (delta 1622), pack-reused 17391
Receiving objects: 100% (23695/23695), 27.10 MiB | 6.19 MiB/s, done.
Resolving deltas: 100% (14248/14248), done.

Resources

Tags: Android, Java, Phimpme, PSLabs Android, Repo size, git, history, repo cleaner, bfg, commit history

Continue Reading

Fixing Notification Services Across EventYay

In this Blog-Post, I will show you, the fixing of multiple notification messages getting sent on multiple clicks or display of multiple messages for a single response.

What Caused the Problem?

In our Open Event Frontend, We are using Ember Notify Services to inject notification across the web app to show the notifications. Due to improper error handling on the client side sometimes we get multiple notification messages for a single click or for a single action. As shown in this picture.

How Did we Tackle It ?

Since the following issue was long pertaining, I solved the issue by adding a unique ID to each notification injected through the webapp. Since a unique ID was associated with each notification message, A single notification was getting displayed on each and every action irrespective of number of clicks or number of actions called. If an actions caused two notification to be simultaneously triggered, One of them on the basis of ID is suppressed so that a unique and understandable notification get shown.

Code Snippet:

Before the changes:

.then(() => {
          if (state === 'draft') {
            this.notify.success(this.l10n.t('Your event has been published successfully.'));
          } else {
            this.notify.success(this.l10n.t('Your event has been unpublished.'));
          }

After the changes:

this.notify.success(this.isNewInvite ? this.l10n.t('Role Invite sent successfully') : this.l10n.t('Role Invite updated successfully'), {
            id: 'man_role'
          });
        })
        .catch(() => {
          this.notify.error(this.l10n.t('Oops something went wrong. Please try again'), {
            id: 'man_role_error'
          });
        })

Pull Request : Open-Event-Frontend-3438

Issue : Open-Event-Frontend-3437

Tags :

OpenEvent, EventYay, Fossasia, Intern-2k19

Continue Reading

Addition Of Landing Pages For Mobile Apps

In this Blog-Post, I will showcase the landing pages on the Android frontend of Eventyay. As the Eventyay Android App has been launched on Play Store as well as F-Droid, We needed to make sure users know that they can access all the details regarding their event on their phones too. Now EventYay features two landing pages for each of its mobile apps :

EventYay Attendee App 

EventYay Orga App

Addition Of Landing Pages was a 3-Step Procedure, which is stated below :

  1. ) Defining The Needed Routes and Making Space in Footer.
  2. ) Writing the Code and Designing the Layout for the web-page.
  3. ) Writing Integration Tests for both of the pages.

I will explain each one of the processes involved in making Landing Pages for Mobile Apps in brief in this Blog-Post.

1. Defining The Needed Routes and Making Space in Footer.

To add any web-page in the Open-Event Frontend , we need to define its route in the Application Router File. The code snippet which adds them is:-

// app/router.js

router.map(function() {
  this.route('login');
  this.route('register');
  this.route('reset-password');
  this.route('attendee-app');
  this.route('organizer-app');

After successfully defining the routes, We need to create the HandleBars for the corresponding routes. To access those routes we need to create a section on EventYay Footer to show to the users.

The following code will result in showing the links to the landing page in footer-section

2. Writing the Code and Designing the Layout for the web-page.

After successfully defining the routes and links, its time we move on to writing the HandleBars for the above pages. The first design for the page would be the pointing menu offered by Semantic-UI v.2.0, which would describe the current tab.

After the designing of Navigation Menu in the AttendeeApp.hbs, Its time we design the main outlet and the body of the page. The Design of the layout page is based on the Landing Pages Created in Other Platforms Like Event.IO, EventBrite etc. A Mobile View on the right side with the Key Features of The App on the left side

3. Writing Integration Tests for both of the Pages.

Since writing test is the most important part of the Development Lifecycle. I preferred to write Acceptance Test instead of Integration Test or Unit Test, As both of the pages created were very simple in functionality. In Acceptance Test, We would be checking the URL For Both Of The Pages in every case After Login or Without Login.

Test For Attendee App :

test('visiting /landing-attendee-app without login', async function(assert) {
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

  test('visiting /landing-attendee-app with login', async function(assert) {
    await login(assert);
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

Resources

Issue: OpenEventFrontend-2929

Pull Request : OpenEventFrontend-2979

Additional Resources: Routing Guide., Templates in EmberJS.

Tags: OpenEvent, EventYay, Fossasia, Android Apps, Intern-2k19

Continue Reading

Allow Same Discount/Access Code for Multiple Events in the Open Event Server

In this Blog-Post, I will show how to allow the system to create the same Discount/Access Code for multiple events in the Open Event Server.

What was the issue:

The main problem was that the server used to identify the discount code and access code based on the discount code/access code itself, which did not allow multiple events to have the same discount/access codes.

Can you think of a better solution to this?
Yes, we should have been searching for it based on the discount/access code as well as the event they are associated with.

Changing the endpoint:

Now to do so, we want to pass the id of the event as well as discount/access code itself with the endpoint so that we can search the database based on the event_id and the code itself.  

Changes in Discount/Access Code Endpoint:

'/event/<int:discount_event_id>/discount-code/<code>'
'/event/<int:access_event_id>/access-code/<code>'

Change logic for database search:

Now when searching for discount/access code in the database, we need to pass the event_id along with the discount/access code, so that we can get the column of discount/access code associated with that event, even if we have multiple discount/access code with the same name for a different event. 

Changes in Database search logic:

access = db.session.query(AccessCode).filter_by(code=kwargs.get('code')
event_id = kwargs.get('access_event_id')).first()

discount = db.session.query(DiscountCode).filter_by(code=kwargs.get('code'),
event_id = kwargs.get('discount_event_id')).first()

Change endpoint in API docs and update Dredd hooks:

Now that we have changed the endpoint to get a discount/access code, we need to change API docs as well as Dredd hooks to accommodate the change in API docs.

Changes in API docs:

## Get Discount Code Detail using the code [/v1/event/{event_id}/discount-code/{code}]
## Get Access Code Detail using the code [/v1/event/{event_id}/access-code/{code}]

Changes in Dredd Hooks:

In discount code hook:

discount_code.event_id = 1

In access code hook:

event = EventFactoryBasic()
db.session.add(event)
db.session.commit()

Resources:

Link to Issue: fossasia/open-event-server#6027
Link to PR: fossasia/open-event-server#6208

Continue Reading

Creating Custom Widgets in Badge Magic Android

In this blog, we are going to have a look on how I created this badge preview in fossasia/badge-magic-android


What is Canvas?

Canvas is a class in Android that performs 2D drawing of different objects onto the screen. The saying “a blank canvas” is very similar to what a Canvas object is on Android. It is basically, an empty space to draw onto.

Canvas Coordinate System

The coordinate system of the Android canvas starts in the top left corner, where [0,0] represents that point. The y axis is positive downwards, and x axis positive towards the right.


Some basics of Canvas, lets see how we drew this Preview Badge.

The Badge consists of only 2 components:

  1. Rounded Rectangle ( Background )
  2. Normal Rectangles ( LED Lights )

Let’s see how we create rounded rectangles in android. 

// Draw Background
canvas.drawRoundRect(bgBounds, 25f, 25f, bgPaint)

Using drawRoundRect() we can easily create the badge background. 25f specified is the corner radius of the rectangle.

The LED Lights are just drawable resources which are used according to the current state of the LED.

private fun drawLED(condition: Boolean, canvas: Canvas, xValue: Int, yValue: Int) {
   if (condition) {
       ledEnabled.bounds = cells[xValue].list[yValue]
       ledEnabled.draw(canvas)
   } else {
       ledDisabled.bounds = cells[xValue].list[yValue]
       ledDisabled.draw(canvas)
   }
}

This function draws the LED Lights if the condition is satisfied.

When we consider a custom view, we need to consider the changes which occur according. These layout changes are to be controlled and maintained accordingly, Let’s see how we manage the positioning of the led lights for every android device. Spoiler: Simple 10th Grade Maths xD

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
   super.onLayout(changed, left, top, right, bottom)
   val offset = 30
   val singleCell = (right - left - (offset * 3)) / badgeWidth
   val offsetXToAdd: Int = ((((right - offset).toFloat() - (left + offset).toFloat()) - (singleCell * badgeWidth)) / 2).toInt() + 1

   cells = mutableListOf()
   for (i in 0 until badgeHeight) {
       cells.add(Cell())
       for (j in 0 until badgeWidth) {
           cells[i].list.add(Rect(
               (offsetXToAdd * 2) + j * singleCell,
               (offsetXToAdd * 2) + i * singleCell,
               (offsetXToAdd * 2) + j * singleCell + singleCell,
               (offsetXToAdd * 2) + i * singleCell + singleCell
           ))
       }
   }
   bgBounds = RectF((offsetXToAdd).toFloat(), (offsetXToAdd).toFloat(), ((singleCell * badgeWidth) + (offsetXToAdd * 3)).toFloat(), ((singleCell * badgeHeight) + (offsetXToAdd * 3)).toFloat())
}

We create an offset which is nothing but the gap from the screen edge to the badge itself, now we need to have gaps on both sides of the badge and we also leave half the offset inside the badge which is the difference between the badge background and the LED starting point, hence we calculate the value of single cells by: 

val singleCell = (right - left - (offset * 3)) / badgeWidth

We minus the no of pixels on the right of the display to the left, to get the width of the actual screen. Then we minus the padding from the left and right which is offset * 3 . Now we divide it by the number of cells we want in the badge which is the badgeWidth.

Once we have the number of cells, we want to calculate the left, right, top and bottom positions of all the LED. What we now do is loop into the number of LEDs and then multiply the singleLed width with the current position to get the accurate pixels which need to be escaped from the left.

cells = mutableListOf()
   for (i in 0 until badgeHeight) {
       cells.add(Cell())
       for (j in 0 until badgeWidth) {
           cells[i].list.add(Rect(
               (offsetXToAdd * 2) + j * singleCell,
               (offsetXToAdd * 2) + i * singleCell,
               (offsetXToAdd * 2) + j * singleCell + singleCell,
               (offsetXToAdd * 2) + i * singleCell + singleCell
           ))
       }
   }

Now the fun part, we save all of it in a 2D ArrayList to be able to draw it later on.

Conclusion

Working on custom views is very unique. This experience is one of a kind and drawing stuff with basic maths is fun in the first place. Simple equations led me to create a preview which simulates the complete badge in software. 

Ressources

Continue Reading
Close Menu