backlog 0.23.0 → 0.23.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.
data/History.txt CHANGED
@@ -1,3 +1,25 @@
1
+ == 0.23.1 2008-02-26
2
+
3
+ === Features
4
+
5
+ * Declared the daily work sheet to be out of experimental stage.
6
+ * Automatically calculate total hours for work record in daily work sheet.
7
+ * Added summary of who has grabbed a task in the edit task view.
8
+
9
+ === Fixes
10
+
11
+ * Moved to JRuby 1.1RC2 for the WAR.
12
+ * Removed unused keyboard shortcuts, and fixed the remaining shortcuts.
13
+ * Fixed bug where the last date in a search for work records for a work account were omitted.
14
+ * Some speedup of daily work sheet and weekly work sheet.
15
+ * Fixed bug where end time for a work record was filled in when it should be left blank in daily work sheet.
16
+ * Fixed rounding error for hours in daily work sheet.
17
+ * Fixed bug in work lock nagger when the global configuration file was missing.
18
+ * Changed sort order in daily work sheet from completion time to start time.
19
+ * Changed to allow time format without separator: 0800 and 800 equal 08:00.
20
+ * Minor cosmetic tweaks in the daily work sheet.
21
+ * Fixed style sheet to remove the flickers in the sprint view.
22
+
1
23
  == 0.23.0 2008-02-25
2
24
 
3
25
  === Features
@@ -146,12 +146,6 @@ class ApplicationController < ActionController::Base
146
146
  {:key => 'Alt-Shift-N', :function => :new_period, :options => {:controller => 'periods', :action => 'new'}},
147
147
  {:key => 'Alt-Ctrl-N', :function => :new_backlog, :options => {:controller => 'backlogs', :action => 'new'}},
148
148
  {:key => 'Alt-Ctrl-G', :function => :new_group, :options => {:controller => 'groups', :action => :new}},
149
- {:key => "Alt-#{l :up}", :function => :up},
150
- {:key => "Alt-#{l :down}", :function => :down},
151
- {:key => "Alt-Shift-#{l :left}", :function => :move_to_top},
152
- {:key => "Alt-Shift-#{l :right}", :function => :move_to_bottom},
153
- {:key => 'Alt-X', :function => :complete},
154
- {:key => 'Alt-Q', :function => :reopen},
155
149
  ]
156
150
  end
157
151
 
@@ -59,7 +59,7 @@ class WorkAccountsController < ApplicationController
59
59
  work_account = WorkAccount.find(params[:id])
60
60
  @report_filter = ReportFilter.new(params[:report_filter])
61
61
  @report_filter.title = "#{l :hours} for #{work_account.name} #{@report_filter.start_on && @report_filter.start_on.strftime('%Y-%m-%d - ')}#{@report_filter.end_on && @report_filter.end_on.strftime('%Y-%m-%d')}"
62
- @works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? AND work_account_id = ?", @report_filter.start_on, @report_filter.end_on, work_account.id], :page => params[:page], :per_page => @report_filter.page_size
62
+ @works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? AND work_account_id = ?", @report_filter.start_on, @report_filter.end_on + 1, work_account.id], :page => params[:page], :per_page => @report_filter.page_size
63
63
  if params[:export] == 'excel'
64
64
  render :template => '/works/list_excel', :layout => false
65
65
  else
@@ -3,7 +3,7 @@ class WorksController < ApplicationController
3
3
  in_place_edit_for :work, :invoice
4
4
  in_place_edit_for :work, :started_at_time
5
5
  in_place_edit_for :work, :completed_at_time
6
- skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update, :daily_work_sheet, :weekly_work_sheet_by_work_account]
6
+ skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update]
7
7
  auto_complete_for :work, :description
8
8
 
9
9
  def index
@@ -59,9 +59,8 @@ class WorksController < ApplicationController
59
59
  end
60
60
  end
61
61
  convert_hours_param
62
- convert_start_time_param
63
62
  @work = Work.new(params[:work])
64
- @work.completed_at = Time.now unless @work.completed_at
63
+ @work.completed_at = Time.now unless @work.started_at || @work.completed_at
65
64
  @work.user_id = current_user.id
66
65
  if @work.save
67
66
  flash[:notice] = 'Work was successfully created.'
@@ -120,6 +119,10 @@ class WorksController < ApplicationController
120
119
  @work = Work.find(params[:id])
121
120
  convert_hours_param
122
121
  if @work.update_attributes(params[:work])
122
+ if params[:work] && (params[:work][:started_at_time] || params[:work][:completed_at_time])
123
+ @work.calculate_hours!
124
+ @work.save!
125
+ end
123
126
  flash[:notice] = 'Work was successfully updated.'
