backlog 0.5.8 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|