exist 0.1.0.beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +84 -0
- data/Rakefile +34 -0
- data/exist.gemspec +33 -0
- data/lib/exist.rb +16 -0
- data/lib/exist/api.rb +122 -0
- data/lib/exist/attribute_list.rb +12 -0
- data/lib/exist/attributes.rb +4 -0
- data/lib/exist/authentication.rb +25 -0
- data/lib/exist/average.rb +4 -0
- data/lib/exist/average_list.rb +11 -0
- data/lib/exist/correlation.rb +4 -0
- data/lib/exist/correlation_list.rb +7 -0
- data/lib/exist/insight.rb +8 -0
- data/lib/exist/insight_list.rb +11 -0
- data/lib/exist/user.rb +8 -0
- data/lib/exist/version.rb +3 -0
- data/spec/exist/api_spec.rb +238 -0
- data/spec/exist_spec.rb +7 -0
- data/spec/fixtures/attributes.json +47 -0
- data/spec/fixtures/average_for_specific_attribute.json +31 -0
- data/spec/fixtures/averages.json +26 -0
- data/spec/fixtures/correlations.json +27 -0
- data/spec/fixtures/insights.json +51 -0
- data/spec/fixtures/insights_for_specific_attribute.json +29 -0
- data/spec/fixtures/overview.json +41 -0
- data/spec/fixtures/specific_attribute.json +29 -0
- data/spec/fixtures/user_stripped.json +13 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/api_helpers.rb +20 -0
- metadata +275 -0
data/lib/exist/user.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Exist::API do
|
4
|
+
context "initialization" do
|
5
|
+
let(:token) { 'SOMETOKEN' }
|
6
|
+
|
7
|
+
it 'initializes with a token' do
|
8
|
+
client = described_class.new(token: token)
|
9
|
+
expect(client.api_key).to eq(token)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'reads the token from environment if not given' do
|
13
|
+
ENV['EXIST_API_TOKEN'] = token
|
14
|
+
client = described_class.new
|
15
|
+
expect(client.api_key).to eq(token)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an error if no token present' do
|
19
|
+
ENV.delete('EXIST_API_TOKEN')
|
20
|
+
|
21
|
+
expect {
|
22
|
+
described_class.new
|
23
|
+
}.to raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'negotiates a token if username and password are provided' do
|
27
|
+
stub_request(:post, api_root + '/auth/simple-token').to_return(
|
28
|
+
body: { token: token }.to_json,
|
29
|
+
headers: { 'Content-Type' => 'application/json'}
|
30
|
+
)
|
31
|
+
|
32
|
+
client = described_class.new(username: 'some', password: 'pass')
|
33
|
+
expect(client.api_key).to be_truthy
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an error if fails to login' do
|
37
|
+
stub_request(:post, api_root + '/auth/simple-token').to_return(
|
38
|
+
status: 401
|
39
|
+
)
|
40
|
+
expect {
|
41
|
+
described_class.new(username: 'john', password: 'doe')
|
42
|
+
}.to raise_error(/There was a problem authenticating with the API/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'API' do
|
47
|
+
let(:client) { described_class.new(token: 'SOME') }
|
48
|
+
|
49
|
+
describe '#me' do
|
50
|
+
let(:user) { client.me }
|
51
|
+
|
52
|
+
before do
|
53
|
+
stub_request_with_fixture(:get, '/users/$self/', :user_stripped)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns the current user' do
|
57
|
+
expect(user.id).to eq(1)
|
58
|
+
expect(user.local_time).to be_kind_of(Time)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#overview' do
|
63
|
+
let(:overview) { client.overview }
|
64
|
+
|
65
|
+
before do
|
66
|
+
stub_request_with_fixture(:get, '/users/$self/today/', :overview)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns the overview for the current user' do
|
70
|
+
expect(overview.username).to eq('josh')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'coerces keys' do
|
74
|
+
expect(overview.local_time).to be_kind_of(Time)
|
75
|
+
expect(overview.private).to eq(false)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'contains attributes' do
|
79
|
+
expect(overview.attributes.first.group).to eq('steps')
|
80
|
+
expect(overview.attributes.first.items.size).to eq(2)
|
81
|
+
expect(overview.attributes.first.items.first.value).to eq(258)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#attributes' do
|
86
|
+
let(:attributes) { client.attributes }
|
87
|
+
|
88
|
+
before do
|
89
|
+
stub_request_with_fixture(:get, '/users/$self/attributes/?limit=31', :attributes)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'returns the attributes for the current user' do
|
93
|
+
expect(attributes).to be
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'wraps into an attribute list' do
|
97
|
+
expect(attributes).to be_kind_of(Exist::AttributeList)
|
98
|
+
expect(attributes.size).to eq(2)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'coerces values' do
|
102
|
+
pending
|
103
|
+
expect(attributes.first.values.first.date).to be_kind_of(Date)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#attribute' do
|
108
|
+
let(:attribute) { client.attribute(:steps) }
|
109
|
+
|
110
|
+
before do
|
111
|
+
stub_request_with_fixture(
|
112
|
+
:get, '/users/$self/attributes/steps/?date_max=&date_min=&limit=31&page=1',
|
113
|
+
:specific_attribute
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'returns a specific attribute' do
|
118
|
+
expect(attribute.attributes).to be
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'wraps into an attribute list' do
|
122
|
+
expect(attribute.total).to eq(655)
|
123
|
+
expect(attribute.size).to eq(7)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#insights' do
|
128
|
+
let(:insights) { client.insights }
|
129
|
+
|
130
|
+
before do
|
131
|
+
stub_request_with_fixture(
|
132
|
+
:get, '/users/$self/insights/?date_max=&date_min=&limit=31&page=1',
|
133
|
+
:insights
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'returns the user insights' do
|
138
|
+
expect(insights.insights).to be
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'wraps inside an insight list' do
|
142
|
+
expect(insights.total).to eq(740)
|
143
|
+
expect(insights.size).to eq(2)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#insights_for_attribute' do
|
148
|
+
let(:insights) { client.insights_for_attribute(:sleep) }
|
149
|
+
|
150
|
+
before do
|
151
|
+
stub_request_with_fixture(
|
152
|
+
:get, '/users/$self/insights/attribute/sleep/?date_max=&date_min=&limit=31&page=1',
|
153
|
+
:insights_for_specific_attribute
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'returns the specific attribute insights' do
|
158
|
+
expect(insights.insights).to be
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'wrap in an insights list' do
|
162
|
+
expect(insights.size).to eq(1)
|
163
|
+
expect(insights.total).to eq(220)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#averages' do
|
168
|
+
let(:averages) { client.averages }
|
169
|
+
|
170
|
+
before do
|
171
|
+
stub_request_with_fixture(:get, '/users/$self/averages/', :averages)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'returns the current averages' do
|
175
|
+
expect(averages.averages).to be
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'wraps in an average list' do
|
179
|
+
expect(averages.size).to eq(2)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#average_for_attribute' do
|
184
|
+
let(:average) { client.average_for_attribute(:steps) }
|
185
|
+
|
186
|
+
before do
|
187
|
+
stub_request_with_fixture(
|
188
|
+
:get, '/users/$self/averages/attribute/steps/?date_max=&date_min=&limit=31&page=1',
|
189
|
+
:average_for_specific_attribute
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'returns the average for an specific attribute' do
|
194
|
+
expect(average.averages).to be
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'wraps in an average list' do
|
198
|
+
expect(average.size).to eq(2)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#correlations' do
|
203
|
+
let(:correlations) { client.correlations(:$self, :steps) }
|
204
|
+
|
205
|
+
before do
|
206
|
+
stub_request_with_fixture(
|
207
|
+
:get, '/users/$self/correlations/attribute/steps/?date_max=&date_min=&limit=31&page=1',
|
208
|
+
:correlations
|
209
|
+
)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'returns the correlations for an attribute' do
|
213
|
+
expect(correlations.correlations).to be
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'wraps in a correlation list' do
|
217
|
+
expect(correlations.total).to eq(479)
|
218
|
+
expect(correlations.size).to eq(2)
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when specifying latest_only' do
|
222
|
+
before do
|
223
|
+
stub_request(
|
224
|
+
:get,
|
225
|
+
api_root + '/users/$self/correlations/attribute/steps/?latest_only=true&limit=31&page=1'
|
226
|
+
).to_return(
|
227
|
+
body: {}.to_json,
|
228
|
+
headers: { 'Content-Type' => 'application/json'}
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'returns only the latest correlations, avoiding date ranges' do
|
233
|
+
client.correlations(:$self, :steps, latest_only: true)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/spec/exist_spec.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"attribute": "steps",
|
4
|
+
"label": "Steps",
|
5
|
+
"group": "activity",
|
6
|
+
"service": "Fitbit",
|
7
|
+
"private": false,
|
8
|
+
"values": [
|
9
|
+
{
|
10
|
+
"value": 331,
|
11
|
+
"date": "2014-08-01"
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"value": 5872,
|
15
|
+
"date": "2014-07-31"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"value": 2832,
|
19
|
+
"date": "2014-07-30"
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"value": 5153,
|
23
|
+
"date": "2014-07-29"
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"value": 4354,
|
27
|
+
"date": "2014-07-28"
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"value": 7132,
|
31
|
+
"date": "2014-07-27"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"value": 4144,
|
35
|
+
"date": "2014-07-26"
|
36
|
+
}
|
37
|
+
]
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"attribute": "floors",
|
41
|
+
"label": "Floors",
|
42
|
+
"group": "activity",
|
43
|
+
"service": "Fitbit",
|
44
|
+
"private": false,
|
45
|
+
"values": [ ]
|
46
|
+
}
|
47
|
+
]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{
|
2
|
+
"count": 11,
|
3
|
+
"next": null,
|
4
|
+
"previous": null,
|
5
|
+
"results": [
|
6
|
+
{
|
7
|
+
"attribute": "steps",
|
8
|
+
"date": "2020-04-29",
|
9
|
+
"overall": 4174.0,
|
10
|
+
"monday": 4057.0,
|
11
|
+
"tuesday": 6614.0,
|
12
|
+
"wednesday": 4001.0,
|
13
|
+
"thursday": 3923.0,
|
14
|
+
"friday": 4528.0,
|
15
|
+
"saturday": 3649.0,
|
16
|
+
"sunday": 3904.0
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"attribute": "steps",
|
20
|
+
"date": "2020-03-30",
|
21
|
+
"overall": 4062.0,
|
22
|
+
"monday": 4057.0,
|
23
|
+
"tuesday": 6618.0,
|
24
|
+
"wednesday": 3610.0,
|
25
|
+
"thursday": 3923.0,
|
26
|
+
"friday": 4063.0,
|
27
|
+
"saturday": 3636.0,
|
28
|
+
"sunday": 3904.0
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"attribute": "steps",
|
4
|
+
"date": "2020-04-29",
|
5
|
+
"overall": 4174.0,
|
6
|
+
"monday": 4057.0,
|
7
|
+
"tuesday": 6614.0,
|
8
|
+
"wednesday": 4001.0,
|
9
|
+
"thursday": 3923.0,
|
10
|
+
"friday": 4528.0,
|
11
|
+
"saturday": 3649.0,
|
12
|
+
"sunday": 3904.0
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"attribute": "floors",
|
16
|
+
"date": "2020-04-29",
|
17
|
+
"overall": 13.0,
|
18
|
+
"monday": 13.0,
|
19
|
+
"tuesday": 16.0,
|
20
|
+
"wednesday": 14.0,
|
21
|
+
"thursday": 12.0,
|
22
|
+
"friday": 14.0,
|
23
|
+
"saturday": 13.0,
|
24
|
+
"sunday": 12.0
|
25
|
+
}
|
26
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"count": 479,
|
3
|
+
"next": "https://exist.io/api/1/users/josh/correlations/attribute/steps/?page=2",
|
4
|
+
"previous": null,
|
5
|
+
"results": [
|
6
|
+
{
|
7
|
+
"date": "2015-05-11",
|
8
|
+
"period": 90,
|
9
|
+
"attribute": "steps",
|
10
|
+
"attribute2": "steps_distance",
|
11
|
+
"value": 0.999735821732415,
|
12
|
+
"p": 5.43055953485446e-146,
|
13
|
+
"first_person": "I am almost certain (100%) to travel a further distance on days I take more steps.",
|
14
|
+
"second_person": "You are almost certain (100%) to travel a further distance on days you take more steps."
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"date": "2015-05-11",
|
18
|
+
"period": 90,
|
19
|
+
"attribute": "steps",
|
20
|
+
"attribute2": "floors",
|
21
|
+
"value": 0.693332839316818,
|
22
|
+
"p": 3.63477333939521e-14,
|
23
|
+
"first_person": "I am much more likely (69%) to climb floors on days I take more steps.",
|
24
|
+
"second_person": "You are much more likely (69%) to climb floors on days you take more steps."
|
25
|
+
}
|
26
|
+
]
|
27
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"count": 740,
|
3
|
+
"next": "https://exist.io/api/1/users/josh/insights/?page=2",
|
4
|
+
"previous": null,
|
5
|
+
"results": [
|
6
|
+
{
|
7
|
+
"created": "2015-05-09T01:00:02Z",
|
8
|
+
"target_date": "2015-05-08",
|
9
|
+
"html": "<div class=\"secondary\">Friday night: Shortest sleep for 3 days</div>...",
|
10
|
+
"value": "303",
|
11
|
+
"value2": "3 days",
|
12
|
+
"comment": null,
|
13
|
+
"type": {
|
14
|
+
"name": "sleep_worst_since_x",
|
15
|
+
"period": 1,
|
16
|
+
"priority": 2,
|
17
|
+
"attribute": {
|
18
|
+
"name": "sleep",
|
19
|
+
"label": "Time asleep",
|
20
|
+
"group": {
|
21
|
+
"name": "sleep",
|
22
|
+
"priority": 3
|
23
|
+
},
|
24
|
+
"priority": 2
|
25
|
+
}
|
26
|
+
}
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"created": "2015-05-08T21:00:03Z",
|
30
|
+
"target_date": null,
|
31
|
+
"html": "<div class=\"number\">09:38</div>...",
|
32
|
+
"value": "578",
|
33
|
+
"value2": "65 min earlier",
|
34
|
+
"comment": null,
|
35
|
+
"type": {
|
36
|
+
"name": "sleep_end_average_week",
|
37
|
+
"period": 7,
|
38
|
+
"priority": 3,
|
39
|
+
"attribute": {
|
40
|
+
"name": "sleep_end",
|
41
|
+
"label": "Wake time",
|
42
|
+
"group": {
|
43
|
+
"name": "sleep",
|
44
|
+
"priority": 3
|
45
|
+
},
|
46
|
+
"priority": 4
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
]
|
51
|
+
}
|