v2gpti 0.2.0.10 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|