backlog 0.5.8 → 0.5.9
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.txt +4 -0
- data/app/controllers/periods_controller.rb +1 -72
- data/app/models/period.rb +90 -13
- metadata +1 -1
data/History.txt
CHANGED
@@ -108,82 +108,11 @@ class PeriodsController < ApplicationController
|
|
108
108
|
|
109
109
|
def send_burn_down_chart(size)
|
110
110
|
period = Period.find(params[:id])
|
111
|
-
g =
|
112
|
-
g.theme_37signals
|
113
|
-
g.title = l(:burn_down_chart) + " " + period.to_s
|
114
|
-
g.font = '/usr/share/fonts/bitstream-vera/Vera.ttf'
|
115
|
-
g.legend_font_size = 14
|
116
|
-
g.hide_dots = true
|
117
|
-
g.colors = %w{blue orange}
|
118
|
-
if period.track_work?
|
119
|
-
g.colors += %w{green}
|
120
|
-
end
|
121
|
-
if previous_period = period.higher_item
|
122
|
-
g.colors += %w{grey grey}
|
123
|
-
end
|
124
|
-
if period.active?
|
125
|
-
g.colors += %w{lightblue #d7a790}
|
126
|
-
end
|
127
|
-
|
128
|
-
recorded_dates = period.recorded_dates
|
129
|
-
observed_todo_data = get_todo_data(recorded_dates, period)
|
130
|
-
actual_todo_data = get_todo_data(recorded_dates, period, true)
|
131
|
-
g.data("#{l :todo} (#{l :obs})", observed_todo_data)
|
132
|
-
g.data("#{l :calc}", actual_todo_data)
|
133
|
-
g.data(l(:done), get_work_data(recorded_dates, period)) if period.track_work?
|
134
|
-
if previous_period = period.higher_item
|
135
|
-
g.data("#{l :previous} #{l :obs}", get_todo_data(previous_period.dates, previous_period))
|
136
|
-
g.data("#{l :calc}", get_todo_data(previous_period.dates, previous_period, true))
|
137
|
-
end
|
138
|
-
|
139
|
-
if period.active?
|
140
|
-
g.data(l(:projection), projection_data(observed_todo_data, period))
|
141
|
-
g.data(l(:projection), projection_data(actual_todo_data, period))
|
142
|
-
end
|
143
|
-
|
144
|
-
g.minimum_value = 0
|
145
|
-
|
146
|
-
all_dates = period.dates
|
147
|
-
labels = {0 => all_dates.first.to_s, all_dates.length-1 => all_dates.last.to_s}
|
148
|
-
labels.merge({all_dates.index(Date.today) => Date.today.to_s}) if all_dates.index(Date.today) && (all_dates.index(Date.today) / all_dates.length) > 0.10
|
149
|
-
g.labels = labels
|
150
|
-
|
151
|
-
# g.draw_vertical_legend
|
152
|
-
|
153
|
-
g.maximum_value = g.maximum_value.to_i
|
154
|
-
|
111
|
+
g = period.burn_down_graph(size)
|
155
112
|
send_data(g.to_blob,
|
156
113
|
:disposition => 'inline',
|
157
114
|
:type => 'image/png',
|
158
115
|
:filename => "burn_down_chart.png")
|
159
116
|
end
|
160
117
|
|
161
|
-
def get_todo_data(dates, period, actual=false)
|
162
|
-
dates.map { |date| period.estimate_data(date, actual) }
|
163
|
-
end
|
164
|
-
|
165
|
-
def get_work_data(dates, period)
|
166
|
-
dates.map { |date| period.work_data(date) }
|
167
|
-
end
|
168
|
-
|
169
|
-
def projection_data(observed_todo_data, period)
|
170
|
-
if observed_todo_data.length <= 1
|
171
|
-
velocity = 0
|
172
|
-
else
|
173
|
-
velocity = (observed_todo_data[-1] - observed_todo_data[0]).to_f / (observed_todo_data.length-1)
|
174
|
-
end
|
175
|
-
projection_data = period.dates.map do |date|
|
176
|
-
if date < Date.today
|
177
|
-
nil
|
178
|
-
else
|
179
|
-
if observed_todo_data.length == 1
|
180
|
-
observed_todo_data[0]
|
181
|
-
else
|
182
|
-
value = observed_todo_data[0] + (date-period.start_on).to_f*velocity
|
183
|
-
value >= 0 ? value : 0
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
118
|
end
|
data/app/models/period.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
class Period < ActiveRecord::Base
|
2
|
+
include Localization
|
3
|
+
|
2
4
|
belongs_to :party
|
3
5
|
acts_as_list :scope => :party, :order => :position
|
4
6
|
has_many :tasks, :order => 'position, finished_at', :dependent => :destroy
|
5
7
|
validates_associated :party
|
6
8
|
validates_uniqueness_of :position, :scope => :party_id
|
7
|
-
|
9
|
+
|
8
10
|
def validate
|
9
11
|
errors.add "A period cannot end before it starts" unless end_on >= start_on
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def self.find_active_or_future
|
13
15
|
find(:all, :conditions => "end_on >= '#{Date.today.strftime '%Y-%m-%d'}'")
|
14
16
|
end
|
@@ -16,11 +18,11 @@ class Period < ActiveRecord::Base
|
|
16
18
|
def completed_tasks
|
17
19
|
tasks.select {|task| task.completed?}
|
18
20
|
end
|
19
|
-
|
21
|
+
|
20
22
|
def open_tasks
|
21
23
|
tasks.select {|task| task.active?}
|
22
24
|
end
|
23
|
-
|
25
|
+
|
24
26
|
def most_frequent_backlog
|
25
27
|
all_backlogs = tasks.map {|t| t.backlog}.compact
|
26
28
|
return nil if all_backlogs.empty?
|
@@ -35,7 +37,7 @@ class Period < ActiveRecord::Base
|
|
35
37
|
def active?(check_tasks = false)
|
36
38
|
start_on <= Date.today && (end_on >= Date.today || (check_tasks && tasks.select {|task| task.active?}.size > 0))
|
37
39
|
end
|
38
|
-
|
40
|
+
|
39
41
|
def future?
|
40
42
|
start_on >= Date.today
|
41
43
|
end
|
@@ -47,7 +49,7 @@ class Period < ActiveRecord::Base
|
|
47
49
|
def passed?
|
48
50
|
end_on < Date.today
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
def estimate_data(date, actual=false)
|
52
54
|
total = BigDecimal('0')
|
53
55
|
tasks.each do |task|
|
@@ -55,7 +57,7 @@ class Period < ActiveRecord::Base
|
|
55
57
|
end
|
56
58
|
total
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
def work_data(date)
|
60
62
|
total = BigDecimal('0')
|
61
63
|
tasks.each do |task|
|
@@ -63,7 +65,7 @@ class Period < ActiveRecord::Base
|
|
63
65
|
end
|
64
66
|
total
|
65
67
|
end
|
66
|
-
|
68
|
+
|
67
69
|
def required_speed
|
68
70
|
todo = estimate_data(Date.today)
|
69
71
|
remaining_days = end_on - Date.today
|
@@ -77,21 +79,21 @@ class Period < ActiveRecord::Base
|
|
77
79
|
todo * (-remaining_days)
|
78
80
|
end
|
79
81
|
end
|
80
|
-
|
82
|
+
|
81
83
|
def to_s
|
82
84
|
name
|
83
85
|
end
|
84
|
-
|
86
|
+
|
85
87
|
def name
|
86
88
|
"#{party.name}##{position}"
|
87
89
|
end
|
88
|
-
|
90
|
+
|
89
91
|
def dates
|
90
92
|
all_dates = []
|
91
93
|
start_on.upto(end_on) {|date| all_dates << date}
|
92
94
|
return all_dates
|
93
95
|
end
|
94
|
-
|
96
|
+
|
95
97
|
def recorded_dates
|
96
98
|
dates = []
|
97
99
|
if start_on > Date.today
|
@@ -153,9 +155,84 @@ class Period < ActiveRecord::Base
|
|
153
155
|
def invoice?
|
154
156
|
not backlogs.find {|backlog| backlog.enable_invoicing?}.nil?
|
155
157
|
end
|
156
|
-
|
158
|
+
|
157
159
|
def backlogs
|
158
160
|
tasks.map {|task| task.backlog}.uniq
|
159
161
|
end
|
160
162
|
|
163
|
+
def burn_down_graph(size)
|
164
|
+
g = Gruff::Line.new(size)
|
165
|
+
g.theme_37signals
|
166
|
+
g.title = l(:burn_down_chart) + " " + name
|
167
|
+
g.font = '/usr/share/fonts/bitstream-vera/Vera.ttf'
|
168
|
+
g.legend_font_size = 14
|
169
|
+
g.hide_dots = true
|
170
|
+
g.colors = %w{blue orange}
|
171
|
+
if track_work?
|
172
|
+
g.colors += %w{green}
|
173
|
+
end
|
174
|
+
if previous_period = higher_item
|
175
|
+
g.colors += %w{grey grey}
|
176
|
+
end
|
177
|
+
if active?
|
178
|
+
g.colors += %w{lightblue #d7a790}
|
179
|
+
end
|
180
|
+
|
181
|
+
recorded_dates = self.recorded_dates
|
182
|
+
observed_todo_data = get_todo_data(recorded_dates)
|
183
|
+
actual_todo_data = get_todo_data(recorded_dates, true)
|
184
|
+
g.data("#{l :todo} (#{l :obs})", observed_todo_data)
|
185
|
+
g.data("#{l :calc}", actual_todo_data)
|
186
|
+
g.data(l(:done), get_work_data(recorded_dates)) if track_work?
|
187
|
+
if previous_period = higher_item
|
188
|
+
g.data("#{l :previous} #{l :obs}", previous_period.get_todo_data(previous_period.dates))
|
189
|
+
g.data("#{l :calc}", previous_period.get_todo_data(previous_period.dates, true))
|
190
|
+
end
|
191
|
+
|
192
|
+
if active?
|
193
|
+
g.data(l(:projection), projection_data(observed_todo_data))
|
194
|
+
g.data(l(:projection), projection_data(actual_todo_data))
|
195
|
+
end
|
196
|
+
|
197
|
+
g.minimum_value = 0
|
198
|
+
|
199
|
+
all_dates = dates
|
200
|
+
labels = {0 => all_dates.first.to_s, all_dates.length-1 => all_dates.last.to_s}
|
201
|
+
labels.merge({all_dates.index(Date.today) => Date.today.to_s}) if all_dates.index(Date.today) && (all_dates.index(Date.today) / all_dates.length) > 0.10
|
202
|
+
g.labels = labels
|
203
|
+
|
204
|
+
# g.draw_vertical_legend
|
205
|
+
|
206
|
+
g.maximum_value = (g.maximum_value.to_s[0..0].to_i + 1) * (10**Math::log10(g.maximum_value.to_i).to_i)
|
207
|
+
g
|
208
|
+
end
|
209
|
+
|
210
|
+
def get_todo_data(dates, actual=false)
|
211
|
+
dates.map { |date| estimate_data(date, actual) }
|
212
|
+
end
|
213
|
+
|
214
|
+
def get_work_data(dates)
|
215
|
+
dates.map { |date| work_data(date) }
|
216
|
+
end
|
217
|
+
|
218
|
+
def projection_data(observed_todo_data)
|
219
|
+
if observed_todo_data.length <= 1
|
220
|
+
velocity = 0
|
221
|
+
else
|
222
|
+
velocity = (observed_todo_data[-1] - observed_todo_data[0]).to_f / (observed_todo_data.length-1)
|
223
|
+
end
|
224
|
+
projection_data = dates.map do |date|
|
225
|
+
if date < Date.today
|
226
|
+
nil
|
227
|
+
else
|
228
|
+
if observed_todo_data.length == 1
|
229
|
+
observed_todo_data[0]
|
230
|
+
else
|
231
|
+
value = observed_todo_data[0] + (date-start_on).to_f*velocity
|
232
|
+
value >= 0 ? value : 0
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
161
238
|
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: backlog
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.5.
|
6
|
+
version: 0.5.9
|
7
7
|
date: 2007-08-06 00:00:00 +02:00
|
8
8
|
summary: Application to aid collecting, processing, organizing, reviewing and doing tasks.
|
9
9
|
require_paths:
|