pivotalprinter 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  pkg
3
3
  Gemfile.lock
4
4
  .rvmrc
5
+ .DS_Store
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  # vim: set ft=ruby:
2
2
 
3
- source "http://rubygems.org"
3
+ source :rubygems
4
4
 
5
5
  gemspec
6
+
7
+ gem 'utils'
@@ -1,3 +1,18 @@
1
+ === 0.3.0 2013-01-07
2
+
3
+ * Always show estimated points for all story types.
4
+ * Some refactoring of the code.
5
+ * Improved the images a bit (I think).
6
+ * Display project name on each story card.
7
+
8
+ === 0.2.0 2012-11-27
9
+
10
+ * Adds more information on the ticket.
11
+
12
+ === 0.1.8 2011-10-25
13
+
14
+ * Allow for ruby 1.9 compatibility.
15
+
1
16
  === 0.1.7 2011-07-19
2
17
 
3
18
  * use a newver prawn version
@@ -1,17 +1,21 @@
1
+ COPYING
2
+ Gemfile
1
3
  History.txt
2
4
  Manifest.txt
3
5
  PostInstall.txt
4
6
  README.rdoc
5
7
  Rakefile
6
8
  bin/pivotalprinter
7
- images/bug.png
8
- images/chore.png
9
- images/feature.png
10
- images/release.png
11
9
  lib/pivotalprinter.rb
12
10
  lib/pivotalprinter/client.rb
13
- lib/pivotalprinter/story.rb
11
+ lib/pivotalprinter/images/bug.png
12
+ lib/pivotalprinter/images/chore.png
13
+ lib/pivotalprinter/images/feature.png
14
+ lib/pivotalprinter/images/release.png
14
15
  lib/pivotalprinter/iteration.rb
16
+ lib/pivotalprinter/printer.rb
17
+ lib/pivotalprinter/story.rb
18
+ lib/pivotalprinter/version.rb
15
19
  pivotalprinter.gemspec
16
20
  script/console
17
21
  script/destroy
@@ -5,149 +5,4 @@ require 'pivotalprinter'
5
5
 
6
6
  class String; include Term::ANSIColor; end
7
7
 
