trelloscrum 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b7400785d9440390067e1d35fd8d33cbb6ca7930
4
+ data.tar.gz: 7e4ca5f8d0974e16adbbcae7962f24581a031f24
5
+ SHA512:
6
+ metadata.gz: 3fc10493800ca0dac2f36719fb2f849b147a227dbcaa73f6f5cf53c3b4f181573fe267d06180f0b3201029f15fcd2b8b3d98265b9a7a64c7ffaca92bb39de02d
7
+ data.tar.gz: 3398005874c404863abc6ec9fe4d9e1592421b536edc012e9f7b28c437511ce452d14b5e4e020b572fda1753895b87a83a13bad766ccb361fb75be7acc67bc40
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "pry"
data/bin/trelloscrum ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative '../lib/cli'
5
+
6
+ TrelloScrum::Cli.start()
data/lib/cli.rb ADDED
@@ -0,0 +1,130 @@
1
+ require 'thor'
2
+
3
+ require 'json'
4
+ require 'chronic'
5
+ require 'trello'
6
+
7
+ require_relative 'pdf'
8
+
9
+ module TrelloScrum
10
+ class Cli < Thor
11
+ class_option :"config", :default => "./config.json", :type => :string, :desc => "Path to config, default is local directory/config.json"
12
+ class_option :"verbose", :aliases => ["v"], :default => false, :type => :boolean, :desc => "Verbose output"
13
+
14
+
15
+ desc "pdf OUTFILE", "generate PDF for cards"
16
+ method_option :"only-estimated", :default => true, :type => :boolean, :desc => "Wether or not to output only estimates"
17
+ method_option :"list", :type => :string, :desc => "Listname to use"
18
+ method_option :"board", :type => :string, :desc => "Board id to use"
19
+ method_option :"filter-title", :type => :string, :desc => "Regexp to filter on titles, only show's cards matching title"
20
+
21
+ def pdf(outfile)
22
+ setup_trello
23
+ cards = get_cards
24
+ generate_pdf(cards, outfile)
25
+ end
26
+
27
+ desc "setup DEVELOPER_PUBLIC_KEY MEMBER_TOKEN [BOARD_ID]", "config trello"
28
+ long_desc <<-EOT
29
+ Generate the appropriate keys for Trello:
30
+
31
+ 1. Get the developer public key
32
+ \x5 1. Log in to Trello
33
+ \x5 2. Go to https://trello.com/1/appKey/generate
34
+ \x5 3. Save the developer public key
35
+
36
+ 2. Get the member token
37
+ \x5 1. Go to https://trello.com/1/connect?key=DEVELOPER_PUBLIC_KEY&name=TrelloScrumCard&response_type=token
38
+ Replace DEVELOPER_PUBLIC_KEY with the previously generated key
39
+ \x5 2. Click "Allow"
40
+ \x5 3. Save the member token
41
+
42
+ EOT
43
+ def setup(developer_public_key, member_token, board_id=nil)
44
+ self.config["developer_public_key"] = developer_public_key
45
+ self.config["member_token"] = member_token
46
+ self.config["board_id"] = board_id if board_id
47
+
48
+ File.open(options.config, "w") do |f|
49
+ f.write JSON.pretty_generate(self.config)
50
+ end
51
+
52
+ puts "New config written to #{options.config}"
53
+ end
54
+
55
+ protected
56
+
57
+ def log(msg)
58
+ puts msg if options.verbose
59
+ end
60
+
61
+ def setup_trello
62
+ if !config["developer_public_key"] || config["developer_public_key"].empty?
63
+ puts "Please make sure you have configured a developer public key (run setup help for more info)"
64
+ exit(1)
65
+ end
66
+
67
+ if !config["member_token"] || config["member_token"].empty?
68
+ puts "Please make sure you have configured a member token (run setup help for more info)"
69
+ exit(1)
70
+ end
71
+
72
+ Trello.configure do |c|
73
+ c.developer_public_key = config["developer_public_key"]
74
+ c.member_token = config["member_token"]
75
+ end
76
+ end
77
+
78
+ def config
79
+ if File.exist?(options.config)
80
+ @config ||= JSON.parse(File.read(options.config));
81
+ else
82
+ @config ||= {}
83
+ end
84
+ end
85
+
86
+ def get_cards
87
+ list_name = options.list || config["list_name"]
88
+
89
+ if !list_name || list_name.empty?
90
+ puts "Please enter a lisname or configurate one (use --list)"
91
+ exit(1)
92
+ end
93
+
94
+ board_id = options.board || config["board_id"]
95
+
96
+ if !board_id || board_id.empty?
97
+ puts "Please enter a board_id or configurate one (use --board)"
98
+ exit(1)
99
+ end
100
+
101
+ log "Getting cards from list #{list_name} of board #{board_id}"
102
+
103
+ board = Trello::Board.find(board_id)
104
+
105
+ list = board.lists.find{|l| l.name == list_name }
106
+
107
+ log "Found list: #{list ? "yes" : "no"}"
108
+
109
+ cards = list.cards.sort!{|a, b| a.pos <=> b.pos }
110
+
111
+ log "List contains #{cards.size} cards"
112
+
113
+ cards.find_all do |card|
114
+ keep = true
115
+ keep = false if options[:"only-estimated"] && !(card.name =~ /^\(\d+/)
116
+ keep = false if options[:"filter-title"] && !(card.name =~ Regexp.new(options[:"filter-title"]))
117
+ keep
118
+ end
119
+ end
120
+
121
+ def generate_pdf(cards, output_path)
122
+ pdf = Pdf.new
123
+ pdf.render_cards(cards)
124
+ pdf.save(output_path)
125
+ end
126
+
127
+
128
+
129
+ end
130
+ end
data/lib/pdf.rb ADDED
@@ -0,0 +1,203 @@
1
+ require 'prawn'
2
+ require 'prawn/table'
3
+
4
+ module TrelloScrum
5
+ class Pdf
6
+
7
+ attr_reader :doc, :options
8
+
9
+ def initialize(page_size = 'A4', options = {})
10
+
11
+ @options = {
12
+ base_font_size: 20
13
+ }.update(options)
14
+
15
+ @doc = Prawn::Document.new :page_size => page_size, :page_layout => :landscape
16
+
17
+ self.doc.font_families.update("FontAwesome" => {:normal => "#{File.dirname(__FILE__)}/../resources/fontawesome-webfont.ttf"})
18
+ self.doc.font "Helvetica", :size => self.options[:base_font_size]
19
+
20
+
21
+ end
22
+
23
+ def render_cards(cards)
24
+ cards.each_with_index do |card, i|
25
+ render_card(card)
26
+
27
+ # Start next card on new page
28
+ self.doc.start_new_page unless i == cards.size - 1
29
+ end
30
+ end
31
+
32
+ def save(filename)
33
+ self.doc.render_file filename
34
+ end
35
+
36
+ protected
37
+
38
+ def render_card(card)
39
+ points,client,title = parse_card_title(card.name)
40
+
41
+ puts "- #{points} :: #{client} :: #{title}"
42
+
43
+ box_width = 100
44
+
45
+ # Output storypoints
46
+ points_box_size = render_points_box(points)
47
+
48
+ self.doc.bounding_box([0, self.doc.cursor], :width => self.doc.bounds.width - points_box_size) do
49
+ self.doc.text(client, {
50
+ :size => self.options[:base_font_size],
51
+ :overflow => :expand
52
+ })
53
+
54
+ self.doc.text(title, {
55
+ :size => self.options[:base_font_size] * 2.25,
56
+ :style => :bold,
57
+ :overflow => :expand
58
+ })
59
+ end
60
+
61
+ # Output due date
62
+ render_due_date(card.due)
63
+
64
+ self.doc.move_down 10
65
+
66
+ if card.checklists.any?
67
+ # Take half the size of the remaining card for description, leave the rest fot checklists
68
+ # Yes, taking doc.cursor is weird, but keep in mind the coordinate system's origin is bottom left
69
+ desc_height = doc.cursor / 2
70
+ else
71
+ desc_height = doc.cursor
72
+ end
73
+
74
+ # Manually creating box here so we can get heigh later.
75
+ desc_box = Prawn::Text::Box.new(card.desc,
76
+ at: [0, self.doc.cursor],
77
+ width: self.doc.bounds.width,
78
+ height: desc_height,
79
+ overflow: :shrink_to_fit,
80
+ document: self.doc)
81
+
82
+
83
+ desc_box.render()
84
+
85
+ # Textbox doesn't move the cursor so we'll do it manually
86
+ self.doc.move_down desc_box.height
87
+
88
+ if card.checklists.any?
89
+ self.doc.move_down 20
90
+
91
+ card.checklists.each do |checklist|
92
+ render_checklist(checklist)
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ def render_checklist(checklist)
99
+ data = checklist.items.map do |item|
100
+ [
101
+ (item.state != "complete" ? "\uF096" : "\uF046"),
102
+ item.name
103
+ ]
104
+ end
105
+
106
+ if data.any?
107
+ self.doc.text(checklist.name, {
108
+ :style => :bold
109
+ })
110
+
111
+ table_height = self.doc.cursor
112
+
113
+ table = nil
114
+ real_table_height = self.doc.bounds.height + 1 # Should always be bigger than table_height
115
+ table_font_size = self.options[:base_font_size]
116
+ while(real_table_height > table_height && table_font_size > 8) do
117
+ table = Prawn::Table.new(data, self.doc) do |tbl|
118
+ tbl.width = self.doc.bounds.width
119
+ tbl.cells.size = table_font_size
120
+ tbl.cells.borders = []
121
+ if table_font_size < 15
122
+ tbl.cells.padding = [0,0,2,0]
123
+ tbl.column(0).padding = [1.5,0,2,0]
124
+ else
125
+ tbl.cells.padding = [0,0,5,0]
126
+ end
127
+ tbl.column(0).font = "FontAwesome"
128
+ tbl.column(0).width = table_font_size + 2
129
+ end
130
+ real_table_height = table.height
131
+ table_font_size -= 1
132
+ end
133
+
134
+ table.draw
135
+ end
136
+ end
137
+
138
+ def render_due_date(date)
139
+ return unless date
140
+
141
+ date_text = date.strftime("%-d %^B")
142
+
143
+ due_box_text = {
144
+ text: date_text,
145
+ color: "FF0000",
146
+ styles: [:bold],
147
+ style: :bold, # Hack to make it work for width measuring too...
148
+ size: self.options[:base_font_size] * 3
149
+ }
150
+
151
+ due_box_width = self.doc.width_of(due_box_text[:text], due_box_text)
152
+ due_box_height = self.doc.height_of_formatted([due_box_text], {})
153
+
154
+ due_box = Prawn::Text::Formatted::Box.new([due_box_text], {
155
+ at: [self.doc.bounds.right - due_box_width, self.doc.bounds.absolute_bottom],
156
+ width: due_box_width,
157
+ height: due_box_height,
158
+ disable_wrap_by_char: true,
159
+ align: :right,
160
+ overflow: :shrink_to_fit,
161
+ document: doc
162
+ })
163
+
164
+ due_box.render()
165
+ end
166
+
167
+ def render_points_box(points)
168
+ return 0 if points.to_s.empty?
169
+
170
+ points_box_size = 100
171
+ points_box_padding = 10
172
+
173
+ points_box = Prawn::Text::Box.new(points.to_s,
174
+ {
175
+ at: [self.doc.bounds.absolute_right - points_box_size + points_box_padding, self.doc.bounds.absolute_top - points_box_padding],
176
+ align: :center,
177
+ valign: :center,
178
+ disable_wrap_by_char: true,
179
+ width: points_box_size - 2 * points_box_padding,
180
+ height: points_box_size - 2 * points_box_padding,
181
+ size: 60,
182
+ style: :bold,
183
+ overflow: :shrink_to_fit,
184
+ document: self.doc
185
+ }
186
+ )
187
+
188
+ points_box.render
189
+
190
+ self.doc.stroke do
191
+ self.doc.rectangle [self.doc.bounds.absolute_right - points_box_size, self.doc.bounds.absolute_top], points_box_size, points_box_size
192
+ end
193
+
194
+ return points_box_size
195
+ end
196
+
197
+ def parse_card_title(title)
198
+ match = title.match(/^\s*(\((\d+)\))?\s*(\[(.*?)\])?\s*(.*)/)
199
+ [match[2], match[4], match[5]]
200
+ end
201
+
202
+ end
203
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module TrelloScrum
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/readme.md ADDED
@@ -0,0 +1,59 @@
1
+ # Trello SCRUM
2
+
3
+ Generates PDF's with (+/-) one page per card with title, body and checklists. Print 4 of them on an A4 for the best action.
4
+
5
+ ## Getting started
6
+
7
+ 1. Install the trelloscrum gem: `gem install trelloscrum`
8
+ 1. Create a `config.json` in the current directory by running `trelloscrum setup`. For more information on how to set up everything run `trelloscrum help setup`
9
+ 1. Run
10
+
11
+ ## Card format
12
+
13
+ To get a nice output for cards (see below) write card titles in the following format (all fields are optional).
14
+
15
+ `(STORYPOINTS) [CLIENT] Rest of the title`
16
+
17
+ Other supported (native) Trello fields are:
18
+
19
+ * The card body of course (markdown is not supported)
20
+ * Card checklists (works best with just one list)
21
+ * Due date
22
+
23
+ This is an example output for a card generated by TrelloScrum
24
+
25
+ ![Example card](https://github.com/flurin/trelloscrum/raw/master/doc/card_example.png)
26
+
27
+ ## Commandline options
28
+
29
+ ```
30
+ Commands:
31
+ trelloscrum help [COMMAND] # Describe available commands or one specific command
32
+ trelloscrum pdf OUTFILE # generate PDF for cards
33
+ trelloscrum setup DEVELOPER_PUBLIC_KEY MEMBER_TOKEN [BOARD_ID] # config trello
34
+
35
+ Options:
36
+ [--config=CONFIG] # Path to config, default is local directory/config.json
37
+ # Default: ./config.json
38
+ v, [--verbose], [--no-verbose] # Verbose output
39
+ ```
40
+
41
+ For more options run `trelloscrum help [COMMAND]`
42
+
43
+ ## Config.json
44
+
45
+ ```
46
+ {
47
+ "developer_public_key" : "",
48
+ "member_token" : "",
49
+ "list_name" : "",
50
+ "board_id" : ""
51
+ }
52
+ ```
53
+
54
+ ## License
55
+
56
+ - All code and documentation is licesned under the MIT license:
57
+ - http://opensource.org/licenses/mit-license.html
58
+ - The Font Awesome font is licensed under the SIL OFL 1.1:
59
+ - http://scripts.sil.org/OFL
Binary file
@@ -0,0 +1,34 @@
1
+ basedir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{basedir}/lib/version"
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "trelloscrum"
6
+ spec.version = TrelloScrum::VERSION
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.summary = "Creates product backlog cards from trello cards"
9
+ spec.files = Dir.glob("{bin,lib,resources}/**/**/*") +
10
+ ["trelloscrum.gemspec", "Gemfile", "readme.md"]
11
+ spec.require_path = "lib"
12
+
13
+ spec.executables = ["trelloscrum"]
14
+
15
+ spec.required_ruby_version = '>= 1.9.3'
16
+ spec.required_rubygems_version = ">= 1.3.6"
17
+
18
+ spec.authors = ["Flurin Egger"]
19
+ spec.email = ["flurin@digitpaint.nl"]
20
+ spec.licenses = ['MIT', 'SIL OFL']
21
+
22
+
23
+ spec.add_dependency "json", "~> 1.8.1"
24
+ spec.add_dependency "prawn", "~> 2.0.1"
25
+ spec.add_dependency "prawn-table", "~> 0.2.1"
26
+ spec.add_dependency "chronic", "~> 0.10.2"
27
+ spec.add_dependency "ruby-trello", "~> 1.1.2"
28
+ spec.add_dependency "thor", "~> 0.19.1"
29
+
30
+ spec.homepage = "https://github.com/flurin/trelloscrum"
31
+ spec.description = <<END_DESC
32
+ Generates PDF's with (+/-) one page per card with title, body and checklists. Print 4 of them on an A4 for the best action.
33
+ END_DESC
34
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trelloscrum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Flurin Egger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: prawn
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: prawn-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.2.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: chronic
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-trello
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.1.2
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.1.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: thor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 0.19.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 0.19.1
97
+ description: |2
98
+ Generates PDF's with (+/-) one page per card with title, body and checklists. Print 4 of them on an A4 for the best action.
99
+ email:
100
+ - flurin@digitpaint.nl
101
+ executables:
102
+ - trelloscrum
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - Gemfile
107
+ - bin/trelloscrum
108
+ - lib/cli.rb
109
+ - lib/pdf.rb
110
+ - lib/version.rb
111
+ - readme.md
112
+ - resources/fontawesome-webfont.ttf
113
+ - trelloscrum.gemspec
114
+ homepage: https://github.com/flurin/trelloscrum
115
+ licenses:
116
+ - MIT
117
+ - SIL OFL
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - '>='
126
+ - !ruby/object:Gem::Version
127
+ version: 1.9.3
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - '>='
131
+ - !ruby/object:Gem::Version
132
+ version: 1.3.6
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.2.2
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Creates product backlog cards from trello cards
139
+ test_files: []