Created
May 7, 2012 07:33
-
-
Save yuanying/2626497 to your computer and use it in GitHub Desktop.
Syncronize from Garmin Connect activity to Runkeeper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby -wKU | |
# Syncronize from Garmin Connect to Runkeeper | |
require 'rubygems' | |
require 'runkeeper' | |
require 'rss/1.0' | |
require 'rss/2.0' | |
require 'rss/dublincore' | |
require 'open-uri' | |
require 'uri' | |
require 'cgi' | |
require 'optparse' | |
require "rexml/document" | |
require 'time' | |
runkeeper_client_id = nil | |
runkeeper_client_secret = nil | |
runkeeper_access_token = nil | |
garmin_username = nil | |
proxy_host = nil | |
proxy_port = nil | |
opts = OptionParser.new | |
opts.on('-c', '--runkeeper-client-id CLIENT_ID') { |v| runkeeper_client_id = v } | |
opts.on('-s', '--runkeeper-client-secret CLIENT_SECRET') { |v| runkeeper_client_secret = v } | |
opts.on('-a', '--runkeeper-access-token ACCESS_TOKEN') { |v| runkeeper_access_token = v } | |
opts.on('-g', '--garmin-username GARMIN_USERNAME') { |v| garmin_username = v } | |
opts.on('--proxy-host PROXY_HOST') { |v| proxy_host = v } | |
opts.on('--proxy-port PROXY_PORT', Integer) { |v| proxy_port = v } | |
opts.parse!(ARGV) | |
tmp = '/tmp/last-garmin-connect-to-runkeeper-url.txt' | |
url = nil | |
if File.exist?(tmp) | |
open(tmp) do |io| | |
url = io.read.chomp | |
end | |
end | |
rss = nil | |
rss_url = "http://connect.garmin.com/feed/rss/activities?feedname=Garmin#{Time.now.strftime('%Y%m%d%H')}&explore=true&activityType=running&eventType=all&activitySummarySumDistance-unit=kilometer&activitySummarySumDuration-unit=hour&activitySummaryGainElevation-unit=meter&owner=#{garmin_username}&sortField=beginTimestamp" | |
open(rss_url) do |io| | |
rss = RSS::Parser.parse(io.read) | |
end | |
Runkeeper.configure do |config| | |
config.client_id = runkeeper_client_id | |
config.client_secret = runkeeper_client_secret | |
end | |
if proxy_host && proxy_port | |
require 'runkeeper/connection' | |
class Runkeeper | |
class Connection | |
http_proxy proxy_host, proxy_port | |
end | |
end | |
end | |
class Runkeeper | |
class Activity | |
attr_accessor :heart_rate | |
alias old_optional_attrs optional_attrs | |
def optional_attrs | |
h = {} | |
[ | |
:total_distance, | |
:average_heart_rate, | |
:total_calories, | |
:heart_rate, | |
:notes, | |
:path, | |
:post_to_facebook, | |
:post_to_twitter | |
].each do |method_name| | |
v = send(method_name) | |
h[method_name] = v if v | |
end | |
h | |
end | |
end | |
end | |
class Garmin2Runkeeper | |
attr_accessor :runkeeper | |
def initialize runkeeper | |
@runkeeper = runkeeper | |
end | |
def post activity_id | |
type = 'Running' | |
start_time = nil | |
total_distance = 0 | |
duration = nil | |
# average_heart_rate = 0 | |
heart_rate = [] | |
total_calories = 0 | |
notes = nil | |
path = [] | |
open("http://connect.garmin.com/proxy/activity-service-1.1/gpx/activity/#{activity_id}?full=true") do |gpx| | |
doc = REXML::Document.new gpx | |
title = doc.elements['/gpx/trk/name'] ? doc.elements['/gpx/trk/name'].text : 'Untitle' | |
desc = doc.elements['/gpx/trk/desc'] ? doc.elements['/gpx/trk/desc'].text : '' | |
notes = "#{title}, #{desc}" | |
end | |
open("http://connect.garmin.com/proxy/activity-service-1.1/tcx/activity/#{activity_id}?full=true") do |tcx| | |
doc = REXML::Document.new tcx | |
doc.elements.each('/TrainingCenterDatabase/Activities/Activity/Lap') do |lap| | |
if distance = lap.elements['DistanceMeters'] | |
distance = distance.text.to_i | |
total_distance += distance | |
end | |
if calories = lap.elements['Calories'] | |
calories = calories.text.to_i | |
total_calories += calories | |
end | |
trigger_method = trigger_method.text.downcase if trigger_method = lap.elements['TriggerMethod'] | |
lap.elements.each('Track/Trackpoint') do |tp| | |
if time = tp.elements['Time'] | |
time = Time.parse(time.text) | |
start_time = time unless start_time | |
duration = time.to_i - start_time.to_i | |
latitude = tp.elements['Position/LatitudeDegrees'] | |
longitude = tp.elements['Position/LongitudeDegrees'] | |
altitude = tp.elements['AltitudeMeters'] | |
trigger_method = trigger_method || 'manual' | |
if latitude && longitude && altitude | |
wgs84 = {} | |
wgs84[:timestamp] = duration | |
wgs84[:latitude] = latitude.text | |
wgs84[:longitude] = longitude.text | |
wgs84[:altitude] = altitude.text | |
wgs84[:type] = trigger_method | |
path << wgs84 | |
end | |
if hr_value = tp.elements['HeartRateBpm/Value'] | |
hr_value = hr_value.text.to_i | |
hr = {} | |
hr[:timestamp] = duration | |
hr[:heart_rate] = hr_value | |
heart_rate << hr | |
end | |
end | |
end | |
end | |
end | |
activity = runkeeper.new_activity( | |
type: type, | |
start_time: start_time.localtime, | |
duration: duration, | |
heart_rate: heart_rate, | |
path: path, | |
total_distance: total_distance, | |
total_calories: total_calories, | |
notes: notes, | |
post_to_facebook: true, | |
post_to_twitter: true | |
) | |
activity.save | |
end | |
end | |
user = Runkeeper.new(runkeeper_access_token) | |
synchronizer = Garmin2Runkeeper.new(user) | |
urls = [] | |
rss.items.each do |item| | |
if item.link == url | |
break | |
end | |
urls << item.link | |
end | |
urls.reverse.each do |url| | |
activity_id = url.split('/').last | |
synchronizer.post(activity_id) | |
open(tmp, 'w') do |io| | |
io.write url | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment