Pokemon Go Ljubljana

What I learned by becoming a Pokémon trainer for a week

I have just survived a week of the latest cultural phenomenon – Pokémon Go. It started innocently enough – with a US iTunes account and a couple of free hours on a Monday evening. Since I wanted to meet a few more players, I decided to create an event for the following Friday. Something simple – let’s meet at the Congress Square in Ljubljana to mingle. It exploded almost immediately. On the first day, 300 people indicated on Facebook that they would come. By the end of the week, this number rose to 750 with 2000+ interested.

Lesson 1: Facebook is still a great viral machine

Organising events is a lot like a horror movie. You never go alone into the woods. In this case Alja Isakovic co-hosted the event and provided the much-needed sanity check. As a consequence, we had a Facebook Event that looked official (correct style of announcement, good graphics, sane venue and time). But most importantly, it was about something that everyone was hearing about, but was still hard to get in Slovenia. With the basics in place, we just had to share it to our personal Facebook networks. This exposed the event to enough people that we got noticed by existing Facebook Pokémon Go communities. A couple of cross-promotion partnerships later, we 10x our reach. More importantly, we reached the right people – early Pokémon Go adopters.

InterviewLesson 2: Journalists like a positive, feel-good story

Within a few hours of creating the event, we also got contacted by journalists. In my experience, once the first journalist gets in touch, others soon follow. Alja and I cleared our schedule for the next day and started explaining the game. The aspect that I liked the most is that we didn’t focus on the actual game content too much. We rather talked about the social and urban aspects of it. As it stands right now, the game itself is a pure antithesis of modern society. It encourages you to go out and explore the world around you, mingling with other players, and working together on the same goals. This explained the context for the global interest and makes it easier for journalists to explain it to the general audience. At some point, Alja also suggested that we need better titles. It was a light, summer-time story, so picking Pokémon trainer made complete sense. It’s just silly enough that it brings smile to people’s faces. It will also give me an opportunity to measure its reach and how long people will remember it.

Lesson 3: Letting it go

Pokémon Go is big at the moment and it’s going to have a huge following for months and years ahead. But high level of required engagement and battery drain on phones will be deal breakers for the general public. This will reduce the audience to the younger generation that will actually play the game. Discuss all the stats, trade Pokémon (once thats available) and battle each other. This will be a community of interest that needs enthusiastic leaders that will help them teach and organise follow-up events. I’m happy that many of them them came to the event in person and I’m sure they’ll figure out how to collaborate.

Conclusions

Facebook on its own is enough to get you traction, but you still need traditional media to get legitimacy and wider reach.

When 750 people on Facebook say they are going, 20% of that number will actually come (~150), as seen in our case. Still a huge success, just don’t plan your food budget based on that.

Just relax and go with the flow. Not everything has to have an immediate business value or a long term goal.

Thanks to Tamara Muhic, for proof-reading draft of this post.

Example of ember-form-for and mirage server side validation

At the time of writing, ember-form-for is the top Ember.js plugin for forms on Ember Observer. It provides a straightforward way to display form fields without having to write all the boilerplate HTML code around it.

