backlog 0.13.0 → 0.13.1

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.
Files changed (35) hide show
  1. data/History.txt +15 -1
  2. data/Rakefile +2 -0
  3. data/app/controllers/application.rb +3 -6
  4. data/app/controllers/backlogs_controller.rb +84 -4
  5. data/app/controllers/estimates_controller.rb +1 -5
  6. data/app/controllers/periods_controller.rb +84 -2
  7. data/app/controllers/tasks_controller.rb +0 -51
  8. data/app/controllers/works_controller.rb +7 -3
  9. data/app/helpers/application_helper.rb +9 -0
  10. data/app/helpers/backlogs_helper.rb +62 -0
  11. data/app/helpers/periods_helper.rb +63 -0
  12. data/app/helpers/tasks_helper.rb +0 -72
  13. data/app/models/period.rb +17 -7
  14. data/app/models/task.rb +11 -6
  15. data/app/views/backlogs/_buttons.rhtml +1 -0
  16. data/app/views/backlogs/_tasks.rhtml +9 -11
  17. data/app/views/backlogs/finish_task.rjs +3 -0
  18. data/app/views/backlogs/move_task_to_period.rjs +3 -0
  19. data/app/views/backlogs/reopen_task.rjs +3 -0
  20. data/app/views/{tasks/finish.rjs → periods/finish_task.rjs} +0 -0
  21. data/app/views/{tasks/move_to_period.rjs → periods/move_task_to_period.rjs} +0 -0
  22. data/app/views/{tasks/reopen.rjs → periods/reopen_task.rjs} +0 -0
  23. data/app/views/tasks/_completed.rhtml +10 -11
  24. data/app/views/tasks/_fields_header.rhtml +3 -3
  25. data/app/views/tasks/_task.rhtml +6 -6
  26. data/app/views/tasks/update_estimate.rjs +2 -0
  27. data/app/views/works/_form.rhtml +1 -1
  28. data/app/views/works/update_time.rjs +1 -1
  29. data/cruise_config.rb +1 -2
  30. data/test/functional/periods_controller_test.rb +116 -0
  31. data/test/functional/tasks_controller_test.rb +0 -116
  32. data/test/functional/works_controller_test.rb +9 -1
  33. data/test/performance/test_threaded.rb +38 -18
  34. metadata +9 -6
  35. data/app/views/estimates/create_ajax.rjs +0 -11
data/History.txt CHANGED
@@ -1,4 +1,18 @@
1
- == 0.13.0 2007-11-12
1
+ == 0.13.1 2007-11-17
2
+
3
+ === Features
4
+
5
+ * Changed to allow not setting a task for a work record.
6
+ * Fixed finishing and reopening tasks in the Backlog view.
7
+
8
+ === Fixes
9
+
10
+ * Added missing web.xml file to WAR distribution.
11
+ * Fixed wrong parsing of updated start time when using single digit hour (eg. 8:15)
12
+ * Added "Add Task" button to Backlog view.
13
+ * Speed up of all pages.
14
+
15
+ == 0.13.0 2007-11-14
2
16
 
3
17
  === Features
4
18
 
data/Rakefile CHANGED
@@ -43,6 +43,8 @@ task :release_all do
43
43
  end
44
44
 
45
45
  task :release_war do
46
+ File.mkdir 'WEB-INF' unless File.exists? 'WEB-INF'
47
+ FileUtils.cp 'vendor/plugins/goldspike/generators/goldspike/templates/web.xml.erb', 'WEB-INF/web.xml.erb' unless File.exists? 'WEB-INF/web.xml.erb'
46
48
  Rake::Task['war:standalone:create'].invoke
47
49
  war_pkg_file = "pkg/backlog-#{APP::VERSION}.war"
48
50
  if File.exists? 'backlog.war'
@@ -143,7 +143,9 @@ class ApplicationController < ActionController::Base
143
143
  populate_shortcuts
144
144
 
145
145
  # TODO (uwe): This does not scale!
146
- periods = Period.find(:all).select {|period| period.active_or_future?(true) && period.party.includes?(user)}
146
+ #periods = Period.find(:all).select {|period| period.active_or_future?(true) && period.party.includes?(user)}
147
+ periods = Period.find_active_or_future(true)
148
+
147
149
 
148
150
  @sidebars = periods.sort_by {|p| p.required_speed}.reverse.map do |period|
149
151
  content = '<ul>'
@@ -186,9 +188,4 @@ class ApplicationController < ActionController::Base
186
188
  User.find_by_id(user_id) if user_id
187
189
  end
188
190
 
189
- def setup_remove
190
- @last_active = @task.higher_item.nil? && @task.lower_item.nil?
191
- @last_active_in_backlog = @last_active || ((@task.higher_item.nil? || @task.higher_item.backlog != @task.backlog) && (@task.lower_item.nil? || @task.lower_item.backlog != @task.backlog))
192
- end
193
-
194
191
  end
@@ -33,10 +33,7 @@ class BacklogsController < ApplicationController
33
33
  return
34
34
  end
35
35
  end
36
- unplanned_tasks = @backlog.tasks.select {|t| t.period.nil? && t.finished_at.nil?}.sort_by {|t| t.position}
37
- planned_tasks = @backlog.tasks.select {|t| t.period && t.finished_at.nil?}.sort_by {|t| [t.period.end_on, t.position]}
38
- @tasks = planned_tasks + unplanned_tasks
39
- @completed_tasks = @backlog.tasks.select {|t| t.finished_at}.sort {|t1, t2| t2.finished_at <=> t1.finished_at}
36
+ load_tasks(@backlog)
40
37
  end
41
38
 
42
39
  def show_no_layout
@@ -93,8 +90,91 @@ class BacklogsController < ApplicationController
93
90
  send_burn_down_chart 640
94
91
  end
95
92
 
93
+ def update_task_estimate
94
+ if params[:id]
95
+ @task = Task.find_by_id(params[:id])
96
+ if params[:estimate] && params[:estimate][:todo]
97
+ begin
98
+ Float(params[:estimate][:todo])
99
+ @task.estimate(params[:estimate][:todo])
100
+ @period = @task.period
101
+ @success, @message = true, 'Estimate updated'
102
+ rescue ArgumentError => e
103
+ @success, @message = false, "Estimate was not numeric"
104
+ end
105
+ if @task.finished?
106
+ load_tasks(@task.period)
107
+ render :action => 'finish_task', :layout => false
108
+ else
109
+ render :template => '/tasks/update_estimate', :layout => false
110
+ end
111
+ else
112
+ return false, "Estimate is missing."
113
+ end
114
+ else
115
+ @estimate = Estimate.new
116
+ return false, 'Task id is missing.'
117
+ end
118
+ end
119
+
120
+ def finish_task
121
+ @task = Task.find(params[:id])
122
+ @task.finish(Task::COMPLETED, true)
123
+ load_tasks(@task.backlog)
124
+ end
125
+
126
+ def abort_task
127
+ @task = Task.find params[:id]
128
+ @task.abort
129
+ load_tasks(@task.backlog)
130
+ render :action => :finish_task, :layout => false
131
+ end
132
+
133
+ def move_task_to_period
134
+ params[:id] = $1 if params[:id] =~ /^task_(\d*)/
135
+ @task = Task.find(params[:id])
136
+ if request.post?
137
+ period = params[:period_id] && Period.find(params[:period_id])
138
+ if period && period.active_or_future?
139
+ next_task = @task.higher_item ? @task.higher_item : @task.lower_item ? @task.lower_item : @task
140
+ @task.move_to_period period
141
+ load_tasks(@task.backlog)
142
+ render :action => :move_task_to_period, :layout => false
143
+ else
144
+ rjs_detour_to :controller => 'periods', :action => :new, :period => {:party_id => @task.period.party_id}, :layout => false
145
+ end
146
+ else
147
+ redirect_to :controller => 'periods', :action => :show, :id => @task.period, :task_id => @task.id, :layout => false
148
+ end
149
+ end
150
+
151
+ def move_task_to_next_period
152
+ @task = Task.find(params[:id])
153
+ if @task.period
154
+ next_period = @task.period.lower_item
155
+ next_period = next_period.lower_item while next_period && next_period.passed?
156
+ params[:period_id] = next_period && next_period.id
157
+ move_task_to_period
158
+ else
159
+ rjs_redirect_to :action => :edit, :id => @task
160
+ end
161
+ end
162
+
163
+ def reopen_task
164
+ @task = Task.find(params[:id]).reopen
165
+ load_tasks(@task.backlog)
166
+ end
167
+
96
168
  private
