AngelHack London 2016- Explor-ing the Possibilities

This article also appears on the AngelHack blog in the Global Hackathon Guest Post Series!


 

Last weekend, I went to AngelHack London. This was my third hackathon. Unlike my previous hackathons, I decided to go in with an idea this time.

The Prep

I moved to a new flat in March and I resolved to keep my bike commute exciting and to discover as much of London as possible by taking at least one new road each commute. I wanted a way to keep track of my progress on this goal, perhaps with a map showing all the roads that I had covered.

In the week leading up to AngelHack, I sketched out a bunch of pseudocode and class diagrams to efficiently calculate which sections of each ride overlapped with previous rides and which sections were new. I was very excited to sink my teeth into this logic, hopefully using C++ and test-driven development, while the rest of my team built a beautiful, usable interface for it.

The Pitch

At the hackathon, after the rules were explained and the sponsors talked through their APIs, participants were invited up on stage for “one-minute pitches” to recruit teammates. I jumped on the opportunity.

I was the first one up and after what felt like about 9 seconds of me mumbling about bikes, the environment, and design, I was promptly shot with a nerf gun and told that my time was up.

The Plan

Despite my incoherent pitch, several people approached me after my pitch. However, even without time constraints, I was still mostly met with blank stares when I tried to explain the idea. Apparently, not everyone likes the idea of extending their already very lengthy commutes just to see a few nice sights. One team member, Elliott, from the video production company Biscuit Bunker, helped to refine the idea and focus on a much simpler minimum viable product. He also came up with the hip, tech-ish name “Explor”.

Armed with a game plan, we split things up. Unfortunately, two of our five team members had to leave unexpectedly, even before dinner!

The Picnic

And then there were three. Pictured in the tweet below are Carlos, Elliott, and me, feeling fresh but somehow looking more bleary-eyed and delirious on Saturday evening than most participants would later look on Sunday afternoon following many more Red Bulls than hours of sleep. I guess the camera caught me mid-blink 🙂

Carlos had to leave relatively early as well, but thankfully he whipped up an initial html mockup for us.

The Process

I found it so much easier to communicate my ideas to my team through sketching than through words or code. Here are a few of our sketches.

IMG_2993

Elliott had made it clear from the start that he wasn’t a programmer and would not be coding, but could help with graphics and UX. So he worked on our logo and design, and I set out to chip away at tweaking the JavaScript code I copied from the Google Maps API’s documentation.

Once that was mostly done, we started to look at Carlos’s mockup, but neither of us had done much with html or css before.

The Progress

Two amazing AngelHack ambassadors, Harry and Aaron, heard us struggling and offered to help. They made it clear that while they couldn’t code for us, they would be happy to talk us through a thing or two. I can’t thank Harry and Aaron enough for their patience and kindness in helping us along. I gained a lot of confidence in web development and Elliott literally learned to code html and css overnight. By the morning, he was combining our pages, making the styling consistent, and just smashing it in general.

The Product

At the absolute last minute, we remembered that we had to submit a youtube video before our demo. Elliott once again stepped up and somehow made this awesome little video in five minutes flat.

By the 1pm deadline, we managed to have a very simple (but definitely demoable) product, live on the web.

Our demo went well and while we couldn’t compete with the incredible work that some of the other teams did over the weekend, we were very proud that we managed to put something together and we learned a lot in the process.

Thanks, AngelHack!

The Postscript

I’m considering finally implementing that complex ride comparison code that I planned and/or building on the Explor prototype, so if you want to talk commuting, offer advice, or lend a hand, please get in touch!

Advertisements

Simple Hack- Allowing Unsubscribe from Internal Mailing Lists

Shane Gryzko, Head of Vampire Operations

At my previous company I volunteered to coordinate group blood donations. I prided myself on always improving the donation count year-over-year. However, this required sending lots of emails to recruit and recognize donors and I quickly started to worry about annoying people. So I investigated ways of allowing unsubscribe.
First, I checked out MailChimp. Unfortunately, our machines aren’t directly connected to the Internet, so even though MailChimp does provide unsubscribe links, they wouldn’t work in our internal network.

