trollolo 0.0.3
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 +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
|