Project

General

Profile

Patch #1671 » version_times_320.diff

Ben Quinn, 2015-12-15 17:03

View differences:

app/helpers/versions_helper.rb (kopia robocza)
54 54
  def render_issue_status_by(version, criteria)
55 55
    criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
56 56

  
57
    h = Hash.new {|k,v| k[v] = [0, 0]}
58
    begin
59
      # Total issue count
60
      version.fixed_issues.group(criteria).count.each {|c,s| h[c][0] = s}
61
      # Open issues count
62
      version.fixed_issues.open.group(criteria).count.each {|c,s| h[c][1] = s}
63
    rescue ActiveRecord::RecordNotFound
64
    # When grouping by an association, Rails throws this exception if there's no result (bug)
57
    #sort them alphabetically by category name
58
    metrics = version.get_grouped_metrics(criteria).to_a.sort {|x, y| x[0].to_s <=> y[0].to_s} 
59
    max = {}
60
    
61
    [{:count => :total}, {:time => :total}].each do |metric_info| 
62
      metrics_group, total_metric = metric_info.to_a.flatten
63
      max[metrics_group] = metrics.map{|item| item[1]}.map {|item| item[metrics_group]}.map {|item| item[total_metric]}.max
64
      max[metrics_group] = 1 if max[metrics_group] == 0
65 65
    end
66 66
    # Sort with nil keys in last position
67
    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])}}
68
    max = counts.collect {|c| c[:total]}.max
69

  
70
    render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
67
    render :partial => 'issue_counts', :locals => {:version => version, 
68
      :criteria => criteria, :grouped_metrics => metrics, :max => max, 
69
      :spent_time_allowed => User.current.allowed_to_view_all_time_entries?(@project), 
70
      }
71 71
  end
72
  
73
  def time_progress(time_info)  
74
    logger.debug "time_info[:spent] = #{time_info[:spent].inspect}"
75
    logger.debug "time_info[:total] = #{time_info[:total].inspect}"
76
    if (time_info[:total] != 0)
77
      time_progress = time_info[:spent].to_f / time_info[:total]
78
    else
79
      time_progress = 0 #no total also means there's no spent time
80
    end    
81
    time_progress
82
  end
72 83

  
73 84
  def status_by_options_for_select(value)
74 85
    options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
app/models/version.rb (kopia robocza)
93 93
  def spent_hours
94 94
    @spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f
95 95
  end
96

  
96
   
97
  def calc_remaining_and_total_time
98
    @remaining_hours = 0
99
    @total_hours = 0
100
    get_grouped_metrics(:category).to_a.map{|item| item[1]}.map {|item| item[:time]}.each do |times| 
101
      @remaining_hours += times[:remaining]
102
      @total_hours += times[:total]
103
    end
104
  end
105
  
106
  def remaining_hours
107
    return @remaining_hours if @remaining_hours
108
    calc_remaining_and_total_time
109
    @remaining_hours    
110
  end
111
  
112
  def total_hours
113
    return @total_hours if @total_hours
114
    calc_remaining_and_total_time
115
    @total_hours
116
  end
117
  
97 118
  def closed?
98 119
    status == 'closed'
99 120
  end
......
162 183
    @closed_issues_count
163 184
  end
164 185

  
186
  def not_estimated_undone_count
187
    @not_estimated_undone_count ||= Issue.where("fixed_version_id = ? AND estimated_hours IS NULL AND " +
188
        "(closed_on is null)", self.id).count
189
  end
190

  
191

  
165 192
  def wiki_page
166 193
    if project.wiki && !wiki_page_title.blank?
167 194
      @wiki_page ||= project.wiki.find_page(wiki_page_title)
......
234 261
    fixed_issues.empty? && !referenced_by_a_custom_field?
235 262
  end
236 263

  
264
  def get_grouped_metrics(criteria)
265
    condition = issues_version_condition
266
    
267
    issues = Issue.includes(:status, criteria).where(condition)
268
    
269
    spent_times = {}
270
    TimeEntry.group(:issue_id).sum(:hours, :include => :issue,
271
        :conditions => condition).each do |issue_id, hours|
272
        
273
      spent_times[issue_id] = hours
274
    end
275

  
276
    categories_metrics = {}
277
    issues.each do |issue|
278
      category = issue.send(criteria)
279
      categories_metrics[category] ||= {}
280
      categories_metrics[category][:time] ||= {:estimated => 0, 
281
        :spent => 0, :remaining => 0, :total => 0}
282
      metrics = categories_metrics[category][:time]
283
      
284
      estimated = issue.estimated_hours || 0
285
      metrics[:estimated] += estimated
286
      spent = spent_times[issue.id] || 0
