trello_lead_time 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a25cde3ef1f75314cce048dcb11f087905d8f9c1
4
+ data.tar.gz: 616f613d0eb115cf779a7aff107f88186fd33019
5
+ SHA512:
6
+ metadata.gz: dd7741923539d8c0b86fd10c95c9cd9858e04ad78a7abab39d6d61f9d740e21d921d5c855ba1f558410490fe9c623968b8ef3efaccaeacae9ce8be042df9bc50
7
+ data.tar.gz: 4ba87e3d671cfb5a6ac9324809ad49c9eab3d83dee0d8e51d5c9eb3fa6d958a74283e52a021df63a39b88cafcd865a9b6a335e55dc3a7bf278ccaf649e0da039
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ run.rb
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ trello_lead_time
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p247
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in trello_lead_time.gemspec
4
+ gemspec
5
+ gem "rspec"
6
+ gem 'pry'
7
+ gem 'pry-debugger'
8
+ gem 'ruby-trello'
9
+ gem 'webmock'
data/Gemfile.lock ADDED
@@ -0,0 +1,81 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ trello_lead_time (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activemodel (4.1.0)
10
+ activesupport (= 4.1.0)
11
+ builder (~> 3.1)
12
+ activesupport (4.1.0)
13
+ i18n (~> 0.6, >= 0.6.9)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.1)
17
+ tzinfo (~> 1.1)
18
+ addressable (2.3.6)
19
+ builder (3.2.2)
20
+ coderay (1.1.0)
21
+ columnize (0.3.6)
22
+ crack (0.4.2)
23
+ safe_yaml (~> 1.0.0)
24
+ debugger (1.6.6)
25
+ columnize (>= 0.3.1)
26
+ debugger-linecache (~> 1.2.0)
27
+ debugger-ruby_core_source (~> 1.3.2)
28
+ debugger-linecache (1.2.0)
29
+ debugger-ruby_core_source (1.3.2)
30
+ diff-lcs (1.2.5)
31
+ i18n (0.6.9)
32
+ json (1.8.1)
33
+ method_source (0.8.2)
34
+ mime-types (2.2)
35
+ minitest (5.3.2)
36
+ oauth (0.4.7)
37
+ pry (0.9.12.6)
38
+ coderay (~> 1.0)
39
+ method_source (~> 0.8)
40
+ slop (~> 3.4)
41
+ pry-debugger (0.2.2)
42
+ debugger (~> 1.3)
43
+ pry (~> 0.9.10)
44
+ rake (10.2.2)
45
+ rest-client (1.6.7)
46
+ mime-types (>= 1.16)
47
+ rspec (2.14.1)
48
+ rspec-core (~> 2.14.0)
49
+ rspec-expectations (~> 2.14.0)
50
+ rspec-mocks (~> 2.14.0)
51
+ rspec-core (2.14.8)
52
+ rspec-expectations (2.14.5)
53
+ diff-lcs (>= 1.1.3, < 2.0)
54
+ rspec-mocks (2.14.6)
55
+ ruby-trello (1.1.1)
56
+ activemodel (>= 3.2.0)
57
+ addressable (~> 2.3)
58
+ json
59
+ oauth (~> 0.4.5)
60
+ rest-client (~> 1.6.7)
61
+ safe_yaml (1.0.2)
62
+ slop (3.5.0)
63
+ thread_safe (0.3.3)
64
+ tzinfo (1.1.0)
65
+ thread_safe (~> 0.1)
66
+ webmock (1.17.4)
67
+ addressable (>= 2.2.7)
68
+ crack (>= 0.3.2)
69
+
70
+ PLATFORMS
71
+ ruby
72
+
73
+ DEPENDENCIES
74
+ bundler (~> 1.5)
75
+ pry
76
+ pry-debugger
77
+ rake
78
+ rspec
79
+ ruby-trello
80
+ trello_lead_time!
81
+ webmock
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Scott Baldwin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ trello_lead_time
2
+ =====
3
+
4
+ Trello is awesome for lightweight task/process management. It's so lightweight that you have to figure out your own way to gather analytics about your process.
5
+
6
+ *trello_lead_time* is a Ruby gem to calculate the lead time, queue time, and cycle time of cards in a Trello list.
7
+
8
+ **Queue time** - the time a Trello card spends waiting to be started.
9
+
10
+ **Cycle time** - the time a Trello card spends in progress.
11
+
12
+ **Lead time** - the time it takes a Trello card to get started and completed, e.g. queue time + cycle time.
13
+
14
+ Requirements
15
+ -----
16
+
17
+ 1. Currently, only lists belonging to a Trello organization are supported
18
+ 1. Uses the 'ruby-trello' gem (see https://github.com/jeremytregunna/ruby-trello/)
19
+
20
+ Usage
21
+ -----
22
+
23
+ Because *trello_lead_time* depends on *ruby-trello*, there is some configuration required.
24
+
25
+ First, load up the gem in your script.
26
+
27
+ require 'trello_lead_time'
28
+
29
+ Establish your Trello credentials.
30
+
31
+ developer_public_key = 'YOUR TRELLO PUBLIC KEY'
32
+ member_token = 'YOUR TRELLO MEMBER TOKEN'
33
+
34
+ Decide where to find the data. `organization_name` is the URL name that can be used to find your board by URL. You can find this organization name by navigating to your organization's page in Trello.
35
+
36
+ The `board_url` is the URL to the actual board. Why do we need the organization name if we have the board's URL? Well, the Trello API doesn't allow us to directly find a board by its URL, so we have to go through the organization. (Finding a board's internal ID to use with the API isn't possible without first using the API to get a list of the boards!)
37
+
38
+ organization_name = 'fogcreek'
39
+ board_url = 'https://trello.com/b/nC8QJJoZ/trello-development'
40
+
41
+ Now, configure how you will calculate the metrics.
42
+
43
+ `source_lists` is an array of Trello list names (can be open or archived) that contain cards you want to analyze. This gem will compute the lead time, queue time, and cycle time for each of these lists.
44
+
45
+ `queue_time_lists` is an array of lists that represent the phase of the Trello card's life when it was waiting to start.
46
+
47
+ `cycle_time_lists` is an array of lists that represent the phase of the Trello card's life when it was actively being worked, or in progress.
48
+
49
+ source_lists = ['Live (4/8)', 'Live (3/17)', 'Live (3/3)', 'Live (2/11)', 'Live (1/14)']
50
+ queue_time_lists = ['Next Up']
51
+ cycle_time_lists = ['In Progress', 'Testing']
52
+
53
+ When analyzing the timeline of each Trello card, the gem has to find out when it was moved into a "Done" state. This usually is the event when the card is placed in the one of the `source_lists`, e.g. "Live (4/8)".
54
+
55
+ You must provide a regular expression that will be compared to the names of lists a Trello card was in (at some point of its life) to identify when it was marked done.
56
+
57
+ list_name_matcher_for_done = /^Live/
58
+
59
+ Now that all those variables are set, you can configure the gem!
60
+
61
+ TrelloLeadTime.configure do |cfg|
62
+ cfg.organization_name = organization_name
63
+ cfg.set_trello_key_and_token(developer_public_key, member_token)
64
+ cfg.queue_time_lists = queue_time_lists
65
+ cfg.cycle_time_lists = cycle_time_lists
66
+ cfg.list_name_matcher_for_done = /^Live/
67
+ end
68
+
69
+ Easy right? Now let's put it to use!
70
+
71
+ puts "-" * 40
72
+ puts "Calculating metrics for:"
73
+ puts "#{board_url}"
74
+ puts "-" * 40
75
+
76
+ board = TrelloLeadTime::Board.from_url board_url
77
+ source_lists.each do |source_list|
78
+ puts "Using cards in list: #{source_list}"
79
+ puts "Average Card Age: #{TrelloLeadTime::TimeHumanizer.humanize_seconds(board.average_age(source_list))}"
80
+ puts "Average Lead Time: #{TrelloLeadTime::TimeHumanizer.humanize_seconds(board.average_lead_time(source_list))}"
81
+ puts "Average Queue Time: #{TrelloLeadTime::TimeHumanizer.humanize_seconds(board.average_queue_time(source_list))}"
82
+ puts "Average Cycle Time: #{TrelloLeadTime::TimeHumanizer.humanize_seconds(board.average_cycle_time(source_list))}"
83
+ puts ""
84
+ end
85
+
86
+ For each of the source lists, the cards are analyzed and aggregate (average) metrics are displayed. This takes some time because the Trello API must be hit for each card being analyzed. Therefore, the longer the lists in Trello, the longer it takes to process.
87
+
88
+ See [sample.rb](sample.rb) for an entire listing of the explanation above.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,10 @@
1
+ require 'trello'
2
+ Dir["#{File.dirname(__FILE__)}/trello_lead_time/**/*.rb"].each { |f| require f }
3
+
4
+ module TrelloLeadTime
5
+ extend self
6
+
7
+ def configure
8
+ yield Config
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module TrelloLeadTime
2
+ class Action
3
+ def self.from_trello_action(trello_action)
4
+ Action.new(trello_action)
5
+ end
6
+
7
+ def initialize(trello_action)
8
+ @trello_action = trello_action
9
+ end
10
+
11
+ def type
12
+ @trello_action.type
13
+ end
14
+
15
+ def data
16
+ @trello_action.data
17
+ end
18
+
19
+ def date
20
+ @trello_action.date
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ module TrelloLeadTime
2
+ class Board
3
+
4
+ def self.from_url(url)
5
+ @org = Trello::Organization.find(TrelloLeadTime::Config.organization_name)
6
+ if @org
7
+ @boards = @org.boards
8
+ @board = @boards.detect { |b| b.url == url }
9
+ return Board.new(@board) if @board
10
+ end
11
+ nil
12
+ end
13
+
14
+ def initialize(trello_board)
15
+ @trello_board = trello_board
16
+ @_lists = {}
17
+ end
18
+
19
+ def name
20
+ @trello_board.name
21
+ end
22
+
23
+ def average_age(name_of_list_with_done_cards)
24
+ list = find_list_by_name(name_of_list_with_done_cards)
25
+ return 0 if list.nil?
26
+ list.average_age
27
+ end
28
+
29
+ def average_lead_time(name_of_list_with_done_cards)
30
+ list = find_list_by_name(name_of_list_with_done_cards)
31
+ return 0 if list.nil?
32
+ list.average_lead_time
33
+ end
34
+
35
+ def average_queue_time(name_of_list_with_done_cards)
36
+ list = find_list_by_name(name_of_list_with_done_cards)
37
+ return 0 if list.nil?
38
+ list.average_queue_time
39
+ end
40
+
41
+ def average_cycle_time(name_of_list_with_done_cards)
42
+ list = find_list_by_name(name_of_list_with_done_cards)
43
+ return 0 if list.nil?
44
+ list.average_cycle_time
45
+ end
46
+
47
+ private
48
+
49
+ def find_list_by_name(name)
50
+ if !@_lists.has_key?(name)
51
+ trello_list = @trello_board.lists({filter: 'all'}).detect { |l| l.name == name }
52
+ @_lists[name] = TrelloLeadTime::List.from_trello_list(trello_list) if trello_list
53
+ end
54
+ @_lists[name]
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ module TrelloLeadTime
2
+ class Card
3
+ def self.from_trello_card(trello_card)
4
+ Card.new(trello_card)
5
+ end
6
+
7
+ def initialize(trello_card)
8
+ @trello_card = trello_card
9
+ @timeline = Timeline.for_trello_card(trello_card)
10
+ end
11
+
12
+ def name
13
+ @trello_card.name
14
+ end
15
+
16
+ def done?
17
+ @timeline.done?
18
+ end
19
+
20
+ def closed?
21
+ @trello_card.closed
22
+ end
23
+
24
+ def age_in_seconds
25
+ @timeline.age_in_seconds
26
+ end
27
+
28
+ def lead_time
29
+ @_lead_time ||= queue_time + cycle_time
30
+ end
31
+
32
+ def queue_time
33
+ @_queue_time ||= sum_of_times_in_lists(Config.queue_time_lists)
34
+ end
35
+
36
+ def cycle_time
37
+ @_cycle_time ||= sum_of_times_in_lists(Config.cycle_time_lists)
38
+ end
39
+
40
+ private
41
+
42
+ def sum_of_times_in_lists(lists)
43
+ lists.inject(0) { |sum, list_name| sum + @timeline.seconds_in_list(list_name) }
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ require 'singleton'
2
+ require 'trello'
3
+
4
+ module TrelloLeadTime
5
+ module Config
6
+ extend self
7
+
8
+ attr_accessor :organization_name, :queue_time_lists, :cycle_time_lists, :list_name_matcher_for_done
9
+
10
+ def configure
11
+ yield self
12
+ end
13
+
14
+ def set_trello_key_and_token(key, token)
15
+ Trello.configure do |cfg|
16
+ cfg.developer_public_key = key
17
+ cfg.member_token = token
18
+ end
19
+ end
20
+
21
+ def queue_time_lists
22
+ @queue_time_lists = [] if !@queue_time_lists
23
+ @queue_time_lists
24
+ end
25
+
26
+ def cycle_time_lists
27
+ @cycle_time_lists = [] if !@cycle_time_lists
28
+ @cycle_time_lists
29
+ end
30
+
31
+ def list_name_matcher_for_done
32
+ @list_name_matcher_for_done = /^Done/i if !@list_name_matcher_for_done
33
+ @list_name_matcher_for_done
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,69 @@
1
+ module TrelloLeadTime
2
+ class List
3
+ def self.from_trello_list(list)
4
+ List.new(list)
5
+ end
6
+
7
+ def initialize(list)
8
+ @trello_list = list
9
+ end
10
+
11
+ def name
12
+ @trello_list.name
13
+ end
14
+
15
+ def average_age
16
+ calculate_average_age_of_cards
17
+ end
18
+
19
+ def average_lead_time
20
+ calculate_average_lead_time_of_cards
21
+ end
22
+
23
+ def average_queue_time
24
+ calculate_average_queue_time_of_cards
25
+ end
26
+
27
+ def average_cycle_time
28
+ calculate_average_cycle_time_of_cards
29
+ end
30
+
31
+ private
32
+
33
+ def done_or_closed_cards
34
+ @_done_or_closed_cards ||= cards.select { |c| c.done? || c.closed? }
35
+ end
36
+
37
+ def cards
38
+ @_cards ||= @trello_list.cards.map { |c| TrelloLeadTime::Card.from_trello_card(c) }
39
+ end
40
+
41
+ def calculate_average_age_of_cards
42
+ times = done_or_closed_cards.map { |c| c.age_in_seconds }
43
+ average(times)
44
+ end
45
+
46
+ def calculate_average_lead_time_of_cards
47
+ times = done_or_closed_cards.map { |c| c.lead_time }
48
+ average(times)
49
+ end
50
+
51
+ def calculate_average_queue_time_of_cards
52
+ times = done_or_closed_cards.map { |c| c.queue_time }
53
+ average(times)
54
+ end
55
+
56
+ def calculate_average_cycle_time_of_cards
57
+ times = done_or_closed_cards.map { |c| c.cycle_time }
58
+ average(times)
59
+ end
60
+
61
+ def average(times)
62
+ avg = 0.0
63
+ if times.size > 0
64
+ avg = (times.inject(0.0) { |sum, el| sum + el } / times.size).round(0)
65
+ end
66
+ avg
67
+ end
68
+ end
69
+ end