backlog 0.23.0 → 0.23.1

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