One of the perks of working at gap intelligence is access to San Diego Padres season tickets. Despite our beloved Padres maintaining their position of dead last in their division, these tickets remain highly coveted. They include a spectacular view of home plate, access to the Omni premier club, a full bar and buffet, and waiter service. Needless to say, fairly distributing 84 games, with 2 seats each, among 49 gappers was no easy task.
Working as a software engineer here at gap intelligence, I saw an opportunity for improvement and innovation. Or maybe I just couldn’t deal with the chaos that ensued as we all raced to the abruptly announced Google Doc to try to claim our games while not stepping on each other’s toes (literally and figuratively).
Once a month, developers at gap intelligence hold a dev day where we can work on anything we want, typically outside of our normal sprint commits. This is how Golden Ticket was born. Myself and another back-end developer spent three dev days (no test coverage, obviously) creating a web application to manage the distribution and selection of baseball tickets.
Technical Overview
Golden Ticket is a Ruby on Rails application built on top of a Postgres database. The front end is HTML5, CSS3 and JavaScript, which our talented front-end developer so graciously beautified on his own dev day, which I’ll give you a glimpse into later on. The meat of the application is in the back end where the number of tickets, picking order and selection times are assigned to each user. But first, here’s a quick overview of our data model.
The data model is set up around the Season object. Each Season has many Games and each Game has many Tickets, as you would expect. The distribution logic is built around the Picks. Each User (in our case, an employee of gap intelligence) begins with having many Picks, which belongs to a particular Season. For example, I have 2 picks for the Padres 2016 season. Each Pick has a certain number of Tickets. In our case, all of our picks have 2 tickets so we can bring a guest to each game, but the app does allow for flexibility if we someday wanted to pick 1 seat at a time. I’ll describe the logic for distributing these picks next.
Determining the Pick Order
The main thing we wanted to accomplish with this application was to add fairness to a previously chaotic process. This is what the SeasonPicksShuffler
is responsible for. It creates a schedule of picks. This schedule is randomly generated, giving each user specific times to make their picks. Here’s a snippet of the shuffler code:
class SeasonPicksShuffler def shuffle position = starting_position tiers.each do |tier| tier_picks = picks.where(tier: tier) shuffled_picks(tier_picks).each do |pick| pick.update_attribute(:position, position) position += 1 end end end def starting_position picks.maximum(:position).to_i + 1 end def picks season.picks.where(date_time: nil) end def tiers picks.order(tier: :asc).distinct.pluck(:tier) end end
One thing I didn’t previously mention was the concept of Tiers. You’ll see above, the shuffle method begins by looping though each tier but preserving their order. This may not be useful to everyone but allows us to have groups (i.e. the senior management) to pick first in their own random order.
You’ll see the actual shuffling happens in shuffled_picks, below:
def shuffled_picks(filtered_picks) 10.times { filtered_picks = filtered_picks.shuffle } filtered_picks end
No, we didn’t implement some crazy algorithm or even include any lottery/raffle gems (which we did initially look into). Instead we simply call Ruby’s Array shuffle
and call it a day. Or really, we call it 10 times, because it calling it once seemed too simple. And that’s it.
Setting the Pick Times
Once we have the picking order, we use another service, the impetuously named SeasonDateTimeSetter
, to assign actual times to each of the picks (see below).
class SeasonPicksDateTimeSetter def set if season.start_at start_time = season.start_at season.picks.order(position: :asc).each do |pick| pick.update_attribute(:date_time, start_time) start_time = next_start_time(start_time) end end end end
You’ll notice a reference to next_start_time
, which could be customized to whatever makes sense for your group. I won’t dive the logic it but, in our case, it keeps all the times on weekdays between 9am and 5pm, spaced out 1 hour each. One thing to note is the picking time is not a 1 hour window in which you HAVE to make your pick, it’s simply the time that you become eligible to pick. And our 1 hour buffer allows you to make your pick knowing that no one else will become eligible for another hour and the people before you have probably already finished their picks.
The User Interface
Below is a glimpse of our interface, which I mentioned earlier. You’ll see most of the selections are crossed out, as this season is in progress and picks have already been made. Notice the ‘Un-Claim’ button next to my name, which would show as ‘Claim’ if the game was still available to pick.
It’s worth noting, that on the right hand column, before the schedule is created, a big, bright ‘GENERATE’ button sits there which we press live (scary) at our company meetings. This provides some excitement as well as some trust (hopefully), that we, the dev team, did not rig it.
The Future
At this point, the future of Golden Ticket is unknown. We’ve discussed everything from open-sourcing it, to adding multi-tenancy and making it available to the public, or just keeping as is. For right now, we’re not making many updates as it works great for our company baseball and recently added hockey tickets. If you’re at all interested in using it, contributing to it or just have questions about it, don’t hesitate to contact Katie Hess at katie@gapintelligence.com. Thanks!