97
169
 
170
+ def load_tasks(backlog)
171
+ unplanned_tasks = backlog.tasks.select {|t| t.period.nil? && t.finished_at.nil?}.sort_by {|t| t.position}
172
+ planned_tasks = backlog.tasks.select {|t| t.period && t.finished_at.nil?}.sort_by {|t| [t.period.end_on, t.position]}
173
+ @tasks = planned_tasks + unplanned_tasks
174
+ @completed_tasks = backlog.tasks.select {|t| t.finished_at}.sort {|t1, t2| t2.finished_at <=> t1.finished_at}
175
+
176
+ end
177
+
98
178
  def send_burn_down_chart(size)
99
179
  backlog = Backlog.find(params[:id])
100
180
  g = Gruff::Line.new(size)
@@ -17,12 +17,8 @@ class EstimatesController < ApplicationController
17
17
  end
18
18
 
19
19
  def create_ajax
20
- if @task = Task.find_by_id(params[:id])
21
- setup_remove
22
- end
20
+ @task = Task.find_by_id(params[:id])
23
21
  @success, @message = do_create
24
- # Only necessary since we have an unecessary complex update of estimates
25
- @task.reload
26
22
  if @task.finished?
27
23
  render :template => '/tasks/finish', :layout => false
28
24
  end
@@ -36,8 +36,7 @@ class PeriodsController < ApplicationController
36
36
  if @selected_task.nil?
37
37
  @selected_task = @period.open_tasks.first
38
38
  end
39
- @tasks = @period.open_tasks
40
- @completed_tasks = @period.completed_tasks.reverse
39
+ load_tasks(@period)
41
40
  end
42
41
 
43
42
  def show_no_layout
@@ -121,6 +120,84 @@ class PeriodsController < ApplicationController
121
120
  end
122
121
  end
123
122
 
123
+ def move_task_to_period
124
+ params[:id] = $1 if params[:id] =~ /^task_(\d*)/
125
+ @task = Task.find(params[:id])
126
+ if request.post?
127
+ period = params[:period_id] && Period.find(params[:period_id])
128
+ if period && period.active_or_future?
129
+ next_task = @task.higher_item ? @task.higher_item : @task.lower_item ? @task.lower_item : @task
130
+ @task.move_to_period period
131
+ load_tasks(@task.period)
132
+ render :action => :move_task_to_period, :layout => false
133
+ else
134
+ rjs_detour_to :controller => 'periods', :action => :new, :period => {:party_id => @task.period.party_id}, :layout => false
135
+ end
136
+ else
137
+ redirect_to :controller => 'periods', :action => :show, :id => @task.period, :task_id => @task.id, :layout => false
138
+ end
139
+ end
140
+
141
+ def move_task_to_next_period
142
+ @task = Task.find(params[:id])
143
+ if @task.period
144
+ next_period = @task.period.lower_item
145
+ next_period = next_period.lower_item while next_period && next_period.passed?
146
+ params[:period_id] = next_period && next_period.id
147
+ move_task_to_period
148
+ else
149
+ rjs_redirect_to :action => :edit, :id => @task
150
+ end
151
+ end
152
+
153
+ def finish_task
154
+ @task = Task.find(params[:id])
155
+ @task.finish(Task::COMPLETED, true)
156
+ load_tasks(@task.period)
157
+ end
158
+
159
+ def abort_task
160
+ @task = Task.find params[:id]
161
+ @task.abort
162
+ load_tasks(@task.period)
163
+ render :action => :finish_task, :layout => false
164
+ end
165
+
166
+ def reopen_task
167
+ @task = Task.find(params[:id])
168
+ @task.reopen
169
+ @task.reload
170
+ finished_count = Task.count(:conditions => ['period_id = ? AND finished_at IS NOT NULL', @task.period_id])
171
+ @last_finished = finished_count == 0
172
+ end
173
+
174
+ def update_task_estimate
175
+ if params[:id]
176
+ @task = Task.find_by_id(params[:id])
177
+ if params[:estimate] && params[:estimate][:todo]
178
+ begin
179
+ Float(params[:estimate][:todo])
180
+ @task.estimate(params[:estimate][:todo])
181
+ @period = @task.period
182
+ @success, @message = true, 'Estimate updated'
183
+ rescue ArgumentError => e
184
+ @success, @message = false, "Estimate was not numeric"
185
+ end
186
+ if @task.finished?
187
+ load_tasks(@task.period)
188
+ render :action => 'finish_task', :layout => false
189
+ else
190
+ render :template => '/tasks/update_estimate', :layout => false
191
+ end
192
+ else
193
+ return false, "Estimate is missing."
194
+ end
195
+ else
196
+ @estimate = Estimate.new
197
+ return false, 'Task id is missing.'
198
+ end
199
+ end
200
+
124
201
  private
125
202
 
126
203
  def send_burn_down_chart(size)
@@ -129,4 +206,9 @@ class PeriodsController < ApplicationController
129
206
  send_data(g, :disposition => 'inline', :type => 'image/png', :filename => "burn_down_chart.png")
130
207
  end
131
208
 
209
+ def load_tasks(period)
210
+ @tasks = period.open_tasks
211
+ @completed_tasks = period.completed_tasks.reverse
212
+ end
213
+
132
214
  end
@@ -187,57 +187,6 @@ class TasksController < ApplicationController
187
187
  back_or_redirect_to :controller => 'periods', :action => :show_no_layout, :id => task.period_id, :task => task.id
188
188
  end
189
189
 
190
- def move_to_period
191
- params[:id] = $1 if params[:id] =~ /^task_(\d*)/
192
- @task = Task.find(params[:id])
193
- if request.post?
194
- period = params[:period_id] && Period.find(params[:period_id])
195
- if period && period.active_or_future?
196
- next_task = @task.higher_item ? @task.higher_item : @task.lower_item ? @task.lower_item : @task
197
- setup_remove
198
- @task.move_to_period period
199
- render :action => :move_to_period, :layout => false
200
- else
201
- rjs_detour_to :controller => 'periods', :action => :new, :period => {:party_id => @task.period.party_id}, :layout => false
202
- end
203
- else
204
- redirect_to :controller => 'periods', :action => :show, :id => @task.period, :task_id => @task.id, :layout => false
205
- end
206
- end
207
-
208
- def move_to_next_period
209
- @task = Task.find(params[:id])
210
- if @task.period
211
- next_period = @task.period.lower_item
212
- next_period = next_period.lower_item while next_period && next_period.passed?
213
- params[:period_id] = next_period && next_period.id
214
- move_to_period
215
- else
216
- rjs_redirect_to :action => :edit, :id => @task
217
- end
218
- end
219
-
220
- def finish
221
- @task = Task.find(params[:id])
222
- setup_remove
223
- @task.finish(Task::COMPLETED, true)
224
- end
225
-
226
- def abort
227
- @task = Task.find params[:id]
228
- setup_remove
229
- @task.abort
230
- render :action => :finish
231
- end
232
-
233
- def reopen
234
- @task = Task.find(params[:id])
235
- @task.reopen
236
- @task.reload
237
- finished_count = Task.count(:conditions => ['period_id = ? AND finished_at IS NOT NULL', @task.period_id])
238
- @last_finished = finished_count == 0
239
- end
240
-
241
190
  def start_work
242
191
  @task = Task.find(params[:id])
243
192
  @task.start_work
@@ -186,14 +186,18 @@ class WorksController < ApplicationController
186
186
 
187
187
  def convert_start_time_param
188
188
  if params[:work] && params[:work][:started_at_time]
189
- new_hour = params[:work][:started_at_time][0..1]
190
- new_minutes = params[:work][:started_at_time][3..4]
189
+ if params[:work][:started_at_time] =~ /^(\d*):(\d{2})$/
190
+ new_hours = $1
191
+ new_minutes = $2.to_i
192
+ else
193
+ raise "Unknown time format: '#{params[:work][:hours_time]}'"
194
+ end
191
195
  if @work && @work.started?
192
196
  t = @work.started_at
193
197
  else
194
198
  t = Time.now
195
199
  end
196
- params[:work][:started_at] = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
200
+ params[:work][:started_at] = Time.local(t.year, t.month, t.day, new_hours, new_minutes)
197
201
  params[:work].delete :started_at_time
198
202
  end
199
203
  end
@@ -94,4 +94,13 @@ module ApplicationHelper
94
94
  end
95
95
  end
96
96
 
97
+ def record(page, script)
98
+ page.call("#{script};//")
99
+ end
100
+
101
+ def insert(page, content, selector, position = :top)
102
+ escaped_content = content.gsub("\n", '').gsub("'", "\\\\'")
103
+ record(page, "new Insertion.#{position.to_s.capitalize}($$('#{selector}').first(), '#{escaped_content}')")
104
+ end
105
+
97
106
  end
@@ -1,2 +1,64 @@
1
1
  module BacklogsHelper
2
+ def remove_active_task(page)
3
+ page.visual_effect :blind_up, "task_#{@task.id}"
4
+ if not @tasks.find {|t| t.period_id == @task.period_id}
5
+ page["task_#{@task.id}"].ancestors.first.ancestors.first.remove
6
+ else
7
+ page.remove "task_#{@task.id}"
8
+ end
9
+
10
+ if @tasks.empty?
11
+ page.visual_effect :blind_up, "active_tasks"
12
+ page.visual_effect :appear, "no_tasks_message"
13
+ end
14
+ end
15
+
16
+ def add_finished_task(page)
17
+ not_first = @completed_tasks.find {|t| t.id != @task.id && t.period_id == @task.period_id}
18
+ if not_first
19
+ page.select("#completed_tasks_#{@task.period_id} tr").first.remove
20
+ page.select("#completed_tasks_#{@task.period_id} tr").first.remove
21
+ else
22
+ page.insert_html :top, "completed_tasks", "<table id=\"completed_tasks_#{@task.period.id}\" class=\"input\" />"
23
+ end
24
+
25
+ page.insert_html :top, "completed_tasks_#{@task.period_id}", :partial => '/tasks/task', :locals => {:active => false, :hidden => true, :highlight_task => false}
26
+ page.visual_effect :appear, "task_#{@task.id}"
27
+
28
+ page.insert_html :top, "completed_tasks_#{@task.period_id}", :partial => '/tasks/fields_header', :locals => {:backlog => @task.backlog, :active => false}
29
+ page.insert_html :top, "completed_tasks_#{@task.period_id}", :partial => '/tasks/period_header', :locals => {:period => @task.period}
30
+ end
31
+
32
+ def remove_finished_task(page)
33
+ page.visual_effect :fade, "task_#{@task.id}"
34
+ page.remove "task_#{@task.id}"
35
+
36
+ unless @completed_tasks.find {|t| t.period_id == @task.period_id}
37
+ page["completed_tasks_#{@task.period_id}"].remove
38
+ end
39
+ end
40
+
41
+ def add_active_task(page)
42
+ first_in_period = @tasks.find{|t| t.id != @task.id && t.period_id == @task.period_id}.nil?
43
+ if first_in_period
44
+ page.insert_html :top, :active_tasks, %Q{<table id="active_tasks_#{@task.period_id}" class="input" style="width: 100%;"/>}
45
+ else
46
+ page.select('#active_tasks_#{@task.period_id} tr').first.remove
47
+ page.select('#active_tasks_#{@task.period_id} tr').first.remove
48
+ end
49
+
50
+ page.insert_html(:top, "active_tasks_#{@task.period_id}", render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true }))
51
+
52
+ fields_header = render(:partial => '/tasks/fields_header', :locals => { :backlog => @task.backlog, :active => true } )
53
+ page.insert_html :top, "active_tasks_#{@task.period_id}", fields_header
54
+ page.insert_html :top, "active_tasks_#{@task.period_id}", render(:partial => "/tasks/period_header", :locals => {:period => @task.period})
55
+
56
+ page[:no_tasks_message].hide
57
+
58
+ if @tasks.size == 1
59
+ page.visual_effect :appear, "active_tasks"
60
+ end
61
+ page.visual_effect :appear, "task_#{@task.id}"
62
+ end
63
+
2
64
  end
@@ -1,2 +1,65 @@
1
1
  module PeriodsHelper
2
+ def remove_active_task(page)
3
+ page.visual_effect :blind_up, "task_#{@task.id}"
4
+ if @tasks.find {|t| t.backlog_id == @task.backlog_id}.nil?
5
+ page["task_#{@task.id}"].ancestors.first.ancestors.first.remove
6
+ else
7
+ page.remove "task_#{@task.id}"
8
+ end
9
+
10
+ if @tasks.empty?
11
+ page.visual_effect :blind_up, "active_tasks"
12
+ page.visual_effect :appear, "no_tasks_message"
13
+ end
14
+ end
15
+
16
+ def remove_finished_task(page)
17
+ page.visual_effect :fade, "task_#{@task.id}"
18
+ page.remove "task_#{@task.id}"
19
+
20
+ if @last_finished
21
+ page.select('#completed_tasks tr').first.remove
22
+ end
23
+ end
24
+
25
+ def add_active_task(page)
26
+ if @task.lower_item.nil? || @task.lower_item.backlog != @task.backlog
27
+ page.insert_html :top, :active_tasks, '<table class="input" style="width: 100%;"/>'
28
+ elsif @task.lower_item
29
+ page.select('#active_tasks table tr').first.remove
30
+ page.select('#active_tasks table tr').first.remove
31
+ end
32
+
33
+ insert(page, render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true }), '#active_tasks table')
34
+
35
+ fields_header = render(:partial => '/tasks/fields_header', :locals => { :backlog => @task.backlog, :active => true } )
36
+ insert(page, fields_header, '#active_tasks table')
37
+ insert(page, render(:partial => "/tasks/backlog_header", :locals => {:backlog => @task.backlog}), '#active_tasks table')
38
+
39
+ page[:no_tasks_message].hide
40
+
41
+ if @task.lower_item.nil?
42
+ page.visual_effect :appear, "active_tasks"
43
+ end
44
+ page.visual_effect :appear, "task_#{@task.id}"
45
+ end
46
+
47
+ def add_finished_task(page)
48
+ first_finished = @completed_tasks.find {|t| t.id != @task.id}.nil?
49
+ unless first_finished
50
+ page.select("#completed_tasks_#{@task.period_id} tr").first.remove
51
+ end
52
+
53
+ page.insert_html :top, "completed_tasks_#{@task.period_id}", :partial => '/tasks/task', :locals => {:active => false, :hidden => true, :highlight_task => false}
54
+ page.visual_effect :appear, "task_#{@task.id}"
55
+
56
+ page.insert_html :top, "completed_tasks_#{@task.period_id}", :partial => '/tasks/fields_header', :locals => {:backlog => @task.backlog, :active => false}
57
+ end
58
+
59
+ def update_burn_down_chart(page)
60
+ if @period
61
+ page['burn_down_chart'].src = url_for(:controller => 'periods', :action => :burn_down_chart_thumbnail, :id => @task.period.id, :rnd => rand)
62
+ end
63
+ end
64
+
2
65
  end