Home > Programming > Nike: all your runs are belong to us

Nike: all your runs are belong to us

August 3rd, 2010

I like to run, and I like tech, so obviously I like to measure and graph my runs. So I use Nike+. Unfortunately, Nike+ has a rubbish pure-flash website (rant: do people still make pure-flash sites? This is 2010!).

I want a way to get to my run data without having to use Nike’s website, so I’ve decided to code something up. I’m learning Rails at the moment, so I’ve decided to do this as a Rails app. In this post, and probably some subsequent posts, I’ll be showing my progress.

Step 1: getting the data

First off, we need some way of getting the data from Nike. This post on labs.interfacedigital.co.uk shows how to get the raw run data through Nike’s public API. The two URLs we’ll be using are this one for getting a summary of all runs:

http://nikerunning.nike.com/nikeplus/v1/services/app/run_list.jsp?userID=#{user_id}

and this one for getting the details of a single run:

http://nikerunning.nike.com/nikeplus/v1/services/app/get_run.jsp?id=#{run_id}&userID=#{user_id}

The all runs XML looks like this:

<plusService>
  <status>success</status>
  <runList endIndex="-1" startIndex="0">
    <run id="1752070113" workoutType="standard">
      <startTime>2010-02-28T13:24:40+00:00</startTime>
      <distance>6.264</distance>
      <duration>4067000</duration>
      <syncTime>2010-02-28T14:34:16+00:00</syncTime>
      <calories>470.0</calories>
      <name/>
      <description/>
      <howFelt>3</howFelt>
      <weather>2</weather>
      <terrain>1</terrain>
      <intensity/>
      <gpxId/>
      <equipmentType>sportband</equipmentType>
    </run>
    ...
  </runList>
</plusService>

Step 2: read the data

I’m doing the following in a Rails model called Run. The underlying table has fields for the necessary run attributes. I’m not going to go into this in detail because this isn’t a Rails tutorial.

require 'open-uri'
require 'rexml/document'

...

attributes = {
 #:attr         #xml tagname
  :distance   => "distance",
  :start_time => "startTime",
  :duration   => "duration",
  :calories   => "calories"
}

open(all_runs_url) do |f|  #open the XML file using the open-uri library
  doc = REXML::Document.new f.read  #create a new REXML object
  doc.elements.each("plusService/runList/run") do |run_info|  #iterate over 'run' elements
    run_id = run_info.attributes["id"]  #get the Nike id of the run
    run = find_or_initialize_by_run_id(run_id)  #get a new Run object, either from an existing record
                                                #in the database or create a new one

    attributes.each do |key, value|  #iterate through attributes hash, which maps object attrs to XML tags
      run.write_attribute(key, run_info.elements[value].text)
    end

    run.calculate_avg_pace  #work out the average pace
    run.save
  end
end

calculate_avg_pace looks like this:

def calculate_avg_pace
  self.avg_pace = duration_in_minutes / distance_in_miles
end

and uses the following virtual attributes:

def duration_in_minutes
  duration / (60.0 * 1000)
end

def distance_in_miles
  distance * 0.62
end

At some point I’ll refactor so miles/km is an option, but for now I just want to get something working. So far though, so good:

First run listing

First run listing

Next steps will be to make the numbers look a bit nicer, for example by showing average pace in mm:ss. Other than that, who knows?

Links

Categories: Programming Tags: , ,
  1. July 28th, 2013 at 13:03 | #1

    What’s up colleagues, its fantastic piece of writing on the topic of teachingand fully defined, keep it up all the time.

  2. July 5th, 2015 at 14:09 | #2

    top high-quality report you are glaernely truly getting some type of good web business operator. This word broad internet website performing velocity is usually incredible. It similar to feels you choose to are accomplishing any type of unique approach. In add-on, This materi

Comments are closed.