In this post I'm going to try and briefly describe how I built the popular content section working on my Octopress blog using data from Gauges.

I'm using Gauges as the traffic and analytics service for my blog. I have done for over a year now. It's simple and very easy to use which is why I like it so much. One of the great things about it though is that it also provides an API for people who want to do more with the data that they have accumulated. One feature I wanted to include on my blog has been a link to what most people have been reading in the last few days. I thought that this would be extremely easy to add given that Gauges already has all the data that I need for it.

The Leaderboard

What we essentially want to build is a leaderboard comprising of the most popular articles sorted by the number of page views they have with the most popular article at the top. With this analogy in mind I just needed to populate the leaderboard with page titles, URLs and views for popular articles from my Gauges data.

In adding this feature to my Octopress blog, I started thinking about how I would interact with this feature. That was easy. A Rake task. Adding this as a Rake task means that I can add this to another Rake task that could pull in the popular content during the generate stage. For now though I run it as a separate Rake task on its own as I might generate the site a couple of times before publishing it.

Here's how I envisioned this popular content section to run:

PopularContent.top_content(3)

So, all we're doing here is passing in the number of days that we want to look back on but there's a number of things that need to happen here:

  1. We need to connect to the Gauges API.
  2. We need to ask for the content stats we need.
  3. We need to calculate the most popular article over the time period that we pass in.
  4. We need to return the most popular article as an object with a title and a URL.

Get Connected

When we initialise this object we need to do two things:

  1. Connect to the Gauges API
  2. Create our leaderboard

This is done quite easily. When we initialise the TopContent class we pass in our Gauges ID and API key so that we can connect to our own data. In order to make this process even easier, we're going to use the Gauges gem rather than working directly with the Gauges API.

Once this is done we create a blank array that will serve as our leaderboard for compiling the top viewed articles over the last few days.

def initialize(gauges_id, api_key, days = 3)
  @gauges_id = gauges_id
  @api_key = api_key
  @client = Gauges.new(token: api_key)
  @leaderboard = []
end

Building the Leaderboard

Gauges doesn't group popular content data. Instead it serves the data you need on a date basis. So we need to ask for each date that falls in the date range we specified:

def build_popular_posts(days)
  end_date = Date.today
  start_date = end_date - days

while start_date <= end_date do content = @client.content(@gauges_id, date: start_date) content["content"].each { |c| update_leaderboard(c["title"], c["views"], c["url"]) if valid_content_page?(c) } start_date = 1 end

@leaderboard = @leaderboard.sort! { |x,y| y[:views] <=> x[:views] } end

There's a lot going on here, so let's take it one step at a time. First of all we initialize some variables for our start and end date using the number of days variable that pass in.

end_date = Date.today
start_date = end_date - days

Next, we iterate over these dates beginning with the start date.

while start_date <= end_date do
  # ...
end

For each date we're going to pull the data we need from Gauges. This is a list of top content for the date we are on.

content = @client.content(@gauges_id, date: start_date)

Next we update our leaderboard by iterating over the content that has been viewed for the current date.

content["content"].each { |c| update_leaderboard(c["title"], c["views"], c["url"]) if valid_content_page?(c) }

This is the point where we start accumulating page views for each page. The update_leaderboard method takes the page title, URL and number of views and uses them to create or update an existing entry on the leaderboard.

def update_leaderboard(title, views, url)
  position = @leaderboard.index { |a| a[:title] == title }
  if position
    @leaderboard[position][:views]  = views
  else
    @leaderboard << { title: title, views: views, url: url }
  end
end

Finally we sort the leaderboard according with the highest number of page views at the top.

@leaderboard = @leaderboard.sort! { |x,y| y[:views] <=> x[:views] }

Display the Results

Once this is done, it's a simple matter of writing the popular content to a config file using the Rake task so that we can use it in the generating of our Octopress blog. Accessing these from an html file is straight forward enough if you're familiar with working with Octopress layouts. I have the following code located in my post layout just after the post section:

<div class="related">
  <h2>Popular Posts</h2>
  <ul class="related-posts">
    <li>
      <h3><a href="{% raw %}{{ site.popular_post_first_url }}{% endraw %}">{% raw %}{{ site.popular_post_first_title }}{% endraw %}</a></h3>
    </li>
    <li>
      <h3><a href="{% raw %}{{ site.popular_post_second_url }}{% endraw %}">{% raw %}{{ site.popular_post_second_title }}{% endraw %}</a></h3>
    </li>
    <li>
      <h3><a href="{% raw %}{{ site.popular_post_third_url }}{% endraw %}">{% raw %}{{ site.popular_post_third_title }}{% endraw %}</a></h3>
    </li>
  </ul>
</div>

That's it really.

If you look at the code, you'll see a few other methods that are used to filter out data. I intentionally filter out the home page views and other pages that I don't want to include in the list. This is done just by checking page titles or paths.

I've been running this code on my blog for quite a while now. Previously it only returned a single page URL and title, but I wanted to include a list of posts so I added methods to pull the second and third most popular posts.

The code itself is quite straight forward to read and follow. It might not use all the latest Ruby idioms and tricks that many Ruby developers know and love but for me it's straight forward code that works.

The fully listed code to accompany this post can be found on Github.