ruby-fitbit 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ruby-fitbit.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  require 'rubygems'
2
2
  require 'mechanize'
3
3
  require 'nokogiri'
4
+ require 'json'
4
5
 
5
6
  class RubyFitbit
6
7
 
7
- attr_reader :calories, :steps, :miles_walked, :sedentary_active, :lightly_active, :fairly_active, :very_active
8
-
9
8
  #TODO change tests so reader isn't needed
10
9
  attr_reader :logged_in
11
10
 
@@ -15,10 +14,8 @@ class RubyFitbit
15
14
  @agent = WWW::Mechanize.new #{|a| a.log = Logger.new(STDERR) } #turn on if debugging
16
15
  @logged_in = false
17
16
  @cached_data = {}
18
- get_data
19
17
  end
20
18
 
21
- #todo only login once
22
19
  def login
23
20
  unless @logged_in
24
21
  page = @agent.get 'https://www.fitbit.com/login'
@@ -37,6 +34,93 @@ class RubyFitbit
37
34
  end
38
35
  end
39
36
 
37
+ def submit_food_log(options = {})
38
+ login
39
+
40
+ date = options.fetch(:date) {Time.now}
41
+ date = get_fitbit_date_format(date)
42
+ meal_type = options.fetch(:meal_type){'7'}
43
+ food_id = options[:food_id]
44
+ food = options[:food]
45
+ raise "food_id or food required to submit food log" unless food_id || food
46
+ unless food_id
47
+ food_recommendation = get_food_items(food)
48
+ if food_recommendation.length > 0
49
+ food_recommendation = food_recommendation.first
50
+ food = food_recommendation['name']
51
+ food_id = food_recommendation['id']
52
+ end
53
+ end
54
+
55
+ unit_id = options[:unit_id]
56
+ unit = options[:unit]
57
+ raise "unit_id or unit required to submit food log" unless unit_id || unit
58
+ unless unit_id
59
+ unit_id = get_unit_id_for_unit(unit)
60
+ end
61
+
62
+ page = @agent.get 'http://www.fitbit.com/foods/log'
63
+
64
+ form = page.forms[1]
65
+
66
+ form.action="/foods/log/foodLog?apiFormat=htmljson&log=on&date=#{date}"
67
+ form.foodId = food_id
68
+ form.foodselectinput = food
69
+ form.unitId = unit_id
70
+ form.quantityselectinput = unit
71
+ form.quantityConsumed = unit
72
+ form.mealTypeId = meal_type
73
+
74
+ result = @agent.submit(form, form.buttons.first)
75
+ end
76
+
77
+ def get_unit_id_for_unit(unit)
78
+ unit_id = nil
79
+ unit_type = unit.match(/\d+ (.*)/)[1]
80
+
81
+ type_map = {'oz' => '226',
82
+ 'lb' => '180',
83
+ 'gram' => '147',
84
+ 'kilogram' => '389',
85
+ 'roll' => '290',
86
+ 'serving' => '304',
87
+ 'link' => '188',
88
+ 'piece' => '251',
89
+ 'fl oz' => '128',
90
+ 'ml' => '209',
91
+ 'tsp' => '364',
92
+ 'tbsp' => '349',
93
+ 'cup' => '91',
94
+ 'pint' => '256',
95
+ 'slice' => '311',
96
+ 'liter' => '189',
97
+ 'quart' => '279',
98
+ 'entree' => '117',
99
+ 'portion' => '270'
100
+ }
101
+
102
+ unit_id = type_map[unit_type]
103
+ unit_id
104
+ end
105
+
106
+ def get_food_items(food="Coffe")
107
+ login
108
+ result = @agent.get "http://www.fitbit.com/solr/food/select?q=#{food}&wt=foodjson&qt=food"
109
+ foods = JSON.parse(result.body).first[1]["foods"]
110
+ foods
111
+ end
112
+
113
+ def get_eaten_calories(date = Time.now)
114
+ login
115
+
116
+ date = get_fitbit_date_format(date).gsub('-','/')
117
+ page = @agent.get "https://www.fitbit.com/foods/log/#{date}"
118
+ calories_data = page.search("//div[@id='dailyTotals']").first
119
+ calories_xml = calories_data.to_xml
120
+ calories_text = calories_data.text
121
+ {:calories_xml => calories_xml, :calories_text => calories_text}
122
+ end
123
+
40
124
  def get_data(date = Time.now)
