rescuetime 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26e48ef7046be4852886e8a2f7854e4b328002d3
4
- data.tar.gz: 1e7201de9e2011839b55755a52e0e0f3224cb710
3
+ metadata.gz: 9e7a807f2b3aaf12d096fc4547b5990436cc661c
4
+ data.tar.gz: ef7cc81293795d19b560d028f51b752c85038261
5
5
  SHA512:
6
- metadata.gz: 4ee47235b7780e6bbd9e54673722c9a6e44eaadc1eddc2621002943641648bf2fc275deca712c5696435e40025f2e4755a89301c0d07d3b8d47ef08b885f9f4b
7
- data.tar.gz: 59127d45d97eb47d2ff464410835d724e64be87867f9500e0a847abb40187432369dbe3c6aa5c56dd8d6e05fa00e18fd92ed68afb2b138de234bda2ae5c908a6
6
+ metadata.gz: 55a8baa378221aef8445cac82fa16240fdfaf10dc296acb4f63316e13a9d4c02cfad3489627dcdc452e4e4553d79afe9b1fb7c44eecb75f8dc083a6227c5dbf3
7
+ data.tar.gz: 75df0025d9269632ae9659eb7f4dabac97b6047a248ba86cc11cf079ad09ad222bfc5c22b3904ea2f9c58adf394a6a633fe878e35665765157f715e3f2f867db
data/.travis.yml CHANGED
@@ -2,7 +2,6 @@ cache: bundler
2
2
 
3
3
  language: ruby
4
4
  rvm:
5
- - 1.9.3
6
5
  - 2.0.0
7
6
  - 2.1
8
7
  - 2.2
data/CONTRIBUTING.md CHANGED
@@ -29,13 +29,7 @@ You should be able to run the test specs now:
29
29
  bundle exec rake
30
30
  ```
31
31
 
32
- Some great ways to contribute include:
33
- - fixing [bugs](https://github.com/leesharma/rescuetime/issues?q=is%3Aopen+is%3Aissue+-label%3A%22in+progress%22+label%3Abug)
34
- - adding [features listed in our current milestone](https://github.com/leesharma/rescuetime/issues?q=is%3Aopen+is%3Aissue+-label%3A%22in+progress%22) (filter by milestone)
35
- - adding/improving documentation, comments, etc.
36
- - refactoring existing code
37
-
38
- Check our [issue tracker](https://github.com/leesharma/rescuetime/issues) for more ideas.
32
+ This is a very small project, so if you are interested in contributing, it will be easiest if you contact me first.
39
33
 
40
34
  ## Standards
41
35
 
@@ -43,6 +37,13 @@ rescuetime uses RSpec for testing along with VCR and WebMock for mocking HTTP re
43
37
 
44
38
  Be careful not to commit sensitive information (API keys, OAuth credentials, etc.) to public repositories!
45
39
 
40
+ ### Comment Tags
41
+
42
+ * TODO
43
+ * FIXME
44
+ * OPTIMIZE
45
+ * REVIEW
46
+
46
47
  ## Questions
47
48
 
48
- Have any questions? Feel free to send me (@leesharma) a message!
49
+ Have any questions? Post on the github repo at leesharma/rescuetime.
data/README.md CHANGED
@@ -20,7 +20,8 @@ For more information about RescueTime, visit [the RescueTime homepage](https://w
20
20
  * [Installation](#installation)
21
21
  * [Usage](#usage)
22
22
  * [Prerequisites](#prerequisites)
23
- * [Getting Started](#getting-started)
23
+ * [In a Nutshell](#in-a-nutshell) (skip to here if you want to see the syntax)
24
+ * [Finding Answers (Documentation)](#finding-answers-documentation)
24
25
  * [Defaults](#defaults)
25
26
  * [Rescuetime Exceptions](#rescuetime-exceptions)
26
27
  * [Development](https://github.com/leesharma/rescuetime/wiki/Development) ([section](#development))
@@ -54,9 +55,7 @@ Ensure that you are using a [supported ruby version](https://github.com/leesharm
54
55
 
55
56
  In order to use access your RescueTime data, you will need an API key. If you do not already have a key, visit the [API key management page](https://www.rescuetime.com/anapi/manage).
56
57
 
57
- ### Getting Started
58
-
59
- Using the rescuetime gem is simple. Here is some example code using the rescuetime gem (a full feature list can be found [here](https://github.com/leesharma/rescuetime/wiki#full-specs):
58
+ ### In a Nutshell
60
59
 
61
60
  ```ruby
62
61
  require 'rescuetime'
@@ -65,19 +64,88 @@ require 'rescuetime'
65
64
  @client.api_key? #=> true
66
65
  @client.valid_credentials? #=> true
67
66
 