124
127
  if @work.task
125
128
  if params[:estimate]
@@ -234,33 +237,15 @@ class WorksController < ApplicationController
234
237
 
235
238
  private
236
239
 
237
- def convert_start_time_param
238
- if params[:work] && params[:work][:started_at_time]
239
- if params[:work][:started_at_time] =~ /^(\d*):(\d{2})$/
240
- new_hours = $1
241
- new_minutes = $2.to_i
242
- else
243
- raise "Unknown time format: '#{params[:work][:hours_time]}'"
244
- end
245
- if params[:work][:completed_at]
246
- t = Time.parse params[:work][:completed_at]
247
- elsif @work && @work.started?
248
- t = @work.started_at
249
- else
250
- t = Time.now
251
- end
252
- params[:work][:started_at] = Time.local(t.year, t.month, t.day, new_hours, new_minutes)
253
- params[:work].delete :started_at_time
254
- end
255
- end
256
-
257
240
  def convert_hours_param
258
241
  if params[:work] && params[:work][:hours_time]
259
242
  params[:work][:hours_time].strip!
260
243
  if params[:work][:hours_time] =~ /^(\d*):(\d{2})$/
261
244
  new_hours = $1
262
245
  new_minutes = $2.to_i
263
- params[:work][:hours] = BigDecimal("#{new_hours}.#{new_minutes / 0.6}")
246
+ new_value_str = "#{new_hours}.#{(new_minutes / 0.6).ceil}"
247
+ new_value = BigDecimal(new_value_str)
248
+ params[:work][:hours] = new_value
264
249
  elsif params[:work][:hours_time] =~ /^(\d*(?:.\d)?)$/
265
250
  params[:work][:hours] = BigDecimal($1)
266
251
  else
data/app/models/work.rb CHANGED
@@ -98,7 +98,7 @@ class Work < ActiveRecord::Base
98
98
 
99
99
  def self.find_work_for_day date
100
100
  Work.find(:all, :conditions => "(completed_at IS NULL OR completed_at BETWEEN '#{date}' AND '#{date+1}') AND user_id = #{current_user.id}",
101
- :order => 'completed_at, started_at')
101
+ :order => 'started_at, completed_at')
102
102
  end
103
103
 
104
104
  def started?
@@ -111,7 +111,7 @@ class Work < ActiveRecord::Base
111
111
 
112
112
  def started_at_time=(new_value)
113
113
  new_value = '0:00' if new_value == ''
114
- raise "invalid time format: #{new_value}" unless new_value =~ /(\d{0,2}):(\d{2})/
114
+ raise "invalid time format: #{new_value}" unless new_value =~ /(\d{0,2}):?(\d{2})/
115
115
  new_hour, new_minutes = $1.to_i, $2.to_i
116
116
  t = started_at || Time.now
117
117
  self.started_at = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
@@ -126,10 +126,15 @@ class Work < ActiveRecord::Base
126
126
  self.completed_at = nil
127
127
  return
128
128
  end
129
- raise "invalid time format: #{new_value}" unless new_value =~ /(\d{2}):(\d{2})/
129
+ raise "invalid time format: #{new_value}" unless new_value =~ /(\d{0,2}):?(\d{2})/
130
130
  new_hour, new_minutes = $1, $2
131
131
  t = completed_at || Time.now
132
132
  self.completed_at = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
133
133
  end
134
134
 
135
+ def calculate_hours!
136
+ return unless started_at && completed_at
137
+ self.hours = (completed_at - started_at) / 3600
138
+ end
139
+
135
140
  end
@@ -8,8 +8,11 @@ class WorkLockNagger
8
8
  sleep 1.minute
9
9
  puts "Work Lock Nagger started"
10
10
  begin
11
- config = YAML::load(ERB.new(IO.read(APP_CONFIG_FILE)).result) || {}
12
- if app_url = config[:app_url]
11
+ if File.exists? APP_CONFIG_FILE
12
+ config = YAML::load(ERB.new(IO.read(APP_CONFIG_FILE)).result) || {}
13
+ app_url = config[:app_url]
14
+ end
15
+ if app_url
13
16
  url = app_url + 'works/weekly_work_sheet_by_work_account'
14
17
  else
15
18
  host = Socket::gethostname
@@ -9,11 +9,15 @@
9
9
  <script type="text/javascript">
