kanban_metrics 0.1.0

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.
File without changes
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib' , __FILE__)
4
+ require 'kanban_metrics'
5
+
6
+ def optional_date_arg(argv)
7
+ argv ? Date.parse(argv) : nil
8
+ end
9
+
10
+
11
+ work_items = KanbanMetrics::CsvLoader.new.load(ARGV[1])
12
+
13
+ if ARGV[0] == "cfd"
14
+ command = KanbanMetrics::Commands::Cfd.new
15
+ view = KanbanMetrics::Commands::CfdView.new
16
+ elsif ARGV[0] == "io"
17
+ command = KanbanMetrics::Commands::Io.new
18
+ view = KanbanMetrics::Commands::IoView.new
19
+ elsif ARGV[0] == "leadtime"
20
+ command = KanbanMetrics::Commands::Leadtime.new
21
+ view = KanbanMetrics::Commands::LeadtimeView.new
22
+ end
23
+
24
+ chart_data = command.execute(work_items, optional_date_arg(ARGV[2]), optional_date_arg(ARGV[3]))
25
+ view.render(chart_data)
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'kanban_metrics/version'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'kanban_metrics'
10
+ s.version = KanbanMetrics::VERSION
11
+ s.date = '2013-09-09'
12
+ s.summary = "kanban_metrics-#{s.version}"
13
+ s.description = "A simple tool for processing data from the Kanban board (in csv) and providing metrics"
14
+ s.authors = ["Zsolt Fabok"]
15
+ s.email = 'me@zsoltfabok.com'
16
+ s.homepage = 'https://github.com/ZsoltFabok/kanban_metrics'
17
+ s.license = 'GPLv3'
18
+
19
+ s.files = `git ls-files`.split("\n").reject {|path| path =~ /\.gitignore$/ || path =~ /file$/ }
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_development_dependency('rspec', '~> 2.12')
25
+ s.add_development_dependency('turnip', '~> 1.1.0')
26
+ s.add_development_dependency('rake', '>= 10.0.3')
27
+ s.add_development_dependency('simplecov', '>= 0.7.1')
28
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+
3
+ require 'csv'
4
+ require 'kanban_metrics/work_item'
5
+ require 'kanban_metrics/date_helper'
6
+ require 'kanban_metrics/chart_data_helper'
7
+ require 'kanban_metrics/csv_loader'
8
+ require 'kanban_metrics/commands/cfd'
9
+ require 'kanban_metrics/commands/cfd_view'
10
+ require 'kanban_metrics/commands/io'
11
+ require 'kanban_metrics/commands/io_view'
12
+ require 'kanban_metrics/commands/leadtime'
13
+ require 'kanban_metrics/commands/leadtime_view'
@@ -0,0 +1,11 @@
1
+ module KanbanMetrics
2
+ class ChartDataHelper
3
+ def self.sort(chart_data)
4
+ ordered_chart_data = {}
5
+ chart_data.keys.sort.each do |key|
6
+ ordered_chart_data[key] = chart_data[key]
7
+ end
8
+ ordered_chart_data
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class Cfd
4
+ USED_ATTRIBUTES = [:committed, :started, :finished, :delivered]
5
+ def execute(work_items, start = nil, end_ = nil)
6
+ chart_data = create_empty_chart_data_with_all_the_dates(work_items)
7
+
8
+ work_items.each do |work_item|
9
+ USED_ATTRIBUTES.each_with_index do |attribute, index|
10
+ chart_data.keys.select {|date| attribute_lg_date?(work_item, attribute, date)}.each do |entry|
11
+ chart_data[entry][index] += 1
12
+ end
13
+ end
14
+ end
15
+
16
+ start = earliest_date(work_items) if start.nil?
17
+ end_ = latest_date(work_items) if end_.nil?
18
+ chart_data.select! do |date, values|
19
+ start <= date && date <= end_
20
+ end
21
+
22
+ ChartDataHelper.sort(chart_data)
23
+ end
24
+
25
+ private
26
+ def create_empty_chart_data_with_all_the_dates(work_items)
27
+ chart_data = {}
28
+ work_items.each do |work_item|
29
+ USED_ATTRIBUTES.each do |attribute|
30
+ date = work_item.send(attribute)
31
+ if date != nil && chart_data[date] == nil
32
+ chart_data[date] = [0, 0, 0, 0]
33
+ end
34
+ end
35
+ end
36
+ chart_data
37
+ end
38
+
39
+ def attribute_lg_date?(work_item, attribute, date)
40
+ work_item.send(attribute) && date >= work_item.send(attribute)
41
+ end
42
+
43
+ def earliest_date(work_items)
44
+ work_items.map {|work_item| work_item_dates(work_item)}.flatten.min
45
+ end
46
+
47
+ def latest_date(work_items)
48
+ work_items.map {|work_item| work_item_dates(work_item)}.flatten.max
49
+ end
50
+
51
+ def work_item_dates(work_item)
52
+ USED_ATTRIBUTES.map {|attribute| work_item.send(attribute)}.compact
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class CfdView
4
+ def render(chart_data)
5
+ chart_data.each do |date, values|
6
+ puts "#{date},#{values.join(',')}"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class Io
4
+ def execute(work_items, start = nil, end_ = nil)
5
+ chart_data = {}
6
+ work_items.each do |work_item|
7
+ in_ = "#{work_item.started.year}-#{work_item.started.cweek}"
8
+ out = "#{work_item.delivered.year}-#{work_item.delivered.cweek}"
9
+ if chart_data[in_].nil?
10
+ chart_data[in_] = [0, 0]
11
+ end
12
+
13
+ if chart_data[out].nil?
14
+ chart_data[out] = [0, 0]
15
+ end
16
+
17
+ chart_data[in_][0] += 1
18
+ chart_data[out][1] += 1
19
+ end
20
+
21
+ start = earliest_started(work_items) if start.nil?
22
+ end_ = latest_delivered(work_items) if end_.nil?
23
+ start_cweek = "#{start.year}-#{start.cweek}"
24
+ end_cweek = "#{end_.year}-#{end_.cweek}"
25
+
26
+ chart_data.select! do |date, values|
27
+ start_cweek <= date && date <= end_cweek
28
+ end
29
+
30
+ ChartDataHelper.sort(chart_data)
31
+ end
32
+
33
+ private
34
+ def earliest_started(work_items)
35
+ work_items.map {|work_item| work_item.started}.min
36
+ end
37
+
38
+ def latest_delivered(work_items)
39
+ work_items.map {|work_item| work_item.delivered}.max
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class IoView
4
+ def render(chart_data)
5
+ puts "week, in, out"
6
+ chart_data.keys.each do |cweek|
7
+ puts "#{cweek},#{chart_data[cweek].join(',')}"
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class Leadtime
4
+ def execute(work_items, start = nil, end_ = nil)
5
+ chart_data = {}
6
+ start = earliest_committed(work_items) if start.nil?
7
+ end_ = latest_delivered(work_items) if end_.nil?
8
+ work_items.each do |work_item|
9
+ if work_item_finished?(work_item) && work_item_delivered_in_time_frame?(work_item, start, end_)
10
+ work_item_life_period = work_item.committed..work_item.delivered
11
+ lead_time = DateHelper.weekdays_in_date_range(work_item_life_period)
12
+
13
+ if chart_data[lead_time] == nil
14
+ chart_data[lead_time] = 0
15
+ end
16
+
17
+ chart_data[lead_time] += 1
18
+ end
19
+ end
20
+ ChartDataHelper.sort(chart_data)
21
+ end
22
+
23
+ private
24
+ def work_item_finished?(work_item)
25
+ work_item.committed && work_item.delivered
26
+ end
27
+
28
+ def work_item_delivered_in_time_frame?(work_item, start, end_)
29
+ start <= work_item.delivered && work_item.delivered <= end_
30
+ end
31
+
32
+ def earliest_committed(work_items)
33
+ work_items.map {|work_item| work_item.committed}.compact.min
34
+ end
35
+
36
+ def latest_delivered(work_items)
37
+ work_items.map {|work_item| work_item.delivered}.compact.max
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ module KanbanMetrics
2
+ module Commands
3
+ class LeadtimeView
4
+ def render(chart_data)
5
+ chart_data.keys.each do |lead_time|
6
+ puts "#{lead_time},#{chart_data[lead_time]}"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ module KanbanMetrics
2
+ class CsvLoader
3
+ def load(csv_file)
4
+ work_items = []
5
+ CSV.foreach(csv_file, {:force_quotes => true, :headers => true}) do |row|
6
+ unless row.header_row?
7
+ data = {}
8
+ row.headers.each do |header|
9
+ if (row[header] && !row[header].empty?)
10
+ data[header.sub(" ", "_").to_sym] = row[header]
11
+ end
12
+ end
13
+ work_items << WorkItem.new(data)
14
+ end
15
+ end
16
+ work_items
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ module KanbanMetrics
2
+ class DateHelper
3
+ def self.weekdays_in_date_range(range)
4
+ weekdays_only(range).size
5
+ end
6
+
7
+ private
8
+ def self.weekdays_only(range)
9
+ range.select { |d| (1..5).include?(d.wday) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module KanbanMetrics
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,23 @@
1
+ require 'date'
2
+
3
+ module KanbanMetrics
4
+ class WorkItem
5
+ attr_accessor :id, :committed, :started, :finished, :delivered, :type, :back_count, :estimated_time, :spent_time
6
+
7
+ def initialize(map)
8
+ map.each do |key, value|
9
+ eval("@#{key}=\"#{value}\"")
10
+ # this didn't work: eval("@#{key}=\"#{Date.parse(value)}\"")
11
+ if key == :committed && @committed
12
+ @committed = Date.parse(@committed)
13
+ elsif key == :started && @started
14
+ @started = Date.parse(@started)
15
+ elsif key == :finished && @finished
16
+ @finished = Date.parse(@finished)
17
+ elsif key == :delivered && @delivered
18
+ @delivered = Date.parse(@delivered)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,119 @@
1
+ Feature: command line
2
+ Background:
3
+ Given I delete "test.csv"
4
+
5
+ Scenario: generate cfd csv
6
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
7
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-27,2011-05-27"
8
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
9
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
10
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
11
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
12
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
13
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
14
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
15
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
16
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
17
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
18
+ When I run the command "bin/kanban_metrics cfd test.csv"
19
+ Then I see "2011-05-23,1,0,0,0\n2011-05-24,7,0,0,0\n2011-05-25,8,0,0,0\n2011-05-26,9,4,4,4\n2011-05-27,9,6,6,6\n2011-05-30,10,8,8,8\n2011-05-31,11,11,11,11"
20
+
21
+ Scenario: generate cfd csv in a time window
22
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
23
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-27,2011-05-27"
24
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
25
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
26
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
27
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
28
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
29
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
30
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
31
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
32
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
33
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
34
+ When I run the command "bin/kanban_metrics cfd test.csv 2011-05-26 2011-05-29"
35
+ Then I see "2011-05-26,9,4,4,4\n2011-05-27,9,6,6,6"
36
+
37
+ Scenario: generate cfd with partial data
38
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
39
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-27,2011-05-27"
40
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,"
41
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,"
42
+ And the line in "test.csv" ",2011-05-24,2011-05-26,,"
43
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
44
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
45
+ And the line in "test.csv" ",2011-05-24,,,"
46
+ When I run the command "bin/kanban_metrics cfd test.csv"
47
+ Then I see "2011-05-24,5,0,0,0\n2011-05-26,6,2,1,1\n2011-05-27,6,3,2,2\n2011-05-30,7,5,4,3\n2011-05-31,7,6,5,3"
48
+
49
+ Scenario: generate input output
50
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
51
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-31,2011-05-31"
52
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
53
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
54
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
55
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
56
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
57
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
58
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
59
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
60
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
61
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
62
+ When I run the command "bin/kanban_metrics io test.csv"
63
+ Then I see "week, in, out\n2011-21,6,5\n2011-22,5,6"
64
+
65
+ Scenario: generate input output in a time window
66
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
67
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-31,2011-05-31"
68
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
69
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
70
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
71
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
72
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
73
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
74
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
75
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
76
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
77
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
78
+ When I run the command "bin/kanban_metrics io test.csv 2011-05-07 2011-05-29"
79
+ Then I see "week, in, out\n2011-21,6,5"
80
+
81
+ Scenario: lead time histogram
82
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
83
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-31,2011-05-31"
84
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
85
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
86
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
87
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
88
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
89
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
90
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
91
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
92
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
93
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
94
+ When I run the command "bin/kanban_metrics leadtime test.csv"
95
+ Then I see "1,2\n3,4\n4,1\n5,3\n6,1"
96
+
97
+ Scenario: lead time histogram in a time window
98
+ Given the line in "test.csv" "id,committed,started,finished,delivered,type,back count,estimated time,spent time"
99
+ And the line in "test.csv" ",2011-05-26,2011-05-27,2011-05-31,2011-05-31"
100
+ And the line in "test.csv" ",2011-05-24,2011-05-30,2011-05-30,2011-05-30"
101
+ And the line in "test.csv" ",2011-05-24,2011-05-31,2011-05-31,2011-05-31"
102
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
103
+ And the line in "test.csv" ",2011-05-30,2011-05-30,2011-05-30,2011-05-30"
104
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
105
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
106
+ And the line in "test.csv" ",2011-05-25,2011-05-31,2011-05-31,2011-05-31"
107
+ And the line in "test.csv" ",2011-05-23,2011-05-27,2011-05-27,2011-05-27"
108
+ And the line in "test.csv" ",2011-05-31,2011-05-31,2011-05-31,2011-05-31"
109
+ And the line in "test.csv" ",2011-05-24,2011-05-26,2011-05-26,2011-05-26"
110
+ When I run the command "bin/kanban_metrics leadtime test.csv 2011-05-07 2011-05-28"
111
+ Then I see "3,4\n5,1"
112
+
113
+ # Scenario: Little, stability, ageing
114
+
115
+ # Scenario: phase based cycle time distribution
116
+
117
+ # Scenario: flow efficiency
118
+
119
+ # Scenario: deadline (behind?)