Feature #1244 ยป version-dependency.patch
app/helpers/versions_helper.rb (working copy) | ||
---|---|---|
26 | 26 |
h = Hash.new {|k,v| k[v] = [0, 0]} |
27 | 27 |
begin |
28 | 28 |
# Total issue count |
29 |
Issue.count(:group => criteria, |
|
30 |
:conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s} |
|
31 |
# Open issues count |
|
32 |
Issue.count(:group => criteria, |
|
33 |
:include => :status, |
|
34 |
:conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} |
|
29 |
if version.children.count > 0 |
|
30 |
Issue.count(:group => criteria, |
|
31 |
:conditions => ["#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{version.children.collect{|p| p.id}.join(',')})", version.id]).each {|c,s| h[c][0] = s} |
|
32 |
# Open issues count |
|
33 |
Issue.count(:group => criteria, |
|
34 |
:include => :status, |
|
35 |
:conditions => ["(#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{version.children.collect{|p| p.id}.join(',')})) AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} |
|
36 |
else |
|
37 |
Issue.count(:group => criteria, |
|
38 |
:conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s} |
|
39 |
# Open issues count |
|
40 |
Issue.count(:group => criteria, |
|
41 |
:include => :status, |
|
42 |
:conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} |
|
43 |
end |
|
35 | 44 |
rescue ActiveRecord::RecordNotFound |
36 | 45 |
# When grouping by an association, Rails throws this exception if there's no result (bug) |
37 | 46 |
end |
app/models/version.rb (working copy) | ||
---|---|---|
18 | 18 |
class Version < ActiveRecord::Base |
19 | 19 |
before_destroy :check_integrity |
20 | 20 |
belongs_to :project |
21 |
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id' |
|
21 |
belongs_to :parent, :class_name => "Version", :foreign_key => "parent_version_id" |
|
22 |
has_many :children, :class_name => "Version", :foreign_key => "parent_version_id" |
|
23 |
has_many :fixed_issues, :class_name => 'Issue', :finder_sql => 'select distinct i.* from issues i,versions v WHERE i.fixed_version_id = #{id} OR (i.fixed_version_id = v.id AND v.parent_version_id = #{id})', :counter_sql => 'select count(distinct i.id) from issues i,versions v WHERE i.fixed_version_id = #{id} OR (i.fixed_version_id = v.id AND v.parent_version_id = #{id})' |
|
22 | 24 |
has_many :attachments, :as => :container, :dependent => :destroy |
23 | 25 | |
24 | 26 |
validates_presence_of :name |
... | ... | |
36 | 38 |
|
37 | 39 |
# Returns the total estimated time for this version |
38 | 40 |
def estimated_hours |
39 |
@estimated_hours ||= fixed_issues.sum(:estimated_hours).to_f |
|
41 |
hours = 0 |
|
42 |
fixed_issues.each {|i| hours += i.estimated_hours.to_f} |
|
43 |
@estimated_hours ||= hours |
|
40 | 44 |
end |
41 | 45 |
|
42 | 46 |
# Returns the total reported time for this version |
43 | 47 |
def spent_hours |
44 |
@spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f |
|
48 |
if self.children.count > 0 |
|
49 |
@spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})", id]).to_f |
|
50 |
else |
|
51 |
@spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f |
|
52 |
end |
|
45 | 53 |
end |
46 | 54 |
|
47 | 55 |
# Returns true if the version is completed: due date reached and no open issues |
... | ... | |
54 | 62 |
0 |
55 | 63 |
elsif open_issues_count == 0 |
56 | 64 |
100 |
57 |
else |
|
58 |
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["fixed_version_id = ? AND is_closed = ?", id, false]).to_f) / fixed_issues.count |
|
65 |
elsif self.children.count > 0 |
|
66 |
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", id, false]).to_f) / fixed_issues.count |
|
67 |
else |
|
68 |
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["(fixed_version_id = ?) AND is_closed = ?", id, false]).to_f) / fixed_issues.count |
|
59 | 69 |
end |
60 | 70 |
end |
61 | 71 |
|
... | ... | |
73 | 83 |
end |
74 | 84 |
|
75 | 85 |
def open_issues_count |
76 |
@open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status) |
|
86 |
if self.children.count > 0 |
|
87 |
@open_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", self.id, false], :include => :status) |
|
88 |
else |
|
89 |
@open_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ?) AND is_closed = ?", self.id, false], :include => :status) |
|
90 |
end |
|
77 | 91 |
end |
78 | 92 | |
79 | 93 |
def closed_issues_count |
80 |
@closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status) |
|
94 |
if self.children.count > 0 |
|
95 |
@closed_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", self.id, true], :include => :status) |
|
96 |
else |
|
97 |
@closed_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ?) AND is_closed = ?", self.id, true], :include => :status) |
|
98 |
end |
|
81 | 99 |
end |
82 | 100 |
|
83 | 101 |
def wiki_page |
app/controllers/versions_controller.rb (working copy) | ||
---|---|---|
23 | 23 |
end |
24 | 24 |
|
25 | 25 |
def edit |
26 |
@versions = Version.find(:all, :conditions => ["id <> ? AND parent_version_id IS NULL", params[:id]]).collect{|p| [ p.name, p.id ] } |
|
26 | 27 |
if request.post? and @version.update_attributes(params[:version]) |
27 | 28 |
flash[:notice] = l(:notice_successful_update) |
28 | 29 |
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project |
app/controllers/projects_controller.rb (working copy) | ||
---|---|---|
180 | 180 |
# Add a new version to @project |
181 | 181 |
def add_version |
182 | 182 |
@version = @project.versions.build(params[:version]) |
183 |
@versions = Version.find(:all, :conditions => ["parent_version_id IS NULL"]).collect{|p| [ p.name, p.id ] } |
|
183 | 184 |
if request.post? and @version.save |
184 | 185 |
flash[:notice] = l(:notice_successful_create) |
185 | 186 |
redirect_to :action => 'settings', :tab => 'versions', :id => @project |
... | ... | |
213 | 214 |
def roadmap |
214 | 215 |
@trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true]) |
215 | 216 |
retrieve_selected_tracker_ids(@trackers) |
216 |
@versions = @project.versions.sort
|
|
217 |
@versions = (@project.versions.delete_if {|x| x.parent_version_id }).sort
|
|
217 | 218 |
@versions = @versions.select {|v| !v.completed? } unless params[:completed] |
218 | 219 |
end |
219 | 220 |
|
app/views/versions/show.rhtml (working copy) | ||
---|---|---|
2 | 2 |
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> |
3 | 3 |
</div> |
4 | 4 | |
5 |
<h2><%= h(@version.name) %></h2> |
|
5 |
<h2><%= h(@version.name) %> <% if @version.parent_version_id %> |
|
6 |
(<%= l(:label_child_of)%> <%= link_to @version.parent.name, :controller => 'versions', :action => 'show', :id => @version.parent.id %>) |
|
7 |
<% end %></h2> |
|
6 | 8 | |
7 | 9 |
<div id="version-summary"> |
8 | 10 |
<% if @version.estimated_hours > 0 || User.current.allowed_to?(:view_time_entries, @project) %> |
... | ... | |
25 | 27 |
<div id="status_by"> |
26 | 28 |
<%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %> |
27 | 29 |
</div> |
30 | ||
31 |
<% if @version.children.count > 0 %> |
|
32 |
<div id="child_versions"> |
|
33 |
<fieldset> |
|
34 |
<legend><%= l(:label_child_versions)%></legend> |
|
35 |
<ul> |
|
36 |
<% @version.children.each do |v| %> |
|
37 |
<li><%= link_to v.name, :controller => 'versions', :action => 'show', :id => v.id %></li> |
|
38 |
<% end %> |
|
39 |
</ul> |
|
40 |
</fieldset> |
|
28 | 41 |
</div> |
42 |
<% end %> |
|
29 | 43 | |
44 |
</div> |
|
45 | ||
30 | 46 |
<div id="roadmap"> |
31 | 47 |
<%= render :partial => 'versions/overview', :locals => {:version => @version} %> |
32 | 48 |
<%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %> |
... | ... | |
34 | 50 |
<% issues = @version.fixed_issues.find(:all, |
35 | 51 |
:include => [:status, :tracker], |
36 | 52 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %> |
37 |
<% if issues.size > 0 %> |
|
53 |
<% if issues && issues.size > 0 %>
|
|
38 | 54 |
<fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend> |
39 | 55 |
<ul> |
40 | 56 |
<% issues.each do |issue| -%> |
app/views/versions/_form.rhtml (working copy) | ||
---|---|---|
5 | 5 |
<p><%= f.text_field :description, :size => 60 %></p> |
6 | 6 |
<p><%= f.text_field :wiki_page_title, :label => :label_wiki_page, :size => 60, :disabled => @project.wiki.nil? %></p> |
7 | 7 |
<p><%= f.text_field :effective_date, :size => 10 %><%= calendar_for('version_effective_date') %></p> |
8 |
<p><%= f.select :parent_version_id, @versions, {:include_blank => true} %></p> |
|
8 | 9 |
</div> |
app/views/versions/_issue_counts.rhtml (working copy) | ||
---|---|---|
15 | 15 |
<% counts.each do |count| %> |
16 | 16 |
<tr> |
17 | 17 |
<td width="130px" align="right" > |
18 |
<%= link_to count[:group], {:controller => 'issues', |
|
19 |
:action => 'index', |
|
20 |
:project_id => version.project, |
|
21 |
:set_filter => 1, |
|
22 |
:fixed_version_id => version, |
|
23 |
"#{criteria}_id" => count[:group]} %> |
|
18 |
<% puts "BOO " + (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq.to_s %> |
|
19 |
<%= link_to count[:group], {:controller => 'issues', |
|
20 |
:action => 'index', |
|
21 |
:project_id => version.project, |
|
22 |
:set_filter => 1, |
|
23 |
:fields => ["#{criteria}_id", "fixed_version_id"], |
|
24 |
:operators => { :start_date => "<t+", |
|
25 |
:created_on => ">t-", |
|
26 |
:estimated_hours => "=", |
|
27 |
:updated_on => ">t-", |
|
28 |
:priority_id => "=", |
|
29 |
:subject => "~", |
|
30 |
:fixed_version_id => "=", |
|
31 |
:category_id =>"=", |
|
32 |
:tracker_id => "=", |
|
33 |
:done_ratio =>"=", |
|
34 |
:due_date => "<t+", |
|
35 |
:assigned_to_id => "=", |
|
36 |
:author_id => "=", |
|
37 |
:status_id => "o" }, |
|
38 |
:values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, |
|
39 |
"#{criteria}_id".to_sym => [count[:group]]} } %> |
|
24 | 40 |
</td> |
25 | 41 |
<td width="240px"> |
26 | 42 |
<%= progress_bar((count[:closed].to_f / count[:total])*100, |
app/views/versions/_overview.rhtml (working copy) | ||
---|---|---|
9 | 9 |
<% if version.fixed_issues.count > 0 %> |
10 | 10 |
<%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %> |
11 | 11 |
<p class="progress-info"> |
12 |
<%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %>
|
|
12 |
<%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :set_filter => 1, :fields => ["status_id", "fixed_version_id"], :operators => {:fixed_version_id => "=",:status_id => "c"}, :values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, :status_id =>["1"]}) %>
|
|
13 | 13 |
<%= lwr(:label_closed_issues, version.closed_issues_count) %> |
14 | 14 |
(<%= '%0.0f' % (version.closed_issues_count.to_f / version.fixed_issues.count * 100) %>%) |
15 | 15 |
  |