68
- @client.activities # Returns a list of activities, ordered by "rank"
69
- @client.productivity # Returns a productivity report
70
- @client.efficiency # Returns an efficiency report, ordered by "time"
71
-
72
- @client.activities.class # => Array
73
- @client.activities[0].class # => Hash
74
-
75
- @client.efficiency( from: '2015-03-20', # returns weekly efficiency report between March 20th and
76
- to: '2015-04-20' , # April 20th of 2015 by member in csv format
77
- interval: 'week',
78
- format: 'csv' )
67
+ # Rescuetime uses lazy evaluation, so until you either manipulate the collection
68
+ # or explicitly call for it (with #all), it will remain in the Rescuetime::Collection
69
+ # format.
70
+ @client.overview.class #=> Rescuetime::Collection
71
+ @client.overview.all.class #=> Array
72
+ @client.overview.map {...} #=> Array
73
+
74
+ @client.overview # Returns an overview report, defaulting to "rank" order
75
+ @client.categories # Returns a catigorical report, defaulting to "rank" order
76
+ @client.activities # Returns a list of activities, defaulting to "rank" order
77
+ @client.productivity # Returns a productivity report, defaulting to "rank" order
78
+ @client.efficiency # Returns an efficiency report, defaulting to "time order"
79
+
80
+ ##
81
+ # Date Range (:date, :frome, :to)
82
+ # -------------------------------
83
+ # Defaults:
84
+ # If nothing is provided, defaults to current day (since 00:00)
85
+ # If :from is provided, defaults :to to current day
86
+ #
87
+ # Valid date formats:
88
+ # - "YYYY-MM-DD" - "MM-DD-YYYY" - "DD/MM"
89
+ # - Object#strftime
90
+ @client.overview # Fetches results from today
91
+ @client.overview.date('2014-12-31') # Fetches results from Dec 31, 2014.
92
+ @client.overview.from('2015-01-01').to('2015-02-01')
93
+ @client.overview.from('2015-04-01')
94
+
95
+
96
+ ##
97
+ # Report Order (:order_by)
98
+ # ------------------------
99
+ # Defaults:
100
+ # Efficiency defaults to chronological order; everything else defaults to "rank" order
101
+ #
102
+ # You can order_by:
103
+ # :rank, :time, or :member (note: efficiency can't be sorted by :rank)
104
+ #
105
+ # When ordering by time, default interval is 1 hour.
106
+ # Options include:
107
+ # :minute (5-minute chunks), :hour, :day, :week, :month
108
+ @client.efficiency # Defaults to :time
109
+ @client.productivity # Defaults to :rank
110
+
111
+ @client.productivity.order_by(:rank)
112
+ @client.productivity.order_by(:time)
113
+ @client.productivity.order_by(:member)
114
+
115
+ @client.productivity.order_by(:time) # Defaults to :hour
116
+ @client.productivity.order_by(:time, interval: :minute)
117
+ @client.productivity.order_by(:time, interval: :hour)
118
+ @client.productivity.order_by(:time, interval: :day)
119
+ @client.productivity.order_by(:time, interval: :week)
120
+ @client.productivity.order_by(:time, interval: :month)
121
+
122
+ ##
123
+ # Name Restrictions (:where)
124
+ # --------------------------
125
+ # Fetches results where name is an exact match
126
+ # The following reports can be limited by name:
127
+ # :activities, :categories, :overview
128
+ #
129
+ # For activities, you can also limit by specific document title
130
+ # (ex. document 'rails/rails' for activity 'github.com')
131
+ # Try the query sans document for a list of valid options
132
+ #
133
+ # Names must be exact matches.
134
+ @client.activities.where(name: 'github.com')
135
+ @client.categories.where(name: 'Intelligence')
136
+ @client.overview.where(name: 'Utilities')
137
+ @client.activities.where(name: 'github.com', document: 'vcr/vcr')
138
+
139
+ ##
140
+ # Formatting options (:csv, :array)
141
+ # ---------------------------------
142
+ @client.efficiency # Default return type is Array<Hash>
143
+ @client.efficiency.format(:cvs) # Returns a CSV
144
+ @client.efficiency.format(:array) # Returns Array<Hash>
79
145
  ```
80
146
 
147
+ ### Finding Answers (Documentation)
148
+
81
149
  For more details, please see [official gem documentation](http://www.rubydoc.info/gems/rescuetime/0.1.0) or [read the wiki](https://github.com/leesharma/rescuetime/wiki).
82
150
 
83
151
  ### Defaults
@@ -86,10 +154,9 @@ The `Rescuetime::Client#activities` action has the following defaults:
86
154
 
87
155
  ```ruby
88
156
 
89
- { by: 'rank'
90
- time_interval: 'hour'
91
- date: <TODAY>
92
- detail: 'activity' }
157
+ { order_by: 'rank'
158
+ interval: 'hour'
159
+ date: <TODAY> }
93
160
 
94
161
  ```
95
162
 
@@ -97,8 +164,25 @@ The `Rescuetime::Client#activities` action has the following defaults:
97
164
 
98
165
  There are a number of exceptions that extend from the custom Rescuetime::Error class:
99
166
 
100
- * **Rescuetime::MissingCredentials** is raised when a request is attempted by a client with no credentials. Try setting credentials with `@client.api_key=<YOUR_API_KEY>`.
101
- * **Rescuetime::InvalidCredentials** is raised when a request is attempted by a client with invalid credentials. Double-check your API key and fix your client with `@client.api_key=<VALID_API_KEY>`.
167
+ * * **Rescuetime::MissingCredentialsError** is raised when a request is attempted by a client with no credentials. Try setting credentials with `@client.api_key = <YOUR_API_KEY>`.
168
+ * **Rescuetime::InvalidCredentialsError** is raised when a request is attempted by a client with invalid credentials. Double-check your API key and fix your client with `@client.api_key = <VALID_API_KEY>`.
169
+ * **Rescuetime::InvalidQueryError** is raised if you enter an invalid value for any of the RescueTime query methods (or if the server returns a bad query error)
170
+ * **Rescuetime::InvalidFormatError** is raised if you pass a disallowed format to the client
171
+ * HTTP Response Errors:
172
+ * **4xx => Rescuetime:: ClientError**
173
+ * 400 => Rescuetime::BadRequest
174
+ * 401 => Rescuetime::Unauthorized
175
+ * 403 => Rescuetime::Forbidden
176
+ * 404 => Rescuetime::NotFound
177
+ * 406 => Rescuetime::NotAcceptable
178
+ * 422 => Rescuetime::UnprocessableEntity
179
+ * 429 => Rescuetime::TooManyRequests
180
+ * **5xx => Rescuetime:: ServerError**
181
+ * 500 => Rescuetime::InternalServerError
182
+ * 501 => Rescuetime::NotImplemented
183
+ * 502 => Rescuetime::BadGateway
184
+ * 503 => Rescuetime::ServiceUnavailable
185
+ * 504 => Rescuetime::GatewayTimeout
102
186
 
103
187
  ## Development
104
188
 
data/Rakefile CHANGED
@@ -8,4 +8,4 @@ RSpec::Core::RakeTask.new(:spec)
8
8
  task test: :spec
9
9
 
10
10
  desc 'Default: run specs'
11
- task default: :spec
11
+ task default: :spec
data/lib/rescuetime.rb CHANGED
@@ -2,6 +2,7 @@ require 'rescuetime/version'
2
2
  require 'rescuetime/client'
3
3
  require 'rescuetime/errors'
4
4
 
5
+ # lib/rescuetime.rb
5
6
  module Rescuetime
6
7
  # Wrapper module for rescuetime gem
7
8
  #
@@ -1,7 +1,10 @@
1
1
  require 'faraday'
2
2
  require 'csv'
3
3
 
4
- require 'rescuetime/api'
4
+ require 'rescuetime/query_buildable'
5
+
6
+ require 'rescuetime/requester'
7
+ require 'rescuetime/collection'
5
8
 
6
9
  module Rescuetime
7
10
  # Rescuetime::Client makes HTTP requests to the RescueTime API and returns
@@ -9,117 +12,33 @@ module Rescuetime
9
12
  #
10
13
  # @since v0.1.0
11
14
  class Client
12
- include Rescuetime::Api
13
-
14
- # Default options passed in any request
15
- # @since v0.2.0
16
- DEFAULT_OPTIONS = {format: 'csv', version: 0, operation: 'select' }
15
+ include QueryBuildable
17
16
 
18
- # Overwrites the set RescueTime API key
19
- #
20
- # @!attribute [w] api_key
17
+ # @!attribute [rw] api_key
21
18
  # @since v0.1.0
22
- attr_writer :api_key
19
+ attr_accessor :api_key
23
20
 
24
- # Initializes a new Client object
25
- #
26
- # @example
27
- # @client = Rescuetime::Client.new # options must be set before requests can be made
28
- # @example
29
- # @client = Rescuetime::Client.new(api_key: '1234567890abcdefg')
30
- #
31
- # @param [Hash] options hash of RescueTime client options
32
- # @option options [String] :api_key RescueTime API key
33
- # @return [Rescuetime::Client]
34
21
  # @since v0.1.0
35
- def initialize(options={})
36
- @api_key = options[:api_key]
22
+ def initialize(key = nil, **opts)
23
+ @api_key = key || opts[:api_key]
37
24
  end
38
25
 
39
- # Checks whether an api key is set
40
- #
41
- # @return [Boolean]
42
26
  # @since v0.1.0
43
27
  def api_key?
44
- !!@api_key
28
+ !!@api_key && !@api_key.empty?
45
29
  end
46
30
 
47
- # Checks whether client credentials are valid. If credentials are present, this
48
- # action involves an HTTP request.
49
- #
50
- # @example Three cases of checking credentials (missing, invalid, and valid)
51
- # @client = Rescuetime::Client.new
52
- # @client.valid_credentials? # => false
53
- #
54
- # # Note: The following scenarios involve an HTTP request.
55
- # @client.api_key = 'Invalid Key'
56
- # @client.valid_credentials? # => false
57
- #
58
- # @client.api_key = VALID_API_KEY
59
- # @client.valid_credentials? # => true
60
- #
61
- # @return [Boolean]
62
31
  # @since v0.2.0
63
32
  def valid_credentials?
64
33
  return false unless api_key?
65
- !!self.activities rescue false
34
+ !!activities.all rescue false
66
35
  end
67
36
 
68
- protected
69
-
70
- # Performs an HTTP get request
71
- #
72
- # @param [String] url request url
73
- # @param [Hash] options query params for request
74
- #
75
- # @raise [Rescuetime::MissingCredentials] if the Rescuetime::Client has no set api key
76
- # @raise [Rescuetime::InvalidCredentials] if the provided api key is rejected by RescueTime
77
- # @since v0.2.0
78
- def get(url, options={})
79
- raise Rescuetime::MissingCredentials unless api_key?
80
- response = Faraday.get url, query_params(options).
81
- merge(DEFAULT_OPTIONS).
82
- merge({key: @api_key})
83
-
84
- invalid_credentials_body = '{"error":"# key not found","messages":"key not found"}'
85
- raise Rescuetime::InvalidCredentials if response.body == invalid_credentials_body
86
-
87
- response
88
- end
89
-
90
- # Takes client request options hash and returns correct key/value pairs for HTTP request
91
- #
92
- # @param [Hash] options options hash of client request
93
- # @return [Hash]
94
- # @since v0.2.0
95
- def query_params(options)
96
- params = {}
97
- params_mapping = { detail: :restrict_kind, by: :perspective, interval: :resolution_time }
98
-
99
- params_mapping.each do |local, server|
100
- params[server] = options[local] if options[local]
101
- end
102
-
103
- # Special Cases
104
- params[:perspective] = 'interval' if params[:perspective] == 'time'
105
- if options[:date]
106
- params[:restrict_begin] = date_string options[:date]
107
- params[:restrict_end] = params[:restrict_begin]
108
- end
109
- if options[:from]
110
- params[:restrict_begin] = date_string options[:from]
111
- params[:restrict_end] = date_string(options[:to] || Time.now)
112
- end
113
- params
114
- end
37
+ private
115
38
 
