trollolo 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +7 -0
- data/COPYING +674 -0
- data/Gemfile +11 -0
- data/README.md +92 -0
- data/Rakefile +12 -0
- data/bin/trollolo +33 -0
- data/lib/burndown_chart.rb +158 -0
- data/lib/burndown_data.rb +172 -0
- data/lib/card.rb +64 -0
- data/lib/cli.rb +251 -0
- data/lib/settings.rb +54 -0
- data/lib/trello.rb +66 -0
- data/lib/trollolo.rb +34 -0
- data/lib/version.rb +5 -0
- data/man/.gitignore +4 -0
- data/man/trollolo.1.md +152 -0
- data/scripts/create_burndown.py +149 -0
- data/spec/burndown_chart_spec.rb +307 -0
- data/spec/burndown_data_spec.rb +125 -0
- data/spec/card_spec.rb +15 -0
- data/spec/cli_spec.rb +18 -0
- data/spec/command_line_spec.rb +56 -0
- data/spec/data/burndown-data.yaml +26 -0
- data/spec/data/burndown_dir/burndown-data-01.yaml +9 -0
- data/spec/data/burndown_dir/burndown-data-02.yaml +9 -0
- data/spec/data/burndown_dir/create_burndown +142 -0
- data/spec/data/cards.json +1002 -0
- data/spec/data/lists.json +50 -0
- data/spec/data/trollolorc +2 -0
- data/spec/settings_spec.rb +39 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/trello_spec.rb +32 -0
- data/spec/wrapper/credentials_input_wrapper +19 -0
- data/spec/wrapper/empty_config_trollolo_wrapper +10 -0
- data/spec/wrapper/trollolo_wrapper +11 -0
- data/trollolo.gemspec +28 -0
- metadata +131 -0
data/lib/card.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
class Card
|
19
|
+
|
20
|
+
attr_accessor :sp, :tasks, :tasks_done
|
21
|
+
|
22
|
+
def self.name_to_points(card_name)
|
23
|
+
card_name =~ /^\(([\d.]+)\)/
|
24
|
+
return nil if $1.nil?
|
25
|
+
$1.to_f
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@sp = nil
|
30
|
+
@extra = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_sp?
|
34
|
+
@sp != nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def extra?
|
38
|
+
@extra
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_extra
|
42
|
+
@extra = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.parse json
|
46
|
+
card = Card.new
|
47
|
+
|
48
|
+
title = json["name"]
|
49
|
+
card.sp = name_to_points(title)
|
50
|
+
|
51
|
+
labels = json["labels"]
|
52
|
+
labels.each do |label|
|
53
|
+
if label["name"] == "Under waterline"
|
54
|
+
card.set_extra
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
card.tasks = json["badges"]["checkItems"]
|
59
|
+
card.tasks_done = json["badges"]["checkItemsChecked"]
|
60
|
+
|
61
|
+
card
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
class Cli < Thor
|
19
|
+
|
20
|
+
default_task :global
|
21
|
+
|
22
|
+
class_option :version, :type => :boolean, :desc => "Show version"
|
23
|
+
class_option :verbose, :type => :boolean, :desc => "Verbose mode"
|
24
|
+
class_option :raw, :type => :boolean, :desc => "Raw mode"
|
25
|
+
class_option "board-id", :type => :string, :desc => "id of Trello board"
|
26
|
+
|
27
|
+
def self.settings= s
|
28
|
+
@@settings = s
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "global", "Global options", :hide => true
|
32
|
+
def global
|
33
|
+
if options[:version]
|
34
|
+
puts "Trollolo: #{@@settings.version}"
|
35
|
+
else
|
36
|
+
Cli.help shell
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "get-lists", "Get lists"
|
41
|
+
option "board-id", :desc => "Id of Trello board", :required => true
|
42
|
+
def get_lists
|
43
|
+
process_global_options options
|
44
|
+
require_trello_credentials
|
45
|
+
|
46
|
+
trello = Trello.new(board_id: options["board-id"], developer_public_key: @@settings.developer_public_key, member_token: @@settings.member_token)
|
47
|
+
lists = trello.lists
|
48
|
+
|
49
|
+
if @@settings.raw
|
50
|
+
puts JSON.pretty_generate lists
|
51
|
+
else
|
52
|
+
lists.each do |list|
|
53
|
+
puts "#{list[ "name" ]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "get-cards", "Get cards"
|
59
|
+
option "board-id", :desc => "Id of Trello board", :required => true
|
60
|
+
def get_cards
|
61
|
+
process_global_options options
|
62
|
+
require_trello_credentials
|
63
|
+
|
64
|
+
trello = Trello.new(board_id: options["board-id"], developer_public_key: @@settings.developer_public_key, member_token: @@settings.member_token)
|
65
|
+
|
66
|
+
cards = trello.cards
|
67
|
+
|
68
|
+
if @@settings.raw
|
69
|
+
puts JSON.pretty_generate cards
|
70
|
+
else
|
71
|
+
burndown = BurndownData.new @@settings
|
72
|
+
burndown.board_id = options["board-id"]
|
73
|
+
|
74
|
+
todo_list_id = burndown.fetch_todo_list_id
|
75
|
+
doing_list_id = burndown.fetch_doing_list_id
|
76
|
+
done_list_id = burndown.fetch_done_list_id
|
77
|
+
|
78
|
+
cards_todo = Array.new
|
79
|
+
cards_doing = Array.new
|
80
|
+
cards_done = Array.new
|
81
|
+
|
82
|
+
above_waterline = true
|
83
|
+
|
84
|
+
cards.each do |card|
|
85
|
+
name = card["name"]
|
86
|
+
list = card["idList"]
|
87
|
+
puts "CARD #{name} (#{list})"
|
88
|
+
|
89
|
+
if name == "Waterline"
|
90
|
+
above_waterline = false
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
if Card.name_to_points(name).nil?
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
if list == todo_list_id && above_waterline
|
99
|
+
cards_todo.push card
|
100
|
+
elsif list == doing_list_id
|
101
|
+
cards_doing.push card
|
102
|
+
elsif list == done_list_id
|
103
|
+
cards_done.push card
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
story_points_todo = 0
|
108
|
+
story_points_doing = 0
|
109
|
+
story_points_done = 0
|
110
|
+
|
111
|
+
puts
|
112
|
+
|
113
|
+
puts "Todo"
|
114
|
+
cards_todo.each do |card|
|
115
|
+
puts " #{card["name"]}"
|
116
|
+
story_points_todo += Card.name_to_points(card["name"])
|
117
|
+
end
|
118
|
+
|
119
|
+
puts "Doing"
|
120
|
+
cards_doing.each do |card|
|
121
|
+
puts " #{card["name"]}"
|
122
|
+
story_points_doing += Card.name_to_points(card["name"])
|
123
|
+
end
|
124
|
+
|
125
|
+
puts "Done"
|
126
|
+
cards_done.each do |card|
|
127
|
+
puts " #{card["name"]}"
|
128
|
+
story_points_done += Card.name_to_points(card["name"])
|
129
|
+
end
|
130
|
+
|
131
|
+
puts
|
132
|
+
|
133
|
+
story_points_total = story_points_todo + story_points_doing + story_points_done
|
134
|
+
|
135
|
+
puts "Done: #{story_points_done}/#{story_points_total} (#{story_points_doing} in progress)"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "get-checklists", "Get checklists"
|
140
|
+
option "board-id", :desc => "Id of Trello board", :required => true
|
141
|
+
def get_checklists
|
142
|
+
process_global_options options
|
143
|
+
require_trello_credentials
|
144
|
+
|
145
|
+
trello = Trello.new(board_id: options["board-id"], developer_public_key: @@settings.developer_public_key, member_token: @@settings.member_token)
|
146
|
+
|
147
|
+
data = trello.checklists
|
148
|
+
|
149
|
+
puts JSON.pretty_generate data
|
150
|
+
end
|
151
|
+
|
152
|
+
desc "fetch-burndown-data", "Fetch data for burndown chart"
|
153
|
+
option "board-id", :desc => "Id of Trello board", :required => true
|
154
|
+
def fetch_burndown_data
|
155
|
+
process_global_options options
|
156
|
+
require_trello_credentials
|
157
|
+
|
158
|
+
burndown = BurndownData.new @@settings
|
159
|
+
burndown.board_id = options["board-id"]
|
160
|
+
burndown.fetch
|
161
|
+
|
162
|
+
puts "Story points:"
|
163
|
+
puts " Open: #{burndown.story_points.open}"
|
164
|
+
puts " Done: #{burndown.story_points.done}"
|
165
|
+
puts " Total: #{burndown.story_points.total}"
|
166
|
+
puts "Tasks:"
|
167
|
+
puts " Open: #{burndown.tasks.open}"
|
168
|
+
puts " Done: #{burndown.tasks.done}"
|
169
|
+
puts " Total: #{burndown.tasks.total}"
|
170
|
+
puts
|
171
|
+
puts "Extra story points:"
|
172
|
+
puts " Open: #{burndown.extra_story_points.open}"
|
173
|
+
puts " Done: #{burndown.extra_story_points.done}"
|
174
|
+
puts " Total: #{burndown.extra_story_points.total}"
|
175
|
+
puts "Extra tasks:"
|
176
|
+
puts " Open: #{burndown.extra_tasks.open}"
|
177
|
+
puts " Done: #{burndown.extra_tasks.done}"
|
178
|
+
puts " Total: #{burndown.extra_tasks.total}"
|
179
|
+
end
|
180
|
+
|
181
|
+
desc "burndown-init", "Initialize burndown chart"
|
182
|
+
option :output, :aliases => :o, :desc => "Output directory", :required => true
|
183
|
+
option "board-id", :desc => "Id of Trello board", :required => true
|
184
|
+
def burndown_init command=nil
|
185
|
+
process_global_options options
|
186
|
+
require_trello_credentials
|
187
|
+
|
188
|
+
chart = BurndownChart.new @@settings
|
189
|
+
puts "Preparing directory..."
|
190
|
+
chart.setup(options[:output],options["board-id"])
|
191
|
+
end
|
192
|
+
|
193
|
+
desc "burndown", "Update burndown chart"
|
194
|
+
option :output, :aliases => :o, :desc => "Output directory", :required => false
|
195
|
+
option :new_sprint, :aliases => :n, :desc => "Create new sprint"
|
196
|
+
def burndown
|
197
|
+
process_global_options options
|
198
|
+
require_trello_credentials
|
199
|
+
|
200
|
+
chart = BurndownChart.new @@settings
|
201
|
+
begin
|
202
|
+
if options[:new_sprint]
|
203
|
+
chart.create_next_sprint(options[:output] || Dir.pwd)
|
204
|
+
end
|
205
|
+
chart.update(options[:output] || Dir.pwd)
|
206
|
+
rescue TrolloloError => e
|
207
|
+
STDERR.puts e
|
208
|
+
exit 1
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
desc "plot", "Plot burndown chart"
|
213
|
+
def plot(sprint_number)
|
214
|
+
process_global_options options
|
215
|
+
|
216
|
+
plot_helper = File.expand_path("../../scripts/create_burndown.py", __FILE__ )
|
217
|
+
system "python #{plot_helper} #{sprint_number}"
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def process_global_options options
|
223
|
+
@@settings.verbose = options[:verbose]
|
224
|
+
@@settings.raw = options[:raw]
|
225
|
+
end
|
226
|
+
|
227
|
+
def require_trello_credentials
|
228
|
+
write_back = false
|
229
|
+
|
230
|
+
if !@@settings.developer_public_key
|
231
|
+
puts "Put in Trello developer public key:"
|
232
|
+
@@settings.developer_public_key = STDIN.gets.chomp
|
233
|
+
write_back = true
|
234
|
+
end
|
235
|
+
|
236
|
+
if !@@settings.member_token
|
237
|
+
puts "Put in Trello member token:"
|
238
|
+
@@settings.member_token = STDIN.gets.chomp
|
239
|
+
write_back = true
|
240
|
+
end
|
241
|
+
|
242
|
+
if write_back
|
243
|
+
@@settings.save_config
|
244
|
+
end
|
245
|
+
|
246
|
+
if !@@settings.developer_public_key || !@@settings.member_token
|
247
|
+
STDERR.puts "Require trello credentials in config file"
|
248
|
+
exit 1
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
data/lib/settings.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
class Settings
|
19
|
+
|
20
|
+
attr_accessor :verbose, :raw
|
21
|
+
attr_accessor :developer_public_key, :member_token
|
22
|
+
|
23
|
+
def initialize config_file_path
|
24
|
+
@config_file_path = config_file_path
|
25
|
+
if File.exists? config_file_path
|
26
|
+
@config = YAML.load_file(config_file_path)
|
27
|
+
|
28
|
+
if @config
|
29
|
+
@developer_public_key = @config["developer_public_key"]
|
30
|
+
@member_token = @config["member_token"]
|
31
|
+
else
|
32
|
+
raise "Couldn't read config data from '#{config_file_path}'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@verbose = false
|
37
|
+
@raw = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def save_config
|
41
|
+
@config = {}
|
42
|
+
@config["developer_public_key"] = @developer_public_key
|
43
|
+
@config["member_token"] = @member_token
|
44
|
+
|
45
|
+
File.open(@config_file_path,"w") do |f|
|
46
|
+
f.write(@config.to_yaml)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def version
|
51
|
+
Trollolo::VERSION
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/trello.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
class Trello
|
19
|
+
class ApiError < StandardError; end
|
20
|
+
|
21
|
+
attr_accessor :board_id
|
22
|
+
|
23
|
+
attr_reader :developer_public_key, :member_token
|
24
|
+
|
25
|
+
def initialize settings
|
26
|
+
@developer_public_key = settings.fetch(:developer_public_key)
|
27
|
+
@member_token = settings.fetch(:member_token)
|
28
|
+
@board_id = settings.fetch(:board_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lists
|
32
|
+
get "lists"
|
33
|
+
end
|
34
|
+
|
35
|
+
def cards
|
36
|
+
get "cards"
|
37
|
+
end
|
38
|
+
|
39
|
+
def checklists
|
40
|
+
get "checklists"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def resource_url resource
|
46
|
+
"/1/boards/#{board_id}/#{resource}?key=#{developer_public_key}&token=#{member_token}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# FIXME: we should handle time-outs gracefully.
|
50
|
+
|
51
|
+
def get resource
|
52
|
+
resp = http_client.get resource_url(resource)
|
53
|
+
unless resp.code == "200"
|
54
|
+
raise ApiError.new("Error occured while connecting to the trello API.")
|
55
|
+
end
|
56
|
+
JSON.parse(resp.body)
|
57
|
+
rescue JSON::ParserError => e
|
58
|
+
raise ApiError.new(e)
|
59
|
+
end
|
60
|
+
|
61
|
+
def http_client
|
62
|
+
http = Net::HTTP.new "trello.com", 443
|
63
|
+
http.use_ssl = true
|
64
|
+
http
|
65
|
+
end
|
66
|
+
end
|
data/lib/trollolo.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
require "thor"
|
19
|
+
require "net/http"
|
20
|
+
require "net/https"
|
21
|
+
require "json"
|
22
|
+
require "yaml"
|
23
|
+
require "erb"
|
24
|
+
|
25
|
+
require_relative 'version'
|
26
|
+
require_relative 'cli'
|
27
|
+
require_relative 'settings'
|
28
|
+
require_relative 'trello'
|
29
|
+
require_relative 'card'
|
30
|
+
require_relative 'burndown_chart'
|
31
|
+
require_relative 'burndown_data'
|
32
|
+
|
33
|
+
class TrolloloError < StandardError
|
34
|
+
end
|