16 |
<%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %>
|
|
16 |
<%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :set_filter => 1, :fields => ["status_id", "fixed_version_id"], :operators => {:fixed_version_id => "=",:status_id => "o"}, :values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, :status_id =>["1"]}) %>
|
|
17 | 17 |
<%= lwr(:label_open_issues, version.open_issues_count)%> |
18 | 18 |
(<%= '%0.0f' % (version.open_issues_count.to_f / version.fixed_issues.count * 100) %>%) |
19 | 19 |
</p> |
app/views/projects/roadmap.rhtml (working copy) | ||
---|---|---|
5 | 5 |
<% else %> |
6 | 6 |
<div id="roadmap"> |
7 | 7 |
<% @versions.each do |version| %> |
8 |
<div class="top-level-version"> |
|
8 | 9 |
<%= tag 'a', :name => version.name %> |
9 | 10 |
<h3 class="icon22 icon22-package"><%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %></h3> |
10 | 11 |
<%= render :partial => 'versions/overview', :locals => {:version => version} %> |
11 | 12 |
<%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %> |
12 | 13 | |
13 |
<% issues = version.fixed_issues.find(:all, |
|
14 |
:include => [:status, :tracker], |
|
15 |
:conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"], |
|
16 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty? |
|
17 |
issues ||= [] |
|
18 |
%> |
|
14 |
<% |
|
15 |
issues = version.fixed_issues.delete_if {|i| !@selected_tracker_ids.include? i.tracker_id.to_s } |
|
16 |
%> |
|
19 | 17 |
<% if issues.size > 0 %> |
20 | 18 |
<fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend> |
21 | 19 |
<ul> |
... | ... | |
25 | 23 |
</ul> |
26 | 24 |
</fieldset> |
27 | 25 |
<% end %> |
26 |
<% if version.children.count > 0 %> |
|
27 |
<div class="child-versions"> |
|
28 |
<h3><%= l(:label_child_versions) %></h3> |
|
29 |
<% version.children.each do |v| %> |
|
30 |
<div class="child-version"> |
|
31 |
<%= tag 'a', :name => v.name %> |
|
32 |
<h3 class="icon22 icon22-package"><%= link_to h(v.name), :controller => 'versions', :action => 'show', :id => v %></h3> |
|
33 |
<%= render :partial => 'versions/overview', :locals => {:version => v} %> |
|
34 |
<% |
|
35 |
issues = v.fixed_issues.delete_if {|i| !@selected_tracker_ids.include? i.tracker_id.to_s } |
|
36 |
if issues.size > 0 %> |
|
37 |
<fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend> |
|
38 |
<ul> |
|
39 |
<%- issues.each do |issue| -%> |
|
40 |
<li><%= link_to_issue(issue) %>: <%=h issue.subject %></li> |
|
41 |
<%- end -%> |
|
42 |
</ul> |
|
43 |
</fieldset> |
|
44 |
<% end %> |
|
45 |
</div> |
|
46 |
<% end %> |
|
47 |
|
|
48 |
</div> |
|
49 |
<% end %> |
|
50 |
</div> |
|
28 | 51 |
<% end %> |
29 | 52 |
</div> |
30 | 53 |
<% end %> |
lang/en.yml (working copy) | ||
---|---|---|
184 | 184 |
field_default_value: Default value |
185 | 185 |
field_comments_sorting: Display comments |
186 | 186 |
field_parent_title: Parent page |
187 |
field_parent_version: Parent version |
|
187 | 188 | |
188 | 189 |
setting_app_title: Application title |
189 | 190 |
setting_app_subtitle: Application subtitle |
... | ... | |
429 | 430 |
label_wiki_edit_plural: Wiki edits |
430 | 431 |
label_wiki_page: Wiki page |
431 | 432 |
label_wiki_page_plural: Wiki pages |
433 |
label_parent_version: Parent version |
|
432 | 434 |
label_index_by_title: Index by title |
433 | 435 |
label_index_by_date: Index by date |
434 | 436 |
label_current_version: Current version |
... | ... | |
527 | 529 |
label_incoming_emails: Incoming emails |
528 | 530 |
label_generate_key: Generate a key |
529 | 531 |
label_issue_watchers: Watchers |
532 |
label_child_versions: Child Versions |
|
533 |
label_child_of: child of |
|
530 | 534 | |
531 | 535 |
button_login: Login |
532 | 536 |
button_submit: Submit |
db/migrate/099_add_version_dependency.rb (revision 0) | ||
---|---|---|
1 |
class AddVersionDependency < ActiveRecord::Migration |
|
2 |
def self.up |
|
3 |
add_column :versions, :parent_version_id, :integer |
|
4 |
end |
|
5 | ||
6 |
def self.down |
|
7 |
remove_column :versions, :parent_version_id |
|
8 |
end |
|
9 |
end |
public/stylesheets/application.css (working copy) | ||
---|---|---|
209 | 209 |
div#roadmap .wiki h1:first-child { display: none; } |
210 | 210 |
div#roadmap .wiki h1 { font-size: 120%; } |
211 | 211 |
div#roadmap .wiki h2 { font-size: 110%; } |
212 |
div#roadmap .top-level-version { padding: 8px; margin-bottom: 10px; border: 4px solid #e0e0e0;} |
|
213 |
div#roadmap .child-version { margin-left: 20px;} |
|
212 | 214 | |
213 | 215 |
div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; } |
214 | 216 |
div#version-summary fieldset { margin-bottom: 1em; } |