What you do is you take:



    <label>First name: {{input value=model.firstname}}</label>
    {{#each model.errors.firstname as |error|}}
      <span class="errors">{{error.message}}</span>
    {{/each}}
  

and replace it with form-for-model helper:

{{#form-for model as |f|}}
  {{f.text-field "firstname"}}

  .. // other fields and submit button
{{/form-for}}

It will generate HTML code for you, as well as display validation errors.

Complete form becomes much more readable this way:

{{#form-for model as |f|}}
  {{f.text-field "firstname"}}
  {{f.text-field "lastname"}}

  {{f.select-field "gender" "unknown male female"}}
  {{f.checkbox-field "terms" label="I agree to the Terms of Service"}}

  {{f.submit "Submit"}}
{{/form-for}}

I’ve created a dedicated Github branch ember-form-for, that you can experiment with:
https://github.com/ember-examples/server-side-validation-mirage/tree/ember-form-for

Travel Time distances from Ljubljana Center

traveltime

View the interactive map

My friends are looking to buy an affordable house around Ljubljana. But what are their options, if they want to choose based on commute time? I decided to build a tool to visualise their options.

An isochrone map (isochrone plan, isochrone diagram) in science and urban planning is a map showing areas related to isochrones between different points. An isochrone is defined as “a line drawn on a map connecting points at which something occurs or arrives at the same time” (https://en.wikipedia.org/wiki/Isochrone_map)

Methodology and Technology

I’ve generated a grid of ~40.000 points in Slovenia and calculated travel time to it from a fixed point in Ljubljana Centre. Each point is displayed on a map with radius of 800 meters. It’s a tradeoff between number of points and realistic travel time with a car in a populated area.

For routing and time calculations, I used Project OSRM with OpenStreetMap database of Slovenia. A Python script connected all of this together into a static dataset for the the web site.

Lessons learned

I first coded a prototype with  Google Maps Directions API. It turns out that 2500 requests/day isn’t enough to make such an application practical.

I then found OSRM and it allowed me to host directions API directly on my computer. It’s also really fast. On a single i7 core, it can do about 40 requests/second. In practice is almost as good as commercial API’s. Based on my tests, the difference of results is about +/- 5 minutes.

What is missing is traffic data. OSRM is returning travel time in optimal traffic conditions. While some of this data is openly available, I’m not sure how to incorporate it. It would also mean that the whole visualisation would need to display the time component too.

What’s next

I’d like to build a similar version for public transportation – both inside Ljubljana and in the greater Ljubljana region.

It wouldn’t be too hard to make this map dynamic – so you could select your own starting point.

I’ve also discovered that running your own directions API opens up a number of new possibilities. One of them would is potentially adding a layer of routing to Prevoz.org so we could calculate travel time and also suggest rides that pass through your city.

Do you have any ideas?

Let me know in the comments.

I now also have a newsletter so you can be notified when I release something new. Subscribe to it now 🙂.

Ember Server Side Form validation with Django

In previous post (Ember Server Side Form validation with Mirage), I’ve looked at just the Ember.js side. In this article, I’ll connect it with Django so that we have an actual backend that’s returning our errors. It builds on top of previous Ember.js code. There are also full Github repositories at the end.

Lets start with Django. We’ll be using Django Rest Framework to generate server side Django.

We have a basic models.py:

from __future__ import unicode_literals
from django.db import models

class Registration(models.Model):
    GENDER_CHOICES = (
        ('male', 'Male'),
        ('female', 'Female'),
        ('unspecified', 'Unspecified')
    )

    firstname = models.CharField(max_length=100)
    lastname = models.CharField(max_length=100)
    submitted = models.DateTimeField(auto_now_add=True)

and an API in views.py:

from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status

from .models import Registration
from .serializers import RegistrationSerializer

class RegistrationViewSet(viewsets.ViewSet):
    def create(self, request):
        serializer = RegistrationSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

One thing to note – we return 400 Error code, instead of 422 that we mocked with Mirage. That is because Ember Django Adapter wraps 400 error to make it compatible with JSON API specification.

All of our business logic of API validation happens in serializers.py:

from rest_framework import serializers

from . models import Registration

class RegistrationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Registration
        fields = ('id', 'firstname', 'lastname')
   
    def validate_firstname(self, value):
    	if 'john' not in value.lower():
    		raise serializers.ValidationError('First name must be John')
    	return value

    def validate_lastname(self, value):

    	if 'smith' in value.lower():
    		raise serializers.ValidationError("Last name can't be Smith")
    	return value

    def validate(self, data):
    	if data['firstname'] == 'John' and data['lastname'] == 'Doe':
    		raise serializers.ValidationError("Please enter a more original name")

With server side in place, we can extend the previous example Ember.js app. We’ll be using Ember Django Adapter to make things easier.

First of, we need to turn of ember-cli-mirage in config/environment.js. We also set the development URL and API namespace at the same time.

  if (environment === 'development') {
    ENV.APP.API_HOST = 'http://localhost:8000';
    ENV.APP.API_NAMESPACE = 'api';

    ENV['ember-cli-mirage'] = {
      enabled: false
    };
  }

Another source of problems is that DS.RESTAdapter pluralises api endpoints. So instead of /api/registration/ it posts to /api/registrations/. To make it stop doing that we can define ‘registration’ as uncountable:
(ember generate drf-adapter application)

import DRFAdapter from './drf';
import Inflector from 'ember-inflector';

const inflector = Inflector.inflector;
inflector.uncountable('registration');

export default DRFAdapter.extend({});

Controller is still the same:

import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    save() {
      var model = this.get('model');

      model.save().then((registration) => {
        // it's a mock, we don't do anything
      }).catch((adapterError) => {
        // we just need to catch error
      });
    }
  }
});

The main improvement is that template error handling now also displays non-field errors (via model.errors.base):

{{#each model.errors.base as |error|}}
  <span class="errors">{{error.message}}</span>
{{/each}}

<form>
  <p>
    <label>First name: {{input value=model.firstname}}</label>
    {{#each model.errors.firstname as |error|}}
      <span class="errors">{{error.message}}</span>
    {{/each}}
  </p>
  <p>
    <label>Last name:
      {{input value=model.lastname}}
    </label>
    {{#each model.errors.lastname as |error|}}
      <span class="errors">{{error.message}}</span>
    {{/each}}
  </p>
  <button {{action 'save'}}>Save</button>
</form>

With this in place, form validation errors now work correctly. If server side form validation passes, it also writes data into Data Store.

Ember.js code is in ‘django-api’ branch at https://github.com/ember-examples/server-side-validation-mirage/tree/django-api
Django code is at https://github.com/ember-examples/django-drf-serializer-validations

Ember Server Side Form validation with Mirage

In this Ember.js example, I’ll show how to get ember-cli-mirage to return correct JSON-API response, so that Ember Data correctly processes Error message and makes it accessible inside the template.

This code was tested on Ember 2.6.0.

We have the following registration model:

import Model from 'ember-data/model';
import attr from 'ember-data/attr';

export default Model.extend({
  firstname: attr('string'),
  lastname: attr('string'),
});

There are three main elements that make this work: Controller that saves the data, Template that renders form and errors, and Mirage that mocks the response.

Controller’s save action is very basic, it calls save on the model and catches any errors:

import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    save() {
      this.get('model').save().then((registration) => {
        // it's a mock, we don't do anything
      }).catch((adapterError) => {
        // we just need to catch error
      });
    }
  }
});

Template displays form and any connected errors:

<form>
  <p>
    <label>First name: {{input value=model.firstname}}</label>
    {{#each model.errors.firstname as |error|}}
      <span class="errors">{{error.message}}</span>
    {{/each}}
  </p>
  <p>
    <label>Last name:
      {{input value=model.lastname}}
    </label>
    {{#each model.errors.lastname as |error|}}
      <span class="errors">{{error.message}}</span>
    {{/each}}
  </p>
  <button {{action 'save'}}>Save</button>
</form>

Now we only need to setup Ember Mirage. We emulate two different states. If firstname is ‘John’, we let request come through, and in all other cases we display error.

import Response from 'ember-cli-mirage/response';

export default function() {

  this.namespace = '/api';

  this.post('registrations', function(schema, request) {
   let attrs = JSON.parse(request.requestBody).data.attributes;

   if ( attrs.firstname === 'John') {
     return {
       'id': 1,
       'firstname': 'John',
       'lastname': attrs.lastname
     }
   } else {
     return new Response(422, {}, {
        "errors": [
          {
            "source": { "pointer": "/data/attributes/firstname"},
            "detail": "Please enter John as name"
          }
        ]}
     );
   }
  });
}

In the above example, it’s important that we return error code 422 (Unprocessable Entity), otherwise it will not be processed by JSONAPIAdapter as error.

Example repository

To make easier to see a complete and working example, I’ve also published a repository with complete code:
https://github.com/ember-examples/server-side-validation-mirage