287
      metrics[:spent] += spent
288
      remaining = issue.closed? ? 0 : estimated - spent
289
      remaining = 0 if remaining < 0
290
      metrics[:remaining] += remaining
291
      metrics[:total] += (remaining + spent)
292
      
293
      categories_metrics[category][:count] ||= {:open => 0, :closed => 0, :total => 0}
294
      metrics = categories_metrics[category][:count]
295
      metrics[:total] += 1
296
      if issue.closed?
297
        metrics[:closed] += 1 
298
      else
299
        metrics[:open] += 1 
300
      end
301
    end
302
    categories_metrics
303
  end
304
  
305
  def issues_version_condition
306
    ["#{Issue.table_name}.fixed_version_id = ?", id]
307
  end
308

  
237 309
  private
238 310

  
239 311
  def load_issue_counts
240 312
    unless @issue_count
241 313
      @open_issues_count = 0
app/views/versions/_issue_counts.html.erb (kopia robocza)
7 7
                   :id => 'status_by_select',
8 8
                   :data => {:remote => true, :method => 'post', :url => status_by_version_path(version)})).html_safe %>
9 9
</legend>
10
<% if counts.empty? %>
10
<% if grouped_metrics.empty? %>
11 11
    <p><em><%= l(:label_no_data) %></em></p>
12 12
<% else %>
13
    <table>
14
    <% counts.each do |count| %>
15
    <tr>
16
        <td style="width:130px; text-align:right;">
17
          <% if count[:group] -%>
18
            <%= link_to(count[:group], project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => count[:group])) %>
19
          <% else -%>
20
            <%= link_to(l(:label_none), project_issues_path(version.project, :set_filter => 1, :status_id => '*', :fixed_version_id => version, "#{criteria}_id" => "!*")) %>
21
          <% end %>
22
        </td>
23
        <td style="width:240px;">
24
            <%= progress_bar((count[:closed].to_f / count[:total])*100,
25
                  :legend => "#{count[:closed]}/#{count[:total]}") %>
26
        </td>
27
    </tr>
13
    <table class="category_metrics">
14
    <% grouped_metrics.each do |metrics_group| 
15
      category, metrics = *metrics_group
16
    %>
17
    <% color_class = cycle('odd', 'even')%>
18
        <tr class="header <%= color_class %>">
19
            <td colspan="5">
20
                <%= criteria_operator = category ? "=" : "!*" 
21
                link_to category || "[#{l(:text_not_assigned)}]", 
22
                                            {:controller => 'issues', 
23
                                            :action => 'index',
24
                                            :project_id => version.project,
25
                                            :set_filter => 1,
26
                                            :fields => ["#{criteria}_id", "fixed_version_id", "status_id"],
27
                                            :values => {"#{criteria}_id" => [category], "fixed_version_id" => [version], "status_id" => [1]},
28
                                            :operators => {"#{criteria}_id" => criteria_operator, "fixed_version_id" => "=", "status_id" => "*"}
29
                                            }
30
                                          %>
31
            </td>
32
        </tr>
33
        <tr class="<%= color_class %>">
34
            <td><%= l(:label_issues_count) %> </td>
35
            <% if spent_time_allowed %>
36
            <td><%= l(:label_time) %></td>
37
            <% end %>
38
            <td class="metric_comment">
39
                <span title="<%= l(:field_estimated_hours) %>">
40
                    <%= l(:label_estimated_time_short) %> 
41
                </span>
42
            </td>
43
            <% if spent_time_allowed %>
44
            <td class="metric_comment">
45
                <span title="<%= l(:label_spent_time) %>">
46
                    <%= l(:label_spent_time_short) %> 
47
                </span>
48
            </td>
49
            <td class="metric_comment">
50
                <span title="<%= l(:label_remaining_time) %>">
51
                    <%= l(:label_remaining_time_short) %> 
52
                </span>
53
            </td>
54
            <% max_progress_width = 95 %>
55
            <% else
56
                max_progress_width = 150
57
            end %>
58
        </tr>
59
        <tr class="<%= color_class %>">
60
            <td class="progress count">
61
                <%= count = metrics[:count]; progress_bar((count[:closed].to_f / count[:total])*100, 
62
                      :legend => 
63
                        "#{count[:closed]}/#{count[:total]}</span>".html_safe,                          
64
                      :width => "#{(count[:total].to_f / max[:count] * max_progress_width).floor}px;") %>
65
            </td>
66
            
67
            <% 
68
              time = metrics[:time]
69
              if spent_time_allowed %>
70
            <td class="progress time">
71
                <%= progress_bar(time_progress(time)*100, 
72
                      :legend => 
73
                        "#{time[:spent].ceil}/#{time[:total].ceil}",                          
74
                      :width => "#{(time[:total] / max[:time] * max_progress_width).floor}px;") %>
75
            </td>
76
            <% end %>
77
            <% hours = l(:text_hours_short) %>
78
            <td class="metric_comment">
79
                <span title="<%= l(:field_estimated_hours) %>">
80
                    <%= "#{time[:estimated].ceil}#{hours}" %>
81
                </span>                
82
            </td>
83
            <% if spent_time_allowed %>
84
            <td class="metric_comment">
85
                <span title="<%= l(:label_spent_time) %>">
86
                    <%= "#{time[:spent].ceil}#{hours}" %>
87
                </span>                                
88
            </td>
89
            <td class="metric_comment">
90
                <span title="<%= l(:label_remaining_time) %>">
91
                    <%= "#{time[:remaining].ceil}#{hours}" %>
92
                </span>                                                
93
            </td>
94
            <% end %>
95
        </tr>
28 96
    <% end %>
29 97
    </table>
30 98
<% end %>
app/views/versions/show.html.erb (kopia robocza)
19 19
    <th><%= l(:field_estimated_hours) %></th>
20 20
    <td class="total-hours"><%= html_hours(l_hours(@version.estimated_hours)) %></td>
21 21
</tr>
22
<% if @version.not_estimated_undone_count > 0 %>
23
<tr>
24
    <th><%= l(:label_not_estimated_and_undone) %></th>
25
    <td class="not_estimated total-hours"><%= link_to("%s" % 
26
        [@version.not_estimated_undone_count.to_s+" "+l(:label_issue_plural).downcase, @version.not_estimated_undone_count],                                             
27
        {:controller => 'issues', 
28
        :action => 'index',
29
        :project_id => @version.project,
30
        :set_filter => 1,
31
        :fields => ["estimated_hours", "fixed_version_id", "status_id"],
32
        :values => {"estimated_hours" => [1], "fixed_version_id" => [@version], "status_id" => [1]},
33
        :operators => {"estimated_hours" => "!*", "fixed_version_id" => "=", "status_id" => "o"}
34
        }
35
)%></td>
36
</tr>
37
<% end %>
38

  
22 39
<% if User.current.allowed_to_view_all_time_entries?(@project) %>
23 40
<tr>
24 41
    <th><%= l(:label_spent_time) %></th>
25 42
    <td class="total-hours"><%= html_hours(l_hours(@version.spent_hours)) %></td>
26 43
</tr>
44
<tr>
45
    <th><%= l(:label_remaining_time) %></th>
46
    <td class="total-hours"><%= html_hours(l_hours(@version.remaining_hours)) %></td>
47
</tr>
48
<tr>
49
    <% title = "#{l(:label_spent_time)} + #{l(:label_remaining_time)}" %>
50
    <th title="<%= title %>"><%= l(:label_current_total_time) %></th>
51
    <td class="total-hours" "><%= html_hours(l_hours(@version.total_hours)) %></td>
52
</tr>
27 53
<% end %>
28 54
</table>
29 55
</fieldset>
config/locales/en.yml (kopia robocza)
389 389
  setting_enabled_scm: Enabled SCM
390 390
  setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
391 391
  setting_mail_handler_api_enabled: Enable WS for incoming emails
392
  label_remaining_time: Remaining time
393
  label_current_total_time: Total
394
  label_estimated_time_short: Est.
395
  label_spent_time_short: Spent
396
  label_remaining_time_short: Rem.
397
  label_issues_count: Count
398
  label_not_estimated_and_undone: Not estimated
399
  label_time: Time
400
  text_hours_short: h
401
  text_not_assigned: Not Assigned
392 402
  setting_mail_handler_api_key: API key
393 403
  setting_sequential_project_identifiers: Generate sequential project identifiers
394 404
  setting_gravatar_enabled: Use Gravatar user icons
......
635 645
  label_open_issues_plural: open
636 646
  label_closed_issues: closed
637 647
  label_closed_issues_plural: closed
648
  label_done_issues: done
649
  label_done_issues_plural: done
650
  label_undone_issues: undone
651
  label_undone_issues_plural: undone
638 652
  label_x_open_issues_abbr:
639 653
    zero:  0 open
640 654
    one:   1 open
public/stylesheets/application.css (kopia robocza)
444 444
div#version-summary fieldset { margin-bottom: 1em; }
445 445
div#version-summary fieldset.time-tracking table { width:100%; }
446 446
div#version-summary th, div#version-summary td.total-hours { text-align: right; }
447
.not_estimated a { color: red; }
447 448

  
448 449
table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
449 450
table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
(6-6/6)