tps_reporter 0.0.2

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.
data/HISTORY.md ADDED
@@ -0,0 +1,4 @@
1
+ v0.0.2 - Feb 04, 2012
2
+ ---------------------
3
+
4
+ Initial.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Task progress sheet reporter
2
+
3
+ ![TPS report](https://img.skitch.com/20120203-nr24dn9u7euchmqa516718unpe.png)
4
+
5
+ We often need to make regular reports of things done for our projects at work. I
6
+ hate doing these by hand. This tool lets us build these reports from YAML files.
7
+
8
+ Get started
9
+ -----------
10
+
11
+ Install TPS (Ruby):
12
+
13
+ $ gem install tps_reporter
14
+
15
+ ...then generate a sample file. (or create `tasks.yml` based on [this][s]
16
+ sample.)
17
+
18
+ $ tps sample
19
+
20
+ Edit it, then generate the report:
21
+
22
+ $ tps open
23
+
24
+ [s]: https://github.com/rstacruz/tps_reporter/blob/master/data/sample.yml
25
+
26
+ Format
27
+ ------
28
+
29
+ The tasks file, usually `tasks.yml`, is in YAML format.
30
+
31
+ Tasks are always keys (ie, they all end in `:`). They can be nested as far
32
+ as you like.
33
+
34
+ ``` yaml
35
+ Edit users:
36
+ Register and signup:
37
+ Login and logout:
38
+ ```
39
+
40
+ To define task metadata for *leaf* tasks, add it as an array inside the task:
41
+
42
+ ``` yaml
43
+ Manage employees: [done]
44
+ ```
45
+
46
+ Or for *branch* tasks, add it under the `_` task:
47
+
48
+ ``` yaml
49
+ Manage employees:
50
+ _: [done]
51
+ Creating employees:
52
+ Editing employees:
53
+ ```
54
+
55
+ The metadata is just a simple YAML array that you can conveniently define using
56
+ `[tag1, tag2, etc]`. Allowed metadata are:
57
+
58
+ - `done`
59
+ - `in progress`
60
+ - `pt/2839478` *(Pivotal tracker ID. Links to a Pivotal tracker story.)*
61
+ - `0pt` *(points; influences percentage. needs to end in __pt__ or __pts__.)*
62
+ - `10%` *(task progress. implies __in progress__.)*
63
+
64
+ Example:
65
+
66
+ ``` yaml
67
+ Creating employees: [40%]
68
+ Editing employees: [done, 2pts]
69
+ ```
70
+
71
+ Exporting to PDF or image
72
+ -------------------------
73
+
74
+ If you're on a Mac, install [Paparazzi](http://derailer.org/paparazzi)
75
+ and use the `tps paparazzi` command. This will open the report in Paparazzi
76
+ where you can save or copy it as an image, or PDF.
77
+
78
+ Command line
79
+ ------------
80
+
81
+ There's also a command line reporter that you can access via `tps print`. It
82
+ looks like this:
83
+
84
+ ![Comamnd line reporter][cli]
85
+
86
+ [cli]: https://img.skitch.com/20120204-ccb2guerhrjmj3rht3e4ies4ur.png
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ desc "Invokes the test suite in multiple RVM environments"
2
+ task :'test!' do
3
+ # Override this by adding RVM_TEST_ENVS=".." in .rvmrc
4
+ envs = ENV['RVM_TEST_ENVS'] || '1.9.2@sinatra,1.8.7@sinatra'
5
+ puts "* Testing in the following RVM environments: #{envs.gsub(',', ', ')}"
6
+ system "rvm #{envs} rake test" or abort
7
+ end
8
+
9
+ desc "Runs tests"
10
+ task :test do
11
+ Dir['test/*_test.rb'].each { |f| load f }
12
+ end
13
+
14
+ task :default => :test
data/bin/tps ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'tps'
4
+
5
+ module Params
6
+ def extract(what) i = index(what) and slice!(i, 2)[1] end;
7
+ def first_is(what) shift if first == what; end
8
+ end
9
+
10
+ ARGV.extend Params
11
+
12
+ module TPS::Command
13
+ extend self
14
+
15
+ def help
16
+ puts "Usage: tps <command> [-f filename] [-o output]"
17
+ puts ""
18
+ puts "Commands:"
19
+ puts " html Builds HTML"
20
+ puts " open Builds HTML and opens it in the browser"
21
+ puts " paparazzi Builds HTML and opens it in Paparazzi (Mac)"
22
+ puts " print Prints the report to the console."
23
+ puts ""
24
+ puts "Options (optional):"
25
+ puts " -f FILE Specifies the input file. Defaults to tasks.yml."
26
+ puts " -o/--output OUTPUT Specifies the output HTML file."
27
+ puts ""
28
+ end
29
+
30
+ def html
31
+ t = get_tasks
32
+ path = output { |file| file.write t.to_html }
33
+ info "Wrote to '#{path}'."
34
+
35
+ path
36
+ end
37
+
38
+ def sample
39
+ require 'fileutils'
40
+ fn = tasks_filename
41
+
42
+ if File.exists?(fn)
43
+ err "Error: #{fn} already exists."
44
+ exit 130
45
+ end
46
+
47
+ FileUtils.cp TPS.root('data', 'sample.yml'), fn
48
+ info "Created '#{fn}'."
49
+ info "Edit it, then use `tps html` to generate HTML from it."
50
+ end
51
+
52
+ def open
53
+ fn = html
54
+ open_file fn
55
+ end
56
+
57
+ def paparazzi
58
+ fn = html
59
+ open_file "paparazzi:(minwidth=1,minheight=1)#{fn}"
60
+ end
61
+
62
+ def print
63
+ t = get_tasks
64
+ reporter = TPS::CliReporter.new(t)
65
+ reporter.print
66
+ end
67
+
68
+ private
69
+ def err(str)
70
+ $stdout << "#{str}\n"
71
+ end
72
+
73
+ def info(str)
74
+ puts str
75
+ end
76
+
77
+ def tasks_filename
78
+ ARGV.extract('-f') ||
79
+ ENV['TPS_FILE'] ||
80
+ Dir['./{Tasksfile,tasks.yml}'].first ||
81
+ "tasks.yml"
82
+ end
83
+
84
+ def output(&blk)
85
+ fn = ARGV.extract('--output') || ARGV.extract('-o') || get_temp_filename
86
+
87
+ File.open(fn, 'w', &blk)
88
+ fn
89
+ end
90
+
91
+ def get_tasks
92
+ fn = tasks_filename
93
+ if !File.exists?(fn)
94
+ err "No tasks file found."
95
+ err "Create a sample using `tsp sample`."
96
+ exit 256
97
+ end
98
+
99
+ begin
100
+ TPS::TaskList.new yaml: fn
101
+ rescue => e
102
+ err "Parse error: #{e.message}"
103
+ exit 256
104
+ end
105
+ end
106
+
107
+ def opener
108
+ program = %w[open xdg-open start].detect { |cmd| `which #{cmd}` }
109
+ unless program
110
+ err "No opener found."
111
+ exit 256
112
+ end
113
+
114
+ program
115
+ end
116
+
117
+ def open_file(file)
118
+ require 'shellwords'
119
+ system "#{opener} #{file.shellescape}"
120
+ end
121
+
122
+ def get_temp_filename
123
+ require 'tmpdir'
124
+ File.join Dir.tmpdir, "tasks-#{'%x' % [rand * 2**48]}.html"
125
+ end
126
+ end
127
+
128
+ if ARGV.first_is('html')
129
+ TPS::Command.html
130
+
131
+ elsif ARGV.first_is('open')
132
+ TPS::Command.open
133
+
134
+ elsif ARGV.first_is('sample')
135
+ TPS::Command.sample
136
+
137
+ elsif ARGV.first_is('print')
138
+ TPS::Command.print
139
+
140
+ elsif ARGV.first_is('paparazzi')
141
+ TPS::Command.paparazzi
142
+
143
+ else
144
+ TPS::Command.help
145
+ end
data/data/index.haml ADDED
@@ -0,0 +1,182 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %script{src: "http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js"}
5
+
6
+ :javascript
7
+ $("td").live('click', function() {
8
+ $(this).closest('tr').toggleClass('highlight');
9
+ });
10
+
11
+ %style
12
+ :plain
13
+ body {
14
+ padding: 0;
15
+ margin: 0; }
16
+
17
+ body, td {
18
+ font-family: pt sans, sans-serif;
19
+ color: #333;
20
+ line-height: 13.5pt;
21
+ font-size: 9pt; }
22
+
23
+ table {
24
+ border: solid 2px #aaa;
25
+ padding: 2px;
26
+
27
+ background: #fafafa;
28
+ margin: 0;
29
+ width: 600px;
30
+ border-collapse: collapse; }
31
+
32
+ table td, table th {
33
+ padding: 5px 10px;
34
+ border-top: solid 1px #eee; }
35
+
36
+ /* Columns */
37
+ tr>.task { width: 50%; text-align: left; }
38
+ tr>.points { width: 9%; }
39
+ tr>.progress { width: 18%; }
40
+ tr>.owner { display: none; }
41
+
42
+ /* Indentation */
43
+ .level-1 .task { padding-left: 25px; }
44
+ .level-2 .task { padding-left: 50px; }
45
+ .level-3 .task { padding-left: 75px; }
46
+ .level-4 .task { padding-left: 100px; }
47
+
48
+ /* Overrides for parents */
49
+ .milestone td.task,
50
+ .feature td.task {
51
+ font-weight: bold;
52
+ font-size: 1.1em; }
53
+
54
+ tr.milestone td,
55
+ tr.feature td {
56
+ border-top: solid 1px #888; }
57
+
58
+ tr .progress .bar {
59
+ display: none; }
60
+
61
+ tr.milestone .progress .bar,
62
+ tr.feature .progress .bar {
63
+ display: block; }
64
+
65
+ tr td.points>* {
66
+ display: none; }
67
+
68
+ tr.milestone td.points>*,
69
+ tr.feature td.points>* {
70
+ display: inline; }
71
+
72
+ /* Header */
73
+ thead {
74
+ display: none; }
75
+
76
+ th {
77
+ color: #888; }
78
+
79
+ /* Zebra */
80
+ tr td,
81
+ tr {
82
+ background: #ffffff; }
83
+
84
+ tr:nth-child(odd) td,
85
+ tr:nth-child(odd) {
86
+ background: #fafafa; }
87
+
88
+ tr.highlight,
89
+ tr.highlight td {
90
+ background: #fafae0; }
91
+
92
+ /* Status box */
93
+ span.status {
94
+ display: inline-block;
95
+ width: 12px;
96
+ height: 12px;
97
+ margin: 0 5px 0 0;
98
+
99
+ border-radius: 2px;
100
+
101
+ position: relative;
102
+ top: 2px;
103
+ background: #ddd; }
104
+
105
+ span.status.in_progress {
106
+ background: #ea3; }
107
+
108
+ span.status.done {
109
+ background: #393; }
110
+
111
+ /* Progress */
112
+ .progress .number {
113
+ display: none; }
114
+
115
+ .progress .bar {
116
+ height: 10px;
117
+ border-radius: 5px;
118
+ background: #ddd; }
119
+
120
+ .progress .bar span {
121
+ display: block;
122
+ height: 10px;
123
+ border-radius: 5px;
124
+ background: #888; }
125
+
126
+ a.meta {
127
+ font-size: 0.9em;
128
+ text-decoration: none;
129
+
130
+ background: #ddd;
131
+ padding: 1px 3px;
132
+ border-radius: 2px;
133
+ border-bottom: solid 1px #ccc;
134
+
135
+ margin: 0 5px;
136
+ color: #777; }
137
+
138
+ /* Progress */
139
+ td.points {
140
+ text-align: left;
141
+ font-size: 0.9em; }
142
+
143
+ td.points .points.done {
144
+ font-weight: bold; }
145
+
146
+ td.points .of,
147
+ td.points .points.total {
148
+ color: #888; }
149
+
150
+ %body
151
+
152
+ %table
153
+ %thead
154
+ %tr
155
+ %th.task Task
156
+ %th.owner Owner
157
+ %th.progress Progress
158
+ %th.points Points
159
+
160
+ - list.walk do |task, recurse|
161
+
162
+ %tr{class: "level-#{task.level} #{task.tasks? ? 'parent' : 'leaf'} #{'root' if task.root?} #{'feature' if task.feature?} #{'milestone' if task.milestone?}"}
163
+ %td.task
164
+ %span.status{class: "#{task.status}"}
165
+ = task
166
+ - if task.pivotal_id
167
+ %a.meta{href: task.pivotal_url}= "Pivotal: #{task.pivotal_id}"
168
+
169
+ %td.owner
170
+ = task.owner
171
+
172
+ %td.progress
173
+ .bar
174
+ %span{style: "width: #{(task.percent*95+5).to_i}%"}
175
+ %span.number= "#{(task.percent*100).to_i}%"
176
+
177
+ %td.points
178
+ %span.points.done= task.points_done.round(1).to_s.gsub(/\.0+$/,'')
179
+ %span.of of
180
+ %span.points.total= task.points.round(1).to_s.gsub(/\.0+$/,'')
181
+
182
+ - recurse.call if recurse
data/data/sample.yml ADDED
@@ -0,0 +1,18 @@
1
+ Version 1:
2
+
3
+ User signup:
4
+ Register for an account:
5
+ Log in: [done]
6
+ Forget password:
7
+
8
+ Manage users:
9
+ _: [in progress]
10
+ Create users: [in progress]
11
+ Delete users:
12
+ User profile page:
13
+
14
+ Blog:
15
+ Creating new posts: [done]
16
+ Comments: [done]
17
+ Moderating comments: [done]
18
+
@@ -0,0 +1,76 @@
1
+ module TPS
2
+ class CliReporter
3
+ attr_reader :task
4
+
5
+ def initialize(task)
6
+ @task = task
7
+ end
8
+
9
+ def print
10
+ puts report
11
+ end
12
+
13
+ def report
14
+ re = ""
15
+ task.walk do |t, recurse|
16
+ re += CliReporter.new(t).report_task
17
+ recurse.call if recurse
18
+ end
19
+ re
20
+ end
21
+
22
+ def report_task
23
+ indent = ' ' * (4 * task.level)
24
+
25
+ # Columns
26
+ c1 = "%s %s %s" % [ indent, status, task.name ]
27
+ c2 = if task.feature? || task.milestone?
28
+ progress
29
+ else
30
+ ' '*12
31
+ end
32
+
33
+ pref = c("-"*80, 30)+"\n" if task.feature?
34
+
35
+ # Put together
36
+ "#{pref}" + "%-95s%s\n" % [ c1, c2 ]
37
+ end
38
+
39
+ def color
40
+ if task.done?
41
+ 32
42
+ elsif task.in_progress?
43
+ 34
44
+ else
45
+ 30
46
+ end
47
+ end
48
+
49
+ def status
50
+ l = c("(", 30)
51
+ r = c(")", 30)
52
+ if task.done?
53
+ l + c('##', color) + r
54
+ elsif task.in_progress?
55
+ l + c('--', color) + r
56
+ else
57
+ l + c(' ', color) + r
58
+ end
59
+ end
60
+
61
+ def progress
62
+ max = 12
63
+ len = (task.percent * max).to_i
64
+
65
+ prog = ("%-#{max}s" % ["="*len])
66
+ prog = c("|"*len, color) + c("."*(max-len), 30)
67
+
68
+ prog
69
+ end
70
+
71
+ private
72
+ def c(str, c=nil)
73
+ c ? "\033[#{c}m#{str}\033[0m" : str
74
+ end
75
+ end
76
+ end
data/lib/tps/task.rb ADDED
@@ -0,0 +1,166 @@
1
+ require 'yaml'
2
+ require 'tilt'
3
+
4
+ module TPS
5
+ class Task
6
+ attr_reader :name # "Create metrics"
7
+ attr_reader :tasks # Array of Tasks
8
+ attr_reader :owner
9
+ attr_reader :pivotal_id
10
+ attr_reader :parent
11
+
12
+ def initialize(parent, name, data=nil)
13
+ @name = name
14
+ @tasks = Array.new
15
+ @parent = parent
16
+
17
+ if data.is_a?(Array)
18
+ tags = data
19
+ tasks = nil
20
+ elsif data.is_a?(Hash) && data['_']
21
+ tags = data.delete('_')
22
+ tasks = data
23
+ else
24
+ tags = Array.new
25
+ tasks = data
26
+ end
27
+
28
+ # Parse tags.
29
+ tags.each do |t|
30
+ # [done]
31
+ if ['done', 'ok'].include?(t)
32
+ @status = :done
33
+ elsif ['in progress', '...'].include?(t)
34
+ @status = :in_progress
35
+ # [@rstacruz]
36
+ elsif t =~ /^@/
37
+ @owner = t[1..-1]
38
+ # [pt/28394] -- Pivotal tracker
39
+ elsif t =~ /^pt\/(.*)$/i
40
+ @pivotal_id = $1.strip
41
+ # [50%] -- percentage
42
+ elsif t =~ /^([\d\.]+)%$/
43
+ @status = :in_progress
44
+ @percent = $1.strip.to_f / 100
45
+ # [0pt] -- points
46
+ elsif t =~ /^([\d\.]+)pts?/i
47
+ @points = $1.strip.to_f
48
+ end
49
+ end
50
+
51
+ @tasks = tasks.map { |task, data| Task.new self, task, data } if tasks
52
+
53
+ n = @name.to_s.downcase
54
+ @milestone = root? && (n.include?('milestone') || n.include?('version'))
55
+ end
56
+
57
+ def status
58
+ # If no status is given, infer the status based on tasks.
59
+ if !@status && tasks?
60
+ if all_tasks_done?
61
+ return :done
62
+ elsif has_started_tasks?
63
+ return :in_progress
64
+ end
65
+ end
66
+
67
+ @status or :unstarted
68
+ end
69
+
70
+ def points
71
+ if @points
72
+ @points
73
+ elsif tasks?
74
+ tasks.inject(0.0) { |pts, task| pts + task.points }
75
+ else
76
+ 1.0
77
+ end
78
+ end
79
+
80
+ def points_done
81
+ points * percent
82
+ end
83
+
84
+ def all_tasks_done?
85
+ tasks? and !tasks.any? { |t| ! t.done? }
86
+ end
87
+
88
+ def has_started_tasks?
89
+ tasks? and tasks.any? { |t| t.in_progress? or t.done? }
90
+ end
91
+
92
+ def to_s
93
+ name
94
+ end
95
+
96
+ def done?
97
+ status == :done
98
+ end
99
+
100
+ def in_progress?
101
+ status == :in_progress
102
+ end
103
+
104
+ def unstarted?
105
+ status == :unstarted
106
+ end
107
+
108
+ def tasks?
109
+ tasks.any?
110
+ end
111
+
112
+ def pivotal_url
113
+ "https://www.pivotaltracker.com/story/show/#{pivotal_id}" if pivotal_id
114
+ end
115
+
116
+ def percent
117
+ if done?
118
+ 1.0
119
+ elsif @percent
120
+ @percent
121
+ elsif tasks?
122
+ total = tasks.inject(0.0) { |pts, task| pts + task.points }
123
+ tasks.inject(0) { |i, task| i + task.points_done } / total
124
+ else
125
+ in_progress? ? 0.5 : 0
126
+ end
127
+ end
128
+
129
+ def level
130
+ parent ? parent.level + 1 : 0
131
+ end
132
+
133
+ def root?
134
+ ! parent
135
+ end
136
+
137
+ def feature?
138
+ root? or parent.milestone?
139
+ end
140
+
141
+ def milestone?
142
+ !! @milestone
143
+ end
144
+
145
+ # - list.walk do |task, recurse|
146
+ # %ul
147
+ # %li
148
+ # = task
149
+ # - recurse.call if recurse
150
+ def walk(&blk)
151
+ tasks.each do |task|
152
+ yield task, lambda {
153
+ task.walk { |t, recurse| blk.call t, recurse }
154
+ }
155
+ end
156
+ end
157
+
158
+ def to_html(template=nil)
159
+ require 'tilt'
160
+ template ||= TPS.root('data', 'index.haml')
161
+
162
+ tpl = Tilt.new(template)
163
+ tpl.evaluate({}, list: self)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,17 @@
1
+ module TPS
2
+ class TaskList < Task
3
+ def initialize(options)
4
+ super nil, nil
5
+
6
+ data = if options[:yaml]
7
+ YAML::load_file options[:yaml]
8
+ elsif options[:data]
9
+ options[:data]
10
+ else
11
+ options
12
+ end
13
+
14
+ @tasks = data.map { |task, data| Task.new nil, task, data }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module TPS
2
+ def self.version
3
+ "0.0.2"
4
+ end
5
+ end
data/lib/tps.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+ require 'tilt'
3
+
4
+ # Common usage:
5
+ #
6
+ # list = TPS::TaskList.new data: yaml_data
7
+ # list = TPS::TaskList.new yaml: filename
8
+ #
9
+ # # Returns an array of tasks.
10
+ # task.tasks
11
+ #
12
+ # # Metadata:
13
+ # task.name
14
+ # task.owner
15
+ # task.status
16
+ #
17
+ # task.done?
18
+ # task.in_progress?
19
+ #
20
+ # list.to_html
21
+ #
22
+ module TPS
23
+ ROOT = File.expand_path('../../', __FILE__)
24
+
25
+ autoload :Task, 'tps/task'
26
+ autoload :TaskList, 'tps/task_list'
27
+ autoload :CliReporter, 'tps/cli_reporter'
28
+
29
+ require 'tps/version'
30
+
31
+ def self.root(*a)
32
+ File.join ROOT, *a
33
+ end
34
+ end
data/test/hello.yml ADDED
@@ -0,0 +1,42 @@
1
+ Milestone 1:
2
+ First task: [25%] #0
3
+
4
+ User login: #1
5
+ Login:
6
+ Signup:
7
+
8
+ Overridden percent: #2
9
+ _: [50%]
10
+ one: [done]
11
+ two: [done]
12
+ three: [done]
13
+
14
+ Explicit points: #3
15
+ _: [15pt]
16
+ one: [done]
17
+ two: [done]
18
+ three: [done]
19
+ four:
20
+
21
+ Compound points: #4
22
+ one:
23
+ two:
24
+ three:
25
+ sub1: [3pts, done]
26
+ sub2:
27
+
28
+ Point rescaling: #5
29
+ # Has 6 sub points, but will be rescaled to 8.
30
+ # That is, the done "3" point task is actually worth 4 points.
31
+ one:
32
+ _: [8pts]
33
+ sub1: [3pts, done]
34
+ sub2: [1pt]
35
+ sub3: [1pt]
36
+ sub4: [1pt]
37
+
38
+ In progress: #6
39
+ one:
40
+ two: [in progress]
41
+
42
+ Milestone 2:
@@ -0,0 +1,13 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'contest'
3
+ require 'tps'
4
+
5
+ class UnitTest < Test::Unit::TestCase
6
+ def f(*a)
7
+ File.join fixture_root, *a
8
+ end
9
+
10
+ def fixture_root
11
+ File.expand_path('../', __FILE__)
12
+ end
13
+ end
data/test/tps_test.rb ADDED
@@ -0,0 +1,71 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class MyTest < UnitTest
4
+ setup do
5
+ @list = TPS::TaskList.new yaml: f('hello.yml')
6
+ @milestone = @list.tasks.first
7
+ end
8
+
9
+ test "Has tasks" do
10
+ assert @list.tasks?
11
+ assert @list.tasks.size == 2
12
+ assert @list.tasks.first.tasks.size >= 2
13
+ end
14
+
15
+ test "Explicit percent" do
16
+ task = @milestone.tasks[0]
17
+ assert task.in_progress?
18
+ assert task.status == :in_progress
19
+ assert task.percent == 0.25
20
+ end
21
+
22
+ test "Overriding percent" do
23
+ task = @milestone.tasks[2]
24
+ assert task.name == "Overridden percent"
25
+ assert task.in_progress?
26
+ assert task.status == :in_progress
27
+ assert task.percent == 0.5
28
+ end
29
+
30
+ test "Points" do
31
+ task = @milestone.tasks[1]
32
+ assert task.points == 2.0
33
+ end
34
+
35
+ test "Explicit points" do
36
+ task = @milestone.tasks[3]
37
+ assert task.points == 15
38
+ assert task.percent == 0.75
39
+ assert task.points_done == 11.25
40
+ end
41
+
42
+ test "Compound points" do
43
+ task = @milestone.tasks[4]
44
+ assert task.points == 6
45
+ assert task.percent == 0.50
46
+ end
47
+
48
+ test "Point rescaling" do
49
+ task = @milestone.tasks[5]
50
+ assert task.points == 8
51
+ assert task.points_done == 4.0
52
+ assert task.percent == 0.5
53
+ end
54
+
55
+ test "In progress" do
56
+ task = @milestone.tasks[6]
57
+ assert_equal 2, task.tasks.size
58
+ assert task.tasks[1].in_progress?
59
+ assert_equal 0.5, task.tasks[1].percent
60
+ assert_equal 0.5, task.tasks[1].points_done
61
+ assert_equal 0.25, task.percent
62
+ end
63
+
64
+ test "Milestone" do
65
+ assert @milestone.milestone?
66
+ end
67
+
68
+ test "HTML works" do
69
+ assert @list.to_html
70
+ end
71
+ end
@@ -0,0 +1,16 @@
1
+ require './lib/tps/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "tps_reporter"
5
+ s.version = TPS.version
6
+ s.summary = %{Task progress sheet reporter.}
7
+ s.description = %Q{A YAML-powered, simple command-line task report builder.}
8
+ s.authors = ["Rico Sta. Cruz"]
9
+ s.email = ["rico@sinefunc.com"]
10
+ s.homepage = "http://github.com/rstacruz/tps_reporter"
11
+ s.files = `git ls-files`.strip.split("\n")
12
+ s.executables = Dir["bin/*"].map { |f| File.basename(f) }
13
+
14
+ s.add_dependency "tilt"
15
+ s.add_development_dependency "contest"
16
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tps_reporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rico Sta. Cruz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: tilt
16
+ requirement: &2152658640 !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: *2152658640
25
+ - !ruby/object:Gem::Dependency
26
+ name: contest
27
+ requirement: &2152658200 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152658200
36
+ description: A YAML-powered, simple command-line task report builder.
37
+ email:
38
+ - rico@sinefunc.com
39
+ executables:
40
+ - tps
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - HISTORY.md
45
+ - README.md
46
+ - Rakefile
47
+ - bin/tps
48
+ - data/index.haml
49
+ - data/sample.yml
50
+ - lib/tps.rb
51
+ - lib/tps/cli_reporter.rb
52
+ - lib/tps/task.rb
53
+ - lib/tps/task_list.rb
54
+ - lib/tps/version.rb
55
+ - test/hello.yml
56
+ - test/test_helper.rb
57
+ - test/tps_test.rb
58
+ - tps_reporter.gemspec
59
+ homepage: http://github.com/rstacruz/tps_reporter
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.10
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Task progress sheet reporter.
83
+ test_files: []