backlog 0.13.0 → 0.13.1

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