8
- module Pivotalprinter
9
- class ConfigNeeded < StandardError; end
10
- class TokenMissing < StandardError; end
11
- class ProjectMissing < StandardError; end
12
- end
13
-
14
- module Pivotalprinter
15
- class Printer
16
- def run
17
- opts = parse_options
18
- yaml = load_yaml_config
19
- project,token = extract_config(opts, yaml)
20
- Pivotalprinter::Client.project = project
21
- Pivotalprinter::Client.token = token
22
- @stories = case ARGV.last
23
- when "done" then Pivotalprinter::Iteration.open('done').stories
24
- when "backlog" then Pivotalprinter::Iteration.open('backlog').stories
25
- when /^\d+$/ then Pivotalprinter::Story.open(ARGV.map(&:to_i))
26
- else Pivotalprinter::Iteration.open('current').stories
27
- end
28
- generate_output
29
- rescue ConfigNeeded
30
- print_config
31
- rescue TokenMissing
32
- Trollop::die :token, "token must be specified through config file or explicitly"
33
- rescue ProjectMissing
34
- Trollop::die :project, "project must be a valid number or one of the configured projects (#{(yaml.keys - ["default"]).join(", ")}) if no default is configured in $HOME/.pivotalprinterrc"
35
- rescue SystemExit
36
- rescue Exception
37
- puts "[!] There was an error while generating the PDF file... What happened was:".white.on_red
38
- raise
39
- end
40
-
41
- def print_config
42
- puts %{---\ndefault: &default\n token: "YOUR TOKEN HERE"\n project: 123\n\nsome_project:\n <<: *default # so you don't need to rewrite the token\n project: 789}
43
- end
44
-
45
- def parse_options
46
- opts = Trollop::options do
47
- version "pivotalprinter #{Pivotalprinter::VERSION} (c) 2010#{(year = Time.now.year) > 2010 ? "-#{year}" : ''} pkw.de"
48
- banner <<-EOS
49
- Pivotalprinter is an awesome program to print stories from the PivotalTracker.
50
-
51
- Usage:
52
- pivotalprinter [-p PROJECT] [-t TOKEN] [current|backlog|done|STORY]
53
- pivotalprinter --config > ~/.pivotalprinterrc
54
-
55
- Options:
56
- EOS
57
- opt :project, "Project Name defined in $HOME/.pivotalprinterrc or project number", :short => "-p", :type => :string
58
- opt :token, "Pivotaltracker Token", :short => "-t", :type => :string
59
- opt :config, "show example configuration", :short => "-c"
60
- end
61
-
62
- raise ConfigNeeded if opts[:config]
63
- opts
64
- end
65
-
66
- def load_yaml_config
67
- File.exist?("#{ENV["HOME"]}/.pivotalprinterrc") ? YAML::load_file("#{ENV["HOME"]}/.pivotalprinterrc") : {}
68
- end
69
-
70
- def extract_config(opts, yaml)
71
- if opts[:project].nil? && yaml.key?("default")
72
- return yaml["default"]["project"], yaml["default"]["token"]
73
- elsif opts[:project] =~ /[0-9]+/
74
- return opts[:project], (opts[:token] || yaml["default"]["project"])
75
- elsif yaml.key?(opts[:project])
76
- return yaml[opts[:project]]["project"], yaml[opts[:project]]["token"]
77
- else
78
- raise ProjectMissing
79
- end
80
- end
81
-
82
- def generate_output
83
- Prawn::Document.generate("/tmp/stories.pdf", :page_layout => :landscape, :margin => [25, 25, 50, 25], :page_size => 'A4') do |pdf|
84
- @num_cards_on_page = 0
85
- pdf.font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
86
- @stories.each_with_index do |story, i|
87
- # --- Split pages
88
- if i > 0 and i % 4 == 0
89
- # puts "New page..."
90
- pdf.start_new_page
91
- @num_cards_on_page = 1
92
- else
93
- @num_cards_on_page += 1
94
- end
95
-
96
- # --- Define 2x2 grid
97
- pdf.define_grid(:columns => 2, :rows => 2, :gutter => 42)
98
- # pdf.grid.show_all
99
-
100
- row = (@num_cards_on_page + 1) / 4
101
- column = i % 2
102
- padding = 12
103
- cell = pdf.grid( row, column )
104
- cell.bounding_box do
105
-
106
- pdf.stroke_color = "666666"
107
- pdf.stroke_bounds
108
-
109
- pdf.text_box story.name, :size => 14, :height => 50, :overflow => :truncate,
110
- :width => cell.width - (padding * 2) - 50,
111
- :at => [pdf.bounds.left + padding + 50, pdf.bounds.top - padding]
112
-
113
- pdf.text_box story.description, :size => 8, :overflow => :truncate,
114
- :width => cell.width - ( padding * 2), :height => cell.height - (padding * 7),
115
- :at => [pdf.bounds.left + padding, pdf.bounds.top - padding - 55]
116
-
117
- pdf.text_box "Created at: #{story.created_at.strftime('%F')}", :size => 8,
118
- :at => [pdf.bounds.left + padding, pdf.bounds.bottom + padding]
119
-
120
- pdf.text_box "ID: #{story.id}", :size => 8,
121
- :at => [pdf.bounds.left + 110, pdf.bounds.bottom + padding]
122
-
123
- pdf.text_box "State: #{story.current_state}", :size => 8,
124
- :at => [pdf.bounds.left + 170, pdf.bounds.bottom + padding]
125
-
126
- pdf.text_box "Requester: #{story.requested_by}", :size => 8,
127
- :at => [pdf.bounds.left + 240, pdf.bounds.bottom + padding]
128
-
129
-
130
- x, y = 32, 205
131
- if story.story_type == "feature"
132
- pdf.fill_color story.points_background
133
- pdf.fill_circle [x, y], 25
134
- pdf.font_size = 36
135
- pdf.font "Helvetica", :style => :bold
136
- pdf.fill_color 'ffffff'
137
- pdf.draw_text "#{story.points}", :at => [x - 10, y - 12]
138
- pdf.fill_color '000000'
139
- pdf.font_size = 14
140
- pdf.font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf", :style => :normal
141
- else
142
- pdf.image open("#{File.dirname(__FILE__)}/../images/#{story.story_type}.png"), :position => 5, :vposition => 5
143
- end
144
- end
145
- end
146
- end
147
- puts ">>> Generated PDF file in 'stories.pdf' with #{@stories.size} stories".black.on_green
148
- system "open /tmp/stories.pdf"
149
- end
150
- end
151
- end
152
-
153
- Pivotalprinter::Printer.new.run
8
+ Pivotalprinter::Printer.new.run(ARGV)
@@ -1,18 +1,13 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
-
4
1
  module Pivotalprinter
5
- require 'rubygems'
6
- require 'pivotalprinter/version'
7
- require 'term/ansicolor'
8
- require 'prawn'
9
- require 'prawn/layout/grid'
10
- require 'tins/xt/full'
11
- require 'trollop'
12
- require 'yaml'
13
- require "open-uri"
14
- require "nokogiri"
15
- require "pivotalprinter/client"
16
- require "pivotalprinter/iteration"
17
- require "pivotalprinter/story"
2
+ class ConfigNeeded < StandardError; end
3
+ class TokenMissing < StandardError; end
4
+ class ProjectMissing < StandardError; end
18
5
  end
6
+
7
+ require 'pivotalprinter/version'
8
+ require 'pivotalprinter/client'
9
+ require 'pivotalprinter/iteration'
10
+ require 'pivotalprinter/story'
11
+ require 'pivotalprinter/printer'
12
+ require 'term/ansicolor'
13
+ require 'tins/xt/full'
@@ -1,3 +1,6 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
1
4
  module Pivotalprinter
2
5
  class Client
3
6
  class << self
@@ -6,7 +9,8 @@ module Pivotalprinter
6
9
  def get(path, options={})
7
10
  raise TokenMissing if @token.nil?
8
11
  raise ProjectMissing if @project.nil?
9
- open("http://www.pivotaltracker.com/services/v3#{path}", 'X-TrackerToken' => @token, 'Content-Type' => 'application/xml').read
12
+ response = open("http://www.pivotaltracker.com/services/v3#{path}", 'X-TrackerToken' => @token, 'Content-Type' => 'application/xml').read
13
+ Nokogiri::XML.parse(response)
10
14
  end
11
15
  end
12
16
  end
@@ -1,24 +1,23 @@
1
1
  module Pivotalprinter
2
2
  class Iteration
3
-
3
+
4
4
  attr_accessor :stories
5
-
5
+
6
6
  def self.open(name)
7
7
  self.new(name)
8
8
  end
9
-
9
+
10
10
  def initialize(name)
11
11
  @stories = []
12
- extract_stories(Client.get "/projects/#{Client.project}/iterations/#{name}")
12
+ extract_stories Client.get("/projects/#{Client.project}/iterations/#{name}")
13
13
  end
14
-
14
+
15
15
  private
16
-
16
+
17
17
  def extract_stories(response)
18
- response = Nokogiri::XML.parse(response)
19
18
  response.css("story").each do |story|
20
19
  @stories << Story.new(story)
21
20
  end
22
21
  end
23
22
  end
24
- end
23
+ end
@@ -0,0 +1,151 @@
1
+ require 'trollop'
2
+ require 'yaml'
3
+ require 'prawn'
4
+ require 'prawn/layout/grid'
5
+
6
+ module Pivotalprinter
7
+ class Printer
8
+ def run(argv)
9
+ argv = argv.dup
10
+ opts = parse_options
11
+ yaml = load_yaml_config
12
+ project, token = extract_config(opts, yaml)
13
+ Pivotalprinter::Client.project = project
14
+ Pivotalprinter::Client.token = token
15
+ @project_name = (Client.get("/projects/#{project}") / 'project/name').text
16
+ @stories =
17
+ case argv.last
18
+ when "done" then Pivotalprinter::Iteration.open('done').stories
19
+ when "backlog" then Pivotalprinter::Iteration.open('backlog').stories
20
+ when /^\d+$/ then Pivotalprinter::Story.open(argv.map(&:to_i))
21
+ else Pivotalprinter::Iteration.open('current').stories
22
+ end
23
+ generate_output
24
+ rescue ConfigNeeded
25
+ print_config
26
+ rescue TokenMissing
27
+ Trollop::die :token, "token must be specified through config file or explicitly"
28
+ rescue ProjectMissing
29
+ Trollop::die :project, "project must be a valid number or one of the configured projects (#{(yaml.keys - ["default"]).join(", ")}) if no default is configured in $HOME/.pivotalprinterrc"
30
+ rescue Exception
31
+ puts "[!] There was an error while generating the PDF file... What happened was:".white.on_red
32
+ raise
33
+ end
34
+
35
+ def print_config
36
+ puts <<CONFIG
37
+ ---
38
+ default: &default
39
+ token: "YOUR TOKEN HERE"
40
+ project: 123
41
+
42
+ some_project:
43
+ <<: *default # so you don't need to rewrite the token
44
+ project: 789
45
+ CONFIG
46
+ end
47
+
48
+ def parse_options
49
+ opts = Trollop::options do
50
+ version "pivotalprinter #{Pivotalprinter::VERSION} (c) 2010#{(year = Time.now.year) > 2010 ? "-#{year}" : ''} pkw.de"
51
+ banner <<-EOS
52
+ Pivotalprinter is an awesome program to print stories from the PivotalTracker.
53
+
54
+ Usage:
55
+ pivotalprinter [-p PROJECT] [-t TOKEN] [current|backlog|done|STORY]
56
+ pivotalprinter --config > ~/.pivotalprinterrc
57
+
58
+ Options:
59
+ EOS
60
+ opt :project, "Project Name defined in $HOME/.pivotalprinterrc or project number",
61
+ :short => "-p", :type => :string
62
+ opt :token, "Pivotaltracker Token", :short => "-t", :type => :string
63
+ opt :config, "show example configuration", :short => "-c"
64
+ end
65
+
66
+ raise ConfigNeeded if opts[:config]
67
+ opts
68
+ end
69
+
70
+ def load_yaml_config
71
+ File.exist?("#{ENV["HOME"]}/.pivotalprinterrc") ? YAML::load_file("#{ENV["HOME"]}/.pivotalprinterrc") : {}
72
+ end
73
+
74
+ def extract_config(opts, yaml)
75
+ if opts[:project].nil? && yaml.key?("default")
76
+ return yaml["default"]["project"], yaml["default"]["token"]
77
+ elsif opts[:project] =~ /[0-9]+/
78
+ return opts[:project], (opts[:token] || yaml["default"]["project"])
79
+ elsif yaml.key?(opts[:project])
80
+ return yaml[opts[:project]]["project"], yaml[opts[:project]]["token"]
81
+ else
82
+ raise ProjectMissing
83
+ end
84
+ end
85
+
86
+ def generate_output
87
+ Prawn::Document.generate("/tmp/stories.pdf", :page_layout => :landscape, :margin => [25, 25, 50, 25], :page_size => 'A4') do |pdf|
88
+ @num_cards_on_page = 0
89
+ pdf.font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
90
+ @stories.each_with_index do |story, i|
91
+ # --- Split pages
92
+ if i > 0 and i % 4 == 0
93
+ # puts "New page..."
94
+ pdf.start_new_page
95
+ @num_cards_on_page = 1
96
+ else
97
+ @num_cards_on_page += 1
98
+ end
99
+
100
+ # --- Define 2x2 grid
101
+ pdf.define_grid(:columns => 2, :rows => 2, :gutter => 42)
102
+ # pdf.grid.show_all
103
+
104
+ row = (@num_cards_on_page + 1) / 4
105
+ column = i % 2
106
+ padding = 12
107
+ cell = pdf.grid( row, column )
108
+ cell.bounding_box do
109
+ pdf.stroke_color = "666666"
110
+ pdf.stroke_bounds
111
+
112
+ pdf.text_box "#@project_name ##{story.id}", :size => 10, :overflow => :truncate,
113
+ :width => cell.width - ( padding * 2),
114
+ :at => [pdf.bounds.left + padding, pdf.bounds.top - padding / 2 ]
115
+
116
+ pdf.text_box story.name, :size => 14, :height => 50, :overflow => :truncate,
117
+ :width => cell.width - (padding * 2) - 100,
118
+ :at => [pdf.bounds.left + padding + 50, pdf.bounds.top - 2 * padding]
119
+
120
+ pdf.text_box story.description.strip, :size => 8, :overflow => :truncate,
121
+ :width => cell.width - ( padding * 2), :height => cell.height - (padding * 8),
122
+ :at => [pdf.bounds.left + padding, pdf.bounds.top - 2 * padding - 55]
123
+
124
+ pdf.text_box "Created at: #{story.created_at.strftime('%F')}", :size => 8,
125
+ :at => [pdf.bounds.left + padding, pdf.bounds.bottom + padding]
126
+
127
+ pdf.text_box "State: #{story.current_state}", :size => 8,
128
+ :at => [pdf.bounds.left + 120, pdf.bounds.bottom + padding]
129
+
130
+ pdf.text_box "Requester: #{story.requested_by}", :size => 8,
131
+ :at => [pdf.bounds.left + 200, pdf.bounds.bottom + padding]
132
+
133
+ x, y = 32, 205 - padding
134
+ pdf.fill_color story.points_background
135
+ pdf.fill_circle [x, y], 25
136
+ pdf.font_size = 36
137
+ pdf.font "Helvetica", :style => :bold
138
+ pdf.fill_color 'ffffff'
139
+ pdf.draw_text "#{story.points}", :at => [x - 10, y - 12]
140
+ pdf.fill_color '000000'
141
+ pdf.font_size = 14
142
+ pdf.font "#{Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf", :style => :normal
143
+ pdf.image open("#{File.dirname(__FILE__)}/images/#{story.story_type}.png"), :position => cell.width - 51, :vposition => 5 + padding
144
+ end
145
+ end
146
+ end
147
+ puts ">>> Generated PDF file in 'stories.pdf' with #{@stories.size} stories".black.on_green
148
+ system "open /tmp/stories.pdf"
149
+ end
150
+ end
151
+ end
@@ -4,7 +4,7 @@ module Pivotalprinter
4
4
  class Story
5
5
  FIELDS = [ :name, :id, :story_type, :estimate, :current_state, :description, :requested_by, :labels, :created_at, :updated_at ]
6
6
  attr_accessor(*FIELDS)
7
-
7
+
8
8
  def initialize(xml)
9
9
  FIELDS.each do |field|
10
10
  value = xml.css("#{field}")
@@ -13,19 +13,18 @@ module Pivotalprinter
13
13
  created_at and self.created_at = Time.parse(created_at)
14
14
  updated_at and self.updated_at = Time.parse(updated_at)
15
15
  end
16
-
16
+
17
17
  def self.open(ids)
18
18
  ids.map do |id|
19
19
  response = Client.get "/projects/#{Client.project}/stories/#{id}"
20
- response = Nokogiri::XML.parse(response)
21
- self.new(response.css('story'))
20
+ self.new(response.css('story'))
22
21
  end
23
22
  end
24
-
23
+
25
24
  def points
26
25
  estimate.to_i >= 0 ? estimate.to_i : '?'
27
26
  end
28
-
27
+
29
28
  def points_background
30
29
  case points
31
30
  when '?' then 'cccccc'
@@ -1,3 +1,3 @@
1
1
  module Pivotalprinter
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pivotalprinter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-27 00:00:00.000000000 Z
12
+ date: 2013-01-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: term-ansicolor
@@ -140,13 +140,14 @@ files:
140
140
  - README.rdoc
141
141
  - Rakefile
142
142
  - bin/pivotalprinter
143
- - images/bug.png
144
- - images/chore.png
145
- - images/feature.png
146
- - images/release.png
147
143
  - lib/pivotalprinter.rb
148
144
  - lib/pivotalprinter/client.rb
145
+ - lib/pivotalprinter/images/bug.png
146
+ - lib/pivotalprinter/images/chore.png
147
+ - lib/pivotalprinter/images/feature.png
148
+ - lib/pivotalprinter/images/release.png
149
149
  - lib/pivotalprinter/iteration.rb
150
+ - lib/pivotalprinter/printer.rb
150
151
  - lib/pivotalprinter/story.rb
151
152
  - lib/pivotalprinter/version.rb
152
153
  - pivotalprinter.gemspec
@@ -169,7 +170,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
169
170
  version: '0'
170
171
  segments:
171
172
  - 0
172
- hash: 3495001522045933990
173
+ hash: -738262424533095811
173
174
  required_rubygems_version: !ruby/object:Gem::Requirement
174
175
  none: false
175
176
  requirements:
@@ -178,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
179
  version: '0'
179
180
  segments:
180
181
  - 0
181
- hash: 3495001522045933990
182
+ hash: -738262424533095811
182
183
  requirements: []
183
184
  rubyforge_project:
184
185
  rubygems_version: 1.8.24
Binary file
Binary file