116
- # Takes a date in either "YYYY-MM-DD" format or as a Time object and
117
- # returns a date string in "YYYY-MM-DD" format
118
- #
119
- # @return [String]
120
- def date_string(date)
121
- return date if date.is_a? String
122
- date.strftime('%Y-%m-%d')
39
+ # @since v0.3.0
40
+ def state
41
+ { key: @api_key }
123
42
  end
124
43
  end
125
- end
44
+ end
@@ -0,0 +1,51 @@
1
+ require 'rescuetime/query_buildable'
2
+
3
+ module Rescuetime
4
+ class Collection
5
+ include QueryBuildable
6
+ include Enumerable
7
+
8
+ HOST = 'https://www.rescuetime.com/anapi/data'
9
+
10
+ def initialize(*terms)
11
+ @params = terms.reduce({}, :merge)
12
+ @format = :array
13
+ end
14
+
15
+ def <<(terms)
16
+ @params.merge! terms
17
+ end
18
+
19
+ def all
20
+ parse_response Requester.get(HOST, @params).body
21
+ end
22
+
23
+ def each(&block)
24
+ all.each &block
25
+ end
26
+
27
+ # TODO: Chainable to client
28
+ def format(format)
29
+ fail InvalidFormatError unless %w(array csv).include? format.to_s
30
+ @format = format.to_sym
31
+ self
32
+ end
33
+
34
+ private
35
+
36
+ # @since v0.1.0
37
+ def parse_response(body)
38
+ report = CSV.new(body,
39
+ headers: true,
40
+ header_converters: :symbol,
41
+ converters: :all)
42
+
43
+ case @format
44
+ when :array then report.to_a.map(&:to_hash)
45
+ when :csv then report
46
+ else
47
+ fail InvalidFormatError
48
+ end
49
+ end
50
+ end
51
+ end
@@ -2,9 +2,85 @@ module Rescuetime
2
2
  # Error class for rescuing all RescueTime errors
3
3
  class Error < StandardError; end
4
4
 
5
+ ##
6
+ # HTTP Errors
7
+ # ===========
8
+ # 4xx HTTP status code
9
+ class ClientError < Error; end
10
+ # HTTP status code 400
11
+ class BadRequest < ClientError; end
12
+ # HTTP status code 401
13
+ class Unauthorized < ClientError; end
14
+ # HTTP status code 403
15
+ class Forbidden < ClientError; end
16
+ # HTTP status code 404
17
+ class NotFound < ClientError; end
18
+ # HTTP status code 406
19
+ class NotAcceptable < ClientError; end
20
+ # HTTP status code 422
21
+ class UnprocessableEntity < ClientError; end
22
+ # HTTP status code 429
23
+ class TooManyRequests < ClientError; end
24
+
25
+ # 5xx HTTP status code
26
+ class ServerError < Error; end
27
+ # HTTP status code 500
28
+ class InternalServerError < ServerError; end
29
+ # HTTP status code 501
30
+ class NotImplemented < ServerError; end
31
+ # HTTP status code 502
32
+ class BadGateway < ServerError; end
33
+ # HTTP status code 503
34
+ class ServiceUnavailable < ServerError; end
35
+ # HTTP status code 504
36
+ class GatewayTimeout < ServerError; end
37
+
38
+ ##
39
+ # Custom Errors
40
+ # =============
41
+ # class CredentialsError < Error; end
5
42
  # Raised when a method requires credentials but none are provided
6
- class MissingCredentials < Error; end
43
+ class MissingCredentialsError < Unauthorized
44
+ def initialize(msg = 'No API key provided. Please provide a valid key.')
45
+ super
46
+ end
47
+ end
48
+
49
+ # Raised when a method requires credentials but credentials are invalid
50
+ class InvalidCredentialsError < Unauthorized
51
+ def initialize(msg = 'API key is invalid. Please provide a valid key.')
52
+ super
53
+ end
54
+ end
55
+
56
+ # Raised when a user-submitted query value is invalid
57
+ class InvalidQueryError < BadRequest
58
+ def initialize(msg = 'Likely a badly formatted or missing parameter')
59
+ super
60
+ end
61
+ end
62
+
63
+ # Raised when a user-submitted query value is invalid
64
+ class InvalidFormatError < Error
65
+ def initialize(msg = 'Invalid format. Please see docs for allowed formats.')
66
+ super
67
+ end
68
+ end
7
69
 