10
10
  function handlePageEvent(event) {
11
11
  if (event.altKey && event.shiftKey && event.charCode == 110) {
12
- window.location = '<%=url_for(:controller => 'backlogs', :action => :new)%>';
12
+ window.location = '<%=url_for(:controller => 'periods', :action => :new)%>';
13
+ } else if (event.altKey && event.ctrlKey && event.charCode == 110) {
14
+ window.location = '<%=url_for(:controller => 'backlogs', :action => :new ) %>';
13
15
  } else if (event.altKey && event.charCode == 110) {
14
- window.location = '<%=url_for(:controller => 'tasks', :action => :new, :period_id => (@backlog ? @backlog.id : nil) ) %>';
16
+ window.location = '<%=url_for(:controller => 'tasks', :action => :new, :task => {:backlog_id => (@backlog ? @backlog.id : nil), :period_id => (@period ? @period.id : nil)} ) %>';
17
+ } else if (event.altKey && event.ctrlKey && event.charCode == 103) {
18
+ window.location = '<%=url_for(:controller => 'groups', :action => :new ) %>';
15
19
  } else {
16
- // alert("shift: " + event.shiftKey + ", alt: " + event.altKey + ", ctrl: " + event.ctrlKey + ", " + event.charCode + ", " + event.keyCode);
20
+ // alert("shift: " + event.shiftKey + ", alt: " + event.altKey + ", ctrl: " + event.ctrlKey + ", " + event.charCode + ", " + event.keyCode);
17
21
  }
18
22
  }
19
23
  </script>
@@ -74,6 +74,20 @@
74
74
  <% end %>
75
75
  <br clear="all" />
76
76
 
77
+ <% if @task.backlog.enable_users? %>
78
+ <% unless @task.new_record? %>
79
+ <p><label><%=l :ownership%></label><br/>
80
+ <%= if @task.users.empty?
81
+ l(:not_grabbed)
82
+ elsif @task.users.include? current_user
83
+ l(:grabbed)
84
+ end
85
+ %>
86
+ <%=link_to_remote l(:invite), :action => :invite, :id => @task.id %>
87
+ </p>
88
+ <% end %>
89
+ <% end %>
90
+
77
91
  <p><label for="task_notes"><%=l :notes%></label><br/>
78
92
  <%=text_area 'task', 'notes', :cols => '72', :rows => 16 %>
79
93
  <%=if @task.notes and not @task.notes.empty? then image_link_to 'view_fullscreen.png', l(:view_fullscreen), :controller => 'tasks', :action => :notes, :id => @task end%>
@@ -15,17 +15,17 @@
15
15
  <td align="right">
16
16
  <%=render :partial => 'row_field', :locals => {:field => 'started_at_time', :next_field => 'completed_at_time'} %>
17
17
  </td>
18
- <td align="right">
18
+ <td align="left">
19
19
  <%=render :partial => 'row_field', :locals => {:field => 'completed_at_time', :next_field => 'hours_time'} %>
20
20
  </td>
21
21
  <td align="right">
22
- <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id} do |f|%>
23
- <%=f.text_field :hours_time, :value => (@work.hours && @work.hours > 0 ? t(@work.hours) : ''), :id => "work_#{@work.id}_hours_time", :class => 'task_hours', :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?field=description', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
22
+ <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :next_field => :invoice} do |f|%>
23
+ <%=f.text_field :hours_time, :value => (@work.hours && @work.hours > 0 ? t(@work.hours) : ''), :id => "work_#{@work.id}_hours_time", :class => 'task_hours', :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=invoice', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
24
24
  <% end %>
25
25
  </td>
26
- <td align="right">
26
+ <td style="text-align: left">
27
27
  <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id} do |f|%>
28
- <%=f.check_box :invoice, :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?field=description', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
28
+ <%=f.check_box :invoice, :id => "work_#{@work.id}_invoice", :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=invoice', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
29
29
  <% end %>
30
30
  </td>
31
31
  <td>
@@ -1,3 +1,6 @@
1
1
  <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :next_field => next_field} do |f|%>
2
- <%=text_field :work, field, :id => "work_#{@work.id}_#{field}", :class => ('task_time' if field=~/_at_time$/), :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=#{next_field}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})" %>
2
+ <%=text_field :work, field, :id => "work_#{@work.id}_#{field}",
3
+ :class => ('task_time' if field=~/_at_time$/),
4
+ :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=#{next_field}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)});"
5
+ %>
3
6
  <% end %>
@@ -1,4 +1,4 @@
1
- <% @page_title = "#{l :experimental} #{l :daily_work_sheet} on #{@date}" + (" for #{user.login}" if user?) %>
1
+ <% @page_title = "#{l :daily_work_sheet} on #{@date}" + (" for #{user.login}" if user?) %>
2
2
 
3
3
  <div id="spotlight">
4
4
 
@@ -52,13 +52,16 @@
52
52
  <td align="right" valign="bottom">