I (Don’t) Want to Suck Your Blood

So I came up with this hacky but simple solution to allow people to opt out. The directions below are for Outlook but should work with any rich text email.
  1. Create a mailing list with everyone who may be interested.
  2. At the top of every email, put the following:

    To opt out of future blood-related emails, simply unsubscribe.
  3. If you’re using outlook, set up the hyperlink as follows:
    image001Otherwise, just add a hyperlink (Ctrl+K works in Gmail and Yahoo Mail) and paste the following text in, obviously replacing the email with your own email:

    mailto:your.email@company.com?subject=Unsubscribe

  4. Any time you receive an email with “unsubscribe” as the subject, remove that address from your mailing list.
It was a relatively small company so I only ever got a handful of unsubscribe requests, but if I were to get much more, this unsubscription process could be automated quite easily by adding filters based on the subject.
Are there better ways to accomplish this? Have you managed to automate something like it? Let me know!

Connecting Strava and Google Sheets using IFTTT

Update (2016-06-19)

As of May 2016, IFTTT officially started supporting Strava, so for most people, instead of following the instructions in this article, I strongly recommend using this IFTTT recipe.

However, if you want to get at more-detailed Strava data such as moving time and elevation, I’m told that this is the only way. Happy riding!



Last year (2014) I made a goal of cycling or running to work at least 100 times. I ended up doing 172!

This year, I moved from Canada to London, but not before doing over 252 commutes! (Note: I stretched the definition of commute to include any time I saved at least 5km of driving or transit)

I won’t reach 300 this year (my original goal) but instead of going sans goal until 2016 (I am very motivated by goals, even if they are completely arbitrary), I decided to set a new goal.

Cycle 1000km on my “new” bike before the end of the year.

So I made a quick chart in Excel.

newgoal excel

But then I realized that this would be a great opportunity for some tinkering. You see, the Excel chart above requires the dreaded MANUAL DATA ENTRY. If there was a way for me to use my beloved Strava to automatically update a similar chart, I’d be set.

So I tried to plug into the Strava API. But then got a little overwhelmed when it started asking me about authorization codes and application names.

I then went to my favorite “automate everything” site, IFTTT. While they don’t support Strava directly, they do support RSS feeds, and FeedMyRide allows you to create an RSS feed from your Strava activities. IFTTT allowed me to easily create a recipe for hooking up RSS to Google Sheets.

Add in some parsing, some array formulas, and a nice chart et voila: As you can see here, I now have automatic updating of my progress toward my goal!

1000km complete!

I’m a little behind on my goal as I write this(November 24), so stay tuned to see if I manage to catch up!

Update (01/03/2016)

I did it! I ran into a few hiccups along the way, such as four (mostly) unrelated flat tires in two days, an unexpected house move closer to work, and a cold/flu that lingered for a couple weeks, but my mileage at midnight on December 31st, 2015 was 1011.9km*.

mission accomplished!

Mission Accomplished! Me celebrating hitting the 1000km mark on a sunny New Years Eve day at Tower Bridge

*One little disclaimer: I had to cheat a little by including the approximately 50km I cycled in Copenhagen while on vacation there, so technically my new bike’s 2016 mileage was just under 1000km.

Anyway, I found it a very powerful motivator to have my goal out in public for all to see (even though, if we’re being honest, I’d be surprised if more than two people saw said goal). Also, I really enjoyed trying to keep the blue line above the red line in real time. And I absolutely loved exploring the sights of London. I may not keep up the pace of 500 km per month in 2016 but I can see myself continuing to cycle to work most days.

5 Tips for Hackathon Newbies

This past weekend, I entered my first hackathon, called FinTechathon: Hack the Financial Industry.

Hackathon Logo

After a whirlwind of activity, with many ups and downs, my team, “Credit Passport”, ended up winning the award for Best Use of Salesforce Technology! I wish I could take credit but as I explain below, the win was all thanks to my teammates, who put together a great pitch and hacked together an impressive solution in less than 48 hours. Those teammates were:

  • Tommy Nakamura
  • Daniel Forbes
  • Matt Williams
  • Johnny Chan
  • Richard Kirsch
  • Joao Veiga
  • Kavita Kalaichelvan
  • Ann Chen

