<?php | |
chdir(dirname(__FILE__)); | |
chdir('..'); | |
require('vendor/autoload.php'); | |
require_once('includes/inc.php'); | |
require_once('includes/database.php'); | |
$places = array( | |
array( | |
'name' => 'Home', | |
'latitude' => Config::$homeLat, | |
'longitude' => Config::$homeLng, | |
'radius' => 200 | |
), | |
array( | |
'name' => 'Esri Portland', | |
'latitude' => 45.52154, | |
'longitude' => -122.6775348, | |
'radius' => 200 | |
) | |
); | |
if(array_key_exists(1, $argv) && $argv[1] == 'auth') { | |
if(array_key_exists(2, $argv)) { | |
$ch = curl_init('https://runkeeper.com/apps/token'); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( | |
'grant_type' => 'authorization_code', | |
'code' => $argv[2], | |
'client_id' => Config::$runkeeperClientID, | |
'client_secret' => Config::$runkeeperClientSecret, | |
'redirect_uri' => 'http://localhost/' | |
))); | |
$response = curl_exec($ch); | |
$data = json_decode($response); | |
print_r($data); | |
} else { | |
echo "https://runkeeper.com/apps/authorize?response_type=code&redirect_uri=http://localhost/&client_id=" . Config::$runkeeperClientID . "\n"; | |
} | |
die(); | |
} | |
function rk_request($path) { | |
$ch = curl_init('https://api.runkeeper.com' . $path); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
curl_setopt($ch, CURLOPT_HTTPHEADER, array( | |
'Authorization: Bearer ' . Config::$runkeeperToken | |
)); | |
$response = curl_exec($ch); | |
if($response) { | |
return json_decode($response); | |
} else { | |
return null; | |
} | |
} | |
$lastImportFilename = Config::dataDir().'last-runkeeper.txt'; | |
$lastDate = file_get_contents($lastImportFilename); | |
$should_continue = true; | |
$request_url = '/fitnessActivities?page=0&pageSize=50&noEarlierThan='.$lastDate.'&modifiedNoEarlierThan='.$lastDate; | |
// Find all existing runkeeper IDs to see if we've already imported a run | |
$existingItemIDs = array(); | |
foreach(explode("\n",shell_exec('grep runkeeper content/metrics/*/*/*/*.md')) as $line) { | |
if(preg_match('/activity\/(\d+)/', $line, $match)) { | |
$existingItemIDs[] = $match[1]; | |
} | |
} | |
while($should_continue) { | |
echo "Fetching $request_url\n"; | |
if($activities = rk_request($request_url)) { | |
if($activities->items) { | |
foreach($activities->items as $item) { | |
echo $item->uri . "\n"; | |
if(preg_match('/fitnessActivities\/(\d+)/', $item->uri, $match)) { | |
// Check if it's already been imported | |
if(!in_array($match[1], $existingItemIDs)) { | |
$post = process_rk_item($item); | |
if($post) { | |
file_put_contents($lastImportFilename, $post->date->format('Y-m-d')); | |
} | |
} else { | |
echo "Skipping because it's already been imported\n\n"; | |
} | |
} else { | |
echo "Encountered an unknown activity\n\n"; | |
} | |
} | |
if(property_exists($activities, 'next') && $activities->next) { | |
$request_url = $activities->next; | |
echo "Continuing...\n\n"; | |
} else { | |
$should_continue = false; | |
echo "Stopping because no 'next' URL was found for: $request_url\n\n"; | |
} | |
} else { | |
$should_continue = false; | |
echo "Stopping because no items found in this request: $request_url\n\n"; | |
} | |
} else { | |
$should_continue = false; | |
echo "Stopping because request failed: $request_url\n\n"; | |
} | |
} | |
echo "Done\n"; | |
function process_rk_item($item) { | |
global $places; | |
if($activity=rk_request($item->uri)) { | |
$path = $activity->path; | |
if(count($path) == 0) { | |
echo "skipping because no path data was found\n\n"; | |
return false; | |
} | |
$first = $path[0]; | |
$last = $path[count($path)-1]; | |
$tz = getTimezoneForLocation($first->latitude, $first->longitude); | |
if($tz) { | |
$timezone = new DateTimeZone($tz->timezone); | |
} else { | |
$timezone = null; | |
} | |
// The date from Runkeeper does not include a timezone, so assume it is the timezone | |
// that corresponds to the location of the start of the run | |
$start_date = new DateTime($activity->start_time, $timezone); | |
$end_date = new DateTime($activity->start_time, $timezone); | |
$end_date->add(new DateInterval('PT' . round($activity->duration) . 'S')); | |
echo $activity->activity."\n"; | |
echo $start_date->format('c') . "\n"; | |
// echo $end_date->format('c') . "\n"; | |
switch($activity->type) { | |
case 'Cycling': | |
$type = 'bikeride'; | |
break; | |
case 'Running': | |
$type = 'run'; | |
break; | |
case 'Walking': | |
$type = 'walk'; | |
break; | |
} | |
$post = p3k\Post::newPage('metrics', $start_date, $type); | |
echo $post->fullURL . "\n"; | |
echo $post->filename . "\n"; | |
if(file_exists(Config::contentDir().$post->filename)) { | |
echo "already imported\n\n"; | |
return false; | |
} | |
if(property_exists($activity, 'notes') && $activity->notes) { | |
$post->set('title', trim($activity->notes)); | |
} | |
$context = p3k\ReverseGeocoder::adr($first->latitude, $first->longitude); | |
if($tz) { | |
$post->set('timezone', $tz->timezone); | |
} | |
if($context) { | |
$post->set('locality', $context->localityName); | |
$post->set('region', $context->regionName); | |
$post->set('country', $context->countryName); | |
} | |
$post->set('latitude', $first->latitude); | |
$post->set('longitude', $first->longitude); | |
foreach($places as $place) { | |
if(gc_distance($place['latitude'], $place['longitude'], $first->latitude, $first->longitude) <= $place['radius']) { | |
$post->set('place-name', $place['name']); | |
} | |
} | |
$post->set('syndication', array($activity->activity)); | |
$routeData = array( | |
'start' => array( | |
'date' => $start_date->format('c'), | |
'latitude' => $first->latitude, | |
'longitude' => $first->longitude, | |
), | |
'end' => array( | |
'date' => $end_date->format('c'), | |
'latitude' => $last->latitude, | |
'longitude' => $last->longitude, | |
), | |
'meters' => (int)round($activity->total_distance), | |
'seconds' => (int)round($activity->duration) | |
); | |
if(property_exists($activity, 'average_heart_rate')) { | |
$routeData['heartrate'] = $activity->average_heart_rate; | |
} | |
$post->set('route', $routeData); | |
$geojson = array(); | |
foreach($activity->path as $point) { | |
$feature = array( | |
'type' => 'Feature', | |
'geometry' => array( | |
'type' => 'Point', | |
'coordinates' => array($point->longitude, $point->latitude) | |
), | |
'properties' => array( | |
'date' => date('Y-m-d\TH:i:s', $start_date->format('U')+round($point->timestamp)).'Z', | |
'altitude' => $point->altitude | |
) | |
); | |
if(property_exists($activity, 'heart_rate')) { | |
foreach($activity->heart_rate as $hr) { | |
if($hr->timestamp == $point->timestamp) { | |
$feature['properties']['heartrate'] = $hr->heart_rate; | |
} | |
} | |
} | |
$geojson[] = $feature; | |
} | |
$post->body = json_encode(array( | |
'path' => $geojson, | |
)); | |
$filesAdded = array(Config::contentDir().$post->filename); | |
@mkdir($post->assetFolder, 0755, true); | |
file_put_contents($post->assetFolder.'/runkeeper.json', json_encode($activity)); | |
$filesAdded[] = $post->assetFolder.'/runkeeper.json'; | |
// If there are any photos, download those too! | |
if(property_exists($activity, 'images')) { | |
$photos = array(); | |
foreach($activity->images as $i=>$image) { | |
$fn = 'photo-'.$i.'.jpg'; | |
file_put_contents($post->assetFolder.'/'.$fn, file_get_contents($image->uri)); | |
$filesAdded[] = $post->assetFolder.'/'.$fn; | |
$photos[] = $fn; | |
} | |
$post->set('photos', $photos); | |
} | |
$post->save(); | |
p3k\irc\alert('[runkeeper:' . $type . '] ' . round($routeData['meters']/1000,1) . 'km ' . round($routeData['seconds']/60) . ' minutes ' . $post->fullURL); | |
// Generate the two map images | |
$mapImages = $post->generateMapImages(false); | |
$filesAdded = array_merge($filesAdded, $mapImages); | |
\DeferredTask::queue('GitClient', 'addCommitPush', array($filesAdded, '[route] imported from runkeeper: '.$post->fullURL)); | |
echo "\n"; | |
return $post; | |
} else { | |
echo "Error: request for $item failed\n"; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment