backlog 0.3.2 → 0.3.3

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 CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.3.3 2007-08-03
2
+
3
+ * Made validation of tasks position tighter
4
+ * Fixed bugs in task positioning :)
5
+
1
6
  == 0.3.2 2007-08-02
2
7
 
3
8
  * Improved setup on linux
data/README.txt CHANGED
@@ -5,6 +5,12 @@ Welcome to Backlog!
5
5
  Backlog is a tool to help you collect and organize all your tasks,
6
6
  wether you are a single persion or a small or large group.
7
7
 
8
+ A timekeeping module is also included to track time spent on the different tasks.
9
+
10
+ === Backlog is not meant to be
11
+
12
+ * an issue tracker with customer communication.
13
+
8
14
  === Installation
9
15
 
10
16
  * Install ruby
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ require 'tasks/rails'
11
11
 
12
12
  require 'hoe'
13
13
 
14
- Hoe.new("backlog", '0.3.2') do |p|
14
+ Hoe.new("backlog", '0.3.3') do |p|
15
15
  p.rubyforge_name = "backlog"
16
16
  p.summary = "Application to aid collecting, processing, organizing, reviewing and doing tasks."
17
17
  p.description = p.paragraphs_of('README.txt', 0..-1).join("\n\n")
@@ -1,6 +1,5 @@
1
1
  class TasksController < ApplicationController
2
- skip_before_filter :populate_layout, :only => [:create, :update, :set_task_description, :move_to,
3
- :move_to_next_period, :move_to_period, :reopen, :start_work]
2
+ skip_before_filter :populate_layout, :only => [:create, :update, :destroy, :set_task_description, :move_to, :move_to_next_period, :move_to_period, :reopen, :start_work]
4
3
 
5
4
  verify :method => :post, :except => [ :new, :show, :edit, :list_started, :move_to_next_period],
6
5
  :redirect_to => { :controller => 'backlogs' }
@@ -28,7 +27,11 @@ class TasksController < ApplicationController
28
27
  if @task.save
29
28
  flash[:notice] = 'Task was successfully created.'
30
29
  @task.move_to_top
31
- redirect_to :controller => 'periods', :action => 'show', :id => @task.period, :task => @task.id
30
+ if @task.period
31
+ back_or_redirect_to :controller => 'periods', :action => 'show', :id => @task.period, :task => @task.id
32
+ else
33
+ back_or_redirect_to :controller => 'backlogs', :action => 'show', :id => @task.backlog, :task => @task.id
34
+ end
32
35
  else
33
36
  populate_layout
34
37
  @backlogs = Backlog.find(:all, :order => 'name')
@@ -75,6 +78,7 @@ class TasksController < ApplicationController
75
78
  end
76
79
  else
77
80
  populate_layout
81
+ edit
78
82
  render :action => 'edit'
79
83
  end
80
84
  end
@@ -146,7 +150,7 @@ class TasksController < ApplicationController
146
150
 
147
151
  def finish
148
152
  @task = Task.find(params[:id])
149
- @task.finish(Task::COMPLETED, true, user)
153
+ @task.finish(Task::COMPLETED, true)
150
154
  redirect_to :controller => 'periods', :action => :show, :id => @task.period, :task_id => @task.id
151
155
  end
152
156
 
@@ -158,14 +162,13 @@ class TasksController < ApplicationController
158
162
 
159
163
  def reopen
160
164
  task = Task.find(params[:id])
161
- task.reopen(user)
165
+ task.reopen
162
166
  back_or_redirect_to :controller => 'periods', :action => :show, :id => task.period, :task => task.id
163
167
  end
164
168
 
165
169
  def start_work
166
170
  task = Task.find(params[:id])
167
- task.start_work(user)
168
- task.save!
171
+ task.start_work
169
172
  redirect_to :controller => 'periods', :action => :show, :id => task.period, :task => task.id
170
173
  end
171
174
 
@@ -42,7 +42,7 @@ class WorksController < ApplicationController
42
42
  return
43
43
  end
44
44
 
45
- @work.task.estimates.new(params[:estimate]).save! if params[:estimate]
45
+ @work.task.estimates.create!(params[:estimate]) if params[:estimate]
46
46
 
47
47
  back_or_redirect_to :controller => 'periods', :action => 'show', :id => @work.task.period, :task_id => @work.task.id
48
48
  end
@@ -61,11 +61,14 @@ class WorksController < ApplicationController
61
61
  convert_hours_param
62
62
  if @work.update_attributes(params[:work])
63
63
  flash[:notice] = 'Work was successfully updated.'
64
- back_or_redirect_to :controller => 'periods', :action => 'show', :id => @work.task.period, :task_id => @work.task.id
65
- else
66
- render :action => 'edit'
64
+ if params[:estimate].nil? || (@estimate = @work.task.estimates.create(params[:estimate])).errors.size == 0
65
+ back_or_redirect_to :controller => 'periods', :action => 'show', :id => @work.task.period, :task_id => @work.task.id
66
+ return
67
+ end
67
68
  end
68
- @work.task.estimates.new(params[:estimate]).save! if params[:estimate]
69
+ @task = @work.task
70
+ edit
71
+ render :action => 'edit'
69
72
  end
70
73
 
71
74
  def destroy
@@ -102,7 +105,7 @@ class WorksController < ApplicationController
102
105
 
103
106
  def auto_complete_for_work_backlog_name
104
107
  @backlogs = Backlog.find(:all,
105
- :conditions => [ 'LOWER(name) LIKE ?',
108
+ :conditions => [ 'LOWER(name) LIKE ?',
106
109
  '%' + params[:work][:backlog_name].downcase + '%' ],
107
110
  :order => 'name ASC',
108
111
  :limit => 16)
@@ -37,7 +37,7 @@ class Backlog < ActiveRecord::Base
37
37
 
38
38
  def first_active_period
39
39
  t = active_tasks
40
- periods = t.map {|t| t.period}.uniq
40
+ periods = t.map {|t| t.period}.compact.uniq
41
41
  periods = periods.select {|p| p.active?}
42
42
  periods.sort_by {|p| p.end_on}.first
43
43
  end
@@ -10,7 +10,7 @@ class Estimate < ActiveRecord::Base
10
10
  validates_associated :task
11
11
 
12
12
  def after_create
13
- task.finish(Task::COMPLETED, false, user) if todo == 0
13
+ task.finish(Task::COMPLETED, false) if todo == 0
14
14
  end
15
15
 
16
16
  end
data/app/models/task.rb CHANGED
@@ -8,7 +8,7 @@ class Task < ActiveRecord::Base
8
8
 
9
9
  belongs_to :backlog
10
10
  belongs_to :period
11
- acts_as_list :scope => '#{period_id ? "period_id = #{period_id}" : "parent_id = #{parent_id}"} AND finished_at IS NULL'
11
+ acts_as_list :scope => '#{period_id ? "period_id = #{period_id}" : parent_id ? "parent_id = #{parent_id}" : "backlog_id = #{backlog_id}"} AND finished_at IS NULL'
12
12
  has_many :estimates, :order => 'created_at', :dependent => :destroy
13
13
  has_many :works, :order => 'completed_at', :dependent => :destroy
14
14
  acts_as_tree :order => 'position'
@@ -20,12 +20,15 @@ class Task < ActiveRecord::Base
20
20
  #validates_absence_of :position, :if => :finished_at
21
21
  #validates_absence_of :finished_at, :if => :position
22
22
  validates_presence_of :resolution, :if => :finished_at
23
- validates_uniqueness_of :description, :scope => :period_id
24
- validates_uniqueness_of :position, :scope => :period_id, :allow_nil => true
23
+ validates_uniqueness_of :description, :scope => :backlog_id, :if => :backlog_id
24
+ validates_uniqueness_of :description, :scope => :period_id, :if => :period_id
25
+ validates_uniqueness_of :position, :scope => :period_id, :if => :period_id, :allow_nil => true
26
+ validates_uniqueness_of :position, :scope => :parent_id, :if => :parent_id, :allow_nil => true
27
+ validates_uniqueness_of :position, :scope => :backlog_id, :if => Proc.new {|task| task.period_id.nil? && task.parent_id.nil?}, :allow_nil => true
25
28
 
26
29
  def validate
27
- unless (self.period_id || self.parent_id) && !(self.period_id && self.parent_id)
28
- errors.add :parent_id, "A task may have either period or parent task set, not both."
30
+ if self.parent_id && (self.period_id || self.backlog_id)
31
+ errors.add :parent_id, "A subtask may not have neither period nor backlog set."
29
32
  end
30
33
  if new_record? && self.period && self.period.passed?
31
34
  errors.add :period_id, "You may not add a task to a past period."
@@ -99,21 +102,21 @@ class Task < ActiveRecord::Base
99
102
 
100
103
  def open
101
104
  if finished_at
105
+ insert_at 1
102
106
  self.finished_at = nil
103
107
  self.resolution = nil
104
108
  estimate(initial_estimate)
105
- insert_at 1
106
109
  parent.open if parent
107
110
  end
108
111
  end
109
112
 
110
- def reopen(user)
113
+ def reopen
111
114
  if period.passed?
112
115
  flash[:notice] = "You cannot reopen a task in a period that is passed."
113
116
  else
114
- open(user)
117
+ open
115
118
  save!
116
- children.each {|child_task| child_task.reopen(user)}
119
+ children.each {|child_task| child_task.reopen}
117
120
  end
118
121
  end
119
122
 
@@ -137,24 +140,24 @@ class Task < ActiveRecord::Base
137
140
  self.finish(new_task.period.party == self.period.party ? Task::POSTPONED : Task::MOVED, true, current_user)
138
141
  end
139
142
 
140
- def finish(resolution, save_work, user)
143
+ def finish(resolution, save_work)
141
144
  unless finished_at || work_started?
142
145
  self.finished_at = Time.now
143
146
  self.resolution = resolution
147
+ remove_from_list
144
148
  self.position = nil
145
149
  save!
146
- remove_from_list
147
150
  estimate(0) if save_work
148
- parent.check_finished(self.finished_at, resolution, save_work, user) if parent
151
+ parent.check_finished(self.finished_at, resolution, save_work) if parent
149
152
  end
150
153
  end
151
154
 
152
- def check_finished(subtask_finsihed_at, resolution, save_work, user)
155
+ def check_finished(subtask_finsihed_at, resolution, save_work)
153
156
  return if self.finished_at
154
157
  children.each do |child_task|
155
158
  return if child_task.active?
156
159
  end
157
- finish(resolution, save_work, user)
160
+ finish(resolution, save_work)
158
161
  end
159
162
 
160
163
  def active?
@@ -263,14 +266,14 @@ class Task < ActiveRecord::Base
263
266
  total
264
267
  end
265
268
 
266
- def start_work(user)
269
+ def start_work
267
270
  return if work_started?
268
- open(user)
271
+ open
269
272
  new_work = works.new
270
273
  new_work.started_at = Time.previous_quarter
271
274
  if works.size > 0
272
- if user
273
- last_work = works.select {|work| work.user == user}.last
275
+ if current_user
276
+ last_work = works.select {|work| work.user == current_user}.last
274
277
  end
275
278
  unless last_work
276
279
  last_work = works.select {|work| work.user.nil?}.last
@@ -279,8 +282,16 @@ class Task < ActiveRecord::Base
279
282
  new_work.started_at = last_work.completed_at
280
283
  end
281
284
  end
282
- new_work.user = user
285
+ new_work.user = current_user
286
+ begin
283
287
  new_work.save!
288
+ rescue Exception => e
289
+ p e
290
+ p e.record
291
+ p e.record.task
292
+ p e.record.task.errors
293
+ p e.record.task.errors.full_messages
294
+ end
284
295
  end
285
296
 
286
297
  def works_with_children
@@ -288,7 +299,7 @@ class Task < ActiveRecord::Base
288
299
  end
289
300
 
290
301
  def abort(user)
291
- finish(Task::ABORTED, false, user)
302
+ finish(Task::ABORTED, false)
292
303
  end
293
304
 
294
305
  def work_started?
data/app/models/work.rb CHANGED
@@ -3,6 +3,7 @@ class Work < ActiveRecord::Base
3
3
  belongs_to :user
4
4
 
5
5
  validates_presence_of :task_id
6
+ validates_associated :task
6
7
  validates_presence_of :started_at
7
8
  validates_presence_of :user_id, :if => :validate_user?
8
9
 
@@ -34,14 +34,14 @@ function handlePageEvent(event) {
34
34
  <% if user? %>| <%=user.email%><% end %>
35
35
  </div>
36
36
  <div id="header">
37
+ <%= error_messages_for :backlog, :user, :group, :period, :task, :work, :estimate %>
38
+
37
39
  <div id="introtext">
38
40
  <h1><%=@application_title%></h1>
39
41
  <h3><%=@page_title ? @page_title : @application_description%></h3>
40
42
  </div>
41
43
  </div>
42
44
  <div id="content">
43
- <%= error_messages_for 'period', 'task' %>
44
-
45
45
  <%= yield %>
46
46
  </div>
47
47
  </div>
@@ -1,5 +1,3 @@
1
- <%= error_messages_for 'task' %>
2
-
3
1
  <!--[form:task]-->
4
2
  <p>
5
3
  <% if @task.backlog.nil? || @task.new_record? %>
@@ -12,6 +10,7 @@
12
10
  <% if @task.backlog %>
13
11
  <%=image_detour_to('clipboard.png', "#{l(:backlog)} #{@task.backlog.name}", {:controller => 'backlogs', :action => :edit, :id => @task.backlog}, {:class => 'image-submit', :style => 'vertical-align: bottom'}) %>
14
12
  <% end %>
13
+ <%=detour_to l(:new_backlog), :controller => 'backlogs', :action => :new %>
15
14
  </p>
16
15
 
17
16
 
@@ -8,7 +8,7 @@
8
8
  <% end %>
9
9
  </td>
10
10
  <td align="left" valign="top" width="1" nowrap="true">
11
- <%=detour_to @task.position.to_s, :controller => 'tasks', :action => :edit, :id => @task.id if @task.depth == 0 %>
11
+ <%=detour_to @task.position.to_s, :controller => 'tasks', :action => :edit, :id => @task.id if @task.position || @task.depth == 0 %>
12
12
  <%= ("&nbsp;" * @task.depth * 4) if @task.depth > 0 %>
13
13
  <%= l(@task.resolution.downcase) if @task.finished_at %>
14
14
  <%= "-" if @task.children.size > 0 %>
@@ -34,8 +34,8 @@
34
34
  <% end %>
35
35
  <% end -%>
36
36
  </td>
37
- <% if @task.track_done? %>
38
37
  <td align="<%=@task.loggable? ? 'center' : 'left'%>" nowrap="true" width="1">
38
+ <% if @task.track_done? %>
39
39
  <% if @task.loggable? || @task.finished_at -%>
40
40
  <% form_tag({:controller => 'works', :action => (@task.work_started? ? :edit : :create), :id => @task.started_work}) do %>
41
41
  <%= hidden_field('work', 'task_id', :value => @task.id)%>
@@ -48,8 +48,8 @@
48
48
  <%=t @task.total_done if @task.total_done != 0%>
49
49
  <% end -%>
50
50
  <% end -%>
51
- </td>
52
51
  <% end -%>
52
+ </td>
53
53
  <td nowrap="true" width="1">
54
54
  <% if active && @task.loggable? -%>
55
55
  <% form_tag({:controller => 'estimates', :action => 'create', :id => @task}) do %>
@@ -17,12 +17,12 @@ th.hours {text-align: right;}
17
17
  }
18
18
 
19
19
  #errorExplanation {
20
- width: 400px;
21
20
  border: 2px solid red;
22
21
  padding: 7px;
23
22
  padding-bottom: 12px;
24
23
  margin-bottom: 20px;
25
- background-color: #f0f0f0;
24
+ background: transparent;
25
+ float: right;
26
26
  }
27
27
 
28
28
  #errorExplanation h2 {
@@ -5,12 +5,26 @@ first:
5
5
  backlog_id: 1
6
6
  period_id: 1
7
7
  description: first task
8
- position: 1
8
+ position: 0
9
9
  another:
10
10
  id: 2
11
11
  created_at: 2007-06-12
12
12
  backlog_id: 1
13
13
  period_id: 2
14
14
  description: second task
15
+ position: 0
16
+ started:
17
+ id: 3
18
+ created_at: 2007-08-02 14:15:42
19
+ backlog_id: 1
20
+ period_id: 2
21
+ description: third task
22
+ position: 1
23
+ last:
24
+ id: 4
25
+ created_at: 2007-08-02 14:15:42
26
+ backlog_id: 1
27
+ period_id: 2
28
+ description: last task
15
29
  position: 2
16
30
 
@@ -4,7 +4,7 @@ first:
4
4
  task_id: 1
5
5
  started_at: 2007-06-12T13:35:00
6
6
  completed_at: 2007-06-12T14:35:00
7
- another:
7
+ started:
8
8
  id: 2
9
- task_id: 2
9
+ task_id: 3
10
10
  started_at: 2007-06-12T13:35:00
@@ -5,7 +5,7 @@ require 'tasks_controller'
5
5
  class TasksController; def rescue_action(e) raise e end; end
6
6
 
7
7
  class TasksControllerTest < Test::Unit::TestCase
8
- fixtures :users, :backlogs, :periods, :tasks, :works, :estimates
8
+ fixtures :parties, :users, :groups, :backlogs, :periods, :tasks, :works, :estimates
9
9
 
10
10
  def setup
11
11
  @controller = TasksController.new
@@ -65,6 +65,20 @@ class TasksControllerTest < Test::Unit::TestCase
65
65
  assert_equal num_tasks + 1, Task.count
66
66
  end
67
67
 
68
+ def test_create_without_period
69
+ num_tasks = Task.count
70
+
71
+ post :create, :task => {:description => 'an important task', :backlog_id => '2'}
72
+
73
+ task = assigns(:task)
74
+ assert_equal [], task.errors.full_messages
75
+
76
+ assert_response :redirect
77
+ assert_redirected_to :controller => 'backlogs', :action => 'show', :id => 2
78
+
79
+ assert_equal num_tasks + 1, Task.count
80
+ end
81
+
68
82
  def test_edit
69
83
  get :edit, :id => 1
70
84
 
@@ -86,4 +86,32 @@ class WorksControllerTest < Test::Unit::TestCase
86
86
  Work.find(1)
87
87
  }
88
88
  end
89
+
90
+ def test_update_with_finish
91
+ num_tasks = Task.count
92
+ num_open_tasks = Task.find_open.size
93
+ task = tasks(:started)
94
+ work = works(:started)
95
+
96
+ post :update, "commit"=>"Lagre", "action"=>"update", "id"=>work.id.to_s, "controller"=>"works", "estimate"=>{"todo"=>"0"}, "work"=>{"completed_at"=>"2007-08-02 14:15", "task_id"=> task.id.to_s, "user_id"=>"1000001", "started_at"=>"2007-08-02 14:00"}
97
+
98
+ assert_response :redirect
99
+ assert_redirected_to :controller => 'periods', :action => :show, :id => task.period, :task => nil
100
+
101
+ assert_equal num_tasks, Task.count
102
+ assert_equal num_open_tasks - 1, Task.find_open.size
103
+ assert_sequence
104
+ end
105
+
106
+
107
+ private
108
+
109
+ def assert_sequence
110
+ Period.find(:all).each do |p|
111
+ p.open_tasks.each_with_index do |t, i|
112
+ assert_equal i, t.position
113
+ end
114
+ end
115
+ end
116
+
89
117
  end
@@ -1,7 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/../test_helper'
2
2
 
3
3
  class TaskTest < Test::Unit::TestCase
4
- fixtures :backlogs, :periods, :tasks, :estimates, :works
4
+ fixtures :parties, :users, :groups, :backlogs, :periods, :tasks, :estimates, :works
5
5
 
6
6
  # Replace this with your real tests.
7
7
  def test_truth
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: backlog
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.2
7
- date: 2007-08-02 00:00:00 +02:00
6
+ version: 0.3.3
7
+ date: 2007-08-03 00:00:00 +02:00
8
8
  summary: Application to aid collecting, processing, organizing, reviewing and doing tasks.
9
9
  require_paths:
10
10
  - lib
11
11
  email: ryand-ruby@zenspider.com
12
12
  homepage: http://www.zenspider.com/ZSS/Products/backlog/
13
13
  rubyforge_project: backlog
14
- description: == Backlog Welcome to Backlog! Backlog is a tool to help you collect and organize all your tasks, wether you are a single persion or a small or large group. === Installation * Install ruby * Install RubyGems * Install PostgreSQL * run <tt>sudo gem install backlog -y</tt> * run <tt>sudo backlog setup_linux</tt> * run <tt>sudo backlog start</tt> === Updates * run <tt>sudo backlog stop</tt> * run <tt>sudo gem update -y</tt> * run <tt>sudo backlog start</tt>
14
+ description: == Backlog Welcome to Backlog! Backlog is a tool to help you collect and organize all your tasks, wether you are a single persion or a small or large group. A timekeeping module is also included to track time spent on the different tasks. === Backlog is not meant to be * an issue tracker with customer communication. === Installation * Install ruby * Install RubyGems * Install PostgreSQL * run <tt>sudo gem install backlog -y</tt> * run <tt>sudo backlog setup_linux</tt> * run <tt>sudo backlog start</tt> === Updates * run <tt>sudo backlog stop</tt> * run <tt>sudo gem update -y</tt> * run <tt>sudo backlog start</tt>
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
@@ -105,7 +105,6 @@ files:
105
105
  - public/stylesheets/zpcal/themes/scroller-down.gif
106
106
  - public/stylesheets/zpcal/themes/scroller-up.gif
107
107
  - public/stylesheets/backlog.css
108
- - public/stylesheets/scaffold.css
109
108
  - public/stylesheets/user.css
110
109
  - public/robots.txt
111
110
  - public/404.html
@@ -1,74 +0,0 @@
1
- body { background-color: #fff; color: #333; }
2
-
3
- body, p, ol, ul, td {
4
- font-family: verdana, arial, helvetica, sans-serif;
5
- font-size: 13px;
6
- line-height: 18px;
7
- }
8
-
9
- pre {
10
- background-color: #eee;
11
- padding: 10px;
12
- font-size: 11px;
13
- }
14
-
15
- a { color: #000; }
16
- a:visited { color: #666; }
17
- a:hover { color: #fff; background-color:#000; }
18
-
19
- .fieldWithErrors {
20
- padding: 2px;
21
- background-color: red;
22
- display: table;
23
- }
24
-
25
- #errorExplanation {
26
- width: 400px;
27
- border: 2px solid red;
28
- padding: 7px;
29
- padding-bottom: 12px;
30
- margin-bottom: 20px;
31
- background-color: #f0f0f0;
32
- }
33
-
34
- #errorExplanation h2 {
35
- text-align: left;
36
- font-weight: bold;
37
- padding: 5px 5px 5px 15px;
38
- font-size: 12px;
39
- margin: -7px;
40
- background-color: #c00;
41
- color: #fff;
42
- }
43
-
44
- #errorExplanation p {
45
- color: #333;
46
- margin-bottom: 0;
47
- padding: 5px;
48
- }
49
-
50
- #errorExplanation ul li {
51
- font-size: 12px;
52
- list-style: square;
53
- }
54
-
55
- div.uploadStatus {
56
- margin: 5px;
57
- }
58
-
59
- div.progressBar {
60
- margin: 5px;
61
- }
62
-
63
- div.progressBar div.border {
64
- background-color: #fff;
65
- border: 1px solid grey;
66
- width: 100%;
67
- }
68
-
69
- div.progressBar div.background {
70
- background-color: #333;
71
- height: 18px;
72
- width: 0%;
73
- }
74
-