Without further ado, here’s what I learned.

1) There will be people there who are much stronger coders than you

You will be completely outclassed. It will not be a comfortable feeling. I hate sitting back and letting others make the decision and do the work but I just didn’t have enough confidence (in either the technical aspects or the domain of financials) to take the reins. I was clearly the weakest link on my team and felt really frustrated for the first 24 hours or so. Honestly, I considered just giving up and going home because I was very intimidated and didn’t feel like I was contributing much at all.

Then, midway through Saturday in the Friday to Sunday event, I remembered something I read in The Start-Up of You.

“The most important choice of all is who you choose to surround yourself with.”

-Ben Casanocha, Co-Author, The Start-Up of You (Source: Business Insider)

The best way to grow is to put yourself in those weakest link situations. I didn’t pack up and move from Calgary to London because it would be easy. I did it because it was a push outside of my comfort zone. Same as this hackathon. After I came to terms with this I was able to enjoy myself and learn a ton. I messed around with HTML5 canvases, I configured Heroku to host a prototype, I sketched ideas, and I observed my talented teammates.

2) Everyone is so helpful

I’m sure it varies, but I was blown away by how friendly and helpful everyone was. I expected it to be competitive and cutthroat but it wasn’t. Don’t get me wrong, people were in it to win it, but there was a general feeling of camaraderie over the weekend. I have to especially thank my teammate Matt Williams who, after staying up all night coding, patiently walked me through his work to explain what it did. I even spotted Matt taking the time to sit down with other teams to help them troubleshoot. Everyone wants everyone to succeed.

3) Not just for programmers any more!

Or maybe hackathons were never just for programmers. I just assumed that they were always just a bunch of coders hacking away, but they’re so much more. Observers were welcome at this hackathon (great for checking things out before diving in). Also, we couldn’t have done it without our fantastic team of BizDevs, who put together a concise, impressive pitch deck. Designers were welcome as well, but unfortunately I didn’t come across very many of them.

4) Roll with it

Plans change. Sure, it’s frustrating when code you’ve been working on has to be thrown out due to an incorrect assumption but that’s just part of the experience. It’s important not to get too attached to your ideas or the work you’ve done.

“Don’t get too attached to your original plan, because it’s probably wrong.”

-Paul Graham (Source: The 18 Mistakes that Kill Startups)

5) They treat you well

Not only is there delicious food non-stop, but where else are you going to have in-person api support at your fingertips? There’s no way that I would have wrapped my head around ColoredCoins if we didn’t have Tal from Colu right there to talk us through it. Oh, and did I mention delicious food and snacks?

IMG_1418

What’s Next?

I started the weekend feeling frustrated but ended it amazingly motivated and inspired. I’m already planning my next hackathon and my solo projects that I want to do on my own as soon as possible!

Comments

Did you learn any other lessons from hackathons? Let me know in the comments below!

Using boost::bind and boost::signals2 to Automatically Propagate Signals

Background

At my previous job, we implement a modified version of the MVVM pattern using C++. Our model followed a hierarchical structure where each item owns zero or more other items. We wanted the View Model to hear about everything that happened in the Model, but we didn’t want the Model to know about the View Model. As far as I know, C++11 doesn’t have any signaling, messaging, or event handling built in, so we decided to use boost::signals2 to allow communication where the sender of a message doesn’t need to know anything about the listener(s).

When hooking up boost::signals2, we used boost::bind to connect member functions as listeners (i.e. slots, subscribers, etc.).

A quick search turned a great StackOverflow question on a similar topic, but that didn’t cover automatic propagation.

boost::bind Syntax

The syntax for boost::bind confuses me regularly. The arguments to boost::bind are as follows:

  1. A function pointer
  2. A reference to the instance that the function should be called on (but only if that function is a member function).
  3. The first parameter that goes into the function (if applicable).
  4. The second parameter that goes into the function (if applicable).
  5. Etc.

