v2gpti 0.2.0.10 → 0.2.1
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 +4 -4
- data/lib/git-pivotal-tracker-integration/command/base.rb +63 -3
- data/lib/git-pivotal-tracker-integration/command/configuration.rb +20 -6
- data/lib/git-pivotal-tracker-integration/command/finish.rb +11 -1
- data/lib/git-pivotal-tracker-integration/util/story.rb +1 -0
- data/lib/git-pivotal-tracker-integration/util/togglV8.rb +417 -0
- metadata +93 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d42ceb59abdcceff2e129b8c7144be02fa73995
|
4
|
+
data.tar.gz: ad715bb77d922aa00d920367fbbd53b75d457b90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c595151e4329039ed4235470dea778b0e0c13e5af5a3c7f59f4a2d1fa20df121acf781bf0ca37d58e2b32dfa87ca66be8aef7c7a5b4b1c2ca8df662a58cd0d5c
|
7
|
+
data.tar.gz: 7bd8509a922209ff1edeb5473bd48f69642e1f12429d2b2ebeaa562f82f88249e15f4b900ea6a66480ebb4ee22f93954f76393b099d5a9c94a7f4c4bc4b60eda
|
@@ -20,7 +20,6 @@ require 'pivotal-tracker'
|
|
20
20
|
require 'parseconfig'
|
21
21
|
require 'logger'
|
22
22
|
|
23
|
-
|
24
23
|
# An abstract base class for all commands
|
25
24
|
# @abstract Subclass and override {#run} to implement command functionality
|
26
25
|
class GitPivotalTrackerIntegration::Command::Base
|
@@ -36,13 +35,18 @@ class GitPivotalTrackerIntegration::Command::Base
|
|
36
35
|
self.check_version
|
37
36
|
@repository_root = GitPivotalTrackerIntegration::Util::Git.repository_root
|
38
37
|
@configuration = GitPivotalTrackerIntegration::Command::Configuration.new
|
38
|
+
@toggl = Toggl.new
|
39
39
|
|
40
40
|
PivotalTracker::Client.token = @configuration.api_token
|
41
41
|
PivotalTracker::Client.use_ssl = true
|
42
42
|
|
43
43
|
@project = PivotalTracker::Project.find @configuration.project_id
|
44
44
|
end
|
45
|
-
|
45
|
+
def finish_toggle(configuration, time_spent)
|
46
|
+
current_story = @configuration.story(@project)
|
47
|
+
@toggl.create_task(parameters(configuration, time_spent))
|
48
|
+
@toggl.create_time_entry(parameters(configuration, time_spent))
|
49
|
+
end
|
46
50
|
def start_logging
|
47
51
|
$LOG = Logger.new("#{Dir.home}/.v2gpti_local.log", 'weekly')
|
48
52
|
end
|
@@ -65,4 +69,60 @@ class GitPivotalTrackerIntegration::Command::Base
|
|
65
69
|
raise NotImplementedError
|
66
70
|
end
|
67
71
|
|
68
|
-
|
72
|
+
# name : The name of the task (string, required, unique in project)
|
73
|
+
# pid : project ID for the task (integer, required)
|
74
|
+
# wid : workspace ID, where the task will be saved (integer, project's workspace id is used when not supplied)
|
75
|
+
# uid : user ID, to whom the task is assigned to (integer, not required)
|
76
|
+
# estimated_seconds : estimated duration of task in seconds (integer, not required)
|
77
|
+
# active : whether the task is done or not (boolean, by default true)
|
78
|
+
# at : timestamp that is sent in the response for PUT, indicates the time task was last updated
|
79
|
+
# -- Additional fields --
|
80
|
+
# done_seconds : duration (in seconds) of all the time entries registered for this task
|
81
|
+
# uname : full name of the person to whom the task is assigned to
|
82
|
+
TIMER_TOKENS = {
|
83
|
+
"m" => (60),
|
84
|
+
"h" => (60 * 60),
|
85
|
+
"d" => (60 * 60 * 8) # a work day is 8 hours
|
86
|
+
}
|
87
|
+
def parameters(configuration, time_spent)
|
88
|
+
current_story = configuration.story(@project)
|
89
|
+
params = Hash.new
|
90
|
+
params[:name] = "#{current_story.id}" + " - " + "#{current_story.name}"
|
91
|
+
params[:estimated_seconds] = estimated_seconds current_story
|
92
|
+
params[:pid] = configuration.toggl_project_id
|
93
|
+
params[:uid] = @toggl.me["id"]
|
94
|
+
params[:description] = "#{current_story.id}" + " commit:" + "#{(GitPivotalTrackerIntegration::Util::Shell.exec "git rev-parse HEAD").chomp[0..6]}"
|
95
|
+
params[:created_with] = "v2gpti"
|
96
|
+
params[:start] = current_story.created_at.iso8601
|
97
|
+
params[:duration] = seconds_spent(time_spent)
|
98
|
+
task = @toggl.get_project_task_with_name(configuration.toggl_project_id, "#{current_story.id}")
|
99
|
+
if !task.nil?
|
100
|
+
params[:tid] = task['id']
|
101
|
+
end
|
102
|
+
params
|
103
|
+
end
|
104
|
+
def seconds_spent(time_spent)
|
105
|
+
seconds = 0
|
106
|
+
time_spent.scan(/(\d+)(\w)/).each do |amount, measure|
|
107
|
+
seconds += amount.to_i * TIMER_TOKENS[measure]
|
108
|
+
end
|
109
|
+
seconds
|
110
|
+
end
|
111
|
+
def estimated_seconds(story)
|
112
|
+
estimate = story.estimate
|
113
|
+
seconds = 0
|
114
|
+
case estimate
|
115
|
+
when 0
|
116
|
+
estimate = 15 * 60
|
117
|
+
when 1
|
118
|
+
estimate = 1.25 * 60 * 60
|
119
|
+
when 2
|
120
|
+
estiamte = 3 * 60 * 60
|
121
|
+
when 3
|
122
|
+
estimate = 8 * 60 * 60
|
123
|
+
else
|
124
|
+
estimate = -1 * 60 * 60
|
125
|
+
end
|
126
|
+
estimate
|
127
|
+
end
|
128
|
+
end
|
@@ -17,6 +17,8 @@ require 'git-pivotal-tracker-integration/command/command'
|
|
17
17
|
require 'git-pivotal-tracker-integration/util/git'
|
18
18
|
require 'highline/import'
|
19
19
|
require 'pivotal-tracker'
|
20
|
+
require 'git-pivotal-tracker-integration/util/togglV8'
|
21
|
+
require 'time'
|
20
22
|
|
21
23
|
# A class that exposes configuration that commands can use
|
22
24
|
class GitPivotalTrackerIntegration::Command::Configuration
|
@@ -39,16 +41,28 @@ class GitPivotalTrackerIntegration::Command::Configuration
|
|
39
41
|
api_token
|
40
42
|
end
|
41
43
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def toggl_project_id
|
45
|
+
toggle_config = self.pconfig["toggl"]
|
46
|
+
if toggle_config.nil?
|
47
|
+
abort "toggle project id not set"
|
48
|
+
else
|
49
|
+
toggle_config["project-id"]
|
48
50
|
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_config_project_id
|
54
|
+
GitPivotalTrackerIntegration::Util::Git.set_config("pivotal.project-id", self.pconfig["pivotal-tracker"]["project-id"])
|
49
55
|
nil
|
50
56
|
end
|
51
57
|
|
58
|
+
def pconfig
|
59
|
+
pc = nil
|
60
|
+
config_filename = "#{GitPivotalTrackerIntegration::Util::Git.repository_root}/.v2gpti/config"
|
61
|
+
if File.file?(config_filename)
|
62
|
+
pc = ParseConfig.new(config_filename)
|
63
|
+
end
|
64
|
+
pc
|
65
|
+
end
|
52
66
|
# Returns the Pivotal Tracker project id for this repository. If this id
|
53
67
|
# has not been configuration, prompts the user for the value. The value is
|
54
68
|
# checked for in the _inherited_ Git configuration, but is stored in the
|
@@ -30,7 +30,15 @@ class GitPivotalTrackerIntegration::Command::Finish < GitPivotalTrackerIntegrati
|
|
30
30
|
def run(argument)
|
31
31
|
$LOG.debug("#{self.class} in project:#{@project.name} pwd:#{(GitPivotalTrackerIntegration::Util::Shell.exec 'pwd').chop} branch:#{GitPivotalTrackerIntegration::Util::Git.branch_name}")
|
32
32
|
no_complete = argument =~ /--no-complete/
|
33
|
-
|
33
|
+
time_spent = ""
|
34
|
+
while 1
|
35
|
+
time_spent = ask("How much time did you spend on this task? (example: 15m, 2.5h)")
|
36
|
+
if (/\d/.match( time_spent )) && /[mhd]/.match(time_spent)
|
37
|
+
break
|
38
|
+
end
|
39
|
+
end
|
40
|
+
finish_toggle(@configuration, time_spent)
|
41
|
+
# ask("pause")
|
34
42
|
GitPivotalTrackerIntegration::Util::Git.trivial_merge?
|
35
43
|
$LOG.debug("configuration:#{@configuration}")
|
36
44
|
$LOG.debug("project:#{@project}")
|
@@ -39,4 +47,6 @@ class GitPivotalTrackerIntegration::Command::Finish < GitPivotalTrackerIntegrati
|
|
39
47
|
GitPivotalTrackerIntegration::Util::Git.push GitPivotalTrackerIntegration::Util::Git.branch_name
|
40
48
|
end
|
41
49
|
|
50
|
+
|
51
|
+
|
42
52
|
end
|
@@ -0,0 +1,417 @@
|
|
1
|
+
#! /usr/bin/env rvm ruby-1.9.3-head do ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'logger'
|
6
|
+
require 'faraday'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
require 'awesome_print' # for debug output
|
10
|
+
|
11
|
+
class Toggl
|
12
|
+
attr_accessor :conn, :debug
|
13
|
+
|
14
|
+
def initialize(username=nil, password='api_token', debug=nil)
|
15
|
+
self.debug_on(debug) if !debug.nil?
|
16
|
+
if (password.to_s == 'api_token' && username.to_s == '')
|
17
|
+
toggl_api_file = self.toggl_file
|
18
|
+
username = IO.read(toggl_api_file)
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
@conn = connection(username, password)
|
23
|
+
end
|
24
|
+
|
25
|
+
def toggl_file
|
26
|
+
t_file = ENV['HOME']+'/.toggl'
|
27
|
+
if !FileTest.exist?(t_file) then
|
28
|
+
puts "\n\nIt looks like this is the first time you have used Toggl on this machine.\n"
|
29
|
+
t_API_key = ask("Please enter your Toggl API key:")
|
30
|
+
output = File.open( t_file, "w")
|
31
|
+
output << t_API_key
|
32
|
+
output.close
|
33
|
+
end
|
34
|
+
|
35
|
+
t_file
|
36
|
+
end
|
37
|
+
def connection(username, password)
|
38
|
+
Faraday.new(url: 'https://www.toggl.com/api/v8') do |faraday|
|
39
|
+
faraday.request :url_encoded
|
40
|
+
faraday.response :logger, Logger.new('faraday.log')
|
41
|
+
faraday.adapter Faraday.default_adapter
|
42
|
+
faraday.headers = {"Content-Type" => "application/json"}
|
43
|
+
faraday.basic_auth username, password
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug_on(debug=true)
|
48
|
+
puts "debugging is %s" % [debug ? "ON" : "OFF"]
|
49
|
+
@debug = debug
|
50
|
+
end
|
51
|
+
|
52
|
+
def checkParams(params, fields=[])
|
53
|
+
raise ArgumentError, 'params is not a Hash' unless params.is_a? Hash
|
54
|
+
return if fields.empty?
|
55
|
+
errors = []
|
56
|
+
for f in fields
|
57
|
+
errors.push("params[#{f}] is required") unless params.has_key?(f)
|
58
|
+
end
|
59
|
+
raise ArgumentError, errors.join(', ') if !errors.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
#----------#
|
63
|
+
#--- Me ---#
|
64
|
+
#----------#
|
65
|
+
|
66
|
+
def me(all=nil)
|
67
|
+
# TODO: Reconcile this with get_client_projects
|
68
|
+
res = get "me%s" % [all.nil? ? "" : "?with_related_data=#{all}"]
|
69
|
+
end
|
70
|
+
|
71
|
+
def my_clients(user)
|
72
|
+
user['projects']
|
73
|
+
end
|
74
|
+
|
75
|
+
def my_projects(user)
|
76
|
+
user['projects']
|
77
|
+
end
|
78
|
+
|
79
|
+
def my_tags(user)
|
80
|
+
user['tags']
|
81
|
+
end
|
82
|
+
|
83
|
+
def my_time_entries(user)
|
84
|
+
user['time_entries']
|
85
|
+
end
|
86
|
+
|
87
|
+
def my_workspaces(user)
|
88
|
+
user['workspaces']
|
89
|
+
end
|
90
|
+
|
91
|
+
#---------------#
|
92
|
+
#--- Clients ---#
|
93
|
+
#---------------#
|
94
|
+
|
95
|
+
# name : The name of the client (string, required, unique in workspace)
|
96
|
+
# wid : workspace ID, where the client will be used (integer, required)
|
97
|
+
# notes : Notes for the client (string, not required)
|
98
|
+
# hrate : The hourly rate for this client (float, not required, available only for pro workspaces)
|
99
|
+
# cur : The name of the client's currency (string, not required, available only for pro workspaces)
|
100
|
+
# at : timestamp that is sent in the response, indicates the time client was last updated
|
101
|
+
|
102
|
+
def create_client(params)
|
103
|
+
checkParams(params, [:name, :wid])
|
104
|
+
post "clients", {client: params}
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_client(client_id)
|
108
|
+
get "clients/#{client_id}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def update_client(client_id, params)
|
112
|
+
put "clients/#{client_id}", {client: params}
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete_client(client_id)
|
116
|
+
delete "clients/#{client_id}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_client_projects(client_id, params={})
|
120
|
+
active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
|
121
|
+
get "clients/#{client_id}/projects#{active}"
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
#----------------#
|
126
|
+
#--- Projects ---#
|
127
|
+
#----------------#
|
128
|
+
|
129
|
+
# name : The name of the project (string, required, unique for client and workspace)
|
130
|
+
# wid : workspace ID, where the project will be saved (integer, required)
|
131
|
+
# cid : client ID(integer, not required)
|
132
|
+
# active : whether the project is archived or not (boolean, by default true)
|
133
|
+
# is_private : whether project is accessible for only project users or for all workspace users (boolean, default true)
|
134
|
+
# template : whether the project can be used as a template (boolean, not required)
|
135
|
+
# template_id : id of the template project used on current project's creation
|
136
|
+
# billable : whether the project is billable or not (boolean, default true, available only for pro workspaces)
|
137
|
+
# at : timestamp that is sent in the response for PUT, indicates the time task was last updated
|
138
|
+
# -- Undocumented --
|
139
|
+
# color : number (in the range 0-23?)
|
140
|
+
|
141
|
+
def create_project(params)
|
142
|
+
checkParams(params, [:name, :wid])
|
143
|
+
post "projects", {project: params}
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_project(project_id)
|
147
|
+
get "projects/#{project_id}"
|
148
|
+
end
|
149
|
+
|
150
|
+
def update_project(project_id, params)
|
151
|
+
put "projects/#{project_id}", {project: params}
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_project_users(project_id)
|
155
|
+
get "projects/#{project_id}/project_users"
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_project_tasks(project_id)
|
159
|
+
get "projects/#{project_id}/tasks"
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_project_task_with_name(project_id, task_name)
|
163
|
+
task = nil
|
164
|
+
project_tasks = get_project_tasks(project_id)
|
165
|
+
project_tasks.each { |a_task|
|
166
|
+
a_task_name = "#{a_task["name"]}"
|
167
|
+
if (a_task_name.include?task_name)
|
168
|
+
task = a_task
|
169
|
+
break
|
170
|
+
end
|
171
|
+
}
|
172
|
+
task
|
173
|
+
end
|
174
|
+
#---------------------#
|
175
|
+
#--- Project users ---#
|
176
|
+
#---------------------#
|
177
|
+
|
178
|
+
# pid : project ID (integer, required)
|
179
|
+
# uid : user ID, who is added to the project (integer, required)
|
180
|
+
# wid : workspace ID, where the project belongs to (integer, not-required, project's workspace id is used)
|
181
|
+
# manager : admin rights for this project (boolean, default false)
|
182
|
+
# rate : hourly rate for the project user (float, not-required, only for pro workspaces) in the currency of the project's client or in workspace default currency.
|
183
|
+
# at : timestamp that is sent in the response, indicates when the project user was last updated
|
184
|
+
# -- Additional fields --
|
185
|
+
# fullname : full name of the user, who is added to the project
|
186
|
+
|
187
|
+
def create_project_user(params)
|
188
|
+
checkParams(params, [:pid, :uid])
|
189
|
+
params[:fields] = "fullname" # for simplicity, always request fullname field
|
190
|
+
post "project_users", {project_user: params}
|
191
|
+
end
|
192
|
+
|
193
|
+
def update_project_user(project_user_id, params)
|
194
|
+
params[:fields] = "fullname" # for simplicity, always request fullname field
|
195
|
+
put "project_users/#{project_user_id}", {project_user: params}
|
196
|
+
end
|
197
|
+
|
198
|
+
def delete_project_user(project_user_id)
|
199
|
+
delete "project_users/#{project_user_id}"
|
200
|
+
end
|
201
|
+
|
202
|
+
#------------#
|
203
|
+
#--- Tags ---#
|
204
|
+
#------------#
|
205
|
+
|
206
|
+
# name : The name of the tag (string, required, unique in workspace)
|
207
|
+
# wid : workspace ID, where the tag will be used (integer, required)
|
208
|
+
|
209
|
+
def create_tag(params)
|
210
|
+
checkParams(params, [:name, :wid])
|
211
|
+
post "tags", {tag: params}
|
212
|
+
end
|
213
|
+
|
214
|
+
# ex: update_tag(12345, {name: "same tame game"})
|
215
|
+
def update_tag(tag_id, params)
|
216
|
+
put "tags/#{tag_id}", {tag: params}
|
217
|
+
end
|
218
|
+
|
219
|
+
def delete_tag(tag_id)
|
220
|
+
delete "tags/#{tag_id}"
|
221
|
+
end
|
222
|
+
|
223
|
+
#-------------#
|
224
|
+
#--- Tasks ---#
|
225
|
+
#-------------#
|
226
|
+
|
227
|
+
# name : The name of the task (string, required, unique in project)
|
228
|
+
# pid : project ID for the task (integer, required)
|
229
|
+
# wid : workspace ID, where the task will be saved (integer, project's workspace id is used when not supplied)
|
230
|
+
# uid : user ID, to whom the task is assigned to (integer, not required)
|
231
|
+
# estimated_seconds : estimated duration of task in seconds (integer, not required)
|
232
|
+
# active : whether the task is done or not (boolean, by default true)
|
233
|
+
# at : timestamp that is sent in the response for PUT, indicates the time task was last updated
|
234
|
+
# -- Additional fields --
|
235
|
+
# done_seconds : duration (in seconds) of all the time entries registered for this task
|
236
|
+
# uname : full name of the person to whom the task is assigned to
|
237
|
+
|
238
|
+
def create_task(params)
|
239
|
+
checkParams(params, [:name, :pid])
|
240
|
+
post "tasks", {task: params}
|
241
|
+
end
|
242
|
+
|
243
|
+
def get_task(task_id)
|
244
|
+
get "tasks/#{task_id}"
|
245
|
+
end
|
246
|
+
|
247
|
+
# ex: update_task(1894675, {active: true, estimated_seconds: 4500, fields: "done_seconds,uname"})
|
248
|
+
def update_task(*task_id, params)
|
249
|
+
put "tasks/#{task_id.join(',')}", {task: params}
|
250
|
+
end
|
251
|
+
|
252
|
+
def delete_task(*task_id)
|
253
|
+
delete "tasks/#{task_id.join(',')}"
|
254
|
+
end
|
255
|
+
|
256
|
+
#--------------------#
|
257
|
+
#--- Time entries ---#
|
258
|
+
#--------------------#
|
259
|
+
|
260
|
+
# description : (string, required)
|
261
|
+
# wid : workspace ID (integer, required if pid or tid not supplied)
|
262
|
+
# pid : project ID (integer, not required)
|
263
|
+
# tid : task ID (integer, not required)
|
264
|
+
# billable : (boolean, not required, default false, available for pro workspaces)
|
265
|
+
# start : time entry start time (string, required, ISO 8601 date and time)
|
266
|
+
# stop : time entry stop time (string, not required, ISO 8601 date and time)
|
267
|
+
# duration : time entry duration in seconds. If the time entry is currently running, the duration attribute contains a negative value, denoting the start of the time entry in seconds since epoch (Jan 1 1970). The correct duration can be calculated as current_time + duration, where current_time is the current time in seconds since epoch. (integer, required)
|
268
|
+
# created_with : the name of your client app (string, required)
|
269
|
+
# tags : a list of tag names (array of strings, not required)
|
270
|
+
# duronly : should Toggl show the start and stop time of this time entry? (boolean, not required)
|
271
|
+
# at : timestamp that is sent in the response, indicates the time item was last updated
|
272
|
+
|
273
|
+
def create_time_entry(params)
|
274
|
+
checkParams(params, [:description, :start, :created_with])
|
275
|
+
if !params.has_key?(:wid) and !params.has_key?(:pid) and !params.has_key?(:tid) then
|
276
|
+
raise ArgumentError, "one of params['wid'], params['pid'], params['tid'] is required"
|
277
|
+
end
|
278
|
+
post "time_entries", {time_entry: params}
|
279
|
+
end
|
280
|
+
|
281
|
+
def start_time_entry(params)
|
282
|
+
if !params.has_key?(:wid) and !params.has_key?(:pid) and !params.has_key?(:tid) then
|
283
|
+
raise ArgumentError, "one of params['wid'], params['pid'], params['tid'] is required"
|
284
|
+
end
|
285
|
+
post "time_entries/start", {time_entry: params}
|
286
|
+
end
|
287
|
+
|
288
|
+
def stop_time_entry(time_entry_id)
|
289
|
+
put "time_entries/#{time_entry_id}/stop", {}
|
290
|
+
end
|
291
|
+
|
292
|
+
def get_time_entry(time_entry_id)
|
293
|
+
get "time_entries/#{time_entry_id}"
|
294
|
+
end
|
295
|
+
|
296
|
+
def update_time_entry(time_entry_id, params)
|
297
|
+
put "time_entries/#{time_entry_id}", {time_entry: params}
|
298
|
+
end
|
299
|
+
|
300
|
+
def delete_time_entry(time_entry_id)
|
301
|
+
delete "time_entries/#{time_entry_id}"
|
302
|
+
end
|
303
|
+
|
304
|
+
def iso8601(date)
|
305
|
+
return nil if date.nil?
|
306
|
+
if date.is_a?(Time) or date.is_a?(Date)
|
307
|
+
iso = date.iso8601
|
308
|
+
elsif date.is_a?(String)
|
309
|
+
iso = DateTime.parse(date).iso8601
|
310
|
+
else
|
311
|
+
raise ArgumentError, "Can't convert #{date.class} to ISO-8601 Date/Time"
|
312
|
+
end
|
313
|
+
return Faraday::Utils.escape(iso)
|
314
|
+
end
|
315
|
+
|
316
|
+
def get_time_entries(start_date=nil, end_date=nil)
|
317
|
+
params = []
|
318
|
+
params.push("start_date=#{iso8601(start_date)}") if !start_date.nil?
|
319
|
+
params.push("end_date=#{iso8601(end_date)}") if !end_date.nil?
|
320
|
+
get "time_entries%s" % [params.empty? ? "" : "?#{params.join('&')}"]
|
321
|
+
end
|
322
|
+
|
323
|
+
#-------------#
|
324
|
+
#--- Users ---#
|
325
|
+
#-------------#
|
326
|
+
|
327
|
+
# api_token : (string)
|
328
|
+
# default_wid : default workspace id (integer)
|
329
|
+
# email : (string)
|
330
|
+
# jquery_timeofday_format : (string)
|
331
|
+
# jquery_date_format :(string)
|
332
|
+
# timeofday_format : (string)
|
333
|
+
# date_format : (string)
|
334
|
+
# store_start_and_stop_time : whether start and stop time are saved on time entry (boolean)
|
335
|
+
# beginning_of_week : (integer, Sunday=0)
|
336
|
+
# language : user's language (string)
|
337
|
+
# image_url : url with the user's profile picture(string)
|
338
|
+
# sidebar_piechart : should a piechart be shown on the sidebar (boolean)
|
339
|
+
# at : timestamp of last changes
|
340
|
+
# new_blog_post : an object with toggl blog post title and link
|
341
|
+
|
342
|
+
#------------------#
|
343
|
+
#--- Workspaces ---#
|
344
|
+
#------------------#
|
345
|
+
|
346
|
+
# name : (string, required)
|
347
|
+
# premium : If it's a pro workspace or not. Shows if someone is paying for the workspace or not (boolean, not required)
|
348
|
+
# at : timestamp that is sent in the response, indicates the time item was last updated
|
349
|
+
|
350
|
+
def workspaces
|
351
|
+
get "workspaces"
|
352
|
+
end
|
353
|
+
|
354
|
+
def clients(workspace=nil)
|
355
|
+
if workspace.nil?
|
356
|
+
get "clients"
|
357
|
+
else
|
358
|
+
get "workspaces/#{workspace}/clients"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def projects(workspace, params={})
|
363
|
+
active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
|
364
|
+
get "workspaces/#{workspace}/projects#{active}"
|
365
|
+
end
|
366
|
+
|
367
|
+
def users(workspace)
|
368
|
+
get "workspaces/#{workspace}/users"
|
369
|
+
end
|
370
|
+
|
371
|
+
def tasks(workspace, params={})
|
372
|
+
active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
|
373
|
+
get "workspaces/#{workspace}/tasks#{active}"
|
374
|
+
end
|
375
|
+
|
376
|
+
#---------------#
|
377
|
+
#--- Private ---#
|
378
|
+
#---------------#
|
379
|
+
|
380
|
+
private
|
381
|
+
|
382
|
+
def get(resource)
|
383
|
+
puts "GET #{resource}" if @debug
|
384
|
+
full_res = self.conn.get(resource)
|
385
|
+
# ap full_res.env if @debug
|
386
|
+
res = JSON.parse(full_res.env[:body])
|
387
|
+
res.is_a?(Array) || res['data'].nil? ? res : res['data']
|
388
|
+
end
|
389
|
+
|
390
|
+
def post(resource, data)
|
391
|
+
puts "POST #{resource} / #{data}" if @debug
|
392
|
+
full_res = self.conn.post(resource, JSON.generate(data))
|
393
|
+
ap full_res.env if @debug
|
394
|
+
if (200 == full_res.env[:status]) then
|
395
|
+
res = JSON.parse(full_res.env[:body])
|
396
|
+
res['data'].nil? ? res : res['data']
|
397
|
+
else
|
398
|
+
puts(full_res.env[:body])
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def put(resource, data)
|
403
|
+
puts "PUT #{resource} / #{data}" if @debug
|
404
|
+
full_res = self.conn.put(resource, JSON.generate(data))
|
405
|
+
# ap full_res.env if @debug
|
406
|
+
res = JSON.parse(full_res.env[:body])
|
407
|
+
res['data'].nil? ? res : res['data']
|
408
|
+
end
|
409
|
+
|
410
|
+
def delete(resource)
|
411
|
+
puts "DELETE #{resource}" if @debug
|
412
|
+
full_res = self.conn.delete(resource)
|
413
|
+
# ap full_res.env if @debug
|
414
|
+
(200 == full_res.env[:status]) ? "" : puts(full_res.env[:body])
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: v2gpti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Hale
|
@@ -9,132 +9,202 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-06-
|
12
|
+
date: 2014-06-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: highline
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.6'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.6'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: pivotal-tracker
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0.5'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0.5'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: parseconfig
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '1.0'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '1.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: faraday
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.9.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.9.0
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: awesome_print
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.1.0
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.1.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: json
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 1.8.0
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ~>
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.8.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: logger
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ~>
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.2.8
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ~>
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.2.8
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: jazor
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 0.1.8
|
119
|
+
type: :runtime
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.1.8
|
56
126
|
- !ruby/object:Gem::Dependency
|
57
127
|
name: bundler
|
58
128
|
requirement: !ruby/object:Gem::Requirement
|
59
129
|
requirements:
|
60
|
-
- -
|
130
|
+
- - ~>
|
61
131
|
- !ruby/object:Gem::Version
|
62
132
|
version: '1.3'
|
63
133
|
type: :development
|
64
134
|
prerelease: false
|
65
135
|
version_requirements: !ruby/object:Gem::Requirement
|
66
136
|
requirements:
|
67
|
-
- -
|
137
|
+
- - ~>
|
68
138
|
- !ruby/object:Gem::Version
|
69
139
|
version: '1.3'
|
70
140
|
- !ruby/object:Gem::Dependency
|
71
141
|
name: rake
|
72
142
|
requirement: !ruby/object:Gem::Requirement
|
73
143
|
requirements:
|
74
|
-
- -
|
144
|
+
- - ~>
|
75
145
|
- !ruby/object:Gem::Version
|
76
146
|
version: '10.0'
|
77
147
|
type: :development
|
78
148
|
prerelease: false
|
79
149
|
version_requirements: !ruby/object:Gem::Requirement
|
80
150
|
requirements:
|
81
|
-
- -
|
151
|
+
- - ~>
|
82
152
|
- !ruby/object:Gem::Version
|
83
153
|
version: '10.0'
|
84
154
|
- !ruby/object:Gem::Dependency
|
85
155
|
name: redcarpet
|
86
156
|
requirement: !ruby/object:Gem::Requirement
|
87
157
|
requirements:
|
88
|
-
- -
|
158
|
+
- - ~>
|
89
159
|
- !ruby/object:Gem::Version
|
90
160
|
version: '2.2'
|
91
161
|
type: :development
|
92
162
|
prerelease: false
|
93
163
|
version_requirements: !ruby/object:Gem::Requirement
|
94
164
|
requirements:
|
95
|
-
- -
|
165
|
+
- - ~>
|
96
166
|
- !ruby/object:Gem::Version
|
97
167
|
version: '2.2'
|
98
168
|
- !ruby/object:Gem::Dependency
|
99
169
|
name: rspec
|
100
170
|
requirement: !ruby/object:Gem::Requirement
|
101
171
|
requirements:
|
102
|
-
- -
|
172
|
+
- - ~>
|
103
173
|
- !ruby/object:Gem::Version
|
104
174
|
version: '2.13'
|
105
175
|
type: :development
|
106
176
|
prerelease: false
|
107
177
|
version_requirements: !ruby/object:Gem::Requirement
|
108
178
|
requirements:
|
109
|
-
- -
|
179
|
+
- - ~>
|
110
180
|
- !ruby/object:Gem::Version
|
111
181
|
version: '2.13'
|
112
182
|
- !ruby/object:Gem::Dependency
|
113
183
|
name: simplecov
|
114
184
|
requirement: !ruby/object:Gem::Requirement
|
115
185
|
requirements:
|
116
|
-
- -
|
186
|
+
- - ~>
|
117
187
|
- !ruby/object:Gem::Version
|
118
188
|
version: '0.7'
|
119
189
|
type: :development
|
120
190
|
prerelease: false
|
121
191
|
version_requirements: !ruby/object:Gem::Requirement
|
122
192
|
requirements:
|
123
|
-
- -
|
193
|
+
- - ~>
|
124
194
|
- !ruby/object:Gem::Version
|
125
195
|
version: '0.7'
|
126
196
|
- !ruby/object:Gem::Dependency
|
127
197
|
name: yard
|
128
198
|
requirement: !ruby/object:Gem::Requirement
|
129
199
|
requirements:
|
130
|
-
- -
|
200
|
+
- - ~>
|
131
201
|
- !ruby/object:Gem::Version
|
132
202
|
version: '0.8'
|
133
203
|
type: :development
|
134
204
|
prerelease: false
|
135
205
|
version_requirements: !ruby/object:Gem::Requirement
|
136
206
|
requirements:
|
137
|
-
- -
|
207
|
+
- - ~>
|
138
208
|
- !ruby/object:Gem::Version
|
139
209
|
version: '0.8'
|
140
210
|
description: Provides a set of additional Git commands to help developers when working
|
@@ -166,6 +236,7 @@ files:
|
|
166
236
|
- lib/git-pivotal-tracker-integration/util/git.rb
|
167
237
|
- lib/git-pivotal-tracker-integration/util/shell.rb
|
168
238
|
- lib/git-pivotal-tracker-integration/util/story.rb
|
239
|
+
- lib/git-pivotal-tracker-integration/util/togglV8.rb
|
169
240
|
- lib/git-pivotal-tracker-integration/util/util.rb
|
170
241
|
- lib/git-pivotal-tracker-integration/version-update/gradle.rb
|
171
242
|
- lib/git-pivotal-tracker-integration/version-update/version_update.rb
|
@@ -189,12 +260,12 @@ require_paths:
|
|
189
260
|
- lib
|
190
261
|
required_ruby_version: !ruby/object:Gem::Requirement
|
191
262
|
requirements:
|
192
|
-
- -
|
263
|
+
- - '>='
|
193
264
|
- !ruby/object:Gem::Version
|
194
265
|
version: '1.9'
|
195
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
267
|
requirements:
|
197
|
-
- -
|
268
|
+
- - '>='
|
198
269
|
- !ruby/object:Gem::Version
|
199
270
|
version: '0'
|
200
271
|
requirements: []
|