todaysplan 0.1.0

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.
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