41
125
  login
42
126
 
@@ -45,21 +129,21 @@ class RubyFitbit
45
129
 
46
130
  page = @agent.get "https://www.fitbit.com/#{date}"
47
131
 
48
- @calories = page.search("//div[@class='data']").search("span").children[0].text
49
- @steps = page.search("//div[@class='data']").search("span").children[2].text.strip
50
- @miles_walked = page.search("//div[@class='data']").search("span").children[3].text.strip
51
- @sedentary_active = page.search("//div[@class='sedentary caption']/div[@class='number']").text.strip
52
- @lightly_active = page.search("//div[@class='lightly caption']/div[@class='number']").text.strip
53
- @fairly_active = page.search("//div[@class='caption fairly']/div[@class='number']").text.strip
54
- @very_active = page.search("//div[@class='caption very']/div[@class='number']").text.strip
132
+ calories = page.search("//div[@class='data']").search("span").children[0].text
133
+ steps = page.search("//div[@class='data']").search("span").children[2].text.strip
134
+ miles_walked = page.search("//div[@class='data']").search("span").children[3].text.strip
135
+ sedentary_active = page.search("//div[@class='sedentary caption']/div[@class='number']").text.strip
136
+ lightly_active = page.search("//div[@class='lightly caption']/div[@class='number']").text.strip
137
+ fairly_active = page.search("//div[@class='caption fairly']/div[@class='number']").text.strip
138
+ very_active = page.search("//div[@class='caption very']/div[@class='number']").text.strip
55
139
  data = {}
56
- data['calories'] = @calories.to_i
57
- data['steps'] = @steps.to_i
58
- data['miles_walked'] = @miles_walked.to_f
59
- data['sedentary_active'] = @sedentary_active
60
- data['lightly_active'] = @lightly_active
61
- data['fairly_active'] = @fairly_active
62
- data['very_active'] = @very_active
140
+ data['calories'] = calories.to_i
141
+ data['steps'] = steps.to_i
142
+ data['miles_walked'] = miles_walked.to_f
143
+ data['sedentary_active'] = sedentary_active
144
+ data['lightly_active'] = lightly_active
145
+ data['fairly_active'] = fairly_active
146
+ data['very_active'] = very_active
63
147
  @cached_data[date] = data
64
148
  data
65
149
  end
data/ruby-fitbit.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruby-fitbit}
8
- s.version = "0.2.3"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dan Mayer"]
12
- s.date = %q{2010-07-24}
12
+ s.date = %q{2010-10-14}
13
13
  s.default_executable = %q{ruby-fitbit}
14
14
  s.description = %q{This is a Ruby API for Fitbit.com, currently scraping, but soon will use the officail API (when released)}
15
15
  s.email = %q{Danmayer@gmail.com}
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "bin/ruby-fitbit",
29
+ "fixtures/vcr_cassettes/fitbit_get_data.yml",
29
30
  "lib/ruby-fitbit.rb",
30
31
  "ruby-fitbit.gemspec",
31
32
  "test/helper.rb",
@@ -1,16 +1,16 @@
1
1
  require File.expand_path('./helper', File.dirname(__FILE__))
2
2
  require 'webmock/test_unit'
3
- #require 'vcr'
3
+ require 'vcr'
4
4
  require 'ruby-fitbit'
5
5
  require 'mocha'
6
6
 
7
7
  class TestRubyFitbit < Test::Unit::TestCase
8
8
  include WebMock
9
9
 
