health_hero-human_api 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e96aae7b7516f6ea34360d49f1ba1410248d4307
4
+ data.tar.gz: 6018041870317fde160e9a04fa3cfaa84f334ee3
5
+ SHA512:
6
+ metadata.gz: f095c7f3a356d39c995e78d39d71b35c834cdeac9d90ad30dfa71e0ba5bdda41a21fd3070d713378655c01cf9ab22b21f7e75577c3343822d022dfca3551ac6e
7
+ data.tar.gz: c72328901f30426a1de5343d6ddb4ef3ce7205a7c1eb44017268b05034c3bdc91e32056ea576ca2b14814c432903017caa15c1cbaf478560d3aa969546e2fd85
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .env
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0
4
+ - 2.1
5
+ - 2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in humanapi.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Justin Aiken
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,126 @@
1
+ [![Build Status](https://travis-ci.org/HealthHero/humanapi.svg?branch=master)][travis][![Gem Version](https://badge.fury.io/rb/health_hero-human_api.png)](http://badge.fury.io/rb/health_hero-human_api)
2
+
3
+ # HumanApi
4
+
5
+ This is Health Hero's fork of the HumanApi gem. The original gem (`human_api`) is found at [https://github.com/humanapi/humanapi](https://github.com/humanapi/humanapi).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'health_hero-human_api'
13
+ ```
14
+
15
+ #### Requirements
16
+
17
+ - Ruby 2.0+
18
+ - Activesupport
19
+ - Activerecord if hooking into User model
20
+
21
+ ## Configuration
22
+
23
+ Let's say you have an User model as follows:
24
+
25
+ ```ruby
26
+ class User < ActiveRecord::Base
27
+ ...
28
+
29
+ humanizable :get_the_token
30
+
31
+ def get_the_token
32
+ # the code to reach the token - attribute, association, Redis, etc.
33
+ end
34
+ end
35
+ ```
36
+
37
+ This configuration is really simple. I suggest it over the second one.
38
+
39
+ Always remember to configure the initializer with the access keys:
40
+ ```ruby
41
+ HumanApi.config do |c|
42
+ c.app_id = ENV['HUMANAPI_KEY']
43
+ c.query_key = ENV['HUMANAPI_SECRET']
44
+ end
45
+ ```
46
+
47
+ ### The alternative
48
+
49
+ If you don't like that configuration, you can use a different one, writing right into the initializer:
50
+
51
+ ```ruby
52
+ HumanApi.config do |c|
53
+ c.app_id = "<YOUR_APP_ID>"
54
+ c.query_key = "<YOUR_QUERY_KEY>"
55
+
56
+ # This is the part where the magics happen
57
+ c.human_model = User # Tell me what is the model you want to use
58
+ c.token_method_name = :human_token # Tell me the method you use to retrieve the token (Inside the human_model)
59
+ end
60
+ ```
61
+
62
+ It should work in both ways, the choice is yours.
63
+
64
+ ## Usage
65
+ Once you did the configuration, the usage of the gem is quite easy:
66
+
67
+ ```ruby
68
+ u = User.first
69
+ u.human.profile # => Will return the humanapi user's profile
70
+ u.human.query(:activities) # => Will return everything you asked for (as an array of hashes)
71
+ ```
72
+
73
+ Just use the _human_ instance method from your User instance and that's it
74
+
75
+ ### The query method
76
+ The query method is meant to ask whatever you want whenever you want. Here are some permitted methods (according to humanapi) you can use to retrieve user's data:
77
+
78
+ ```ruby
79
+ :profile
80
+ :activities
81
+ :blood_glucose
82
+ :blood_pressure
83
+ :body_fat
84
+ :genetic_traits
85
+ :heart_rate
86
+ :height
87
+ :locations
88
+ :sleeps
89
+ :weight
90
+ :bmi
91
+ ```
92
+
93
+ ### Query Options
94
+ Mixing up these methods with some options will give you what you want:
95
+
96
+ ```ruby
97
+ u.human.query(:activities, summary: true) #=> will give you a summary of the activities
98
+ u.human.query(:sleeps, date: "2014-01-01") #=> Will give you a single sleep measurement
99
+
100
+ # Getting KPIs (KPIs are just single values you get to retrieve a measurements average value)
101
+ u.human.query(:weight) #=> Will give you a single weight value (The avg I guess)
102
+
103
+ # Getting readings (If you begin with a single avg value and you wanna go deeper)
104
+ u.human.query(:weight, readings: true)
105
+
106
+ # Return metadata (not just the array of hashes)
107
+ u.human.query(:activities, return_metadata: true) #=> Nestful::Response object, with headers and body available
108
+
109
+ # Manual offset/limit:
110
+ u.human.query(:activities, limit: 3, offset: 50).count #=> 3
111
+
112
+ # Return all of a user's data jammed together, despite being across multiple pages:
113
+ u.human.query(:activities).count #=> 50
114
+ u.human.query(:activities, fetch_all: true).count #=> 321
115
+ ```
116
+
117
+ Lastly, as a common rule, I've identified a pattern in humanapis.
118
+ - If the method name is plural, it will give you multiple measurements when calling it. In addition, you can ask for a summary: true, for a group of value in a specific date: "DATE" or for a single known measurement id: "measurement_id"
119
+ - If the method name is singular, it will give you a single avg value for what you asked. In addition, you can ask for all readings: true and for all readings => true in a specific date: "DATE".
120
+
121
+ ## Common errors and troubleshooting
122
+
123
+ ### 'rewrite_human_model': Could not find 'token' in User
124
+ - Causes: it does mean that the method you suggested does not exist!
125
+ - What to check: Check if you misspelled the method name or the attribute does not exist.
126
+ - Solving: If this does not solve, try using the humanizable function passing a method you can create in your model to retrieve manually just the token.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new :spec
5
+ task default: :spec
data/human_api.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'human_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "health_hero-human_api"
8
+ spec.version = HumanApi::VERSION
9
+ spec.authors = ["Justin Aiken"]
10
+ spec.email = ["justin@gohealthhero.com"]
11
+ spec.description = %q{API client for HumanAPI}
12
+ spec.summary = %q{API client for HumanAPI}
13
+ spec.homepage = "https://github.com/HealthHero/humanapi"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(spec)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "nestful", "~> 1.0.7"
23
+ spec.add_dependency "json", "~> 1.8.1"
24
+ spec.add_dependency "activesupport", "> 3.0"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec", "~> 3.1"
29
+ spec.add_development_dependency "vcr"
30
+ spec.add_development_dependency "webmock"
31
+ spec.add_development_dependency "dotenv"
32
+ spec.add_development_dependency "pry"
33
+ end
@@ -0,0 +1,16 @@
1
+ class String
2
+ def is_singular?
3
+ self.pluralize != self and self.singularize == self
4
+ end
5
+ end
6
+
7
+
8
+ class ActiveRecord::Base
9
+ attr_accessor :human_var, :test
10
+
11
+ def self.humanizable(method)
12
+ define_method :human do
13
+ @human_var ||= HumanApi::Human.new(access_token: self.send(method.to_sym))
14
+ end
15
+ end
16
+ end if defined? ActiveRecord
@@ -0,0 +1,40 @@
1
+ module HumanApi
2
+ class App < Nestful::Resource
3
+
4
+ endpoint 'https://api.humanapi.co'
5
+ path "/v1/apps/#{HumanApi.config.app_id}"
6
+ options auth_type: :basic, user: HumanApi.config.query_key, password: ''
7
+
8
+ # Get the humans of your app:
9
+ def self.humans
10
+ get 'users'
11
+ rescue Nestful::UnauthorizedAccess => e
12
+ if HumanApi.config.handle_access_error
13
+ HumanApi.config.handle_access_error.call e
14
+ else
15
+ raise if HumanApi.config.raise_access_errors
16
+ []
17
+ end
18
+ end
19
+
20
+ # Create a new human:
21
+ def self.create_human(id)
22
+ response = post 'users', externalId: id
23
+
24
+ # If the response is true
25
+ if response.status >= 200 && response.status < 300 # Leave it for now
26
+ JSON.parse response.body
27
+ else
28
+ # Else tell me something went wrong:
29
+ false # Nothing was created
30
+ end
31
+ rescue Nestful::UnauthorizedAccess => e
32
+ if HumanApi.config.handle_access_error
33
+ HumanApi.config.handle_access_error.call e
34
+ else
35
+ raise if HumanApi.config.raise_access_errors
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ module HumanApi
2
+ class Config
3
+ attr_accessor :app_id, :query_key, :human_model, :token_method_name, :hardcore, :raise_access_errors, :handle_access_error
4
+
5
+ CHECK_THE_GUIDE = "Read the guide for more information. (https://github.com/Pazienti/humanapi)"
6
+
7
+ def initialize
8
+ @hardcore = false
9
+ @raise_access_errors = false
10
+ end
11
+
12
+ def configure
13
+ rewrite_human_model
14
+ end
15
+
16
+ def rewrite_human_model
17
+ if human_model.present? && token_method_name.present?
18
+ if human_model.instance_methods.include?(token_method_name)
19
+
20
+ human_model.class_eval do
21
+ attr_accessor :human_var
22
+
23
+ def human
24
+ @human_var ||= HumanApi::Human.new(access_token: self.send(HumanApi.config.token_method_name.to_sym))
25
+ end
26
+ end
27
+ else
28
+ raise "Could not find '#{token_method_name}' in #{human_model}. #{CHECK_THE_GUIDE}"
29
+ end
30
+ else
31
+ # unless hardcore
32
+ # raise "You must set a human_model and a token_method_name. #{CHECK_THE_GUIDE}"
33
+ # end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,106 @@
1
+ module HumanApi
2
+ class Human < Nestful::Resource
3
+
4
+ attr_reader :token
5
+
6
+ endpoint 'https://api.humanapi.co'
7
+ path '/v1/human'
8
+
9
+ # The available methods for this api
10
+ AVAILABLE_METHODS = %i{
11
+ profile activities blood_glucose blood_oxygen blood_pressure body_fat genetic_traits heart_rate height locations sleeps weight bmi
12
+ }.freeze
13
+
14
+ def initialize(options)
15
+ @token = options[:access_token]
16
+ super
17
+ end
18
+
19
+ # Profile =====================================
20
+
21
+ def summary
22
+ get '', access_token: token
23
+ rescue Nestful::UnauthorizedAccess => e
24
+ if HumanApi.config.handle_access_error
25
+ HumanApi.config.handle_access_error.call e
26
+ else
27
+ raise if HumanApi.config.raise_access_errors
28
+ false
29
+ end
30
+ end
31
+
32
+ def profile(options = {})
33
+ query :profile, options
34
+ end
35
+
36
+ # =============================================
37
+
38
+ def query(method, options = {})
39
+ unless AVAILABLE_METHODS.include? method.to_sym
40
+ raise ArgumentError, "The method '#{method}' does not exist!"
41
+ end
42
+
43
+ # From sym to string
44
+ method = method.to_s
45
+
46
+ # The base of the url
47
+ url = "#{method}"
48
+
49
+ # If it is a singular word prepare for readings
50
+ if method.is_singular?
51
+ if options[:readings] == true
52
+ url += "/readings"
53
+ end
54
+ else
55
+ if options[:summary] == true
56
+ url += "/summary"
57
+ end
58
+ end
59
+
60
+ # You passed a date
61
+ if options[:date].present?
62
+ # Make a request for a specific date
63
+ url += "/daily/#{options[:date]}"
64
+ # If you passed an id
65
+ elsif options[:id].present?
66
+ # Make a request for a single
67
+ url += "/#{options[:id]}"
68
+ end
69
+
70
+ params = {access_token: token}
71
+
72
+ if options[:fetch_all]
73
+ results = []
74
+ first_request = get url, params
75
+ total_size = first_request.headers['x-total-count'].to_i
76
+ results = results + JSON.parse(first_request.body)
77
+ next_page_link = first_request.headers['link'].match(/<(https[^>]*)>/)[1]
78
+
79
+ while results.count < total_size
80
+ next_page = Nestful.get next_page_link
81
+ next_page_link = next_page.headers['link'].match(/<(https[^>]*)>/)[1]
82
+ results = results + JSON.parse(next_page.body)
83
+ end
84
+
85
+ results
86
+ else
87
+ params.merge!(limit: options[:limit]) if options[:limit].present?
88
+ params.merge!(offset: options[:offset]) if options[:offset].present?
89
+
90
+ result = get url, params
91
+ if options[:return_metadata]
92
+ result
93
+ else
94
+ JSON.parse result.body
95
+ end
96
+ end
97
+ end
98
+ rescue Nestful::UnauthorizedAccess => e
99
+ if HumanApi.config.handle_access_error
100
+ HumanApi.config.handle_access_error.call e
101
+ else
102
+ raise if HumanApi.config.raise_access_errors
103
+ false
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,3 @@
1
+ module HumanApi
2
+ VERSION = "0.2.0"
3
+ end
data/lib/human_api.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'active_support/all'
2
+ require 'json'
3
+ require 'nestful'
4
+ require 'human_api/version'
5
+ require 'human_api/config'
6
+ require 'config/initializers/core_ext'
7
+
8
+ module HumanApi
9
+
10
+ @config = HumanApi::Config.new
11
+
12
+ def self.config
13
+ if block_given?
14
+ yield @config
15
+ @config.configure
16
+ else
17
+ @config
18
+ end
19
+ end
20
+
21
+ autoload :Human, 'human_api/human'
22
+ autoload :App, 'human_api/app'
23
+ end
@@ -0,0 +1,40 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://APP_ID:@api.humanapi.co/v1/apps/CLIENT_ID/users
6
+ body:
7
+ encoding: US-ASCII
8
+ string: externalId=test_user
9
+ headers:
10
+ Content-Type:
11
+ - application/x-www-form-urlencoded
12
+ Accept-Encoding:
13
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
+ Accept:
15
+ - "*/*"
16
+ User-Agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 201
21
+ message: Created
22
+ headers:
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Date:
26
+ - Tue, 01 Sep 2015 18:23:56 GMT
27
+ Server:
28
+ - nginx/1.4.6 (Ubuntu)
29
+ X-Powered-By:
30
+ - Express
31
+ Content-Length:
32
+ - '200'
33
+ Connection:
34
+ - keep-alive
35
+ body:
36
+ encoding: UTF-8
37
+ string: '{"humanId":"abcd","externalId":"test_user","appId":"CLIENT_ID","createdAt":"2015-09-01T18:18:18.500Z","updatedAt":"2015-09-01T18:19:55.177Z"}'
38
+ http_version:
39
+ recorded_at: Tue, 01 Sep 2015 18:19:55 GMT
40
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,54 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.humanapi.co/v1/human/activities?access_token=token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Access-Control-Allow-Headers:
22
+ - Content-Type, Authorization, Accept
23
+ Access-Control-Allow-Methods:
24
+ - OPTIONS,GET,HEAD
25
+ Access-Control-Allow-Origin:
26
+ - "*"
27
+ Access-Control-Max-Age:
28
+ - '86400'
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Date:
32
+ - Tue, 01 Sep 2015 19:07:34 GMT
33
+ Etag:
34
+ - '"-1281148962"'
35
+ Link:
36
+ - <https://api.humanapi.co/v1/human/activities?access_token=token&offset=50>;
37
+ rel="next",<https://api.humanapi.co/v1/human/activities?access_token=token&offset=200>;
38
+ rel="last"
39
+ Server:
40
+ - nginx/1.4.6 (Ubuntu)
41
+ X-Powered-By:
42
+ - Express
43
+ X-Total-Count:
44
+ - '234'
45
+ Content-Length:
46
+ - '20611'
47
+ Connection:
48
+ - keep-alive
49
+ body:
50
+ encoding: UTF-8
51
+ string: '[{"id":"55e37dd14a99f60b00f9ef96","userId":"55b1327e7313370100c17161","startTime":"2015-08-30T13:40:00.000Z","endTime":"2015-08-30T15:37:56.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":7076,"distance":21167.628734835,"steps":0,"calories":1436,"sourceData":{},"createdAt":"2015-08-30T22:04:01.355Z","updatedAt":"2015-08-30T22:04:01.355Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55e37dd14a99f60b00f9ef9c","userId":"55b1327e7313370100c17161","startTime":"2015-08-29T17:42:00.000Z","endTime":"2015-08-29T18:12:26.000Z","tzOffset":"-06:00","type":"cycling","source":"runkeeper","duration":1826,"distance":10863.072,"steps":0,"calories":307,"sourceData":{},"createdAt":"2015-08-30T22:04:01.366Z","updatedAt":"2015-08-30T22:04:01.366Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55e37dd14a99f60b00f9efa2","userId":"55b1327e7313370100c17161","startTime":"2015-08-28T14:54:48.000Z","endTime":"2015-08-28T15:27:45.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1977,"distance":6444.04127907749,"steps":0,"calories":434,"sourceData":{},"createdAt":"2015-08-30T22:04:01.376Z","updatedAt":"2015-08-30T22:04:01.376Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55e37dd14a99f60b00f9efa7","userId":"55b1327e7313370100c17161","startTime":"2015-08-27T16:05:38.000Z","endTime":"2015-08-27T16:42:46.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2228,"distance":6845.75340414214,"steps":0,"calories":468,"sourceData":{},"createdAt":"2015-08-30T22:04:01.384Z","updatedAt":"2015-08-30T22:04:01.384Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55e37dd14a99f60b00f9efab","userId":"55b1327e7313370100c17161","startTime":"2015-08-26T15:10:33.000Z","endTime":"2015-08-26T15:45:27.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2094,"distance":6562.9452587098,"steps":0,"calories":442,"sourceData":{},"createdAt":"2015-08-30T22:04:01.391Z","updatedAt":"2015-08-30T22:04:01.391Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55e37dd14a99f60b00f9efaf","userId":"55b1327e7313370100c17161","startTime":"2015-08-25T14:56:39.000Z","endTime":"2015-08-25T15:30:55.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2056,"distance":6384.72515693381,"steps":0,"calories":426,"sourceData":{},"createdAt":"2015-08-30T22:04:01.397Z","updatedAt":"2015-08-30T22:04:01.397Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55da18c74625a4080005294a","userId":"55b1327e7313370100c17161","startTime":"2015-08-23T15:45:00.000Z","endTime":"2015-08-23T17:16:46.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":5506,"distance":16900.7034856655,"steps":0,"calories":1146,"sourceData":{},"createdAt":"2015-08-23T19:02:31.386Z","updatedAt":"2015-08-23T19:02:31.386Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55d89d174625a40800043e0c","userId":"55b1327e7313370100c17161","startTime":"2015-08-22T14:37:18.000Z","endTime":"2015-08-22T15:16:52.138Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2374.138,"distance":6893.33608423727,"steps":0,"calories":481,"sourceData":{},"createdAt":"2015-08-22T16:02:31.379Z","updatedAt":"2015-08-22T16:02:31.379Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55da18c74625a4080005294e","userId":"55b1327e7313370100c17161","startTime":"2015-08-21T16:08:41.000Z","endTime":"2015-08-21T16:45:54.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2233,"distance":6979.46988309577,"steps":0,"calories":478,"sourceData":{},"createdAt":"2015-08-23T19:02:31.410Z","updatedAt":"2015-08-23T19:02:31.410Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55d4fcf78892cc08002e0522","userId":"55b1327e7313370100c17161","startTime":"2015-08-19T08:56:09.000Z","endTime":"2015-08-19T09:31:53.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2144,"distance":6538.2353476637,"steps":0,"calories":442,"sourceData":{},"createdAt":"2015-08-19T22:02:31.534Z","updatedAt":"2015-08-19T22:02:31.534Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55d4fcf78892cc08002e0528","userId":"55b1327e7313370100c17161","startTime":"2015-08-18T08:53:32.000Z","endTime":"2015-08-18T09:27:53.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2061,"distance":6500.99039061797,"steps":0,"calories":453,"sourceData":{},"createdAt":"2015-08-19T22:02:31.543Z","updatedAt":"2015-08-19T22:02:31.543Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55d205798892cc08002bd2a1","userId":"55b1327e7313370100c17161","startTime":"2015-08-16T07:57:52.000Z","endTime":"2015-08-16T09:38:08.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":6016,"distance":18334.0457494036,"steps":0,"calories":1237,"sourceData":{},"createdAt":"2015-08-17T16:02:01.430Z","updatedAt":"2015-08-17T16:02:01.430Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55d1f7698892cc08002bc7c3","userId":"55b1327e7313370100c17161","startTime":"2015-08-15T09:04:35.000Z","endTime":"2015-08-15T09:49:06.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2671,"distance":8101.67207317344,"steps":0,"calories":556,"sourceData":{},"createdAt":"2015-08-17T15:02:01.409Z","updatedAt":"2015-08-17T15:02:01.409Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55ce1f098892cc080028d16f","userId":"55b1327e7313370100c17161","startTime":"2015-08-14T07:13:18.000Z","endTime":"2015-08-14T07:54:50.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2492,"distance":8088.75966609549,"steps":0,"calories":552,"sourceData":{},"createdAt":"2015-08-14T17:02:01.603Z","updatedAt":"2015-08-14T17:02:01.603Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55cccd898892cc080027c65e","userId":"55b1327e7313370100c17161","startTime":"2015-08-12T07:26:22.000Z","endTime":"2015-08-12T08:04:08.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2266,"distance":7267.66455299153,"steps":0,"calories":495,"sourceData":{},"createdAt":"2015-08-13T17:02:01.428Z","updatedAt":"2015-08-13T17:02:01.428Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55cccd898892cc080027c662","userId":"55b1327e7313370100c17161","startTime":"2015-08-11T08:44:34.000Z","endTime":"2015-08-11T09:17:54.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2000,"distance":6913.36663730125,"steps":0,"calories":465,"sourceData":{},"createdAt":"2015-08-13T17:02:01.434Z","updatedAt":"2015-08-13T17:02:01.434Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55c7bfab8892cc080023a730","userId":"55b1327e7313370100c17161","startTime":"2015-08-09T10:19:22.000Z","endTime":"2015-08-09T12:01:54.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":6152,"distance":17856.6732247731,"steps":0,"calories":1204,"sourceData":{},"createdAt":"2015-08-09T21:01:31.453Z","updatedAt":"2015-08-09T21:01:31.453Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55c68a4b8892cc080022b774","userId":"55b1327e7313370100c17161","startTime":"2015-08-08T11:47:02.000Z","endTime":"2015-08-08T12:33:02.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2760,"distance":8115.59730310348,"steps":0,"calories":569,"sourceData":{},"createdAt":"2015-08-08T23:01:31.786Z","updatedAt":"2015-08-08T23:01:31.786Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55c68a4b8892cc080022b778","userId":"55b1327e7313370100c17161","startTime":"2015-08-06T08:30:04.000Z","endTime":"2015-08-06T09:05:16.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2112,"distance":6777.99816345787,"steps":0,"calories":461,"sourceData":{},"createdAt":"2015-08-08T23:01:31.793Z","updatedAt":"2015-08-08T23:01:31.793Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55c68a4b8892cc080022b77c","userId":"55b1327e7313370100c17161","startTime":"2015-08-05T07:20:47.000Z","endTime":"2015-08-05T07:56:11.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2124,"distance":6548.10519801716,"steps":0,"calories":442,"sourceData":{},"createdAt":"2015-08-08T23:01:31.799Z","updatedAt":"2015-08-08T23:01:31.799Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55c68a4b8892cc080022b780","userId":"55b1327e7313370100c17161","startTime":"2015-08-04T08:13:48.000Z","endTime":"2015-08-04T08:55:13.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2485,"distance":8084.56047541964,"steps":0,"calories":552,"sourceData":{},"createdAt":"2015-08-08T23:01:31.806Z","updatedAt":"2015-08-08T23:01:31.806Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55be92f004ad0d0800e5e552","userId":"55b1327e7313370100c17161","startTime":"2015-08-02T09:18:00.000Z","endTime":"2015-08-02T11:38:23.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":8423,"distance":25811.3561216763,"steps":0,"calories":1735,"sourceData":{},"createdAt":"2015-08-02T22:00:16.429Z","updatedAt":"2015-08-02T22:00:16.429Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55be92f004ad0d0800e5e556","userId":"55b1327e7313370100c17161","startTime":"2015-07-31T09:20:00.000Z","endTime":"2015-07-31T09:50:02.000Z","tzOffset":"-06:00","type":"cycling","source":"runkeeper","duration":1802,"distance":10686.04416,"steps":0,"calories":300,"sourceData":{},"createdAt":"2015-08-02T22:00:16.435Z","updatedAt":"2015-08-02T22:00:16.435Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55be92f004ad0d0800e5e55a","userId":"55b1327e7313370100c17161","startTime":"2015-07-30T08:27:34.000Z","endTime":"2015-07-30T08:55:17.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1663,"distance":4826.52010011822,"steps":0,"calories":322,"sourceData":{},"createdAt":"2015-08-02T22:00:16.441Z","updatedAt":"2015-08-02T22:00:16.441Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b96506ce04850900911e78","userId":"55b1327e7313370100c17161","startTime":"2015-07-29T08:26:11.000Z","endTime":"2015-07-29T09:03:16.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2225,"distance":6537.06814296831,"steps":0,"calories":444,"sourceData":{},"createdAt":"2015-07-29T23:43:02.588Z","updatedAt":"2015-07-29T23:43:02.588Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b96506ce04850900911e7a","userId":"55b1327e7313370100c17161","startTime":"2015-07-28T08:37:50.000Z","endTime":"2015-07-28T09:20:42.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2572,"distance":8190.82102893706,"steps":0,"calories":564,"sourceData":{},"createdAt":"2015-07-29T23:43:02.596Z","updatedAt":"2015-07-29T23:43:02.596Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b96506ce04850900911e7c","userId":"55b1327e7313370100c17161","startTime":"2015-07-26T07:27:06.000Z","endTime":"2015-07-26T09:25:37.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":7111,"distance":21765.9545514157,"steps":0,"calories":1462,"sourceData":{},"createdAt":"2015-07-29T23:43:02.605Z","updatedAt":"2015-07-29T23:43:02.605Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b96506ce04850900911e7e","userId":"55b1327e7313370100c17161","startTime":"2015-07-25T07:57:20.000Z","endTime":"2015-07-25T09:03:38.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":3978,"distance":12897.1377998602,"steps":0,"calories":862,"sourceData":{},"createdAt":"2015-07-29T23:43:02.612Z","updatedAt":"2015-07-29T23:43:02.612Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b26071ce04850900803746","userId":"55b1327e7313370100c17161","startTime":"2015-07-24T09:02:13.000Z","endTime":"2015-07-24T09:35:01.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1968,"distance":6548.89061920189,"steps":0,"calories":441,"sourceData":{},"createdAt":"2015-07-24T15:57:37.580Z","updatedAt":"2015-07-24T15:57:37.580Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b13a07ce048509007e1659","userId":"55b1327e7313370100c17161","startTime":"2015-07-23T09:07:00.000Z","endTime":"2015-07-23T09:50:35.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2615,"distance":8085.25682848678,"steps":0,"calories":553,"sourceData":{},"createdAt":"2015-07-23T19:01:27.874Z","updatedAt":"2015-07-23T19:01:27.874Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b13a07ce048509007e165c","userId":"55b1327e7313370100c17161","startTime":"2015-07-22T08:33:03.000Z","endTime":"2015-07-22T09:04:34.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1891,"distance":5516.98266195084,"steps":0,"calories":364,"sourceData":{},"createdAt":"2015-07-23T19:01:27.879Z","updatedAt":"2015-07-23T19:01:27.879Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfde3","userId":"55b1327e7313370100c17161","startTime":"2015-07-19T10:30:24.000Z","endTime":"2015-07-19T11:16:54.000Z","tzOffset":"-06:00","type":"elliptical","source":"runkeeper","duration":2790,"distance":7370.79537038086,"steps":0,"calories":500,"sourceData":{},"createdAt":"2015-07-23T18:36:44.310Z","updatedAt":"2015-07-23T18:36:44.310Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfde6","userId":"55b1327e7313370100c17161","startTime":"2015-07-18T11:02:13.000Z","endTime":"2015-07-18T11:48:13.000Z","tzOffset":"-06:00","type":"cycling","source":"runkeeper","duration":2760,"distance":17541.8489222168,"steps":0,"calories":422,"sourceData":{},"createdAt":"2015-07-23T18:36:44.317Z","updatedAt":"2015-07-23T18:36:44.317Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfde9","userId":"55b1327e7313370100c17161","startTime":"2015-07-17T11:20:03.000Z","endTime":"2015-07-17T12:04:03.000Z","tzOffset":"-06:00","type":"elliptical","source":"runkeeper","duration":2640,"distance":7145.48742607178,"steps":0,"calories":483,"sourceData":{},"createdAt":"2015-07-23T18:36:44.324Z","updatedAt":"2015-07-23T18:36:44.324Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdec","userId":"55b1327e7313370100c17161","startTime":"2015-07-16T10:21:43.000Z","endTime":"2015-07-16T10:55:59.569Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2056.569,"distance":6519.07434407043,"steps":0,"calories":416,"sourceData":{},"createdAt":"2015-07-23T18:36:44.331Z","updatedAt":"2015-07-23T18:36:44.331Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdef","userId":"55b1327e7313370100c17161","startTime":"2015-07-15T10:33:02.000Z","endTime":"2015-07-15T11:03:36.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1834,"distance":5605.00501698855,"steps":0,"calories":367,"sourceData":{},"createdAt":"2015-07-23T18:36:44.339Z","updatedAt":"2015-07-23T18:36:44.339Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdf2","userId":"55b1327e7313370100c17161","startTime":"2015-07-08T09:18:16.000Z","endTime":"2015-07-08T09:56:22.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2286,"distance":6714.30396235965,"steps":0,"calories":442,"sourceData":{},"createdAt":"2015-07-23T18:36:44.347Z","updatedAt":"2015-07-23T18:36:44.347Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdf5","userId":"55b1327e7313370100c17161","startTime":"2015-07-07T09:06:01.000Z","endTime":"2015-07-07T09:40:41.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2080,"distance":6488.45114683759,"steps":0,"calories":430,"sourceData":{},"createdAt":"2015-07-23T18:36:44.355Z","updatedAt":"2015-07-23T18:36:44.355Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdf8","userId":"55b1327e7313370100c17161","startTime":"2015-07-05T08:28:30.000Z","endTime":"2015-07-05T10:09:50.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":6080,"distance":17752.5679464697,"steps":0,"calories":1176,"sourceData":{},"createdAt":"2015-07-23T18:36:44.363Z","updatedAt":"2015-07-23T18:36:44.363Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdfb","userId":"55b1327e7313370100c17161","startTime":"2015-07-04T07:54:34.000Z","endTime":"2015-07-04T09:20:47.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":5173,"distance":16107.6936988416,"steps":0,"calories":1066,"sourceData":{},"createdAt":"2015-07-23T18:36:44.370Z","updatedAt":"2015-07-23T18:36:44.370Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfdfe","userId":"55b1327e7313370100c17161","startTime":"2015-07-03T08:31:53.000Z","endTime":"2015-07-03T09:17:58.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2765,"distance":8139.58162263241,"steps":0,"calories":542,"sourceData":{},"createdAt":"2015-07-23T18:36:44.378Z","updatedAt":"2015-07-23T18:36:44.378Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe01","userId":"55b1327e7313370100c17161","startTime":"2015-07-02T09:58:28.000Z","endTime":"2015-07-02T10:35:38.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2230,"distance":6863.0258096377,"steps":0,"calories":458,"sourceData":{},"createdAt":"2015-07-23T18:36:44.385Z","updatedAt":"2015-07-23T18:36:44.385Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe04","userId":"55b1327e7313370100c17161","startTime":"2015-07-01T08:14:40.000Z","endTime":"2015-07-01T08:51:17.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2197,"distance":6887.61287682173,"steps":0,"calories":478,"sourceData":{},"createdAt":"2015-07-23T18:36:44.392Z","updatedAt":"2015-07-23T18:36:44.392Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe07","userId":"55b1327e7313370100c17161","startTime":"2015-06-28T07:21:28.000Z","endTime":"2015-06-28T09:00:12.890Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":5924.89,"distance":17813.0159608506,"steps":0,"calories":1186,"sourceData":{},"createdAt":"2015-07-23T18:36:44.400Z","updatedAt":"2015-07-23T18:36:44.400Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe0a","userId":"55b1327e7313370100c17161","startTime":"2015-06-27T09:16:04.000Z","endTime":"2015-06-27T09:59:54.880Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2630.88,"distance":8096.25450805071,"steps":0,"calories":549,"sourceData":{},"createdAt":"2015-07-23T18:36:44.407Z","updatedAt":"2015-07-23T18:36:44.407Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe0d","userId":"55b1327e7313370100c17161","startTime":"2015-06-26T08:12:00.000Z","endTime":"2015-06-26T08:46:49.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2089,"distance":6692.83905664089,"steps":0,"calories":445,"sourceData":{},"createdAt":"2015-07-23T18:36:44.415Z","updatedAt":"2015-07-23T18:36:44.415Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe10","userId":"55b1327e7313370100c17161","startTime":"2015-06-25T10:02:00.000Z","endTime":"2015-06-25T10:30:52.000Z","tzOffset":"-06:00","type":"cycling","source":"runkeeper","duration":1732,"distance":9527.31648,"steps":0,"calories":255,"sourceData":{},"createdAt":"2015-07-23T18:36:44.422Z","updatedAt":"2015-07-23T18:36:44.422Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe13","userId":"55b1327e7313370100c17161","startTime":"2015-06-24T08:29:20.000Z","endTime":"2015-06-24T09:10:36.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":2476,"distance":8111.74199284542,"steps":0,"calories":552,"sourceData":{},"createdAt":"2015-07-23T18:36:44.429Z","updatedAt":"2015-07-23T18:36:44.429Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe16","userId":"55b1327e7313370100c17161","startTime":"2015-06-23T09:46:49.000Z","endTime":"2015-06-23T10:19:32.000Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":1963,"distance":6505.25232645365,"steps":0,"calories":469,"sourceData":{},"createdAt":"2015-07-23T18:36:44.440Z","updatedAt":"2015-07-23T18:36:44.440Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"},{"id":"55b1343cce048509007dfe19","userId":"55b1327e7313370100c17161","startTime":"2015-06-21T08:49:16.000Z","endTime":"2015-06-21T09:55:05.880Z","tzOffset":"-06:00","type":"running","source":"runkeeper","duration":3949.88,"distance":12873.5890010781,"steps":0,"calories":924,"sourceData":{},"createdAt":"2015-07-23T18:36:44.448Z","updatedAt":"2015-07-23T18:36:44.448Z","humanId":"ef30511710618584fb02a34e5b5c1f7f"}]'
52
+ http_version:
53
+ recorded_at: Tue, 01 Sep 2015 19:03:32 GMT
54
+ recorded_with: VCR 2.9.3