Arguments 2 and higher in the list above can be any of the following:

  • variables (ex. bind(f, x), where f is a static function that takes one int and x is an int)
  • constants (ex. bind(f, 7))
  • fancy “placeholder” arguments (ex, bind(f, _1))
    • These are what usually trip me up a little. What the above example means is pass through the first argument.

How to Do It

As a simplified example, let’s pretend that ViewModel needs to know when something in Model has changed, but we don’t want Model to be dependent on ViewModel, as shown in the UML below.

This allows us, for example, to add more view models without having to change anything about the Model.

The following code shows the use of a tree-style “ModelItem” class, where each ModelItem owns zero or more other ModelItems. Signals are automatically connected and sent.

#include "stdafx.h"
#include <iostream>
#include <cstdio> // for getchar

#include "ModelItem.h"
#include "ViewModel.h"

int main()
{
    // Heirarchy of Ownership:
    //
    //    viewModel
    //       |
    //    mainItem
    //    |   |       \
    // itemA itemB itemC
    //         |
    //       itemB1
    //         |
    //       itemB1A

    ViewModel* viewModel = new ViewModel();

    auto mainItem = viewModel->GetMainItem();
    auto itemA = mainItem->AddItem("itemA");
    auto itemB = mainItem->AddItem("itemB");
    auto itemB1 = itemB->AddItem("itemB1");
    auto itemB1A = itemB1->AddItem("itemB1A");
    auto itemC = mainItem->AddItem("itemC");

    delete viewModel;

    getchar();

    return 0;
}

The following output is generated.

Item Created: 'MainItem'
Item Created: 'itemA'
Item Created: 'itemB'
Item Created: 'itemB1'
Item Created: 'itemB1A'
Item Created: 'itemC'
Item Deleted: 'itemA'
Item Deleted: 'itemB1A'
Item Deleted: 'itemB1'
Item Deleted: 'itemB'
Item Deleted: 'itemC'
Item Deleted: 'MainItem'

Here are the details of the classes used.

#pragma once
#include <string>
#include <vector>
#include <boost/signals2.hpp>

class ModelItem; // Forward declaration for the typedefs below.
typedef boost::signals2::signal<void(ModelItem*)> ModelItemSignal;
typedef ModelItemSignal::slot_type ModelItemSlot;

class ModelItem
{
public:
    ModelItem(std::string name);
    ~ModelItem();

    ModelItem* AddItem(std::string name);
    std::string GetName() const;

    void SubscribeItemDeleted(const ModelItemSlot& slot);
    void SubscribeItemCreated(const ModelItemSlot& slot);

private:
    std::string _name;
    std::vector<ModelItem*> _items;

    void ConnectSignals(ModelItem* listener);

    // These wrappers were written because it doesn't seem that boost::bind works with the signal's overloaded () operator.
    void OnItemCreated(ModelItem* item) { _itemCreated(item); }
    void OnItemDeleted(ModelItem* item) { _itemDeleted(item); }

    ModelItemSignal _itemCreated;
    ModelItemSignal _itemDeleted;
};
#include "stdafx.h"
#include "ModelItem.h"
#include <boost/signals2.hpp>
#include <boost/bind.hpp>

using namespace std;
using namespace boost;
using namespace boost::signals2;

ModelItem::ModelItem(string name)
{
    _name = name;
}

ModelItem::~ModelItem()
{
    for (auto item : _items)
    {
        delete item;
    }

    _itemDeleted(this);
}

ModelItem* ModelItem::AddItem(string name)
{
    ModelItem* item = new ModelItem(name);
    _itemCreated(item);
    item->ConnectSignals(this);
    _items.push_back(item);

    return item;
}

string ModelItem::GetName() const
{
    return _name;
}

void ModelItem::SubscribeItemCreated(const ModelItemSlot& slot)
{
    _itemCreated.connect(slot);
}

void ModelItem::SubscribeItemDeleted(const ModelItemSlot& slot)
{
    _itemDeleted.connect(slot);
}

