Index: app/helpers/versions_helper.rb =================================================================== --- app/helpers/versions_helper.rb (wersja 13103) +++ app/helpers/versions_helper.rb (kopia robocza) @@ -32,21 +32,32 @@ def render_issue_status_by(version, criteria) criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria) - h = Hash.new {|k,v| k[v] = [0, 0]} - begin - # Total issue count - Issue.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][0] = s} - # Open issues count - Issue.open.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][1] = s} - rescue ActiveRecord::RecordNotFound - # When grouping by an association, Rails throws this exception if there's no result (bug) + #sort them alphabetically by category name + metrics = version.get_grouped_metrics(criteria).to_a.sort {|x, y| x[0].to_s <=> y[0].to_s} + max = {} + + [{:count => :total}, {:time => :total}].each do |metric_info| + metrics_group, total_metric = metric_info.to_a.flatten + max[metrics_group] = metrics.map{|item| item[1]}.map {|item| item[metrics_group]}.map {|item| item[total_metric]}.max + max[metrics_group] = 1 if max[metrics_group] == 0 end # Sort with nil keys in last position - counts = h.keys.sort {|a,b| a.nil? ? 1 : (b.nil? ? -1 : a <=> b)}.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}} - max = counts.collect {|c| c[:total]}.max - - render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max} + render :partial => 'issue_counts', :locals => {:version => version, + :criteria => criteria, :grouped_metrics => metrics, :max => max, + :spent_time_allowed => User.current.allowed_to?(:view_time_entries, @project), + } end + + def time_progress(time_info) + logger.debug "time_info[:spent] = #{time_info[:spent].inspect}" + logger.debug "time_info[:total] = #{time_info[:total].inspect}" + if (time_info[:total] != 0) + time_progress = time_info[:spent].to_f / time_info[:total] + else + time_progress = 0 #no total also means there's no spent time + end + time_progress + end def status_by_options_for_select(value) options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value) Index: app/models/version.rb =================================================================== --- app/models/version.rb (wersja 13103) +++ app/models/version.rb (kopia robocza) @@ -82,7 +82,28 @@ def spent_hours @spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f end - + + def calc_remaining_and_total_time + @remaining_hours = 0 + @total_hours = 0 + get_grouped_metrics(:category).to_a.map{|item| item[1]}.map {|item| item[:time]}.each do |times| + @remaining_hours += times[:remaining] + @total_hours += times[:total] + end + end + + def remaining_hours + return @remaining_hours if @remaining_hours + calc_remaining_and_total_time + @remaining_hours + end + + def total_hours + return @total_hours if @total_hours + calc_remaining_and_total_time + @total_hours + end + def closed? status == 'closed' end @@ -163,6 +184,14 @@ @closed_issues_count end + def not_estimated_undone_count + @not_estimated_undone_count ||= Issue.count(:all, :conditions => + ["fixed_version_id = ? AND estimated_hours IS NULL AND " + + "(closed_on is null)", self.id + ], :include => :status) + end + + def wiki_page if project.wiki && !wiki_page_title.blank? @wiki_page ||= project.wiki.find_page(wiki_page_title) @@ -226,8 +255,53 @@ end end + def get_grouped_metrics(criteria) + condition = issues_version_condition + + issues = Issue.find(:all, :include => [:status, criteria], + :conditions => condition) + + spent_times = {} + TimeEntry.sum(:hours, :group => :issue_id, :include => :issue, + :conditions => condition).each do |issue_id, hours| + + spent_times[issue_id] = hours + end + + categories_metrics = {} + issues.each do |issue| + category = issue.send(criteria) + categories_metrics[category] ||= {} + categories_metrics[category][:time] ||= {:estimated => 0, + :spent => 0, :remaining => 0, :total => 0} + metrics = categories_metrics[category][:time] + + estimated = issue.estimated_hours || 0 + metrics[:estimated] += estimated + spent = spent_times[issue.id] || 0 + metrics[:spent] += spent + remaining = issue.closed? ? 0 : estimated - spent + remaining = 0 if remaining < 0 + metrics[:remaining] += remaining + metrics[:total] += (remaining + spent) + + categories_metrics[category][:count] ||= {:open => 0, :closed => 0, :total => 0} + metrics = categories_metrics[category][:count] + metrics[:total] += 1 + if issue.closed? + metrics[:closed] += 1 + else + metrics[:open] += 1 + end + end + categories_metrics + end + + def issues_version_condition + ["#{Issue.table_name}.fixed_version_id = ?", id] + end + private - def load_issue_counts unless @issue_count @open_issues_count = 0 Index: app/views/versions/_issue_counts.html.erb =================================================================== --- app/views/versions/_issue_counts.html.erb (wersja 13103) +++ app/views/versions/_issue_counts.html.erb (kopia robocza) @@ -7,25 +7,92 @@ :id => 'status_by_select', :data => {:remote => true, :method => 'post', :url => status_by_version_path(version)})).html_safe %> -<% if counts.empty? %> +<% if grouped_metrics.empty? %>
<%= l(:label_no_data) %>
<% else %> -- <% if count[:group] -%> - <%= link_to(h(count[:group]), project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => count[:group])) %> - <% else -%> - <%= link_to(l(:label_none), project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => "!*")) %> - <% end %> - | -- <%= progress_bar((count[:closed].to_f / count[:total])*100, - :legend => "#{count[:closed]}/#{count[:total]}", - :width => "#{(count[:total].to_f / max * 200).floor}px;") %> - | -
+ <%= criteria_operator = category ? "=" : "!*" + link_to category || "[#{l(:text_not_assigned)}]", + {:controller => 'issues', + :action => 'index', + :project_id => version.project, + :set_filter => 1, + :fields => ["#{criteria}_id", "fixed_version_id", "status_id"], + :values => {"#{criteria}_id" => [category], "fixed_version_id" => [version], "status_id" => [1]}, + :operators => {"#{criteria}_id" => criteria_operator, "fixed_version_id" => "=", "status_id" => "*"} + } + %> + | +||||
<%= l(:label_issues_count) %> | + <% if spent_time_allowed %> +<%= l(:label_time) %> | + <% end %> ++ + <%= l(:label_estimated_time_short) %> + + | + <% if spent_time_allowed %> ++ + <%= l(:label_spent_time_short) %> + + | ++ + <%= l(:label_remaining_time_short) %> + + | + <% max_progress_width = 95 %> + <% else + max_progress_width = 150 + end %> +
+ <%= count = metrics[:count]; progress_bar((count[:closed].to_f / count[:total])*100, + :legend => + "#{count[:closed]}/#{count[:total]}".html_safe, + :width => "#{(count[:total].to_f / max[:count] * max_progress_width).floor}px;") %> + | + + <% + time = metrics[:time] + if spent_time_allowed %> ++ <%= progress_bar(time_progress(time)*100, + :legend => + "#{time[:spent].ceil}/#{time[:total].ceil}", + :width => "#{(time[:total] / max[:time] * max_progress_width).floor}px;") %> + | + <% end %> + <% hours = l(:text_hours_short) %> ++ + <%= "#{time[:estimated].ceil}#{hours}" %> + + | + <% if spent_time_allowed %> ++ + <%= "#{time[:spent].ceil}#{hours}" %> + + | ++ + <%= "#{time[:remaining].ceil}#{hours}" %> + + | + <% end %> +