tfl-toggl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.4.0
5
+ - 2.3.3
6
+ - 2.2.6
7
+ - 2.1.10
8
+ - 2.0.0-p648
9
+ script: bundle exec rspec --format documentation
10
+ cache: bundler
11
+ branches:
12
+ only:
13
+ - master
14
+ addons:
15
+ code_climate:
16
+ repo_token: eb44970f9efc497fb68c6ec1ce051dea530776b6a5bbe75e57734625f761ac86
@@ -0,0 +1,101 @@
1
+ # Change Log
2
+
3
+ Notable changes are documented here following conventions outlined at [Keep a CHANGELOG](http://keepachangelog.com/).
4
+
5
+ Changes that are not intended to affect usage (e.g. documentation, specs, removal of dead code, etc.) are generally not documented here.
6
+
7
+ Version numbers are meant to adhere to [Semantic Versioning](http://semver.org/).
8
+
9
+
10
+ ## [Unreleased]
11
+
12
+
13
+ ## [1.2.1] - 2016-07-25
14
+ ### Changed
15
+
16
+ * Fix ReportsV2#project by requiring project_id.
17
+ * Improve spec code coverage.
18
+ * Include version in `user_agent` and `created_with` (e.g. TogglV8 v1.2.1)
19
+
20
+
21
+ ## [1.2.0] - 2016-07-24
22
+ ### Added
23
+
24
+ * Add support for [Toggl Reports API v2](https://github.com/toggl/toggl_api_docs/blob/master/reports.md).
25
+
26
+
27
+ ## [1.1.0] - 2016-02-22
28
+ ### Added
29
+
30
+ * Add `tags(workspace_id)`.
31
+
32
+
33
+ ## [1.0.5] - 2016-02-22
34
+ ### Added
35
+
36
+ * Add specs for encoding of ISO8601 times with + UTC offset. (See [1.0.4](#104---2016-01-22))
37
+
38
+
39
+ ## [1.0.4] - 2016-01-22
40
+ ### Fixed
41
+
42
+ * Manually encode `+` to `%2B` before every API call. (Fixes #11)
43
+
44
+
45
+ ## [1.0.3] - 2016-01-22
46
+ ### Added
47
+
48
+ * Add `debug()` method to enable debugging output including full API response.
49
+
50
+ ## [1.0.2] - 2015-12-12
51
+ ### Changed
52
+
53
+ * Require params 'tags' and 'tag_action' in `update_time_entries_tags()`.
54
+
55
+ ## [1.0.1] - 2015-12-10
56
+ ### Fixed
57
+
58
+ * Fix Toggl API call in `get_project_tasks()`. (Fixes #5)
59
+
60
+ ### Added
61
+
62
+ * Add `my_tasks()`.
63
+ * Add null checks to various methods.
64
+
65
+ ### Changed
66
+
67
+ * Require params 'name' and 'pid' in `create_task()`.
68
+
69
+ ## [1.0.0] - 2015-12-06
70
+ ### Added
71
+
72
+ * Add `my_deleted_projects()`.
73
+
74
+ ### Changed
75
+
76
+ * Exclude deleted projects from `my_projects()` results.
77
+ * Change `get_time_entries()` parameters.
78
+ - old: `start_timestamp=nil, end_timestamp=nil`
79
+ - new: `dates = {}`
80
+ * Raise RuntimeError w/ HTTP Status code if request is not successful.
81
+ * Handle 429 (Too Many Requests) by pausing for 1 second and retrying up to 3 times.
82
+ - API calls are limited to 1/sec due to toggl.com limits
83
+ * Refactor duplication out of GET/POST/PUT/DELETE API calls.
84
+
85
+ ## [0.2.0] - 2015-08-21
86
+ ### Added
87
+
88
+ * Add Ruby interface to most functions of [Toggl V8 API](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) (as of 2015-08-21).
89
+
90
+
91
+ [Unreleased]: https://github.com/kanet77/togglv8/compare/v1.2.1...HEAD
92
+ [1.2.1]: https://github.com/kanet77/togglv8/compare/v1.2.0...v1.2.1
93
+ [1.2.0]: https://github.com/kanet77/togglv8/compare/v1.1.0...v1.2.0
94
+ [1.1.0]: https://github.com/kanet77/togglv8/compare/v1.0.5...v1.1.0
95
+ [1.0.5]: https://github.com/kanet77/togglv8/compare/v1.0.4...v1.0.5
96
+ [1.0.4]: https://github.com/kanet77/togglv8/compare/v1.0.3...v1.0.4
97
+ [1.0.3]: https://github.com/kanet77/togglv8/compare/v1.0.2...v1.0.3
98
+ [1.0.2]: https://github.com/kanet77/togglv8/compare/v1.0.1...v1.0.2
99
+ [1.0.1]: https://github.com/kanet77/togglv8/compare/v1.0.0...v1.0.1
100
+ [1.0.0]: https://github.com/kanet77/togglv8/compare/v0.2.0...v1.0.0
101
+ [0.2.0]: https://github.com/kanet77/togglv8/compare/a1d5cc5...v0.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in togglv8.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013-2015 Tom Kane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,160 @@
1
+
2
+ # Toggl API v8
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/togglv8.svg)](https://badge.fury.io/rb/togglv8) [![Build Status](https://api.travis-ci.org/kanet77/togglv8.svg "Build Status")](https://travis-ci.org/kanet77/togglv8) [![Coverage Status](https://coveralls.io/repos/kanet77/togglv8/badge.svg?branch=master&service=github)](https://coveralls.io/github/kanet77/togglv8?branch=master) [![Code Climate](https://codeclimate.com/github/kanet77/togglv8/badges/gpa.svg)](https://codeclimate.com/github/kanet77/togglv8)
5
+
6
+ [Toggl](http://www.toggl.com) is a time tracking tool.
7
+
8
+ [togglv8](/) is a Ruby Wrapper for [Toggl API v8](https://github.com/toggl/toggl_api_docs). It is designed to mirror the Toggl API as closely as possible.
9
+
10
+ togglv8 supports both [Toggl API](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) and [Reports API](https://github.com/toggl/toggl_api_docs/blob/master/reports.md)
11
+
12
+ ## Change Log
13
+
14
+ See [CHANGELOG](CHANGELOG.md) for a summary of notable changes in each version.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'togglv8'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install togglv8
31
+
32
+ ## Initialization
33
+
34
+ ### TogglV8::API
35
+
36
+ TogglV8::API communicates with [Toggl API v8](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) and can be initialized in one of three ways.
37
+
38
+ ```ruby
39
+ TogglV8::API.new # reads API token from file ~/.toggl
40
+ TogglV8::API.new(api_token) # explicit API token
41
+ TogglV8::API.new(email, password) # email & password
42
+ ```
43
+
44
+ ### TogglV8::ReportsV2
45
+
46
+ TogglV8::ReportsV2 communicates with [Toggl Reports API v2](https://github.com/toggl/toggl_api_docs/blob/master/reports.md) and can be initialized in one of three ways. Toggl.com requires authentication with an API token for Reports API v2.
47
+
48
+ ```ruby
49
+ TogglV8::ReportsV2.new # reads API token from file ~/.toggl
50
+ TogglV8::ReportsV2.new(toggl_api_file: toggl_file) # reads API token from toggl_file
51
+ TogglV8::ReportsV2.new(api_token: api_token) # explicit API token
52
+ ```
53
+
54
+ **Note:** `workspace_id` must be set in order to generate reports.
55
+
56
+ ```ruby
57
+ toggl = TogglV8::API.new
58
+ reports = TogglV8::ReportsV2.new
59
+ reports.workspace_id = toggl.workspaces.first['id']
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ This short example shows one way to create a time entry for the first workspace of the user identified by `<API_TOKEN>`. It then generates various reports containing that time entry.
65
+
66
+ ```ruby
67
+ require 'togglv8'
68
+ require 'json'
69
+
70
+ toggl_api = TogglV8::API.new(<API_TOKEN>)
71
+ user = toggl_api.me(all=true)
72
+ workspaces = toggl_api.my_workspaces(user)
73
+ workspace_id = workspaces.first['id']
74
+ time_entry = toggl_api.create_time_entry({
75
+ 'description' => "My awesome workspace time entry",
76
+ 'wid' => workspace_id,
77
+ 'duration' => 1200,
78
+ 'start' => toggl_api.iso8601((Time.now - 3600).to_datetime),
79
+ 'created_with' => "My awesome Ruby application"
80
+ })
81
+
82
+ begin
83
+ reports = TogglV8::ReportsV2.new(api_token: <API_TOKEN>)
84
+ begin
85
+ reports.summary
86
+ rescue Exception => e
87
+ puts e.message # workspace_id is required
88
+ end
89
+ reports.workspace_id = workspace_id
90
+ summary = reports.summary
91
+ puts "Generating summary JSON..."
92
+ puts JSON.pretty_generate(summary)
93
+ puts "Generating summary PDF..."
94
+ reports.write_summary('toggl_summary.pdf')
95
+ puts "Generating weekly CSV..."
96
+ reports.write_weekly('toggl_weekly.csv')
97
+ puts "Generating details XLS..."
98
+ reports.write_details('toggl_details.xls')
99
+ # Note: toggl.com does not generate Weekly XLS report (as of 2016-07-24)
100
+ ensure
101
+ toggl_api.delete_time_entry(time_entry['id'])
102
+ end
103
+ ```
104
+
105
+ See specs for more examples.
106
+
107
+ **Note:** Requests are rate-limited. The togglv8 gem will handle a 429 response by pausing for 1 second and trying again, for up to 3 attempts. See [Toggl API docs](https://github.com/toggl/toggl_api_docs#the-api-format):
108
+
109
+ > For rate limiting we have implemented a Leaky bucket. When a limit has been hit the request will get a HTTP 429 response and it's the task of the client to sleep/wait until bucket is empty. Limits will and can change during time, but a safe window will be 1 request per second. Limiting is applied per api token per IP, meaning two users from the same IP will get their rate allocated separately.
110
+
111
+ ## Debugging
112
+
113
+ The `TogglV8::API#debug` method determines if debug output is printed to STDOUT. This code snippet demonstrates the debug output.
114
+
115
+ ```ruby
116
+ require 'togglv8'
117
+
118
+ toggl = TogglV8::API.new
119
+
120
+ toggl.debug(true) # or simply toggl.debug
121
+ user1 = toggl.me
122
+ puts "user: #{user1['fullname']}, debug: true"
123
+
124
+ puts '-'*80
125
+
126
+ toggl.debug(false)
127
+ user2 = toggl.me
128
+ puts "user: #{user2['fullname']}, debug: false"
129
+ ```
130
+
131
+ ## Documentation
132
+
133
+ Run `rdoc` to generate documentation. Open `doc/index.html` in your browser.
134
+
135
+ Also available on [DocumentUp](https://documentup.com/kanet77/togglv8)
136
+
137
+ ## Acknowledgements
138
+
139
+ - Thanks to the following contributors (in alphabetical order):
140
+ * [archonic](https://github.com/archonic) ([fork](https://github.com/archonic/togglv8))
141
+ * [ddiatmb](https://github.com/ddiatmb) ([fork](https://github.com/ddiatmb/togglv8))
142
+ * [itaymendel](https://github.com/itaymendel)
143
+ * [ppawlikmb](https://github.com/ppawlikmb) ([fork](https://github.com/ppawlikmb/togglv8))
144
+ * [worldsmithroy](https://github.com/worldsmithroy) ([fork](https://github.com/worldsmithroy/togglv8))
145
+ - Thanks to [Koen Van der Auwera](https://github.com/atog) for the [Ruby Wrapper for Toggl API v6](https://github.com/atog/toggl)
146
+ - Thanks to the Toggl team for exposing the API.
147
+
148
+ ## Contributing
149
+
150
+ 1. Fork it ( https://github.com/kanet77/togglv8/fork )
151
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
152
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
153
+ 4. Push to the branch (`git push origin my-new-feature`)
154
+ 5. Create a new Pull Request
155
+
156
+ Pull Requests that include tests are **much** more likely to be accepted and merged quickly.
157
+
158
+ ## License
159
+
160
+ Copyright (c) 2013-2016 Tom Kane. Released under the [MIT License](http://opensource.org/licenses/mit-license.php). See [LICENSE.txt](LICENSE.txt) for details.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/clean'
3
+
4
+ CLEAN.add 'coverage'
5
+ CLEAN.add 'doc'
6
+ CLEAN.include 'tmp-*'
@@ -0,0 +1,38 @@
1
+ # :nocov:
2
+ require 'logger'
3
+ # require 'awesome_print' # for debug output
4
+
5
+ # From http://stackoverflow.com/questions/917566/ruby-share-logger-instance-among-module-classes
6
+ module Logging
7
+ class << self
8
+ def logger
9
+ @logger ||= Logger.new($stdout)
10
+ end
11
+
12
+ def logger=(logger)
13
+ @logger = logger
14
+ end
15
+ end
16
+
17
+ # Addition
18
+ def self.included(base)
19
+ class << base
20
+ def logger
21
+ Logging.logger
22
+ end
23
+ end
24
+ end
25
+
26
+ def logger
27
+ Logging.logger
28
+ end
29
+
30
+ def debug(debug=true)
31
+ if debug
32
+ logger.level = Logger::DEBUG
33
+ else
34
+ logger.level = Logger::WARN
35
+ end
36
+ end
37
+ end
38
+ # :nocov:
@@ -0,0 +1,177 @@
1
+ module TogglV8
2
+ TOGGL_REPORTS_URL = 'https://toggl.com/reports/api/'
3
+
4
+ class ReportsV2
5
+ include TogglV8::Connection
6
+
7
+ REPORTS_V2_URL = TOGGL_REPORTS_URL + 'v2/'
8
+
9
+ attr_reader :conn
10
+
11
+ attr_accessor :workspace_id
12
+
13
+ def initialize(opts={})
14
+ debug(false)
15
+
16
+ @user_agent = TogglV8::NAME
17
+
18
+ username = opts[:api_token]
19
+ if username.nil?
20
+ toggl_api_file = opts[:toggl_api_file] || File.join(Dir.home, TOGGL_FILE)
21
+ if File.exist?(toggl_api_file) then
22
+ username = IO.read(toggl_api_file)
23
+ else
24
+ raise "Expecting one of:\n" +
25
+ " 1) api_token in file #{toggl_api_file}, or\n" +
26
+ " 2) parameter: (toggl_api_file), or\n" +
27
+ " 3) parameter: (api_token), or\n" +
28
+ "\n\tSee https://github.com/kanet77/togglv8#togglv8reportsv2" +
29
+ "\n\tand https://github.com/toggl/toggl_api_docs/blob/master/reports.md#authentication"
30
+ end
31
+ end
32
+
33
+ @conn = TogglV8::Connection.open(username, API_TOKEN, REPORTS_V2_URL, opts)
34
+ end
35
+
36
+ ##
37
+ # ---------
38
+ # :section: Report
39
+ #
40
+ # The following parameters and filters can be used in all of the reports
41
+ #
42
+ # user_agent : the name of this application so Toggl can get in touch
43
+ # (string, *required*)
44
+ # workspace_id : The workspace whose data you want to access.
45
+ # (integer, *required*)
46
+ # since : ISO 8601 date (YYYY-MM-DD), by default until - 6 days.
47
+ # (string)
48
+ # until : ISO 8601 date (YYYY-MM-DD), by default today
49
+ # (string)
50
+ # billable : possible values: yes/no/both, default both
51
+ # client_ids : client ids separated by a comma, 0 if you want to filter out time entries without a client
52
+ # project_ids : project ids separated by a comma, 0 if you want to filter out time entries without a project
53
+ # user_ids : user ids separated by a comma
54
+ # members_of_group_ids : group ids separated by a comma. This limits provided user_ids to the provided group members
55
+ # or_members_of_group_ids : group ids separated by a comma. This extends provided user_ids with the provided group members
56
+ # tag_ids : tag ids separated by a comma, 0 if you want to filter out time entries without a tag
57
+ # task_ids : task ids separated by a comma, 0 if you want to filter out time entries without a task
58
+ # time_entry_ids : time entry ids separated by a comma
59
+ # description : time entry description
60
+ # (string)
61
+ # without_description : filters out the time entries which do not have a description ('(no description)')
62
+ # (true/false)
63
+ # order_field : date/description/duration/user in detailed reports
64
+ # title/duration/amount in summary reports
65
+ # title/day1/day2/day3/day4/day5/day6/day7/week_total in weekly report
66
+ # order_desc : on for descending and off for ascending order
67
+ # (on/off)
68
+ # distinct_rates : on/off, default off
69
+ # rounding : on/off, default off, rounds time according to workspace settings
70
+ # display_hours : decimal/minutes, display hours with minutes or as a decimal number, default minutes
71
+ #
72
+ # NB! Maximum date span (until - since) is one year.
73
+
74
+ # extension can be one of ['.pdf', '.csv', '.xls']. Possibly others?
75
+ def report(type, extension, params)
76
+ raise "workspace_id is required" if @workspace_id.nil?
77
+ get "#{type}#{extension}", {
78
+ :'user_agent' => @user_agent,
79
+ :'workspace_id' => @workspace_id,
80
+ }.merge(params)
81
+ end
82
+
83
+ def weekly(extension='', params={})
84
+ report('weekly', extension, params)
85
+ end
86
+
87
+ def details(extension='', params={})
88
+ report('details', extension, params)
89
+ end
90
+
91
+ def summary(extension='', params={})
92
+ report('summary', extension, params)
93
+ end
94
+
95
+ ##
96
+ # ---------
97
+ # :section: Write report to file
98
+ #
99
+ def write_report(filename)
100
+ extension = File.extname(filename)
101
+ report = yield(extension)
102
+ File.open(filename, "wb") do |file|
103
+ file.write(report)
104
+ end
105
+ end
106
+
107
+ def write_weekly(filename, params={})
108
+ write_report(filename) do |extension|
109
+ weekly(extension, params)
110
+ end
111
+ end
112
+
113
+ def write_details(filename, params={})
114
+ write_report(filename) do |extension|
115
+ details(extension, params)
116
+ end
117
+ end
118
+
119
+ def write_summary(filename, params={})
120
+ write_report(filename) do |extension|
121
+ summary(extension, params)
122
+ end
123
+ end
124
+
125
+
126
+ ##
127
+ # ---------
128
+ # :section: Miscellaneous information
129
+ #
130
+ def revision
131
+ get "revision"
132
+ end
133
+
134
+ def index
135
+ get "index"
136
+ end
137
+
138
+ def env
139
+ get "env"
140
+ end
141
+
142
+ ##
143
+ # ---------
144
+ # :section: Project Dashboard
145
+ #
146
+ # Project dashboard returns at-a-glance information for a single project.
147
+ # This feature is only available with Toggl pro.
148
+ #
149
+ # user_agent : email, or other way to contact client application developer
150
+ # (string, *required*)
151
+ # workspace_id : The workspace whose data you want to access
152
+ # (integer, *required*)
153
+ # project_id : The project whose data you want to access
154
+ # (integer, *required*)
155
+ # page : number of 'tasks_page' you want to fetch
156
+ # (integer, optional)
157
+ # order_field string : name/assignee/duration/billable_amount/estimated_seconds
158
+ # order_desc string : on/off, on for descending and off for ascending order
159
+ def project(project_id, params={})
160
+ raise "workspace_id is required" if @workspace_id.nil?
161
+ get "project", {
162
+ :'user_agent' => @user_agent,
163
+ :'workspace_id' => @workspace_id,
164
+ :'project_id' => project_id,
165
+ }.merge(params)
166
+ end
167
+
168
+ ##
169
+ # ---------
170
+ # :section: Error (for testing)
171
+ #
172
+ # excludes endpoints 'error500' and 'division_by_zero_error'
173
+ def error400
174
+ get "error400"
175
+ end
176
+ end
177
+ end