8
- # Raised when a method requires credentials but provided credentials are invalid
9
- class InvalidCredentials < Error; end
10
- end
70
+ class Error
71
+ CODES = {
72
+ 400 => Rescuetime::BadRequest,
73
+ 401 => Rescuetime::Unauthorized,
74
+ 403 => Rescuetime::Forbidden,
75
+ 404 => Rescuetime::NotFound,
76
+ 406 => Rescuetime::NotAcceptable,
77
+ 422 => Rescuetime::UnprocessableEntity,
78
+ 429 => Rescuetime::TooManyRequests,
79
+ 500 => Rescuetime::InternalServerError,
80
+ 501 => Rescuetime::NotImplemented,
81
+ 502 => Rescuetime::BadGateway,
82
+ 503 => Rescuetime::ServiceUnavailable,
83
+ 504 => Rescuetime::GatewayTimeout
84
+ }
85
+ end
86
+ end
@@ -0,0 +1,114 @@
1
+ module Rescuetime
2
+ # The Rescuetime::Reportable module contains client methods relating to
3
+ # generating requests to fetch reports on the /data endpoint of the
4
+ # Data Analytics API.
5
+ #
6
+ # @since v0.1.0
7
+ module QueryBuildable
8
+ BASE_PARAMS = { format: 'csv',
9
+ operation: 'select',
10
+ version: 0 }
11
+
12
+ # @return [Rescuetime::Collection]
13
+ # @since v0.3.0
14
+ def overview
15
+ add_to_query restrict_kind: 'overview'
16
+ end
17
+
18
+ # @return [Rescuetime::Collection]
19
+ # @since v0.3.0
20
+ def categories
21
+ add_to_query restrict_kind: 'category'
22
+ end
23
+
24
+ # @return [Rescuetime::Collection]
25
+ # @since v0.1.0
26
+ def activities
27
+ add_to_query restrict_kind: 'activity'
28
+ end
29
+
30
+ # @return [Rescuetime::Collection]
31
+ # @since v0.2.0
32
+ def productivity
33
+ add_to_query restrict_kind: 'productivity'
34
+ end
35
+
36
+ # @return [Rescuetime::Collection]
37
+ # @since v0.2.0
38
+ def efficiency
39
+ add_to_query restrict_kind: 'efficiency', perspective: 'interval'
40
+ end
41
+
42
+ # @return [Rescuetime::Collection]
43
+ # @since v0.3.0
44
+ def order_by(order, opts = {})
45
+ fail(InvalidQueryError, "#{order} is not a valid order") unless
46
+ VALID[:order_by].include? order
47
+ fail(InvalidQueryError, "#{opts[:interval]} is not a valid interval") if
48
+ opts[:interval] && !VALID[:interval].include?(opts[:interval])
49
+
50
+ add_to_query perspective: (order.to_s == 'time' ? 'interval' : order),
51
+ resolution_time: opts[:interval] || opts['interval']
52
+ end
53
+
54
+ # @return [Rescuetime::Collection]
55
+ # @since v0.3.0
56
+ def date(date)
57
+ add_to_query restrict_end: to_formatted_s(date),
58
+ restrict_begin: to_formatted_s(date)
59
+ end
60
+
61
+ # @return [Rescuetime::Collection]
62
+ # @since v0.3.0
63
+ def from(date)
64
+ add_to_query restrict_begin: to_formatted_s(date)
65
+ end
66
+
67
+ # @return [Rescuetime::Collection]
68
+ # @since v0.3.0
69
+ def to(date)
70
+ add_to_query restrict_end: to_formatted_s(date)
71
+ end
72
+
73
+ # @return [Rescuetime::Collection]
74
+ # @since v0.3.0
75
+ def where(options)
76
+ add_to_query restrict_thing: options[:name],
77
+ restrict_thingy: options[:document]
78
+ end
79
+
80
+ private
81
+
82
+ VALID = {
83
+ order_by: [:time, :rank, :member],
84
+ interval: [:minute, :hour, :day, :week, :month]
85
+ }
86
+
87
+ def add_to_query(**terms)
88
+ if self.is_a? Rescuetime::Collection
89
+ self << terms
90
+ self
91
+ else
92
+ Rescuetime::Collection.new(BASE_PARAMS, state, terms)
93
+ end
94
+ end
95
+
96
+ # @since v0.3.0
97
+ def to_formatted_s(date)
98
+ case
99
+ when date.respond_to?(:strftime) then date.strftime('%Y-%m-%d')
100
+ when date =~ /\d{4}-\d{2}-\d{2}/ then date
101
+ when date =~ %r{\d{4}\/\d{2}\/\d{2}} then date.gsub '/', '-'
102
+ when date =~ %r{\d{2}[-\/]\d{2}[-\/]\d{4}/}
103
+ date_chunks = date.scan(/\d+/)
104
+ "#{ date_chunks[2] }-#{ date_chunks[0] }-#{ date_chunks[1] }"
105
+ when date =~ %r{\d{2}[-\/]\d{2}}
106
+ date_chunks = date.scan(/\d+/)
107
+ "#{ Time.now.year }-#{ date_chunks[0] }-#{ date_chunks[1] }"
108
+ else
109
+ fail InvalidQueryError,
110
+ 'Invalid date entered. Please see docs for allowed formats.'
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,40 @@
1
+ module Rescuetime
2
+ # The Rescuetime::Requestable module contains client methods relating to
3
+ # sending HTTP requests
4
+ #
5
+ # @since v0.2.0
6
+ class Requester
7
+ class << self
8
+ # @since v0.2.0
9
+ def get(host, params)
10
+ fail(Rescuetime::MissingCredentialsError) unless
11
+ params[:key] && !params[:key].to_s.empty?
12
+
13
+ response = Faraday.get(host,
14
+ params.delete_if { |_, v| !v || v.to_s.empty? })
15
+
16
+ fail_or_return response
17
+ end
18
+
19
+ private
20
+
21
+ INVALID = {
22
+ key_not_found: '"error":"# key not found","messages":"key not found"',
23
+ query: '"error": "# query error",'\
24
+ '"messages": "Error: Likely a badly formatted or missing parameter"'
25
+ }
26
+
27
+ def fail_or_return(response)
28
+ fail Rescuetime::InvalidCredentialsError if
29
+ response.body =~ /#{INVALID[:key_not_found]}/
30
+ fail Rescuetime::InvalidQueryError if
31
+ response.body =~ /#{INVALID[:query]}/
32
+
33
+ error = Rescuetime::Error::CODES[response.status.to_i]
34
+ fail(error) if error
35
+
36
+ response
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  # Contains Rescuetime::VERSION, the gem version number
2
2
  module Rescuetime
3
3
  # rescuetime gem version number
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/rescuetime.gemspec CHANGED
@@ -9,17 +9,18 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Lee Sharma']
10
10
  spec.email = ['lee@leesharma.com']
11
11
 
12
- spec.summary = %q{Ruby interface for RescueTime}
13
- spec.description = %q{Ruby interface for the RescueTime Data Analytics API.}
12
+ spec.summary = %w(Ruby interface for RescueTime)
13
+ spec.description = %w(Ruby interface for the RescueTime Data Analytics API.)
14
14
  spec.homepage = 'https://github.com/leesharma/rescuetime'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
18
19
  spec.bindir = 'exe'
19
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
21
  spec.require_paths = ['lib']
21
22
 
22
- spec.required_ruby_version = '>= 1.9.3'
23
+ spec.required_ruby_version = '>= 2.0.0'
23
24
 
24
25
  spec.add_dependency 'faraday', '~> 0.9.1'
25
26
 
@@ -27,6 +28,7 @@ Gem::Specification.new do |spec|
27
28
  spec.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
28
29
 
29
30
  spec.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
31
+ spec.add_development_dependency 'rspec-its'
30
32
  spec.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3'
31
33
  spec.add_development_dependency 'webmock', '~> 1.21', '>= 1.21.0'
32
34
  end
metadata CHANGED
@@ -1,133 +1,148 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rescuetime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Sharma
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-23 00:00:00.000000000 Z
11
+ date: 2015-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.9.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.9.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '10.4'
48
- - - '>='
48
+ - - ">="
49
49
  - !ruby/object:Gem::Version
50
50
  version: 10.4.2
51
51
  type: :development
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
- - - ~>
55
+ - - "~>"
56
56
  - !ruby/object:Gem::Version
57
57
  version: '10.4'
58
- - - '>='
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: 10.4.2
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rspec
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ~>
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '3.2'
68
- - - '>='
68
+ - - ">="
69
69
  - !ruby/object:Gem::Version
70
70
  version: 3.2.0
71
71
  type: :development
72
72
  prerelease: false
73
73
  version_requirements: !ruby/object:Gem::Requirement
74
74
  requirements:
75
- - - ~>
75
+ - - "~>"
76
76
  - !ruby/object:Gem::Version
77
77
  version: '3.2'
78
- - - '>='
78
+ - - ">="
79
79
  - !ruby/object:Gem::Version
80
80
  version: 3.2.0
81
+ - !ruby/object:Gem::Dependency
82
+ name: rspec-its
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
81
95
  - !ruby/object:Gem::Dependency
82
96
  name: vcr
83
97
  requirement: !ruby/object:Gem::Requirement
84
98
  requirements:
85
- - - ~>
99
+ - - "~>"
86
100
  - !ruby/object:Gem::Version
87
101
  version: '2.9'
88
- - - '>='
102
+ - - ">="
89
103
  - !ruby/object:Gem::Version
90
104
  version: 2.9.3
91
105
  type: :development
92
106
  prerelease: false
93
107
  version_requirements: !ruby/object:Gem::Requirement
94
108
  requirements:
95
- - - ~>
109
+ - - "~>"
96
110
  - !ruby/object:Gem::Version
97
111
  version: '2.9'
98
- - - '>='
112
+ - - ">="
99
113
  - !ruby/object:Gem::Version
100
114
  version: 2.9.3
101
115
  - !ruby/object:Gem::Dependency
102
116
  name: webmock
103
117
  requirement: !ruby/object:Gem::Requirement
104
118
  requirements:
105
- - - ~>
119
+ - - "~>"
106
120
  - !ruby/object:Gem::Version
107
121
  version: '1.21'
108
- - - '>='
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: 1.21.0
111
125
  type: :development
112
126
  prerelease: false
113
127
  version_requirements: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - ~>
129
+ - - "~>"
116
130
  - !ruby/object:Gem::Version
117
131
  version: '1.21'
118
- - - '>='
132
+ - - ">="
119
133
  - !ruby/object:Gem::Version
120
134
  version: 1.21.0
121
- description: Ruby interface for the RescueTime Data Analytics API.
135
+ description: '["Ruby", "interface", "for", "the", "RescueTime", "Data", "Analytics",
136
+ "API."]'
122
137
  email:
123
138
  - lee@leesharma.com
124
139
  executables: []
125
140
  extensions: []
126
141
  extra_rdoc_files: []
127
142
  files:
128
- - .gitignore
129
- - .rspec
130
- - .travis.yml
143
+ - ".gitignore"
144
+ - ".rspec"
145
+ - ".travis.yml"
131
146
  - CHANGELOG.md
132
147
  - CODE_OF_CONDUCT.md
133
148
  - CONTRIBUTING.md
@@ -138,10 +153,11 @@ files:
138
153
  - bin/console
139
154
  - bin/setup
140
155
  - lib/rescuetime.rb
141
- - lib/rescuetime/activities.rb
142
- - lib/rescuetime/api.rb
143
156
  - lib/rescuetime/client.rb
157
+ - lib/rescuetime/collection.rb
144
158
  - lib/rescuetime/errors.rb
159
+ - lib/rescuetime/query_buildable.rb
160
+ - lib/rescuetime/requester.rb
145
161
  - lib/rescuetime/version.rb
146
162
  - rescuetime.gemspec
147
163
  homepage: https://github.com/leesharma/rescuetime
@@ -154,12 +170,12 @@ require_paths:
154
170
  - lib
155
171
  required_ruby_version: !ruby/object:Gem::Requirement
156
172
  requirements:
157
- - - '>='
173
+ - - ">="
158
174
  - !ruby/object:Gem::Version
159
- version: 1.9.3
175
+ version: 2.0.0
160
176
  required_rubygems_version: !ruby/object:Gem::Requirement
161
177
  requirements:
162
- - - '>='
178
+ - - ">="
163
179
  - !ruby/object:Gem::Version
164
180
  version: '0'
165
181
  requirements: []
@@ -167,5 +183,6 @@ rubyforge_project:
167
183
  rubygems_version: 2.4.5
168
184
  signing_key:
169
185
  specification_version: 4
170
- summary: Ruby interface for RescueTime
186
+ summary: '["Ruby", "interface", "for", "RescueTime"]'
171
187
  test_files: []