void ModelItem::ConnectSignals(ModelItem* listener)
{
    // Hooks up this ModelItem's signals to listener.
    SubscribeItemCreated(bind(&ModelItem::OnItemCreated, listener, _1));
    SubscribeItemDeleted(bind(&ModelItem::OnItemDeleted, listener, _1));
}
#pragma once
class ModelItem;

class ViewModel
{
public:
    ViewModel::ViewModel();
    ViewModel::~ViewModel();

    ModelItem* GetMainItem() const { return _mainItem; }

    void OnItemCreated(ModelItem* item);
    void OnItemDeleted(ModelItem* item);

private:
    ModelItem* _mainItem;
};
#include "stdafx.h"
#include "ViewModel.h"
#include "ModelItem.h"

#include <iostream>

#include <boost/signals2.hpp>

using namespace std;

ViewModel::ViewModel()
{
    _mainItem = new ModelItem("MainItem");
    OnItemCreated(_mainItem);

    _mainItem->SubscribeItemCreated(bind(&ViewModel::OnItemCreated, this, _1));
    _mainItem->SubscribeItemDeleted(bind(&ViewModel::OnItemDeleted, this, _1));
}

ViewModel::~ViewModel()
{
    delete _mainItem;
}

void ViewModel::OnItemCreated(ModelItem * item)
{
    cout << "Item Created: '" << item->GetName() << "'\n";
}

void ViewModel::OnItemDeleted(ModelItem * item)
{
    cout << "Item Deleted: '" << item->GetName() << "'\n";
}

Notes

  • The above code was compiled with Visual Studio Community 2015.
  • If anyone wants to play with the above code, let me know and I’ll put it up on GitHub.
  • I used raw pointers in this code but if this were production code I would use shared pointers instead.
  • If there are significant differences between different derived classes of ModelItem, this type of design can lead to over-generalized signal arguments. For example, if Bird is a ModelItem and so is Tree and we were to add an ItemChanged signal, that signal could get pretty bloated. (Thanks to Joe Bren for pointing this out.)
  • This may cause slowdowns if the calling of signals takes a non-negligible amount of time and/or the tree is extremely deep. (Again, thanks to Joe Bren for this.)

Customizing Google Forms and Sheets

The Client

Alberta Science Network (ASN) is a charity that is very close to my heart. They have several educational initiatives, including “Scientists And Engineers in the Classroom”. Through this program, from 2011 to 2015, I made over 40 presentations at local schools, teaching over 1500 students about Chemistry using magic and hands-on activities.
Here’s a quick video I whipped up when ASN wanted their volunteers to talk about what they do for the organization.
ASN approached me asking if I knew of any technologies or people who could help smooth out some of their processes. I initially declined because it was a busy time at work, but later approached the organization to see if I could help out.

Before

Here’s a screenshot of the member form that was in use originally.
Original Form Screenshot
More importantly, here’s the original process:
  1. Member fills out form
  2. ASN staff gets an email with form data
  3. ASN staff copies form data into a local spreadsheet
  4. At end of year, all members are sent an email, asking them to fill out the form again

This is quite a labor-intensive process for staff, the data is not backed up, and it does not make it easy for members to renew.

After

As you can see below, the form was remade using Google Forms. The look and feel wasn’t significantly changed but a few unnecessary fields were removed and the fields were aligned and spaced further apart from each other.
New Form Partial Screenshot

The Process

Going into the project with ASN, I was hoping to be able to use Ruby on Rails (RoR) to make a custom solution because I had recently done the fantastic Rails Tutorial by Michael Hartl. After considering RoR and looking into Customer Relationship Management tools such as the open-source platform CiviCRM, I realized that Google Forms and Sheets could do the trick in considerably less time.
Google automatically inserts form data into a spreadsheet, but there were a few things that weren’t automatically supported. For example, ASN has annual memberships, so every June, a script goes through and sets all members to inactive then invites them to renew their membership. In order to improve the renewal rate, I used the following script to pre-fill the membership form for these renewal emails.
/**
* Use Form API to generate pre-filled form URLs
* Based on http://stackoverflow.com/a/26395487/1704355
*
* Note: This script can either be run manually or linked to a trigger such as a time-based trigger.
*/

function GeneratePrefilledURLs()
{
var range = GetSpreadsheetDataRange();
var data = range.getValues();
var headerRow = data[0];

// Use form attached to sheet
var formUrl = SpreadsheetApp.getActive().getFormUrl();
var form = FormApp.openByUrl(formUrl);
var formItems = form.getItems();

// Grab column numbers for later use.
var urlCol = headerRow.indexOf("Prefilled URL");
var activeMemberColumn = headerRow.indexOf("Approved Active Member");

// Skip headers, then build URLs for each row in this sheet.
for (var row = 1; row < data.length; row++)
{
var activeMember = data[row][activeMemberColumn];

// Only update for active members.
if (activeMember == "y" || activeMember == "")
{
// Create a form response object to prefill.
var formResponse = form.createResponse();

// Cycle through all form items that have matching columns.
for (var i = 0; i < formItems.length; i++) { // Get text of question for item var ques = formItems[i].getTitle(); // Get col index that contains this question var quesCol = headerRow.indexOf(ques); var resp = ques ? data[row][quesCol] : ""; var type = formItems[i].getType().toString(); //Logger.log("Question='"+ques+"', resp='"+resp+"' type:"+type); // Treat Membership Type separately because we always prepopulate the same response (Renewal) regardless of what the data says. if (ques == "Membership Type") { var item = formItems[i].asMultipleChoiceItem(); resp = "Renewal"; } else { // Need to treat every type of answer as its specific type. switch (formItems[i].getType()) { case FormApp.ItemType.TEXT: var item = formItems[i].asTextItem(); break; case FormApp.ItemType.PARAGRAPH_TEXT: item = formItems[i].asParagraphTextItem(); break; case FormApp.ItemType.LIST: item = formItems[i].asListItem(); break; case FormApp.ItemType.MULTIPLE_CHOICE: item = formItems[i].asMultipleChoiceItem(); break; case FormApp.ItemType.CHECKBOX: item = formItems[i].asCheckboxItem(); // Resp is a CSV string. Convert to array of separate choices, ready for createResponse(). resp = resp.split(/ *, */);   // Convert CSV to array break; case FormApp.ItemType.DATE: item = formItems[i].asDateItem(); resp = new Date( resp ); break; case FormApp.ItemType.DATETIME: item = formItems[i].asDateTimeItem(); resp = new Date( resp ); break; default: item = null;  // Not handling DURATION, GRID, IMAGE, PAGE_BREAK, SCALE, SECTION_HEADER, TIME break; } } // Add this answer to our pre-filled URL if (item && resp != "") { try { // Store the item response in formResponse. If it doesn't work, just log the exception and move on. var response = item.createResponse(resp); formResponse.withItemResponse(response); } catch (e) { Logger.log(e.toString()); } } // else if we have any other type of response, we'll skip it else { Logger.log("Skipping i = " + i + ", question = " + ques + " type: " + type); } } // Generate the pre-filled URL for this row. var editResponseUrl = formResponse.toPrefilledUrl(); var shortUrl = UrlShortener.Url.insert({longUrl: editResponseUrl}).id; // If there is a "Prefilled URL" column, update it. if (urlCol >= 0)
{
var urlCell = range.getCell(row+1,urlCol+1);
urlCell.setValue(shortUrl);
}
}
}
};

// Helper function that returns the size of an object.
// From http://stackoverflow.com/a/6700/1704355
Object.size = function(obj)
{
var size = 0, key;
for (key in obj)
{
if (obj.hasOwnProperty(key)) size++;
}

return size;
};
I had never worked with Google Apps Script (GAS) or JavaScript (which GAS is based on) before, so it took a lot of reading, searching, and trial and error, but I made sure to document my code to make it easy for myself or others to be able to modify the code in the future. I’m sure that there are some “rookie mistakes” in there but so far, no problems have been reported and on-time member renewals almost tripled from 2014 to 2015. ASN was quite pleased with my work!