pivotal_reporting 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ gem 'pivotal-tracker'
4
+
5
+ group :development do
6
+ gem 'jeweler', '~> 1.8.3'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (3.0.0)
5
+ git (1.2.5)
6
+ happymapper (0.4.0)
7
+ libxml-ruby (~> 2.0)
8
+ jeweler (1.8.3)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ rdoc
13
+ json (1.6.5)
14
+ libxml-ruby (2.2.2)
15
+ mime-types (1.17.2)
16
+ nokogiri (1.5.0)
17
+ pivotal-tracker (0.4.1)
18
+ builder
19
+ builder
20
+ happymapper (>= 0.3.2)
21
+ happymapper (>= 0.3.2)
22
+ nokogiri (>= 1.4.3)
23
+ nokogiri (~> 1.4)
24
+ rest-client (~> 1.6.0)
25
+ rest-client (~> 1.6.0)
26
+ rake (0.9.2.2)
27
+ rdoc (3.11)
28
+ json (~> 1.4)
29
+ rest-client (1.6.7)
30
+ mime-types (>= 1.16)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ jeweler (~> 1.8.3)
37
+ pivotal-tracker
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ ## Pivotal Reporting
2
+
3
+ This is a simple ruby script I use to produce a weekly report on what us
4
+ developers have been up to.
5
+
6
+ ### Usage
7
+
8
+ bundle exec ./bin/pivotal_report.rb -t <pivotal token> -p <project id>
9
+
10
+ Where `<pivotal token>` is your pivotal API token available at the end
11
+ of [your profile page][1], and `<project id>` is the id of your project (I
12
+ just look at the URL when I'm examining the project, like in
13
+ https://www.pivotaltracker.com/projects/**12345**, 12345 is the project
14
+ id.)
15
+
16
+
17
+ ### Output
18
+
19
+ You'll get four sections: Discussion Items, Story Bullets, PPU Table,
20
+ and In Progress. The first two are specific to my team's process (we
21
+ pre- and post- estimate stories to improve our estimation process and
22
+ create a feedback loop for production tokens). The PPU Table and In
23
+ Progress sections are probably generically useful.
24
+
25
+ #### The PPU Table
26
+
27
+ PPU is *Points Per User*. Here's an example table:
28
+
29
+ | Elvis | Frank | Ron | Bob | Bill | Reggie | Total || Feature | Bug | Chore |
30
+ client | 0 | 5 | 9 | 0 | 8 | 3 | 25 || 22 | 0 | 3 |
31
+ fires | 0 | 1 | 1 | 0 | 0 | 0 | 2 || 0 | 1 | 1 |
32
+ operations | 10 | 1 | 0 | 5 | 0 | 1 | 17 || 12 | 2 | 3 |
33
+ ops:intake | 2 | 0 | 0 | 0 | 0 | 0 | 2 || 2 | 0 | 0 |
34
+ performance | 0 | 0 | 0 | 0 | 1 | 0 | 1 || 1 | 0 | 0 |
35
+ servers | 0 | 0 | 0 | 2 | 0 | 0 | 2 || 0 | 0 | 2 |
36
+ sourcing | 0 | 0 | 0 | 1 | 0 | 0 | 1 || 0 | 0 | 1 |
37
+ ------------+-------+-------+-----+-----+------+--------+--------++---------+-----+-------+
38
+ Total | 12 | 7 | 10 | 8 | 9 | 4 | 50 || 37 | 3 | 10 |
39
+
40
+ This table is about the stories in the most recently completed
41
+ iteration. The first Y axis is the unique labels in the iteration, the X
42
+ axis is first the developers who contributed and then a breakdown of
43
+ story type. The numbers in the body of the table are the points
44
+ allocated to that combination of label and either developer or story
45
+ type. There are of course a pair of totals columns.
46
+
47
+ This table is useful in our process of both planning for development
48
+ capacity and monitoring usage. I take this report and add a column for
49
+ planned points by label and over/under by label. We then use this
50
+ analysis to discuss the tech department's future planning and modify our
51
+ plans and expectations.
52
+
53
+ #### In Progress
54
+
55
+ This is just a sum of the points allocated to stories that are currently
56
+ started, delivered, or unaccepted. I use this to show the current state
57
+ of projects in progress during my iteration planning meeting.
58
+
59
+ [1]: https://www.pivotaltracker.com/profile
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "pivotal_reporting"
18
+ gem.homepage = "http://github.com/joshsz/pivotal_reporting"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A command-line reporting tool for @pivotaltracker}
21
+ gem.description = %Q{Please see the README. This tool generates some reports that could be useful if you're managing a team of developers using Pivotal Tracker}
22
+ gem.email = "joshsz@gmail.com"
23
+ gem.authors = ["Joshua Szmajda"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ # require 'rspec/core'
29
+ # require 'rspec/core/rake_task'
30
+ # RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ # spec.pattern = FileList['spec/**/*_spec.rb']
32
+ # end
33
+
34
+ # RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ # spec.pattern = 'spec/**/*_spec.rb'
36
+ # spec.rcov = true
37
+ # end
38
+
39
+ # task :default => :spec
40
+
41
+ # require 'rdoc/task'
42
+ # Rake::RDocTask.new do |rdoc|
43
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ # rdoc.rdoc_dir = 'rdoc'
46
+ # rdoc.title = "pivotal_reporting_2 #{version}"
47
+ # rdoc.rdoc_files.include('README*')
48
+ # rdoc.rdoc_files.include('lib/**/*.rb')
49
+ # end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ opts = {}
5
+ parser = OptionParser.new do |os|
6
+ os.banner =<<-EOT
7
+ Usage: pivotal_report.rb [options]
8
+ EOT
9
+
10
+ os.on('-t [TOKEN]', '--token [TOKEN]', 'Set API token') do |v|
11
+ opts[:token] = v
12
+ end
13
+
14
+ os.on('-p [PROJECT ID]', '--project-id [PROJECT ID]', 'Set Project ID') do |v|
15
+ opts[:project_id] = v
16
+ end
17
+ end
18
+
19
+ begin
20
+ parser.parse!
21
+ if opts[:token].nil? || opts[:project_id].nil?
22
+ puts parser
23
+ exit
24
+ end
25
+ end
26
+
27
+ require 'rubygems'
28
+ require 'bundler/setup'
29
+ require 'pivotal-tracker'
30
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'pivotal_report')
31
+
32
+ app = PivotalReport.new(opts)
33
+ app.report
@@ -0,0 +1,158 @@
1
+ class PivotalTracker::Story
2
+ def initials
3
+ owned_by.split(/ /).map{|e| e[0] }.join
4
+ end
5
+ end
6
+ class PivotalReport
7
+ def initialize(options)
8
+ PivotalTracker::Client.token = options[:token]
9
+ @project = PivotalTracker::Project.find(options[:project_id])
10
+ end
11
+
12
+ POST_EST = 'epost'
13
+
14
+ def report
15
+ show_discussion_items
16
+ separator
17
+ show_story_bullets
18
+ separator
19
+ show_ppu_table
20
+ separator
21
+ show_stories_in_progress
22
+ end
23
+
24
+ def show_accounting_breakdown
25
+ # TODO
26
+ raise 'Unimplemented!'
27
+ end
28
+
29
+ def separator
30
+ puts "\n-----\n\n"
31
+ end
32
+
33
+ def show_discussion_items
34
+ puts "Discussion Items"
35
+ puts ""
36
+ estimates = stories.sort{|a,b| a.owned_by <=> b.owned_by }.map(&:labels).map{|e| e.to_s.split(/,/) }.flatten.map(&:strip).compact.uniq.select{|l| l =~ /^(#{POST_EST}|e\d)$/ }.sort
37
+ estimates.each do |est|
38
+ set = stories_with_label(est)
39
+ set.each do |story|
40
+ puts "(\e[31m#{story.estimate}\e[0m: \e[32m#{est}\e[0m) #{story.name.strip} (\e[33m#{story.initials}\e[0m) [\e[34m#{story.labels}\e[0m]"
41
+ end
42
+ end
43
+ end
44
+
45
+ def show_story_bullets
46
+ puts "Story Bullets"
47
+ puts ""
48
+ lis = nil
49
+ stories.sort{|a,b| a.owned_by <=> b.owned_by }.each do |story|
50
+ if story.initials != lis
51
+ lis = story.initials
52
+ puts "#{story.initials}:"
53
+ end
54
+ puts "#{story.story_type.to_s[0]} #{story.name.strip} (#{story.estimate} point#{story.estimate > 1 ? 's' : ''})"
55
+ end
56
+ end
57
+
58
+ def show_ppu_table
59
+ puts "PPU Table"
60
+
61
+ categories = stories.map(&:labels).map{|e| e.to_s.split(/,/) }.flatten.map(&:strip).compact.uniq.reject{|l| l =~ /^(#{POST_EST}|e\d)$/ }.sort
62
+ people = stories.map(&:owned_by).uniq.sort.map{|p| [p, p.split(/ /).first]}
63
+ c_width = (['Total'] + categories).map(&:length).max
64
+
65
+ sseen = []
66
+
67
+ o = []
68
+ o << "".ljust(c_width)
69
+ o << " | "
70
+ people.each {|p| o << p[1]; o << " | " }
71
+ o << " Total || Feature | Bug | Chore |"
72
+ puts o.join
73
+
74
+ categories.each do |cat|
75
+ o = []
76
+ o << cat.ljust(c_width)
77
+ o << " | "
78
+ people.each do |person, short|
79
+ o << stories_for_user(person, stories_with_label(cat)).map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust(short.length)
80
+ o << " | "
81
+ end
82
+ set = stories_with_label(cat)
83
+ sseen += set
84
+ o << set.map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust('Total '.length)
85
+ o << " || "
86
+ %w{feature bug chore}.each do |type|
87
+ o << stories_with_label(cat, stories_for_type(type)).map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust(type.length)
88
+ o << " | "
89
+ end
90
+ puts o.join
91
+ end
92
+
93
+ o = []
94
+ o << "-" * c_width
95
+ o << "-+-"
96
+ people.each do |person, short|
97
+ o << "-" * short.length
98
+ o << "-+-"
99
+ end
100
+ o << "-------++---------+-----+-------+"
101
+ puts o.join
102
+
103
+ o = []
104
+ o << "Total".ljust(c_width)
105
+ o << " | "
106
+ people.each do |person, short|
107
+ o << stories_for_user(person).map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust(short.length)
108
+ o << " | "
109
+ end
110
+ o << stories.map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust('Total '.length)
111
+ o << " || "
112
+ %w{feature bug chore}.each do |type|
113
+ o << stories_for_type(type).map(&:estimate).inject(0){|s,e| s + e }.to_s.rjust(type.length)
114
+ o << " | "
115
+ end
116
+ puts o.join
117
+
118
+ missing = stories - sseen
119
+ if missing.any?
120
+ puts "Missing:"
121
+ puts missing.map(&:name).join("\n")
122
+ end
123
+
124
+ end
125
+
126
+ def show_stories_in_progress
127
+ in_progress = @project.stories.all(:state => ['started', 'delivered'])
128
+ points_in_progress = in_progress.map(&:estimate).inject(0){|s,e| s+e}
129
+
130
+ puts "In Progress: #{points_in_progress} points"
131
+ end
132
+
133
+
134
+
135
+ private
136
+ def iteration
137
+ @iteration ||= PivotalTracker::Iteration.done(@project, :offset => '-1').first
138
+ end
139
+
140
+ def stories
141
+ @stories ||= iteration.stories
142
+ end
143
+
144
+ def stories_with_label(label, set=nil)
145
+ set ||= stories
146
+ set.select{|s| s.labels =~ /#{label}/ }
147
+ end
148
+
149
+ def stories_for_user(user, set=nil)
150
+ set ||= stories
151
+ set.select{|s| s.owned_by == user }
152
+ end
153
+
154
+ def stories_for_type(type, set=nil)
155
+ set ||= stories
156
+ set.select{|s| s.story_type == type }
157
+ end
158
+ end
@@ -0,0 +1,50 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "pivotal_reporting"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Joshua Szmajda"]
12
+ s.date = "2012-02-07"
13
+ s.description = "Please see the README. This tool generates some reports that could be useful if you're managing a team of developers using Pivotal Tracker"
14
+ s.email = "joshsz@gmail.com"
15
+ s.executables = ["pivotal_report"]
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "bin/pivotal_report",
26
+ "lib/pivotal_report.rb",
27
+ "pivotal_reporting.gemspec"
28
+ ]
29
+ s.homepage = "http://github.com/joshsz/pivotal_reporting"
30
+ s.licenses = ["MIT"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = "1.8.10"
33
+ s.summary = "A command-line reporting tool for @pivotaltracker"
34
+
35
+ if s.respond_to? :specification_version then
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
39
+ s.add_runtime_dependency(%q<pivotal-tracker>, [">= 0"])
40
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
41
+ else
42
+ s.add_dependency(%q<pivotal-tracker>, [">= 0"])
43
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
44
+ end
45
+ else
46
+ s.add_dependency(%q<pivotal-tracker>, [">= 0"])
47
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
48
+ end
49
+ end
50
+
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal_reporting
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joshua Szmajda
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: pivotal-tracker
16
+ requirement: &21831180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *21831180
25
+ - !ruby/object:Gem::Dependency
26
+ name: jeweler
27
+ requirement: &21830700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.8.3
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *21830700
36
+ description: Please see the README. This tool generates some reports that could be
37
+ useful if you're managing a team of developers using Pivotal Tracker
38
+ email: joshsz@gmail.com
39
+ executables:
40
+ - pivotal_report
41
+ extensions: []
42
+ extra_rdoc_files:
43
+ - README.md
44
+ files:
45
+ - Gemfile
46
+ - Gemfile.lock
47
+ - README.md
48
+ - Rakefile
49
+ - VERSION
50
+ - bin/pivotal_report
51
+ - lib/pivotal_report.rb
52
+ - pivotal_reporting.gemspec
53
+ homepage: http://github.com/joshsz/pivotal_reporting
54
+ licenses:
55
+ - MIT
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ segments:
67
+ - 0
68
+ hash: -1627381237856098628
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.10
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: A command-line reporting tool for @pivotaltracker
81
+ test_files: []