188
+ has_rdoc:
@@ -1,182 +0,0 @@
1
- module Rescuetime
2
- # The Rescuetime::Activities module contains client methods relating to user
3
- # activities on the /data endpoint of the Data Analytics API.
4
- #
5
- # @since v0.1.0
6
- module Activities
7
- # Base URL for RescueTime Data Analytics API endpoint
8
- # @since v0.1.0
9
- BASE_URL = 'https://www.rescuetime.com/anapi/data'
10
- # Map of numeric productivity levels to meaning
11
- # @since v0.2.0
12
- PRODUCTIVITY_LEVELS = { -2 => 'Very Unproductive',
13
- -1 => 'Unproductive',
14
- 0 => 'Neutral',
15
- 1 => 'Productive',
16
- 2 => 'Very Productive' }
17
-
18
- # Returns array of all activities.
19
- #
20
- # @example Basic behavior
21
- # @activities = @client.activities
22
- # @activities.class # => Array
23
- # @acitvities[0].class # => Hash
24
- #
25
- # @activities[0]
26
- # # => {
27
- # # :rank=>1,
28
- # # :time_spent_seconds=>5307,
29
- # # :number_of_people=>1,
30
- # # :activity=>"github.com",
31
- # # :category=>"General Software Development",
32
- # # :productivity=>2
33
- # # }
34
- #
35
- # @example Set level of detail
36
- # @client.activities(detail: 'overview')[0]
37
- # # => { :rank=>1, :time_spent_seconds=>13140, :number_of_people=>1, :category=>'Software Development' }
38
- #
39
- # @client.activities(detail: 'category')[0]
40
- # # => { :rank=>1, :time_spent_seconds=>5835, :number_of_people=>1, :category=>'Editing and IDEs' }
41
- #
42
- # @client.activities(detail: 'activity')[0]
43
- # # => { :rank=>1,
44
- # # :time_spent_seconds=>5835,
45
- # # :number_of_people=>1,
46
- # # :category=>'Editing and IDEs',
47
- # # :activity=>'RubyMine',
48
- # # :productivity=>2 }
49
- #
50
- # @client.activities(detail: 'productivity') # Equivalent to @client.productivity
51
- # @client.activities(detail: 'efficiency', by: 'time') # Equivalent to @client.efficiency
52
- #
53
- # @example Set perspective
54
- # @client.activities(by: 'rank') # Returns activities by rank (time spent per activity)
55
- # @client.activities(by: 'interval') # Returns activities chronologically
56
- # @client.activities(by: 'member') # Returns activities grouped by member
57
- #
58
- # @example Set date range (note: no time constraints are allowed)
59
- # @client.activities(date: '2014-05-06') # Returns report for May 6, 2014
60
- # @client.activities(from: '2014-05-06', to: '2014-06-06') # Returns report for May 6 to June 6, 2014
61
- # @client.activities(from: '2015-04-10') # Returns report for April 10, 2015 to today
62
- #
63
- # @example Set time interval
64
- # @client.efficiency( from: '2015-01-01', # Returns an efficiency report by
65
- # to: Time.now, # week from the Jan 1, 2015 through
66
- # interval: 'week') # today.
67
- #
68
- # @example Format
69
- # @client.activities # Returns array of hashes
70
- # @client.activities format: 'csv' # Returns Mime::CSV
71
- #
72
- # @param [Hash] options Query parameters to be passed to RescueTime
73
- # @option options [String] :detail
74
- # Restricts the level of detail of report returned
75
- # 1. 'overview': sums statistics for all activities into their top level category
76
- # 2. 'category': sums statistics for all activities into their sub category
77
- # 3. 'activity' (default): sums statistics for individual applications / web sites / activities
78
- # 4. 'productivity': productivity calculation (@see #productivity)
79
- # 5. 'efficiency': efficiency calculation (not applicable in "rank" perspective, @see #efficiency)
80
- # @option options [String] :by
81
- # Lets you set the perspective of your report
82
- # 1. 'rank' (default): returns a ranked report of activities by total time spent
83
- # 2. 'time': returns a chronological report of activities
84
- # 3. 'member': returns an activity report grouped by member
85
- # @option options [String] :date
86
- # Lets you set a single date for your report in a 'YYYY-MM-DD' format. Not
87
- # valid with :to or :from. Cannot be a future date.
88
- # @option options [String] :from
89
- # Lets you set a start date for your report in a 'YYYY-MM-DD' format. Valid
90
- # with the :to option or alone (:to defaults to current date). Cannot be a
91
- # future date or after the :to date (if supplied).
92
- # @option options [String] :to
93
- # Lets you set an end date for your report in a 'YYYY-MM-DD' format. If a
94
- # :to is supplied, :from must be supplied as well. Cannot be a future date or
95
- # before the :from date.
96
- # @option options [String] :interval
97
- # Lets you set the time interval for your report (ie. client.efficiency(interval:'day')
98
- # returns the efficiency report by day.) Possible values include:
99
- # 1. 'minute': returns data in 5-minute increments
100
- # 2. 'hour' (default): returns data in 1-hour increments
101
- # 3. 'day': returns data in 1-day increments
102
- # 4. 'week': returns data in 1-week increments
103
- # 5. 'month': returns data in 1-month increments
104
- # @option options [String] :format
105
- # Lets you specify a return type for your report
106
- # 1. default: returns an array of hashes with symbolized keys
107
- # 2. 'csv': returns a Mime::CSV object
108
- #
109
- # @return [Array<Hash>]
110
- #
111
- # @raise [Rescuetime::MissingCredentials] if the Rescuetime::Client has no set api key
112
- # @raise [Rescuetime::InvalidCredentials] if the provided api key is rejected by RescueTime
113
- # @since v0.1.0
114
- def activities(options={})
115
- response = self.get BASE_URL, options
116
-
117
- case options[:format]
118
- when 'csv' then CSV.new(response.body, headers: true)
119
- else array_of_hashes_from_csv(response.body)
120
- end
121
- end
122
-
123
- # Returns efficiency report. Equivalent to #activities(by: time, detail: efficiency)
124
- # @see #activities valid options
125
- #
126
- # @param [Hash] options options hash (same as #activities)
127
- # @return [Array<Hash>]
128
- def efficiency(options={})
129
- self.activities({by: 'time'}.merge(options.merge(detail: 'efficiency')))
130
- end
131
-
132
- # Returns productivity report. Equivalent to #activities(detail: productivity)
133
- # @see #activities valid options
134
- #
135
- # @example
136
- # @client.productivity # Equivalent to @client.activities(detail: 'productivity')
137
- # # => [
138
- # # { :rank=>1, :time_spent_seconds=>6956, :number_of_people=>1, :productivity=>2 },
139
- # # { :rank=>2, :time_spent_seconds=>2635, :number_of_people=>1, :productivity=>-2 },
140
- # # { :rank=>3, :time_spent_seconds=>2415, :number_of_people=>1, :productivity=>1 },
141
- # # { :rank=>4, :time_spent_seconds=>1210, :number_of_people=>1, :productivity=>0 },
142
- # # { :rank=>5, :time_spent_seconds=>93, :number_of_people=>1, :productivity=>-1 }
143
- # # ]
144
- #
145
- # @param [Hash] options options hash (same as #activities)
146
- # @return [Array<Hash>]
147
- def productivity(options={})
148
- self.activities(options.merge(detail: 'productivity'))
149
- end
150
-
151
- # Returns map of numeric productivity levels to meaning
152
- #
153
- # @example
154
- # @activity = @client.activities(restrict_kind: 'productivity')[0]
155
- # @primary_productivity = @client.productivity_levels[@activity[:productivity]]
156
- #
157
- # puts "I have spent most of my time being #{@primary_productivity}."
158
- # # => I have spent most of my time being Very Productive.
159
- #
160
- # @return [Hash] productivity levels, with integers from -2 to 2 as keys
161
- # @since v0.2.0
162
- def productivity_levels
163
- PRODUCTIVITY_LEVELS
164
- end
165
-
166
- private
167
-
168
- # Takes a CSV with headers and returns an array of hashes
169
- #
170
- # @param body[CSV] the original CSV file
171
- # @return [Array] an array of hashes, containing keys of the CSV headers
172
- # @since v0.1.0
173
- def array_of_hashes_from_csv(body)
174
- activities = CSV.new(body,
175
- headers: true,
176
- header_converters: :symbol,
177
- converters: :all)
178
-
179
- activities.to_a.map(&:to_hash)
180
- end
181
- end
182
- end
@@ -1,13 +0,0 @@
1
- require 'rescuetime/activities'
2
-
3
- module Rescuetime
4
- # The Rescuetime::API module includes all modules corresponding to RescueTime
5
- # API endpoints.
6
- #
7
- # For more information on the RescueTime Client, see #Rescuetime::Client.
8
- # For more information on included methods, see method summary below.
9
- # @since v0.1.0
10
- module Api
11
- include Rescuetime::Activities
12
- end
13
- end