10
- #VCR.config do |c|
11
- # c.cassette_library_dir = 'fixtures/vcr_cassettes'
12
- # c.http_stubbing_library = :webmock
13
- #end
10
+ VCR.config do |c|
11
+ c.cassette_library_dir = 'fixtures/vcr_cassettes'
12
+ c.http_stubbing_library = :webmock
13
+ end
14
14
 
15
15
  #TODO this is a mess it took awhile to figure out a way to make the tests work at all clean this up
16
16
 
@@ -62,7 +62,9 @@ END
62
62
  data = File.read("./test/responses/data.txt")
63
63
  response = "#{HEADERS} #{data}"
64
64
 
65
- stub_request(:get, "https://www.fitbit.com/2010/07/18").to_return(response)
65
+ date = Time.now()
66
+ date = date.strftime("%Y/%m/%d")
67
+ stub_request(:get, "https://www.fitbit.com/#{date}").to_return(response)
66
68
 
67
69
  RubyFitbit.any_instance.stubs(:login)
68
70
  fitbit = RubyFitbit.new("fake@fake.com","pass")
@@ -70,15 +72,32 @@ END
70
72
  @logged_in = val
71
73
  end
72
74
  fitbit.set_logged_in(true)
75
+ data = fitbit.get_data
73
76
 
74
- assert_equal "1928", fitbit.calories, "wrong calories"
75
- assert_equal "7821", fitbit.steps, "wrong steps"
76
- assert_equal "3.38", fitbit.miles_walked, "wrong miles"
77
- assert_equal "16hrs 58min", fitbit.sedentary_active, "wrong sedentary"
78
- assert_equal "1hr 42min", fitbit.lightly_active, "wrong lightly"
79
- assert_equal "1hr 51min", fitbit.fairly_active, "wrong fairly"
80
- assert_equal "17min", fitbit.very_active, "wrong very"
77
+ assert_equal 1928, data['calories'], "wrong calories"
78
+ assert_equal 7821, data['steps'], "wrong steps"
79
+ assert_equal 3.38, data['miles_walked'], "wrong miles"
80
+ assert_equal "16hrs 58min", data['sedentary_active'], "wrong sedentary"
81
+ assert_equal "1hr 42min", data['lightly_active'], "wrong lightly"
82
+ assert_equal "1hr 51min", data['fairly_active'], "wrong fairly"
83
+ assert_equal "17min", data['very_active'], "wrong very"
84
+ end
85
+
86
+ should "use VCR successfully" do
87
+ fitbit = nil
88
+ data = nil
89
+ VCR.use_cassette('fitbit_get_data', :record => :new_episodes) do
90
+ fitbit = RubyFitbit.new("fake@fake.com","fake")
91
+ data = fitbit.get_data(Time.parse("2010/08/03"))
92
+ end
81
93
 
94
+ assert_equal 2084, data['calories'], "wrong calories"
95
+ assert_equal 10018, data['steps'], "wrong steps"
96
+ assert_equal 4.61, data['miles_walked'], "wrong miles"
97
+ assert_equal "18hrs 26min", data['sedentary_active'], "wrong sedentary"
98
+ assert_equal "1hr 17min", data['lightly_active'], "wrong lightly"
99
+ assert_equal "1hr 29min", data['fairly_active'], "wrong fairly"
100
+ assert_equal "42min", data['very_active'], "wrong very"
82
101
  end
83
102
 
84
103
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
7
  - 3
9
- version: 0.2.3
8
+ - 1
9
+ version: 0.3.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Dan Mayer
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-24 00:00:00 -04:00
17
+ date: 2010-10-14 00:00:00 -04:00
18
18
  default_executable: ruby-fitbit
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -88,6 +88,7 @@ files:
88
88
  - Rakefile
89
89
  - VERSION
90
90
  - bin/ruby-fitbit
91
+ - fixtures/vcr_cassettes/fitbit_get_data.yml
91
92
  - lib/ruby-fitbit.rb
92
93
  - ruby-fitbit.gemspec
93
94
  - test/helper.rb