kanban_metrics 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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?)