53
53
  <%=text_field :work, :started_at_time, :value => last_work && last_work.completed_at && last_work.completed_at.strftime('%H:%M'), :class => 'task_time' %>
54
54
  </td>
55
- <td align="right" valign="bottom">
56
- <%=text_field :work, :completed_at_time, :class => 'task_time', :value => @work && @work.completed_at.strftime('%H:%M'), :onchange => "update_hours(this.form);" %>
55
+ <td align="left" valign="bottom">
56
+ <%=text_field :work, :completed_at_time, :class => 'task_time',
57
+ :value => @work && @work.completed_at.strftime('%H:%M'),
58
+ :onchange => "update_hours(this.form);"
59
+ %>
57
60
  </td>
58
61
  <td align="right" valign="bottom">
59
62
  <%=text_field :work, :hours_time, :value => '', :class => 'task_hours' %>
60
63
  </td>
61
- <td align="right" valign="bottom">
64
+ <td align="left" valign="bottom">
62
65
  <%=check_box :work, :invoice %>
63
66
  </td>
64
67
  <td valign="bottom">
@@ -90,10 +93,17 @@
90
93
 
91
94
  <script type="text/JavaScript">
92
95
  //<!--
93
- function update_hours(form){
94
- var started_at = form.elements['work_started_at_time'];
95
- var completed_at = form.elements['work_completed_at_time'];
96
- var hours_field = form.elements['work_hours_time'];
96
+ function update_hours(form, id) {
97
+ var id_str;
98
+ if(!id) {
99
+ id_str = '';
100
+ } else {
101
+ id_str = '_' + id;
102
+ }
103
+
104
+ var started_at = $('work' + id_str + '_started_at_time');
105
+ var completed_at = $('work' + id_str + '_completed_at_time');
106
+ var hours_field = $('work' + id_str + '_hours_time');
97
107
 
98
108
  if (started_at.value.length >= 4 && completed_at.value.length >= 4){
99
109
  var start_hours = started_at.value.substr(0,2);
@@ -107,7 +117,7 @@ function update_hours(form){
107
117
  hours--;
108
118
  minutes += 60;
109
119
  }
110
-
120
+
111
121
  hours_field.value = '' + hours + ':' + (minutes < 10 ? '0' : '') + minutes;
112
122
  }
113
123
  }
data/config/war.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # Goldspike configuration
2
2
 
3
3
  # Set the version of JRuby and GoldSpike to use:
4
- maven_library 'org.jruby', 'jruby-complete', '1.1RC1'
4
+ maven_library 'org.jruby', 'jruby-complete', '1.1RC2'
5
5
  maven_library 'backport-util-concurrent', 'backport-util-concurrent', '3.0'
6
6
  #maven_library 'org.jruby.extras', 'goldspike', '1.3-SNAPSHOT'
7
7
 
data/cruise_config.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  Project.configure do |project|
2
- project.email_notifier.emails = ['uwe@datek.no']
2
+ project.email_notifier.emails = ['espen@datek.no', 'uwe@datek.no']
3
3
  project.scheduler.polling_interval = 5.minutes
4
4
  project.build_command = 'export RAILS_ENV=test ; rake db:migrate ; rake'
5
5
  end
@@ -15,7 +15,7 @@ table.input td {vertical-align: top; margin: 0; border: 0; padding: 0 1px;}
15
15
  .highlight {background: yellow;}
16
16
  .task_list {width: 100%; text-align: left; list-style-type: none; padding-left: 0}
17
17
  .task_list li {margin-left: 0}
18
- .tasks {cursor: move}
18
+ .tasks {cursor: move; border: 1px solid transparent}
19
19
  .task_id {float: left; padding-left: 2px; width: 3.5em; text-align: center}
20
20
  .task_description {float: left; padding: 1px; width: auto}
21
21
  .task_start {float: left; width: 5em; text-align: center}
@@ -90,4 +90,25 @@ class WorkAccountsControllerTest < Test::Unit::TestCase
90
90
  WorkAccount.find(@first_id)
91
91
  }
92
92
  end
93
+
94
+ def test_works
95
+ get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}
96
+
97
+ assert_response :success
98
+ assert_template 'list'
99
+
100
+ assert_not_nil assigns(:works)
101
+ assert_equal 4, assigns(:works).size
102
+ end
103
+
104
+ def test_works_excel
105
+ get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}, :export => 'excel'
106
+
107
+ assert_response :success
108
+ assert_template 'list_excel'
109
+
110
+ assert_not_nil assigns(:works)
111
+ assert_equal 4, assigns(:works).size
112
+ end
113
+
93
114
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backlog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.0
4
+ version: 0.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uwe Kubosch
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-02-25 00:00:00 +01:00
12
+ date: 2008-02-26 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency