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 +22 -0
- data/app/controllers/application.rb +0 -6
- data/app/controllers/work_accounts_controller.rb +1 -1
- data/app/controllers/works_controller.rb +9 -24
- data/app/models/work.rb +8 -3
- data/app/models/work_lock_nagger.rb +5 -2
- data/app/views/layouts/mwrt002.rhtml +7 -3
- data/app/views/tasks/_form.rhtml +14 -0
- data/app/views/works/_row.rhtml +5 -5
- data/app/views/works/_row_field.rhtml +4 -1
- data/app/views/works/daily_work_sheet.rhtml +19 -9
- data/config/war.rb +1 -1
- data/cruise_config.rb +1 -1
- data/public/stylesheets/backlog.css +1 -1
- data/test/functional/work_accounts_controller_test.rb +21 -0
- metadata +2 -2
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
|
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
|
-
|
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 => '
|
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})
|
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})
|
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
|
-
|
12
|
-
|
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 => '
|
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, :
|
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
|
-
|
20
|
+
// alert("shift: " + event.shiftKey + ", alt: " + event.altKey + ", ctrl: " + event.ctrlKey + ", " + event.charCode + ", " + event.keyCode);
|
17
21
|
}
|
18
22
|
}
|
19
23
|
</script>
|
data/app/views/tasks/_form.rhtml
CHANGED
@@ -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%>
|
data/app/views/works/_row.rhtml
CHANGED
@@ -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="
|
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}?
|
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
|
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}?
|
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}",
|
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 :
|
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="
|
56
|
-
|
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="
|
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
|
95
|
-
|
96
|
-
|
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.
|
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.
|
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-
|
12
|
+
date: 2008-02-26 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|