todaysplan 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ [![Build Status](https://travis-ci.org/9kSoftware/todaysplan-ruby.svg?branch=master)](https://travis-ci.org/9kSoftware/todaysplan-ruby)
2
+ # todaysplan-ruby
3
+ A Ruby Library for TodaysPlan
4
+
5
+ This library is currently limited to only a few API calls needed. Feel free to do
6
+ Pull Request to include additional calls.
7
+
8
+ ## Installation
9
+
10
+ Add the gem to your app
11
+
12
+ ```ruby
13
+ gem 'todaysplan-ruby'
14
+ ```
15
+
16
+ ### Usage
17
+
18
+ To authenticate with TodaysPlan API by setting the username and password variables
19
+ ```ruby
20
+ TodaysPlan.username = 'email@example.com'
21
+ TodaysPlan.password = 'secret'
22
+ athletes = TodaysPlan::Athlete.all
23
+ ```
24
+ or create a TodaysPlan client and pass the client to the method
25
+ ```ruby
26
+ client = TodaysPlan::Client.new('email@example.com','secret')
27
+ athletes = TodaysPlan::Athlete.all(client)
28
+ ```
29
+
30
+ ### Testing
31
+ You can test live data by setting your credentials in todays_plan.yml. If this file
32
+ exists, live connections can be made and log the response to the console.
33
+
34
+ ```yaml
35
+ ---
36
+ username: 'email@example.com'
37
+ password: 'secret'
38
+ endpoint:
39
+ timeout: 120
40
+ logger: 'stdout'
41
+ debug: true
42
+ ```
43
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
11
+
12
+ begin
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+ rescue LoadError
19
+ # no rspec available
20
+ end
@@ -0,0 +1,47 @@
1
+ require 'todays_plan/version'
2
+ require 'todays_plan/client'
3
+ require 'todays_plan/connector'
4
+ require 'todays_plan/athlete'
5
+ require 'todays_plan/activity'
6
+ require 'rest_client'
7
+
8
+ module TodaysPlan
9
+
10
+ class << self
11
+ attr_accessor :username,
12
+ :password,
13
+ :endpoint,
14
+ :timeout,
15
+ :logger,
16
+ :debug
17
+
18
+ def configure(&block)
19
+ raise ArgumentError, "must provide a block" unless block_given?
20
+ block.arity.zero? ? instance_eval(&block) : yield(self)
21
+ end
22
+
23
+ # set default endpoint
24
+ def endpoint
25
+ @endpoint ||= 'https://whats.todaysplan.com.au/rest'
26
+ end
27
+
28
+ #set default timeout
29
+ def timeout
30
+ @timeout ||= 120 #rest-client default
31
+ end
32
+
33
+ #set default logger
34
+ def logger
35
+ if @debug
36
+ @logger = 'stdout'
37
+ return
38
+ end
39
+ @logger ||= nil
40
+ end
41
+
42
+ def debug
43
+ @debug ||= false
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,67 @@
1
+ module TodaysPlan
2
+ # Uses the TodaysPlan API for workouts and athlete information
3
+
4
+ class Activity
5
+
6
+ attr_reader :id
7
+ attr_reader :athlete_id
8
+ attr_reader :name
9
+ attr_reader :state
10
+ attr_reader :ts
11
+ attr_reader :type
12
+ attr_reader :completed_time
13
+ attr_reader :planned_time
14
+ attr_reader :description
15
+ attr_reader :completed_tscorepwr
16
+ attr_reader :planned_tscorepwr
17
+
18
+ def initialize(fields)
19
+ @id = fields['id'].to_i
20
+ @athlete_id = fields['user']['id'].to_i
21
+ @name = fields['name']
22
+ @state = fields['state']
23
+ @ts = Time.at(fields['ts'].to_i/1000).to_i
24
+ @type = fields['type']
25
+ @completed_time = fields["training"]
26
+ @planned_time = fields.has_key?('scheduled') ? fields['scheduled']["durationSecs"] : nil
27
+ @description = fields.has_key?('scheduled')? fields['scheduled']["preDescr"] : nil
28
+ @completed_tscorepwr = fields["tscorepwr"]
29
+ @planned_tscorepwr = fields.has_key?('scheduled')? fields['scheduled']["tscorepwr"] : nil
30
+ end
31
+
32
+
33
+ # Find all coaches athletes workouts
34
+ # +payload+:: they payload to send to in json format
35
+ # +offset+:: record count to start out
36
+ # +total+:: number of records to return
37
+ # +client+:: the authenticated client
38
+ # payload example:
39
+ # {
40
+ # "criteria": {
41
+ # "fromTs": 1451566800000,
42
+ # "toTs": 1496239200000,
43
+ # "isNull": [
44
+ # "fileId"
45
+ # ],
46
+ # "excludeWorkouts": [
47
+ # "rest"
48
+ # ]
49
+ # },
50
+ # "fields": [
51
+ # "scheduled.name",
52
+ # "scheduled.day",
53
+ # "workout",
54
+ # “state”,
55
+ # “reason”
56
+ # ],
57
+ # "opts": 1
58
+ #}
59
+ def self.all(payload,offset = 0, total = 100, client: nil)
60
+ Connector.new("/users/activities/search/#{offset}/#{total}/", client).
61
+ post(payload)['result']['results'].map do |data|
62
+ new(data)
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,43 @@
1
+ module TodaysPlan
2
+ # Uses the TodaysPlan API for workouts and athlete information
3
+
4
+ class Athlete
5
+
6
+ attr_reader :id
7
+ attr_reader :name
8
+ attr_reader :first_name
9
+ attr_reader :last_name
10
+ attr_reader :timezone
11
+ attr_reader :coach
12
+ attr_reader :email
13
+ attr_reader :dob
14
+
15
+
16
+ def initialize(fields)
17
+ @id = fields['id'].to_i
18
+ @name = fields['_name']
19
+ @first_name = fields['firstname']
20
+ @last_name = fields['lastname']
21
+ @timezone = fields['timezone']
22
+ @coach = fields['coach']["id"].to_i if fields.has_key?("coach")
23
+ @email = fields['email']
24
+ @dob = Time.at(fields['dob'].to_i/1000)
25
+ end
26
+
27
+ # Find a single athlete
28
+ # +client+:: the authenticated client
29
+ # +id+:: the athlete id
30
+ # +options+:: has of options
31
+ def self.find(id)
32
+ all().find{|a| a.id==id}
33
+ end
34
+
35
+ # Find all the coaches athletes
36
+ # +client+:: the authenticated client
37
+ def self.all(client = nil)
38
+ Connector.new('/users/delegates/users', client).get.map do |data|
39
+ new(data)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module TodaysPlan
2
+ class Client
3
+
4
+ attr_reader :token
5
+
6
+ def initialize(username,password)
7
+ RestClient.log=TodaysPlan.logger
8
+ @token = authenticate(username,password)
9
+ end
10
+
11
+
12
+ def logout
13
+ url = TodaysPlan.endpoint+"/auth/logout"
14
+ response= RestClient.get(url, {:Authorization => "Bearer #{token}"})
15
+ response.body
16
+ end
17
+
18
+ private
19
+ def authenticate(username,password)
20
+ payload= {'username'=>username,"password"=>password, "token"=> true}
21
+ url = TodaysPlan.endpoint+"/auth/login"
22
+ response= RestClient.post(url, payload.to_json,{content_type: :json, accept: :json} )
23
+ body = JSON.parse(response.body)
24
+ body['token']
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ module TodaysPlan
2
+ class Connector
3
+ attr_reader :uri, :response, :body, :timeout, :client
4
+
5
+ def initialize(path, client = nil)
6
+ @client = client || Client.new(TodaysPlan.username, TodaysPlan.password)
7
+ @uri = TodaysPlan.endpoint + path
8
+ @timeout = TodaysPlan.timeout
9
+ end
10
+
11
+ # Internal: Run GET request.
12
+ #
13
+ # Returns the parsed JSON response body.
14
+ def get()
15
+ @response = RestClient::Request.execute(method: :get, url: @uri,
16
+ timeout: @timeout,
17
+ headers: {:Authorization => "Bearer #{@client.token}",accept: :json} )
18
+ puts "#{@response.body}" if TodaysPlan.debug
19
+ @body = JSON.parse(@response.body)
20
+ end
21
+
22
+ # Internal: Run GET request.
23
+ #
24
+ # Returns the parsed JSON response body.
25
+ def post(payload = {})
26
+ @response = RestClient::Request.execute(method: :post,
27
+ payload: payload, url: @uri,
28
+ timeout: @timeout,
29
+ headers:{:Authorization => "Bearer #{@client.token}",
30
+ content_type: :json,
31
+ accept: :json})
32
+ puts "#{@response.body}" if TodaysPlan.debug
33
+ @body = JSON.parse(@response.body)
34
+ @body
35
+ end
36
+
37
+
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module TodaysPlan
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,20 @@
1
+ {
2
+ "criteria": {
3
+ "fromTs": 1451566800000,
4
+ "toTs": 1496239200000,
5
+ "isNull": [
6
+ "fileId"
7
+ ],
8
+ "excludeWorkouts": [
9
+ "rest"
10
+ ]
11
+ },
12
+ "fields": [
13
+ "scheduled.name",
14
+ "scheduled.day",
15
+ "workout",
16
+ "state",
17
+ "reason"
18
+ ],
19
+ "opts": 1
20
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "cnt": 1.0,
3
+ "offset": 0.0,
4
+ "result": {
5
+ "fields": {},
6
+ "results": [
7
+ {
8
+ "id": 60390.0,
9
+ "scheduled": {
10
+ "name": "Efforts",
11
+ "day": 1
12
+ },
13
+ "source": "file",
14
+ "user": {
15
+ "id": 64.0,
16
+ "dob": 4.853952E11,
17
+ "firstname": "Joe",
18
+ "lastname": "Athlete",
19
+ "isPremium": true
20
+ },
21
+ "name": "Efforts",
22
+ "workoutId": 46.0,
23
+ "activityId": 46.0,
24
+ "activity": "workouts",
25
+ "ts": 1.4786712E12,
26
+ "tz": "Europe/Madrid",
27
+ "type": "ride",
28
+ "equipment": "road",
29
+ "workout": "training",
30
+ "permissions": [
31
+ "edit1",
32
+ "del",
33
+ "edit2",
34
+ "view2",
35
+ "view1",
36
+ "view3",
37
+ "edit3"
38
+ ]
39
+ }
40
+ ],
41
+ "keys": []
42
+ }
43
+ }
@@ -0,0 +1,25 @@
1
+ [{
2
+ "_name": "Joe Athlete",
3
+ "id": 7187033,
4
+ "email": "joeathlete@example.com",
5
+ "md5": "df1cc92fbe295b86fdceb070d5458e84",
6
+ "dob": 315532800000,
7
+ "_dob-display": "1/1/80",
8
+ "firstname": "Joe",
9
+ "lastname": "Athlete",
10
+ "timezone": "US/Mountain",
11
+ "relationship": "coach",
12
+ "_relationship-display": "Coach",
13
+ "isPremium": false,
14
+ "hasPwr": false,
15
+ "hasBpm": false,
16
+ "lastLogin": 1478439753344,
17
+ "_lastLogin-display": "11/6/16 6:42 AM",
18
+ "coach": {
19
+ "id": 7187033
20
+ },
21
+ "schedulePref": "prefer_single",
22
+ "_schedulePref-display": "Single sessions",
23
+ "roles": ["pi", "user"],
24
+ "attsMask": 514
25
+ }]
@@ -0,0 +1,31 @@
1
+ require 'shoulda-matchers'
2
+ require 'todays_plan'
3
+ require 'webmock/rspec'
4
+
5
+ CONFIG={}
6
+ if File.exists?('todays_plan.yml')
7
+ CONFIG=YAML.load(ERB.new(File.read("todays_plan.yml")).result)
8
+ end
9
+
10
+ TodaysPlan.configure do |config|
11
+ config.username = CONFIG["username"] ||= 'id'
12
+ config.password = CONFIG["password"] ||= 'secret'
13
+ #config.endpoint = 'https://whats.todaysplan.com.au/rest/'
14
+ config.timeout = CONFIG["timeout"] ||= 120
15
+ config.logger = CONFIG["logger"] ||= nil
16
+ config.debug = CONFIG["debug"] ||= false
17
+ end
18
+
19
+ WebMock.allow_net_connect! if TodaysPlan.debug
20
+
21
+ RSpec.configure do |config|
22
+ config.mock_with :rspec
23
+ config.order = "random"
24
+ config.before {
25
+ stub_request(:post, TodaysPlan.endpoint+'/auth/login').
26
+ with(:body => "{\"username\":\"#{TodaysPlan.username}\",\"password\":\"#{TodaysPlan.password}\",\"token\":true}",
27
+ :headers => {'Accept'=>'application/json',
28
+ 'Content-Type'=>'application/json', }).
29
+ to_return(:status => 200, :body => '{"token":"abc-123"}', :headers => {}) unless TodaysPlan.debug
30
+ }
31
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe TodaysPlan::Activity do
4
+
5
+
6
+ #let(:json){File.read("spec/fixtures/activities/incomplete.json")}
7
+ let(:json){'{"criteria":{"fromTs":1482735600000.0,"toTs":1486364400000.0,"userIds":[7184519]},"fields":["scheduled.name","scheduled.day","workout","state","reason"],"opts":1}'}
8
+ let(:response){File.read("spec/fixtures/activities/incomplete_response.json")}
9
+
10
+ it "expect to get incomplete activites" do
11
+
12
+ stub_request(:post, "#{TodaysPlan.endpoint}/users/activities/search/0/100/").
13
+ with(:body => json,
14
+ :headers => {'Accept'=>'application/json',
15
+ 'Authorization'=>'Bearer abc-123',
16
+ 'Content-Type'=>'application/json', }).
17
+ to_return(:status => 200, :body => response, :headers => {})
18
+ all = TodaysPlan::Activity.all(json,0,100)
19
+ expect(all).to be_a(Array)
20
+ expect(all[0]).to be_a(TodaysPlan::Activity)
21
+ end
22
+
23
+ end